前言

osgearth_eci示例,展示了J2000的天体坐标系和ECEF地固系的转换,绘制坐标系,以及读取卫星参数绘制卫星的功能。绘制卫星轨迹,添加差值效果和未添加差值的效果。

关于卫星两行根数的数据文件下载路径:CelesTrak: Historical NORAD Two-Line Element Sets

关于卫星两行根数的解释:CelesTrak: NORAD Two-Line Element Set Format

执行命令

// J2000ECI坐标系下绘制, --tessellate 开启差值功能。最好不要开启
osgearth_ecid.exe earth_image\world.earth --tle F:\osgData\Data\space\gps1-01.txt --tessellate// 还可以通过 --maxpoints 1000 控制添加跟踪卫星的数量
osgearth_ecid.exe earth_image\world.earth --tle F:\osgData\Data\space\gps1-01.txt --maxpoints 1000 --tessellate// ECEF地固系下 绘制,--tle 加载的文件,一定要写全路径,否则无法找到文件,即使放入系统环境变量也没用。
osgearth_ecid.exe earth_image\world.earth --tle F:\osgData\Data\space\gps1-01.txt --ecef --tessellate

效果

默认J2000ECI坐标系下绘制,此时设置时间为最终时间。共读取了4076条数据。

在ECEF地固坐标系下绘制,此时设置时间为最终时间。共读取了4076条数据。如果代码中app.trackDrawable->load(app.true);参数设置为true,就会出现这种效果。

在ECEF地固坐标系下绘制,此时设置时间为最终时间。共读取了4076条数据。限制绘制100条的情况。如果代码中app.trackDrawable->load(app.true);参数设置为true,就会出现这种效果。

代码分析

1、J2000ECI坐标系:本例中,以地球为中心,Z轴指向北天极,X轴指向2000年1月1日中午12点的春分点,Y轴与Z、X成右手直角坐标系。所以定义J2000ECI坐标系时,需要定义时间。这个坐标系,是不会随着地球自转的。

2、ECEF地固坐标系:以地球质心为中心,Z轴指向北天极,X轴指向格林尼治天文台零度子午面与协议地球极赤道的交点,Y轴与Z、X成右手直角坐标系。这个坐标系是会随着地球自转而自转的。所以两个坐标系转换,需要乘以地球自转的旋转角矩阵,来实现转换。

3、卫星两行根数:跟据两行根数的各个内容,就可以计算出,卫星什么时候在什么坐标下,且速度、加速度等参数,都会有。

