本教程展示了如何在一个应用程序中使用不同的数据上下文。它设置了两个数据上下文,一个是虚构的,只包含少量的闰秒数据和没有地球定向参数,另一个是完整的,使用位于用户主目录中的orekit-data
文件夹中的文件,并比较使用这两个上下文计算的日期和参考系。
DataContext
接口是构建依赖于外部数据的物理模型的顶级接口。这些模型包括时间尺度,如UTC(需要UTC-TAI偏移数据)或UT1(需要地球定向参数数据),参考系,如ITRF(也需要地球定向参数数据),天体(需要星历数据)。DataContext
接口允许检索所有这些模型的工厂,而工厂将构建它们。
有一个默认的DataContext
,它在需要时使用多个DataProvider
实例来延迟加载模型数据,通过遍历磁盘存储上的目录树并根据文件名解析文件来定位这些数据。例如,它知道如何从用户下载并保存的tai-utc.dat
文件中加载UTC-TAI偏移。
默认的数据上下文通过指定数据搜索时的根目录来进行配置:
// 配置参考上下文
final File home = new File(System.getProperty("user.home"));
final File orekitData = new File(home, "orekit-data");
if (!orekitData.exists()) {
System.err.format(Locale.US, "无法找到 %s 文件夹%n",
orekitData.getAbsolutePath());
System.err.format(Locale.US, "您需要从 %s 下载 %s,解压缩到 %s 并将其重命名为 'orekit-data',以使本教程正常工作%n",
"https://gitlab.orekit.org/orekit/orekit-data/-/archive/master/orekit-data-master.zip",
"orekit-data-master.zip",
home.getAbsolutePath());
System.exit(1);
}
DataContext.
getDefault().
getDataProvidersManager().
addProvider(new DirectoryCrawler(orekitData));
自定义上下文由应用程序类本身实现:
// 创建由该类实现的本地数据上下文
final DataContext context = new Context();
这是因为该类定义了五个方法,允许获取五个模型工厂:
/** {@inheritDoc} */
@Override
public TimeScales getTimeScales() {
// 仅设置2009年至2017年的偏移量和零EOP
return TimeScales.of(Arrays.asList(new OffsetModel(new DateComponents(2009, 1, 1), 34),
new OffsetModel(new DateComponents(2012, 1, 1), 35),
new OffsetModel(new DateComponents(2015, 1, 1), 36)),
(convention, timescale) -> Collections.emptyList());
}
/** {@inheritDoc} */
@Override
public Frames getFrames() {
return Frames.of(getTimeScales(), getCelestialBodies());
}
/** {@inheritDoc} */
@Override
public CelestialBodies getCelestialBodies() {
// 只使用惰性加载的天体
return DataContext.getDefault().getCelestialBodies();
}
/** {@inheritDoc} */
@Override
public GravityFields getGravityFields() {
// 只使用惰性加载的重力场
return DataContext.getDefault().getGravityFields();
}
/** {@inheritDoc} */
@Override
public GeoMagneticFields getGeoMagneticFields() {
// 只使用惰性加载的地磁场
return DataContext.getDefault().getGeoMagneticFields();
}
最重要的部分,也是大多数人需要实现的部分,对应于TimeScales
工厂。在这里,我们使用静态方法TimeScales.of(utcMinusTai, eopSupplier)
创建它。对于教程,实现非常有限:偏移量的utcMinusTai
集合是硬编码的,并且仅限于2009年、2012年和2015年发生的三个闰秒。eopSupplier
函数更加基本,它总是返回一个空列表。
Frames
工厂通常与TimeScales
和CelestialBodies
密切相关,因为它需要相同的数据,所以建议的构建工厂的方法是参考教程中显示的其他工厂。
所有剩余的工厂实际上都是默认上下文自动检索的相同工厂。
我们首先想要检查从日历表示转换为Orekit AbsoluteDate
的日期在两个上下文中的偏移量。我们将使用一个在自定义上下文的第一个闰秒之前的日期,一个在两个上下文中都覆盖的范围内的日期,以及一个在自定义上下文中缺失但在默认上下文中存在的第一个闰秒之后的日期。
// compare date conversions for the two contexts
final TimeScale contextUTC = context.getTimeScales().getUTC();
final TimeScale referenceUTC = DataContext.getDefault().getTimeScales().getUTC();
System.out.format(Locale.US, "自定义数据上下文和默认数据上下文之间的时间刻度差异%n");
for (DateComponents day : days) {
final AbsoluteDate contextDate = new AbsoluteDate(day, TimeComponents.H00, contextUTC);
final AbsoluteDate referenceDate = new AbsoluteDate(day, TimeComponents.H00, referenceUTC);
System.out.format(Locale.US, "在%s上的UTC偏移量: %8.5f%n",
day, contextDate.durationFrom(referenceDate));
}
这个小循环的输出结果如下:
自定义数据上下文和默认数据上下文之间的时间刻度差异
在2003-11-23上的UTC偏移量: 8.08645
在2010-07-11上的UTC偏移量: 0.00000
在2017-08-21上的UTC偏移量: -1.00000
最后两行是预期的。在2010年,自定义上下文和默认上下文都认为TAI和UTC之间有35秒的偏移量,因此它们以相同的方式转换日历日期。在2017年,自定义上下文错过了2017-01-01发生的一个闰秒,因此认为从2015年开始偏移量为36秒,而默认上下文知道偏移量已经改变为37秒。第一行可能看起来奇怪,值不是整数秒。这是由于Orekit创建UTC时间刻度的方式的副作用。由于在1961年至1972年之间使用的非恒定偏移量很麻烦解析,而且一些自定义文件甚至不包括它们,Orekit查看提供的偏移量(这里是我们硬编码的三个偏移量),如果第一个偏移量晚于1968年(对应于线性模型的最后一次更改,从1968-02-01到1971-12-31生效),则自动添加它们。这意味着我们的自定义上下文使用了从1968-02-01到2008-12-31的最后一个线性模型!不过,如果使用的最早日期是2009年,这不是一个问题。因此,从这个例子中学到的教训是,如果实现了一个具有自定义TimeScales
的自定义上下文,最好从1972年开始实现所有常量偏移量,而不是从更晚的时间开始。
我们还想要进行另一个比较,即检查ITRF坐标系。我们将在日期转换有效的一天(即2009年至2015年之间)进行此操作。我们选择了2010-07-01。我们分别使用两个不同的上下文构建两个ITRF坐标系。由于上下文不使用相同的地球定向参数,这些坐标系将不匹配。我们计算它们之间的转换,在一天内提取旋转角度,并将其乘以地球赤道半径以获得更容易理解的值:
// 比较两个上下文的坐标系转换
final Frame contextITRF = context.getFrames().getITRF(IERSConventions.IERS_2010, false);
final Frame referenceITRF = DataContext.getDefault().getFrames().getITRF(IERSConventions.IERS_2010, false);
System.out.format(Locale.US, "%n虚构数据上下文和默认数据上下文之间的坐标系差异%n");
final AbsoluteDate t0 = new AbsoluteDate(days.get(1), contextUTC);
for (double dt = 0; dt < Constants.JULIAN_DAY; dt += 3600) {
final AbsoluteDate date = t0.shiftedBy(dt);
final Transform transform = contextITRF.getTransformTo(referenceITRF, date);
System.out.format(Locale.US, "%s时刻的坐标系偏移量: %6.3f 米%n",
date, transform.getRotation().getAngle() * Constants.WGS84_EARTH_EQUATORIAL_RADIUS);
}
这个循环的输出结果如下:
虚构数据上下文和默认数据上下文之间的坐标系差异
2010-07-11T00:00:00.000Z时刻的坐标系偏移量: 29.396 米
2010-07-11T01:00:00.000Z时刻的坐标系偏移量: 29.388 米
2010-07-11T02:00:00.000Z时刻的坐标系偏移量: 29.380 米
2010-07-11T03:00:00.000Z时刻的坐标系偏移量: 29.373 米
2010-07-11T04:00:00.000Z时刻的坐标系偏移量: 29.365 米
2010-07-11T05:00:00.000Z时刻的坐标系偏移量: 29.358 米
2010-07-11T06:00:00.000Z时刻的坐标系偏移量: 29.351 米
2010-07-11T07:00:00.000Z时刻的坐标系偏移量: 29.344 米
2010-07-11T08:00:00.000Z时刻的坐标系偏移量: 29.338 米
2010-07-11T09:00:00.000Z时刻的坐标系偏移量: 29.331 米
2010-07-11T10:00:00.000Z时刻的坐标系偏移量: 29.325 米
2010-07-11T11:00:00.000Z时刻的坐标系偏移量: 29.318 米
2010-07-11T12:00:00.000Z时刻的坐标系偏移量: 29.312 米
2010-07-11T13:00:00.000Z时刻的坐标系偏移量: 29.306 米
2010-07-11T14:00:00.000Z时刻的坐标系偏移量: 29.300 米
2010-07-11T15:00:00.000Z时刻的坐标系偏移量: 29.294 米
2010-07-11T16:00:00.000Z时刻的坐标系偏移量: 29.289 米
2010-07-11T17:00:00.000Z时刻的坐标系偏移量: 29.283 米
2010-07-11T18:00:00.000Z时刻的坐标系偏移量: 29.277 米
2010-07-11T19:00:00.000Z时刻的坐标系偏移量: 29.272 米
2010-07-11T20:00:00.000Z时刻的坐标系偏移量: 29.266 米
2010-07-11T21:00:00.000Z时刻的坐标系偏移量: 29.261 米
2010-07-11T22:00:00.000Z时刻的坐标系偏移量: 29.256 米
2010-07-11T23:00:00.000Z时刻的坐标系偏移量: 29.250 米
我们看到的是EOP的影响。
这个示例的完整代码可以在教程的源代码树中找到,文件名为src/main/java/org/orekit/tutorials/data/Context.java
。