Orekit cross-validation
June 13, 2026 ยท View on GitHub
This directory holds the Orekit cross-validation tests and their
pre-generated, checked-in fixture (data/orekit_reference.json), used to
cross-check LuPNT's time-scale conversions, sidereal/Earth-rotation angles,
frame conversions (Earth and Moon), Sun/Moon ephemerides, two-body Keplerian
propagation, and J2 acceleration/propagation against
Orekit (v13.1).
Normal users and CI never need Orekit/Java. The tests in this directory:
test_time_conversions_orekit.cc-- time scales, GMST, ERAtest_frame_conversions_orekit.cc-- GCRF/EME2000/ITRF and Moon framestest_ephemeris_orekit.cc-- Sun/Moon DE440 ephemeridestest_orbit_dynamics_orekit.cc-- Kepler propagation, J2 acceleration and propagation
only read data/orekit_reference.json (via LoadTestJson() in
cpp/test/utils.cc) and run as part of the regular pixi run test-cpp
suite, with no JVM/Orekit dependency at build or run time.
A summary of the observed LuPNT-vs-Orekit differences per category is in the
documentation page docs/pages/cross_validation.rst.
What's in data/orekit_reference.json
| Section | Contents | Consumed by |
|---|---|---|
_meta | Provenance: generator script, Orekit version, orekit-data source, and the physical constants (GM_EARTH, GM_MOON, R_EARTH, J2_EARTH) used so the comparison isolates algorithmic differences from differences in adopted constants. | (informational) |
time_scales | For six epochs (bracketing the 2012-06-30 and 2016-12-31 leap seconds), the offsets TAI-UTC, TT-TAI, TDB-TAI, GPS-TAI, and UT1-UTC from Orekit's TimeScalesFactory. Epochs within ~1 day of a leap insertion carry near_leap_second: true (see "Known limitation" below). | test_time_conversions_orekit.cc |
sidereal | GMST (IERS 1996 conventions == the classic IAU-82 formula LuPNT implements) and the IAU-2000 Earth Rotation Angle at the same six epochs. | test_time_conversions_orekit.cc |
frames | For three epochs and four position/velocity states (LEO, MEO, GEO, trans-lunar), the same state in GCRF, EME2000, and ITRF (IERS 2010, no simpleEOP). | test_frame_conversions_orekit.cc |
moon_frames | For two epochs and two Moon-centered states (LLO-like and ELFO-like), the same state in GCRF, Moon-centered inertial (r_moon_ci, a pure translation by the DE440 Earth->Moon vector), and Orekit's body-oriented Moon frame (r_moon_fixed_iau, the analytical IAU pole model). | test_frame_conversions_orekit.cc |
ephemerides | Position/velocity of the Moon and the Sun relative to the Earth (GCRF axes, DE440) at three epochs. | test_ephemeris_orekit.cc |
kepler | Six ClassicalOE cases -- four Earth orbits (MEO-HEO, LEO, GEO, Molniya) and two lunar orbits (LLO, ELFO, with GM_MOON) -- with the initial Cartesian state, eccentric/true anomalies, orbital period, and the propagated state at 0.25/0.5/0.75/1.5 orbital periods via Orekit's KeplerianOrbit/KeplerianPropagator. | test_orbit_dynamics_orekit.cc (dynamics.kepler_orekit_reference) |
j2_acceleration | For five sample positions, the J2 perturbing acceleration from Orekit's J2OnlyPerturbation.computeAccelerationInJ2Frame. | test_orbit_dynamics_orekit.cc (dynamics.j2_acceleration_orekit_reference) |
j2_propagation | Three cases (MEO-HEO, LEO, Molniya) numerically propagated with NumericalPropagator + NewtonianAttraction + J2OnlyPerturbation with GCRF as the J2 frame -- the same inertial-Z-axis modeling choice as LuPNT's JToCartTwoBodyDynamics, so this comparison isolates pure numerical-integration differences (unlike the GMAT one, which exposes the body-fixed-vs-inertial J2 modeling difference). | test_orbit_dynamics_orekit.cc (dynamics.j2_propagation_orekit_reference) |
Regenerating the fixture (developers only)
data/orekit_reference.json is produced by gen_orekit_reference.py. You
only need to run this if you're adding new cross-validation cases, or
deliberately updating the reference values (e.g. after an intentional
algorithm change).
- Make sure the optional
devpixi environment is installed. It adds the JVM-basedorekitPython package (via JCC) on top of the default environment:pixi install -e dev - Run the generator from the repo root:
On first run, this downloads the smallpixi run -e dev python cpp/test/orekit/gen_orekit_reference.pyorekit-databundle (leap seconds, EOP, DE440 ephemerides, ...) intodata/orekit-data-main/(under the repo's top-leveldata/directory). That directory is matched by the**/*orekit-data*/pattern in.gitignore, so it is never committed. - The script overwrites
cpp/test/orekit/data/orekit_reference.json. Review the diff, then runpixi run test-cppto confirm the*_orekit_referencetests still pass (or update the affected tests/tolerances if you intentionally changed something).
Known limitation: UT1 near leap seconds
LuPNT's EOP table stores daily UT1-UTC values and linearly interpolates
across the 1 s leap-second jump, producing spurious UT1-UTC (up to
~0.94 s one hour before an insertion, ~0.09 s six hours after) for epochs
within ~1 day of a leap second. Orekit interpolates the continuous UT1-TAI
instead and is unaffected. The fixture flags such epochs with
near_leap_second: true, and the tests skip the UT1-dependent checks
(UT1-UTC, GMST, ERA) there while still checking the constant-offset scales
(TAI/TT/TDB/GPS). See docs/pages/cross_validation.rst for details.
Tolerance rationale
The comparisons in the *_orekit_reference tests are not exact-equality
checks. The main sources of unavoidable disagreement are documented in the
test files themselves and summarized (with the observed magnitudes) in
docs/pages/cross_validation.rst. In brief:
- Floating-point representation of large epochs (time-scale tests): ~1e-7 s artifacts from representing epochs as seconds-since-J2000 doubles; 1 us tolerances.
- EOP table "vintage" (UT1, GMST/ERA, and ITRF tests): LuPNT's and
Orekit's EOP tables are different (but both valid) IERS snapshots,
disagreeing by ~tens of ms in UT1; the ITRF position tolerance scales
with the orbit radius (
max(100 m, 2e-5 * |r|)). - DE440 distribution differences (ephemerides, MOON_CI): ~5e-11 relative (~0.1-0.35 m for the Moon, ~1-7 m for the Sun).
- IAU pole model vs DE440 principal axes (Moon body-fixed test):
Orekit's body-oriented Moon frame is the analytical IAU model, which
approximates the DE mean-Earth axes to ~3e-5 rad; the tolerance scales as
3e-4 * |r_moon|. (LuPNT's MOON_PA is validated separately against GMAT's
DE-kernel Luna frame -- see
cpp/test/gmat/README.md.) - Numerical-integration differences (j2_propagation): observed ~1e-5 m over 1.5 orbits between LuPNT's fixed-step RK4 (1 s) and Orekit's adaptive DP853.