//! 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"); }