Blueprints

Couple navigation to the physics stack

Navigation, chemistry, aero and control are entries in one ordered stage tuple executed after every marcher step. Composition is static, so there is no dyn dispatch and no scheduler.

Coupling::between_stepsPhysicsStageTrajectoryNavCoupledField

Coupling::between_steps() builds a cons-tuple of PhysicsStage values that becomes the loop body after every marcher step. The return type is impl PhysicsStage<2, FloatType>.

From the corridor’s coupling stack, examples/avionics_examples/src/shared/world.rs:159-210:

    Coupling::between_steps()
        .then(
            VibrationalLagStage::new(
                utils::ft(T_VE_INITIAL),
                utils::ft(FALLBACK_PRESSURE_ATM),
                utils::ft(REDUCED_MASS_AMU),
                utils::ft(THETA_VIB),
                utils::ft(SHEATH_PEAK_AGE_S),
            )
            .with_pressure_field("pressure_atm"),
        )
        .then(
            FiniteRateIonizationStage::new(utils::ft(FALLBACK_N_TOT))
                .with_density_field("n_tot")
                .with_sheath_renewal(utils::ft(SHEATH_PEAK_AGE_S)),
        )
        .then(FreestreamFeeds)
        .then(RegimeClassify::new(utils::ft(L_CHAR), utils::trigger()))
        .then(
            BankSteeredLift::new(
                utils::ft(RHO_REF),
                utils::ft(CDA_OVER_M),
                utils::ft(L_OVER_D),
            )
            .with_speed_field("equivalent_airspeed"),
        )
        .then(SuttonGravesLoads)
        .then(TruthGnss { noise_draw })
        .then(
            TrajectoryNav::new(
                core::array::from_fn(|i| utils::ft(Q_DIAG[i])),
                utils::ft(GNSS_VAR),
                utils::ft(OPTICAL_VAR),
            )
            .with_imu(imu),
        )
        .then(CommandedBank)
        .then(WeatherTelemetry)
        .then(CyberneticCorrect::new(SafetyEnvelope::new(
            utils::ft(MAX_HEAT_FLUX),
            utils::ft(MAX_G_LOAD),
            utils::ft(MAX_BANK_RAD),
        )))
        .build()

Cadence

TrajectoryNav runs one ReentryNavEngine predict and one ESKF correct per entry. Because it sits in the same tuple as the fluid and chemistry stages, the filter advances at the marcher’s step cadence rather than in a separate loop.

Ordering

Stage order is load-bearing in two places.

TruthGnss publishes the fix that TrajectoryNav consumes on the same step. Reversing the two would fold the previous step’s fix.

CyberneticCorrect runs last, so it clamps what the guidance stage wrote. It carries the SafetyEnvelope limits on heat flux, g-load and bank angle.

State handoff

Stages communicate through CoupledField. initial_field() seeds the engine that TrajectoryNav then threads through the march (world.rs:215-242):

field.set_nav(ReentryNavEngine::new(...))

The ImuModel passed to .with_imu(imu) scales the accelerometer bias away from the filter’s standard-day priors. The weather example varies that departure across six atmospheres and measures the effect on dead-reckoning drift.

Composition cost

PhysicsStage is implemented for () and for the (A, B) cons-tuple (deep_causality_cfd/src/types/flow/coupling.rs:298-335). The stack is a compile-time type, so stage dispatch is static and adding a stage costs no indirection.

powered_descent_coupling (world.rs:271-286) is the same stack extended with the burn stages.

Worked examples using this