SkillAgentSearch skills...

Sgp4

A Rust implementation of the SGP4 satellite propagation algorithm

Install / Use

/learn @neuromorphicsystems/Sgp4
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

crates.io mit-badge

The SGP4 algorithm, ported to Rust from the reference Celestrak implementation [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].

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

The code documentation is hosted at 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:

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 with libm for floating point functions when std is not available.

See https://github.com/neuromorphicsystems/sgp4-no-std for a minimal no-std example that runs on Docker Linux x86_64.

All serde-related features, such as 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] in improved mode
  • cpp-afspc: the Celestrak implementation [1] in AFSPC compatibility mode
  • cpp-fastmath: the Celestrak implementation [1] in improved mode with the fast-math compiler flag
  • cpp-afspc-fastmath: the Celestrak implementation [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]) 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]. Partial expressions without a name in [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
  2. Propagation variables
  3. Third-body initialization variables
  4. 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]

Related Skills

View on GitHub
GitHub Stars107
CategoryDevelopment
Updated1mo ago
Forks26

Languages

Rust

Security Score

100/100

Audited on Feb 25, 2026

No findings