Orekit库依赖于一些外部数据来进行物理模型计算。典型的数据包括地球定向参数和闰秒历史,这些数据由IERS提供,或者由JPL或IMCCE提供的行星星历。这些数据可以以特定格式的文本或二进制文件存储,Orekit知道如何找到并读取这些文件,也可以存储在用户应用程序可以连接的数据库中。这些数据也可以完全嵌入在构建在Orekit之上的应用程序中。
一组一致的数据被称为数据上下文。
常规的独立应用程序只会使用一个数据上下文,即一个EOP集合,一个JPL星历集合,一个闰秒历史...但是对于某些需求来说,只有一个数据上下文是不够的,例如:
Orekit必须进行适当的配置,以为每个任务选择正确的上下文。
只有几十千字节且可以假定永远不会更改的数据,例如预定义的岁差-章动模型的常数,已经嵌入在核心库中,它们不与任何数据上下文相关联。通过在代码本身中设置常量来定义小而简单的数据集。例如,地球时间和国际原子时间之间的32.184秒偏移量就是这种情况。大型或复杂的固定数据通过将相应的资源文件原样复制到编译后的jar文件中的assets目录下来嵌入。例如,这是IAU-2000岁差-章动模型表的情况。对于这些固定数据集,无需进行任何配置,因为它们已经嵌入在库中,所以用户可以完全忽略它们。
其他类型的数据集对应于庞大或变化的数据集。从长远来看,这些数据集无法实际上嵌入到特定版本的库中。这样的数据集的典型示例是地球定向参数,这些参数对于精确的坐标转换是必需的。另一个例子是行星星历。IERS定期发布新的地球定向参数文件,涵盖新的时间范围,而行星星历则代表几兆字节大小的文件。这些数据集必须由用户通过数据上下文提供,并且Orekit必须在运行时配置以找到并加载它们。
下面的类图展示了Orekit中如何处理上下文。
顶层接口是DataContext
。它收集了Orekit用于创建物理模型所需的所有工厂的链接。有一个用于TimeScales
的工厂,它需要UTC-TAI历史数据来创建UTC
时间尺度,以及需要EOP数据来创建UT1
时间尺度。有一个用于坐标系的工厂,它需要地球坐标系的EOP数据。有一个用于天体的工厂,它需要所有天体的行星星历数据。
DataContext
接口有一个实现LazyLoadedDataContext
,它在首次访问时从外部资源(通常是文件)上动态加载数据,并依赖于DataProvidersManager
来查找如何定位和解析这些资源。
用户可以提供自己的DataContext
实现,例如如果所有数据存储在特定任务的数据库中,这将不适用于基于资源或文件的LazyLoadedDataContext
和DataProvidersManager
。
提供DataContext
的实现包括创建自定义工厂并设置一个类以获得所有工厂的单一访问点。预定义的LazyLoadedDataContext
例如使用LazyLoadedTimeScales
、LazyLoadedFrames
、LazyLoadedEOP
和LazyLoadedCelestialBodies
,它们都依赖于相同的DataProvidersManager
来查找和加载数据。在这个实现中,一些工厂依赖于其他工厂,因为一个数据上下文必须提供一致的数据。例如,EOP数据既被LazyLoadedFrames
(用于地球坐标系)使用,也被LazyLoadedTimeScales
(用于UT1
)使用。而LazyLoadedFrames
还依赖于CelestialBodies
接口(用于ICRF
)。
各个工厂中有一些静态方法(例如TimeScales.of(...)
、Frames.of(...)
),它们创建带有预加载常量数据的工厂。这些方法对于需要实现自己的数据上下文的用户非常有用。
需要使用特定数据上下文的应用程序必须指定它。如果使用的是直接引用依赖于上下文的物理模型(例如ITRF
地球坐标系),则可以从与所需上下文关联的Frames
工厂中获取模型的引用:
Frame itrf = myDataContext.getFrames().getITRF();
有时,引用是隐式的,并且在Orekit代码中隐藏。在这种情况下,将使用默认上下文。例如,仅使用GalileoOrbitalElements
创建Galileo专用的传播器的构建器将自动从默认上下文中检索EME2000和ITRF坐标系。如果默认上下文不是用户想要使用的上下文,则必须通过显式地给出Frames
工厂来创建构建器。因此,有许多构造函数和方法有两个签名,一个没有DataContext
或工厂,另一个带有DataContext
或工厂,用户必须根据自己的需求选择正确的构造函数/方法进行调用。
这个特性对于需要多个上下文的应用程序非常有用。
当应用程序需要使用自定义数据上下文并避免使用默认数据上下文时,有几个工具可以帮助找到使用默认数据上下文的位置。首先是一个编译器插件,它可以检测应用程序是否使用了被注解为@DefaultDataContext
的方法/构造函数/字段/类,而调用它的方法/构造函数/字段/类没有该注解。Orekit中所有使用默认数据上下文的方法、构造函数和字段都有这个注解。这个注解和插件的操作方式类似于@Deprecated
注解。它将找到所有在编译时已知的使用情况。要使用该插件,将-Xplugin:dataContextPlugin
添加到javac
命令行参数中。例如,下面是一个使用编译器插件的pom.xml
片段:
<project>
...
<dependencies>
<dependency>
<groupId>org.orekit</groupId>
<artifactId>orekit</artifactId>
<version>10.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<showWarnings>true</showWarnings>
<compilerArgs>
<arg>-Xplugin:dataContextPlugin</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
编译器插件可以检测到在编译时已知的使用默认数据上下文的情况,但无法检测到多态使用默认数据上下文的情况。例如,插件将检测到Object o = AbsoluteDate.J2000_EPOCH;
使用了默认数据上下文,但不会检测到o.toString();
。要检测后者类型的情况,需要使用运行时工具。
检测使用默认数据上下文的第二个工具是ExceptionalDataContext
。这是一个简单地在调用其方法时抛出异常的DataContext
。在运行测试时,可以使用它来确保被测试的代码不访问默认数据上下文。因为Orekit中的一些静态字段使用默认数据上下文进行初始化,所以在替换默认数据上下文之前必须先初始化这些类。要使用ExceptionalDataContext
,请在使用Orekit的任何其他代码之前运行以下代码:
// 强制初始化使用默认数据上下文的类的静态字段
Object o = AbsoluteDate.ARBITRARY_DATE;
o = InertialProvider.of(Frame);
// 阻止进一步使用默认数据上下文
DataContext.setDefault(new ExceptionalDataContext());
ExceptionalDataContext
无法检测使用默认数据上下文的静态字段的使用情况,例如AbsoluteDate
和InertialProvider
中的字段。同时使用编译器插件和ExceptionalDataContext
将能够检测到所有使用默认数据上下文的情况。这提供了一种验证调用重载的构造函数或方法的正确实例,以避免意外使用默认数据上下文的方式。