Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/astrodyn_verif_jeod/src/bin/extract_body_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const SCENARIOS: &[Scenario] = &[
reference_inertial: true,
orbit_inits: &[
"trans_Orbit_inertial_body_set01",
"trans_Orbit_inertial_body_set02",
"trans_Orbit_pfix_body_set01",
],
trans_states: &["trans_TransState_inertial_body"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@
//! parity wrapper drives bit-identity at the same propagation depth as
//! the matching tier3 runner test.
//!
//! Five recipes correspond to JEOD's RUN list:
//! The recipes correspond to JEOD's RUN list:
//! * RUN_0001 — ISS orbital elements (set01) in `Earth.inertial`;
//! * RUN_0002 — ISS orbital elements (set02, mean anomaly) in `Earth.inertial`;
//! * RUN_0101 — STS-114 orbital elements (set01) in `Earth.inertial`;
//! * RUN_0102 — STS-114 orbital elements (set02, mean anomaly) in `Earth.inertial`;
//! * RUN_0201 — ISS orbital elements (set01) in `Earth.pfix`;
//! * RUN_0301 — STS-114 orbital elements (set01) in `Earth.pfix`;
//! * RUN_0401 — STS-114 direct Cartesian state in `Earth.inertial`.
//!
//! Both parameterizations resolve to [`init_from_mean_anomaly`]; set01
//! derives `M = t_peri·√(μ/a³)` from the fixture's `time_periapsis`, while
//! set02 reads the fixture's `mean_anomaly` field directly.
//!
//! The orbital-element-to-Cartesian conversion is the substance of this
//! test; it runs inside every scenario factory rather than being
//! pre-baked, so the parity wrapper exercises `init_from_mean_anomaly`
Expand Down Expand Up @@ -154,6 +160,35 @@ fn orbital_element_state(vehicle: &str, init_name: &str, mu_earth: f64) -> Trans
}
}

/// Materialize a JEOD set02 (`SmaEccIncAscnodeArgperMeanAnomaly`) fixture
/// into an inertial-frame translational state. Unlike set01, the mean
/// anomaly is supplied directly by the deck (fixture field `mean_anomaly`,
/// stored in radians by `extract_body_init`), so this is exactly
/// [`init_from_mean_anomaly`] with no time-periapsis derivation. The set02
/// decks are `Earth.inertial` only.
fn mean_anomaly_element_state(vehicle: &str, init_name: &str, mu_earth: f64) -> TranslationalState {
let init = load_orbital_init(vehicle, init_name);
let mean_anomaly = init.mean_anomaly.unwrap_or_else(|| {
panic!("{vehicle}/{init_name}: set02 expected mean_anomaly in the fixture")
});
assert_eq!(
init.reference_frame.as_str(),
"Earth.inertial",
"{vehicle}/{init_name}: set02 recipe only supports Earth.inertial frames, \
got '{}' — add frame handling if a pfix set02 RUN is introduced",
init.reference_frame,
);
init_from_mean_anomaly(
init.semi_major_axis,
init.eccentricity,
init.inclination,
init.ascending_node,
init.arg_periapsis,
mean_anomaly,
mu_earth,
)
}

/// Materialize a JEOD direct-Cartesian fixture (RUN_0401 only) into an
/// inertial-frame translational state. The fixture is a pass-through:
/// `position`/`velocity` arrays are taken verbatim.
Expand Down Expand Up @@ -244,6 +279,24 @@ fn build_run_0401(_init: &InitialConditions) -> SimulationBuilder {
build_orbinit_docker(mu, state)
}

/// RUN_0002: ISS set02 (mean-anomaly) elements from the committed
/// `iss.json` fixture (`trans_Orbit_inertial_body_set02`), in
/// `Earth.inertial`.
fn build_run_0002(_init: &InitialConditions) -> SimulationBuilder {
let mu = load_mu_earth();
let state = mean_anomaly_element_state("ISS", "trans_Orbit_inertial_body_set02", mu);
build_orbinit_docker(mu, state)
}