/*** Experiment with using a J2000/ECI reference frame as the root of the scene,* with the MapNode under an ECI-to-ECEF transform.* 尝试使用J2000/ECI参考帧作为场景的根,并在ECI到ECEF变换下使用MapNode。*/
#include <osgEarth/MapNode>
#include <osgEarth/DateTime>
#include <osgEarth/NodeUtils>
#include <osgEarth/PointDrawable>
#include <osgEarth/CullingUtils>
#include <osgEarth/LineDrawable>
#include <osgEarth/Lighting>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/Sky>
#include <osgEarthSymbology/Color>
#include <osgEarthAnnotation/LabelNode>
#include <osgViewer/Viewer>
#include <iostream>#define LC "[eci] "using namespace osgEarth;
using namespace osgEarth::Util;
using namespace osgEarth::Symbology;
using namespace osgEarth::Annotation;
namespace ui = osgEarth::Util::Controls;int
usage(const char* name, const char* msg)
{OE_NOTICE << "\nUsage: " << name << " [file.earth]\n"<< "     --tle <filename>    : Load a NORAD TLE file\n"       // 加载tle文件<< "     --maxpoints <num>   : Limit the track size to <num> points\n"        // 限制要绘制卫星的个数<< "     --ecef              : View the track in ECEF space instead of ECI\n" // 在ECEF空间而不是ECI中查看轨迹。<< "     --tessellate        : Add interpolated points to the track data\n"        // 向轨迹数据添加插值点<< "\nDownload NORAD TLE files from https://www.celestrak.com/NORAD/archives\n\n" // 下载 NORAD TLE 文件地址<< msg << std::endl;return 0;
}// Reference time for the J2000 ECI coordinate frame
// J2000 ECI坐标系的参考时间。起始时间为2000年1月1日中午12点整
static DateTime J2000Epoch(2000, 1, 1, 12.00);// Transform that takes us from a J2000 ECI reference frame
// to an ECEF reference frame (i.e. MapNode)
// 定义 从J2000ECI坐标系转到ECEF坐标系(地固系)的 转换矩阵
class J2000ToECEFTransform : public osg::MatrixTransform
{
public:void setDateTime(const DateTime& dt){osg::Matrix matrix = createMatrix(dt);setMatrix(matrix);// 设置给父类}// 根据传入UTC时间,定义当前矩阵static osg::Matrix createMatrix(const DateTime& dt){// Earth's rotation rate: International Astronomical Union (IAU) GRS 67// 地球自转率,即角速度const double IAU_EARTH_ANGULAR_VELOCITY = 7292115.1467e-11; // (rad/sec)// 都转化为儒略日时间,double类型double secondsElapsed = (double)(dt.asTimeStamp() - J2000Epoch.asTimeStamp());const double rotation = IAU_EARTH_ANGULAR_VELOCITY * secondsElapsed;// 时间*角速度,得到旋转角度osg::Matrix matrix;matrix.makeRotate(rotation, 0, 0, 1);return matrix;}
};// Code to read TLE track data files from https://celestrak.com/NORAD
// 从TLE文件读取跟踪数据的代码
struct ECILocation
{DateTime timestamp;     // point time 运行在某个点的时间Angle incl;             // inclination 倾角Angle raan;             // right ascencion of ascending node 上升节点的右上升Distance alt;           // altitude    高度osg::Vec3d eci;         // ECI coordinate ECI坐标系下的点位置osg::Vec3d ecef;        // ECEF coordinate  ECEF坐标系下的点位置void computeECIAndECEF()// 分别计算两个坐标系{// 根据从文件中获取的 raan incl alt ,计算ecieci =osg::Quat(raan.as(Units::RADIANS), osg::Vec3d(0, 0, 1)) *osg::Quat(incl.as(Units::RADIANS), osg::Vec3d(1, 0, 0)) *osg::Vec3d(alt.as(Units::METERS), 0, 0);// 根据时间获取转换矩阵osg::Matrix eci2ecef = J2000ToECEFTransform::createMatrix(timestamp);ecef = eci * eci2ecef;// 计算出ecef下坐标点}
};// ECI跟踪
struct ECITrack : public std::vector<ECILocation>
{// interpolate points for a smoother track// 差值,使得点连线更圆滑.此算法有问题!!!绘制效果特别差!!!void tessellate(){// 声明一个对象ECITrack newTrack;for(unsigned k=0; k<size()-1; ++k){// 每两个点之间,添加10个点??for(float t=0; t<1.0f; t+=0.1){const ECILocation& p0 = at(k);const ECILocation& p1 = at(k+1);newTrack.push_back(ECILocation());ECILocation& loc = newTrack.back();loc.timestamp = DateTime(p0.timestamp.asTimeStamp() + (p1.timestamp.asTimeStamp()-p0.timestamp.asTimeStamp())*t);loc.raan.set(p0.raan.as(Units::RADIANS) + (p1.raan.as(Units::RADIANS)-p0.raan.as(Units::RADIANS))*t, Units::RADIANS);loc.incl.set(p0.incl.as(Units::RADIANS) + (p1.incl.as(Units::RADIANS)-p0.incl.as(Units::RADIANS))*t, Units::RADIANS);loc.alt = p0.alt;loc.computeECIAndECEF();}}swap(newTrack);// c1.swap(c2); 交换两个变量的值}
};// 读取TLE文件
class TLEReader
{
public:// https://celestrak.com/NORAD/documentation/tle-fmt.phpbool read(const std::string& filename, ECITrack& track) const{std::cout << "filename = " << filename << std::endl;std::ifstream fin(filename.c_str());int i = 0;// 记录读取多少两行根数while(!fin.eof()){std::string line1, line2;// 每次从fin中读取两条数据std::getline(fin, line1);std::getline(fin, line2);if (line1.empty() || line2.empty()) {std::cout << "line1 or line2 is empty" << std::endl;break;}  ++i;// 完成一次读取,则自增一次// 新增一个 ECILocation,即一个卫星的参数,并放到列表中track.push_back(ECILocation());ECILocation& loc = track.back();// 从列表中取出刚放入的卫星数据// 将卫星数据,计算后,填充 loc的各个参数:timestamp incl raan alt eci ecef// read timestampint year2digit = osgEarth::as<int>(line1.substr(18, 2), 99);int year = year2digit > 50? 1900+year2digit : 2000+year2digit;double dayOfYear = osgEarth::as<double>(line1.substr(20, 12), 0);loc.timestamp = DateTime(year, dayOfYear);// read ra/declloc.incl.set(osgEarth::as<double>(line2.substr(8,8),0), Units::DEGREES);loc.raan.set(osgEarth::as<double>(line2.substr(17,8),0), Units::DEGREES);loc.alt.set(6371 + 715, Units::KILOMETERS);loc.computeECIAndECEF();}std::cout << "共解析数据条数:" << i << std::endl;OE_INFO << "Read " << track.size() << " track points" << std::endl;return true;}
};// If the "global" coordinate system is ECI, you can put this transform
// under the MapNode (in ECEF space) to "revert" to that global ECI frame.
// Useful if you want to put ECI-space data under the MapNode.
// 如果“全局”坐标系是ECI,则可以将ECI坐标系变换放在MapNode(在ECEF空间中)下,
// 然后再“还原”到该全局ECI坐标系。
// 如果要将ECI空间数据放在MapNode下,则非常有用。
class ECIReferenceFrame : public osg::Group
{
public:ECIReferenceFrame(){// 关闭光照Lighting::set(getOrCreateStateSet(), osg::StateAttribute::OFF);}// 遍历void traverse(osg::NodeVisitor& nv){osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);if (cv){const osg::Camera* cam = cv->getRenderStage()->getCamera();// 添加模型视口矩阵cv->pushModelViewMatrix(new osg::RefMatrix(cam->getViewMatrix()), osg::Transform::ABSOLUTE_RF);osg::Group::traverse(nv);cv->popModelViewMatrix();// 再移除该矩阵}else osg::Group::traverse(nv);}
};// Loads up an ECITrack for display as a series of points.
// 加载ECITrack以显示为一系列线/点。对LineDrawble类不太熟悉
class ECITrackDrawable : public LineDrawable //public PointDrawable
{
public:ECITrackDrawable() : LineDrawable(GL_LINE_STRIP){Lighting::set(getOrCreateStateSet(), 0);//setPointSmooth(true);//setPointSize(4.0f);}// 通过 滑块 设置时间void setDateTime(const DateTime& dt){// getVertexAttribArray(); 属于爷爷类的方法// 获取到当前顶点的时间osg::FloatArray* times = dynamic_cast<osg::FloatArray*>(getVertexAttribArray(6));unsigned i;for (i = 0; i < getNumVerts(); ++i){if (dt.asTimeStamp() < getVertexAttrib(times, i))// getVertexAttrib() 父类方法break;}setCount(i);}// 加载 track 列表的所有数据void load(const ECITrack& track, bool drawECEF){osg::FloatArray* times = new osg::FloatArray();times->setBinding(osg::Array::BIND_PER_VERTEX);setVertexAttribArray(6, times);// 调用爷爷类的方法osg::Vec4f HSLA;// 颜色变量Color color;// 循环处理每一个卫星for(unsigned i=0; i<track.size(); ++i){const ECILocation& loc = track[i];pushVertex(drawECEF? loc.ecef : loc.eci);// 根据坐标系类型,决定用哪个点进行绘制pushVertexAttrib(times, (float)loc.timestamp.asTimeStamp());// 添加属性// simple color ramp 随机生成颜色HSLA.set((float)i/(float)(track.size()-1), 1.0f, 1.0f, 1.0f);color.fromHSL(HSLA);setColor(i, color);      }finish();// 更新}
};// 创建J2000ECI坐标系的坐标轴
osg::Node* createECIAxes()
{// 坐标轴长度const float R = 10e6;LineDrawable* d = new LineDrawable(GL_LINES);d->allocate(6);// X轴d->setVertex(0, osg::Vec3(0,0,0));d->setColor(0, osg::Vec4(1,0,0,1));d->setVertex(1, osg::Vec3(R,0,0));d->setColor(1, osg::Vec4(1,0,0,1));// Y轴d->setVertex(2, osg::Vec3(0,0,0));d->setColor(2, osg::Vec4(0,1,0,1));d->setVertex(3, osg::Vec3(0,R,0));d->setColor(3, osg::Vec4(0,1,0,1));// Z轴d->setVertex(4, osg::Vec3(0,0,0));d->setColor(4, osg::Vec4(0,0,1,1));d->setVertex(5, osg::Vec3(0,0,R));d->setColor(5, osg::Vec4(0,0,1,1));// 坐标轴宽度d->setLineWidth(10);return d;
}// Application-wide data and control structure
// 应用程序范围的数据和控制结构
struct App
{DateTime start, end;   // 时间HSliderControl* time;  // 时间滑块LabelControl* timeLabel;// 时间标签SkyNode* sky;         // 深空节点J2000ToECEFTransform* ecef;  // 坐标系转换矩阵osg::Group* eci;ECITrackDrawable* trackDrawable;// ECI跟踪绘制ECITrack track;             // J2000坐标系下卫星数据列表App() {trackDrawable = 0L;start = J2000Epoch;// 设置为J2000坐标系的开始时间end = start + 24.0; // 初始设置时,必须保证start和end保持间距,且start < end}// 通过滑块设置时间void setTime(){// 获取最新时间DateTime newTime(time->getValue());if (sky)sky->setDateTime(newTime);// 如果是地固系,则需要根据时间旋转坐标系if (ecef)ecef->setDateTime(newTime);if (trackDrawable)trackDrawable->setDateTime(newTime);// 设置时间的显示timeLabel->setText(newTime.asRFC1123());}
};// 将app.setTime 设置到这个宏定义中,之前其他例子有涉及到此宏定义的用法
OE_UI_HANDLER(setTime);int
main(int argc, char** argv)
{osg::ArgumentParser arguments(&argc,argv);if ( arguments.read("--help") )return usage(argv[0], "");// 声明app变量App app;// Read in an optiona TLE track data file// 读取TLE文件获取数据std::string tlefile;if (arguments.read("--tle", tlefile)){// 读取并解析卫星数据,到app.track列表中TLEReader().read(tlefile, app.track);if (!app.track.empty()){int maxPoints;if (arguments.read("--maxpoints", maxPoints) && app.track.size() > maxPoints)app.track.resize(maxPoints);// 是否需要调整vector空间大小if (arguments.read("--tessellate")) {// 是否开启差值功能。最好不要开启,算法有点问题app.track.tessellate();std::cout << "开启差值功能 tessellate" << std::endl;}app.start = app.track.front().timestamp;// 设置开始时间app.end   = app.track.back().timestamp; // 设置结束时间}}osgViewer::Viewer viewer(arguments);viewer.setCameraManipulator( new EarthManipulator(arguments) );bool drawECEF = arguments.read("--ecef");ui::VBox* container = new ui::VBox();container->setChildSpacing(3);// 根据是否为ECI坐标系,更改标题if (drawECEF) {container->addControl(new ui::LabelControl("ECEF COORDINATE SYSTEM EXAMPLE", Color::Red));}else {container->addControl(new ui::LabelControl("ECI COORDINATE SYSTEM EXAMPLE", Color::Yellow));}// UI control to modify the time of day.ui::HBox* h = container->addControl(new ui::HBox());h->addControl(new ui::LabelControl("Time:"));app.time = h->addControl(new HSliderControl(app.start.asTimeStamp(), app.end.asTimeStamp(), app.end.asTimeStamp(),new setTime(app)));app.time->setWidth(500);// 500宽度太大了,修改小一些,也没有起作用,注释带此行,也没啥变化。app.timeLabel = container->addControl(new LabelControl());// Load an earth file  osg::Node* earth = MapNodeHelper().load(arguments, &viewer, container);if (earth){// New scene graph rootosg::Group* root = new osg::Group();// First create a Sky which we will place in the (default) ECI frame.SkyOptions skyOptions;if (drawECEF) {// 不太确定这里的 skyOptions.coordinateSystem() 设置为 COORDSYS_ECEF,是否正确,但运行效果没问题。skyOptions.coordinateSystem() = SkyOptions::COORDSYS_ECEF;// 地固坐标系std::cout << "ecef 坐标系" << std::endl;}else {skyOptions.coordinateSystem() = SkyOptions::COORDSYS_ECI;// 深空坐标系std::cout << "eci 坐标系" << std::endl;}app.sky = SkyNode::create(MapNode::get(earth));// 创建深空节点app.sky->attach(&viewer);app.sky->getSunLight()->setAmbient(osg::Vec4(0.5,0.5,0.5,1.0));// 环境光root->addChild(app.sky);// A special transform takes us from the ECI into an ECEF frame// based on the current date and time.// The earth (MapNode) lives here since it is ECEF.app.ecef = new J2000ToECEFTransform();// 坐标系转换app.sky->addChild(app.ecef);app.ecef->addChild(earth);// 将地球节点加入到坐标转换矩阵中// This group holds data in the ECI frame.app.eci = new ECIReferenceFrame();app.eci->addChild(createECIAxes());// 添加坐标轴MapNode::get(earth)->addChild(app.eci);// Track dataif (!app.track.empty()){app.trackDrawable = new ECITrackDrawable();if (drawECEF)// 地固系,绘制连线{app.trackDrawable->load(app.track, false);// 如果设置为true,则会出现很多杂乱的线MapNode::get(earth)->addChild(app.trackDrawable);std::cout << "ecef 坐标系" << std::endl;}else // J2000坐标系,不绘制连线{app.trackDrawable->load(app.track, false);// 如果设置为true,则会出现很多杂乱的线app.eci->addChild(app.trackDrawable);std::cout << "eci 坐标系" << std::endl;}}viewer.realize();app.time->setWidth(viewer.getCamera()->getViewport()->width()-40);app.setTime();viewer.setSceneData(root);viewer.run();}else{return usage(argv[0], "Bad earth file");}return 0;
}

osgEarth示例分析——osgearth_eci相关推荐

