Satellite astrodynamics in Rust, with full Python bindings.
Satkit is a high-performance orbital mechanics library written in Rust with complete Python bindings via PyO3. It handles coordinate transforms, orbit propagation, time systems, gravity models, atmospheric density, and JPL ephemerides -- everything needed for satellite astrodynamics work.
Documentation and tutorials (Python examples, but the concepts and API apply equally to Rust) | Rust API reference
Rust:
cargo add satkitPython:
pip install satkitPre-built wheels are available for Linux, macOS, and Windows on Python 3.10--3.14.
The IERS nutation tables and gravity models are compiled in, so frames, gravity, SGP4 and time work with no data files. The JPL ephemeris (~100 MB) downloads on first use (SHA-256 verified) into the user data directory; Earth orientation and space weather are fetched on first use and should be refreshed periodically:
import satkit as sk
sk.utils.update_datafiles() # provisions everything up front; re-run periodically for fresh EOP/space weatherSet SATKIT_OFFLINE=1 to forbid downloads, or pip install satkit[data] for the optional offline data bundle.
import satkit as sk
tle = sk.TLE.from_lines([
"ISS (ZARYA)",
"1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9003",
"2 25544 51.6432 351.4697 0007417 130.5364 329.6482 15.48915330299357"
])
pos, vel = sk.sgp4(tle, sk.time(2024, 1, 2))import satkit as sk
import numpy as np
r0 = 6378e3 + 500e3 # 500 km altitude
v0 = np.sqrt(sk.consts.mu_earth / r0)
settings = sk.propsettings(
gravity_model=sk.gravmodel.egm96, # default; also jgm3, jgm2, itugrace16
gravity_degree=8,
integrator=sk.integrator.rkv98, # default; also rkv87, rkv65, rkts54,
# gauss_jackson8 (fixed-step multistep)
)
result = sk.propagate(
np.array([r0, 0, 0, 0, v0, 0]),
sk.time(2024, 1, 1),
end=sk.time(2024, 1, 1) + sk.duration.from_days(1),
propsettings=settings,
)
state = result.interp(sk.time(2024, 1, 1) + sk.duration.from_hours(6))import satkit as sk
time = sk.time(2024, 1, 1, 12, 0, 0)
coord = sk.itrfcoord(latitude_deg=42.0, longitude_deg=-71.0, altitude=100.0)
q = sk.frametransform.qitrf2gcrf(time)
gcrf_pos = q * coord.vectoruse satkit::{Instant, SolarSystem, jplephem};
let time = Instant::from_datetime(2024, 1, 1, 0, 0, 0.0)?;
let (pos, vel) = jplephem::geocentric_state(SolarSystem::Moon, &time)?;Full IERS 2010 Conventions reduction (IAU 2006/2000A precession-nutation) with Earth orientation parameters:
| Frame | Description |
|---|---|
| ITRF | International Terrestrial Reference Frame (Earth-fixed) |
| GCRF | Geocentric Celestial Reference Frame (inertial) |
| TEME | True Equator Mean Equinox (SGP4 output frame) |
| CIRS | Celestial Intermediate Reference System |
| TIRS | Terrestrial Intermediate Reference System |
| EME2000 / ICRF | J2000 mean equator and the International Celestial Reference Frame |
| Geodetic | Latitude / longitude / altitude (WGS-84) |
Plus satellite-local RTN, NTW, and LVLH frames (maneuvers, covariance), and ENU, NED, and geodesic distance (Vincenty) utilities.
- Numerical -- Selectable adaptive Runge-Kutta integrators (9(8), 8(7), 6(5), 5(4)) plus RODAS4 (stiff) and Gauss-Jackson 8 (fixed-step multistep for high-precision long-duration propagation), with dense output, state transition matrix, and configurable force models. With matched force models it agrees with NASA GMAT to a few centimetres over 7 days in LEO, MEO, and GEO (see Testing and Validation)
- SGP4 -- Standard TLE/OMM propagator with TLE fitting from precision states
- Keplerian -- Analytical two-body propagation
- Impulsive maneuvers -- Instantaneous delta-v applied at a scheduled time during propagation. Supported frames: GCRF (inertial), RTN (radial/tangential/normal — the CCSDS OEM convention, also exposed as
RSWandRICaliases), NTW (velocity-aligned — natural for prograde burns on eccentric orbits, where a pure +T delta-v adds exactly Δv to |v|), and LVLH (Local Vertical / Local Horizontal). Ergonomic helpersadd_prograde/add_retrograde/add_radial/add_normalfor common scalar-magnitude burns. - Continuous thrust -- Constant-acceleration thrust arcs over time windows in any of the frames above, integrated directly into the force model
- Automatic segmentation -- Propagation through maneuver sequences is handled transparently, including backward propagation
- Earth gravity: JGM2, JGM3, EGM96, ITU GRACE16 (spherical harmonics up to degree/order 40; Montenbruck & Gill 2000, §3.2)
- Solid Earth tides: IERS Conventions 2010 §6.2.1 Step-1 corrections to the gravity field
- Third-body gravity: Sun and Moon via JPL DE440/441 ephemerides
- Atmospheric drag: NRLMSISE-00 (Picone et al. 2002) fed automatically from CelesTrak space-weather data — observed F10.7 / centred F10.7A and the 7-element 3-hourly geomagnetic ap history, so density responds to storms within hours; validated against GMAT (below)
- Solar radiation pressure: Cannonball model with shadow function
- Relativity: IERS 2010 Eq. 10.12 — Schwarzschild, geodesic (de Sitter) precession, and Lense–Thirring
Seamless conversion between UTC, TAI, TT, TDB, UT1, and GPS time scales with full leap-second handling.
- JPL DE440/DE441 ephemerides for all planets, Sun, Moon, and barycenters
- Fast analytical Sun/Moon models for lower-precision work
- Sunrise/sunset and Moon phase calculations
SatKit uses numeris for all linear algebra (vectors, matrices, quaternions, ODE integration). If you also use nalgebra in your project, enable the nalgebra feature on numeris for zero-cost From/Into conversions between types:
numeris = { version = "0.5.18", features = ["nalgebra"] }| Feature | Default | Description |
|---|---|---|
omm-xml |
yes | XML OMM deserialization via quick-xml |
download |
yes | Data-file downloader (update_datafiles) via ureq |
chrono |
no | TimeLike impl for chrono::DateTime |
Three tiers, handled differently by size and how often they change:
Compiled in (no files needed): IERS 2010 nutation tables and the EGM96 / JGM2 / JGM3 / ITU_GRACE16 gravity models to degree 70 (~300 KB gzip'd). Frames, gravity, SGP4, time scales, Kepler and Lambert work offline out of the box.
Downloaded once, on first use: the JPL DE440 ephemeris (~100 MB; DE421 at 14 MB via SATKIT_JPLEPHEM_FILE), SHA-256 verified against the manifest compiled into satkit (data/manifest.json), fetched from the GitHub release asset, the origin server (JPL), or a SATKIT_DATA_URL mirror.
Refreshed periodically: space weather (F10.7, Ap) and Earth orientation parameters (polar motion, UT1−UTC), sourced from CelesTrak by update_datafiles().
Downloads go to the platform user-data directory (satkit.utils.datadir(): ~/Library/Application Support/satkit-data, $XDG_DATA_HOME/satkit-data, or %LOCALAPPDATA%\satkit-data) unless SATKIT_DATA is set; files are also looked up in an installed satkit-data package and /usr/share/satkit-data. SATKIT_OFFLINE=1 turns any needed download into an error. Details: Data Files.
The library is validated against:
- Vallado test cases for SGP4, coordinate transforms, and Keplerian elements
- JPL test vectors for DE440/441 ephemeris interpolation (10,000+ cases)
- NASA GMAT reference trajectories for the high-precision propagator (see below)
- ICGEM reference values for gravity field calculations
- GPS SP3 precise ephemerides for multi-day numerical propagation
Around 300 Rust tests and 150 Python tests run on every commit across Linux, macOS, and Windows.
The numerical propagator is regression-tested against NASA's General Mission Analysis Tool (GMAT R2026A). The corpus in tests/gmat/ holds 25 reference trajectories: 17 seven-day gravity/third-body cases -- ISS-like LEO, sun-synchronous, GPS MEO, Molniya, GEO, the lunar-resonant TESS orbit, and a 300,000 km cislunar orbit -- each with a low-degree gravity model, a 36×36 EGM96 + solid tides model, and (for three orbits) relativity; plus 8 three-day atmospheric-drag cases (ISS altitude, 300 km, 550 km sun-synchronous, GTO with a 250 km perigee), each run with fixed space-weather indices and with the CelesTrak space-weather file driving NRLMSISE-00 on both sides. GMAT cannot run in CI, so the trajectories are generated offline (tests/gmat/generate.py, SPICE DE440, EarthICRF) and committed; tests/gmat_regression.rs and python/test/test_gmat.py replay them hour by hour and gate on the worst residual.
With matched force models the two agree to 3 cm (ISS), 2 cm (SSO), 8 cm (GPS), and 13 cm (GEO, Molniya) over 7 days. At 200,000 km and beyond the residual is ~1 m, which is GMAT's own integration floor (its point-mass runs differ from the analytic Kepler solution by the same amount). The remaining differences with tides and relativity enabled are documented with the tolerances in tests/gmat/README.md: GMAT omits the anelastic phase lag in its solid-tide Love numbers that satkit includes, while the relativity cases sit at the same floors (both tools apply the full IERS 2010 Eq. 10.12 correction).
With drag the two agree to 1–2 × 10⁻⁴ of the drag-induced displacement when the space-weather indices are fixed (26 m against 152 km of drag decay over 3 days at ISS altitude; the two NRLMSISE-00 implementations agree to 0.06 % rms in density) and to 1–2 × 10⁻³ when both read the CelesTrak file (198 m at ISS altitude), the residual being the F10.7 timing convention -- GMAT interpolates between 20:00 UT nodes, satkit steps at 00:00 UT. Building the drag corpus found and fixed a radians-for-degrees error in satkit's NRLMSISE-00 inputs (8 km over 3 days at ISS altitude) and moved the space-weather feed to the observed F10.7 and the 3-hourly ap history (0.21.1); details on the GMAT validation page.
Tests require two sets of external data: the astro-data files (gravity models, ephemerides, etc.) and the test vectors (reference outputs for validation). Download both before running:
# Install the download helper
pip install requests
# Download data files and test vectors into the current directory
python python/test/download_data.py astro-data
python python/test/download_testvecs.py satkit-testvecsThen run tests with the environment variables pointing to the downloaded directories:
# Rust tests
SATKIT_DATA=astro-data SATKIT_TESTVEC_ROOT=satkit-testvecs cargo test
# Python tests (after `pip install -e ".[test]"`)
SATKIT_DATA=astro-data SATKIT_TESTVEC_ROOT=satkit-testvecs pytest python/test/The GMAT regression tests need only the data files; their reference trajectories are checked in.
- Rust: docs.rs/satkit
- Python: satkit.dev -- tutorials, Jupyter notebooks, and API reference
The primary sources for every model and algorithm — IERS Conventions (2010), Vallado (2013), Montenbruck & Gill (2000), Vallado et al. (2006) for SGP4, Picone et al. (2002) for NRLMSISE-00, Park et al. (2021) for DE440, Verner (2010) and Berry & Healy (2004) for the integrators, Izzo (2015) for Lambert, and the gravity-model reports — are collected with DOIs on the documentation site's References page.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.