/// RUN_0102: STS-114 set02 (mean-anomaly) elements from the committed
/// `sts_114.json` fixture (`trans_Orbit_inertial_body_set02`), in
/// `Earth.inertial`.
fn build_run_0102(_init: &InitialConditions) -> SimulationBuilder {
let mu = load_mu_earth();
let state = mean_anomaly_element_state("STS_114", "trans_Orbit_inertial_body_set02", mu);
build_orbinit_docker(mu, state)
}

/// RUN_0001: ISS orbital elements (set01) in `Earth.inertial`.
pub fn run_0001() -> VerificationCase {
VerificationCase {
Expand All @@ -260,6 +313,38 @@ pub fn run_0001() -> VerificationCase {
}
}

/// RUN_0002: ISS orbital elements (set02, mean-anomaly) in `Earth.inertial`.
pub fn run_0002() -> VerificationCase {
VerificationCase {
name: "tier3_orbinit_docker_run_0002",
scenario: build_run_0002,
reference: CsvReference::SyntheticTimes {
dt: DT_S,
num_steps: NUM_STEPS,
},
duration: Time::new::<second>(0.0),
tolerances: synthetic_tolerances(),
extras: None,
pre_step: None,
}
}

/// RUN_0102: STS-114 orbital elements (set02, mean-anomaly) in `Earth.inertial`.
pub fn run_0102() -> VerificationCase {
VerificationCase {
name: "tier3_orbinit_docker_run_0102",
scenario: build_run_0102,
reference: CsvReference::SyntheticTimes {
dt: DT_S,
num_steps: NUM_STEPS,
},
duration: Time::new::<second>(0.0),
tolerances: synthetic_tolerances(),
extras: None,
pre_step: None,
}
}

/// RUN_0101: STS-114 orbital elements (set01) in `Earth.inertial`.
pub fn run_0101() -> VerificationCase {
VerificationCase {
Expand Down
16 changes: 8 additions & 8 deletions crates/astrodyn_verif_jeod/test_data/body_init/iss.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"source": "models/dynamics/body_action/verif/SIM_orbinit/Modified_data/ISS/",
"jeod_version": "5.4",
"jeod_commit": "27893108bbde8bb162b3213a30d57af89acd1c76",
"generated_utc": "unknown (pre-#503 baseline)",
"generated_utc": "2026-05-25T07:10:54Z",
"note": "Body initialization vectors. Regenerate with: cargo run -p astrodyn_verif_jeod --bin extract_body_init -- --jeod-home $JEOD_HOME",
"reference_inertial": {"position": [1244540.53, 5655938.85, 3425643.22], "velocity": [-6003.833051, -1469.496044, 4590.511776]},
"orbital_inits": [
Expand All @@ -13,8 +13,8 @@
"semi_major_axis": 6732901.20152,
"eccentricity": 0.0012907335,
"inclination": 0.9018194918388728,
"ascending_node": 0.8675755493238396,
"arg_periapsis": 1.755494852217414,
"ascending_node": 0.8675755493238397,
"arg_periapsis": 1.7554948522174143,
"time_periapsis": 4581.96167293,
"mean_anomaly": null,
"true_anomaly": null,
Expand All @@ -26,8 +26,8 @@
"semi_major_axis": 6732901.20152,
"eccentricity": 0.0012907335,
"inclination": 0.9018194918388728,
"ascending_node": 0.8675755493238396,
"arg_periapsis": 1.755494852217414,
"ascending_node": 0.8675755493238397,
"arg_periapsis": 1.7554948522174143,
"time_periapsis": null,
"mean_anomaly": 5.236209017533277,
"true_anomaly": null,
Expand All @@ -39,8 +39,8 @@
"semi_major_axis": 6732901.20152,
"eccentricity": 0.0012907335,
"inclination": 0.9018194918388728,
"ascending_node": 0.8675755493238396,
"arg_periapsis": 1.755494852217414,
"ascending_node": 0.8675755493238397,
"arg_periapsis": 1.7554948522174143,
"time_periapsis": null,
"mean_anomaly": null,
"true_anomaly": 5.2339718836974285,
Expand All @@ -51,7 +51,7 @@
"name": "trans_Orbit_pfix_body_set01",
"semi_major_axis": 6732901.205250001,
"eccentricity": 0.00129073426,
"inclination": 0.9014390876767299,
"inclination": 0.90143908767673,
"ascending_node": 5.429530680701693,
"arg_periapsis": 1.7559744228809693,
"time_periapsis": 4581.96222432,
Expand Down
21 changes: 17 additions & 4 deletions crates/astrodyn_verif_jeod/test_data/body_init/sts_114.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"source": "models/dynamics/body_action/verif/SIM_orbinit/Modified_data/STS_114/",
"jeod_version": "5.4",
"jeod_commit": "27893108bbde8bb162b3213a30d57af89acd1c76",
"generated_utc": "unknown (pre-#503 baseline)",
"generated_utc": "2026-05-25T07:10:54Z",
"note": "Body initialization vectors. Regenerate with: cargo run -p astrodyn_verif_jeod --bin extract_body_init -- --jeod-home $JEOD_HOME",
"reference_inertial": {"position": [1244471.94, 5655811.8, 3425518.88], "velocity": [-6003.553468, -1469.321965, 4590.57723]},
"orbital_inits": [
Expand All @@ -13,20 +13,33 @@
"semi_major_axis": 6732163.59764,
"eccentricity": 0.00122446354,
"inclination": 0.9018261209658911,
"ascending_node": 0.8675961322535078,
"ascending_node": 0.867596132253508,
"arg_periapsis": 1.801291139680453,
"time_periapsis": 4541.07695545,
"mean_anomaly": null,
"true_anomaly": null,
"planet_name": "Earth",
"reference_frame": "Earth.inertial"
},
{
"name": "trans_Orbit_inertial_body_set02",
"semi_major_axis": 6732163.59764,
"eccentricity": 0.00122446354,
"inclination": 0.9018261209658911,
"ascending_node": 0.867596132253508,
"arg_periapsis": 1.801291139680453,
"time_periapsis": null,
"mean_anomaly": 5.19033936505041,
"true_anomaly": null,
"planet_name": "Earth",
"reference_frame": "Earth.inertial"
},
{
"name": "trans_Orbit_pfix_body_set01",
"semi_major_axis": 6732163.61166,
"eccentricity": 0.00122446412,
"inclination": 0.9014457086530604,
"ascending_node": 5.429551274731655,
"inclination": 0.9014457086530605,
"ascending_node": 5.429551274731656,
"arg_periapsis": 1.8017698046402992,
"time_periapsis": 4541.07829767,
"mean_anomaly": null,
Expand Down
2 changes: 2 additions & 0 deletions crates/astrodyn_verif_jeod/test_data/orbinit_0002_orbinit.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sys.exec.out.time {s},target.dyn_body.composite_body.state.trans.position[0] {m},target.dyn_body.composite_body.state.trans.position[1] {m},target.dyn_body.composite_body.state.trans.position[2] {m},target.dyn_body.composite_body.state.trans.velocity[0] {m/s},target.dyn_body.composite_body.state.trans.velocity[1] {m/s},target.dyn_body.composite_body.state.trans.velocity[2] {m/s}
0, 1244540.530006297, 5655938.850008326, 3425643.219975276, -6003.833092393274, -1469.496054158121, 4590.511807712143
2 changes: 2 additions & 0 deletions crates/astrodyn_verif_jeod/test_data/orbinit_0102_orbinit.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sys.exec.out.time {s},target.dyn_body.composite_body.state.trans.position[0] {m},target.dyn_body.composite_body.state.trans.position[1] {m},target.dyn_body.composite_body.state.trans.position[2] {m},target.dyn_body.composite_body.state.trans.velocity[0] {m/s},target.dyn_body.composite_body.state.trans.velocity[1] {m/s},target.dyn_body.composite_body.state.trans.velocity[2] {m/s}
0, 1244471.939980413, 5655811.799992709, 3425518.880036598, -6003.553509436046, -1469.321975147157, 4590.577261632054
40 changes: 35 additions & 5 deletions crates/astrodyn_verif_jeod/tests/tier3_sim_orbinit_docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ fn assert_orbinit_match(
fn tier3_orbinit_docker_run0001_iss_inertial() {
// RUN_0001: ISS, SmaEccIncAscnodeArgperTimeperi, reference=Earth.inertial.
// No frame rotation required — recipe output is already in inertial.
// Observed: pos=3.76e-9 m, vel=3.43e-12 m/s (5% above → listed).
// Observed: pos=6.25e-9 m, vel=6.19e-12 m/s (5% above → listed).
assert_orbinit_match(
sim_orbinit_docker::run_0001(),
"orbinit_0001_orbinit.csv",
"RUN_0001 (ISS inertial set01)",
3.95e-9,
3.61e-12,
6.56e-9,
6.50e-12,
);
}

Expand All @@ -181,13 +181,43 @@ fn tier3_orbinit_docker_run0001_iss_inertial() {

#[test]
fn tier3_orbinit_docker_run0101_sts_inertial() {
// Observed: pos=1.04e-9 m, vel=1.83e-12 m/s (5% above → listed).
// Observed: pos=1.04e-9 m, vel=2.27e-13 m/s (5% above → listed).
assert_orbinit_match(
sim_orbinit_docker::run_0101(),
"orbinit_0101_orbinit.csv",
"RUN_0101 (STS-114 inertial set01)",
1.10e-9,
1.93e-12,
2.39e-13,
);
}

// ───────────────────────────────────────────────────────────────────────────
// RUN_0002 / RUN_0102: set02 (mean-anomaly parameterization), inertial frame.
// Exercises `init_from_mean_anomaly` directly (distinct from set01's
// time-periapsis → mean-anomaly derivation). Tolerances are 1.05× observed.
// ───────────────────────────────────────────────────────────────────────────

#[test]
fn tier3_orbinit_docker_run0002_iss_inertial() {
// Observed: pos=3.26e-9 m, vel=3.40e-12 m/s (5% above → listed).
assert_orbinit_match(
sim_orbinit_docker::run_0002(),
"orbinit_0002_orbinit.csv",
"RUN_0002 (ISS inertial set02, mean anomaly)",
3.42e-9,
3.57e-12,
);
}

#[test]
fn tier3_orbinit_docker_run0102_sts_inertial() {
// Observed: pos=1.68e-9 m, vel=2.33e-12 m/s (5% above → listed).
assert_orbinit_match(
sim_orbinit_docker::run_0102(),
"orbinit_0102_orbinit.csv",
"RUN_0102 (STS-114 inertial set02, mean anomaly)",
1.76e-9,
2.45e-12,
);
}

Expand Down
18 changes: 8 additions & 10 deletions crates/astrodyn_verif_jeod_fixtures/src/orbital_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
//! binary; runtime test paths never call them.

use regex::Regex;
use std::f64::consts::PI;

use crate::body_init_fixtures::{
load_vehicle_bundle, BodyInitFixtureError, OrbitalInitRecord, TransStateRecord,
Expand All @@ -33,7 +32,7 @@ use crate::body_init_fixtures::{
/// - `key = value` (bare numeric)
///
/// Unit conversions applied automatically:
/// - `"degree"` -> radians (multiply by PI/180)
/// - `"degree"` -> radians (via `f64::to_radians`)
/// - `"km"` -> meters (multiply by 1000)
/// - `"s"` -> seconds (no conversion)
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -171,12 +170,12 @@ pub fn parse_orbital_init_py(content: &str) -> Result<OrbitalInitRecord, BodyIni
match key {
"semi_major_axis" => semi_major_axis = Some(val * 1000.0), // assume km
"eccentricity" => eccentricity = Some(val),
"inclination" => inclination = Some(val * PI / 180.0), // assume degrees
"ascending_node" => ascending_node = Some(val * PI / 180.0),
"arg_periapsis" => arg_periapsis = Some(val * PI / 180.0),
"inclination" => inclination = Some(val.to_radians()), // assume degrees
"ascending_node" => ascending_node = Some(val.to_radians()),
"arg_periapsis" => arg_periapsis = Some(val.to_radians()),
"time_periapsis" => time_periapsis = Some(val),
"mean_anomaly" => mean_anomaly = Some(val * PI / 180.0),
"true_anomaly" => true_anomaly = Some(val * PI / 180.0),
"mean_anomaly" => mean_anomaly = Some(val.to_radians()),
"true_anomaly" => true_anomaly = Some(val.to_radians()),
_ => {}
}
continue;
Expand Down Expand Up @@ -218,7 +217,7 @@ pub fn parse_orbital_init_py(content: &str) -> Result<OrbitalInitRecord, BodyIni
/// Convert a raw value from the given unit string to SI (meters, radians, seconds).
fn convert_units(val: f64, unit: &str) -> Result<f64, BodyInitFixtureError> {
match unit {
"degree" => Ok(val * PI / 180.0),
"degree" => Ok(val.to_radians()),
"km" => Ok(val * 1000.0),
"s" => Ok(val),
"m" => Ok(val),
Expand Down Expand Up @@ -372,8 +371,7 @@ vehicle.set01.subject.orbit_frame_name = "Earth.inertial"
let rec = parse_orbital_init_py(py).unwrap();
assert!((rec.semi_major_axis - 6_732_901.201_52).abs() < 1e-6);
assert!((rec.eccentricity - 0.00129073350).abs() < 1e-12);
let deg2rad = PI / 180.0;
assert!((rec.inclination - 51.670450765 * deg2rad).abs() < 1e-12);
assert!((rec.inclination - 51.670450765_f64.to_radians()).abs() < 1e-12);
assert_eq!(rec.planet_name, "Earth");
assert_eq!(rec.reference_frame, "Earth.inertial");
assert!((rec.time_periapsis.unwrap() - 4581.96167293).abs() < 1e-9);
Expand Down
10 changes: 10 additions & 0 deletions crates/astrodyn_verif_parity/tests/bevy_parity_orbinit_docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ fn bevy_parity_orbinit_docker_run_0101() {
sim_orbinit_docker::run_0101().run_and_assert_parity::<astrodyn::Earth>();
}

#[test]
fn bevy_parity_orbinit_docker_run_0002() {
sim_orbinit_docker::run_0002().run_and_assert_parity::<astrodyn::Earth>();
}

#[test]
fn bevy_parity_orbinit_docker_run_0102() {
sim_orbinit_docker::run_0102().run_and_assert_parity::<astrodyn::Earth>();
}

#[test]
fn bevy_parity_orbinit_docker_run_0201() {
sim_orbinit_docker::run_0201().run_and_assert_parity::<astrodyn::Earth>();
Expand Down
3 changes: 3 additions & 0 deletions trick/generate_references.sh
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,9 @@ run_orbinit_group() {
"SET_test/RUN_0201:orbinit_0201:orbinit_0201_orbinit.csv"
"SET_test/RUN_0301:orbinit_0301:orbinit_0301_orbinit.csv"
"SET_test/RUN_0401:orbinit_0401:orbinit_0401_orbinit.csv"
# set02 mean-anomaly parameterization (ISS + STS, inertial)
"SET_test/RUN_0002:orbinit_0002:orbinit_0002_orbinit.csv"
"SET_test/RUN_0102:orbinit_0102:orbinit_0102_orbinit.csv"
)
local needs_build=0
for entry in "${RUNS[@]}"; do
Expand Down