siderust 0.15.0

High-precision astronomy and satellite mechanics in Rust.
docs.rs failed to build siderust-0.15.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: siderust-0.15.1

Siderust

Crates.io Docs.rs CI License: AGPL-3.0-only

Type-safe astronomy and satellite mechanics in Rust.

Siderust brings coordinate transformations, planetary and lunar ephemerides, time scales, and observation/orbit analysis into one Rust crate. Reference centers, frames, and physical units are represented in types, making many invalid combinations compile-time errors.

API documentation · Runnable examples · Crates.io · Report an issue

Quick Start

Add Siderust to a Rust project:

cargo add siderust

Calculate the Earth–Mars separation at the J2000 epoch using the bundled VSOP87 model:

use siderust::bodies::solar_system::{Earth, Mars};
use siderust::ephemeris::VSOP87;
use siderust::time::J2000;

fn main() {
    let earth = Earth::vsop87a(J2000);
    let mars = Mars::vsop87a(J2000);

    println!("Earth–Mars separation at J2000: {}", earth.distance_to(&mars));
}

Or run a complete example from the repository:

git clone https://github.com/Siderust/siderust.git
cd siderust
cargo run --example 08_solar_system

No external JPL kernel is required for these VSOP87 examples. For more workflows, see the example gallery.

Why Siderust?

  • Catch coordinate mistakes early. Positions carry their reference center, frame, and units in Rust types; incompatible coordinate operations cannot be mixed silently.
  • Start without downloading large datasets. Analytical VSOP87 planetary and ELP2000 lunar ephemerides are available by default; JPL DE4xx BSP kernels can be loaded separately at runtime.
  • Go beyond ephemerides. Work with coordinate transformations, observatory sites, altitude windows, TLE/SGP4 workflows, and optional precise orbit determination.
  • Choose your environment. Default builds use std; default-features = false supports no_std with alloc for suitable integrations.

Scientific use: Accuracy and coverage depend on the chosen model, epoch, time data, and reference frame. See the tests, dataset guidance, and benchmark methodology before selecting a backend for a precision-sensitive workflow.

Documentation map


Supported Feature Flags

Feature Default What it enables
std ✔ Standard library (filesystem, OS I/O, Utc::now); implies heap via always-on alloc
serde ✔ Serialization support and runtime TOML observatory catalogs (default)
(base) VSOP87 + ELP2000-82B analytical ephemerides, full coordinate/altitude API
atmosphere Atmospheric tables and radiative transfer helpers
photometry Photometric passbands and throughput unit (Johnson–Cousins UBVRI)
spice High-level SPICE kernel context (SpiceContext, KernelSet); implies std
pod Precise Orbit Determination toolkit (WLS, EKF, force models, I/O); implies std
pod-parquet Parquet residuals writer (implies pod)
pod-doris DORIS RINEX observation parser (implies pod)
runtime-data Runtime dataset-loading helpers via siderust-archive; implies std

no_std: use default-features = false for no_std + alloc. Byte-oriented parsers (TLE, SCK, SPICE SPK from &[u8]) and analytical ephemerides remain available; filesystem/I/O format helpers and POD require std. C ABI bindings live in the separate siderust-ffi crate. f128 quad-precision is not supported.


Features

Category What you get
Coordinate Systems Position and spherical Direction types parameterised by ReferenceCenter, ReferenceFrame, and Unit. Compile‑time guarantees prevent mixing frames by accident.
Target Tracking Target<T> couples any coordinate with an observation epoch and optional ProperMotion, enabling extrapolation & filtering.
Physical Units Strongly typed Mass, Length, Angle, Velocity, Duration & more via the qtty crate, dimensional correctness at compile time.
Celestial Mechanics VSOP87 & ELP2000 theories, Pluto (Meeus/Williams), light‑time & aberration, nutation & precession, apparent Sun & Moon, culmination searches, full Vallado/AFSPC SGP4/SDP4 from TLE.
Planetary / lunar orientation Generic body_orientation over IAU body, ephemeris, geocentric/topocentric center, and equatorial sky frame; Mars/Saturn conveniences; lunar bright-limb position angle.
Ephemeris Backends Pluggable Ephemeris / DynEphemeris traits: Vsop87Ephemeris (always available) and RuntimeEphemeris for JPL DE4xx BSP files at runtime.
Altitude API Unified AltitudeProvider trait for Sun, Moon, stars, and arbitrary ICRS directions; find crossings, culminations, altitude_ranges, above_threshold, and below_threshold periods.
Catalogs & Bodies Built‑in Sun→Neptune, asteroids (Ceres, Bennu, Apophis), comets (Halley, Encke, Hale-Bopp), a starter star catalog, typed Gaia DR3 raw/domain ingestion, + helpers for custom datasets.
Starlight & Photometry HEALPix stellar surface-brightness maps, S10 diagnostics, passband-aware Gaia XP sampled-spectrum photon-flux integration, and typed passband-integrated stellar source records.
Observatories TOML-backed built-in and runtime-extensible sites using one typed Observatory model.

