Expand description
§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.
§When to use
- Building a Bevy-based mission simulation — Earth-orbit
constellation, lunar / Mars approach, station-keeping study,
rendezvous-and-proximity scenarios — where the Bevy
Appis the runtime and the ECS world is the single source of truth for state. - Composing a scenario — sources, bodies, ephemeris, mass tree,
integrator config — with
SimulationBuilder::populate_app::<P>and the typestateVehicleBuilder(canonical entry point per CLAUDE.md). - Inserting a single vehicle into an existing
App(a smaller example, a follow-up insert during a running sim) viaVehicleConfig::spawn_bevy.
For non-Bevy use — Tier 3 cross-validation tests, batch propagation,
offline studies — astrodyn_runner is the parallel arena-state
consumer of the same astrodyn pipeline. For pure physics math
(coordinate conversions, attitude algebra, gravity evaluation) without
either runtime, depend on astrodyn directly.
§Key concepts
The crate is the thin glue layer of the three-layer architecture
(see below): components are typed newtypes around astrodyn
quantities, systems delegate to astrodyn pipeline functions, and
the AstrodynPlugin wires the seven AstrodynSet variants into
FixedUpdate in JEOD-step order. There is no physics in this crate
— a CI lint refuses any astrodyn_*-physics-crate direct dep, so
the adapter cannot quietly reimplement what the gateway re-exports.
The typestate VehicleBuilder makes vehicle construction a compile-
time refusal of misconfigurations: .three_dof_point_mass(...) is
unavailable until a state is set, .rk4() until mass is set,
.build() until an integrator is chosen. Frame mismatches at
spawn_bevy boundaries surface as physics-language errors
(“expected Position<RootInertial>, found Position<Ecef> — apply
a FrameTransform<Ecef, RootInertial> first”) via
#[diagnostic::on_unimplemented], not as raw PhantomData
type-mismatch walls.
§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. Composesastrodyn_*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 toastrodyn, plugin registration. Depends only onastrodyn+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, FrameUid, PlanetInertial};
fn setup(mut commands: Commands) {
let earth_recipe = earth::point_mass();
let mu = earth_recipe.source.mu.m3_per_s2();
commands.spawn((
// The source's inertial-frame identity (issue #668): gravity
// controls reference it by this value, in every host.
FrameUidC(FrameUid::of::<PlanetInertial<Earth>>()),
GravitySourceC(earth_recipe.source),
SourceInertialPositionC::default(),
TranslationalStateC::<Earth>::default(),
));
let cfg = VehicleBuilder::new()
.vehicle_named("iss")
.from_orbital_elements(orbital_elements::iss(), mu)
.three_dof_point_mass(vehicle::iss_mass())
.rk4()
.gravity(GravityControl::new_spherical(
FrameUid::of::<PlanetInertial<Earth>>(),
GravityGradient::Skip,
))
.build();
cfg.spawn_bevy::<Earth>(&mut commands);
}
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 3cargo 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
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
NASA JEOD itself is distributed under NASA’s open-source license and is not redistributed by this project.
Re-exports§
pub use app_ext::AstrodynAppExt;pub use body_action::add_body_action_via;pub use body_action::body_action_intake_system;pub use body_action::body_action_system;pub use body_action::body_action_unregistered_planet_fence_system;pub use body_action::BodyActionCommandsExt;pub use body_action::BodyActionEvent;pub use body_action::BodyActionsR;pub use body_action::RegisteredPlanetsR;pub use body_mutator::BodyMutator;pub use body_mutator::BodyReader;pub use frame_attach_system::frame_attach_system;pub use frame_attach_system::propagate_frame_attached_state_post_integration_system;pub use frame_attach_system::propagate_frame_attached_state_system;pub use kinematic_propagation::propagate_state_from_root_post_integration_system;pub use kinematic_propagation::propagate_state_from_root_system;pub use mass_tree::composite_mass_system;pub use mass_tree::MassTreeQueries;pub use mass_tree::MassTreeView;pub use scenario::ScenarioHandles;pub use scenario::SimulationBuilderBevyExt;pub use source_mutator::SourceMutator;pub use source_mutator::SourceReader;pub use wrench::wrench_aggregation_system;pub use bundles::*;pub use components::*;pub use sets::*;pub use systems::*;
Modules§
- app_ext
AstrodynAppExt— ergonomicAppsetup and fixed-step advancement.- body_
action - Bevy adapter for
astrodyn::BodyAction: queue body actions against an entity at startup or mid-sim, then have them applied bybody_action_systemeach tick before the rest of the pipeline runs. - body_
mutator - Body-state read/write API for Bevy missions.
- bundles
- Convenience bundles for spawning common entity types.
- components
- Bevy
Componentnewtypes wrappingastrodyntyped siblings (state, mass, gravity controls, interactions, derived states). - frame_
attach_ system - Bevy systems for frame-attached body integration.
- frame_
param - Bevy-native
SystemParams for cross-frame state computation (Frame-Tree-ECS-Native § 13). - kinematic_
propagation - Bevy system for kinematic state propagation root → leaves.
- mass_
tree - Bevy-side ECS mass-tree adapter.
- prelude
- Mission-crate prelude:
use astrodyn_bevy::prelude::*;. - recipes
- Recipe catalogue re-export:
use astrodyn_bevy::recipes::*;. - scenario
SimulationBuilder → Bevy Appbridge — the canonical mission entry point for whole-scenario composition.- sets
AstrodynSet— the BevySystemSetpartition that mirrors JEOD’s per-step pipeline.- source_
mutator - Source-state read/write API for Bevy missions.
- systems
- Bevy
Systems that delegate per-body work toastrodynper-body orchestration functions. Each system queries the relevant components, calls intoastrodyn, and writes the result back. No physics algorithms live here. - validation
- Runtime validation of JEOD invariants.
- wrench
- Bevy system for composite-rigid-body wrench aggregation.
Structs§
- Astrodyn
Plugin - Unified JEOD plugin — registers all pipeline systems and schedule sets.
- Atmosphere
Config - Planet-level atmosphere configuration (ECS-agnostic).
- Atmosphere
ModelR - Bevy resource wrapping
AtmosphereConfigwith an entity reference for the planet whose rotation matrix is used for geodetic conversion. - EphemerisR
- Bevy resource wrapping
astrodyn::Ephemerisfor DE4xx ephemeris access. - Integration
DtR - Bit-exact f64 pipeline integration timestep.
- Mass
TreeR - Bevy resource wrapping
MassTreefor multi-body vehicles. - Polar
MotionR - Optional Bevy resource for polar motion (xp, yp) in radians.
- Root
Frame EntityR - Bevy resource holding the
Entityof the root frame entity in the ECS-native frame hierarchy. The root frame entity carriescomponents::FrameTransC/components::FrameRotC/components::FrameAngVelCat identity and is the parent of every source’s inertial frame entity (which is the parent of every body or pfix frame entity, and so on). Spawned byAstrodynPlugin::buildbefore any source/body registration so the registration systems canChildOf-link their frame entities to it. Mission code reads cross-frame state viacrate::frame_param::RelativeFrameStateandcrate::frame_param::FrameOrigin. - Simulation
TimeR - Bevy resource wrapping
SimulationTime.
Enums§
- Atmosphere
Model - Selectable atmosphere model.
Traits§
- Vehicle
Config Bevy Ext - Bevy-side terminal for
astrodyn::VehicleBuilder.
Functions§
- register_
planet_ systems - Register the planet-generic system instantiations needed for a downstream multi-planet mission.