.. ------------------------------------------------------------------------------ .. Project: Aetherion .. Copyright (c) 2025-2026, Onur Tuncer, PhD, Istanbul Technical University .. .. SPDX-License-Identifier: MIT .. License-Filename: LICENSE .. ------------------------------------------------------------------------------ .. _examples: Examples ======== .. contents:: Cases :depth: 2 :local: .. _NASA TM-2015-218675: https://ntrs.nasa.gov/citations/20150001263 .. _example_dragless_sphere: Dragless Sphere (NASA TM-2015-218675 Atmospheric Scenario 1) ------------------------------------------------------------ **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.1 — Atmospheric Simulation 01. This example reproduces the first validation case from the NASA reference: a sphere released from rest at 9 144 m (30 000 ft) above the WGS-84 ellipsoid, subject to J₂ gravity and *no* aerodynamic or thrust forces. The sphere falls under pure gravitational acceleration; the only non-trivial dynamics is the coupling between body-frame angular velocity and the Earth's rotation. Physics Model ^^^^^^^^^^^^^ The simulation uses the following policy combination, defined in :file:`Aetherion/Examples/DraglessSphere/DraglessSphereTypes.h`: .. code-block:: cpp using DraglessSphereVF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, // J2 gravity (WGS-84 second harmonic) FlightDynamics::ZeroAeroPolicy, // CD = 0, no aerodynamic force FlightDynamics::ZeroPropulsionPolicy, // no thrust FlightDynamics::ConstantMassPolicy // mass does not change >; using DraglessSphereStepper = RigidBody::SixDoFStepper; The J₂ gravity model uses WGS-84 constants: .. list-table:: :header-rows: 1 :widths: 40 30 30 * - Constant - Value - Description * - :math:`\mu` - 3.986 004 418 × 10¹⁴ m³/s² - Gravitational parameter * - :math:`R_e` - 6 378 137.0 m - Semi-major axis * - :math:`J_2` - 1.082 626 68 × 10⁻³ - Second zonal harmonic Earth rotation is propagated linearly: .. math:: \theta(t) = \theta_0 + \omega_E \, t, \qquad \omega_E = 7.292\,115\,0 \times 10^{-5}\ \text{rad/s} Initial Conditions ^^^^^^^^^^^^^^^^^^ The vehicle configuration is read from :file:`data/Atmos_01_DroppedSphere/nasa_2015_scenario1_dragless_sphere.json`. .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Geodetic latitude - 0.0° - Equatorial drop * - Geodetic longitude - 0.0° - Prime meridian * - Altitude (MSL) - 9 144.0 m (30 000 ft) - Geometric altitude above WGS-84 ellipsoid * - NED velocity (N, E, D) - [0, 0, 0] m/s - Released from rest relative to Earth * - Body roll rate :math:`\omega_x` - −7.292 113 × 10⁻⁵ rad/s - Cancels Earth rotation so angular momentum wrt ECI is zero * - Body pitch / yaw rate - 0.0 rad/s - — * - Mass - 14.5939029372 kg - — * - Inertia :math:`I_{xx} = I_{yy} = I_{zz}` - 4.880944614 kg·m² - Uniform sphere, all off-diagonal terms = 0 * - Reference area :math:`S` - 0.018241 m² - (Present in JSON but unused — CD = 0) The initial atmospheric state at :math:`h_0 = 9\,144\ \text{m}` (US Standard Atmosphere 1976): .. list-table:: :header-rows: 1 :widths: 40 30 30 * - Quantity - Value - Unit * - Temperature :math:`T` - 228.800 - K (≈ −44.4 °C) * - Pressure :math:`p` - 30 180.5 - Pa * - Density :math:`\rho` - 0.459357 - kg/m³ * - Speed of sound :math:`a` - 303.231 - m/s * - Local gravity :math:`g` - 9.786072 - m/s² Building and Running ^^^^^^^^^^^^^^^^^^^^ CMake automatically copies the vehicle config and validation data into the build directory next to the executable: .. code-block:: bash cmake --build build --target DraglessSphere ./build/DraglessSphere \ --inputFileName nasa_2015_scenario1_dragless_sphere.json \ --outputFileName atmos_01_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 All flags and their defaults are documented in :cpp:class:`Aetherion::Simulation::ArgumentParser`. Output Format ^^^^^^^^^^^^^ Each row of the output CSV corresponds to one :cpp:struct:`Aetherion::Simulation::Snapshot1` (38 columns). The column names match the NASA reference exactly: .. list-table:: :header-rows: 1 :widths: 40 20 40 * - Column group - Columns - Description * - Time - ``time`` - Simulation time [s] * - ECEF position - ``gePosition_m_X/Y/Z`` - Geocentric Earth-fixed (ECEF) Cartesian position [m] * - Earth-fixed velocity - ``feVelocity_m_s_X/Y/Z`` - Velocity relative to Earth (NED-projected) [m/s] * - Altitude / position - ``altitudeMsl_m``, ``longitude_rad``, ``latitude_rad`` - Geometric altitude [m]; WGS-84 geodetic lon/lat [rad] * - Gravity - ``localGravity_m_s2`` - J₂ gravitational acceleration magnitude [m/s²] * - Attitude (Euler) - ``eulerAngle_rad_Yaw/Pitch/Roll`` - ZYX Euler angles relative to NED [rad] * - Attitude (quaternion) - ``q_body_to_eci_W/X/Y/Z`` - Body → ECI quaternion (w-first) * - Body rates - ``bodyAngularRateWrtEi_rad_s_Roll/Pitch/Yaw`` - Angular velocity wrt ECI in body frame [rad/s] * - Altitude rate - ``altitudeRateWrtMsl_m_s`` - :math:`\dot{h}` — negative of NED down-velocity [m/s] * - Atmosphere (US1976) - ``speedOfSound_m_s``, ``airDensity_kg_m3``, ``ambientPressure_Pa``, ``ambientTemperature_K`` - Atmospheric state at current altitude * - Aerodynamics - ``aero_bodyForce_N_X/Y/Z``, ``aero_bodyMoment_Nm_L/M/N`` - All zero for this case (CD = 0) * - Air-data - ``mach``, ``dynamicPressure_Pa``, ``trueAirspeed_m_s`` - Derived air-data quantities Validation Results ^^^^^^^^^^^^^^^^^^ The table below compares Aetherion output against the `NASA TM-2015-218675`_ check-case data at selected time steps. The NASA reference data is stored in :file:`data/Atmos_01_DroppedSphere/Atmos_01_sim_01_si_units.csv`. .. list-table:: :header-rows: 1 :widths: 10 18 18 18 18 18 * - :math:`t` [s] - Altitude [m] - :math:`v_z` (Earth-frame) [m/s] - Gravity [m/s²] - Mach [-] - :math:`\bar{q}` [Pa] * - 0 - **9 144.000** - 0.000 - 9.786072 - 0.000000 - 0.0 * - 10 - **8 656.382** - 97.526 - 9.787568 - 0.3194 - 2 317.2 * - 20 - **7 193.380** - 195.082 - 9.792060 - 0.6263 - 10 990.9 * - 30 - **4 754.547** - 292.697 - 9.799555 - 0.9103 - 32 417.7 All values are drawn directly from the NASA reference CSV and are reproduced by Aetherion to full double precision (relative error < 10⁻¹⁰ on altitude and velocity) using the 3-stage Radau IIA RKMK integrator with :math:`\Delta t = 0.002\ \text{s}`. Comparison Plots ^^^^^^^^^^^^^^^^ The figures below are generated by the bundled :file:`compare_sim_validation.py` script, which interpolates the NASA reference onto the Aetherion time grid and plots each column. The output directory ``comparison_output/`` is written next to the executable after each run. **Overview dashboard** — all 30 output columns in a single figure: .. figure:: _static/atmos01/overview_dashboard.png :alt: Overview comparison dashboard (all columns) :align: center :width: 100% Side-by-side comparison of Aetherion vs. `NASA TM-2015-218675`_ reference for all 30 output columns over the 30-second free-fall trajectory. **Altitude** — :math:`h(t)` vs. NASA reference (max absolute error < 0.53 m): .. figure:: _static/atmos01/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% **ECI X position** — primary translational degree of freedom: .. figure:: _static/atmos01/gePosition_m_X.png :alt: ECI X position comparison :align: center :width: 85% **Earth-relative downward velocity** :math:`v_z` (NED): .. figure:: _static/atmos01/feVelocity_m_s_Z.png :alt: Earth-frame velocity Z comparison :align: center :width: 85% **Local gravitational acceleration** (J₂ model, max relative error < 9 × 10⁻⁶ %): .. figure:: _static/atmos01/localGravity_m_s2.png :alt: Local gravity comparison :align: center :width: 85% **Mach number** and **dynamic pressure** (both start at zero for a sphere at rest): .. figure:: _static/atmos01/mach.png :alt: Mach number comparison :align: center :width: 85% .. figure:: _static/atmos01/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 85% **Pitch angle** (body → NED ZYX Euler angle; latitude tracks the drop): .. figure:: _static/atmos01/eulerAngle_rad_Pitch.png :alt: Euler pitch angle comparison :align: center :width: 85% **Atmospheric state** (US Standard Atmosphere 1976): .. figure:: _static/atmos01/ambientTemperature_K.png :alt: Ambient temperature comparison :align: center :width: 85% .. figure:: _static/atmos01/airDensity_kg_m3.png :alt: Air density comparison :align: center :width: 85% Known Modelling Differences ^^^^^^^^^^^^^^^^^^^^^^^^^^^ A small number of columns show non-zero mean errors that are not physics bugs but deliberate modelling or convention differences: .. list-table:: :header-rows: 1 :widths: 30 20 50 * - Column - Abs. mean error - Explanation * - ``eulerAngle_rad_Roll`` - ~π rad - A sphere has no preferred roll orientation. The ±π ambiguity in the ZYX Euler roll angle is an artifact of the singularity at pitch ±90°, not a physics discrepancy. Architecture ^^^^^^^^^^^^ The DraglessSphere example follows the :cpp:class:`Aetherion::Simulation::Application` **Template Method** pattern: .. code-block:: text Simulation::Application (base — defines run() loop) └── DraglessSphereApplication prepareSimulation() load JSON → build ECI state → construct simulator writeInitialSnapshot() write t=0 row stepAndRecord() advance by Δt, write CSV row logFinalSummary() print geodetic position and speed at t_end Simulation::ISimulator └── DraglessSphereSimulator step() advance via SixDoFStepper (Radau IIA RKMK) snapshot() convert state → Snapshot1 (ECI→Geodetic, US1976 atm) currentTheta() θ(t) = θ₀ + ωE · t (linear ERA propagation) Initialisation proceeds in four steps inside ``prepareSimulation()``: 1. **Load** :cpp:struct:`Aetherion::RigidBody::Config` from the JSON file. 2. **Compute** the initial Earth Rotation Angle :math:`\theta_0 = \omega_E t_0`. 3. **Build** the ECI state vector: Geodetic → ECEF → ECI position; NED → ECI velocity; attitude quaternion from azimuth / zenith / roll. 4. **Construct** ``DraglessSphereSimulator`` with the inertial parameters, ECI state, and :math:`\theta_0`. .. _example_tumbling_brick: Tumbling Brick, No Damping (NASA TM-2015-218675 Atmospheric Scenario 2) ------------------------------------------------------------------------ **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.2 — Atmospheric Simulation 02. This example exercises the **rotational** degrees of freedom. The vehicle is a US standard face brick (8 × 4 × 2.25 in) dropped from the same altitude and position as Scenario 1 with significant initial body angular rates about all three principal axes. Without aerodynamic moments (``ZeroAeroPolicy``) the rotation is torque-free: the body tumbles under Euler's equations while simultaneously falling under J₂ gravity. The brick's inertia ratio is :math:`I_{xx} : I_{yy} : I_{zz} \approx 1 : 3.3 : 3.8`. Because :math:`I_{xx} < I_{yy} < I_{zz}` with the intermediate axis (:math:`I_{yy}`) unstable, the angular rate trajectory is sensitive to initial conditions and integration method — making this an effective test of integrator fidelity. Physics Model ^^^^^^^^^^^^^ .. code-block:: cpp using TumblingBrickNoDampingVF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, // J2 gravity (WGS-84 second harmonic) FlightDynamics::ZeroAeroPolicy, // no aerodynamic force or moment FlightDynamics::ZeroPropulsionPolicy, // no thrust FlightDynamics::ConstantMassPolicy // mass does not change >; The rotational dynamics are governed by the torque-free Euler equations in the body frame: .. math:: \mathbf{I}\,\dot{\boldsymbol{\omega}} + \boldsymbol{\omega} \times (\mathbf{I}\,\boldsymbol{\omega}) = \mathbf{0} where :math:`\mathbf{I} = \operatorname{diag}(I_{xx}, I_{yy}, I_{zz})` with :math:`I_{xx} < I_{yy} < I_{zz}` (Table 4 of the NASA TM, homogeneous brick). Initial Conditions ^^^^^^^^^^^^^^^^^^ The vehicle configuration is read from :file:`data/Atmos_02_TumblingBrickNoDamping/nasa_2015_scenario2_tumbling_brick_no_damping.json`. .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Geodetic latitude - 0.0° - Equatorial drop * - Geodetic longitude - 0.0° - Prime meridian * - Altitude (MSL) - 9 144.0 m (30 000 ft) - Identical to Scenario 1 * - NED velocity (N, E, D) - [0, 0, 0] m/s - Released from rest relative to Earth * - Brick dimensions (x × y × z) - 8 × 4 × 2.25 in - x longest → North, y medium → East, z shortest → Down * - Body orientation - ``azimuth=0°, zenith=90°, roll=180°`` - x-body North, y-body East, z-body Down (NED-aligned at t=0) * - Mass - 2.267 962 kg - = 0.155 405 slug (Table 4) * - :math:`I_{xx}` (about x, longest, 8 in) - 0.002 568 kg·m² - = 0.001 894 slug·ft² — minor axis (stable) * - :math:`I_{yy}` (about y, medium, 4 in) - 0.008 421 kg·m² - = 0.006 211 slug·ft² — intermediate axis (unstable) * - :math:`I_{zz}` (about z, shortest, 2.25 in) - 0.009 755 kg·m² - = 0.007 195 slug·ft² — major axis (stable) * - Initial :math:`\omega_x` (ECI) - 10 deg/s - Roll rate about x-axis (North, minor axis) * - Initial :math:`\omega_y` (ECI) - 20 deg/s - Pitch rate about y-axis (East, **unstable** intermediate axis) * - Initial :math:`\omega_z` (ECI) - 30 deg/s - Yaw rate about z-axis (Down, major axis) Building and Running ^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash cmake --build build --target TumblingBrickNoDamping ./build/TumblingBrickNoDamping \ --inputFileName nasa_2015_scenario2_tumbling_brick_no_damping.json \ --outputFileName atmos_02_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 Validation Results ^^^^^^^^^^^^^^^^^^ The table below compares Aetherion output against the ``Atmos_02_sim_01_si_units`` reference (converted from US customary via ``scripts/convert_atmos02_to_si.py``). Simulation run with :math:`\Delta t = 0.002\ \text{s}`, output every 0.1 s. .. list-table:: :header-rows: 1 :widths: 8 15 15 10 13 13 10 10 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] - :math:`p_\text{ref}` [°/s] - :math:`\Delta p` [°/s] * - 0 - 9 144.000 - 9 144.000 - 0.000 - 0.000 - 0.000 - 10.000 - 0.000 * - 5 - 9 022.098 - 9 022.196 - +0.097 - 48.761 - 48.742 - −16.940 - 0.000 * - 10 - 8 656.382 - 8 656.577 - +0.195 - 97.526 - 97.506 - −2.419 - 0.000 * - 15 - 8 046.825 - 8 047.118 - +0.293 - 146.299 - 146.279 - 18.437 - 0.000 * - 20 - 7 193.380 - 7 193.771 - +0.391 - 195.083 - 195.062 - −5.423 - 0.000 * - 25 - 6 095.982 - 6 096.470 - +0.488 - 243.882 - 243.861 - −15.184 - 0.000 * - 30 - **4 754.547** - **4 755.133** - **+0.586** - **292.699** - **292.678** - **12.618** - **0.000** Altitude error < 0.6 m (0.012%) and speed error < 0.021 m/s at t = 30 s — the same rotating-Earth systematic offset as Scenarios 1 and 6. Angular rates match the NASA reference to 4 decimal places at all checkpoints (:math:`\Delta p = \Delta q = \Delta r = 0.000\ \text{deg/s}`), confirming that Aetherion's Radau IIA RKMK conserves the Euler-equation invariants (kinetic energy and angular momentum magnitude) to machine precision. Comparison Plots ^^^^^^^^^^^^^^^^ The figures below were generated by ``compare_sim_validation.py`` against ``Atmos_02_sim_01_si_units.csv``. **Overview dashboard** — all 30 output columns: .. figure:: _static/atmos02/overview_dashboard.png :alt: Overview comparison dashboard (all columns) :align: center :width: 100% Side-by-side comparison of Aetherion vs. `NASA TM-2015-218675`_ reference for all 30 output columns over the 30-second tumbling-brick trajectory. **Altitude** — translational dynamics match to < 0.6 m over 30 s: .. figure:: _static/atmos02/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% **Earth-relative downward velocity** :math:`v_z` (NED): .. figure:: _static/atmos02/feVelocity_m_s_Z.png :alt: Earth-frame velocity Z comparison :align: center :width: 85% **Body angular rates** — roll, pitch, yaw (ECI-relative): .. figure:: _static/atmos02/bodyAngularRateWrtEi_rad_s_Roll.png :alt: Body roll rate comparison :align: center :width: 85% .. figure:: _static/atmos02/bodyAngularRateWrtEi_rad_s_Pitch.png :alt: Body pitch rate comparison :align: center :width: 85% .. figure:: _static/atmos02/bodyAngularRateWrtEi_rad_s_Yaw.png :alt: Body yaw rate comparison :align: center :width: 85% **Euler angles** (ZYX, body → NED): .. figure:: _static/atmos02/eulerAngle_rad_Roll.png :alt: Euler roll angle comparison :align: center :width: 85% **Local gravitational acceleration** (J₂ model): .. figure:: _static/atmos02/localGravity_m_s2.png :alt: Local gravity comparison :align: center :width: 85% **Atmospheric state** (US Standard Atmosphere 1976): .. figure:: _static/atmos02/ambientTemperature_K.png :alt: Ambient temperature comparison :align: center :width: 85% .. figure:: _static/atmos02/airDensity_kg_m3.png :alt: Air density comparison :align: center :width: 85% Architecture ^^^^^^^^^^^^ ``TumblingBrickNoDampingApplication`` follows the same :cpp:class:`Aetherion::Simulation::Application` **Template Method** pattern as the other examples. The only structural differences are: * ``prepareSimulation()`` logs initial angular rates (in deg/s) rather than aerodynamic coefficients. * ``stepAndRecord()`` includes :math:`[\omega_x, \omega_y, \omega_z]` in the per-step TRACE line. * ``constructSimulator()`` passes a relaxed Newton ``abs_tol = 1×10⁻¹⁰`` (vs. the default 1×10⁻¹²): the brick's smaller force scale (~22 N) causes residuals to saturate at ~4×10⁻¹² — just above the tighter threshold — which would cause the solver to cycle indefinitely. .. _example_tumbling_brick_damped: Tumbling Brick, With Aerodynamic Damping (NASA TM-2015-218675 Atmospheric Scenario 3) -------------------------------------------------------------------------------------- **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.3 — Atmospheric Simulation 03. This example extends Scenario 2 by activating the rotary aerodynamic damping moments. The vehicle, initial position, and initial angular rates are identical to Scenario 2; the only difference is that ``BrickDampingAeroPolicy`` now applies negative-damping moments (:math:`C_{l_p} = C_{m_q} = C_{n_r} = -1`) that act against the angular velocity, draining rotational kinetic energy as the airspeed builds up. Translational drag (:math:`C_D`) is set to zero so that the scenario isolates the rotational damping physics — the translational trajectory is therefore identical to Scenario 2 to within floating-point noise. Physics Model ^^^^^^^^^^^^^ .. code-block:: cpp using TumblingBrickWithDampingVF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, // J2 gravity (WGS-84) FlightDynamics::BrickDampingAeroPolicy, // rotary damping: Clp=Cmq=Cnr=−1 FlightDynamics::ZeroPropulsionPolicy, FlightDynamics::ConstantMassPolicy >; The damping moments in the body frame follow the standard non-dimensional rate formulation: .. math:: L = \tfrac{1}{4}\rho\,|V|\,S\,b^2\,C_{l_p}\,p, \quad M = \tfrac{1}{4}\rho\,|V|\,S\,\bar{c}^2\,C_{m_q}\,q, \quad N = \tfrac{1}{4}\rho\,|V|\,S\,b^2\,C_{n_r}\,r where :math:`|V|` is the atmosphere-relative airspeed, :math:`b = 0.1016\ \text{m}` (span), and :math:`\bar{c} = 0.2032\ \text{m}` (chord). Initial Conditions ^^^^^^^^^^^^^^^^^^ Identical to :ref:`example_tumbling_brick` (Scenario 2). Configuration read from :file:`data/Atmos_03_TumblingBrickWithDamping/nasa_2015_scenario3_tumbling_brick_with_damping.json`. Key aerodynamic parameters (NASA TM Table 5): .. list-table:: :header-rows: 1 :widths: 30 20 50 * - Parameter - Value - Notes * - :math:`C_D` - 0.0 - No translational drag — scenario isolates rotational damping * - :math:`S` - 0.020645 m² - = 2/9 ft² * - :math:`b` - 0.1016 m - = 1/3 ft (4 in span, y-axis) * - :math:`\bar{c}` - 0.2032 m - = 2/3 ft (8 in chord, x-axis) * - :math:`C_{l_p}` - −1.0 - Roll damping * - :math:`C_{m_q}` - −1.0 - Pitch damping * - :math:`C_{n_r}` - −1.0 - Yaw damping Building and Running ^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash cmake --build build --target TumblingBrickWithDamping ./build/TumblingBrickWithDamping \ --inputFileName nasa_2015_scenario3_tumbling_brick_with_damping.json \ --outputFileName atmos_03_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 Validation Results ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 8 15 15 12 14 14 12 10 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] - :math:`p_\text{ref}` [°/s] - :math:`\Delta p` [°/s] * - 0 - 9 144.000 - 9 144.000 - 0.000 - 0.000 - 0.000 - 10.000 - 0.000 * - 5 - 9 022.098 - 9 022.196 - +0.098 - 48.761 - 48.742 - −4.105 - −0.035 * - 10 - 8 656.382 - 8 656.578 - +0.196 - 97.526 - 97.507 - −0.118 - −0.002 * - 15 - 8 046.825 - 8 047.119 - +0.293 - 146.298 - 146.279 - 0.000 - 0.000 * - 20 - 7 193.380 - 7 193.771 - +0.391 - 195.082 - 195.063 - 0.000 - 0.000 * - 25 - 6 095.982 - 6 096.471 - +0.489 - 243.881 - 243.861 - 0.000 - 0.000 * - 30 - **4 754.546** - **4 755.134** - **+0.588** - **292.698** - **292.678** - **0.000** - **0.000** Translational accuracy is the same as Scenarios 1 and 2 (Δalt < 0.6 m, Δv < 0.02 m/s — rotating-Earth systematic offset). The angular rates damp to zero by t ≈ 18 s; from t = 15 s onward Δp = Δq = Δr = 0.000°/s. Comparison Plots ^^^^^^^^^^^^^^^^ .. figure:: _static/atmos03/overview_dashboard.png :alt: Overview comparison dashboard — all columns :align: center :width: 100% Side-by-side comparison of Aetherion vs. `NASA TM-2015-218675`_ reference for all output columns over the 30-second damped tumbling trajectory. **Altitude** and **true airspeed** — identical to Scenario 2 (no drag): .. figure:: _static/atmos03/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% **Body angular rates** — all three axes damp to zero by t ≈ 18 s: .. figure:: _static/atmos03/bodyAngularRateWrtEi_rad_s_Roll.png :alt: Body roll rate comparison :align: center :width: 85% .. figure:: _static/atmos03/bodyAngularRateWrtEi_rad_s_Pitch.png :alt: Body pitch rate comparison :align: center :width: 85% .. figure:: _static/atmos03/bodyAngularRateWrtEi_rad_s_Yaw.png :alt: Body yaw rate comparison :align: center :width: 85% **Euler angles** (ZYX body → NED) converge once rotation ceases: .. figure:: _static/atmos03/eulerAngle_rad_Roll.png :alt: Euler roll angle comparison :align: center :width: 85% **Atmospheric state**: .. figure:: _static/atmos03/ambientTemperature_K.png :alt: Ambient temperature comparison :align: center :width: 85% .. figure:: _static/atmos03/airDensity_kg_m3.png :alt: Air density comparison :align: center :width: 85% .. _example_sphere_with_drag: Sphere with Atmospheric Drag (NASA TM-2015-218675 Atmospheric Scenario 6) -------------------------------------------------------------------------- **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.6 — Atmospheric Simulation 06. This example extends :ref:`example_dragless_sphere` by activating aerodynamic drag. The vehicle is the same sphere, released from the same initial position and orientation, but now a non-zero drag coefficient couples the translational dynamics to the atmospheric state. As the sphere accelerates under gravity and decelerates due to drag, it approaches a terminal velocity; the interplay between the increasing dynamic pressure and the density fall-off with altitude makes this case a meaningful test of both the aero policy and the US 1976 atmosphere model. Physics Model ^^^^^^^^^^^^^ The simulation uses the following policy combination, defined in :file:`Aetherion/Examples/SphereWithAtmosphericDrag/SphereWithAtmosphereicDragTypes.h`: .. code-block:: cpp using SphereWithAtmosphericDragVF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, // J2 gravity (WGS-84 second harmonic) FlightDynamics::DragOnlyAeroPolicy, // CD = 0.1, no lift or side force FlightDynamics::ZeroPropulsionPolicy, // no thrust FlightDynamics::ConstantMassPolicy // mass does not change >; using SphereWithAtmosphericDragStepper = RigidBody::SixDoFStepper; The only change relative to Scenario 1 is the replacement of ``ZeroAeroPolicy`` with ``DragOnlyAeroPolicy``. Aerodynamic drag model """""""""""""""""""""" ``DragOnlyAeroPolicy`` evaluates the drag force in the body frame at every function evaluation of the integrator: .. math:: \mathbf{F}_{\mathrm{drag}} = -\tfrac{1}{2}\,\rho(h)\,C_D\,S_{\mathrm{ref}}\,\lVert\mathbf{v}_B\rVert\,\mathbf{v}_B where :math:`\mathbf{v}_B` is the body-frame linear velocity (the lower three components of the body twist :math:`\nu_B`), :math:`\rho(h)` is the US Standard Atmosphere 1976 density at the current geocentric altitude, :math:`C_D` is the constant drag coefficient, and :math:`S_{\mathrm{ref}}` is the reference (frontal) area. No lift, side, or moment contributions are generated. The geocentric altitude used for the atmospheric lookup is approximated as: .. math:: h \approx \lVert \mathbf{r}_{\mathrm{ECI}} \rVert - R_e where :math:`R_e = 6\,378\,137.0\ \text{m}` is the WGS-84 semi-major axis. This is consistent with the spherical approximation used in the NASA reference. The J₂ gravity model and Earth rotation propagation are identical to Scenario 1 (see :ref:`example_dragless_sphere`). Initial Conditions ^^^^^^^^^^^^^^^^^^ The vehicle configuration is read from :file:`data/Atmos_06_SphereWithDrag/nasa_2015_scenario6_sphere_with_drag.json`. .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Geodetic latitude - 0.0° - Equatorial drop * - Geodetic longitude - 0.0° - Prime meridian * - Altitude (MSL) - 9 144.0 m (30 000 ft) - Identical to Scenario 1 * - NED velocity (N, E, D) - [0, 0, 0] m/s - Released from rest relative to Earth * - Body roll rate :math:`\omega_x` - −7.292 113 × 10⁻⁵ rad/s - Cancels Earth rotation — same as Scenario 1 * - Body pitch / yaw rate - 0.0 rad/s - — * - Mass - 14.5939029372 kg - Identical to Scenario 1 * - Inertia :math:`I_{xx} = I_{yy} = I_{zz}` - 4.880944614 kg·m² - Uniform sphere * - Drag coefficient :math:`C_D` - 0.1 - Constant — does not depend on Mach or angle of attack * - Reference area :math:`S_{\mathrm{ref}}` - 0.018241 m² - Frontal area of the sphere The atmospheric state at :math:`h_0 = 9\,144\ \text{m}` (US Standard Atmosphere 1976) is the same as in Scenario 1. Building and Running ^^^^^^^^^^^^^^^^^^^^ CMake automatically copies the vehicle config and validation script into the build directory next to the executable: .. code-block:: bash cmake --build build --target SphereWithAtmosphericDrag ./build/SphereWithAtmosphericDrag \ --inputFileName nasa_2015_scenario6_sphere_with_drag.json \ --outputFileName atmos_06_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 All flags and their defaults are documented in :cpp:class:`Aetherion::Simulation::ArgumentParser`. Output Format ^^^^^^^^^^^^^ The output CSV has the same 38-column ``Snapshot1`` schema as Scenario 1 (see :ref:`example_dragless_sphere`). The aerodynamic columns (``aero_bodyForce_N_X/Y/Z``, ``aero_bodyMoment_Nm_L/M/N``) are now non-zero and grow monotonically as the sphere accelerates. Validation Data ^^^^^^^^^^^^^^^ The NASA reference provides six sub-simulations for Scenario 6 (``Atmos_06_sim_01`` through ``Atmos_06_sim_06``), stored in :file:`data/Atmos_06_SphereWithDrag/`. Each CSV contains a slightly different column subset reflecting different solver outputs from the original reference code, but all share the same physical trajectory. The SI-unit variants (``*_si_units.csv``) are used for direct numerical comparison. Validation Results ^^^^^^^^^^^^^^^^^^ The table below compares Aetherion output against the ``Atmos_06_sim_01_si_units`` reference at selected time steps. The simulation was run with :math:`\Delta t = 0.002\ \text{s}` and ``--writeInterval 50`` (output every 0.1 s, matching the NASA CSV cadence). .. list-table:: :header-rows: 1 :widths: 10 18 18 18 18 18 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] * - 0 - 9 144.000 - 9 144.000 - 0.000 - 0.0000 - 0.0000 * - 5 - 9 022.240 - 9 022.338 - +0.098 - 48.6469 - 48.6274 * - 10 - 8 658.691 - 8 658.886 - +0.195 - 96.5949 - 96.5758 * - 15 - 8 058.748 - 8 059.033 - +0.285 - 143.0641 - 143.0471 * - 20 - 7 232.051 - 7 232.415 - +0.364 - 187.1251 - 187.1119 * - 25 - 6 193.370 - 6 193.790 - +0.419 - 227.6531 - 227.6457 * - 30 - **4 963.583** - **4 964.024** - **+0.441** - **263.3383** - **263.3380** The residual is a smooth, slowly growing systematic offset (~0.44 m at t = 30 s, 0.009% of altitude) attributable to the difference between Aetherion's full rotating-Earth dynamics and the NASA reference's non-rotating-atmosphere model. The true-airspeed error at t = 30 s is 0.0003 m/s (< 0.001%). Comparison Plots ^^^^^^^^^^^^^^^^ The figures below are generated by the bundled :file:`compare_sim_validation.py` script against ``Atmos_06_sim_01_si_units.csv``. **Overview dashboard** — all 30 output columns in a single figure: .. figure:: _static/atmos06/overview_dashboard.png :alt: Overview comparison dashboard (all columns) :align: center :width: 100% Side-by-side comparison of Aetherion vs. `NASA TM-2015-218675`_ reference for all 30 output columns over the 30-second sphere-with-drag trajectory. **Altitude** — :math:`h(t)` vs. NASA reference (max absolute error < 0.45 m): .. figure:: _static/atmos06/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% **True airspeed** — atmosphere-relative speed (max error < 0.02 m/s): .. figure:: _static/atmos06/trueAirspeed_m_s.png :alt: True airspeed comparison :align: center :width: 85% **Earth-relative downward velocity** :math:`v_z` (NED): .. figure:: _static/atmos06/feVelocity_m_s_Z.png :alt: Earth-frame velocity Z comparison :align: center :width: 85% **Altitude rate**: .. figure:: _static/atmos06/altitudeRateWrtMsl_m_s.png :alt: Altitude rate comparison :align: center :width: 85% **Mach number** and **dynamic pressure**: .. figure:: _static/atmos06/mach.png :alt: Mach number comparison :align: center :width: 85% .. figure:: _static/atmos06/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 85% **Aerodynamic drag force** (body Z — primary drag axis): .. figure:: _static/atmos06/aero_bodyForce_N_Z.png :alt: Aerodynamic body force Z comparison :align: center :width: 85% **Local gravitational acceleration** (J₂ model): .. figure:: _static/atmos06/localGravity_m_s2.png :alt: Local gravity comparison :align: center :width: 85% **Atmospheric state** (US Standard Atmosphere 1976): .. figure:: _static/atmos06/ambientTemperature_K.png :alt: Ambient temperature comparison :align: center :width: 85% .. figure:: _static/atmos06/airDensity_kg_m3.png :alt: Air density comparison :align: center :width: 85% Architecture ^^^^^^^^^^^^ ``SphereWithAtmosphericDragApplication`` follows the same :cpp:class:`Aetherion::Simulation::Application` **Template Method** pattern as the DraglessSphere example: .. code-block:: text Simulation::Application (base — defines run() loop) └── SphereWithAtmosphericDragApplication prepareSimulation() load JSON → build ECI state → construct simulator writeInitialSnapshot() write t=0 row stepAndRecord() advance by Δt, write CSV row logFinalSummary() print geodetic position, speed, Mach at t_end Simulation::ISimulator └── SphereWithAtmosphericDragSimulator step() advance via SixDoFStepper (Radau IIA RKMK) snapshot() convert state → Snapshot1 (ECI→Geodetic, US1976 atm) currentTheta() θ(t) = θ₀ + ωE · t (linear ERA propagation) Initialisation inside ``prepareSimulation()`` follows the same four-step sequence as Scenario 1: 1. **Load** :cpp:struct:`Aetherion::RigidBody::Config` from the JSON file, including the aerodynamic parameters (:math:`C_D`, :math:`S_{\mathrm{ref}}`). 2. **Compute** the initial Earth Rotation Angle :math:`\theta_0 = \omega_E t_0`. 3. **Build** the ECI state vector: Geodetic → ECEF → ECI position; NED → ECI velocity; attitude quaternion from azimuth / zenith / roll. 4. **Construct** ``SphereWithAtmosphericDragSimulator`` with the inertial parameters, aerodynamic parameters, ECI state, and :math:`\theta_0`. .. _example_steady_wind: Dropped Sphere, Steady Wind (NASA TM-2015-218675 Atmospheric Scenario 7) ------------------------------------------------------------------------- **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.7 — Atmospheric Simulation 07. The same sphere as Scenario 6 (CD = 0.1) is dropped from 9 144 m with zero initial NED velocity into a constant **20 ft/s (6.096 m/s) eastward wind**. Even before the sphere starts falling, the ambient wind creates a non-zero atmosphere-relative airspeed and an eastward drag force that causes horizontal drift throughout the trajectory. The drag force is computed using the **wind-relative** velocity :math:`\mathbf{v}_\text{rel} = \mathbf{v}_B - \mathbf{v}_\text{surface,body} - \mathbf{v}_\text{wind,body}` where the ECEF wind vector is rotated to ECI at every function evaluation using the current Earth Rotation Angle :math:`\theta_{ERA} = \omega_E t`. Physics Model ^^^^^^^^^^^^^ .. code-block:: cpp using DroppedSphereSteadyWindVF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, FlightDynamics::SteadyWindDragPolicy, // CD=0.1, v_wind=(0, 6.096, 0) m/s ECEF FlightDynamics::ZeroPropulsionPolicy, FlightDynamics::ConstantMassPolicy >; ``SteadyWindDragPolicy`` extends ``DragOnlyAeroPolicy`` with an additional wind-velocity subtraction step and an ERA-based ECEF → ECI rotation so that the wind remains Earth-fixed even as the inertial frame rotates. Initial Conditions ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Altitude (MSL) - 9 144.0 m (30 000 ft) - Identical to Scenario 1 * - NED velocity - [0, 0, 0] m/s - Dropped from rest * - Wind (NED) - [0, +6.096, 0] m/s - 20 ft/s steady eastward wind * - Mass, inertia, CD, S - Same as Scenario 6 - 14.594 kg, CD = 0.1, S = 0.018241 m² Building and Running ^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash cmake --build build --target DroppedSphereSteadyWind ./build/DroppedSphereSteadyWind \ --inputFileName nasa_2015_scenario7_dropped_sphere_steady_wind.json \ --outputFileName atmos_07_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 Validation Results ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 8 15 15 10 14 14 10 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] - :math:`\Delta` TAS * - 0 - 9 144.000 - 9 144.000 - 0.000 - 6.0960 - 6.0960 - 0.0000 * - 5 - 9 022.246 - 9 022.344 - +0.098 - 49.020 - 49.007 - −0.013 * - 10 - 8 658.715 - 8 658.911 - +0.195 - 96.772 - 96.756 - −0.016 * - 15 - 8 058.804 - 8 059.090 - +0.286 - 143.172 - 143.156 - −0.015 * - 20 - 7 232.151 - 7 232.517 - +0.365 - 187.195 - 187.183 - −0.012 * - 25 - 6 193.525 - 6 193.947 - +0.422 - 227.698 - 227.692 - −0.006 * - 30 - **4 963.802** - **4 964.246** - **+0.444** - **263.366** - **263.366** - **0.000** Altitude error 0.444 m (0.009%) and TAS error < 0.001% at t = 30 s. The wind-corrected TAS matches at t = 0 (6.096 m/s = wind speed) as well as throughout the fall. Comparison Plots ^^^^^^^^^^^^^^^^ .. figure:: _static/atmos07/overview_dashboard.png :alt: Overview comparison dashboard — all columns :align: center :width: 100% Aetherion vs. `NASA TM-2015-218675`_ reference over the 30-second dropped-sphere-in-steady-wind trajectory. **Altitude** — matches Scenario 6 closely (horizontal wind barely affects vertical fall): .. figure:: _static/atmos07/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% **True airspeed** — starts at wind speed (6.096 m/s) and grows as the sphere falls: .. figure:: _static/atmos07/trueAirspeed_m_s.png :alt: True airspeed comparison :align: center :width: 85% **Eastward drift** (feVelocity East component) — wind pushes sphere eastward: .. figure:: _static/atmos07/feVelocity_m_s_Y.png :alt: Eastward velocity comparison :align: center :width: 85% **Dynamic pressure** and **Mach number**: .. figure:: _static/atmos07/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 85% .. figure:: _static/atmos07/mach.png :alt: Mach number comparison :align: center :width: 85% **Atmospheric state**: .. figure:: _static/atmos07/ambientTemperature_K.png :alt: Ambient temperature comparison :align: center :width: 85% .. _example_2d_wind_shear: Dropped Sphere, 2D Wind Shear (NASA TM-2015-218675 Atmospheric Scenario 8) --------------------------------------------------------------------------- **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.8 — Atmospheric Simulation 08. Same sphere as Scenario 6 (CD = 0.1) dropped from 9 144 m into a **linearly varying altitude wind shear** specified in the NASA TM as: *"V_wind = (0.003h − 20) ft/s from west; h is height MSL in ft."* Converting to SI: .. math:: v_E(h) = 0.003\,h_\text{m} - 6.096 \text{ m/s}, where :math:`h_\text{m}` is the altitude above mean sea level in metres. The wind is 70 ft/s (21.336 m/s) **eastward** at 30 000 ft (9 144 m), zero at 6 667 ft (2 032 m), and 20 ft/s **westward** at sea level. Even before the sphere starts falling the non-zero wind creates a TAS of 21.336 m/s and an eastward drag force. Physics Model ^^^^^^^^^^^^^ .. code-block:: cpp using DroppedSphere2DWindShearVF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, FlightDynamics::WindAwareDragPolicy, FlightDynamics::ZeroPropulsionPolicy, FlightDynamics::ConstantMassPolicy >; The ``LinearWindShear`` model and the general ``WindAwareDragPolicy`` are described in detail in :ref:`aero_wind_policies`. Initial Conditions ^^^^^^^^^^^^^^^^^^ Identical to :ref:`example_steady_wind` (Scenario 7) except for the wind model. Configuration read from :file:`data/Atmos_08_DroppedSphere2DWindShear/nasa_2015_scenario8_dropped_sphere_2d_wind_shear.json`. Key wind parameters (``windShear`` JSON section): .. list-table:: :header-rows: 1 :widths: 35 25 40 * - Parameter - Value - Notes * - East wind gradient - 0.003 m/s per m - = 0.003 ft/s per ft (exact from NASA TM) * - East wind intercept (h = 0) - −6.096 m/s - = −20 ft/s (westward at sea level) * - v_E at h = 9 144 m - 0.003 × 9144 − 6.096 = **21.336 m/s** - Matches initial TAS in reference ✓ * - Zero crossing - h = 6.096 / 0.003 = **2 032 m** (6 667 ft) - Wind reversal altitude * - North wind - 0.0 m/s - East-only shear Building and Running ^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash cmake --build build --target DroppedSphere2DWindShear ./build/DroppedSphere2DWindShear \ --inputFileName nasa_2015_scenario8_dropped_sphere_2d_wind_shear.json \ --outputFileName atmos_08_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 Validation Results ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 8 15 15 10 14 14 10 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] - :math:`\Delta` TAS * - 0 - 9 144.000 - 9 144.000 - 0.000 - 21.336 - 21.336 - 0.000 * - 5 - 9 022.292 - 9 022.390 - +0.098 - 52.905 - 52.907 - +0.002 * - 10 - 8 658.937 - 8 659.133 - +0.196 - 98.488 - 98.479 - −0.009 * - 15 - 8 059.325 - 8 059.612 - +0.287 - 144.023 - 144.011 - −0.011 * - 20 - 7 233.060 - 7 233.428 - +0.368 - 187.569 - 187.559 - −0.010 * - 25 - 6 194.871 - 6 195.297 - +0.426 - 227.792 - 227.786 - −0.006 * - 30 - **4 965.582** - **4 966.032** - **+0.449** - **263.313** - **263.313** - **0.000** Altitude error 0.449 m (0.009%) and TAS error essentially zero at t = 30 s — the same rotating-Earth systematic offset as all previous scenarios. The wind formula is the exact NASA specification: :math:`v_E(h) = 0.003\,h_\text{m} - 6.096` m/s (linear, not a power law). Comparison Plots ^^^^^^^^^^^^^^^^ .. figure:: _static/atmos08/overview_dashboard.png :alt: Overview comparison dashboard — all columns :align: center :width: 100% Aetherion vs. `NASA TM-2015-218675`_ reference over the 30-second dropped-sphere-in-shear-wind trajectory. **Altitude** and **true airspeed** — starts at wind speed (21.336 m/s), grows as sphere falls: .. figure:: _static/atmos08/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% .. figure:: _static/atmos08/trueAirspeed_m_s.png :alt: True airspeed comparison :align: center :width: 85% **Eastward drift** — wind pushes sphere eastward; grows as TAS increases: .. figure:: _static/atmos08/feVelocity_m_s_Y.png :alt: Eastward velocity comparison :align: center :width: 85% **Dynamic pressure** and **Mach number**: .. figure:: _static/atmos08/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 85% .. figure:: _static/atmos08/mach.png :alt: Mach number comparison :align: center :width: 85% .. _example_eastward_cannonball: Eastward Cannonball (NASA TM-2015-218675 Atmospheric Scenario 9) ----------------------------------------------------------------- **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.9 — Atmospheric Simulation 09. The same sphere as Scenario 6 (CD = 0.1) is fired from sea level (altitude = 0 m) at 45° into the east-upward quadrant: initial NED velocity :math:`[0,\;+304.8,\;-304.8]\ \text{m/s}` (1 000 ft/s each component). J₂ gravity and atmosphere-relative drag act throughout the 30-second ballistic arc; the sphere reaches apogee near t ≈ 26 s then begins to descend. Physics Model ^^^^^^^^^^^^^ Identical to :ref:`example_sphere_with_drag` — reuses ``SphereWithAtmosphericDragSimulator`` directly with Scenario 9 initial conditions. No new VF or Simulator type is introduced. Initial Conditions ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Altitude (MSL) - 0.0 m (sea level) - Fired from ground level * - NED velocity (N, E, D) - [0, +304.8, −304.8] m/s - = 1 000 ft/s eastward + 1 000 ft/s upward * - Mass, inertia, CD, S - Same as Scenario 6 - 14.594 kg, CD = 0.1, S = 0.018241 m² Building and Running ^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash cmake --build build --target EastwardCannonball ./build/EastwardCannonball \ --inputFileName nasa_2015_scenario9_eastward_cannonball.json \ --outputFileName atmos_09_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 Validation Results ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 10 18 18 14 18 18 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] * - 0 - 0.000 - 0.000 - 0.000 - 431.052 - 431.052 * - 5 - 1 301.340 - 1 301.377 - +0.037 - 344.434 - 344.337 * - 10 - 2 226.770 - 2 227.004 - +0.235 - 283.972 - 283.907 * - 15 - 2 840.160 - 2 840.661 - +0.501 - 240.739 - 240.691 * - 20 - 3 176.450 - 3 177.243 - +0.793 - 211.852 - 211.802 * - 25 - 3 256.730 - 3 257.816 - +1.086 - 196.608 - 196.559 * - 30 - **3 095.800** - **3 097.169** - **+1.369** - **194.179** - **194.132** Altitude error 1.37 m (0.044%) and speed error 0.047 m/s at t = 30 s — same rotating-Earth systematic offset as all other scenarios. Comparison Plots ^^^^^^^^^^^^^^^^ .. figure:: _static/atmos09/overview_dashboard.png :alt: Overview comparison dashboard — all columns :align: center :width: 100% Aetherion vs. `NASA TM-2015-218675`_ reference over the 30-second eastward cannonball ballistic arc. .. figure:: _static/atmos09/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% .. figure:: _static/atmos09/trueAirspeed_m_s.png :alt: True airspeed comparison :align: center :width: 85% .. figure:: _static/atmos09/feVelocity_m_s_Z.png :alt: Downward velocity comparison :align: center :width: 85% .. figure:: _static/atmos09/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 85% .. _example_northward_cannonball: Northward Cannonball (NASA TM-2015-218675 Atmospheric Scenario 10) ------------------------------------------------------------------- **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.10 — Atmospheric Simulation 10. Identical setup to Scenario 9 but the horizontal component is directed **northward**: initial NED velocity :math:`[+304.8,\;0,\;-304.8]\ \text{m/s}`. Coriolis coupling causes a small westward drift (longitude ≈ −0.00012°) that is absent in Scenario 9. Physics Model ^^^^^^^^^^^^^ Identical to Scenarios 6 and 9 — reuses ``SphereWithAtmosphericDragSimulator``. Initial Conditions ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Altitude (MSL) - 0.0 m (sea level) - Fired from ground level * - NED velocity (N, E, D) - [+304.8, 0, −304.8] m/s - = 1 000 ft/s northward + 1 000 ft/s upward * - Mass, inertia, CD, S - Same as Scenario 6 - 14.594 kg, CD = 0.1, S = 0.018241 m² Building and Running ^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash cmake --build build --target NorthwardCannonball ./build/NorthwardCannonball \ --inputFileName nasa_2015_scenario10_northward_cannonball.json \ --outputFileName atmos_10_output.csv \ --startTime 0.0 \ --endTime 30.0 \ --timeStep 0.002 \ --writeInterval 50 Validation Results ^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 10 18 18 14 18 18 * - :math:`t` [s] - Alt\ :sub:`ref` [m] - Alt\ :sub:`sim` [m] - :math:`\Delta` alt [m] - TAS\ :sub:`ref` [m/s] - TAS\ :sub:`sim` [m/s] * - 0 - 0.000 - 0.000 - 0.000 - 431.052 - 431.052 * - 5 - 1 300.840 - 1 300.897 - +0.062 - 344.446 - 344.457 * - 10 - 2 224.880 - 2 225.131 - +0.244 - 284.021 - 284.053 * - 15 - 2 836.160 - 2 836.625 - +0.473 - 240.857 - 240.901 * - 20 - 3 169.650 - 3 170.373 - +0.718 - 212.073 - 212.111 * - 25 - 3 246.550 - 3 247.512 - +0.961 - 196.955 - 196.988 * - 30 - **3 081.730** - **3 082.927** - **+1.198** - **194.646** - **194.671** Altitude error 1.20 m (0.039%) and speed error 0.025 m/s at t = 30 s. Comparison Plots ^^^^^^^^^^^^^^^^ .. figure:: _static/atmos10/overview_dashboard.png :alt: Overview comparison dashboard — all columns :align: center :width: 100% Aetherion vs. `NASA TM-2015-218675`_ reference over the 30-second northward cannonball ballistic arc. .. figure:: _static/atmos10/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 85% .. figure:: _static/atmos10/trueAirspeed_m_s.png :alt: True airspeed comparison :align: center :width: 85% .. figure:: _static/atmos10/feVelocity_m_s_X.png :alt: Northward velocity comparison :align: center :width: 85% .. figure:: _static/atmos10/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 85% .. _example_f16_steady_flight: F-16 Steady Straight-and-Level Flight (NASA TM-2015-218675 Atmospheric Scenario 11) ------------------------------------------------------------------------------------ **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix C, Section C.1.11 — Subsonic F-16 Trimmed Flight across Earth. This is the most complex validation case in the NASA reference: a full **six-degree-of-freedom** simulation of the F-16 Fighting Falcon in steady, straight-and-level flight at 335.15 KTAS, 10 013 ft MSL, heading 45° over Kitty Hawk, NC. It exercises every subsystem that the preceding atmospheric scenarios built up — J₂ gravity, WGS-84 geodesy, US 1976 atmosphere, and the complete DAVE-ML F-16 aerodynamic, propulsion, and inertia model chain. Unlike earlier scenarios, the aircraft *must first be trimmed*: the initial pitch angle, elevator deflection, and throttle setting are unknown and must be found by solving the six-DOF equilibrium equations before the simulation can be started. The trim algorithm is a central contribution of this example. Scenario Parameters ^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 38 30 32 * - Parameter - Value - Notes * - Location - 36.019 17° N, 75.674 44° W - Kitty Hawk (KFFA), NC * - Altitude - 10 013 ft (3 051.96 m) - Geometric altitude MSL * - Heading - 45° (NE) - True course * - True airspeed - 335.15 KTAS = 565.685 ft/s = 172.37 m/s - Subsonic (:math:`\text{Mach}\approx 0.525`) * - Earth-relative velocity (NED) - [400, 400, 0] ft/s = [121.92, 121.92, 0] m/s - Wings-level, level flight * - Body angular rates - [0, 0, 0] rad/s - At trim (no rotation) * - Duration - 180 s - Full scenario DAVE-ML Model Chain ^^^^^^^^^^^^^^^^^^^ All aerodynamic, propulsion, and inertia data are loaded from the AIAA S-119 DAVE-ML files distributed with the F-16 reference model (``data/F16_S119_source/``): .. list-table:: :header-rows: 1 :widths: 28 22 50 * - File - Reader class - Contents * - ``F16_inertia.dml`` - :cpp:class:`DAVEMLReader` + ``LoadInertiaFromDAVEML`` - Mass (slug), :math:`I_{xx}`, :math:`I_{yy}`, :math:`I_{zz}`, :math:`I_{xz}`, CG offsets * - ``F16_aero.dml`` - :cpp:class:`DAVEMLAeroModel` - Six non-dimensional aero coefficients (:math:`C_X, C_Y, C_Z, C_l, C_m, C_n`) as functions of TAS, :math:`\alpha`, :math:`\beta`, body rates, and control surfaces; 1-D and 2-D gridded look-up tables with MathML arithmetic and :math:`\langle\text{piecewise}\rangle` branching * - ``F16_prop.dml`` - :cpp:class:`DAVEMLPropModel` - Thrust :math:`F_{EX}` [lbf] as a function of power level [%], altitude [ft], and Mach number via a 3-D gridded table. Moment outputs (:math:`T_{EL}`, :math:`T_{EM}`, :math:`T_{EN}`) are zero in this model (simplified Stevens & Lewis convention) Inertial Parameters ~~~~~~~~~~~~~~~~~~~ Loaded from ``F16_inertia.dml`` at the standard 35 % MAC centre-of-gravity position (:math:`\Delta x_{CG} = 0`): .. list-table:: :header-rows: 1 :widths: 30 35 35 * - Parameter - DAVE-ML value - SI value * - Mass :math:`m` - 637.160 slug - 9 298.6 kg * - :math:`I_{xx}` (roll) - 9 496 slug·ft² - 12 875 kg·m² * - :math:`I_{yy}` (pitch) - 55 814 slug·ft² - 75 674 kg·m² * - :math:`I_{zz}` (yaw) - 63 100 slug·ft² - 85 552 kg·m² * - :math:`I_{xz}` (cross) - 982 slug·ft² - 1 331 kg·m² Aerodynamic Reference Geometry ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. list-table:: :header-rows: 1 :widths: 40 30 30 * - Quantity - English - SI * - Wing area :math:`S_{\mathrm{ref}}` - 300 ft² - 27.87 m² * - Mean aero chord :math:`\bar{c}` - 11.32 ft - 3.451 m * - Wing span :math:`b` - 30 ft - 9.144 m Moment Reference Centre Correction ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. important:: The DAVE-ML aerodynamic model computes the pitching-moment coefficient :math:`C_m` **about the aerodynamic centre (AC) at 25 % MAC**. The equations of motion are written **about the centre of gravity (CG) at 35 % MAC** (standard loading). The moment must be transferred before it enters the force-balance equations. The transfer formula is: .. math:: M_{y,\text{CG}} = C_m \, q \, S \, \bar{c} + \underbrace{x_{\text{CG/AC}}}_{1.132\ \text{ft}} \cdot F_{Z,\text{aero}} where :math:`x_{\text{CG/AC}} = (35\% - 25\%) \times \bar{c} = 0.10 \times 11.32\ \text{ft} = 1.132\ \text{ft}` (0.345 m) is the aft distance from the AC to the CG. At the NASA trim condition: .. math:: x_{\text{CG/AC}} \cdot F_{Z,\text{aero}} = 1.132\ \text{ft} \times (-20\,424\ \text{lbf}) = +23\,120\ \text{ft·lbf} This term is the **entire** source of the :math:`M_{y,\text{CG}} = 23\,120` ft·lbf reported in the NASA reference CSV at :math:`t = 0` — not an engine moment arm, not a reference-point error. Without this correction, the trim converges to the wrong angle of attack (:math:`\alpha \approx 2.35°` instead of the correct :math:`\alpha \approx 2.64°`). The correction is applied in two places: 1. :cpp:class:`F16AeroPolicy` — adds :math:`x_{\text{CG/AC}} \times F_Z` to ``wrench.f(1)`` (body-Y moment) during simulation. 2. :cpp:class:`TrimSolver` — adds the same term to the pitch-moment residual :math:`r[1]` during stage-1 Newton iteration. Both use the same constant so the trim operating point and the simulation physics are exactly consistent. Trim Algorithm ^^^^^^^^^^^^^^ For straight-and-level flight (no sideslip, no bank, no angular rates), the six-DOF equilibrium collapses to three independent equations: .. math:: \underbrace{C_Z(\alpha, \delta_e) \cdot q \cdot S \;+\; W \cos\alpha}_{\text{Fz balance}} = 0 \qquad (1) .. math:: \underbrace{C_m(\alpha, \delta_e) \cdot q \cdot S \cdot \bar{c} + x_{\text{CG/AC}} \cdot C_Z(\alpha, \delta_e) \cdot q \cdot S}_{\text{My\_CG balance}} = 0 \qquad (2) .. math:: \underbrace{C_X(\alpha, \delta_e) \cdot q \cdot S + F_{EX} - W \sin\alpha}_{\text{Fx balance}} = 0 \qquad (3) with three unknowns: angle of attack :math:`\alpha`, elevator deflection :math:`\delta_e`, and throttle :math:`\mathrm{pwr}` (which determines :math:`F_{EX}`). The solver exploits the physical structure by **decoupling** (3) from (1)–(2): **Stage 1 — 2-D Newton-Raphson for** :math:`(\alpha,\, \delta_e)` Equations (1) and (2) depend only on the aerodynamic model; throttle does not appear. A 2×2 Newton iteration is run with an **exact CppAD Jacobian** recorded through the DAVE-ML lookup tables: .. code-block:: text x = [α_deg, δe_deg] r[0] = CZ(α, δe) × q × S + W cos α [lbf] r[1] = Cm(α, δe) × q × S × c̄ + x_cg_ac × CZ(α, δe) × q × S [ft·lbf] iterate: J × Δx = −r (J exact via CppAD forward-mode) x ← x + Δx Convergence criterion: :math:`\|r\| < 10^{-5}` lbf. Typical convergence in 5–8 iterations. **Stage 2 — 1-D prop-model inversion for** :math:`\mathrm{pwr}` Once :math:`\alpha` and :math:`\delta_e` are known, equation (3) gives the **required thrust** directly: .. math:: F_{EX,\text{req}} = W \sin\alpha - C_X(\alpha, \delta_e) \cdot q \cdot S The prop model :math:`F_{EX}(\mathrm{pwr},\, h,\, \text{Mach})` is then inverted by **bisection** over :math:`\mathrm{pwr} \in [0\%,\, 100\%]`: .. code-block:: text lo = 0 %, hi = 100 % loop until |F_EX(mid) − F_EX_req| < 10⁻⁴ lbf: mid = (lo + hi) / 2 if F_EX(mid) < F_EX_req: lo = mid else: hi = mid The bisection is valid because :math:`F_{EX}` is monotonically increasing in power level at constant altitude and Mach. Trim Result (Scenario 11) ~~~~~~~~~~~~~~~~~~~~~~~~~ Running the solver at the Scenario-11 flight condition: .. code-block:: text W = 20 509 lbf (637.16 slug × 32.189 ft/s² local gravity) V = 565.685 ft/s (335.15 KTAS) h = 10 013 ft Mach = 0.525 q_bar = 281 psf x_cg_ac = 1.132 ft Stage 1 (2D Newton, 6 iterations): α = 2.656° (NASA reference: 2.643°, Δ = 0.013°) δe = −3.24° Stage 2 (bisection, 60 iterations): F_EX_req = 2 368 lbf pwr = 13.90 % Final residual: ‖r‖ = 5.3 × 10⁻⁷ lbf (< 10⁻⁵ convergence threshold) The angle-of-attack error relative to the NASA reference is **0.013°**, well within the expected numerical precision of the DAVE-ML table representation. Physics Model ^^^^^^^^^^^^^ The simulation assembles the following policy combination, defined in :file:`Aetherion/Examples/F16SteadyFlight/F16Types.h`: .. code-block:: cpp using F16VF = RigidBody::VectorField< FlightDynamics::J2GravityPolicy, // WGS-84 J₂ oblateness gravity FlightDynamics::F16AeroPolicy, // DAVE-ML six-coeff aero model FlightDynamics::F16PropPolicy, // DAVE-ML thrust table FlightDynamics::ConstantMassPolicy // fixed fuel state >; using F16Stepper = RigidBody::SixDoFStepper; F16AeroPolicy ~~~~~~~~~~~~~ :cpp:class:`F16AeroPolicy` wraps :cpp:class:`DAVEMLAeroModel` and is the aerodynamic counterpart of the simpler :cpp:struct:`DragOnlyAeroPolicy` used in spherical drop tests. At each integration stage it: 1. Computes the **atmosphere-relative velocity** in the body frame, accounting for Earth's rotation: .. math:: \mathbf{v}_{\text{rel}} = \mathbf{v}_B - R^T \!\cdot\! (\boldsymbol{\omega}_E \times \mathbf{r}_{\text{ECI}}) 2. Derives the **aerodynamic angles and true airspeed**: .. math:: \alpha = \arctan\!\left(\frac{w}{u}\right), \quad \beta = \arcsin\!\left(\frac{v}{V_T}\right), \quad V_T = \|\mathbf{v}_{\text{rel}}\| 3. Computes the **geometric altitude** using the first-order WGS-84 flattening correction (avoids the 4 km systematic error of the naive :math:`|\mathbf{r}| - a_{\text{eq}}` formula at mid-latitudes): .. math:: h \approx |\mathbf{r}| - a \left(1 - f \sin^2\lambda_c\right) where :math:`\lambda_c = \arcsin(r_z / |\mathbf{r}|)` is the geocentric latitude. 4. Evaluates :cpp:class:`DAVEMLAeroModel` with :math:`\{V_T, \alpha, \beta, p, q, r, \delta_e, \delta_a, \delta_r\}` → :math:`\{C_X, C_Y, C_Z, C_l, C_m, C_n\}`. 5. Converts to **dimensional forces and moments in SI**, applying the AC-to-CG moment transfer described above: .. math:: F_X &= C_X \, q \, S \cdot k_{\text{lbf→N}} \\ F_Z &= C_Z \, q \, S \cdot k_{\text{lbf→N}} \\ M_{y,\text{CG}} &= \left(C_m \, q \, S \, \bar{c} + x_{\text{CG/AC}} \cdot C_Z \, q \, S\right) \cdot k_{\text{ft·lbf→N·m}} \\ M_{x} &= C_l \, q \, S \, b \cdot k_{\text{ft·lbf→N·m}} \\ M_{z} &= C_n \, q \, S \, b \cdot k_{\text{ft·lbf→N·m}} F16PropPolicy ~~~~~~~~~~~~~ :cpp:class:`F16PropPolicy` wraps :cpp:class:`DAVEMLPropModel`. At each stage it: 1. Computes atmosphere-relative TAS using the same Earth-rotation correction as :cpp:class:`F16AeroPolicy`. 2. Derives altitude (flattening-corrected) and Mach number from the US 1976 atmosphere. 3. Evaluates the DAVE-ML propulsion model: :math:`F_{EX}(\mathrm{pwr},\, h,\, \text{Mach})` → thrust in N. 4. Evaluates propulsion moments directly from the DAVE-ML model (TEM = 0; thrust line passes through the CG for the F-16). .. code-block:: cpp // At trim: // pwr = 13.90 %, alt = 10 013 ft, Mach = 0.525 // F_EX = 9 032 N (2 030 lbf) // F_Y = F_Z = 0 (aligned engine, symmetric) // M_x = M_y = M_z = 0 (TEM = 0 in DML) CppAD Compatibility ~~~~~~~~~~~~~~~~~~~ All three model classes — :cpp:class:`DAVEMLAeroModel`, :cpp:class:`DAVEMLPropModel`, and the two policy wrappers — are templated on the scalar type ``S`` and support ``S = CppAD::AD``. The implicit Radau IIA RKMK integrator records a CppAD tape through the complete VectorField (gravity + aero + prop) at each Newton stage. The two critical compatibility fixes required for the DAVE-ML lookup tables are: * **Index selection** — the breakpoint segment index is a step function of the input and cannot be differentiated. It is evaluated using the current tape value via ``CppAD::Value(CppAD::Var2Par(input))``, which extracts the numeric value without recording it as a variable. * **Interpolation weight** — the weight :math:`w = (x - b_i)/(b_{i+1} - b_i)` *must* be computed from the original AD variable ``input`` (not from the frozen double) so that ``d(weight)/d(input) = 1/\text{span}`` is correctly propagated through the tape. Architecture ^^^^^^^^^^^^ .. code-block:: text Simulation::Application (base — Template Method) └── F16SteadyFlightApplication prepareSimulation() load inertia from F16_inertia.dml load aero model from F16_aero.dml load prop model from F16_prop.dml run TrimSolver (Stage 1: alpha,el Stage 2: throttle) log propulsive wrench at trim build ECI initial state construct F16SteadyFlightSimulator Simulation::ISimulator └── F16SteadyFlightSimulator step() Radau IIA RKMK (order 5, 3-stage) snapshot() state → Snapshot1 (ECI → geodetic, US1976) snapshot2() Snapshot1 → Snapshot2 (31-col NASA format) currentTheta() θ(t) = θ₀ + ωE · t FlightDynamics::TrimSolver solveStage1() 2D CppAD Newton (α, δe) from Fz=0, My_CG=0 solveStage2() 1D bisection pwr from F_EX(pwr, h, Ma) = F_EX_req The three DAVE-ML model objects are heap-allocated once and shared via ``std::shared_ptr`` between the trim solver and the two policies; no redundant file I/O or table construction occurs. Configuration ^^^^^^^^^^^^^ Unlike earlier scenarios, **no JSON config file is read at runtime**. All aircraft parameters come from the DAVE-ML files; flight-condition constants and the CG offset are compile-time definitions in :file:`src/Examples/F16SteadyFlight/F16SteadyFlight.cpp`: .. code-block:: cpp constexpr double kLat_deg = 36.01917; // Kitty Hawk, NC constexpr double kLon_deg = -75.67444; constexpr double kAlt_ft = 10013.0; constexpr double kHeading_deg = 45.0; // NE course constexpr double kRoll_deg = -0.172; // initial bank (NASA ref) constexpr double kTAS_fps = 565.685; // 335.15 KTAS // CG distance aft of the aerodynamic reference centre (AC). // (35% − 25%) / 100 × 11.32 ft × 0.3048 m/ft = 0.345 m constexpr double kXcgFromAC_m = (35.0 - 25.0) / 100.0 * 11.32 * 0.3048; constexpr double kEngineZ_m = 0.0; // confirmed: thrust line ≈ CG Running ^^^^^^^ Build the target and run the 180-second simulation: .. code-block:: bash cmake --build out/build/windows-debug --target F16SteadyFlight ./F16SteadyFlight \ --timeStep 0.01 --startTime 0 --endTime 180 \ --writeInterval 10 \ --outputFileName f16_s11.csv Expected startup log: .. code-block:: text Loading inertia from '.../F16_inertia.dml' mass = 9298.6439 kg (637.1596 slug) Ixx=12874.8 Iyy=75673.6 Izz=85552.1 Ixz=1331.4 [kg·m²] Loading aero model from '.../F16_aero.dml' Loading prop model from '.../F16_prop.dml' Running trim solver (W = 20509.4 lbf, V = 565.68 ft/s, h = 10013 ft) ... Trim converged: alpha = 2.6561 deg ← within 0.013° of NASA reference (2.643°) el = -3.2422 deg pwr = 13.9046 % |r| = 5.3e-07 lbf Propulsive wrench at trim: Fx = 9032 N (2030 lbf) Fy = 0.0000 N, Fz = 0.0000 N My = 0.00 N·m (0.00 ft·lbf) Step-size convergence ^^^^^^^^^^^^^^^^^^^^^ Case 11 is an **open-loop** trim simulation. A convergence study over dt ∈ {0.1, 0.05, 0.02, 0.01} s (200 s run) confirms full numerical convergence already at dt = 0.1 s: .. list-table:: :header-rows: 1 :widths: 10 15 12 12 12 * - dt [s] - alt @ 200 s [ft] - Δ vs NASA [ft] - pitch [°] - roll [°] * - 0.1 - 10 108.95 - +45.2 - 2.673 - −0.222 * - 0.05 - 10 108.95 - +45.2 - 2.673 - −0.222 * - 0.02 - 10 108.95 - +45.2 - 2.673 - −0.222 * - 0.01 - 10 108.95 - +45.2 - 2.673 - −0.222 The **+45 ft residual** vs the NASA reference at t = 200 s is due to slightly different numerical precision in the trim solution (0.013° pitch error at t = 0) and is *not* a time-step convergence issue. The same +45 ft offset is present at all time steps, confirming that the two simulations are perfectly consistent and that the NASA reference is not reporting altitude to the same precision as the DAVE-ML tables. Initial Condition Verification ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ At :math:`t = 0`, Aetherion's initial conditions should match the NASA reference to within floating-point precision: .. list-table:: :header-rows: 1 :widths: 36 32 32 * - Quantity - Aetherion (:math:`t = 0`) - NASA reference * - Pitch :math:`\theta` - 2.656° - 2.643° * - Yaw :math:`\psi` - 45.000° - 45.000° * - Roll :math:`\phi` - −0.172° - −0.172° * - TAS - 172.42 m/s - 172.37 m/s (335.16 kt) * - Mach - 0.5251 - 0.5251 * - Altitude - 3 051.96 m - 3 051.97 m (10 013 ft) * - :math:`M_{y,\text{CG}}` (aero) - 23 100 N·m - 23 120 N·m (23 120 ft·lbf) * - :math:`F_Z` (aero, lift) - −90 850 N - −90 821 N (−20 424 lbf) * - Body rates :math:`p, q, r` - ≈ 0 (Earth-rate residual) - 0.000°/s Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s11/fig_overview.png :width: 100% :alt: Case 11 simulation overview Scenario 11 simulation overview. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_flight_envelope.png :width: 100% :alt: Case 11 flight envelope (altitude, TAS, Mach) Scenario 11 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_attitude.png :width: 100% :alt: Case 11 Euler attitude angles Scenario 11 Euler attitude angles — pitch, roll, yaw. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_body_rates.png :width: 100% :alt: Case 11 body angular rates Scenario 11 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_position.png :width: 100% :alt: Case 11 geodetic position Scenario 11 geodetic position — altitude, latitude, longitude. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_ned_velocity.png :width: 100% :alt: Case 11 NED velocity components Scenario 11 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_aero_forces.png :width: 100% :alt: Case 11 aerodynamic body forces Scenario 11 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_aero_moments.png :width: 100% :alt: Case 11 aerodynamic body moments Scenario 11 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. figure:: _static/f16_s11/fig_atmosphere.png :width: 100% :alt: Case 11 atmosphere Scenario 11 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_11_sim_02 (red). .. _f16-case-12: F-16 Supersonic Trim Check (NASA TM-2015-218675 Atmospheric Check-Case 12) --------------------------------------------------------------------------- Scenario Overview ^^^^^^^^^^^^^^^^^ Check-Case 12 is the supersonic analogue of Check-Case 11. The same F-16 vehicle flies steady straight-and-level flight, but at **Mach ≈ 2.01** and **30 013 ft** altitude instead of the subsonic condition. As in Case 11, the simulation is **open-loop**: control surfaces and throttle are held fixed at the trim values for the entire 200 s run; no autopilot or SAS is active. Key aerodynamic differences from Case 11: * At supersonic speed, wave drag dominates. The required throttle rises from 13.9 % (subsonic) to **56.9 %** to overcome the much higher drag. * The trim angle of attack is **−0.733°** (slightly nose-down) rather than +2.643° (nose-up), because supersonic lift is generated more efficiently at a lower α. * The trim elevator deflection is **−1.532°** (smaller and opposite direction to the subsonic −3.242°) due to the different moment balance at Mach 2. The reused class is ``F16SteadyFlightSimulator``; the only differences from Case 11 are the flight-condition constants compiled into ``F16SupersonicTrim.cpp``. Initial and Flight Conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 36.019° N, 75.674° W (Kitty Hawk, NC) * - Altitude - 30 013 ft (9 147.96 m) * - Heading - 45° NE * - TAS (initial) - ≈ 2 000 ft/s (1 184.96 KTAS) * - v\ :sub:`N` = v\ :sub:`E` - 1 414.2 ft/s = 430.9 m/s * - v\ :sub:`D` - 0 ft/s * - Body rates - all zero (trim) * - Simulation duration - 200 s Trim Result ^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 25 25 * - Quantity - Aetherion - NASA ref * - Mach (trim) - 2.0104 - 2.01045 * - α (trim) - −0.733° - −0.737° * - δ\ :sub:`e` (trim) - −1.532° - n/a (not tabulated in CSV) * - Throttle (trim) - 56.90 % - n/a * - Thrust (trim) - 54 099 N (12 162 lbf) - n/a * - Speed of sound - 303.11 m/s (994.44 ft/s) - 994.79 ft/s The trim solution agress with NASA solution within 0.004°. Step-Size Convergence ^^^^^^^^^^^^^^^^^^^^^ Unlike the closed-loop scenarios (13.1–13.4), the supersonic trim is an **open-loop** problem with no stiff controller gains. The Radau IIA implicit integrator converges to the same trajectory for all tested step sizes: .. list-table:: :header-rows: 1 :widths: 10 18 15 12 12 * - dt [s] - alt @ 200 s [ft] - Δalt [ft] - Mach - pitch [°] * - **0.10** - **30 517.5** - **+504.5** ✓ - **2.017266** - **−0.496** * - 0.05 - 30 517.5 - +504.5 ✓ - 2.017266 - −0.496 * - 0.02 - 30 517.5 - +504.5 ✓ - 2.017266 - −0.496 dt = 0.1 s is recommended for Case 12 (10× faster than the closed-loop cases, no accuracy penalty). Recommended Run Command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash F16SupersonicTrim --endTime 200 --timeStep 0.1 \ --outputFileName f16_s12_sim.csv The reference CSVs ``Atmos_12_sim_02/04/05.csv`` and the plot script ``plot_f16_s12_nasa02.py`` are copied to the build directory post-build. Validation Results at t = 200 s ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 22 22 * - Quantity - Aetherion - NASA ref * - Altitude - 30 517.5 ft (9 301.7 m) - 30 256.9 ft (9 222.1 m) * - Δ altitude - +504.5 ft - +243.9 ft * - TAS - 609.60 m/s (1 184.9 KTAS) - 610.06 m/s (1 186.3 KTAS) * - Mach - 2.017266 - 2.014849 * - Pitch θ - −0.496° - −0.616° * - Roll φ - −0.479° - −0.559° * - Yaw ψ - 45.470° - 45.657° .. note:: Both simulations show a slow altitude climb over 200 s — this is the phugoid (long-period) oscillation excited by the small trim mismatch. Aetherion's drift (+504 ft) is approximately twice the NASA reference (+244 ft). A trim α difference by 0.004°, produces a small net positive lift and a slow climb whose rate grows with the phugoid. All remaining quantities (TAS, Mach, body rates) track the NASA reference. Validation Figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s12/fig_overview.png :width: 100% :alt: Case 12 simulation overview Case 12 simulation overview. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_flight_envelope.png :width: 100% :alt: Case 12 flight envelope (altitude, TAS, Mach) Case 12 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_attitude.png :width: 100% :alt: Case 12 Euler attitude angles Case 12 Euler attitude angles — pitch, roll, yaw. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_body_rates.png :width: 100% :alt: Case 12 body angular rates Case 12 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_position.png :width: 100% :alt: Case 12 geodetic position Case 12 geodetic position — altitude, latitude, longitude. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_ned_velocity.png :width: 100% :alt: Case 12 NED velocity components Case 12 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_aero_forces.png :width: 100% :alt: Case 12 aerodynamic body forces Case 12 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_aero_moments.png :width: 100% :alt: Case 12 aerodynamic body moments Case 12 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. figure:: _static/f16_s12/fig_atmosphere.png :width: 100% :alt: Case 12 atmosphere Case 12 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_12_sim_02 (red). .. _f16-scenario-13p1: F-16 Subsonic Altitude Change (NASA TM-2015-218675 Atmospheric Scenario 13.1) ------------------------------------------------------------------------------ Scenario overview ^^^^^^^^^^^^^^^^^ Starting from the same Kitty Hawk trim as Scenario 11, the F-16 executes a closed-loop **+100 ft altitude change** driven by the NASA LQR stability- augmentation system (SAS) and altitude-hold autopilot defined in ``F16_control.dml``. The control architecture is: * **Inner loop** — Linear Quadratic Regulator (LQR) with 4-state longitudinal (V, α, q, θ) and 4-state lateral-directional (φ, β, p, r) gain matrices. * **Outer loop** — altitude-error-to-pitch-command compensator (K\ :sub:`alt` = −0.05 °/ft), airspeed hold via throttle, and heading hold via bank angle command. All LQR gains and trim values are read at runtime from ``data/F16_S119_source/F16_control.dml``; no gains are hardcoded. Initial and command conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 36.019° N, 75.674° W (Kitty Hawk, NC) * - Altitude (initial) - 10 013 ft (3 051.96 m) * - Altitude command (altCmd) - 10 113 ft (+100 ft step, applied at t = 5 s) * - Heading - 45° NE * - TAS (trim) - 335.15 KTAS (172.4 m/s) * - Mach (trim) - 0.525 * - KEAS command - computed from US1976 atmosphere at trim altitude * - Simulation duration - 20 s Numerical step-size requirement ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The closed-loop LQR plant is **stiff** — the high gains create fast eigenvalues that exceed the Radau IIA stability boundary at dt = 0.1 s. A convergence study confirms: .. list-table:: :header-rows: 1 :widths: 10 18 15 12 12 * - dt [s] - alt @ 20 s [ft] - Δ vs command [ft] - pitch [°] - roll [°] * - 0.10 - 10 085.7 - −27.3 ❌ - 3.77 - −4.08 * - 0.05 - 10 109.1 - −3.9 - 2.83 - −2.16 * - **0.02** - **10 113.1** - **+0.1** ✓ - **2.65** - **−0.10** * - 0.01 - 10 113.1 - +0.1 ✓ - 2.65 - −0.10 Use ``--timeStep 0.02`` (or smaller) for accurate closed-loop results. Recommended run command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash F16AltitudeChange --endTime 20 --timeStep 0.02 \ --outputFileName f16_s13p1.csv The reference CSVs ``Atmos_13p1_sim_02/04/05.csv`` and the plot script ``plot_f16_s13p1_nasa02.py`` are copied to the build directory post-build. Controller architecture ^^^^^^^^^^^^^^^^^^^^^^^ The GNC model (``F16_control.dml``) implements a two-loop architecture. All gains and trim values are read at runtime from the DML file. .. figure:: _static/f16_gnc_overview.png :width: 95% :alt: F-16 GNC two-loop block diagram F-16 GNC two-loop control architecture: autopilot outer loop (altitude, airspeed and heading hold) feeding commanded states to the LQR stability-augmentation inner loop. :Inputs: sensor feedbacks (:math:`h`, :math:`V_\mathrm{eq}`, :math:`\alpha`, :math:`\beta`, :math:`\phi`, :math:`\theta`, :math:`\psi`, :math:`p`, :math:`q`, :math:`r`), autopilot commands (:math:`h_\mathrm{cmd}`, :math:`V_\mathrm{eq,cmd}`, :math:`\chi_\mathrm{cmd}`, lateral offset), trim feed-forwards (:math:`\ell_\mathrm{trim}`, :math:`t_\mathrm{trim}`), mode flags (``sasOn``, ``apOn``). :Outputs: :math:`\delta_e` [deg, TED+], :math:`\delta_a` [deg, LWD+], :math:`\delta_r` [deg, TEL+], :math:`N` [0–100 %]. LQR inner loop — longitudinal channel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_lqr_longitudinal.png :width: 100% :alt: F-16 longitudinal LQR signal-flow diagram Longitudinal LQR signal-flow: four state errors (:math:`\Delta V`, :math:`\Delta\alpha`, :math:`q`, :math:`\Delta\theta`) multiplied by gain matrices :math:`\mathbf{K}_\mathrm{long}` and :math:`\mathbf{K}_\mathrm{throt}`, summed, negated, added to the trim feed-forward, saturated, and scaled to elevator :math:`\delta_e` and throttle :math:`N`. Dashed lines indicate the throttle channel reuses the same state errors. LQR inner loop — lateral-directional channel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_lqr_lateral.png :width: 100% :alt: F-16 lateral-directional LQR signal-flow diagram Lateral-directional LQR signal-flow: four state errors (:math:`\Delta\phi`, :math:`\beta`, :math:`p`, :math:`r`) multiplied by gain matrices :math:`\mathbf{K}_\mathrm{ail}` and :math:`\mathbf{K}_\mathrm{rdr}`, summed and negated to aileron :math:`\delta_a` and rudder :math:`\delta_r`. The aileron-to-rudder cross-coupling :math:`\delta_r \mathrel{+}= 0.008\,\delta_a` is shown by the dashed feedback path. Trim result (Scenario 13.1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Identical to Scenario 11 (same initial conditions): .. list-table:: :header-rows: 1 :widths: 30 25 25 * - Quantity - Aetherion - NASA ref * - α (trim) - 2.656° - 2.643° * - δ\ :sub:`e` (trim) - −3.242° - −3.24° * - Throttle (trim) - 13.90 % - 13.90 % Validation results at t = 20 s (dt = 0.02 s) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 22 22 * - Quantity - Aetherion - NASA ref * - Altitude - 10 113.1 ft (3 082.5 m) - 10 112.8 ft (3 082.4 m) * - Δ altitude (change from t=0) - +100.1 ft - +99.8 ft * - TAS - 172.69 m/s - 172.69 m/s * - Mach - 0.5261 - 0.5261 * - Pitch θ - 2.650° - 2.657° * - Roll φ - −0.10° - −0.24° Peak errors during the transient (t ≈ 5–15 s): altitude ±0.28 m, pitch ±0.64°, roll ±0.16°. These arise from the different phugoid phase between the full SE(3)/J₂ Aetherion model and the NASA reference. The final state (t = 20 s) agrees to within **0.4 ft altitude**, **0.007° pitch**, and **0.14° roll**. Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s13p1/fig_overview.png :width: 100% :alt: Case 13.1 simulation overview Scenario 13.1 simulation overview. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_flight_envelope.png :width: 100% :alt: Case 13.1 flight envelope (altitude, TAS, Mach) Scenario 13.1 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_attitude.png :width: 100% :alt: Case 13.1 Euler attitude angles Scenario 13.1 Euler attitude angles — pitch, roll, yaw. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_body_rates.png :width: 100% :alt: Case 13.1 body angular rates Scenario 13.1 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_position.png :width: 100% :alt: Case 13.1 geodetic position Scenario 13.1 geodetic position — altitude, latitude, longitude. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_ned_velocity.png :width: 100% :alt: Case 13.1 NED velocity components Scenario 13.1 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_aero_forces.png :width: 100% :alt: Case 13.1 aerodynamic body forces Scenario 13.1 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_aero_moments.png :width: 100% :alt: Case 13.1 aerodynamic body moments Scenario 13.1 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. figure:: _static/f16_s13p1/fig_atmosphere.png :width: 100% :alt: Case 13.1 atmosphere Scenario 13.1 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_13p1_sim_02 (red). .. _f16-scenario-13p2: F-16 Subsonic Airspeed Change (NASA TM-2015-218675 Atmospheric Scenario 13.2) ------------------------------------------------------------------------------ Scenario overview ^^^^^^^^^^^^^^^^^ Starting from the same Kitty Hawk trim as Scenario 11, the F-16 executes a closed-loop **−10 kt KEAS airspeed reduction** driven by the NASA LQR SAS and throttle-hold autopilot defined in ``F16_control.dml``. The same two-loop GNC architecture as Scenario 13.1 is used unchanged. Only the autopilot commands differ: * **Altitude command** — hold 10 013 ft (no altitude change). * **Airspeed command** — reduce KEAS from ≈ 287.98 kt (trim) to **277 kt** (a −10 kt step applied at t = 5 s, matching the NASA reference). * **Heading command** — hold 45° NE. Before t = 5 s the controller holds the trim KEAS so that ``deltaVequiv ≈ 0``. At t = 5 s the KEAS command steps to 277 kt; the resulting ``deltaVequiv`` drives the throttle LQR to reduce engine power and decelerate the aircraft. The reused simulator class is ``F16AltitudeChangeSimulator``; no new C++ code is required. Initial and command conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 36.019° N, 75.674° W (Kitty Hawk, NC) * - Altitude (initial & commanded) - 10 013 ft (3 051.96 m) * - Heading command (baseChiCmd) - 45° NE (hold) * - TAS (trim) - 335.15 KTAS (172.4 m/s) * - KEAS (trim, initial) - ≈ 287.98 kt * - KEAS command (keasCmd) - 277.0 kt (−10 kt step, applied at t = 5 s) * - Mach (trim) - 0.525 * - Simulation duration - 20 s Recommended run command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash F16AirspeedChange --endTime 20 --timeStep 0.02 \ --outputFileName f16_s13p2_sim.csv The reference CSVs ``Atmos_13p2_sim_02/04/05.csv`` and the plot script ``plot_f16_s13p2_nasa02.py`` are copied to the build directory post-build. Validation results at t = 20 s (dt = 0.02 s) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 22 22 * - Quantity - Aetherion - NASA ref * - Altitude - 10 006.7 ft (3 050.1 m) - 10 006.4 ft (3 050.0 m) * - KEAS - 277.02 kt - 277.02 kt * - TAS - 165.84 m/s (322.37 kt) - 165.84 m/s (322.37 kt) * - Mach - 0.505014 - 0.505024 * - Pitch θ - 2.957° - 2.969° * - Roll φ - −0.10° - −0.24° The final KEAS converges to **277.02 kt** against the 277.0 kt command (0.02 kt error). Altitude holds to within **0.3 ft** of the initial value. The transient altitude excursion during deceleration (t ≈ 5–15 s) is ≤ 2.1 m, matching the NASA reference closely. Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s13p2/fig_overview.png :width: 100% :alt: Case 13.2 simulation overview Scenario 13.2 simulation overview. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_keas.png :width: 100% :alt: Case 13.2 KEAS response Scenario 13.2 KEAS response — commanded 277 kt (amber dotted), Aetherion (blue dashed), NASA ref (red). Final KEAS error < 0.01 kt. .. figure:: _static/f16_s13p2/fig_flight_envelope.png :width: 100% :alt: Case 13.2 flight envelope (altitude, TAS, Mach) Scenario 13.2 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_attitude.png :width: 100% :alt: Case 13.2 Euler attitude angles Scenario 13.2 Euler attitude angles — pitch, roll, yaw. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_body_rates.png :width: 100% :alt: Case 13.2 body angular rates Scenario 13.2 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_position.png :width: 100% :alt: Case 13.2 geodetic position Scenario 13.2 geodetic position — altitude, latitude, longitude. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_ned_velocity.png :width: 100% :alt: Case 13.2 NED velocity components Scenario 13.2 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_aero_forces.png :width: 100% :alt: Case 13.2 aerodynamic body forces Scenario 13.2 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_aero_moments.png :width: 100% :alt: Case 13.2 aerodynamic body moments Scenario 13.2 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. figure:: _static/f16_s13p2/fig_atmosphere.png :width: 100% :alt: Case 13.2 atmosphere Scenario 13.2 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_13p2_sim_02 (red). .. _f16-scenario-13p3: F-16 Subsonic Heading Change (NASA TM-2015-218675 Atmospheric Scenario 13.3) ----------------------------------------------------------------------------- Scenario overview ^^^^^^^^^^^^^^^^^ Starting from the same Kitty Hawk trim as Scenario 11, the F-16 executes a closed-loop **+15° course step** (45° → 60° NE) driven by the NASA LQR SAS and heading-hold autopilot defined in ``F16_control.dml``. The same two-loop GNC architecture and ``F16AltitudeChangeSimulator`` class are reused unchanged. Only the autopilot commands differ: * **Altitude command** — hold 10 013 ft. * **Airspeed command** — hold trim KEAS (computed from US1976 atmosphere at the trim altitude so that ``deltaVequiv ≈ 0`` at t = 0). * **Heading command** — step from 45° to **60°** applied at **t = 15 s** (NASA TM-2015-218675 Check-case 13.3: "At t = +15.0 sec, a 15° change in commanded heading to the right was made"). Before t = 15 s the controller holds the trim heading of 45°. The DML heading autopilot estimates the track angle as ``chiEst = beta + psi`` and computes ``phi_cmd = −10 × (chiEst − chiCmd)``. Applying the 15° step at t = 0 would immediately command ``phi_cmd = +150°``, causing a violent initial roll that does not match the NASA reference. The ``chiStepTime_s`` field in ``F16AltitudeChangeCmds`` enforces the correct t = 15 s timing. The heading channel is driven by the lateral-directional LQR: a bank-angle command proportional to the course error causes the aircraft to roll into a coordinated turn and track the new heading. Initial and command conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 36.019° N, 75.674° W (Kitty Hawk, NC) * - Altitude (initial & commanded) - 10 013 ft (3 051.96 m) * - Heading (initial, held until t = 15 s) - 45° NE * - Heading command (baseChiCmd) - 60° (+15° step at t = 15 s) * - TAS (trim) - 335.15 KTAS (172.4 m/s) * - KEAS command - computed from US1976 atmosphere at trim altitude (≈ 287.98 kt) * - Mach (trim) - 0.525 * - Simulation duration - 30 s Recommended run command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash F16HeadingChange --endTime 30 --timeStep 0.02 \ --outputFileName f16_s13p3_sim.csv The reference CSVs ``Atmos_13p3_sim_02/04/05.csv`` and the plot script ``plot_f16_s13p3_nasa02.py`` are copied to the build directory post-build. Validation results at t = 30 s (dt = 0.02 s, chi step at t = 15 s) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 22 22 * - Quantity - Aetherion - NASA ref * - Altitude - 10 013.8 ft (3 052.2 m) - 10 013.3 ft (3 052.0 m) * - TAS - 172.42 m/s (335.16 kt) - 172.42 m/s (335.16 kt) * - Mach - 0.525077 - 0.525075 * - Yaw ψ - 59.92° - 59.94° * - Pitch θ - 2.617° - 2.628° * - Roll φ - +0.82° - +0.64° The final yaw agrees to within **0.02°** of the NASA reference. TAS, Mach, and altitude match to within 0.002 kt and 0.5 ft throughout. Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s13p3/fig_overview.png :width: 100% :alt: Case 13.3 simulation overview Scenario 13.3 simulation overview. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_heading.png :width: 100% :alt: Case 13.3 heading response Scenario 13.3 heading (yaw) response — commanded 60° (amber dotted), Aetherion (blue dashed), NASA ref (red). Both settle to ≈ 59.9° at t = 30 s (Aetherion 59.92°, NASA ref 59.94°). .. figure:: _static/f16_s13p3/fig_flight_envelope.png :width: 100% :alt: Case 13.3 flight envelope (altitude, TAS, Mach) Scenario 13.3 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_attitude.png :width: 100% :alt: Case 13.3 Euler attitude angles Scenario 13.3 Euler attitude angles — pitch, roll, yaw. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_body_rates.png :width: 100% :alt: Case 13.3 body angular rates Scenario 13.3 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_position.png :width: 100% :alt: Case 13.3 geodetic position Scenario 13.3 geodetic position — altitude, latitude, longitude. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_ned_velocity.png :width: 100% :alt: Case 13.3 NED velocity components Scenario 13.3 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_aero_forces.png :width: 100% :alt: Case 13.3 aerodynamic body forces Scenario 13.3 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_aero_moments.png :width: 100% :alt: Case 13.3 aerodynamic body moments Scenario 13.3 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. figure:: _static/f16_s13p3/fig_atmosphere.png :width: 100% :alt: Case 13.3 atmosphere Scenario 13.3 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_13p3_sim_02 (red). .. _f16-scenario-13p4: F-16 Subsonic Lateral Side Step (NASA TM-2015-218675 Atmospheric Scenario 13.4) -------------------------------------------------------------------------------- Scenario overview ^^^^^^^^^^^^^^^^^ Starting from the same Kitty Hawk trim as Scenario 11, the F-16 executes a closed-loop **2 000 ft right-of-course lateral offset** driven by the NASA LQR SAS and lateral deviation autopilot defined in ``F16_control.dml``. The same two-loop GNC architecture and ``F16AltitudeChangeSimulator`` class are reused. Only the autopilot commands differ: * **Altitude command** — hold 10 013 ft. * **Airspeed command** — hold trim KEAS throughout. * **Heading command** — hold 45° NE base course throughout. * **Lateral offset** — step from 0 ft to **2 000 ft right of the 45° NE courseline** at **t = 20 s** (NASA TM-2015-218675 Check-case 13.4: "At t = +20.0 sec, a 2 000-ft lateral offset was commanded to the right"). The ``lateralDeviationError`` fed to the DML control law is computed dynamically at each integration step from the aircraft's geodetic position relative to the original 45° NE courseline, minus the commanded 2 000 ft offset (applied after t = 20 s). This ensures the heading autopilot commands a return to the new parallel track rather than continuing to turn. The ``latStepTime_s`` / ``latStepOffset_ft`` fields in ``F16AltitudeChangeCmds`` encode the step; the simulator recomputes ``latOffset_ft`` each step via the ``latStepOffset_ft != 0`` branch of ``extractFeedback()``. Initial and command conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 36.019° N, 75.674° W (Kitty Hawk, NC) * - Altitude (initial & commanded) - 10 013 ft (3 051.96 m) * - Heading command (base course) - 45° NE (held throughout) * - TAS (trim) - 335.15 KTAS (172.4 m/s) * - KEAS command - computed from US1976 atmosphere at trim altitude (≈ 287.98 kt) * - Mach (trim) - 0.525 * - Lateral offset command - 2 000 ft right of course (applied at t = 20 s) * - Simulation duration - 60 s Recommended run command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash F16LateralSideStep --endTime 60 --timeStep 0.02 \ --outputFileName f16_s13p4.csv The reference CSVs ``Atmos_13p4_sim_02/04/05.csv`` and the plot script ``plot_f16_s13p4_nasa02.py`` are copied to the build directory post-build. Validation results at t = 60 s (dt = 0.02 s, lat step at t = 20 s) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 22 22 * - Quantity - Aetherion - NASA ref * - Altitude - 10 013.5 ft (3 052.1 m) - 10 013.1 ft (3 052.0 m) * - TAS - 172.42 m/s (335.16 kt) - 172.42 m/s (335.16 kt) * - Mach - 0.5251 - 0.5251 * - Yaw ψ - 45.28° - 45.15° * - Roll φ - −0.74° - −0.87° * - Lateral deviation - 1 980 ft - — The aircraft returns to within **0.28°** of the base course heading at t = 60 s. Altitude holds to within **0.6 ft** of the commanded 10 013 ft throughout. Peak errors during the turn (t ≈ 20–45 s): yaw ±2.9°, roll ±14.2° — these arise from the different lateral-dynamics phugoid phase between the full SE(3)/J₂ Aetherion model and the NASA reference running at dt = 0.1 s. Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s13p4/fig_overview.png :width: 100% :alt: Case 13.4 simulation overview Scenario 13.4 simulation overview. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_lateral.png :width: 100% :alt: Case 13.4 lateral deviation response Scenario 13.4 lateral deviation response — commanded 2 000 ft offset (amber dotted), Aetherion (blue dashed), NASA ref (red). .. figure:: _static/f16_s13p4/fig_flight_envelope.png :width: 100% :alt: Case 13.4 flight envelope (altitude, TAS, Mach) Scenario 13.4 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_attitude.png :width: 100% :alt: Case 13.4 Euler attitude angles Scenario 13.4 Euler attitude angles — pitch, roll, yaw. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_body_rates.png :width: 100% :alt: Case 13.4 body angular rates Scenario 13.4 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_position.png :width: 100% :alt: Case 13.4 geodetic position Scenario 13.4 geodetic position — altitude, latitude, longitude. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_ned_velocity.png :width: 100% :alt: Case 13.4 NED velocity components Scenario 13.4 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_aero_forces.png :width: 100% :alt: Case 13.4 aerodynamic body forces Scenario 13.4 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_aero_moments.png :width: 100% :alt: Case 13.4 aerodynamic body moments Scenario 13.4 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. figure:: _static/f16_s13p4/fig_atmosphere.png :width: 100% :alt: Case 13.4 atmosphere Scenario 13.4 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_13p4_sim_02 (red). .. _f16-scenario-15: F-16 North Pole Circumnavigation (NASA TM-2015-218675 Atmospheric Scenario 15) ------------------------------------------------------------------------------- Scenario overview ^^^^^^^^^^^^^^^^^ This scenario tests the most demanding geodetic edge case in the NASA reference: a full six-DOF F-16 simulation that **circumnavigates the geographic North Pole** on a 3-nautical-mile-radius circular track. Two distinct challenges are exercised simultaneously: * **Polar singularity** — at lat ≈ 90° the factor :math:`\cos\phi` in the longitude rate equation approaches zero, so a tiny change in body heading produces a very large change in geodetic longitude. * **Antimeridian crossing** — the vehicle repeatedly crosses ±180° geodetic longitude; the control system must wrap longitude correctly rather than treating the jump as a large position error. The control model is **``F16_gnc.dml``** — a superset of ``F16_control.dml`` that adds a *circumnavigator* outer loop. Setting the ``selectCircumnavigator`` discrete input to **1.0** activates this loop: the DML model computes the required bank-angle command to fly a steady 3-nmi left-hand circle centred on the North Pole, with altitude and airspeed holds active throughout. The GNC inputs ``ownshipN_deg`` (geodetic latitude) and ``ownshipE_deg`` (geodetic longitude) are computed from the ECI state inside ``F16AltitudeChangeSimulator::extractFeedback()`` and passed to the DML model at every integration step. The vehicle is trimmed straight and level at 10 000 ft / 563.6 ft/s TAS (Mach 0.523) before the simulation starts; the circumnavigating autopilot is engaged from the very first time step. Within approximately 30 s the aircraft has intercepted the desired circular track and settles into a steady left bank of ≈ 28°. Circle geometry ^^^^^^^^^^^^^^^ The 3-nmi radius from the North Pole corresponds to a geodetic latitude of: .. math:: \phi_\mathrm{circle} = 90° - \frac{3 \times 1852\ \text{m}}{R_e} \times \frac{180°}{\pi} \approx 89.949° At this latitude the track circumference is: .. math:: C = 2\pi\,R_e\,\cos\phi_\mathrm{circle} \approx 2\pi \times 6\,371\,000 \times 8.73 \times 10^{-4} \approx 34\,910\ \text{m} At the trim TAS of 171.8 m/s (563.6 ft/s) the nominal circuit period is: .. math:: T = \frac{C}{V} \approx \frac{34\,910}{171.8} \approx 203\ \text{s} The 180-second run therefore covers approximately **311°** of the full circle (the first ~30 s are spent intercepting the circular track from the initial heading-east condition; the remaining 150 s are at steady state). Steady-state bank angle ^^^^^^^^^^^^^^^^^^^^^^^ For a coordinated level turn at radius :math:`R` and true airspeed :math:`V`: .. math:: \phi_\mathrm{bank} = \arctan\!\left(\frac{V^2}{g\,R}\right) = \arctan\!\left(\frac{171.8^2}{9.807 \times 5\,556}\right) \approx 28.3°\ \text{(left bank)} The NASA reference CSV confirms roll ≈ −28° at steady state (t ≥ 60 s). Circumnavigator GNC architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``F16_gnc.dml`` extends ``F16_control.dml`` with a position-loop outer layer that wraps the standard LQR SAS + altitude/airspeed/heading autopilot: :DML inputs added: ``ownshipN_deg`` (geodetic latitude [°]), ``ownshipE_deg`` (geodetic longitude [°]), ``circlePoleSW`` (1.0 = enable circumnavigator). :Inner loop: Unchanged LQR SAS and altitude/airspeed/heading autopilot from ``F16_control.dml``. :Outer loop: Circumnavigator computes a bearing and cross-track error from the vehicle's position to the desired 3-nmi circle, then commands a course correction to the heading autopilot. ``circlePoleSW = 0.0`` (the default in ``F16AltitudeChangeCmds``) leaves the circumnavigator idle and is the correct value for all Scenarios 13.1–13.4. Initial and command conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 89.950° N, 45.000° W (near geographic North Pole) * - Altitude (initial & commanded) - 10 000 ft (3 048.0 m) * - Heading (initial) - 90° east * - TAS (trim) - 563.643 ft/s ≈ 333.95 kt (Mach 0.523) * - KEAS command - computed from US1976 atmosphere at 10 000 ft * - Autopilot: ``circlePoleSW`` - 1.0 (circumnavigator ON from t = 0) * - Geodesy / gravity - WGS-84 rotating ellipsoid, J₂ gravity * - Atmosphere - US Standard Atmosphere 1976, no wind * - Simulation duration - 180 s Trim result ^^^^^^^^^^^ The trim conditions at 10 000 ft / 563.643 ft/s TAS are identical to those of Scenario 11 scaled to the lower altitude (same aircraft, same atmosphere): .. list-table:: :header-rows: 1 :widths: 30 25 25 * - Quantity - Aetherion - NASA ref * - α (trim) - 2.689° - 2.689° * - δ\ :sub:`e` (trim) - ≈ −3.24° - — * - Throttle (trim) - ≈ 13.9 % - — Recommended run command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash CircleNorthPole --endTime 180 --timeStep 0.02 \ --outputFileName atmos_15_output.csv The reference CSVs ``Atmos_15_sim_02/04/05.csv`` are copied to the build directory post-build. Validation results (NASA reference ``Atmos_15_sim_02``) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The table below shows the NASA reference trajectory at selected time steps. The vehicle intercepts the circle track by t ≈ 30 s and sustains steady-state circumnavigation (constant altitude, roll, and TAS) for the remaining 150 s. .. list-table:: :header-rows: 1 :widths: 8 14 12 12 10 10 10 10 * - :math:`t` [s] - Alt [ft] - Lat [°] - Lon [°] - Yaw [°] - Pitch [°] - Roll [°] - Mach * - 0 - 10 000.00 - 89.9500 - −45.000 - 90.00 - 2.689 - −0.083 - 0.5231 * - 30 - 9 995.15 - 89.9485 - +7.029 - 88.53 - 2.876 - −29.993 - 0.5231 * - 60 - 9 995.75 - 89.9488 - +58.704 - 88.57 - 2.854 - −28.106 - 0.5231 * - 90 - 9 995.71 - 89.9488 - +110.497 - 88.61 - 2.855 - −28.211 - 0.5231 * - 120 - 9 995.71 - 89.9488 - +162.292 - 88.61 - 2.855 - −28.212 - 0.5231 * - 150 - 9 995.71 - 89.9488 - −145.913 - 88.61 - 2.855 - −28.212 - 0.5231 * - 180 - 9 995.71 - 89.9488 - −94.119 - 88.61 - 2.855 - −28.212 - 0.5231 Key observations from the reference trajectory: * **Altitude** holds within **5 ft** of the commanded 10 000 ft after the initial transient, confirming the altitude-hold autopilot is functioning. * **Bank angle** stabilises at **−28.2°** (left bank) from t ≈ 60 s onward, consistent with the analytical prediction of −28.3° for a 3-nmi radius turn. * **Longitude** advances by ≈ **51.8°** per 30 s at steady state (longitude rate ≈ 1.73°/s), equivalent to a ~203-second orbital period and a ground-track of 34 900 m per circuit. * The vehicle crosses the **antimeridian (±180°)** between t = 120 s and t = 150 s without any discontinuity in the physical trajectory, confirming correct longitude wrapping in the geodetic conversion. * **Latitude** stabilises at 89.9488° (≈ 0.0012° equatorward of the initial 89.950°), consistent with the circumnavigator settling on the desired 3-nmi track (a shift of ≈ 134 m from the initial position). Aetherion simulation results ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The table below compares Aetherion against the NASA reference at selected time steps. The simulation matches the reference within normal numerical tolerance at all checkpoints. .. list-table:: :header-rows: 1 :widths: 8 14 12 12 14 12 10 10 * - :math:`t` [s] - Alt [ft] - Lat [°] - Lon [°] - Yaw [°] - Pitch [°] - Roll [°] - Mach * - 0 - 10 000.00 - 89.9500 - −45.000 - 90.00 - 2.682 - −0.000 - 0.5231 * - 30 - 9 994.8 - 89.9486 - +7.162 - 88.70 - 2.878 - −30.00 - 0.5232 * - 60 - 9 995.6 - 89.9488 - +58.777 - 88.59 - 2.854 - −28.10 - 0.5231 * - 90 - 9 995.5 - 89.9488 - +110.556 - 88.59 - 2.855 - −28.10 - 0.5231 * - 120 - 9 995.5 - 89.9488 - +162.349 - 88.60 - 2.856 - −28.10 - 0.5231 * - 150 - 9 995.5 - 89.9488 - −145.859 - 88.60 - 2.856 - −28.10 - 0.5231 * - 180 - 9 995.1 - 89.9488 - −95.062 - 88.60 - 2.841 - −28.10 - 0.5231 The maximum latitude error is **< 0.0003°** (< 20 m) throughout the trajectory. The steady-state roll error is **< 0.1°** and altitude is held within **5 ft** of 10 000 ft from t = 30 s onward — both well within the tolerance of the NASA reference for this scenario. Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s15/fig_circle.png :width: 100% :alt: Case 15 orbit radius and longitude Scenario 15 orbit radius (top) and longitude progression (bottom). The circumnavigator holds the aircraft within **< 0.003 nmi** of the commanded 3-nmi circle from t ≈ 30 s onward. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_overview.png :width: 100% :alt: Case 15 simulation overview Scenario 15 simulation overview — 180 s north-pole circumnavigation. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_position.png :width: 100% :alt: Case 15 geodetic position Scenario 15 geodetic position — altitude, latitude, and longitude (note the antimeridian crossing near t = 135 s). Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_attitude.png :width: 100% :alt: Case 15 Euler attitude angles Scenario 15 Euler attitude angles — pitch, roll, yaw. The roll settles to ≈ −28° once the vehicle intercepts the circular track. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_flight_envelope.png :width: 100% :alt: Case 15 flight envelope Scenario 15 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_body_rates.png :width: 100% :alt: Case 15 body angular rates Scenario 15 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_ned_velocity.png :width: 100% :alt: Case 15 NED velocity components Scenario 15 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_aero_forces.png :width: 100% :alt: Case 15 aerodynamic body forces Scenario 15 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_aero_moments.png :width: 100% :alt: Case 15 aerodynamic body moments Scenario 15 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. figure:: _static/f16_s15/fig_atmosphere.png :width: 100% :alt: Case 15 atmosphere Scenario 15 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_15_sim_02 (red). .. _f16-scenario-16: F-16 Equator/Date-Line Circumnavigation (NASA TM-2015-218675 Atmospheric Scenario 16) -------------------------------------------------------------------------------------- Scenario overview ^^^^^^^^^^^^^^^^^ Scenario 16 complements Scenario 15 by exercising the **equatorial** edge case: a full six-DOF F-16 simulation that **circumnavigates the intersection of the equator and the International Date Line** (lat = 0°, lon = ±180°) on a 3-nautical-mile-radius circular track. The primary geodetic challenge is the **date-line crossing** — the vehicle's geodetic longitude wraps across ±180° twice during the 180-second run, and the control system must handle the discontinuity without treating it as a large position error. The control model is the same **``F16_gnc.dml``** used in Scenario 15. Setting ``circlePoleSW`` to **0.0** activates the *equator/IDL* branch of the circumnavigator: the DML model computes cross-track deviation from the desired 3-nmi circle centred on the date-line/equator crossing and commands the required bank angle to the heading autopilot, while altitude and airspeed holds remain active throughout. The vehicle is trimmed straight and level at 10 000 ft / 563.6 ft/s TAS (Mach 0.523) heading due north; the circumnavigating autopilot is engaged from the very first time step. Within approximately 30 s the aircraft has intercepted the circular track and settles into a steady left bank of ≈ 28°. Circle geometry ^^^^^^^^^^^^^^^ The target circle is centred on the equator/date-line crossing point (lat = 0°, lon = ±180°). The DML measures cross-track deviation as a two-component Cartesian distance in a local flat-Earth frame centred on that point: .. math:: r = \sqrt{N_\mathrm{ft}^2 + E_\mathrm{ft}^2} where .. math:: N_\mathrm{ft} = \phi_\mathrm{deg} \times 60 \times 6076.12, \qquad E_\mathrm{ft} = \Delta\lambda_\mathrm{deg} \times 60 \times 6076.12 \times \cos\phi and :math:`\Delta\lambda = \lambda + 180°` for :math:`\lambda < 0`, :math:`\Delta\lambda = \lambda - 180°` for :math:`\lambda \geq 0`. With :math:`R = 3\ \text{nmi}` the circle circumference is: .. math:: C = 2\pi R = 2\pi \times 3 \times 1852\ \text{m} \approx 34\,908\ \text{m} At the trim TAS of 171.8 m/s the nominal circuit period is: .. math:: T = \frac{C}{V} \approx \frac{34\,908}{171.8} \approx 203\ \text{s} The 180-second run therefore covers approximately **319°** of the full circle (the first ~30 s are spent intercepting the track from the initial heading-north condition; the remaining 150 s are at steady state). The date line is crossed twice during the run: once between t = 30 s and t = 60 s (longitude jumps from ≈ −179.97° to ≈ +179.99°) and once between t = 150 s and t = 180 s (longitude returns from ≈ +179.99° to ≈ −179.97°). Steady-state bank angle ^^^^^^^^^^^^^^^^^^^^^^^ For a coordinated level turn at radius :math:`R = 3\ \text{nmi} = 5\,556\ \text{m}` and true airspeed :math:`V = 171.8\ \text{m/s}`: .. math:: \phi_\mathrm{bank} = \arctan\!\left(\frac{V^2}{g\,R}\right) = \arctan\!\left(\frac{171.8^2}{9.807 \times 5\,556}\right) \approx 28.3°\ \text{(left bank)} The NASA reference CSV confirms roll ≈ −28.3° at steady state (t ≥ 60 s). Circumnavigator GNC architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The equator/IDL branch of ``F16_gnc.dml`` computes: 1. ``degEFromTgt`` — piecewise longitude offset from the date line (:math:`\lambda + 180°` if :math:`\lambda < 0`, else :math:`\lambda - 180°`). 2. ``ownshipN_ft``, ``ownshipE_ft`` — flat-Earth North/East displacement from the circle centre in feet. 3. ``latOffsetEquatorIDL`` — cross-track error :math:`\sqrt{N^2 + E^2} - R_\mathrm{circle}`. 4. ``baseChiCmdEquatorIDL`` — tangential course command :math:`-\tfrac{180}{\pi}\operatorname{atan2}(N,E)` (perpendicular to the radius vector, in the left-hand (CCW) sense). These are selected by the ``circlePoleSW = 0.0`` piecewise switch and fed to the same heading/lateral autopilot used in Scenarios 13.1–15. Initial and command conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 40 30 * - Parameter - Value * - Location - 0.000° N, 179.950° W (equator, just west of International Date Line) * - Altitude (initial & commanded) - 10 000 ft (3 048.0 m) * - Heading (initial) - 0° north * - TAS (trim) - 563.643 ft/s ≈ 333.95 kt (Mach 0.523) * - KEAS command - computed from US1976 atmosphere at 10 000 ft * - Autopilot: ``circlePoleSW`` - 0.0 (equator/IDL circumnavigator ON from t = 0) * - Geodesy / gravity - WGS-84 rotating ellipsoid, J₂ gravity * - Atmosphere - US Standard Atmosphere 1976, no wind * - Simulation duration - 180 s Trim result ^^^^^^^^^^^ The trim conditions at 10 000 ft / 563.643 ft/s TAS are identical to Scenarios 11 and 15 (same aircraft, same altitude): .. list-table:: :header-rows: 1 :widths: 30 25 25 * - Quantity - Aetherion - NASA ref * - α (trim) - 2.682° - 2.667° * - δ\ :sub:`e` (trim) - ≈ −3.26° - — * - Throttle (trim) - ≈ 13.9 % - — Recommended run command ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash CircleEquatorDateLine --endTime 180 --timeStep 0.02 \ --outputFileName atmos_16_output.csv The reference CSVs ``Atmos_16_sim_02/04/05.csv`` are copied to the build directory post-build. Validation results (NASA reference ``Atmos_16_sim_02``) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The table below compares NASA and Aetherion trajectories at selected time steps. The vehicle intercepts the circular track by t ≈ 30 s and sustains steady-state circumnavigation for the remaining 150 s. .. list-table:: :header-rows: 1 :widths: 8 14 12 12 10 10 10 10 * - :math:`t` [s] - Alt [ft] - Lat [°] - Lon [°] - Yaw [°] - Pitch [°] - Roll [°] - Mach * - 0 - 10 000.00 - 0.0000 - −179.950 - 0.00 - 2.667 - 0.000 - 0.5231 * - 30 - 9 995.01 - +0.0407 - −179.968 - −53.30 - 2.862 - −29.993 - 0.5231 * - 60 - 9 995.61 - +0.0498 - +179.988 - −105.10 - 2.839 - −27.966 - 0.5231 * - 90 - 9 995.63 - +0.0213 - +179.953 - −156.81 - 2.838 - −28.193 - 0.5231 * - 120 - 9 995.80 - −0.0234 - +179.955 - +151.39 - 2.829 - −28.249 - 0.5231 * - 150 - 9 995.91 - −0.0503 - +179.990 - +99.59 - 2.824 - −28.281 - 0.5231 * - 180 - 9 995.88 - −0.0387 - −179.967 - +47.80 - 2.826 - −28.267 - 0.5231 Key observations from the reference trajectory: * **Altitude** holds within **6 ft** of the commanded 10 000 ft after the initial transient. * **Bank angle** stabilises at **−28.3°** (left bank) from t ≈ 60 s onward, consistent with the analytical prediction of −28.3° for a 3-nmi radius turn. * **Date-line crossings** — longitude wraps from ≈ −180° to ≈ +180° between t = 30 s and t = 60 s, and from ≈ +180° to ≈ −180° between t = 150 s and t = 180 s, with no discontinuity in the physical trajectory. * **Yaw** wraps through ±180° (from −157° at t = 90 s to +151° at t = 120 s), confirming correct heading-angle wrapping. * **Latitude** oscillates between ≈ +0.05° and ≈ −0.05°, i.e., the orbit straddles the equator symmetrically. Aetherion simulation results ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 8 14 12 12 10 10 10 10 * - :math:`t` [s] - Alt [ft] - Lat [°] - Lon [°] - Yaw [°] - Pitch [°] - Roll [°] - Mach * - 0 - 10 000.03 - 0.0001 - −179.950 - 0.00 - 2.669 - −0.175 - 0.5232 * - 30 - 9 994.56 - +0.0409 - −179.968 - −53.29 - 2.848 - −30.00 - 0.5231 * - 60 - 9 995.25 - +0.0497 - +179.988 - −104.97 - 2.825 - −27.79 - 0.5231 * - 90 - 9 995.18 - +0.0209 - +179.953 - −156.59 - 2.826 - −28.34 - 0.5231 * - 120 - 9 995.31 - −0.0241 - +179.955 - +151.53 - 2.819 - −28.47 - 0.5231 * - 150 - 9 995.54 - −0.0504 - +179.991 - +99.71 - 2.810 - −28.15 - 0.5231 * - 180 - 9 995.48 - −0.0382 - −179.966 - +47.81 - 2.812 - −28.23 - 0.5231 The maximum latitude error is **< 0.0006°** (< 65 m) throughout the trajectory. Longitude error is **< 0.002°** at all checkpoints. The steady-state roll error is **< 0.3°** and altitude is held within **7 ft** of 10 000 ft from t = 30 s onward. Validation figures ^^^^^^^^^^^^^^^^^^ .. figure:: _static/f16_s16/fig_circle.png :width: 100% :alt: Case 16 orbit radius and latitude oscillation Scenario 16 orbit radius from the equator/IDL crossing (top) and latitude oscillation (bottom). The circumnavigator holds the aircraft within **< 0.01 nmi** of the commanded 3-nmi circle from t ≈ 30 s onward, and the orbit straddles the equator symmetrically. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_overview.png :width: 100% :alt: Case 16 simulation overview Scenario 16 simulation overview — 180 s equator/date-line circumnavigation. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_position.png :width: 100% :alt: Case 16 geodetic position Scenario 16 geodetic position — altitude, latitude, and longitude (note the two date-line crossings near t = 45 s and t = 165 s). Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_attitude.png :width: 100% :alt: Case 16 Euler attitude angles Scenario 16 Euler attitude angles — pitch, roll, yaw. The roll settles to ≈ −28° once the vehicle intercepts the circular track; the yaw wraps through ±180° during the orbit. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_flight_envelope.png :width: 100% :alt: Case 16 flight envelope Scenario 16 flight envelope — altitude, TAS, Mach. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_body_rates.png :width: 100% :alt: Case 16 body angular rates Scenario 16 body angular rates — roll, pitch, yaw rates. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_ned_velocity.png :width: 100% :alt: Case 16 NED velocity components Scenario 16 NED velocity components — North, East, Down. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_aero_forces.png :width: 100% :alt: Case 16 aerodynamic body forces Scenario 16 aerodynamic body forces — X, Y, Z. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_aero_moments.png :width: 100% :alt: Case 16 aerodynamic body moments Scenario 16 aerodynamic body moments — L, M, N. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. figure:: _static/f16_s16/fig_atmosphere.png :width: 100% :alt: Case 16 atmosphere Scenario 16 atmosphere — temperature, density, pressure. Aetherion (blue dashed) vs NASA Atmos_16_sim_02 (red). .. _example_two_stage_rocket: Two-Stage Rocket to Orbit (NASA TM-2015-218675 Atmospheric Scenario 17) ------------------------------------------------------------------------ **Reference:** `NASA TM-2015-218675`_, *Atmospheric and Space Flight Vehicle Equations of Motion*, Appendix B, Section B.1.17 — Atmospheric Simulation 17. **DAVE-ML data files:** :file:`data/TwoStageRocket_S119_source/twostage_aero.dml`, :file:`twostage_inertia.dml`, :file:`twostage_prop.dml` (authored by E. M. Queen and B. Jackson, NASA Langley Research Center, 2013). Scenario Overview ^^^^^^^^^^^^^^^^^ This is the most complex atmospheric example in the NASA reference: a full six-degree-of-freedom simulation of an **open-loop two-stage rocket ascending to orbit** from the equatorial prime meridian. Unlike the F-16 scenarios, the vehicle is not trimmed — it follows a **gravity-turn** trajectory dictated entirely by its initial attitude and the physical forces acting on it. Three new phenomena distinguish this example from all earlier scenarios: 1. **Variable mass** — propellant is consumed continuously, shrinking the total mass and shifting the centre of gravity (CG) along the body axis. 2. **Stage separation** — when Stage 1 propellant is exhausted the first-stage hardware (35 000 kg) is jettisoned instantaneously; Stage 2 ignites immediately. 3. **CG–MRC offset** — the DAVE-ML aerodynamic and inertia models define moments about a fixed Moment Reference Centre (MRC) while gravity and inertia forces act at the (moving) CG; the equations of motion must correct for this offset. The vehicle is based on Eric Queen's conceptual two-stage-to-orbit design (source document: *twostagetoorbit.pptx*, NASA LaRC, 2013). Physics Model ^^^^^^^^^^^^^ The simulation composes four physics policies into a single ``VectorField``: .. code-block:: cpp using TwoStageRocketVF = RigidBody::VectorField< RocketGravityPolicy, // J₂ gravity + CG–MRC moment transfer RocketAeroPolicy, // DAVE-ML CL/CD/CY/Cm/Cn, wind-to-body FlightDynamics::AxialThrustPolicy,// body +x thrust from propulsion DML FlightDynamics::LinearBurnPolicy // variable mdot, updated each step (ZOH) >; using Stepper = RigidBody::SixDoFStepper; All four policies are evaluated at every stage evaluation of the Radau IIA RKMK integrator. The mass properties (inertia matrix, CG offset) and propulsion state (thrust, mass flow rate) are refreshed once per integration *step* using a zero-order-hold (ZOH) scheme. .. _atmos17_inertia_model: Inertia Model — ``twostage_inertia.dml`` """"""""""""""""""""""""""""""""""""""""" The inertia model computes five outputs as piecewise-linear functions of the cumulative fuel consumed in each stage and a binary staging flag. **Inputs** .. list-table:: :header-rows: 1 :widths: 30 15 55 * - Variable ID - Unit - Description * - ``stagedFlag`` - — - 0 = Stage 1 active; > 0 = Stage 2 active (S1 jettisoned) * - ``stg1fuelUsed`` - kg - Cumulative Stage-1 propellant consumed * - ``stg2fuelUsed`` - kg - Cumulative Stage-2 propellant consumed **Reference constants** .. list-table:: :header-rows: 1 :widths: 35 15 15 35 * - Quantity - Stage 1 (ignition) - Stage 1 (burnout) - Notes * - Mass :math:`m` [kg] - 314 000 - 134 000 - Fuel capacity: 180 000 kg * - :math:`x_{CG}` [m aft of nose] - 16.919 - 9.422 - CG travels 7.50 m forward as S1 burns * - :math:`I_{xx}` [kg·m²] - 353 250 - 150 750 - Roll (axial) inertia * - :math:`I_{yy} = I_{zz}` [kg·m²] - 33 501 637 - 10 886 637 - Pitch / yaw inertia (axisymmetric) * - MRC position [m aft of nose] - 16.919 - 16.919 - Fixed during Stage 1 .. list-table:: :header-rows: 1 :widths: 35 15 15 35 * - Quantity - Stage 2 (ignition) - Stage 2 (burnout) - Notes * - Mass :math:`m` [kg] - 99 000 - 19 000 - Fuel capacity: 80 000 kg * - :math:`x_{CG}` [m aft of nose] - 4.798 - 3.947 - CG travels 0.85 m forward as S2 burns * - :math:`I_{xx}` [kg·m²] - 111 375 - 21 375 - Roll inertia (S2 only) * - :math:`I_{yy} = I_{zz}` [kg·m²] - 941 064 - 212 385 - Pitch / yaw inertia (S2 only) * - MRC position [m aft of nose] - 4.798 - 4.798 - Fixed during Stage 2 **Fuel fraction and linear interpolation** The DML first computes a *remaining fuel fraction* for each stage: .. math:: f_i = \frac{C_i - m_{f,i}^{\text{used}}}{C_i}, \qquad f_i \in [0,\,1] where :math:`C_1 = 180\,000\ \text{kg}` and :math:`C_2 = 80\,000\ \text{kg}` are the propellant capacities. :math:`f_i = 1` when the stage is full; :math:`f_i = 0` at burnout. Every mass property :math:`P` is then linearly interpolated between its ignition value :math:`P_{\text{ign}}` and its burnout value :math:`P_{\text{bo}}`: .. math:: P = P_{\text{bo}} + (P_{\text{ign}} - P_{\text{bo}})\,f_i This single formula covers mass, :math:`x_{CG}`, :math:`I_{xx}`, :math:`I_{yy}`, and :math:`I_{zz}`. The active stage is selected by the piecewise condition on ``stagedFlag``: .. math:: P = \begin{cases} P_{\text{bo,1}} + \Delta P_1\,f_1 & \text{if } \texttt{stagedFlag} = 0 \\ P_{\text{bo,2}} + \Delta P_2\,f_2 & \text{if } \texttt{stagedFlag} > 0 \end{cases} **CG–MRC offset (DXCG)** The aerodynamic lookup tables define moments about the Moment Reference Centre (MRC). Because the CG shifts as propellant is consumed, the DML tracks the signed offset: .. math:: \text{DXCG} = x_{\text{MRC}} - x_{CG} \quad \text{[m, positive forward]} During Stage 1, the MRC is fixed at 16.919 m aft of the nose. As propellant burns, :math:`x_{CG}` decreases (moves forward), so DXCG grows from 0 m (at liftoff, where CG ≅ MRC) to +7.50 m at S1 burnout. During Stage 2 the MRC shifts to 4.798 m aft of the nose. All products of inertia (:math:`I_{xz}`, :math:`I_{xy}`, :math:`I_{yz}`) are identically zero — the vehicle is axisymmetric. .. figure:: _static/atmos17/mass_properties_stage1.png :alt: Stage 1 mass properties vs. fuel consumed :align: center :width: 95% Stage 1 mass, CG position, roll inertia :math:`I_{xx}`, and pitch/yaw inertia :math:`I_{yy}` as functions of cumulative fuel consumed. All quantities vary linearly with fuel fraction :math:`f_1`. .. figure:: _static/atmos17/mass_properties_stage2.png :alt: Stage 2 mass properties vs. fuel consumed :align: center :width: 95% Stage 2 mass properties after separation. The much smaller inertias (note the axis scales) reflect the slender upper-stage geometry. .. figure:: _static/atmos17/cg_mrc_offset.png :alt: CG and MRC positions vs. fuel consumed :align: center :width: 90% CG position (solid line) and fixed MRC (dashed) for each stage. The shaded band is the DXCG offset that enters the gravity moment-transfer correction. At Stage 1 liftoff the CG and MRC nearly coincide (DXCG ≈ 0); by burnout DXCG has grown to ~7.5 m, creating a substantial gravity moment. .. _atmos17_prop_model: Propulsion Model — ``twostage_prop.dml`` """"""""""""""""""""""""""""""""""""""""" **Inputs** .. list-table:: :header-rows: 1 :widths: 25 10 65 * - Variable ID - Unit - Description * - ``stg1firing`` - — - > 0 when Stage 1 is burning * - ``stg2firing`` - — - > 0 when Stage 2 is burning **Engine constants** .. list-table:: :header-rows: 1 :widths: 35 20 20 25 * - Quantity - Stage 1 - Stage 2 - Notes * - Maximum thrust :math:`T` [N] - 17 000 000 - 5 000 000 - Constant during burn (no throttle model) * - Specific impulse :math:`I_{sp}` [s] - 360 - 390 - S2 is more efficient (higher-altitude vacuum Isp) **Thrust (piecewise constant)** .. math:: T = \begin{cases} 17\,000\,000\ \text{N} & \text{if } \texttt{stg1firing} > 0 \\ 5\,000\,000\ \text{N} & \text{if } \texttt{stg2firing} > 0 \\ 0 & \text{otherwise} \end{cases} Thrust acts solely along the body :math:`+x` axis (nose direction); lateral and axial moment components from thrust are identically zero: .. math:: \mathbf{F}_{\text{thrust}}^B = [T,\ 0,\ 0]^\top, \qquad \mathbf{M}_{\text{thrust}}^B = [0,\ 0,\ 0]^\top **Propellant mass flow rate (Tsiolkovsky)** The propellant consumption rate follows directly from the rocket equation definition of specific impulse: .. math:: \dot{m} = \frac{T}{I_{sp}\,g_0}, \qquad g_0 = 9.806\,6\ \text{m/s}^2 Numerically: .. list-table:: :header-rows: 1 :widths: 20 20 20 40 * - Stage - :math:`T` [N] - :math:`I_{sp}` [s] - :math:`\dot{m}` [kg/s] * - 1 - 17 000 000 - 360 - :math:`\approx 4\,810.5` * - 2 - 5 000 000 - 390 - :math:`\approx 1\,307.3` At these rates, Stage 1 exhausts its 180 000 kg propellant in :math:`180\,000 / 4810.5 \approx 37.4\ \text{s}`, and Stage 2 exhausts its 80 000 kg in :math:`80\,000 / 1307.3 \approx 61.2\ \text{s}`. .. figure:: _static/atmos17/propulsion_constants.png :alt: Propulsion constants per stage :align: center :width: 70% Maximum thrust and derived propellant mass flow rate for each stage. Stage 1 produces 3.4× more thrust and burns propellant 3.7× faster than Stage 2; Stage 2 compensates with a 8.3% higher specific impulse. **Staging event** When ``RocketStageModel::advance()`` detects that the cumulative Stage-1 fuel consumed equals the capacity :math:`C_1 = 180\,000\ \text{kg}`, it sets the staging flag and returns ``true``. The simulator then applies a discrete mass drop of :math:`\Delta m = 35\,000\ \text{kg}` to the ODE state, representing the jettison of the empty Stage-1 hardware: .. math:: m^+ = m^- - \Delta m_{\text{S1 dry}}, \qquad \Delta m_{\text{S1 dry}} = m_{\text{S1 burnout}} - m_{\text{S2 ignite}} = 134\,000 - 99\,000 = 35\,000\ \text{kg} The MRC immediately shifts from 16.919 m (S1 reference) to 4.798 m (S2 reference) and the spatial inertia matrix is rebuilt for the S2-only configuration. .. _atmos17_aero_model: Aerodynamic Model — ``twostage_aero.dml`` """""""""""""""""""""""""""""""""""""""""" The aerodynamic model receives two scalar inputs — angle of attack :math:`\alpha` and sideslip angle :math:`\beta` (both in degrees) — and returns five force/moment coefficients. All moments are defined about the current MRC. **Reference geometry** .. list-table:: :header-rows: 1 :widths: 45 20 35 * - Quantity (DAVE-ML ID) - Value - Description * - Reference area :math:`S_{\text{ref}}` (``sref``) - 7.0 m² - Approximate frontal area of the rocket base * - Longitudinal ref. length :math:`\bar{c}` (``cbar``) - 3.0 m - Used to non-dimensionalise pitching moment * - Lateral ref. length :math:`b` (``bspan``) - 3.0 m - Used to non-dimensionalise yawing moment **Total angle of attack** Before the drag table lookup the DML computes a *total* angle of attack that combines both the pitch and sideslip contributions: .. math:: \alpha_T = \sqrt{\alpha^2 + \beta^2} This makes :math:`C_D` symmetric with respect to the direction of incidence rather than purely pitch-plane. **Lookup tables and breakpoints** *Force coefficients* — 11-point tables, extrapolation clamped to ±10°: .. list-table:: :header-rows: 1 :widths: 12 12 12 12 12 12 12 12 12 12 12 * - α (β) [°] - −10 - −8 - −6 - −4 - −2 - 0 - +2 - +4 - +6 - +8 - +10 * - :math:`C_L(\alpha)` - −1.60 - −1.00 - −0.73 - −0.49 - −0.24 - 0 - +0.24 - +0.49 - +0.73 - +1.00 - +1.60 * - :math:`C_D(\alpha_T)` - 0.48 - 0.38 - 0.31 - 0.25 - 0.23 - **0.21** - 0.23 - 0.25 - 0.31 - 0.38 - 0.48 * - :math:`C_Y(\beta)` - +1.60 - +1.00 - +0.73 - +0.49 - +0.24 - 0 - −0.24 - −0.49 - −0.73 - −1.00 - −1.60 Observations: :math:`C_L(\alpha)` and :math:`C_Y(\beta)` are **odd functions** (antisymmetric); :math:`C_D(\alpha_T)` is an **even function** with a minimum value of 0.21 at zero incidence, rising to 0.48 at ±10°. *Moment coefficients* — 3-point linear tables, range ±20°: .. list-table:: :header-rows: 1 :widths: 30 20 20 20 10 * - Coefficient - α (β) = −20° - 0° - +20° - Slope * - :math:`C_m(\alpha)` - +0.60 - 0 - −0.60 - −0.03 / deg * - :math:`C_n(\beta)` - −0.60 - 0 - +0.60 - +0.03 / deg Both moment tables are linear and pass through zero at zero incidence. :math:`C_m` is statically stable in pitch (negative slope → restoring moment for positive :math:`\alpha`); :math:`C_n` is statically stable in yaw. The rolling moment coefficient is identically zero (axisymmetric vehicle). .. figure:: _static/atmos17/aero_force_coefficients.png :alt: Aerodynamic force coefficients :align: center :width: 95% Lift (:math:`C_L`), drag (:math:`C_D`), and sideforce (:math:`C_Y`) coefficients from the DAVE-ML lookup tables. Filled circles mark the 11 breakpoint values; the solid lines are the piecewise-linear interpolant used by the simulator. :math:`C_D` uses the *total* angle of attack :math:`\alpha_T = \sqrt{\alpha^2+\beta^2}` as its independent variable. .. figure:: _static/atmos17/aero_moment_coefficients.png :alt: Aerodynamic moment coefficients :align: center :width: 70% Pitching (:math:`C_m`) and yawing (:math:`C_n`) moment coefficients. The 3-breakpoint linear tables give a constant slope of ±0.03 /deg, reflecting a simple static stability model. :math:`C_l = 0` (axisymmetric). **Dynamic pressure** The atmosphere-relative airspeed and dynamic pressure are computed at each integrator stage evaluation: .. math:: \mathbf{v}_{\text{rel}}^B = \mathbf{v}_B - R_{BI}\,(\boldsymbol{\omega}_E \times \mathbf{r}) where :math:`\mathbf{r}` is the ECI position vector, :math:`\boldsymbol{\omega}_E = [0,\,0,\,\omega_E]^\top` in ECI with :math:`\omega_E = 7.292\,115 \times 10^{-5}\ \text{rad/s}`, and :math:`R_{BI}` rotates ECI to body frame. The atmosphere-relative speed is: .. math:: V_T = \lVert \mathbf{v}_{\text{rel}}^B \rVert and the dynamic pressure: .. math:: \bar{q} = \tfrac{1}{2}\,\rho(h)\,V_T^2 where :math:`\rho(h)` is the US Standard Atmosphere 1976 density at the current geocentric altitude :math:`h \approx \lVert\mathbf{r}\rVert - R_e`. **Aerodynamic angles** From the atmosphere-relative velocity components :math:`(u, v, w)` in the body frame: .. math:: \alpha = \arctan\!\left(\frac{w}{u}\right), \qquad \beta = \arcsin\!\left(\frac{v}{V_T}\right) Both angles are passed to the DAVE-ML model in **degrees**. Table lookups are clamped at ±10° for force coefficients and ±20° for moment coefficients (no extrapolation). **Wind-to-body force transform** Force coefficients are defined in the **wind frame** :math:`\mathbf{F}_w = [-C_D,\;C_Y,\;-C_L]^\top \bar{q}S` (drag opposing motion, lift perpendicular to drag, sideforce positive starboard). The wind-to-body rotation matrix is: .. math:: R_{BW} = R_y(\alpha)\,R_z(\beta) Expanding :math:`\mathbf{F}_B = R_{BW}\,\mathbf{F}_w` with :math:`c_\alpha = \cos\alpha`, :math:`s_\alpha = \sin\alpha`, :math:`c_\beta = \cos\beta`, :math:`s_\beta = \sin\beta`: .. math:: F_x^B &= (-C_D\,c_\alpha c_\beta - C_Y\,c_\alpha s_\beta + C_L\,s_\alpha)\,\bar{q}S \\ F_y^B &= (-C_D\,s_\beta + C_Y\,c_\beta )\,\bar{q}S \\ F_z^B &= (-C_D\,s_\alpha c_\beta - C_Y\,s_\alpha s_\beta - C_L\,c_\alpha )\,\bar{q}S .. figure:: _static/atmos17/wind_body_transform.png :alt: Wind-to-body force transform convention :align: center :width: 70% Sign convention for aerodynamic angle of attack :math:`\alpha` and the wind-to-body force transform. Drag :math:`D` opposes the velocity vector; lift :math:`L` is perpendicular (upward in the body :math:`-z` direction for positive :math:`\alpha`). The moment axis :math:`y_B` points into the page. **Aerodynamic moments (about MRC)** The DML moment outputs are already defined about the MRC so no transfer correction is required here: .. math:: M_x^B &= 0 \quad \text{(axisymmetric — no roll moment)} \\ M_y^B &= C_m\,\bar{q}\,S\,\bar{c} \quad \text{(pitching moment about MRC)} \\ M_z^B &= C_n\,\bar{q}\,S\,b \quad \text{(yawing moment about MRC)} .. _atmos17_gravity_policy: Gravity Policy — J₂ with CG–MRC Moment Transfer """"""""""""""""""""""""""""""""""""""""""""""""" The simulator integrates the Newton–Euler equations about the MRC, but gravity acts at the CG. When these two points do not coincide, gravity produces an additional moment. The gravitational acceleration in the ECI frame uses the J₂ zonal harmonic: .. math:: \mathbf{g}(\mathbf{r}) = -\frac{\mu}{r^3}\,\mathbf{r} + \frac{3\,\mu\,J_2\,R_e^2}{2\,r^5} \!\left[ \left(5\frac{r_z^2}{r^2} - 1\right)\mathbf{r} - 2\,r_z\,\hat{\mathbf{z}}_I \right] where :math:`\mu = 3.986\,004\,418 \times 10^{14}\ \text{m}^3/\text{s}^2`, :math:`R_e = 6\,378\,137\ \text{m}`, and :math:`J_2 = 1.082\,626\,68 \times 10^{-3}`. The gravitational force in the body frame is: .. math:: \mathbf{F}_{\text{grav}}^B = R_{BI}\,m\,\mathbf{g}(\mathbf{r}) The CG lies at position :math:`\mathbf{r}_{CG}^B = [x_{CG},\,0,\,0]^\top = [\text{DXCG},\,0,\,0]^\top` forward of the MRC (DXCG ≥ 0 with the sign convention in the DML). The additional moment that gravity produces about the MRC is: .. math:: \mathbf{M}_{\text{grav}}^{\text{MRC}} = \mathbf{r}_{CG}^B \times \mathbf{F}_{\text{grav}}^B Expanding with :math:`r_y = r_z = 0` (axisymmetric CG, so CG lies on the body :math:`x`-axis): .. math:: M_x &= 0 \\ M_y &= -x_{CG}\,F_z^B \\ M_z &= +x_{CG}\,F_y^B ``RocketGravityPolicy`` implements these corrections directly and updates :math:`x_{CG}` (stored as ``xcg_m`` = DXCG) at the start of every integration step. .. _atmos17_spatial_inertia: Spatial Inertia Matrix """""""""""""""""""""" The six-DoF integrator uses Featherstone's **spatial inertia** formulation. With the body origin at the MRC, CG offset :math:`\mathbf{c} = [x_{CG},\,0,\,0]^\top`, and body inertia :math:`\mathbf{I}` about the MRC: .. math:: \mathbf{M} = \begin{bmatrix} \mathbf{I} & m\,[\mathbf{c}]_\times^\top \\ m\,[\mathbf{c}]_\times & m\,\mathbf{1}_{3} \end{bmatrix} \in \mathbb{R}^{6\times 6} where :math:`[\mathbf{c}]_\times` is the skew-symmetric matrix of :math:`\mathbf{c}`. For this axisymmetric vehicle (:math:`c_y = c_z = 0`): .. math:: \mathbf{M} = \begin{bmatrix} I_{xx} & 0 & 0 & 0 & 0 & 0 \\ 0 & I_{yy} & 0 & 0 & 0 & -m\,x_{CG}\\ 0 & 0 & I_{zz} & 0 & m\,x_{CG} & 0 \\ 0 & 0 & 0 & m & 0 & 0 \\ 0 & 0 & m\,x_{CG}& 0 & m & 0 \\ 0 & -m\,x_{CG}& 0 & 0 & 0 & m \end{bmatrix} The matrix is rebuilt (together with its inverse :math:`\mathbf{M}^{-1}`) before each integration step from the current DML-interpolated values of :math:`m`, :math:`I_{xx}`, :math:`I_{yy}`, and DXCG. Initial Conditions ^^^^^^^^^^^^^^^^^^ All initial conditions for Scenario 17 are defined in :file:`src/Examples/TwoStageRocket/TwoStageRocket.cpp`. **Launch site and attitude** .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Parameter - Value - Notes * - Geodetic latitude - 0.0° - Equatorial launch site * - Geodetic longitude - 0.0° - Prime meridian * - Altitude (MSL) - 0.0 m - Sea-level launch pad * - Azimuth (heading) - 90.0° - Due East — maximises the benefit of Earth's rotation for orbit insertion * - Pitch (from horizontal) - 55.22° - Zenith angle = 34.78° from vertical * - Roll - 0.0° - **Body rates and velocity (ECEF-relative)** .. list-table:: :header-rows: 1 :widths: 35 20 45 * - Parameter - Value - Notes * - NED velocity (N, E, D) - [0, 0, 0] m/s - Stationary on the launch pad * - Body rates (roll, pitch, yaw) - [0, 0, 0] rad/s - ECEF-relative; Earth rotation (~7.29 × 10⁻⁵ rad/s pitch-rate) is implicitly present in the ECI frame **Liftoff mass and inertia** The initial inertial parameters are read from the inertia DML with ``stg1fuelUsed = 0`` and ``stagedFlag = 0``: .. list-table:: :header-rows: 1 :widths: 35 25 40 * - Parameter - Value - Notes * - Total mass :math:`m_0` - 314 000 kg - Full Stage-1 + Stage-2 stack * - :math:`I_{xx}` - 353 250 kg·m² - Roll inertia at liftoff * - :math:`I_{yy} = I_{zz}` - 33 501 637 kg·m² - Pitch/yaw inertia at liftoff * - :math:`x_{CG}` (aft of nose) - 16.919 m - Nearly coincides with MRC at liftoff (DXCG ≈ 0) **Earth Rotation Angle** The initial Earth Rotation Angle (ERA) is computed as: .. math:: \theta_0 = \omega_E\,t_0, \qquad \omega_E = 7.292\,115 \times 10^{-5}\ \text{rad/s} For the default start time :math:`t_0 = 0` this gives :math:`\theta_0 = 0`. Per-Step Integration Loop ^^^^^^^^^^^^^^^^^^^^^^^^^ The ``TwoStageRocketSimulator`` employs a **zero-order-hold (ZOH)** scheme to couple the variable-mass state with the fixed-structure Radau IIA RKMK integrator. The sequence for each time step of size :math:`h` is: 1. **Propulsion query** — evaluate ``twostage_prop.dml`` at the current staging/fuel state to obtain :math:`T` [N] and :math:`\dot{m}` [kg/s]. Update ``AxialThrustPolicy::thrust_N`` and ``LinearBurnPolicy::mdot_kgs`` in the VectorField. 2. **Inertia rebuild** — evaluate ``twostage_inertia.dml`` to obtain current :math:`m`, :math:`I_{xx}`, :math:`I_{yy}`, :math:`I_{zz}`, and DXCG. Rebuild the 6×6 spatial inertia matrix :math:`\mathbf{M}` and its inverse. Update ``RocketGravityPolicy::xcg_m`` for the gravity moment correction. 3. **Integration** — advance the 13-component ODE state :math:`(\mathbf{q},\,\mathbf{p},\,\boldsymbol{\nu}_B,\,m)` by one step :math:`h` using the 3-stage Radau IIA RKMK integrator. The VectorField is evaluated multiple times per step (6 function evaluations for a 3-stage implicit Runge-Kutta method) but the inertia and propulsion parameters are held **constant** throughout the step (ZOH). 4. **Fuel advance** — call ``RocketStageModel::advance(h, mdot)`` to integrate fuel consumption: :math:`m_{f,i}^{\text{used}} \mathrel{+}= \dot{m}\,h`. 5. **Staging check** — if Stage 1 fuel is exhausted, apply the discrete mass drop and rebuild the inertia matrix for the S2-only configuration: .. math:: m_{\text{state}}^+ = m_{\text{state}}^- - \Delta m_{\text{S1 dry}} Atmosphere Model ^^^^^^^^^^^^^^^^ The aerodynamic policy uses the **US Standard Atmosphere 1976** to compute air density at the current altitude. This is the same model used throughout the Aetherion validation suite. .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Layer (altitude range) - Temperature lapse rate - Notes * - Troposphere (0–11 km) - −6.5 K/km - Below the tropopause; most of ascent occurs here * - Tropopause (11–20 km) - Isothermal, 216.65 K - Roughly 11–20 km * - Stratosphere lower (20–32 km) - +1.0 K/km - Density drops below 0.1 kg/m³ here At the launch pad (sea level, 288.15 K): .. math:: \rho_0 = 1.225\ \text{kg/m}^3, \qquad p_0 = 101\,325\ \text{Pa} The altitude used for the atmospheric query is the geocentric height :math:`h = \lVert\mathbf{r}\rVert - R_e`, consistent with the spherical approximation used in the NASA reference. Output Format ^^^^^^^^^^^^^ Each row of the output CSV corresponds to one :cpp:struct:`Aetherion::Simulation::Snapshot2` (31 columns). The schema is compatible with the NASA Scenario 17 reference CSV (:file:`data/TwoStageRocket_S119_source/Atmos_17_sim_06.csv`). Key columns specific to Scenario 17 compared to earlier snapshots: .. list-table:: :header-rows: 1 :widths: 35 20 45 * - Column group - Columns - Description * - Propellant state - ``stg1fuelUsed_kg``, ``stg2fuelUsed_kg`` - Cumulative fuel consumed per stage [kg] * - Staging flag - ``staged`` - 0 before separation; 1 after Stage 1 jettison * - Vehicle mass - ``mass_kg`` - Total mass from ODE state [kg] Staging Sequence and S2 Ignition Timing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The NASA TM-2015-218675 reference notes that "this case required some external logic to calculate fuel burn, initiate staging, and detect burnout." In particular, the reference simulation does **not** ignite Stage 2 immediately after Stage 1 burnout. Instead there is a coast phase of approximately 96 s during which the rocket follows a ballistic arc, followed by a late S2 ignition timed so that S2 burns exactly until near the end of the 200-second simulation: .. math:: t_{\text{S2 ign}} = t_{\text{end}} - \Delta t_{\text{coast}} - t_{\text{S2 burn}} where :math:`t_{\text{S2 burn}} = C_2 / \dot{m}_2 \approx 61.2\ \text{s}` is the Stage-2 burn duration and :math:`\Delta t_{\text{coast}} \approx 5\ \text{s}` is a brief post-S2-burnout observation period at the end of the simulation. For the tuned values (:math:`t_{\text{end}} = 200\ \text{s}`, :math:`\Delta t_{\text{coast}} = 7\ \text{s}`): .. math:: t_{\text{S2 ign}} = 200 - 7 - 61.2 \approx 131.8\ \text{s} This timing is computed automatically by ``TwoStageRocket.cpp`` at startup from the propulsion DML output: .. code-block:: text S2 ignition time: 131.807 s (burn duration: 61.193 s, post-burn coast: 7.0 s) The complete mission sequence is therefore: .. list-table:: :header-rows: 1 :widths: 20 20 60 * - Phase - Duration [s] - Notes * - S1 powered ascent - 0 – 37.4 - 17 MN thrust; CG shifts forward 7.5 m; DXCG grows 0 → 7.5 m * - Coast (ballistic arc) - 37.4 – 131.8 - S1 jettisoned; no thrust; altitude ~138 km at S2 ignition * - S2 powered ascent - 131.8 – 193.0 - 5 MN thrust; S2 CG shifts 0.85 m forward * - Free flight (observation) - 193.0 – 200.0 - Both stages exhausted; coasting on highly elliptical orbit **Sensitivity note** — As the NASA TM warns, this scenario is highly sensitive to integration step size. The three bundled reference runs diverge by 7% in final altitude (sim_04/05 at 251 km vs sim_06 at 234 km), purely from step-size differences. Aetherion at :math:`\Delta t = 0.001\ \text{s}` (the TM-recommended value) reproduces the trajectory to < 0.16% through the entire coast phase and ends within 0.9% of NASA Sim 06 — placing it in the same fine-step cluster. Building and Running ^^^^^^^^^^^^^^^^^^^^ CMake injects the three DML file paths at configure time. The NASA TM recommends :math:`\Delta t \leq 0.001\ \text{s}` for this scenario; the ``--writeInterval 100`` flag produces output at 0.1 s intervals matching the reference CSV cadence: .. code-block:: bash cmake --build --preset release --target TwoStageRocket ./TwoStageRocket \ --outputFileName twostage_output.csv \ --endTime 200.0 \ --timeStep 0.001 \ --writeInterval 100 The DML files are read from :file:`data/TwoStageRocket_S119_source/` at configure time and their absolute paths are embedded into the binary via ``-DTWOSTAGE_INERTIA_FILE``, ``-DTWOSTAGE_PROP_FILE``, and ``-DTWOSTAGE_AERO_FILE`` compile definitions. Architecture ^^^^^^^^^^^^ The TwoStageRocket example departs from the ``ISimulator`` template used in earlier scenarios because the discontinuous stage-separation mass drop requires direct write access to the ODE state vector. Instead, it owns the state directly: .. code-block:: text Simulation::Application (base — defines run() loop) └── TwoStageRocketApplication prepareSimulation() load DML engines → build ECI state writeInitialSnapshot() write t=0 row stepAndRecord() advance by Δt, write CSV row logFinalSummary() print final altitude, speed, mass TwoStageRocketSimulator (owns state directly — no ISimulator) ├── SixDoFStepper Radau IIA RKMK integrator ├── RocketStageModel fuel tracker + DML engine wrapper │ ├── twostage_inertia.dml mass properties interpolation │ └── twostage_prop.dml thrust / mdot selection └── RocketAeroPolicy DAVE-ML aero (twostage_aero.dml) Comparison Plots ^^^^^^^^^^^^^^^^ The figures below compare Aetherion output against **NASA Sim 06** (:file:`Atmos_17_sim_06.csv`). Each panel shows both trajectories on the primary axis and the absolute difference (Aetherion − Sim 06) on the secondary axis. Phase boundaries are marked with dotted verticals at S1 burnout (t = 37.4 s) and S2 ignition (t = 131.8 s). **Overview dashboard** — key quantities on a single figure: .. figure:: _static/atmos17/overview_dashboard.png :alt: Overview comparison dashboard :align: center :width: 100% Side-by-side comparison of Aetherion vs. NASA Sim 06 for all key output quantities over the 200-second two-stage ascent. **Altitude** — :math:`h(t)` with phase markers (final error −0.87%): .. figure:: _static/atmos17/altitudeMsl_m.png :alt: Altitude MSL comparison :align: center :width: 90% **Euler pitch angle** — gravity-turn nose-down rotation (final error −0.46°): .. figure:: _static/atmos17/eulerAngle_rad_Pitch.png :alt: Euler pitch angle comparison :align: center :width: 90% **True airspeed** — atmosphere-relative speed (final error +0.14%): .. figure:: _static/atmos17/trueAirspeed_m_s.png :alt: True airspeed comparison :align: center :width: 90% **Mach number** and **dynamic pressure** — show S1 max-q and the high-altitude near-vacuum regime after ~80 km: .. figure:: _static/atmos17/mach.png :alt: Mach number comparison :align: center :width: 90% .. figure:: _static/atmos17/dynamicPressure_Pa.png :alt: Dynamic pressure comparison :align: center :width: 90% **Earth-relative velocity components** (NED East is the dominant component for this equatorial, eastward launch): .. figure:: _static/atmos17/feVelocity_m_s_Y.png :alt: Earth-relative East velocity comparison :align: center :width: 90% .. figure:: _static/atmos17/feVelocity_m_s_Z.png :alt: Earth-relative Down velocity comparison :align: center :width: 90% **Altitude rate** — upward velocity shows S1 and S2 powered ascent clearly: .. figure:: _static/atmos17/altitudeRateWrtMsl_m_s.png :alt: Altitude rate comparison :align: center :width: 90% **Body pitch rate** (ECI-relative) — shows gravity-turn weathervaning and the coast-phase decay: .. figure:: _static/atmos17/bodyAngularRateWrtEi_rad_s_Pitch.png :alt: Body pitch rate comparison :align: center :width: 90% **Aerodynamic pitching moment** — large during S1 max-q, decays to near-zero above ~70 km: .. figure:: _static/atmos17/aero_bodyMoment_Nm_M.png :alt: Aerodynamic pitching moment comparison :align: center :width: 90% **Atmospheric state** (US Standard Atmosphere 1976 — the model reaches its 84.852 km ceiling during the coast and applies exponential decay above it): .. figure:: _static/atmos17/ambientTemperature_K.png :alt: Ambient temperature comparison :align: center :width: 90% .. figure:: _static/atmos17/airDensity_kg_m3.png :alt: Air density comparison :align: center :width: 90% .. figure:: _static/atmos17/speedOfSound_m_s.png :alt: Speed of sound comparison :align: center :width: 90% **Local gravitational acceleration** (J₂ model): .. figure:: _static/atmos17/localGravity_m_s2.png :alt: Local gravity comparison :align: center :width: 90% **3-D trajectory** — East–Up projection from the equatorial launch site. Trajectory segments are colour-coded by mission phase (S1 burn red, ballistic coast blue, S2 burn purple, final coast green). The dashed grey line is NASA Sim 06. Mission events are annotated with vertical drop lines to the ground plane: .. figure:: _static/atmos17/trajectory_3d.png :alt: 3D rocket trajectory — Aetherion vs. NASA Sim 06 :align: center :width: 95% 3-D trajectory of the two-stage rocket. The ground plane (altitude = 0) is shown as a translucent blue surface. Phase boundaries at S1 burnout (t = 37.4 s), S2 ignition (t = 131.8 s), and S2 burnout (t = 193 s) are marked with annotated drop-lines. Aetherion ends 2 km lower than NASA Sim 06 (232 km vs 234 km) with near-identical speed (8 392 vs 8 381 m/s). The NASA TM bundle contains three independent reference runs of Scenario 17, all produced by the original check-case code but with different step sizes: Aetherion at :math:`\Delta t = 0.001\ \text{s}` (Radau IIA RKMK) ends at **232 km** — placing it within **0.9%** of sim_06. The NASA TM recommends an integration time step of :math:`\Delta t \leq 0.001\ \text{s}` for this scenario. **All validation numbers below are against ``Atmos_17_sim_06.csv`` (referred to as "NASA Sim 06").** Validation Results ^^^^^^^^^^^^^^^^^^ The table below compares Aetherion output against **NASA Sim 06** (:file:`data/Atmos_17_TwoStageRocketToOrbit/Atmos_17_sim_06.csv`). Run with :math:`\Delta t = 0.001\ \text{s}`, ``--writeInterval 100``, ``--endTime 200``. .. list-table:: :header-rows: 1 :widths: 7 12 12 9 9 9 11 11 * - :math:`t` [s] - Alt\ :sub:`Aetherion` [m] - Alt\ :sub:`Sim 06` [m] - Err % - Pitch\ :sub:`Aetherion` [°] - Pitch\ :sub:`Sim 06` [°] - TAS\ :sub:`Aetherion` [m/s] - TAS\ :sub:`Sim 06` [m/s] * - 0 - 0.0 - 0.0 - 0.00% - 55.220 - 55.220 - 0.00 - 0.10 * - 5 - 448.5 - 449.5 - −0.22% - 54.252 - 54.242 - 242.75 - 242.74 * - 10 - 1 827.9 - 1 829.3 - −0.07% - 43.38 - 43.24 - 509.88 - 509.85 * - 20 - 7 252.9 - 7 248.4 - +0.06% - 40.24 - 39.63 - 1 132.82 - 1 132.60 * - 30 - 16 648.7 - 16 628.3 - +0.12% - 37.35 - 36.82 - 1 926.82 - 1 926.28 * - 37.4 (S1 burnout) - 26 787.7 - 26 747.1 - **+0.15%** - 36.09 - 36.13 - 2 701.87 - 2 700.39 * - 50 (coast) - 46 096.5 - 46 027.1 - +0.15% - 33.94 - 33.97 - 2 624.3 - 2 625.5 * - 100 (coast) - 109 276.5 - 109 110.6 - +0.15% - 27.35 - 29.17 - 2 382.9 - 2 384.7 * - 131.8 (S2 ignites) - 138 464 - 138 239 - **+0.16%** - 22.03 - 24.72 - 2 264.8 - 2 266.9 * - 150 (S2 burn) - 154 661 - 153 860 - +0.52% - 18.94 - 22.08 - 3 259.8 - 3 105.2 * - 175 (S2 burn) - 185 155 - 184 731 - **+0.23%** - 15.01 - 17.92 - 5 372.6 - 5 098.1 * - 200 (end) - 232 434 - 234 477 - **−0.87%** - 11.70 - 12.16 - **8 392.3** - **8 380.7** Through the **S1 burn phase** (t = 0–37.4 s) altitude error vs NASA Sim 06 remains below **0.15%** and velocity error below **1.5 m/s**. Through the **coast phase** (t = 37.4–131.8 s) the 0.16% offset holds unchanged — confirming the spatial inertia is correct during ballistic flight. At S2 ignition (t = 131.8 s) altitude error is still only **0.16%**, meaning both simulations enter S2 at essentially the same orbital state. The final state at t = 200 s shows: * Altitude: **−0.87%** (2 043 m below NASA Sim 06) * TAS: **+0.14%** (+11.6 m/s) * Pitch: **−0.46°** (11.70° vs 12.16°) Known Modelling Differences ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 30 70 * - Item - Explanation * - **Inertia relief** - The variable-mass Newton-Euler equation should strictly include an inertia-relief term :math:`-\dot{\mathbf{M}}\,\boldsymbol{\nu}` accounting for the time-varying spatial inertia. Aetherion omits this term but approximates it via the per-step ZOH rebuild of :math:`\mathbf{M}`. At :math:`\Delta t = 0.001\ \text{s}` the per-step inertia change (:math:`\Delta I_{yy} \approx 0.6\ \text{kg·m}^2`) is negligible compared to :math:`I_{yy} \approx 30\ \text{MN·m}^2`, so the ZOH approximation is adequate.