  1. osgEarth示例分析——osgearth_annotation

    前言 本章为osgearth_annotation示例分析,示例中采用osgEarth提供的类,绘制标签.线.billboard.遮盖图.墙等内容. 运行时,在生成的可执行路径下,打开命令框,输入: ...

  2. osgEarth示例分析——osgearth_skyview

    前言 本示例分析osgearth操作深空场景,或者是银河系场景,可以想象人拿着相机站在地球表面上观看天空/银河系的场景. 重点是相机操作器的使用. 在命令框输入执行程序,在data路径下有加载的图,且 ...

  3. osgEarth示例分析——osgearth_manip

    前言 本示例主要演示osgEarth的事件处理的用法,内容比较多,这部分功能也很重要. 输入命令依然采用china-simple.earth的示例,加上了模型,但是模型并没有看到,可能是因为模型没有放 ...

  4. osgEarth示例分析——osgearth_elevation

    前言 osgearth_elevation示例,展示了如何通过点击地球获取不同定义下的高程数据.包括:MSL高程.HAE高程.EGM96高程.点击按钮,可以移除高程图层. MSL高程:是mean se ...

  5. osgEarth示例分析——osgearth_graticule

    前言 本示例最具有借鉴的功能:绘制网格.网格上的文字显示.拾取地球的坐标.在地球网格示例中,可以设置4种网格.执行命令如下: // --geodetic osgearth_graticuled.exe ...

