![crates.io](https://img.shields.io/crates/v/sgp4.svg) ![mit-badge](https://img.shields.io/badge/license-MIT-blue.svg) The SGP4 algorithm, ported to Rust from the reference Celestrak implementation [[1]](#1). The code was entirely refactored to leverage Rust's algebraic data types and highlight the relationship between the implementation and the reference mathematical equations [[2]](#2). SGP4 can be called from JavaScript or Python via WebAssembly wrappers. See https://github.com/wasmerio/sgp4 to install and use SGP4 as a WAPM package. The numerical predictions are almost identical to Celestrak's. The observed differences (less than 2 × 10⁻⁷ km for the position and 10⁻⁹ km.s⁻¹ for the velocity three and a half years after the epoch) are well below the accuracy of the algorithm. We drew inspiration from the incomplete https://github.com/natronics/rust-sgp4 to write mathematical expressions using UTF-8 characters. - [Documentation](#documentation) - [Environments without std or alloc](#environments-without-std-or-alloc) - [Benchmark](#benchmark) - [Variables and mathematical expressions](#variables-and-mathematical-expressions) - [Variables](#variables) - [Initialization variables](#initialization-variables) - [Propagation variables](#propagation-variables) - [Third-body initialization variables](#third-body-initialization-variables) - [Third-body propagation variables](#third-body-propagation-variables) - [Mathematical expressions](#mathematical-expressions) - [UT1 to Julian conversion](#ut1-to-julian-conversion) - [Common initialization](#common-initialization) - [Near earth initialization](#near-earth-initialization) - [High altitude near earth initialization](#high-altitude-near-earth-initialization) - [Elliptic high altitude near earth initialization](#elliptic-high-altitude-near-earth-initialization) - [Deep space initialization](#deep-space-initialization) - [Third body perturbations](#third-body-perturbations) - [Resonant deep space initialization](#resonant-deep-space-initialization) - [Geosynchronous deep space initialization](#geosynchronous-deep-space-initialization) - [Molniya deep space initialization](#molniya-deep-space-initialization) - [Common propagation](#common-propagation) - [Near earth propagation](#near-earth-propagation) - [High altitude near earth propagation](#high-altitude-near-earth-propagation) - [Deep space propagation](#deep-space-propagation) - [Third body propagation](#third-body-propagation) - [Resonant deep space propagation](#resonant-deep-space-propagation) - [Lyddane deep space propagation](#lyddane-deep-space-propagation) - [References](#references) ## Documentation The code documentation is hosted at [https://docs.rs/sgp4/latest/sgp4/](https://docs.rs/sgp4/latest/sgp4/). Examples can be found in this repository's _examples_ directory: - _examples/celestrak.rs_ retrieves the most recent Galileo OMMs from Celestrak and propagates them - _examples/omm.rs_ parses and propagates a JSON-encoded OMM - _examples/space-track.rs_ retrieves the 20 most recent launches OMMs from Space-Track and propagates them - _examples/tle.rs_ parses and propagates a TLE - _examples/tle_afspc.rs_ parses and propagates a TLE using the AFSPC compatibility mode - _examples/advanced.rs_ leverages the advanced API to (marginally) accelerate the propagation of deep space resonant satellites To run an example (here _examples/celestrak.rs_), use: ```sh cargo run --example celestrak ``` To run the Space-Track example, you must first assign your Space-Track.org credentials to the fields `identity` and `password` (see lines 3 and 4 in _examples/space-track.rs_). ## Environments without std or alloc This crate supports `no_std` environments. TLE parsing and SGP4 propagation do not require `alloc` either. We use [num-traits](https://docs.rs/num-traits/latest/num_traits/) with [libm](https://docs.rs/libm/latest/libm/) for floating point functions when `std` is not available. All serde-related features, in particular OMM parsing, require `alloc`. ## Benchmark The benchmark code is available at https://github.com/neuromorphicsystems/sgp4-benchmark. It compares two SGP4 implementations in different configurations: - `cpp`: the Celestrak implementation [[1]](#1) in improved mode - `cpp-afspc`: the Celestrak implementation [[1]](#1) in AFSPC compatibility mode - `cpp-fastmath`: the Celestrak implementation [[1]](#1) in improved mode with the `fast-math` compiler flag - `cpp-afspc-fastmath`: the Celestrak implementation [[1]](#1) in AFSPC compatibility mode with the `fast-math` compiler flag - `rust`: our Rust implementation in default mode - `rust-afspc`: our Rust implementation in AFSPC compatibility mode This benchmark must not be confused with the code in this repository's _bench_ directory. The latter considers only a small subset of the Celestrak catalogue (the tests recommended in [[1]](#1)) and does not measure the original C++ implementation. The present results were obtained using a machine with the following configuration: - **CPU** - Intel Core i7-8700 @ 3.20GHz - **RAM** - Kingston DDR4 @ 2.667 GHz - **OS** - Ubuntu 16.04 - **Compilers** - Rust 1.44.1 and gcc 9.3.0 Accuracy measures the maximum propagation error of each implementation with respect to the reference implementation (`cpp-afspc`) over the full Celestrak catalogue (1 minute timestep over 24 hours). | implementation | maximum position error | maximum speed error | | -------------------- | ---------------------- | ------------------- | | `cpp-afspc` | (reference) | (reference) | | `cpp` | 1.05 km | 1.30 × 10⁻³ km.s⁻¹ | | `cpp-fastmath` | 1.05 km | 1.30 × 10⁻³ km.s⁻¹ | | `cpp-afspc-fastmath` | 4.21 × 10⁻⁸ km | 7.51 × 10⁻¹² km.s⁻¹ | | `rust` | 1.05 km | 1.30 × 10⁻³ km.s⁻¹ | | `rust-afspc` | 4.19 × 10⁻⁸ km | 7.46 × 10⁻¹² km.s⁻¹ | The Rust and C++ fast-math errors have the same order of magnitude. In both cases, they can be attributed to mathematically identical expressions implemented with different floating-point operations. Speed measures the time it takes to propagate every satellite in the Celestrak catalogue (1 minute timestep over 24 hours) using a single thread. 100 values are sampled per implementation. | implementation | minimum | Q1 | median | Q3 | maximum | relative difference | | -------------------- | ------- | ------ | ------ | ------ | ------- | ------------------- | | `cpp-afspc` | 8.95 s | 9.02 s | 9.03 s | 9.06 s | 9.18 s | (reference) | | `cpp` | 8.95 s | 9.01 s | 9.04 s | 9.06 s | 9.25 s | + 0 % | | `cpp-fastmath` | 7.67 s | 7.74 s | 7.77 s | 7.79 s | 7.90 s | - 14 % | | `cpp-afspc-fastmath` | 7.70 s | 7.74 s | 7.76 s | 7.79 s | 7.86 s | - 14 % | | `rust` | 8.36 s | 8.41 s | 8.43 s | 8.45 s | 8.53 s | - 7 % | | `rust-afspc` | 8.36 s | 8.41 s | 8.43 s | 8.46 s | 8.59 s | - 7 % | Rust fast-math support is a work in progress (see https://github.com/rust-lang/rust/issues/21690). Similarly to C++, it should have a very small impact on accuracy while providing a substantial speed gain. ## Variables and mathematical expressions ### Variables Each variable is used to store the result of one and only one expression. Most variables are immutable, with the exception of the variable `(E + ω)ᵢ` used to solve Kepler's equation and the state variables `tᵢ`, `nᵢ` and `λᵢ` used to integrate the resonance effects of Earth gravity. The following tables list the variables used in the code and their associated mathematical symbol. Where possible, we used symbols from [[2]](#2). Partial expressions without a name in [[2]](#2) follow the convention `kₙ, n ∈ ℕ` if they are shared between initialization and propagation, and `pₙ, n ∈ ℕ` if they are local to initialization or propagation. 1. [Initialization variables](#initialization-variables) 2. [Propagation variables](#propagation-variables) 3. [Third-body initialization variables](#third-body-initialization-variables) 4. [Third-body propagation variables](#third-body-propagation-variables) --- #### Initialization variables The following variables depend solely on epoch elements. | variable | symbol | description | | :-------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Elements::datetime.year()` | `yᵤ` | Gregorian calendar year | | `Elements::datetime.month()` | `mᵤ` | Gregorian calendar month in the range `[1, 12]` | | `Elements::datetime.day()` | `dᵤ` | Gregorian calendar day in the range `[1, 31]` | | `Elements::datetime.hour()` | `hᵤ` | Hours since midnight in the range `[0, 23]` | | `Elements::datetime.minute()` | `minᵤ` | Minutes since the hour in the range `[0, 59]` | | `Elements::datetime.second()` | `sᵤ` | Seconds since the minute in the range `[0, 59]` | | `Elements::datetime.nanosecond()` | `nsᵤ` | Nanoseconds since the second in the range `[0, 10⁹[` | | `epoch` | `y₂₀₀₀` | Julian years since UTC 1 January 2000 12h00 (J2000) | | `d1900` | `d₁₉₀₀` | Julian days since UTC 1 January 1900 12h00 (J1900) | | `d1970` | `d₁₉₇₀` | Julian days since UTC 1 January 1970 12h00 (J1970) | | `c2000` | `c₂₀₀₀` | Julian centuries since UTC 1 January 2000 12h00 (J2000) | | `geopotential.ae` | `aₑ` | equatorial radius of the earth in km | | `geopotential.ke` | `kₑ` | square root of the earth's gravitational parameter in earth radii³ min⁻² | | `geopotential.j2` | `J₂` | un-normalised second zonal harmonic | | `geopotential.j3` | `J₃` | un-normalised third zonal harmonic | | `geopotential.j4` | `J₄` | un-normalised fourth zonal harmonic | | `kozai_mean_motion` | `n₀` | mean number of orbits per day (Kozai convention) at epoch in rad.min⁻¹ | | `a1` | `a₁` | semi-major axis at epoch (Kozai convention) | | `p0` | `p₀` | partial expression of `𝛿₀` and `𝛿₁` | | `d1` | `𝛿₁` | used in the Kozai to Brouwer conversion | | `d0` | `𝛿₀` | used in the Kozai to Brouwer conversion | | `B*` | `B*` | radiation pressure coefficient in earth radii⁻¹ | | `orbit_0.inclination` | `I₀` | angle between the equator and the orbit plane at epoch in rad | | `orbit_0.right_ascension` | `Ω₀` | angle between vernal equinox and the point where the orbit crosses the equatorial plane at epoch in rad | | `orbit_0.eccentricity` | `e₀` | shape of the orbit at epoch | | `orbit_0.argument_of_perigee` | `ω₀` | angle between the ascending node and the orbit's point of closest approach to the earth at epoch in rad | | `orbit_0.mean_anomaly` | `M₀` | angle of the satellite location measured from perigee at epoch in rad | | `orbit_0.mean_motion` | `n₀"` | mean number of orbits per day (Brouwer convention) at epoch in rad.min⁻¹ | | `p1` | `p₁` | cosine of the inclination at epoch used in multiple expressions during initialization (`θ` in [[2]](#2), renamed to avoid confusion with the sidereal time) | | `p2` | `p₂` | partial expression of multiple initialization expressions | | `a0` | `a₀"` | semi-major axis at epoch (Brouwer convention) | | `p3` | `p₃` | perigee in earth radii | | `p4` | `p₄` | height of perigee in km | | `p5` | `p₅` | partial expression of `s` | | `s` | `s` | altitude parameter of the atmospheric drag expression | | `p6` | `p₆` | partial expression of the atmospheric drag | | `xi` | `ξ` | partial expression of multiple initialization expressions | | `p7` | `p₇` | partial expression of multiple initialization expressions | | `eta` | `η` | partial expression of multiple initialization expressions and of the argument of perigee and mean anomaly in eccentric high altitude near earth propagation | | `p8` | `p₈` | partial expression of multiple initialization expressions | | `p9` | `p₉` | partial expression of multiple initialization expressions | | `c1` | `C₁` | partial expression of multiple initialization and propagation expressions | | `p10` | `p₁₀` | partial expression of multiple initialization expressions | | `b0` | `β₀` | partial expression of multiple initialization expressions | | `p11` | `p₁₁` | partial expression of multiple initialization expressions | | `p12` | `p₁₂` | partial expression of multiple initialization expressions | | `p13` | `p₁₃` | partial expression of multiple initialization expressions | | `p14` | `p₁₄` | partial expression of multiple initialization expressions | | `p15` | `p₁₅` | partial expression of multiple initialization expressions | | `k14` | `k₁₄` | first order coefficient of the argument of perigee before adding solar and lunar perturbations | | `c4` | `C₄` | partial expression of multiple initialization and propagation expressions (differs from the `C₄` expression in [[2]](#2) by a factor B\*) | | `right_ascension_dot` | `Ω̇` | first order coefficient of the right ascension | | `argument_of_perigee_dot` | `ω̇` | first order coefficient of the argument of perigee | | `mean_anomaly_dot` | `Ṁ` | first order coefficient of the mean anomaly | | `k0` | `k₀` | second order coefficient of the right ascension before adding perturbations | | `k1` | `k₁` | partial expression of the second order coefficient of the mean anomaly | | `k2` | `k₂` | partial expression of `aᵧₙ` in near earth propagation | | `k3` | `k₃` | partial expression of `rₖ`, `ṙₖ` and `rḟₖ` in near earth propagation | | `k4` | `k₄` | partial expression of `uₖ` in near earth propagation | | `k5` | `k₅` | partial expression of the initial Kepler variable `p₃₈` in near earth propagation | | `k6` | `k₆` | partial expression of multiple initialization expressions and of `rₖ` and `rḟₖ` in near earth propagation | | `d2` | `D₂` | partial expression of multiple near earth initialization expressions and of the semi-major axis in near earth propagation | | `p16` | `p₁₆` | partial expression of multiple near earth initialization expressions | | `d3` | `D₃` | partial expression of multiple near earth initialization expressions and of the semi-major axis in near earth propagation | | `d4` | `D₄` | partial expression of multiple near earth initialization expressions and of the semi-major axis in near earth propagation | | `c5` | `C₅` | partial expression of multiple initialization and propagation expressions (differs from the `C₅` expression in [[2]](#2) by a factor B\*) | | `k7` | `k₇` | sine of the mean anomaly at epoch | | `k8` | `k₈` | partial expression of the mean anomaly third order coefficient in high altitude near earth propagation | | `k9` | `k₉` | partial expression of the mean anomaly fourth order coefficient in high altitude near earth propagation | | `k10` | `k₁₀` | partial expression of the mean anomaly fifth order coefficient in high altitude near earth propagation | | `k11` | `k₁₁` | partial expression of the argument of perigee and mean anomaly in eccentric high altitude near earth propagation | | `k12` | `k₁₂` | partial expression of the argument of perigee and mean anomaly in eccentric high altitude near earth propagation | | `k13` | `k₁₃` | partial expression of the argument of perigee and mean anomaly in eccentric high altitude near earth propagation | | `lunar_right_ascension_epsilon` | `Ωₗₑ` | lunar right ascension of the ascending node | | `lunar_right_ascension_sine` | `sin Ωₗ` | sine of the lunar right ascension of the ascending node referred to the equator | | `lunar_right_ascension_cosine` | `cos Ωₗ` | cosine of the lunar right ascension of the ascending node referred to the equator | | `lunar_argument_of_perigee` | `ωₗ` | lunar argument of perigee | | `sidereal_time_0` | `θ₀` | Greenwich sidereal time at epoch | | `lambda_0` | `λ₀` | Earth gravity resonance variable at epoch | | `lambda_dot_0` | `λ̇₀` | time derivative of the Earth gravity resonance variable at epoch | | `p17` | `p₁₇` | partial expression of `𝛿ᵣ₁`, `𝛿ᵣ₂` and `𝛿ᵣ₃` | | `dr1` | `𝛿ᵣ₁` | first Earth gravity resonance coefficient for geosynchronous orbits (`𝛿₁` in [[2]](#2), renamed to avoid confusion with `𝛿₁` used in the Kozai to Brouwer conversion) | | `dr2` | `𝛿ᵣ₂` | second Earth gravity resonance coefficient for geosynchronous orbits (`𝛿₂` in [[2]](#2), renamed to match `𝛿ᵣ₁`) | | `dr3` | `𝛿ᵣ₃` | third Earth gravity resonance coefficient for geosynchronous orbits (`𝛿₃` in [[2]](#2), renamed to match `𝛿ᵣ₁`) | | `p18` | `p₁₈` | partial expression of `D₂₂₀₋₁` and `D₂₂₁₁` | | `p19` | `p₁₉` | partial expression of `D₃₂₁₀` and `D₃₂₂₂` | | `p20` | `p₂₀` | partial expression of `D₄₄₁₀` and `D₄₄₂₂` | | `p21` | `p₂₁` | partial expression of `D₅₂₂₀`, `D₅₂₃₂`, `D₅₄₂₁` and `D₅₄₃₃` | | `f220` | `F₂₂₀` | partial expression of `D₂₂₀₋₁` and `D₄₄₁₀` | | `g211` | `G₂₁₁` | partial expression of `D₂₂₁₁` | | `g310` | `G₃₁₀` | partial expression of `D₃₂₁₀` | | `g322` | `G₃₂₂` | partial expression of `D₃₂₂₂` | | `g410` | `G₄₁₀` | partial expession of `D₄₄₁₀` | | `g422` | `G₄₂₂` | partial expession of `D₄₄₂₂` | | `g520` | `G₅₂₀` | partial expression of `D₅₂₂₀` | | `g532` | `G₅₃₂` | partial expression of `D₅₂₃₂` | | `g521` | `G₅₂₁` | partial expression of `D₅₄₂₁` | | `g533` | `G₅₃₃` | partial expression of `D₅₄₃₃` | | `d220₋1` | `D₂₂₀₋₁` | gravity resonance coefficient for Molniya orbits (the `Dₗₘₚₖ` expression in [[2]](#2) is missing a factor `l - 2p + k` from the original equation in [[4]](#4) with `k = -1` instead of `1`) | | `d2211` | `D₂₂₁₁` | gravity resonance coefficient for Molniya orbits (the `Dₗₘₚₖ` expression in [[2]](#2) is missing a factor `l - 2p + k` from the original equation in [[4]](#4)) | | `d3210` | `D₃₂₁₀` | see `D₂₂₁₁` | | `d3222` | `D₃₂₂₂` | see `D₂₂₁₁` | | `d4410` | `D₄₄₁₀` | see `D₂₂₁₁` | | `d4422` | `D₄₄₂₂` | see `D₂₂₁₁` | | `d5220` | `D₅₂₂₀` | see `D₂₂₁₁` | | `d5232` | `D₅₂₃₂` | see `D₂₂₁₁` | | `d5421` | `D₅₄₂₁` | see `D₂₂₁₁` | | `d5433` | `D₅₄₃₃` | see `D₂₂₁₁` | #### Propagation variables The following expressions depend on the propagation time `t`. | variable | symbol | description | | :---------------------------- | :---------- | :------------------------------------------------------------------------------------------------------------------------ | | `t` | `t` | minutes elapsed since epoch (can be negative) | | `p22` | `p₂₂` | right ascension of the ascending node with neither Earth gravity resonance nor Sun and Moon contributions | | `p23` | `p₂₃` | argument of perigee with neither high altitude drag effects, Earth gravity resonance nor Sun and Moon contributions | | `orbit.inclination` | `I` | inclination at epoch plus `t` without the short-period effects of Earth gravity | | `orbit.right_ascension` | `Ω` | right ascension of the ascending node at epoch plus `t` without the short-period effects of Earth gravity | | `orbit.eccentricity` | `e` | eccentricity at epoch plus `t` without the short-period effects of Earth gravity | | `orbit.argument_of_perigee` | `ω` | argument of perigee at epoch plus `t` without the short-period effects of Earth gravity | | `orbit.mean_anomaly` | `M` | mean anomaly at epoch plus `t` without the short-period effects of Earth gravity | | `orbit.mean_motion` | `n` | mean motion at epoch plus `t` without the short-period effects of Earth gravity | | `a` | `a` | semi-major axis | | `p32` | `p₃₂` | partial expression of `aᵧₙ` | | `p33` | `p₃₃` | partial expression of `rₖ`, `ṙₖ` and `rḟₖ` | | `p34` | `p₃₄` | partial expression of `uₖ` | | `p35` | `p₃₅` | partial expression of the initial Kepler variable `p₃₈` | | `p36` | `p₃₆` | partial expression of `rₖ` and `rḟₖ` | | `p37` | `p₃₇` | partial expression of `aᵧₙ` and the initial Kepler variable `p₃₈` | | `axn` | `aₓₙ` | normalized linear eccentricity projected on the line of nodes | | `ayn` | `aᵧₙ` | normalized linear eccentricity projected on the normal to the line of nodes | | `p38` | `p₃₈` | initial Kepler variable (`U` in [[2]](#2), renamed to avoid confusion with the true anomaly plus argument of perigee `u`) | | `ew` | `(E + ω)ᵢ` | Kepler variable used in an iterative process to estimate the eccentric anomaly `E` | | `delta ` | `Δ(E + ω)ᵢ` | correction to the Kepler variable at iteration `i` | | `p39` | `p₃₉` | eccentricity at epoch plus `t` | | `pl` | `pₗ` | semi-latus rectum | | `p40` | `p₄₀` | normalized linear eccentricity projected on the semi-minor axis | | `r` | `r` | radius (distance to the focus) without the short-period effects of Earth gravity | | `r_dot` | `ṙ` | radius time derivative without the short-period effects of Earth gravity | | `b` | `β` | semi-minor axis over semi-major axis | | `p41` | `p₄₁` | partial expression of `p₄₂` and `p₄₃` | | `p42` | `p₄₂` | sine of `u` | | `p43` | `p₄₃` | cosine of `u` | | `u` | `u` | true anomaly plus argument of perigee without the short-period effects of Earth gravity | | `p44` | `p₄₄` | `sin(2 u)`, partial expression of `uₖ`, `Ωₖ` and `ṙₖ` | | `p45` | `p₄₅` | `cos(2 u)`, partial expression of `rₖ`, `Iₖ` and `rḟₖ` | | `p46` | `p₄₆` | partial expression of `rₖ`, `uₖ`, `Iₖ` and `Ωₖ` | | `rk` | `rₖ` | radius (distance to the focus) | | `uk` | `uₖ` | true anomaly plus argument of perigee | | `inclination_k` | `Iₖ` | inclination at epoch plus `t` | | `right_ascension_k` | `Ωₖ` | right ascension at epoch plus `t` | | `rk_dot` | `ṙₖ` | radius time derivative | | `rfk_dot` | `rḟₖ` | radius times the true anomaly derivative | | `u0` | `u₀` | x component of the position unit vector | | `u1` | `u₁` | y component of the position unit vector | | `u2` | `u₂` | z component of the position unit vector | | `prediction.position[0]` | `r₀` | x component of the position vector in km (True Equator, Mean Equinox (TEME) of epoch reference frame) | | `prediction.position[1]` | `r₁` | y component of the position vector in km (True Equator, Mean Equinox (TEME) of epoch reference frame) | | `prediction.position[2]` | `r₂` | z component of the position vector in km (True Equator, Mean Equinox (TEME) of epoch reference frame) | | `prediction.velocity[0]` | `ṙ₀` | x component of the velocity vector in km.s⁻¹ (True Equator, Mean Equinox (TEME) of epoch reference frame) | | `prediction.velocity[1]` | `ṙ₁` | y component of the velocity vector in km.s⁻¹ (True Equator, Mean Equinox (TEME) of epoch reference frame) | | `prediction.velocity[2]` | `ṙ₂` | z component of the velocity vector in km.s⁻¹ (True Equator, Mean Equinox (TEME) of epoch reference frame) | | `p24` | `p₂₄` | mean anomaly without drag contributions in near earth propagation | | `p25` | `p₂₅` | partial expression of `ω` and `M` in near earth propagation | | `p26` | `p₂₆` | mean anomaly with elliptic correction and without drag contributions in near earth propagation | | `p27` | `p₂₇` | non-clamped eccentricity in near earth propagation | | `p28` | `p₂₈` | semi-major axis with resonance correction in deep space propagation | | `p29` | `p₂₉` | mean anomaly with resonance correction in deep space propagation | | `p31` | `p₃₁` | non-clamped eccentricity in deep space propagation | | `sidereal_time` | `θ` | sidereal time at epoch plus `t` | | `delta_t` | `Δt` | time step used in the integration of resonance effects of Earth gravity in min (either `720` or `-720`) | | `lambda_dot` | `λ̇ᵢ` | resonance effects of Earth gravity variable's time derivative at epoch plus `i Δt` | | `ni_dot` | `ṅᵢ` | mean motion time derivative at epoch plus `i Δt` | | `ni_ddot` | `n̈ᵢ` | mean motion second time derivative at epoch plus `i Δt` | | `ResonanceState::t` | `tᵢ` | resonance effects of Earth gravity integrator time (`i Δt`) | | `ResonanceState::mean_motion` | `nᵢ` | mean motion time derivative at epoch plus `Δt i` | | `ResonanceState::lambda` | `λᵢ` | resonance effects of Earth gravity variable at epoch plus `i Δt` | | `p30` | `p₃₀` | non-normalised `Ω` in Lyddane deep space propagation | #### Third-body initialization variables The contribution of the Sun and the Moon to the orbital elements are calculated with a unique set of expressions. _src/third_body.rs_ provides a generic implementation of these expressions. Variables specific to the third body (either the Sun or the Moon) are annotated with `x`. In every other file, these variables are annotated with `s` if they correspond to solar perturbations, and `l` if they correspond to lunar perturbations. The `aₓₙ`, `Xₓₙ`, `Zₓₙ` (`n ∈ ℕ`), `Fₓ₂` and `Fₓ₃` variables correspond to the `aₙ`, `Xₙ`, `Zₙ`, `F₂` and `F₃` variables in [[2]](#2). The added `x` highlights the dependence on the perturbing third body. The following variables depend solely on epoch elements. | variable | symbol | description | | :-------------------------------------- | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `third_body_inclination_sine` | `sin Iₓ` | sine of the inclination of the Sun (`sin Iₛ`) or the Moon (`sin Iₗ`) | | `third_body_inclination_cosine` | `cos Iₓ` | cosine of the inclination of the Sun (`cos Iₛ`) or the Moon (`cos Iₗ`) | | `delta_right_ascension_sine` | `sin(Ω₀ - Ωₓ)` | sine of the difference between the right ascension of the ascending node of the satellite at epoch and the Sun's (`sin(Ω₀ - Ωₛ)`) or the Moon's (`sin(Ω₀ - Ωₗ)`) | | `delta_right_ascension_cosine` | `cos(Ω₀ - Ωₓ)` | cosine of the difference between the right ascension of the ascending node of the satellite at epoch and the Sun's (`cos(Ω₀ - Ωₛ)`) or the Moon's (`cos(Ω₀ - Ωₗ)`) | | `third_body_argument_of_perigee_sine` | `sin ωₓ` | sine of the argument of perigee of the Sun (`sin ωₛ`) or the Moon (`sin ωₗ`) | | `third_body_argument_of_perigee_cosine` | `cos ωₓ` | cosine of the argument of perigee of the Sun (`sin ωₛ`) or the Moon (`cos ωₗ`) | | `third_body_mean_anomaly_0` | `Mₓ₀` | mean anomaly at epoch of the Sun (`Mₛ₀`) or the Moon (`Mₗ₀`) | | `ax1` | `aₓ₁` | partial expression of multiple `Xₓₙ` and `Zₓₙ` expressions | | `ax3` | `aₓ₃` | partial expression of multiple `Xₓₙ` and `Zₓₙ` expressions | | `ax7` | `aₓ₇` | partial expression of multiple `aₓ₂` and `aₓ₅` | | `ax8` | `aₓ₈` | partial expression of multiple `aₓ₂` and `aₓ₅` | | `ax9` | `aₓ₉` | partial expression of multiple `aₓ₄` and `aₓ₆` | | `ax10` | `aₓ₁₀` | partial expression of multiple `aₓ₄` and `aₓ₆` | | `ax2` | `aₓ₂` | partial expression of multiple `Xₓₙ` and `Zₓₙ` expressions | | `ax4` | `aₓ₄` | partial expression of multiple `Xₓₙ` and `Zₓₙ` expressions | | `ax5` | `aₓ₅` | partial expression of multiple `Xₓₙ` and `Zₓₙ` expressions | | `ax6` | `aₓ₆` | partial expression of multiple `Xₓₙ` and `Zₓₙ` expressions | | `xx1` | `Xₓ₁` | partial expression of multiple `Zₓₙ` expressions, `kₓ₀`, `kₓ₁` and `ėₓ` | | `xx2` | `Xₓ₂` | partial expression of multiple `Zₓₙ` expressions, `kₓ₀`, `kₓ₁` and `ėₓ` | | `xx3` | `Xₓ₃` | partial expression of multiple `Zₓₙ` expressions, `kₓ₀`, `kₓ₁` and `ėₓ` | | `xx4` | `Xₓ₄` | partial expression of multiple `Zₓₙ` expressions, `kₓ₀`, `kₓ₁` and `ėₓ` | | `xx5` | `Xₓ₅` | partial expression of multiple `Zₓₙ` expressions | | `xx6` | `Xₓ₆` | partial expression of multiple `Zₓₙ` expressions | | `xx7` | `Xₓ₇` | partial expression of multiple `Zₓₙ` expressions | | `xx8` | `Xₓ₈` | partial expression of multiple `Zₓₙ` expressions | | `zx31` | `Zₓ₃₁` | partial expression of `Zₓ₃`, `kₓ₈` and `ω̇ₓ` | | `zx32` | `Zₓ₃₂` | partial expression of `Zₓ₂`, `kₓ₇` and `ω̇ₓ` | | `zx33` | `Zₓ₃₃` | partial expression of `Zₓ₃`, `kₓ₈` and `ω̇ₓ` | | `zx11` | `Zₓ₁₁` | partial expression of `kₓ₃` and `İₓ` | | `zx13` | `Zₓ₁₃` | partial expression of `kₓ₃` and `İₓ` | | `zx21` | `Zₓ₂₁` | partial expression of `kₓ₁₁` and `Ω̇ₓ` | | `zx23` | `Zₓ₂₃` | partial expression of `kₓ₁₁` and `Ω̇ₓ` | | `zx1` | `Zₓ₁` | partial expression of `kₓ₅` and `Ṁₓ` | | `zx3` | `Zₓ₃` | partial expression of `kₓ₅` and `Ṁₓ` | | `px0` | `pₓ₀` | partial expression of multiple `kₓₙ` expressions and `Ṁₓ` | | `px1` | `pₓ₁` | partial expression of multiple `kₓₙ` expressions and `İₓ` | | `px2` | `pₓ₂` | partial expression of multiple `kₓₙ` expressions and `ω̇ₓ` | | `px3` | `pₓ₃` | partial expression of multiple `kₓₙ` expressions and `ėₓ` | | `kx0` | `kₓ₀` | `Fₓ₂` coefficient of `δeₓ` | | `kx1` | `kₓ₁` | `Fₓ₃` coefficient of `δeₓ` | | `kx2` | `kₓ₂` | `Fₓ₂` coefficient of `δIₓ` | | `kx3` | `kₓ₃` | `Fₓ₃` coefficient of `δIₓ` | | `kx4` | `kₓ₄` | `Fₓ₂` coefficient of `δMₓ` | | `kx5` | `kₓ₅` | `Fₓ₃` coefficient of `δMₓ` | | `kx6` | `kₓ₆` | `sin fₓ` coefficient of `δMₓ` | | `kx7` | `kₓ₇` | `Fₓ₂` coefficient of `pₓ₄` | | `kx8` | `kₓ₈` | `Fₓ₃` coefficient of `pₓ₄` | | `kx9` | `kₓ₉` | `sin fₓ` coefficient of `pₓ₄` | | `kx10` | `kₓ₁₀` | `Fₓ₂` coefficient of `pₓ₅` | | `kx11` | `kₓ₁₁` | `Fₓ₃` coefficient of `pₓ₅` | | `third_body_dots.inclination` | `İₓ` | secular contribution of the Sun (`İₛ`) or the Moon (`İₗ`) to the inclination | | `third_body_right_ascension_dot` | `Ω̇ₓ` | secular contribution of the Sun (`Ω̇ₛ`) or the Moon (`Ω̇ₗ`) to the right ascension of the ascending node | | `third_body_dots.eccentricity` | `ėₓ` | secular contribution of the Sun (`ėₛ`) or the Moon (`ėₗ`) to the eccentricity | | `third_body_dots.agument_of_perigee` | `ω̇ₓ` | secular contribution of the Sun (`ω̇ₛ`) or the Moon (`ω̇ₗ`) to the argument of perigee | | `third_body_dots.mean_anomaly` | `Ṁₓ` | secular contribution of the Sun (`Ṁₛ`) or the Moon (`Ṁₗ`) to the mean anomaly | #### Third-body propagation variables The following variables depend on the propagation time `t`. | variable | symbol | description | | :------------------------------ | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `third_body_mean_anomaly` | `Mₓ` | mean anomaly of the Sun (`Mₛ`) or the Moon (`Mₗ`) | | `fx` | `fₓ` | third body true anomaly | | `fx2` | `Fₓ₂` | partial expression of the third body long-period periodic contribution | | `fx3` | `Fₓ₃` | partial expression of the third body long-period periodic contribution | | `third_body_delta_eccentricity` | `δeₓ` | long-period periodic contribution of the Sun (`δeₛ`) or the Moon (`δeₗ`) to the eccentricity | | `third_body_delta_inclination` | `δIₓ` | long-period periodic contribution of the Sun (`δIₛ`) or the Moon (`δIₗ`) to the inclination | | `third_body_delta_mean_mootion` | `δMₓ` | long-period periodic contribution of the Sun (`δMₛ`) or the Moon (`δMₗ`) to the mean motion | | `px4` | `pₓ₄` | partial expression of the long-period periodic contribution of the Sun (`pₛ₄`) or the Moon (`pₗ₄`) to the right ascension of the ascending node and the argument of perigee | | `px5` | `pₓ₅` | partial expression of the long-period periodic contribution of the Sun (`pₛ₅`) or the Moon (`pₗ₅`) to the right ascension of the ascending node | ### Mathematical expressions #### UT1 to Julian conversion The epoch (Julian years since UTC 1 January 2000 12h00) can be calculated with either the AFSPC formula: ``` y₂₀₀₀ = (367 yᵤ - ⌊7 (yᵤ + ⌊(mᵤ + 9) / 12⌋) / 4⌋ + 275 ⌊mᵤ / 9⌋ + dᵤ + 1721013.5 + (((nsᵤ / 10⁹ + sᵤ) / 60 + minᵤ) / 60 + hᵤ) / 24 - 2451545) / 365.25 ``` or the more accurate version of the same formula: ``` y₂₀₀₀ = (367 yᵤₜ₁ - ⌊7 (yᵤₜ₁ + ⌊(mᵤₜ₁ + 9) / 12⌋) / 4⌋ + 275 ⌊mᵤₜ₁ / 9⌋ + dᵤₜ₁ - 730531) / 365.25 + (3600 hᵤₜ₁ + 60 minᵤₜ₁ + sᵤₜ₁ - 43200) / (24 × 60 × 60 × 365.25) + nsᵤₜ₁ / (24 × 60 × 60 × 365.25 × 10⁹) ``` #### Common initialization ``` a₁ = (kₑ / n₀)²ᐟ³ 3 3 cos²I₀ - 1 p₀ = - J₂ --------------- 4 (1 − e₀²)³ᐟ² 𝛿₁ = p₀ / a₁² 𝛿₀ = p₀ / (a₁ (1 - ¹/₃ 𝛿₁ - 𝛿₁² - ¹³⁴/₈₁ 𝛿₁³))² n₀" = n₀ / (1 + 𝛿₀) p₁ = cos I₀ p₂ = 1 − e₀² k₆ = 3 p₁² - 1 a₀" = (kₑ / n₀")²ᐟ³ p₃ = a₀" (1 - e₀) p₄ = aₑ (p₃ - 1) p₅ = │ 20 if p₄ < 98 │ p₄ - 78 if 98 ≤ p₄ < 156 │ 78 otherwise s = p₅ / aₑ + 1 p₆ = ((120 - p₅) / aₑ)⁴ ξ = 1 / (a₀" - s) p₇ = p₆ ξ⁴ η = a₀" e₀ ξ p₈ = |1 - η²| p₉ = p₇ / p₈⁷ᐟ² C₁ = B* p₉ n₀" (a₀" (1 + ³/₂ η² + e₀ η (4 + η²)) + ³/₈ J₂ ξ k₆ (8 + 3 η² (8 + η²)) / p₈) p₁₀ = (a₀" p₂)⁻² β₀ = p₂¹ᐟ² p₁₁ = ³/₂ J₂ p₁₀ n₀" p₁₂ = ¹/₂ p₁₁ J₂ p₁₀ p₁₃ = - ¹⁵/₃₂ J₄ p₁₀² n₀" p₁₄ = - p₁₁ p₁ + (¹/₂ p₁₂ (4 - 19 p₁²) + 2 p₁₃ (3 - 7 p₁²)) p₁ k₁₄ = - ¹/₂ p₁₁ (1 - 5 p₁²) + ¹/₁₆ p₁₂ (7 - 114 p₁² + 395 p₁⁴) p₁₅ = n₀" + ¹/₂ p₁₁ β₀ k₆ + ¹/₁₆ p₁₂ β₀ (13 - 78 p₁² + 137 p₁⁴) C₄ = 2 B* n₀" p₉ a₀" p₂ ( η (2 + ¹/₂ η²) + e₀ (¹/₂ + 2 η²) - J₂ ξ / (a p₈) (-3 k₆ (1 - 2 e₀ η + η² (³/₂ - ¹/₂ e₀ η)) + ³/₄ (1 - p₁²) (2 η² - e₀ η (1 + η²)) cos 2 ω₀) k₀ = ⁷/₂ p₂ p₁₁ p₁ C₁ k₁ = ³/₂ C₁ Ω̇ = │ p₁₄ if n₀" > 2π / 225 │ p₁₄ + (Ω̇ₛ + Ω̇ₗ) otherwise ω̇ = │ k₁₄ if n₀" > 2π / 225 │ k₁₄ + (ω̇ₛ + ω̇ₗ) otherwise Ṁ = │ p₁₅ if n₀" > 2π / 225 │ p₁₅ + (Ṁₛ + Ṁₗ) otherwise ``` #### Near earth initialization Defined only if `n₀" > 2π / 225` (near earth). ``` 1 J₃ k₂ = - - -- sin I₀ 2 J₂ k₃ = 1 - p₁² k₄ = 7 p₁² - 1 │ 1 J₃ 3 + 5 p₁ k₅ = │ - - -- sin I₀ -------- if |1 + p₁| > 1.5 × 10⁻¹² │ 4 J₂ 1 + p₁ │ 1 J₃ 3 + 5 p₁ │ - - -- sin I₀ ----------- otherwise │ 4 J₂ 1.5 × 10⁻¹² ``` #### High altitude near earth initialization Defined only if `n₀" > 2π / 225` (near earth) and `p₃ ≥ 220 / (aₑ + 1)` (high altitude). ``` D₂ = 4 a₀" ξ C₁² p₁₆ = D₂ ξ C₁ / 3 D₃ = (17 a + s) p₁₆ D₄ = ¹/₂ p₁₆ a₀" ξ (221 a₀" + 31 s) C₁ C₅ = 2 B* p₉ a₀" p₂ (1 + 2.75 (η² + η e₀) + e₀ η³) k₁₁ = (1 + η cos M₀)³ k₇ = sin M₀ k₈ = D₂ + 2 C₁² k₉ = ¹/₄ (3 D₃ + C₁ (12 D₂ + 10 C₁²)) k₁₀ = ¹/₅ (3 D₄ + 12 C₁ D₃ + 6 D₂² + 15 C₁² (2 D₂ + C₁²)) ``` #### Elliptic high altitude near earth initialization Defined only if `n₀" > 2π / 225` (near earth), `p₃ ≥ 220 / (aₑ + 1)` (high altitude) and `e₀ > 10⁻⁴` (elliptic). ``` J₃ p₇ ξ n₀" sin I₀ k₁₂ = - 2 B* cos ω₀ -- ---------------- J₂ e₀ 2 p₇ B* k₁₃ = - - ----- 3 e₀ η ``` #### Deep space initialization Defined only if `n₀" ≤ 2π / 225` (deep space). ``` e₁₉₀₀ = 365.25 (t₀ + 100) sin Iₛ = 0.39785416 cos Iₛ = 0.91744867 sin(Ω₀ - Ωₛ) = sin Ω₀ cos(Ω₀ - Ωₛ) = cos Ω₀ sin ωₛ = -0.98088458 cos ωₛ = 0.1945905 Mₛ₀ = (6.2565837 + 0.017201977 e₁₉₀₀) rem 2π Ωₗₑ = 4.523602 - 9.2422029 × 10⁻⁴ e₁₉₀₀ rem 2π cos Iₗ = 0.91375164 - 0.03568096 Ωₗₑ sin Iₗ = (1 - cos²Iₗ)¹ᐟ² sin Ωₗ = 0.089683511 sin Ωₗₑ / sin Iₗ cos Ωₗ = (1 - sin²Ωₗ)¹ᐟ² ωₗ = 5.8351514 + 0.001944368 e₁₉₀₀ 0.39785416 sin Ωₗₑ / sin Iₗ + tan⁻¹ ------------------------------------------ - Ωₗₑ cos Ωₗ cos Ωₗₑ + 0.91744867 sin Ωₗ sin Ωₗₑ sin(Ω₀ - Ωₗ) = sin Ω₀ cos Ωₗ - cos Ω₀ sin Ωₗ cos(Ω₀ - Ωₗ) = cos Ωₗ cos Ω₀ + sin Ωₗ sin Ω₀ Mₗ₀ = (-1.1151842 + 0.228027132 e₁₉₀₀) rem 2π ``` #### Third body perturbations Defined only if `n₀" ≤ 2π / 225` (deep space). The following variables are evaluated for two third bodies, the Sun (solar perturbations `s`) and the Moon (lunar perturbations `l`). Variables specific to the third body are annotated with `x`. In other sections, `x` is either `s` or `l`. ``` aₓ₁ = cos ωₓ cos(Ω₀ - Ωₓ) + sin ωₓ cos Iₓ sin(Ω₀ - Ωₓ) aₓ₃ = - sin ωₓ cos(Ω₀ - Ωₓ) + cos ωₓ cos Iₓ sin(Ω₀ - Ωₓ) aₓ₇ = - cos ωₓ sin(Ω₀ - Ωₓ) + sin ωₓ cos Iₓ cos(Ω₀ - Ωₓ) aₓ₈ = sin ωₓ sin Iₓ aₓ₉ = sin ωₓ sin(Ω₀ - Ωₓ) + cos ωₓ cos Iₓ cos(Ω₀ - Ωₓ) aₓ₁₀ = cos ωₓ sin Iₓ aₓ₂ = aₓ₇ cos i₀ + aₓ₈ sin I₀ aₓ₄ = aₓ₉ cos i₀ + aₓ₁₀ sin I₀ aₓ₅ = - aₓ₇ sin I₀ + aₓ₈ cos I₀ aₓ₆ = - aₓ₉ sin I₀ + aₓ₁₀ cos I₀ Xₓ₁ = aₓ₁ cos ω₀ + aₓ₂ sin ω₀ Xₓ₂ = aₓ₃ cos ω₀ + aₓ₄ sin ω₀ Xₓ₃ = - aₓ₁ sin ω₀ + aₓ₂ cos ω₀ Xₓ₄ = - aₓ₃ sin ω₀ + aₓ₄ cos ω₀ Xₓ₅ = aₓ₅ sin ω₀ Xₓ₆ = aₓ₆ sin ω₀ Xₓ₇ = aₓ₅ cos ω₀ Xₓ₈ = aₓ₆ cos ω₀ Zₓ₃₁ = 12 Xₓ₁² - 3 Xₓ₃² Zₓ₃₂ = 24 Xₓ₁ Xₓ₂ - 6 Xₓ₃ Xₓ₄ Zₓ₃₃ = 12 Xₓ₂² - 3 Xₓ₄² Zₓ₁₁ = - 6 aₓ₁ aₓ₅ + e₀² (- 24 Xₓ₁ Xₓ₇ - 6 Xₓ₃ Xₓ₅) Zₓ₁₃ = - 6 aₓ₃ aₓ₆ + e₀² (-24 Xₓ₂ Xₓ₈ - 6 Xₓ₄ Xₓ₆) Zₓ₂₁ = 6 aₓ₂ aₓ₅ + e₀² (24.0 Xₓ₁ Xₓ₅ - 6 Xₓ₃ Xₓ₇) Zₓ₂₃ = 6 aₓ₄ aₓ₆ + e₀² (24 Xₓ₂ Xₓ₆ - 6 Xₓ₄ Xₓ₈) Zₓ₁ = 2 (3 (aₓ₁² + aₓ₂²) + Zₓ₃₁ e₀²) + p₁ Zₓ₃₁ Zₓ₃ = 2 (3 (aₓ₃² + aₓ₄²) + Zₓ₃₃ e₀²) + p₁ Zₓ₃₃ pₓ₀ = Cₓ / n₀" 1 pₓ₀ pₓ₁ = - - --- 2 β₀ pₓ₂ = pₓ₀ β₀ pₓ₃ = - 15 e₀ pₓ₂ Ω̇ₓ = │ 0 if I₀ < 5.2359877 × 10⁻² │ or I₀ > π - 5.2359877 × 10⁻² │ - nₓ pₓ₁ (Zₓ₂₁ + Zₓ₂₃) / sin I₀ otherwise kₓ₀ = 2 pₓ₃ (Xₓ₂ Xₓ₃ + Xₓ₁ Xₓ₄) kₓ₁ = 2 pₓ₃ (Xₓ₂ Xₓ₄ - Xₓ₁ Xₓ₃) kₓ₂ = 2 pₓ₁ (- 6 (aₓ₁ aₓ₆ + aₓ₃ aₓ₅) + e₀² (- 24 (Xₓ₂ Xₓ₇ + Xₓ₁ Xₓ₈) - 6 (Xₓ₃ Xₓ₆ + Xₓ₄ Xₓ₅))) kₓ₃ = 2 pₓ₁ (Zₓ₁₃ - Zₓ₁₁) kₓ₄ = - 2 pₓ₀ (2 (6 (aₓ₁ aₓ₃ + aₓ₂ aₓ₄) + Zₓ₃₂ e₀²) + p₁ Zₓ₃₂) kₓ₅ = - 2 pₓ₀ (Zₓ₃ - Zₓ₁) kₓ₆ = - 2 pₓ₀ (- 21 - 9 e₀²) eₓ kₓ₇ = 2 pₓ₂ Zₓ₃₂ kₓ₈ = 2 pₓ₂ (Zₓ₃₃ - Zₓ₃₁) kₓ₉ = - 18 pₓ₂ eₓ kₓ₁₀ = - 2 pₓ₁ (6 (aₓ₄ aₓ₅ + aₓ₂ aₓ₆) + e₀² (24 (Xₓ₂ Xₓ₅ + Xₓ₁ Xₓ₆) - 6 (Xₓ₄ Xₓ₇ + Xₓ₃ Xₓ₈))) kₓ₁₁ = - 2 pₓ₁ (Zₓ₂₃ - Zₓ₂₁) İₓ = pₓ₁ nₓ (Zₓ₁₁ + Zₓ₁₃) ėₓ = pₓ₃ nₓ (Xₓ₁ Xₓ₃ + Xₓ₂ Xₓ₄) ω̇ₓ = pₓ₂ nₓ (Zₓ₃₁ + Zₓ₃₃ - 6) - cos I₀ Ω̇ₓ Ṁₓ = - nₓ pₓ₀ (Zₓ₁ + Zₓ₃ - 14 - 6 e₀²) ``` #### Resonant deep space initialization Defined only if `n₀" ≤ 2π / 225` (deep space) and either: - `0.0034906585 < n₀" < 0.0052359877` (geosynchronous) - `8.26 × 10⁻³ ≤ n₀" ≤ 9.24 × 10⁻³` and `e₀ ≥ 0.5` (Molniya) The sidereal time `θ₀` at epoch can be calculated with either the IAU formula: ``` c₂₀₀₀ = y₂₀₀₀ / 100 θ₀ = ¹/₂₄₀ (π / 180) (- 6.2 × 10⁻⁶ c₂₀₀₀³ + 0.093104 c₂₀₀₀² + (876600 × 3600 + 8640184.812866) c₂₀₀₀ + 67310.54841) mod 2π ``` or the AFSPC formula: ``` d₁₉₇₀ = 365.25 (y₂₀₀₀ + 30) + 1 θ₀ = 1.7321343856509374 + 1.72027916940703639 × 10⁻² ⌊d₁₉₇₀ + 10⁻⁸⌋ + (1.72027916940703639 × 10⁻² + 2π) (d₁₉₇₀ - ⌊d₁₉₇₀ + 10⁻⁸⌋) + 5.07551419432269442 × 10⁻¹⁵ d₁₉₇₀² mod 2π ``` ``` λ₀ = │ M₀ + Ω₀ + ω₀ − θ₀ rem 2π if geosynchronous │ M₀ + 2 Ω₀ − 2 θ₀ rem 2π otherwise λ̇₀ = │ p₁₅ + (k₁₄ + p₁₄) − θ̇ + (Ṁₛ + Ṁₗ) + (ω̇ₛ + ω̇ₗ) + (Ω̇ₛ + Ω̇ₗ) - n₀" if geosynchronous │ p₁₅ + (Ṁₛ + Ṁₗ) + 2 (p₁₄ + (Ω̇ₛ + Ω̇ₗ) - θ̇) - n₀" otherwise ``` #### Geosynchronous deep space initialization Defined only if `n₀" ≤ 2π / 225` (deep space) and `0.0034906585 < n₀" < 0.0052359877` (geosynchronous orbit). ``` p₁₇ = 3 (n / a₀")² 𝛿ᵣ₁ = p₁₇ (¹⁵/₁₆ sin²I₀ (1 + 3 p₁) - ³/₄ (1 + p₁)) (1 + 2 e₀²) 2.1460748 × 10⁻⁶ / a₀"² 𝛿ᵣ₂ = 2 p₁₇ (³/₄ (1 + p₁)²) (1 + e₀² (- ⁵/₂ + ¹³/₁₆ e₀²)) 1.7891679 × 10⁻⁶ 𝛿ᵣ₃ = 3 p₁₇ (¹⁵/₈ (1 + p₁)³) (1 + e₀² (- 6 + 6.60937 e₀²)) 2.2123015 × 10⁻⁷ / a₀"² ``` #### Molniya deep space initialization Defined only if `n₀" ≤ 2π / 225` (deep space) and `8.26 × 10⁻³ ≤ n₀" ≤ 9.24 × 10⁻³` and `e₀ ≥ 0.5` (Molniya). ``` p₁₈ = 3 n₀"² / a₀"² p₁₉ = p₁₈ / a₀" p₂₀ = p₁₉ / a₀" p₂₁ = p₂₀ / a₀" F₂₂₀ = ³/₄ (1 + 2 p₁ + p₁²) G₂₁₁ = │ 3.616 - 13.247 e₀ + 16.29 e₀² if e₀ ≤ 0.65 │ - 72.099 + 331.819 e₀ - 508.738 e₀² + 266.724 e₀³ otherwise G₃₁₀ = │ - 19.302 + 117.39 e₀ - 228.419 e₀² + 156.591 e₀³ if e₀ ≤ 0.65 │ - 346.844 + 1582.851 e₀ - 2415.925 e₀² + 1246.113 e₀³ otherwise G₃₂₂ = │ - 18.9068 + 109.7927 e₀ - 214.6334 e₀² + 146.5816 e₀³ if e₀ ≤ 0.65 │ - 342.585 + 1554.908 e₀ - 2366.899 e₀² + 1215.972 e₀³ otherwise G₄₁₀ = │ - 41.122 + 242.694 e₀ - 471.094 e₀² + 313.953 e₀³ if e₀ ≤ 0.65 │ - 1052.797 + 4758.686 e₀ - 7193.992 e₀² + 3651.957 e₀³ otherwise G₄₂₂ = │ - 146.407 + 841.88 e₀ - 1629.014 e₀² + 1083.435 e₀³ if e₀ ≤ 0.65 │ - 3581.69 + 16178.11 e₀ - 24462.77 e₀² + 12422.52 e₀³ otherwise G₅₂₀ = │ - 532.114 + 3017.977 e₀ - 5740.032 e₀² + 3708.276 e₀³ if e₀ ≤ 0.65 │ 1464.74 - 4664.75 e₀ + 3763.64 e₀² if 0.65 < e₀ < 0.715 │ - 5149.66 + 29936.92 e₀ - 54087.36 e₀² + 31324.56 e₀³ otherwise G₅₃₂ = │ - 853.666 + 4690.25 e₀ - 8624.77 e₀² + 5341.4 e₀³ if e₀ < 0.7 │ - 40023.88 + 170470.89 e₀ - 242699.48 e₀² + 115605.82 e₀³ otherwise G₅₂₁ = │ - 822.71072 + 4568.6173 e₀ - 8491.4146 e₀² + 5337.524 e₀³ if e₀ < 0.7 │ - 51752.104 + 218913.95 e₀ - 309468.16 e₀² + 146349.42 e₀³ otherwise G₅₃₃ = │ - 919.2277 + 4988.61 e₀ - 9064.77 e₀² + 5542.21 e₀³ if e₀ < 0.7 │ - 37995.78 + 161616.52 e₀ - 229838.2 e₀² + 109377.94 e₀³ otherwise D₂₂₀₋₁ = p₁₈ 1.7891679 × 10⁻⁶ F₂₂₀ (- 0.306 - 0.44 (e₀ - 0.64)) D₂₂₁₁ = p₁₈ 1.7891679 × 10⁻⁶ (³/₂ sin²I₀) G₂₁₁ D₃₂₁₀ = p₁₉ 3.7393792 × 10⁻⁷ (¹⁵/₈ sin I₀ (1 - 2 p₁ - 3 p₁²)) G₃₁₀ D₃₂₂₂ = p₁₉ 3.7393792 × 10⁻⁷ (- ¹⁵/₈ sin I₀ (1 + 2 p₁ - 3 p₁²)) G₃₂₂ D₄₄₁₀ = 2 p₂₀ 7.3636953 × 10⁻⁹ (35 sin²I₀ F₂₂₀) G₄₁₀ D₄₄₂₂ = 2 p₂₀ 7.3636953 × 10⁻⁹ (³¹⁵/₈ sin⁴I₀) G₄₂₂ D₅₂₂₀ = p₂₁ 1.1428639 × 10⁻⁷ (³¹⁵/₃₂ sin I₀ (sin²I₀ (1 - 2 p₁ - 5 p₁²) + 0.33333333 (- 2 + 4 p₁ + 6 p₁²))) G₅₂₀ D₅₂₃₂ = p₂₁ 1.1428639 × 10⁻⁷ (sin I₀ (4.92187512 sin²I₀ (- 2 - 4 p₁ + 10 p₁²) + 6.56250012 (1 + p₁ - 3 p₁²))) G₅₃₂ D₅₄₂₁ = 2 p₂₁ 2.1765803 × 10⁻⁹ (⁹⁴⁵/₃₂ sin I₀ (2 - 8 p₁ + p₁² (- 12 + 8 p₁ + 10 p₁²))) G₅₂₁ D₅₄₃₃ = 2 p₂₁ 2.1765803 × 10⁻⁹ (⁹⁴⁵/₃₂ sin I₀ (- 2 - 8 p₁ + p₁² (12 + 8 p₁ - 10 p₁²))) G₅₃₃ ``` #### Common propagation The following values depend on the propagation time `t` (minutes since epoch). Named conditions have the following meaning: - `near earth`: `n₀" ≤ 2π / 225` - `low altitude near earth`: `near earth` and `p₃ < 220 / (aₑ + 1)` - `high altitude near earth`: `near earth` and `p₃ ≥ 220 / (aₑ + 1)` - `elliptic high altitude near earth`: `high altitude near earth` and `e₀ > 10⁻⁴` - `non-elliptic near earth`: `low altitude near earth` or `high altitude near earth` and `e₀ ≤ 10⁻⁴` - `deep space`: `n₀" > 2π / 225` - `non-Lyddane deep space`: `deep space` and `I ≥ 0.2` - `Lyddane deep space`: `deep space` and `I < 0.2` - `AFSPC Lyddane deep space`: `Lyddane deep space` and use the same expression as the original AFSPC implementation, with an `ω` discontinuity at `p₂₂ = 0` ``` p₂₂ = Ω₀ + Ω̇ t + k₀ t² p₂₃ = ω₀ + ω̇ t I = │ I₀ if near earth │ I₀ + İ t + (δIₛ + δIₗ) otherwise Ω = │ p₂₂ if near earth │ p₂₂ + (pₛ₅ + pₗ₅) / sin I if non-Lyddane deep space │ p₃₀ + 2π if Lyddane deep space and p₃₀ + π < p₂₂ rem 2π │ p₃₀ - 2π if Lyddane deep space and p₃₀ - π > p₂₂ rem 2π │ p₃₀ otherwise e = │ 10⁻⁶ if near earth and p₂₇ < 10⁻⁶ │ p₂₇ if near earth and p₂₇ ≥ 10⁻⁶ │ 10⁻⁶ + (δeₛ + δeₗ) if deep space and p₃₁ < 10⁻⁶ │ p₃₁ + (δeₛ + δeₗ) otherwise ω = │ p₂₃ - p₂₅ if elliptic high altitude near earth │ p₂₃ if non-elliptic near earth │ p₂₃ + (pₛ₄ + pₗ₄) - cos I (pₛ₅ + pₗ₅) / sin I if non-Lyddane deep space │ p₂₃ + (pₛ₄ + pₗ₄) + cos I ((p₂₂ rem 2π) - Ω) │ - (δIₛ + δIₗ) (p₂₂ mod 2π) sin I if AFSPC Lyddane deep space │ p₂₃ + (pₛ₄ + pₗ₄) + cos I ((p₂₂ rem 2π) - Ω) │ - (δIₛ + δIₗ) (p₂₂ rem 2π) sin I otherwise M = │ p₂₆ + n₀" (k₁ t² + k₈ t³ + t⁴ (k₉ + t k₁₀) if high altitude near earth │ p₂₄ + n₀" k₁ t² if low altitude near earth │ p₂₉ + (δMₛ + δMₗ) + n₀" k₁ t² otherwise a = │ a₀" (1 - C₁ t - D₂ t² - D₃ t³ - D₄ t⁴)² if high altitude near earth │ a₀" (1 - C₁ t)² if low altitude near earth │ p₂₈ (1 - C₁ t)² otherwise n = kₑ / a³ᐟ² p₃₂ = │ k₂ if near earth │ 1 J₃ │ - - -- sin I othewise │ 2 J₂ p₃₃ = │ k₃ if near earth │ 1 - cos²I othewise p₃₄ = │ k₄ if near earth │ 7 cos²I - 1 otherwise p₃₅ = │ k₅ if near earth │ 1 J₃ 3 + 5 cos I │ - - -- sin I ----------- if deep space and |1 + cos I| > 1.5 × 10⁻¹² │ 4 J₂ 1 + cos I │ 1 J₃ 3 + 5 cos I │ - - -- sin I ----------- otherwise │ 4 J₂ 1.5 × 10⁻¹² p₃₆ = │ k₆ if near earth │ 3 cos²I - 1 otherwise p₃₇ = 1 / (a (1 - e²)) aₓₙ = e cos ω aᵧₙ = e sin ω + p₃₇ p₃₂ p₃₈ = M + ω + p₃₇ p₃₅ aₓₙ rem 2π (E + ω)₀ = p₃₈ p₃₈ - aᵧₙ cos (E + ω)ᵢ + aₓₙ sin (E + ω)ᵢ - (E + ω)ᵢ Δ(E + ω)ᵢ = --------------------------------------------------- 1 - cos (E + ω)ᵢ aₓₙ - sin (E + ω)ᵢ aᵧₙ (E + ω)ᵢ₊₁ = (E + ω)ᵢ + Δ(E + ω)ᵢ|[-0.95, 0.95] E + ω = │ (E + ω)₁₀ if ∀ j ∈ [0, 9], Δ(E + ω)ⱼ ≥ 10⁻¹² │ (E + ω)ⱼ otherwise, with j the smallest integer | Δ(E + ω)ⱼ < 10⁻¹² p₃₉ = aₓₙ² + aᵧₙ² pₗ = a (1 - p₃₉) p₄₀ = aₓₙ sin(E + ω) - aᵧₙ cos(E + ω) r = a (1 - (aₓₙ cos(E + ω) + aᵧₙ sin(E + ω))) ṙ = a¹ᐟ² p₄₀ / r β = (1 - p₃₉)¹ᐟ² p₄₁ = p₄₀ / (1 + β) p₄₂ = a / r (sin(E + ω) - aᵧₙ - aₓₙ p₄₁) p₄₃ = a / r (cos(E + ω) - aₓₙ + aᵧₙ p₄₁) p₄₂ u = tan⁻¹ --- p₄₃ p₄₄ = 2 p₄₃ p₄₂ p₄₅ = 1 - 2 p₄₂² p₄₆ = (¹/₂ J₂ / pₗ) / pₗ rₖ = r (1 - ³/₂ p₄₆ β p₃₆) + ¹/₂ (¹/₂ J₂ / pₗ) p₃₃ p₄₅ uₖ = u - ¹/₄ p₄₆ p₃₄ p₄₄ Ωₖ = Ω + ³/₂ p₄₆ cos I p₄₄ Iₖ = I + ³/₂ p₄₆ cos I sin I p₄₅ ṙₖ = ṙ + n (¹/₂ J₂ / pₗ) p₃₃ / kₑ rḟₖ = pₗ¹ᐟ² / r + n (¹/₂ J₂ / pₗ) (p₃₃ p₄₅ + ³/₂ p₃₆) / kₑ u₀ = - sin Ωₖ cos Iₖ sin uₖ + cos Ωₖ cos uₖ u₁ = cos Ωₖ cos Iₖ sin uₖ + sin Ωₖ cos uₖ u₂ = sin Iₖ sin uₖ r₀ = rₖ u₀ aₑ r₁ = rₖ u₁ aₑ r₂ = rₖ u₂ aₑ ṙ₀ = (ṙₖ u₀ + rḟₖ (- sin Ωₖ cos Iₖ cos uₖ - cos Ωₖ sin uₖ)) aₑ kₑ / 60 ṙ₁ = (ṙₖ u₁ + rḟₖ (cos Ωₖ cos Iₖ cos uₖ - sin Ωₖ sin uₖ)) aₑ kₑ / 60 ṙ₂ = (ṙₖ u₂ + rḟₖ (sin Iₖ cos uₖ)) aₑ kₑ / 60 ``` #### Near earth propagation Defined only if `n₀" > 2π / 225` (near earth). ``` p₂₄ = M₀ + Ṁ t p₂₇ = | e₀ - (C₄ t + C₅ (sin p₂₆ - k₇)) if high altitude | e₀ - C₄ t otherwise ``` #### High altitude near earth propagation Defined only if `n₀" > 2π / 225` (near earth) and `p₃ ≥ 220 / (aₑ + 1)` (high altitude). `elliptic` means `e₀ > 10⁻⁴`. ``` p₂₅ = k₁₃ ((1 + η cos p₂₄)³ - k₁₁) + k₁₂ t p₂₆ = │ p₂₄ + p₂₅ if elliptic │ p₂₄ otherwise ``` #### Deep space propagation Defined only if `n₀" ≤ 2π / 225` (deep space). ``` p₂₈ = │ (kₑ / (nⱼ + ṅⱼ (t - tⱼ) + ¹/₂ n̈ⱼ (t - tⱼ)²))²ᐟ³ if geosynchronous or Molniya │ a₀" otherwise p₂₉ = │ λⱼ + λ̇ⱼ (t - tⱼ) + ¹/₂ ṅᵢ (t - tⱼ)² - p₂₂ - p₂₃ + θ if geosynchronous │ λⱼ + λ̇ⱼ (t - tⱼ) + ¹/₂ ṅᵢ (t - tⱼ)² - 2 p₂₂ + 2 θ if Molniya │ M₀ + Ṁ t otherwise j is │ the largest positive integer | tⱼ ≤ t if t > 0 │ the smallest negative integer | tⱼ ≥ t if t < 0 │ 0 otherwise p₃₁ = e₀ + ė t - C₄ t ``` #### Third body propagation Defined only if `n₀" ≤ 2π / 225` (deep space). The following variables are evaluated for two third bodies, the Sun (solar perturbations `s`) and the Moon (lunar perturbations `l`). Variables specific to the third body are annotated with `x`. In other sections, `x` is either `s` or `l`. ``` Mₓ = Mₓ₀ + nₓ t fₓ = Mₓ + 2 eₓ sin Mₓ Fₓ₂ = ¹/₂ sin²fₓ - ¹/₄ Fₓ₃ = - ¹/₂ sin fₓ cos fₓ δeₓ = kₓ₀ Fₓ₂ + kₓ₁ Fₓ₃ δIₓ = kₓ₂ Fₓ₂ + kₓ₃ Fₓ₃ δMₓ = kₓ₄ Fₓ₂ + kₓ₅ Fₓ₃ + kₓ₆ sin fₓ pₓ₄ = kₓ₇ Fₓ₂ + kₓ₈ Fₓ₃ + kₓ₉ sin fₓ pₓ₅ = kₓ₁₀ Fₓ₂ + kₓ₁₁ Fₓ₃ ``` #### Resonant deep space propagation Defined only if `n₀" ≤ 2π / 225` (deep space) and either: - `0.0034906585 < n₀" < 0.0052359877` (geosynchronous) - `8.26 × 10⁻³ ≤ n₀" ≤ 9.24 × 10⁻³` and `e₀ ≥ 0.5` (Molniya) ``` θ = θ₀ + 4.37526908801129966 × 10⁻³ t rem 2π Δt = │ |Δt| if t > 0 │ -|Δt| if t < 0 │ 0 otherwise λ̇ᵢ = nᵢ + λ̇₀ ṅᵢ = │ 𝛿ᵣ₁ sin(λᵢ - λ₃₁) + 𝛿ᵣ₂ sin(2 (λᵢ - λ₂₂)) + 𝛿ᵣ₃ sin(3 (λᵢ - λ₃₃)) if geosynchronous │ Σ₍ₗₘₚₖ₎ Dₗₘₚₖ sin((l - 2 p) ωᵢ + m / 2 λᵢ - Gₗₘ) otherwise n̈ᵢ = │ (𝛿ᵣ₁ cos(λᵢ - λ₃₁) + 𝛿ᵣ₂ cos(2 (λᵢ - λ₂₂)) + 𝛿ᵣ₃ cos(3 (λᵢ - λ₃₃))) λ̇ᵢ if geosynchronous │ (Σ₍ₗₘₚₖ₎ m / 2 Dₗₘₚₖ cos((l - 2 p) ωᵢ + m / 2 λᵢ - Gₗₘ)) λ̇ᵢ otherwise (l, m, p, k) ∈ {(2, 2, 0, -1), (2, 2, 1, 1), (3, 2, 1, 0), (3, 2, 2, 2), (4, 4, 1, 0), (4, 4, 2, 2), (5, 2, 2, 0), (5, 2, 3, 2), (5, 4, 2, 1), (5, 4, 3, 3)} tᵢ₊₁ = tᵢ + Δt nᵢ₊₁ = nᵢ + ṅᵢ Δt + n̈ᵢ (Δt² / 2) λᵢ₊₁ = λᵢ + λ̇ᵢ Δt + ṅᵢ (Δt² / 2) ``` #### Lyddane deep space propagation Defined only if `n₀" ≤ 2π / 225` (deep space) and `I < 0.2` (Lyddane). ``` sin I sin p₂₂ + (pₛ₅ + pₗ₅) cos p₂₂ + (δIₛ + δIₗ) cos I sin p₂₂ p₃₀ = tan⁻¹ ------------------------------------------------------------- sin I cos p₂₂ - (pₛ₅ + pₗ₅) sin p₂₂ + (δIₛ + δIₗ) cos I cos p₂₂ ``` ## References [1] David A. Vallado, Paul Crawford, R. S. Hujsak and T. S. Kelso, "Revisiting Spacetrack Report #3", presented at the AIAA/AAS Astrodynamics Specialist Conference, Keystone, CO, 2006 August 21–24, https://doi.org/10.2514/6.2006-6753 [2] F. R. Hoots, P. W. Schumacher Jr. and R. A. Glover, "History of Analytical Orbit Modeling in the U. S. Space Surveillance System", Journal of Guidance, Control, and Dynamics, 27(2), 174–185, 2004, https://doi.org/10.2514/1.9161 [3] F. R. Hoots and R. L. Roehrich, "Spacetrack Report No. 3: Models for propagation of NORAD element sets", U.S. Air Force Aerospace Defense Command, Colorado Springs, CO, 1980, https://www.celestrak.com/NORAD/documentation/ [4] R. S. Hujsak, "A Restricted Four Body Solution for Resonating Satellites Without Drag", Project SPACETRACK, Rept. 1, U.S. Air Force Aerospace Defense Command, Colorado Springs, CO, Nov. 1979, https://doi.org/10.21236/ada081263