astrodyn_bevy 0.1.1

Bevy ECS adapter for the astrodyn orbital-dynamics gateway
Documentation

astrodyn_bevy

A Rust port of NASA JEOD (JSC Engineering Orbital Dynamics, v5.4) with Bevy ECS wiring on top.

astrodyn_bevy reimplements JEOD's spacecraft dynamics — spherical-harmonics gravity, Earth rotation (precession/nutation/polar motion), atmospheric drag, solar radiation pressure, gravity-gradient torque, multi-step integrators, time-scale conversion (TAI/UTC/UT1/TDB/TT/GMST), DE4xx ephemerides — as pure Rust crates, then exposes them through a thin Bevy adapter so they slot into any Bevy app.

Status: pre-1.0. Tier 3 cross-validated against JEOD Trick simulations (see the Tier3-Regeneration wiki page). API may change before 1.0.

Architecture

Three layers, separated by hard dependency rules:

  • astrodyn_* — pure Rust physics crates, zero Bevy dependency. Math, integrators, frame transforms, gravity, time scales, ephemerides.
  • astrodyn — orchestration and recipes. Composes astrodyn_* into a pipeline; the single API surface for any ECS adapter. Zero Bevy dependency.
  • astrodyn_bevy (this crate) — thin Bevy glue. Components, systems that delegate to astrodyn, plugin registration. Depends only on astrodyn + bevy.

See the Strategy and Type-System wiki pages for architecture detail and the typed-quantity layer.

Quick start

[dependencies]
bevy = "0.18"
astrodyn_bevy = "0.1"
use bevy::prelude::*;
use astrodyn_bevy::prelude::*;
use astrodyn_bevy::recipes::{earth, orbital_elements, vehicle};
use astrodyn::Earth;

fn setup(mut commands: Commands) {
    let earth_recipe = earth::point_mass();
    let mu = earth_recipe.source.mu.m3_per_s2();
    let earth_entity = commands
        .spawn((
            GravitySourceC(earth_recipe.source),
            SourceInertialPositionC::default(),
            TranslationalStateC::<Earth>::default(),
        ))
        .id();

    let cfg = VehicleBuilder::new()
        .from_orbital_elements(orbital_elements::iss(), mu)
        .three_dof_point_mass(vehicle::iss_mass())
        .rk4()
        .gravity(GravityControl::new_spherical(0_usize, GravityGradient::Skip))
        .build();

    cfg.spawn_bevy::<Earth>(&mut commands, &[earth_entity]);
}

fn main() {
    App::new()
        .add_plugins(MinimalPlugins)
        .add_astrodyn(10.0)
        .add_systems(Startup, setup)
        .run();
}

The typestate VehicleBuilder rejects misuse at compile time (no integrator chosen, no state set, mismatched coordinate frames). Errors render in physics language — "expected Position<RootInertial>, found Position<Ecef> — apply a FrameTransform<Ecef, RootInertial> first" — not as PhantomData mismatches.

A full worked example lives in examples/typed_mission.rs.

Verification

Three test tiers, all part of the definition of done for every release:

  • Tier 1 — unit tests on pure functions (round-trips, convergence).
  • Tier 2 — comparison against static reference vectors extracted from JEOD source files (gravity test cases, Euler angle tables).
  • Tier 3 — end-to-end trajectory cross-validation: propagate from the same initial conditions as a JEOD Trick simulation and compare position, velocity, attitude, and angular velocity over hours or days. Reference CSVs are committed to the repo.
cargo nextest run --workspace -E 'not test(tier3_)'   # fast: Tier 1 + 2
cargo nextest run --workspace -E 'test(tier3_)'       # Tier 3

cargo nextest run --workspace works on a fresh clone without $JEOD_HOME — every tier (unit, Tier 2, Tier 3) reads from committed fixtures under test_data/. $JEOD_HOME is required only when regenerating those fixtures via the extract_* binaries under crates/astrodyn_verif_jeod/src/bin/ or refreshing the verbatim mirror at crates/astrodyn_verif_jeod/test_data/jeod_inputs/. See CLAUDE.md for the full build / test / regen workflow.

Documentation

Most docs live on the project wiki: architecture and phase history (Strategy), typed-quantity primer (Type-System), Tier 3 regeneration recipe (Tier3-Regeneration), the JEOD↔astrodyn_bevy capability matrix, per-SIM coverage map, and audit findings.

The one exception that stays in the repo is docs/JEOD_invariants.md — the catalog of JEOD C++ invariants and where each is enforced in our Rust port. It lives next to the code because tags like // JEOD_INV: XX.YY in source are consistency-checked against the catalog.

License

Licensed under either of

at your option.

NASA JEOD itself is distributed under NASA's open-source license and is not redistributed by this project.