特别声明,本人对于天体物理学、轨道动力学完全是门外汉,不是一个学科方向,本人仅从代码角度以及STK提供的代码和资料作为参考进行分析,欢迎专业人士指正。若不喜,请勿喷!
继续以STK提供的VC++代码实例Events项目为例分析。
加入卫星的代码如下(Helper源文件中,自己增加了注释):
void CObjectModelHelper::CreateSatellite(STKObjects::IAgStkObjectRootPtr pRoot) { static int iNext = 0; char buf[255]; double aporad, perrad; _bstr_t bstrName; IAgOrbitStateClassicalPtr pClassical; IAgStkObjectPtr pNewObj; // STK Object IAgSatellitePtr pSat; // STK卫星对象 IAgVePropagatorTwoBodyPtr pTwoBodyProp; // 二体轨道预报器 IAgClassicalSizeShapeRadiusPtr pRadius; // Build the satellite name itoa(iNext++, buf, 10); bstrName = "Satellite"; bstrName += buf; // 确保首先执行了 NewScenario 操作 ASSERT(pRoot != NULL); // Add a new instance of the Satellite to the scenario // eSatellite在文件agstkobjects.tlh中定义,表示STK中的卫星对象 //enum AgESTKObjectType //{ // ... = 17, // eSatellite = 18, // ... = 19, //}; // 注:本例中的绝大部分STK对象都是在agstkobjects.tlh/.tli文件中声明的(原型) // IAgStkObjectRootPtr pRoot,此为STK应用中所有对象的ROOT(根) // IAgStkObjectPtr IAgStkObjectRoot::GetCurrentScenario(),通过根对象得到当前的场景对象 // IAgStkObjectCollectionPtr IAgStkObject::GetChildren(),通过当前场景对象得到对象集合(object collection) // IAgStkObjectPtr IAgStkObjectCollection::New(enum AgESTKObjectType EClassType, _bstr_t InstName),通过对象集合新建一个对象 // 执行对象创建的是接口 IAgStkObjectCollection(即,场景的对象集合) pNewObj = pRoot->GetCurrentScenario()->GetChildren()->New(eSatellite, bstrName); // 下面的赋值应当在确定新建对象确实与等号左侧对象为同一类型 pSat = pNewObj; // Set the propagator type // 设置轨道预报器:ePropagatorTwoBody,STK给的对外接口中定义了 15 种轨道预报器 pSat->SetPropagatorType( ePropagatorTwoBody ); // 赋值,pSat->Propagator(IAgVePropagatorPtr Propagator;) // 可以将 IAgVePropagatorPtr 类型的接口指针赋值给 IAgVePropagatorTwoBodyPtr 类型的接口指针? // 类似于前面的赋值语句:pSat = pNewObj; pTwoBodyProp = pSat->Propagator; // Get the initial state's classical representation // IAgOrbitStateClassical pClassical;,经典轨道参数? // IAgVeInitialStatePtr InitialState;,轨道初始状态 // STKUtil::IAgOrbitStatePtr Representation;,轨道状态表示? // IAgOrbitStatePtr IAgOrbitState::ConvertTo(enum AgEOrbitStateType Type),转换为经典轨道参数状态? pClassical = pTwoBodyProp->InitialState->Representation->ConvertTo(eOrbitStateClassical); // 以大小来描述轨道形状,其他的还有:轨道高度、半长轴、轨道周期、平均运动(MeanMotion)等 pClassical->SizeShapeType = eSizeShapeRadius; // IAgClassicalSizeShapePtr SizeShape; pRadius = pClassical->SizeShape ; // Set up the orbit's initial state // 设置卫星的轨道初始状态 // 顾名思义:aporad,远地点;perrad,近地点。根据实际值,变量应当命名为apogee和perigee更合适? // 按理 aporad 需要大于 perrad,随机函数如何保证的呢?或许无所谓,系统会自动处理? aporad = rand() % 12000; // 相当于最大远地点为 12000km perrad = rand() % 12000; pRadius->ApogeeRadius = 6356.75231424 + aporad; pRadius->PerigeeRadius = 6356.75231424 + perrad; // 轨道倾角 pClassical->Orientation->Inclination = rand() % 180; // Assign the orbit state // 设置轨道初始状态,就上面的计算来看,仅指定了三个参数:近地点、远地点、轨道倾角; // 根据近地点和远地点可以计算轨道离心率,轨道六根数还差三个?其他三个参数都是默认值 // 其他三个参数为:RAAN,近日点幅角,指定历元的平近点角 // 其他的参数都默认了,所以在界面上不停增加卫星,会发现卫星的 RAAN 都在一个点上, // 视觉上初始状态时卫星可以在空间连成一条线! pTwoBodyProp->InitialState->Representation->Assign( pClassical ); // Build the satellite's orbit // 执行轨道计算(预报器计算,并显示) pTwoBodyProp->Propagate(); } |
流程大致分析:
- 在当前场景中首先增加一个卫星对象(注:此时只是增加了一个卫星对象,在没有指定卫星的轨道预报器(Orbit Propagator)并根据轨道预报器类型初始化轨道参数之前,三维场景并不会绘制卫星状态,也绘制不出来,这与其他类型的对象不同,也可以通过STK程序操作验证)。
- 获取当前场景,获取当前场景的对象为pRoot(个人理解其代表了一个独立运行的STK应用程序/进程)。
- 由场景对象获取当前场景的对象集合(IAgStkObjectCollection),这个集合应该是包含了场景中的所有对象,不出例外的话是一个树型结构。
- 由(场景)对象集合创建一个新的对象,即本例中new一个卫星对象。
- 将新建的对象(StkObject)赋值给卫星对象(Satellite),以便进行后续针对卫星类型对象的初始化操作。
- 指定卫星的轨道预报器类型,本例中为二体轨道预报器(Two Body),后面接着需要指定的轨道预报器进行不同的初始状态设置。
- 将当前预报器的初始状态表示转换为经典轨道状态表示,这是针对二体轨道预报器的操作,不知道其他类型的轨道预报器该如何操作,下一步继续探索!
- 对经典轨道状态初始化,包括近地点、远地点、轨道倾角,其他的轨道参数保持了默认值。
- 将设置后的轨道数据赋值给当前卫星对象的轨道预报器(…->Assign(pClassical))。
- 由轨道预报器执行轨道计算。(计算完毕后,场景会自动更新)
下面通过指定二体轨道预报器的其他轨道参数,以升交点赤经(RAAN)为例探索一下……
示例代码中是通过如下的赋值初始化轨道参数的:
pTwoBodyProp->InitialState->Representation->Assign( pClassical ); |
顺腾摸瓜:pTwoBodyProp(即,IAgVePropagatorTwoBody)
IAgVePropagatorTwoBodyPtr pTwoBodyProp; // pTwoBodyProp 是IAgVePropagatorTwoBody类型 IAgVePropagatorTwoBody : IAgVePropagator // IAgVePropagator : IUnknown { // 属性 _variant_t StartTime; _variant_t StopTime; double Step; IAgVeInitialStatePtr InitialState; IAgVeHPOPForceModelPtr ForceModel; IAgVeIntegratorPtr Integrator; IAgVeCovariancePtr Covariance; // 方法 HRESULT Propagate ( ); _variant_t GetStartTime ( ); void PutStartTime (const _variant_t & pVal ); _variant_t GetStopTime ( ); void PutStopTime (const _variant_t & pVal ); double GetStep ( ); void PutStep (double pVal ); IAgVeInitialStatePtr GetInitialState ( ); IAgVeHPOPForceModelPtr GetForceModel ( ); IAgVeIntegratorPtr GetIntegrator ( ); IAgVeCovariancePtr GetCovariance ( ); } |
可以看出轨道预报器的基类(IAgVePropagator)对外提供的设置接口(Put接口)包括:
- 轨道计算(Propagate());
- 设置开始时间(PutStartTime());
- 结束时间(PutStopTime());
- 计算步长(PutStep())。
IAgVePropagatorTwoBody : IAgVePropagator { // 属性(忽略了继承自父类的相关属性) VARIANT_BOOL UseScenarioAnalysisTime; // 是否使用场景中的分析时间,通常均为true // 方法(忽略了父类已定义/提供的方法) VARIANT_BOOL GetUseScenarioAnalysisTime ( ); void PutUseScenarioAnalysisTime (VARIANT_BOOL pRetVal ); }; |
可以看出,二体轨道预报器只是增加了一个‘是否使用场景的分析时间’的属性,按理其他轨道预报器也都有这个参数,为啥不定义到父类?
顺腾摸瓜:pTwoBodyProp->InitialState(即,IAgVeInitialState)
IAgVeInitialState : IUnknown { // 属性 _variant_t Epoch; STKUtil::IAgOrbitStatePtr Representation; enum AgEVePropagationFrame PropagationFrame; SAFEARRAY * SupportedPropagationFrames; // 接口方法 _variant_t GetEpoch ( ); void PutEpoch (const _variant_t & pVal ); STKUtil::IAgOrbitStatePtr GetRepresentation ( ); enum AgEVePropagationFrame GetPropagationFrame ( ); void PutPropagationFrame (enum AgEVePropagationFrame pVal ); SAFEARRAY * GetSupportedPropagationFrames ( ); }; |
接口类IAgVeInitialState有一个Epoch属性,应当是跟发射时间有关,另外一个重要属性是STKUtil::IAgOrbitStatePtr Representation,根据名称猜测是轨道状态,继续深挖。
顺藤摸瓜:pTwoBodyProp->InitialState->Representation(即,IAgOrbitState)
IAgOrbitState : IUnknown // 轨道状态表示 { // 属性 enum AgEOrbitStateType OrbitStateType; _bstr_t CentralBodyName; _variant_t Epoch; // 接口方法 IAgOrbitStatePtr ConvertTo (enum AgEOrbitStateType Type ); // 轨道状态类型预定义了6种 // eOrbitStateCartesian = 0, -- 笛卡尔 // eOrbitStateClassical = 1, -- 经典 // eOrbitStateEquinoctial = 2, -- 天球赤道 // eOrbitStateDelaunay = 3, -- 洛尼? // eOrbitStateSpherical = 4, -- 球面坐标系 // eOrbitStateMixedSpherical = 5, -- 混合球面 // eOrbitStateGeodetic = 6 -- 大地测量 enum AgEOrbitStateType GetOrbitStateType ( ); // Assign()方法使得可以设置轨道初始状态 HRESULT Assign (struct IAgOrbitState * pOrbitState ); HRESULT AssignClassical (// 经典表示:半长轴、偏析率、倾角、近地点幅角、RAAN enum AgECoordinateSystem ECoordinateSystem, double SemiMajorAxis, Eccentricity, Inclination, ArgOfPerigee, RAAN, MeanAnomaly ); HRESULT AssignCartesian (// 笛卡尔坐标表示法 enum AgECoordinateSystem ECoordinateSystem, double XPosition, YPosition, ZPosition, XVelocity, YVelocity, ZVelocity ); HRESULT AssignGeodetic (// 大地测量表示法:经纬高、经纬高速度 enum AgECoordinateSystem ECoordinateSystem, double Latitude, Longitude, Altitude, LatitudeRate, LongitudeRate, AltitudeRate ); HRESULT AssignEquinoctial (// 天球赤道表示法? enum AgECoordinateSystem ECoordinateSystem, double SemiMajorAxis, H, K, P, Q, MeanLon, enum AgEEquinoctialFormulation EquinoctialFormulation ); HRESULT AssignMixedSpherical (// 混合球面(坐标)表示法? enum AgECoordinateSystem ECoordinateSystem, double Latitude, Longitude, Altitude, HorFlightPathAngle, FlightPathAzimuth, Velocity ); HRESULT AssignSpherical (// 球面(坐标)表示法? enum AgECoordinateSystem ECoordinateSystem, double Latitude, Longitude, Radius, HorFlightPathAngle, FlightPathAzimuth, Velocity ); _bstr_t GetCentralBodyName ( ); _variant_t GetEpoch ( ); void PutEpoch (const _variant_t & pRetVal ); }; |
故,想要指定升交点赤经(RAAN),需要指定轨道坐标系为经典(Classical)并需要找到设置RAAN的接口方法,并通过方法AssignClassical()来完成设置(实际是通过Assign()方法来完成的),Assign()方法接受的参数为IAgOrbitState,则可以通过设置输入参数中的RAAN来完成设置。
顺腾摸瓜:IAgOrbitStateClassical(即,局部变量pClassical,也是Assig()方法的输入变量))
IAgOrbitStateClassical : IAgOrbitState { // IAgOrbitState相关属性和方法参见上一条摸瓜,本条忽略与父类的重复内容 // 属性 // // 坐标系类型,例如J2000、地固系、地惯系等,共定义了22种 enum AgECoordinateSystem CoordinateSystemType; // 坐标系(参数) IAgOrbitStateCoordinateSystemPtr CoordinateSystem; // 外形尺寸(size shape)类型,包括:高度、周期、半径、半长轴、平均运动等。 enum AgEClassicalSizeShape SizeShapeType; // 外形尺寸(参数) IAgClassicalSizeShapePtr SizeShape; // 方位/定位,包括:轨道倾角(Inclination)、近地点幅角(ArgOfPerigee)、 // 升交点类型(AscNodeType)及升交点(AscNode),说明升交点赤经不止一种类型 // 不能直接设置RAAN IAgClassicalOrientationPtr Orientation; // 位置类型,包括:升交角距(ArgumentOfLatitude)、偏近点角(EccentricAnomaly)、 // 平近点角(MeanAnomaly)、过升交点时间(TimePastAN)、 // 过近地点时间(TimePastPerigee)、真近点角(TrueAnomaly) enum AgEClassicalLocation LocationType; // 位置(参数) IAgClassicalLocationPtr Location; SAFEARRAY * SupportedCoordinateSystemTypes; // 接口方法 enum AgECoordinateSystem GetCoordinateSystemType ( ); void PutCoordinateSystemType (enum AgECoordinateSystem pVal ); IAgOrbitStateCoordinateSystemPtr GetCoordinateSystem ( ); enum AgEClassicalSizeShape GetSizeShapeType ( ); void PutSizeShapeType (enum AgEClassicalSizeShape pVal ); IAgClassicalSizeShapePtr GetSizeShape ( ); IAgClassicalOrientationPtr GetOrientation ( ); enum AgEClassicalLocation GetLocationType ( ); void PutLocationType (enum AgEClassicalLocation pVal ); IAgClassicalLocationPtr GetLocation ( ); SAFEARRAY * GetSupportedCoordinateSystemTypes ( ); }; |
最后定位到接口类IAgOrbitStateClassical的属性Orientation,继续摸瓜。
IAgClassicalOrientation : IUnknown { // 属性 double Inclination; // 轨道倾角 double ArgOfPerigee; // 近地点幅角 enum AgEOrientationAscNode AscNodeType; IAgOrientationAscNodePtr AscNode; // 接口方法 double GetInclination ( ); void PutInclination (double pVal ); double GetArgOfPerigee ( ); void PutArgOfPerigee (double pVal ); enum AgEOrientationAscNode GetAscNodeType ( ); void PutAscNodeType (enum AgEOrientationAscNode pVal ); IAgOrientationAscNodePtr GetAscNode ( ); }; |
只发现:轨道倾角(Inclination)、近地点幅角(ArgOfPerigee),升交点类型(AscNodeType)和升交点(AscNode),没有直接设置RAAN的地方,还得继续找!关键是,还没有设置AscNode的接口,只有GetAscNode,没有SetAscNode。好在是VC++编程直接都用的是IUnknown接口,应该是可以直接赋值。
AscNodeType和AscNode
enum AgEOrientationAscNode {// 升交点有两种表示方法,一种是LAN,另一种是RAAN eAscNodeUnknown = -1, eAscNodeLAN = 0, eAscNodeRAAN = 1 }; IAgOrientationAscNode : IUnknown {}; // 啥也没定义,只是为了下面的两个家伙有同一个父类而已 IAgOrientationAscNodeLAN : IAgOrientationAscNode { // 属性 double Value; // 方法 double GetValue ( ); void PutValue (double pVal ); }; IAgOrientationAscNodeRAAN : IAgOrientationAscNode { // 属性 double Value; // 方法 double GetValue ( ); void PutValue (double pVal ); }; |
终于定位!定制代码如下(下面仅展示了创建卫星函数里增加的局部代码,其他内容参见STK示例项目或本回前面示例代码):
// … // 轨道倾角 pClassical->Orientation->Inclination = rand() % 180; // 在插入第二颗卫星(编号1)时,指定其升交点赤经为例如30° if (iNext == 2) { pClassical->Orientation->AscNodeType = eAscNodeRAAN; IAgOrientationAscNodeRAANPtr ascNode; ascNode = pClassical->Orientation->AscNode; ascNode->Value = 30.0; } // Assign the orbit state // … |
运行效果如下(注:实例项目的界面经调整,并汉化),可以看到编号为1的卫星(紫色标记)的RAAN确实与众不同(默认添加的卫星初始时均在空间一条直线连线上,参见前面的代码注释)。
注:以上所有的信息均源于STK安装后默认自带的VC++编码的支持库,参见安装目录下‘CppInclude’文件夹,主要参考文件为*.tlh, *.tli。
关于以其他方式创建卫星,可通过本回的方法进行探索,STK应该是开放了所有的接口(未经本人证实),那个agstkobjects.tlh文件,有约8万行!