Coordinate algebra and reusable conic geometry are provided by affn; Kepler-equation solving and domain-neutral conic propagation live in keplerian; siderust adds astronomy-specific time, frame transforms, ephemeris backends, and body/observer orchestration on top.

API Design Pillars

Siderust is built on two cross-cutting principles documented in doc/conventions.md:

  1. Typed quantities everywhere. Every scalar that has physical meaning — pressures, scale heights, optical depths, airmasses, albedos, illumination fractions, CIP coordinates — is a qtty newtype. Passing a raw f64 where a Hectopascals or Kilometers is expected is a compile-time error.

  2. Phantom-typed model selection. Algorithm variants (e.g. nutation models) are selected at the call site via zero-sized phantom type parameters such as to_frame_as::<EquatorialFrame, Iau2006A>(jd). There are no runtime enums to match on. Dispatch is fully monomorphised.

Astrometry Compliance Note

  • Stellar aberration uses the full special-relativistic (Lorentz) formula per IERS Conventions (2020, §7.2); annual uses VSOP87E barycentric Earth velocity and topocentric adds a diurnal ω×r term.
  • The Earth-orientation chain now exposes public frame transforms for GCRS ↔ CIRS ↔ TIRS ↔ ITRF/ECEF, plus the usual inertial and operational frames (ICRS, ICRF, EME2000, TEME, Galactic, planetary body-fixed).
  • Local orbital frames (RTN, LVLH, VNC) and covariance transport in those frames are first-class via [astro::dynamics::frames::LocalOrbitalFrame] and [astro::dynamics::covariance::StateCovariance], built from a typed [OrbitState].

Scientific Archive (submodule)

Large scientific datasets — VSOP87, IAU 2000A nutation, ELP2000-82B, Sun-Earth Lagrange Chebyshev kernels, SPICE time/frame/constants kernels, and dataset generators/validators — live in the separate Siderust/archive repository, attached here as the archive/ git submodule.

After cloning Siderust, initialise the submodule:

git submodule update --init --recursive

All archive metadata uses TOML (MANIFEST.toml, per-family manifest.toml). Binary payloads use their authoritative formats (SPICE .bsp, Siderust Chebyshev Kernel .sck, raw .dat). The archive layout, manifest schema, and regeneration recipes are documented in archive/README.md and archive/schema/archive-manifest-v1.md.

The default siderust build does not require a separate archive checkout. Large scientific datasets (VSOP87, ELP2000, nutation, gravity, atmosphere, Pluto) are embedded via the siderust-archive crate which is a regular Cargo dependency. JPL DE4xx kernels are resolved at runtime from the local filesystem or downloaded on demand via runtime-data.


Installation

Add to your Cargo.toml:

[dependencies]
siderust = "0.15"

VSOP87/ELP2000 coefficients, nutation tables, and EOP data are provided by siderust-archive (scientific datasets, manifests, checksums, provenance) and tempoch (UTC/TAI/TT/UT1/TDB time scales, ΔT, and EOP freshness). Optional JPL kernels are downloaded on demand when the corresponding feature is enabled; see doc/datasets.md.

Ephemeris Backends

Siderust always includes Vsop87Ephemeris (VSOP87 + ELP2000-82B). Compile-time DefaultEphemeris is an alias to VSOP87.

JPL DE440/DE441 (and other NAIF BSP kernels) are provided at runtime via RuntimeEphemeris:

[dependencies]
siderust = { version = "0.15", features = ["runtime-data"] }
use siderust::ephemeris::{DynEphemeris, RuntimeEphemeris};
use siderust::time::JulianDate;

let eph = RuntimeEphemeris::from_bsp("/path/to/de440.bsp")?;
let earth = eph.earth_heliocentric(JulianDate::J2000);

With runtime-data, siderust_archive::jpl::DatasetManager can download kernels on first use (see example 12_runtime_ephemeris).

VSOP87-only (explicit):

[dependencies]
siderust = { version = "0.15", default-features = false }

Combine with other features (for example serde):

[dependencies]
siderust = { version = "0.15", features = ["runtime-data", "serde"] }

