Expand description
Powered, three-dimensional point-mass flight, using SI units and simulation time.
Prescribed motors supply thrust and consume propellant in proportion to impulse;
Stages add serial or parallel staging with scheduled separations. Simulator
integrates translation while ForceModel supplies environmental acceleration and
Guidance supplies optional control commands. UniformGravity covers local
flat-frame flight and CentralGravity covers planet-scale arcs; the default
ballistics feature reuses ballistics-engine’s atmosphere and drag kernel.
Coordinates are right-handed; the conventional local frame is X/downrange, Y/up,
Z/lateral, but CentralGravity works in an Earth-centered frame of the caller’s
choosing. No time compression occurs inside this crate. This is a point-mass model,
not an attitude, actuator or aerodynamic-lift simulation.
See the README and examples/boost_coast.rs for integration and model assumptions.
§rockets-engine
A Rust companion to ballistics-engine for powered, three-dimensional point-mass flight. It adds prescribed motor burns, changing mass, incremental RK4 integration and application-owned guidance hooks. The default adapter calls the existing ballistics-engine gravity/drag derivative function; it does not fork or modify that engine.
The crate is published on crates.io and consumes ballistics-engine 0.48.0 from crates.io. Rust 1.99.1 is pinned for development, matching the game workspace.
§Use
[dependencies]
rockets-engine = "0.6"§Run
cargo test --locked --all-features
cargo run --locked --example boost_coast
cargo run --locked --example guidance_hook
cargo run --locked --example staging
cargo run --locked --example icbm_midcourse
cargo doc --locked --no-deps --openboost_coast prints CSV for a synthetic small rocket through ignition, powered flight and coast. Its numbers are illustrative. guidance_hook demonstrates externally supplied control acceleration without a target-tracking algorithm. staging flies a three-stage rocket through separations and inter-stage coasts. icbm_midcourse coasts a post-boost state around a central-gravity Earth to a reentry handoff; it needs no features and shows the planet-scale use of the integrator.
§Command line
The cli feature builds a rockets binary:
cargo install rockets-engine --features cli
rockets simulate --curve 0:0,0.1:20,0.9:15,1:0 --dry-mass 0.18 --propellant-mass 0.04 --ignition-s 0.2 --plotsimulate prints the trajectory as a table, CSV or JSON, with an optional ASCII altitude chart and an apogee/impact summary. Add --diameter-m and --cd to fly against the ballistics-engine atmosphere and drag kernel, --wind for moving air, and --elevation-deg/--azimuth-deg/--speed-mps to aim the launch. rockets help lists every option.
Two analysis commands build on the same options:
dispersionruns Monte Carlo samples with lognormal Cd-scale, per-axis wind and fractional speed perturbations (--runs,--seed,--cd-sigma,--wind-sigma,--speed-sigma) and reports apogee/range statistics and impact CEP. Deterministic per seed; no external RNG dependency.solve-range <metres>bisects launch elevation to hit a downrange ground target on the low-angle branch (--high-anglefor the other solution) and prints the elevation, range, apogee and impact speed.
--stage builds serial or parallel staged vehicles from the terminal: each --stage dry=kg,prop=kg,curve=t:N;t:N,ignition=s,sep=s adds a stage with its own motor schedule and optional separation. --gravity central|vacuum and --space-above-m cover central-gravity and atmosphere/space-switching flights.
§Browser terminal
The same command surface runs at rockets.rs/sh/, compiled to WebAssembly. src/wasm.rs is a thin binding over the shared parser in src/command.rs, so the native and browser builds accept identical commands; scripts/build-wasm.sh rebuilds the module.
§Basic use
use rockets_engine::{
ControlInput, FlightState, IntegratorSettings, Motor, Rocket,
Simulator, ThrustCurve, ThrustPoint, UniformGravity, Vec3,
};
let curve = ThrustCurve::new(vec![
ThrustPoint { time_s: 0.0, thrust_n: 0.0 },
ThrustPoint { time_s: 0.1, thrust_n: 20.0 },
ThrustPoint { time_s: 0.9, thrust_n: 15.0 },
ThrustPoint { time_s: 1.0, thrust_n: 0.0 },
])?;
let motor = Motor::new(curve, 0.04, 0.2)?; // 40 g propellant; ignition at t=0.2 s
let rocket = Rocket::new(0.18, Some(motor))?; // Dry mass includes the motor casing
let simulator = Simulator::new(
rocket,
UniformGravity::default(), // Gravity only; see the atmospheric adapter below
IntegratorSettings::default(),
)?;
let state = FlightState {
time_s: 0.0,
position_m: Vec3::new(0.0, 2.0, 0.0),
velocity_mps: Vec3::new(2.0, 8.0, 0.0),
};
let result = simulator.step(
&state,
0.05,
ControlInput::along(Vec3::new(0.3, 1.0, 0.0)),
)?;
assert_eq!(result.state.time_s, 0.05);To use the same aerodynamic force kernel as the game, replace UniformGravity with BallisticForces:
use rockets_engine::{ballistics::{Atmosphere, BallisticForces}, Vec3};
let forces = BallisticForces::new(
0.025, // Diameter in metres
vec![(0.0, 0.45), (0.8, 0.5), (1.2, 0.7), (3.0, 0.4)], // Actual Mach/Cd pairs
Atmosphere::default(),
Vec3::new(2.0, 0.0, 0.0), // Air velocity: positive X is a tailwind
)?;§Model and units
- All values use SI units: metres, seconds, kilograms, newtons and m/s². Time is simulation time. Any game time compression belongs outside this crate.
- For local flight the fixed right-handed frame is X downrange, Y up, Z lateral. With
BallisticForces, position Y is absolute altitude above sea level; the weather station has its own altitude. Launch elevation is encoded in velocity and thrust direction, not a rotating coordinate frame. - For planet-scale flight, integrate in an Earth-centered frame with
CentralGravity: position measured from Earth’s center, inverse-square gravity in both magnitude and direction.icbm_midcourseshows the pattern. The flat-frame models (UniformGravity,BallisticForces) are invalid at intercontinental ranges;CentralGravityis invalid (singular) at its own center and carries no atmosphere. - Thrust is piecewise linear in time since ignition. Curves must start at zero time, have at least two samples, strictly increase in time and have positive total impulse. Nonzero endpoint thrust is supported.
- Fuel consumption is proportional to delivered impulse. If
I(t)is accumulated impulse,I_totalis total impulse andmpis initial propellant mass, remaining fuel ismp × (1 − I(t)/I_total). This assumes constant effective exhaust velocityI_total/mp; the prescribed thrust already includes exhaust/pressure effects. There is no separate motor pressure compensation. - Mass is computed analytically at every RK4 stage rather than numerically integrating and clamping an independent fuel state. Dry mass is positive and fuel cannot become negative. At burnout thrust is zero and the empty motor remains attached.
- A
Rocketis a base mass plus any number ofStages, each with its own motor schedule and an optional separation time. Total thrust sums over attached burning motors, so serial and parallel (strap-on) staging share one model. At separation the stage’s dry mass, motor and unspent propellant leave the vehicle; separating a burning stage (hot staging) ends its thrust.Rocket::newkeeps the 0.1 single-motor shape. - Ignition, every curve knot, burnout and every stage separation split the integration grid. RK4 uses the appropriate one-sided mass and force at discontinuities: a step closing on a separation still sees the departing stage, and the next step opens without it, including burns shorter than the maximum timestep.
- The default maximum internal step is 0.01 seconds, sized for motor burns. In vacuum coast (midcourse) raise it — seconds per step is plenty under central gravity; the Kepler-invariant tests integrate orbits at 1–2 s steps. This is fixed-step RK4, not an adaptive solver; choose a timestep suitable for the forces and confirm convergence for new vehicle models.
This baseline follows the thrust-curve, propellant-mass, gravity and drag structure illustrated by NASA’s model rocket simulation. The force model is consistent with NASA’s rocket thrust equation.
§Guidance and other forces
Guidance::command(GuidanceContext) returns a ControlInput. A closure can implement the hook directly. The context contains the current flight state, motor output, mass, requested duration and configured steering limit.
- Guidance is called once per
step_guidedcall. Its command is held constant during that call’s internal RK4 stages and substeps. thrust_directionis normalized. It describes externally supplied orientation; it does not model attitude, angular velocity, gimbal travel or turning rate.FixedDirectionprovides a simple unguided orientation.steering_acceleration_mps2is a world-frame acceleration supplied by an external actuator/control model. The solver caps its vector magnitude usingmax_steering_accel_mps2, which defaults to zero. It may act during coast; its physical availability and direction are the caller’s responsibility.- The core has no target, seeker, interception or Godot entity types. Controllers can own their own state and inputs without changing the integrator.
ForceModel::accelerationsupplies environmental acceleration at each RK4 stage with that stage’s current mass. A closure can implement this hook too. Keep it deterministic and free of side effects.BallisticForces::acceleration_in_windsupports wrappers that sample spatial or time-dependent wind.
§Guidance and constraint composition
ProgradeDirectionfollows the velocity vector (gravity turns), with a fallback axis at rest.ThrustMisalignmentdecorates any guidance source with a fixed tilt/clock boresight error — the dominant real-world dispersion term for unguided rockets.SteeringLagadds a first-order actuator time constant to steering acceleration; its lagged state is snapshot-able for replay.RailForceswraps any force model and projects environmental acceleration onto a launch rail until exit; command the thrust axis along the rail and no steering until exit, then let tip-off take over.BoxedGuidance/BoxedForcesare type-erased handles for heterogeneous decorator/model chains.with_atmosphere_zonesandwith_wind_segmentsonBallisticForcesexpose the upstream downrange-segmented atmosphere and wind ladders, evaluated statelessly at each stage’s downrange position.
The atmospheric adapter uses the body’s own Cd-versus-Mach curve and updates both SI mass and the engine’s imperial mass mirror. It includes gravity and drag; bullet-specific reference-BC and postprocessing effects (Litz spin drift) are disabled. Standard atmosphere delegates to the upstream altitude model: humidity affects density, while this derivative path uses dry-air sound speed. Uniform atmosphere obeys the upstream direct-mode limits (0 ≤ density < 2 kg/m³, sound speed > 200 m/s); a zero density retains gravity but removes drag. UniformGravity(Vec3::zeros()) gives an inertial vacuum.
§Reduced-order dynamics (weathercock and roll)
Weathercock is a second-order body-axis model in the guidance layer: the axis relaxes toward the relative wind with natural frequency ωₙ² = q·A·Cnα·(CP−CG)/I and damping 2ζωₙ, and the filtered axis becomes the commanded thrust axis. Positive static margin stabilizes; negative margin diverges. WeathercockParams takes the Cnα and CP Mach tables, a two-point CG model (dry + propellant references, mass-weighted as propellant burns), diameter, scalar pitch inertia and damping ratio. Axis and rate are snapshot-able state, updated once per public step, so choose the cadence well below the natural period. Pair with with_body_lift to close the loop through angle of attack. CLI: --weathercock with --cna, --cp, --cg-dry-m, --cg-prop-m, --pitch-inertia, --damping-ratio.
RollDynamics is the scalar roll channel: fin-cant drive against roll damping, with an explicit equilibrium rate. Feed the resulting effective twist to BallisticForces::set_twist between public steps (pair with with_spin for length and handedness). Still not 6-DOF: no quaternion, no inertia tensor, no gyroscopic coupling.
§Modified point-mass
BallisticForces climbs one rung above point-mass through three opt-in builders, all evaluated in-loop by the upstream kernel:
with_spin(twist_rate_in, body_length_m, right_hand)— Magnus force and yaw of repose for a spinning body.twist_rate_inis rifling language (inches per turn); for a fin-canted rocket it is the roll rate the cant produces per unit velocity. The correlations are McCoy bullet shapes, so treat magnitudes as approximate for blunt or finned bodies; signs and scaling are physical.with_coriolis(latitude_deg, shot_azimuth_deg)— rotating-frame deflection for planet-scale arcs, exact−2Ω×v. Do not combine with an inertial-frameCentralGravitymodel, which already accounts for Earth rotation by not rotating.with_tipoff_yaw(yaw_rad, decay_distance_m)— rail tip-off: an initial yaw decaying exponentially with downrange distance, perturbing the early angle of attack.
The CLI exposes the same surface: --twist-in/--body-length-m/--twist-left, --coriolis lat:az, --tipoff yaw:decay.
§Angle-of-attack aerodynamics
with_body_lift(cna_vs_mach, induced_k_vs_mach) adds quasi-static body lift without integrating attitude. The solver now hands every RK stage’s commanded thrust axis to the force model through ForceModel::acceleration_with_axis (a provided method — orientation-blind models are bit-identical and existing implementations keep working). The adapter measures the angle of attack between that axis and the wind-relative velocity and adds:
- Normal force
N = q·A·Cnα(Mach)·α, perpendicular to the relative wind on the axis side — a guided turn at angle of attack turns harder because the airframe works. - Induced drag
Cd += k(Mach)·α²— the same turn costs speed.
Cnα is per radian (body + fins), k per radian squared; both are piecewise-linear Mach tables needing two or more ascending points. Reference area derives from the diameter. CLI: --cna mach:cna,... --lift-k mach:k,.... This is still prescribed-attitude point-mass — the axis is commanded, not earned; trim, weathercock lag and stability margins belong to a future reduced-order layer.
The upstream ICAO atmosphere is defined to about 80 km; do not fly BallisticForces above it. Wrap it in AltitudeSwitch with a vacuum model above a cutoff (UniformGravity for flat-frame flight, CentralGravity for Earth-centered arcs) when a trajectory leaves the atmosphere.
§Save, replay and host integration
Simulator does not hold evolving flight state. Each step returns a new FlightState, end-of-step motor/mass telemetry and its internal step count. An error leaves the caller’s state untouched. Invalid numeric inputs and non-finite results return errors, and each call has a bounded substep budget.
The default serde feature serializes FlightState and MotorPhase. To resume a flight, restore the state with the same motor schedule, vehicle, environment and solver settings. Motor fuel/phase are derived from simulation time, so they cannot drift apart in a snapshot. Save any stateful guidance controller separately. Invalid deserialized states are rejected when stepped. A controller’s mutations are not rolled back if integration fails after the guidance call.
For deterministic replay, retain the same fixed public update cadence, control inputs and environment history. Internal grids are anchored to each public step; changing how calls are partitioned can change floating-point results. Cross-platform bit-for-bit identity is not promised.
For Counterbattery, a future CbBallistics adapter can repeatedly call step to precompute an unguided powered arc, or call step_guided at the simulation cadence for live control. Convert world coordinates once at that boundary, especially absolute altitude and side-facing X. The host must detect terrain and unit collisions over each returned movement interval; the library does not stop on a ground plane. No game integration is included in this crate.
§Scope of 0.2
The engine models translation with prescribed motor burns across serial or parallel stages, in flat or Earth-centered frames. It does not yet model throttle or restart within a stage, launch rails, changing drag geometry, body attitude, lift coefficients, actuator dynamics, seekers, Earth rotation (Coriolis and launch-site velocity are the caller’s responsibility in an ECI frame), orbit perturbations such as J2, or terrain collision localization. Guidance hooks provide the boundary for future control models; they are not a built-in autopilot.
§Validation
The tests check the ideal rocket equation and analytic position in vacuum, RK4 convergence, delayed ignition, nonzero endpoint thrust, short burns, zero-thrust intervals, fuel bounds, wind/mass/drag response, control limits, invalid inputs, work limits, and snapshot replay. Unpowered trajectories are compared against the upstream RK4 solver across multiple vehicle, wind and atmosphere configurations. Staging tests cover exact mass drops at separation, hot-staging thrust overlap, inter-stage coast phases, parallel boosters and two-burn Δv against Tsiolkovsky — including a regression for one-sided mass at a step closing exactly on a separation. Central-gravity tests assert Kepler invariants: a circular orbit closes to millimetres and an ICBM-scale midcourse ellipse conserves specific energy and angular momentum to better than 1 part in 10¹⁰. These checks establish numerical behavior and compatibility, not empirical validation of a particular real motor or airframe.
cargo fmt --all -- --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-features
cargo test --locked --no-default-features
cargo test --locked --no-default-features --features ballistics
cargo test --locked --no-default-features --features serdeballistics enables the companion adapter and its ballistics-engine dependency. serde enables snapshot serialization. Disabling both leaves the integrator, motor, guidance and custom force hooks usable on their own.
Licensed under MIT; see the repository’s LICENSE file.
Modules§
- ballistics
- Adapter to ballistics-engine 0.48’s public derivative kernel.
Structs§
- Altitude
Switch - Routes to one of two force models depending on altitude (position Y).
- Boxed
Forces - Owned, type-erased force-model handle for heterogeneous model composition.
A newtype rather than
Box<dyn ForceModel>so it cannot collide with the blanket closure implementations. - Boxed
Guidance - Owned, type-erased guidance handle for heterogeneous decorator chains.
A newtype rather than
Box<dyn Guidance>so it cannot collide with the blanket closure implementations. - Central
Gravity - Inverse-square gravity toward a fixed center, for planet-scale flight.
- Control
Input - World-frame controls held constant for one caller-requested simulation step.
- Fixed
Direction - Unguided orientation: thrust always follows the specified fixed world axis. This does not simulate passive body alignment or attitude dynamics.
- Flight
State - Complete translational state. Motor phase and fuel are derived from time and configuration.
- Guidance
Context - Snapshot supplied once to guidance for each
crate::Simulator::step_guidedcall. - Integrator
Settings - Integration accuracy, work budget and external control authority.
- Motor
- A single, prescribed motor burn with constant effective exhaust velocity.
- Motor
Sample - Instantaneous motor output, derived entirely from configuration and simulation time.
- Prograde
Direction - Passive gravity-turn orientation: thrust follows the current velocity vector. At rest (or at an exact apex with zero horizontal speed) the fallback axis is used until there is a measurable direction of motion.
- Rail
Forces - Launch-rail constraint: a force-model wrapper that projects environmental acceleration onto the rail direction while the vehicle is on the rail.
- Rocket
- Base structural mass plus an ordered set of stages.
- Rocket
Sample - Motor output and total current vehicle mass.
- Roll
Dynamics - Scalar roll channel: roll rate driven by fin cant and opposed by roll
damping. Drive torque = q·A·d·Clδ·δ, damping torque = q·A·d²·Clp·ω/(2V).
Equilibrium rate = 2·Clδ·δ·V/(d·Clp). Feed the resulting effective twist
(
speed / rate, in inches per turn) toBallisticForces::set_twistbetween public steps. - Simulator
- Stateless RK4 stepper over an immutable vehicle, force model and settings.
- Stage
- One separable stage: structural mass carried until a scheduled separation, plus an optional motor with its own burn schedule.
- Steering
Lag - First-order actuator lag on the steering acceleration, as a stateful
guidance decorator. The thrust axis passes through untouched; the steering
vector relaxes toward the inner command with time constant
tau_susing an exact discrete updatecurrent += (target - current)·(1 − exp(−dt/τ)). Save/restoreSelf::currentalongside the flight state for replay. - Step
Result - Result of a successful advancement. An error never overwrites the caller’s input state.
- Thrust
Curve - Validated, piecewise-linear thrust curve with exact piecewise-quadratic impulse.
- Thrust
Misalignment - A fixed thrust-axis misalignment, applied to any inner guidance command.
Models the small, constant boresight error that is the dominant dispersion
source for unguided rockets:
tilt_radis the angular offset magnitude andclock_radits orientation about the commanded axis. Zero tilt is bit-identical to the inner command. - Thrust
Point - One measured or synthetic thrust sample, relative to motor ignition.
- Uniform
Gravity - Constant gravity with no atmosphere. Use a zero vector for an inertial vacuum.
- Weathercock
- Second-order weathercock: the body axis relaxes toward the relative wind with natural frequency ωₙ² = q·A·Cnα·(CP−CG)/I and exponential damping 2ζωₙ. Positive static margin (CP aft of CG) is stable; negative margin diverges, as it should. The axis and rate are snapshot-able state, updated once per public step with an explicit first-order-in-dt scheme, so choose the public cadence well below the axis’s natural period.
- Weathercock
Params - Aerodynamic and inertial parameters for the weathercock filter.
Enums§
- Error
- Checked construction, integration and user-supplied model errors.
- Motor
Phase - Motor lifecycle. A spent motor stays attached and contributes to dry mass.
Constants§
- EARTH_
MU_ M3_ S2 - Standard gravitational parameter of Earth, in cubic metres per second squared.
Traits§
- Force
Model - Environmental acceleration hook (gravity, drag, wind, etc.), without motor or controls.
- Guidance
- Application-owned controller hook, independent of entities, targets and render timing.