  6. osgEarth示例分析——osgearth_srstest

    前言 osgearth_srstest示例,主要涉及到两个坐标系转换,wgs84→egm96  wgs84→plate-carre wgs84:World Geodetic System 1984,是 ...

  7. osgEarth示例分析——osgearth_terrainprofile

    前言 osgearth_terrainprofile示例,涉及到一个新的类 TerrainProfileCalculator(地形轮廓计算器类),用来计算两个点连线之间的地形数据.左下角会根据点击的起 ...

  8. osgEarth示例分析——osgearth_features

    前言 osgearth_features示例,主要演示如何通过代码方式加载shp文件,并设置其样式.在执行时,通过不同 的命令,得到不一样的效果. cmd执行命令: // rasterize 光栅化, ...

  9. osgEarth示例分析——osgearth_los

    前言 osgearth_los示例,创建了模型动画路径.透视的用法.透视的顾名思义:两个点连线,从A到B,视线不被遮挡,则绘制绿色,视线被遮挡的部分,则设置为红色.此示例,加载earth文件时,需要加 ...

最新文章

  1. R语言使用car包的scatter3d函数可视化可以交互旋转的3D散点图(Spinning 3D scatter plot by the scatter3d() function of car)
  2. 【MFC】BitBlt详解
  3. css里calculate,calc() ---一个会计算的css属性
  4. discuz X3全局变量$_G
  5. 4152. [AMPPZ2014]The Captain(稠密图最短路)
  6. win10共享打印错误0x0000006_Win10连接共享打印机提示0x80070035错误的解决办法
  7. zeros什么意思_ma=zeros(n);是什么意思'
  8. 【转载】SQL Server 批量插入数据的两种方法
  9. Python 之详解深拷贝和浅拷贝
  10. LeetCode 57. 插入区间(python、c++)
  11. win11如何创建访客账户 windows11创建访客账户的设置方法
  12. 零基础搭建微信小程序商城系统
  13. macd底背离的python_python量化交易是否可以有策略有效识别MACD/SKDJ底背离和顶背离?...
  14. html中颜色打字机效果,基于Css3和JQuery实现打字机效果
  15. centos 基础镜像中安装失败,提示:Error: Failed to download metadata for repo ‘AppStream‘: Cannot prepare internal
  16. 定制婚礼APP开发功能
  17. 集成电路技术——如何制造芯片(1)
  18. 【挨踢人物传】小侠唐在飞:“剑胆琴心,成就网络大侠”(第四期)
  19. Elementui删除表格最后一页全部数据后跳到前一页的骚气写法
  20. 申请美国计算机科学博士,美国计算机博士申请案例分析

热门文章

  1. 图像处理:直方图规定化
  2. 【专题】经典DP问题(上)
  3. FT232H如何使用jtag接口
  4. [生存志] 第89节 太公阴符天人之道
  5. sso单点登录的PHP实现(Laravel框架)
  6. python3安装PIL
  7. canvas rotate() 中心旋转的实际运用
  8. arcgis栅格缺值填补
  9. 在Windows 10上安装GCC
  10. java使用knn实现mnist_java使用knn实现mnist - 百度学术