JPL datasets (runtime, not compile-time features)

DE kernels are not embedded at compile time. Typical files:

  • de440.bsp (~120 MB)
  • de441_part-2.bsp (~1.65 GB)

Prefetch into a persistent cache (optional):

export SIDERUST_DATASETS_DIR="$HOME/.cache/siderust"
# from an archive checkout:
../archive/scripts/prefetch_datasets.sh --de440

cargo test --all-features does not download JPL kernels by itself. Use cargo test --features runtime-data when exercising download paths, or point SIDERUST_BSP_PATH at a local BSP for integration tests (see tests/test_jpl_real_backend.rs).

CI sets SIDERUST_JPL_STUB=all for deterministic --all-features jobs. For local parity:

SIDERUST_JPL_STUB=all cargo test --all-features

Real-kernel validation (manual / optional workflow):

SIDERUST_BSP_PATH=/path/to/de440.bsp cargo test --test test_jpl_real_backend
SIDERUST_BSP_PATH=/path/to/de440.bsp cargo bench --bench de441

Coverage:

  • Fallible JPL APIs such as try_position, try_velocity, and try_position_velocity return an error outside the Chebyshev segment coverage.
  • The legacy infallible JPL APIs are retained for compatibility and panic with an explicit out-of-range message instead of silently extrapolating.
  • Earth-orientation lookups are keyed by UTC/MJD. The default IERS provider uses tempoch's bundled EOP data and reports missing coverage rather than fabricating zero EOP values. Use NullEop only when a documented zero-EOP approximation is intended.

Coordinate Systems

Siderust encodes both the origin and the orientation of every coordinate at the type level:

use siderust::coordinates::{cartesian, centers::*, frames::*};
use qtty::Au;

// Position of Mars in the Heliocentric Ecliptic frame
let mars_helio = cartesian::Position::<Heliocentric, EclipticMeanJ2000, Au>::new(1.5, 0.0, 0.0);

Impossible states (e.g. adding heliocentric and geocentric positions) simply do not compile.

The public frame set includes:

  • inertial / catalogue frames: ICRS, ICRF, EquatorialMeanJ2000, EME2000, EquatorialMeanOfDate, EquatorialTrueOfDate, FK4B1950, Galactic
  • Earth-rotation chain frames: GCRS, CIRS, TIRS, ITRF, ECEF
  • operational / mission frames: TEME, Horizontal, and the planetary body-fixed frames

Compile-Time vs Runtime Safety

For most centers (Barycentric, Heliocentric, Geocentric) all invariants are enforced at compile time with zero runtime cost (Params = ()).

Parameterized centers (Topocentric, Bodycentric) carry runtime data (e.g., ObserverSite). The center type is still checked at compile time, but parameter equality (e.g., "are these two positions at the same site?") is checked at runtime:

API Behaviour on mismatch
pos_a - pos_b / distance_to assert! (panics in all builds)
checked_sub / try_distance_to Returns Err(CenterParamsMismatchError)
ObserverSite::try_new Validates lat/lon ranges, returns Result

Units & Physical Quantities

Siderust uses the qtty crate for dimensionally typed quantities. The compiler prevents mixing incompatible units:

use qtty::*;

let distance = AstronomicalUnits::new(1.523); // Mars semi-major axis
let period   = Days::new(686.97);

// distance + period → compile error (length + time)

Common unit types: AstronomicalUnit (Au), Kilometer (Km), Meter, Degree, Radian, Day, Second, AuPerDay, and many more.


Examples

The examples/ directory is a curated tour of the crate’s major building blocks (coordinates, transforms, altitude periods, ephemeris backends, serialization).

Feature-gated examples:

# Runtime JPL ephemeris (BSP path argument or runtime-data download)
cargo run --example 12_runtime_ephemeris -- /path/to/de440.bsp
cargo run --features runtime-data --example 12_runtime_ephemeris

# Serde
cargo run --example 11_serde_serialization --features serde

Observatory catalogs

Siderust uses one public Observatory model for both bundled sites and custom sites loaded at runtime. The bundled scientific values are generated from the canonical data/observatories.toml catalog and embedded at build time; no data file lookup is needed after installation. With the default serde feature, ObservatoryCatalog::from_toml and ObservatoryCatalog::from_path accept the same [[observatory]] representation for user-provided catalogs.

Downstream crates should consume these Siderust records instead of maintaining duplicate observatory coordinate registries. Site-specific calibrations remain the downstream crate's responsibility.

Crate Layout

