Skip to content

Rust Quick Start

satkit is a Rust library first; the Python package is a binding over it. This section covers using the crate directly. Everything in Learn applies to both languages, since the models and conventions are the same; the pages here show the Rust API for each topic, with complete programs you can run.

Add the crate

cargo add satkit

or in Cargo.toml:

[dependencies]
satkit = "0.24"

satkit builds on stable Rust. Its linear-algebra types (Vector3, Matrix3, Quaternion, ...) come from the numeris crate and are re-exported in satkit::mathtypes; build vectors with Vector3::from_array([x, y, z]), or add numeris yourself for its vector! macro.

Cargo features

Feature Default What it does
download on HTTP(S) downloads through ureq: fetching data files on first use, utils::update_datafiles, TLE::from_url and OMM::from_url. Without it, data files must already be on disk (Provisioning up front) and the URL loaders are not compiled
omm-xml on Parsing OMM XML (OMM::from_xml_string, from_xml_file, and XML in OMM::from_text / from_file) through quick-xml. OMM JSON needs no feature
chrono off Interoperability with chrono: From conversions between Instant and chrono::DateTime, and TimeLike for chrono::DateTime, so chrono times can be passed to every satkit function that takes a time

To leave out the downloader, for a build without an HTTP client:

satkit = { version = "0.24", default-features = false, features = ["omm-xml"] }

Data files

The IERS nutation tables and the gravity models are compiled in, so time scales, SGP4, Kepler and Lambert, gravity, and the precession–nutation part of the frame chain work with no files and no network. The JPL ephemeris (for the Sun and Moon in the numerical propagator, and planetary positions) is downloaded on first use; the Earth orientation and space-weather tables are downloaded on first use and refreshed by utils::update_datafiles:

// Download anything missing and refresh the Earth orientation and
// space-weather tables (needs the `download` feature)
satkit::utils::update_datafiles(None, false)?;

Frame transforms to or from ITRF use Earth orientation parameters (EOP). Without the EOP file they warn once and fall back to zero polar motion and UT1 = UTC, which is off by up to about 12″ (hundreds of meters at LEO), and propagate returns orbitprop::Error::EopUnavailable. The data pages apply unchanged to Rust:

  • Data Files: what is compiled in, downloaded and refreshed.
  • Data Directories: where satkit looks and writes, SATKIT_DATA, SATKIT_OFFLINE, and provisioning a machine up front (utils::set_datadir, utils::set_offline).
  • Data Coverage: checking that the tables cover your epochs (earth_orientation_params::status, spaceweather::status).

Warnings and logging

satkit reports stale or missing data, fallbacks and download notices through the log facade, with the emitting module as the target (e.g. satkit::earth_orientation_params). With no logger installed they are printed to stderr. Once you install a logger it decides what is shown. env_logger shows only errors unless RUST_LOG says otherwise, which would hide satkit's warnings, so give it a default:

fn main() {
    // Show satkit's warnings unless RUST_LOG says otherwise
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("satkit=warn"))
        .init();
    // ...
}

and filter per module at run time, e.g. RUST_LOG=satkit=error or RUST_LOG=satkit=warn,satkit::spaceweather=error. The EOP and space-weather warnings can also be turned off at the source with earth_orientation_params::disable_eop_time_warning() and spaceweather::disable_space_weather_time_warning(). See Warnings and logging for what each warning means.

Errors

Fallible functions return a Result with the error type of their module, each an enum built with thiserror so it can be matched:

Module Error type Typical causes
time InstantError out-of-range date fields, unparsable strings, invalid leap second
frametransform frametransform::Error frame pair not supported by the chosen function
tle, omm tle::Error, omm::Error malformed records (with line number and satellite), checksum mismatch, HTTP errors
sgp4 sgp4::Error initialization failure (eccentricity, decayed orbit); per-time failures are in SGP4State::errcode
orbitprop orbitprop::Error no EOP data, invalid settings, a failed ephemeris lookup
jplephem jplephem::Error ephemeris file missing (offline) or invalid, time outside its span
kepler, lambert, lpephem kepler::Error, lambert::Error, lpephem::Error out-of-domain elements, no convergence, no sunrise
data earth_orientation_params::Error, spaceweather::Error, utils::download::Error unreadable or unwritable data files, failed downloads

Some are #[non_exhaustive], so a match on them needs a wildcard arm. All implement std::error::Error + Send + Sync, so in an application Box<dyn std::error::Error> (as in the examples) or anyhow::Result collects them with ?. The crate-level satkit::Error is deprecated; use the module errors.

A first program

//! Where is the ISS? Parse a TLE, run SGP4, and convert to latitude,
//! longitude and altitude.
//!
//! SGP4 needs no data files; the TEME -> ITRF rotation uses Earth
//! orientation parameters, downloaded on first use.
//!
//! ```text
//! cargo run --example quickstart
//! ```

use satkit::frametransform::qteme2itrf;
use satkit::sgp4::sgp4;
use satkit::{Duration, ITRFCoord, Vector3, TLE};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut tle = TLE::load_3line(
        "ISS (ZARYA)",
        "1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9009",
        "2 25544  51.6432 351.4697 0007417 130.5364 329.6482 15.48915330299350",
    )?;

    // Half an orbit after the element-set epoch
    let t = tle.epoch + Duration::from_minutes(46.0);

    // SGP4 gives TEME position (m) and velocity (m/s), one column per time
    let state = sgp4(&mut tle, &[t])?;
    let p_teme = Vector3::from_slice(state.pos.col_slice(0));

    // Rotate to the Earth-fixed frame and convert to geodetic
    let itrf = ITRFCoord::from(qteme2itrf(&t) * p_teme);
    println!("{} at {t}", tle.name);
    println!(
        "  latitude {:.3} deg, longitude {:.3} deg, altitude {:.1} km",
        itrf.latitude_deg(),
        itrf.longitude_deg(),
        itrf.hae() / 1e3
    );

    Ok(())
}
$ cargo run --example quickstart
ISS (ZARYA) at 2024-01-01T12:46:00.000000Z
  latitude -51.019 deg, longitude -17.397 deg, altitude 441.6 km

Examples

Every program on these pages is a file in examples/ of the repository, compiled in CI, and runnable from a checkout with cargo run --example <name>:

Page Example Needs
this page quickstart EOP for the TEME → ITRF rotation
Time and Time Scales time_scales, chrono_interop EOP for UT1 only; chrono_interop needs --features chrono
Coordinate Frames frames EOP for exact ITRF rotations
SGP4, TLEs and OMMs sgp4_tle EOP for the TEME → ITRF / GCRF rotations
Numerical Propagation propagate_leo JPL ephemeris, EOP, space weather
Sun, Moon and Ephemerides sun_moon, ephemeris nothing; JPL ephemeris for ephemeris
Kepler and Lambert kepler_lambert nothing

"EOP" and "space weather" are fetched on first use when the download feature is on; the JPL ephemeris is a one-time 102 MB download. The API reference on docs.rs documents every type and function.