Files
astrokit/tests/orekit_comparison.rs
T
2026-10-02 20:46:01 -04:00

72 lines
2.9 KiB
Rust

//! This crate's propagator against Orekit's, on the fixtures in `tests/fixtures/orekit`.
//!
//! Generated by `scripts/generate-orekit-fixtures.sh`. Each fixture records the setup
//! Orekit was given, so a disagreement can be attributed to the models rather than left
//! as a mystery; see `tests/common/mod.rs` for what has to match and why.
//!
//! The only fixture so far is a point mass. That is deliberate as a first step: with no
//! field, no third body, no atmosphere and an inertial frame, there is nothing left to
//! mismatch, so the figure it produces measures the comparison itself — frames, time
//! scale, units and `mu` — rather than any model. Anything added after this is measured
//! against a harness already known to be sound.
mod common;
use astrokit::prelude::*;
use common::ReferenceArc;
use nalgebra::Vector3;
use std::path::Path;
/// Propagates the fixture's initial state with this crate and returns the worst position
/// and velocity disagreement over the arc.
fn compare(arc: &ReferenceArc, forces: &[Dynamics]) -> (f64, f64) {
let start = &arc.states[0];
let epoch = Epoch::from_gregorian_utc_hms(2026, 1, 1, 0, 0, 0);
let mut propagation = Propagation::new(
epoch,
Orbit
.with_position(Vector3::from_column_slice(&start.position))
.with_velocity(Vector3::from_column_slice(&start.velocity)),
forces,
)
.frame(Frame::EARTH_J2000)
.duration(Duration::from_seconds(arc.duration()))
.accuracy(Accuracy::High);
let solution = propagation.run();
let (mut worst_position, mut worst_velocity) = (0.0_f64, 0.0_f64);
for state in &arc.states {
let ours = Orbit.state(solution.interpolate(state.elapsed));
worst_position = worst_position
.max((ours.position() - Vector3::from_column_slice(&state.position)).norm());
worst_velocity = worst_velocity
.max((ours.velocity() - Vector3::from_column_slice(&state.velocity)).norm());
}
(worst_position, worst_velocity)
}
#[test]
fn test_a_point_mass_arc_agrees_with_orekit() {
let path = Path::new("tests/fixtures/orekit/two-body-low-earth.json");
let arc = ReferenceArc::load(path).expect("the fixture is committed");
assert_eq!(arc.setup.tool, "orekit");
assert_eq!(arc.duration(), 86400.0);
assert_eq!(arc.states.len(), 1441);
let (position, velocity) = compare(&arc, &[TwoBody { body: Body::EARTH }]);
// Sub-millimeter over a day. Nothing in this case can disagree except the two
// integrators and the arithmetic, so this is the floor the comparison can measure to:
// a later fixture that disagrees by more than this is disagreeing about physics.
assert!(
position < 1.0e-3,
"worst position disagreement {position:.3e} m over the day"
);
assert!(
velocity < 1.0e-6,
"worst velocity disagreement {velocity:.3e} m/s over the day"
);
println!("point mass: {position:.3e} m, {velocity:.3e} m/s");
}