├─ astro/         # Aberration, nutation, precession, sidereal time
├─ bodies/        # Planet, Star, Satellite, Asteroid, Comet + built-in catalogs
├─ calculus/
│   ├─ altitude/     # Unified altitude API (`AltitudeProvider` trait)
│   ├─ ephemeris/    # Ephemeris trait + VSOP87 + runtime JPL backends
│   ├─ jpl/          # Shared JPL DE4xx infrastructure (Chebyshev evaluation)
│   ├─ math_core/    # Root-finding (Brent/bisection), extrema, interval assembly
│   ├─ solar/        # Sun altitude, night/day/twilight periods
│   ├─ lunar/        # Moon altitude with topocentric parallax
│   ├─ stellar/      # Analytical star altitude engine
│   ├─ vsop87/       # VSOP87 planetary theory
│   ├─ elp2000/      # ELP2000-82B lunar theory
│   ├─ conic_equations.rs # Siderust wrappers over keplerian solvers
│   └─ pluto         # Meeus/Williams Pluto ephemeris
├─ coordinates/   # Cartesian/Spherical types, frames, centers, transforms
├─ mission/       # Mission-analysis building blocks
│   ├─ geometry/     # AzElRange, Fov, TerrainMask, eclipse, orbit-relative geometry
│   ├─ context.rs    # MissionContext — runtime aggregation of instruments and sites
│   └─ site.rs       # Location — ground-station / observing-site metadata
├─ catalogs/      # Star catalogs and TOML-backed observatory catalog
├─ targets/       # Target<T> with time & ProperMotion
└─ time           # Re-export of tempoch: JulianDate, MJD, Period<S>, time scales

Roadmap

  • Custom dynamic reference centers (topocentric, bodycentric)
  • DE440/DE441 JPL ephemerides
  • Unified altitude API (AltitudeProvider trait)
  • Serde serialization support
  • Gaia DR3 star ingestion & cone search
  • Relativistic light‑time & gravitational deflection
  • Batch orbit determination helpers (LSQ & EKF)
  • GPU acceleration via wgpu (experiment)

Contributing

Contributions are welcome, including documentation fixes, runnable examples, reference-data comparisons, and bug reports. Start with the open issues or the examples.

  1. Fork and clone the repository; create a feature branch.
  2. Follow the authoring conventions for public APIs and scientific documentation.
  3. Run cargo test && cargo clippy -- -D warnings and add tests for behavioral changes.
  4. Open a pull request describing the problem, approach, and validation performed.

For numerical discrepancies, please include the epoch, reference center/frame, units, selected model, and an independent reference when available.

By participating you agree to follow the Rust Code of Conduct.

If Siderust helps your work, star the repository or share a reproducible example to help others discover it.


License

Copyright (C) 2026 Vallés Puig, Ramon

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). The AGPL-3.0 ensures that:

  • Any modifications and redistributions of the code (including as a network service) remain free and open.
  • End users have access to the full source code, including any improvements or extensions made.

Note for commercial or proprietary use: If you wish to incorporate this code into a closed-source or otherwise differently licensed project, a dual-licensing arrangement can be negotiated. Please contact the authors to discuss terms and conditions for a commercial or proprietary license that suits your needs.


Acknowledgments

Big thanks to Màrius Montón (@mariusmm) for inviting me to his three-week Rust intro course at the Universitat Autònoma de Barcelona (UAB) in 2024 that nudge set this project in motion.

Time coordinates

Siderust uses tempoch 0.11. siderust::time::JulianDate defaults to TT; use tempoch::JulianDate<UTC> for civil UTC coordinates. UTC JD/MJD require fallible conversion with a shared data snapshot:

use siderust::time::{TimeContext, Time, UTC, MJD};
use siderust::qtty::Day;
let ctx = TimeContext::new();
let utc = Time::<UTC, MJD>::try_new_with(Day::new(57754.0), &ctx)?;
let raw = utc.to_with::<MJD>(&ctx)?.try_raw_with(&ctx)?;
# Ok::<(), siderust::time::ConversionError>(())

MJD 57754.0 is 2017-01-01 00:00:00 UTC. Civil days have 86,400 nominal seconds; that midnight follows 2016-12-31 midnight by 86,401 SI seconds. Leap-second instants are valid internal instants but fail civil JD/MJD encoding. TLE model offsets use 1,440 nominal minutes per civil day. Scalar Serde uses civil UTC coordinates and a fresh default context; use upstream tagged split representations for leap instants and explicit snapshots for reproducibility.

Public geometry and mechanics dependencies (affn 0.12, principia 0.7, keplerian, optica, and related crates) resolve from crates.io without Git patches.