Go to file
ApsisRS contributors 77425a6e9d docs: record the benchmark results in the README 2026-10-02 13:06:37 +08:00
.cargo docs: render formulas as math with a vendored MathJax 2026-10-02 12:28:20 +08:00
.github/workflows docs: render formulas as math with a vendored MathJax 2026-10-02 12:28:20 +08:00
crates chore: resolve the pedantic clippy lints across the workspace 2026-10-02 13:06:16 +08:00
docs docs: record the benchmark results in the README 2026-10-02 13:06:37 +08:00
scripts docs: render formulas as math with a vendored MathJax 2026-10-02 12:28:20 +08:00
validation docs: bilingual comments for the validation tools 2026-10-02 01:44:36 +08:00
.gitignore workspace: add geodesy and time cross-check scripts 2026-09-26 13:47:59 +08:00
Cargo.lock access: search a constellation's tracks across several threads 2026-09-30 18:37:02 +08:00
Cargo.toml chore: resolve the pedantic clippy lints across the workspace 2026-10-02 13:06:16 +08:00
LICENSE workspace: initialize workspace with all layer crates 2026-09-26 12:53:13 +08:00
README.md docs: record the benchmark results in the README 2026-10-02 13:06:37 +08:00
README.zh.md docs: record the benchmark results in the README 2026-10-02 13:06:37 +08:00
rust-toolchain.toml workspace: initialize workspace with all layer crates 2026-09-26 12:53:13 +08:00

README.md

ApsisRS

Computational kernels for commercial spaceflight and satellite internet, in Rust.

English | 简体中文

Scope

  • Pure Rust, standard library only, no third-party crate dependency
  • Clear domain boundaries with a one-way dependency order between layers
  • The public facade crate is apsis-rs; its library name is apsis
  • Every crate is publish = false at this stage, so none of them is released

Layers

foundation -> physics -> orbit -> rf -> network -> ops -> sim -> apsis-rs

The rules:

  • Dependencies flow to the left only. A lower layer may use any layer to its left; a higher layer may not use any layer to its right. So network may use orbit, but orbit may not use network. A leftward dependency may cross as many layers as it needs, because the layers in between need not offer a type that carries the value forward.
  • apsis-core in foundation is the global baseline that any crate in any layer may use. apsis-math is the same, because linear algebra is shared across the layers.
  • Within a layer, that layer's facade crate (apsis-orbit, apsis-rf, apsis-sim) is the integration point.

scripts/check-layers.sh enforces these rules and CI runs it.

Layer Responsibility Crates
foundation Base types, units, mathematics, macros apsis-core, apsis-units, apsis-math, apsis-macros
physics Time, leap seconds, coordinates, datum apsis-time, apsis-leap, apsis-coord, apsis-geodesy, apsis-frames
orbit TLE, SGP4, two-body, ephemeris, maneuvers apsis-tle, apsis-sgp4, apsis-kepler, apsis-ephemeris, apsis-maneuver, apsis-orbit
rf Radio frequency, antennas, link budget apsis-rf, apsis-antenna, apsis-doppler, apsis-atmosphere, apsis-noise, apsis-interference, apsis-link
network Constellations, coverage, access, topology, routing apsis-constellation, apsis-coverage, apsis-access, apsis-topology, apsis-isl, apsis-routing, apsis-handover, apsis-mesh
ops Resources, tasks, scheduling, conflicts, schedules apsis-resource, apsis-task, apsis-scheduler, apsis-conflict, apsis-schedule
sim Simulation engine, scenarios, events, output apsis-sim-engine, apsis-scenario, apsis-event, apsis-output, apsis-sim
facade Public facade apsis-rs

Building

Run every Rust command from the repository root:

cargo check --workspace
cargo test -p apsis-core -p apsis-units -p apsis-math

The apsis-rs facade selects the layers it needs by feature:

[dependencies]
apsis-rs = { path = "...", features = ["orbit"] }

The features are foundation, physics, orbit, rf, network, ops, sim and full. A layer feature enables the layers below it. full enables everything including sim; ops is every layer except sim, for a project that does not run simulations.

Performance

The three benchmarks are examples of the facade. They run in release mode and depend on nothing outside the workspace, so a build is the compiler and nothing else. The figures below are one run on an AMD Ryzen 7 8845HS (eight cores, sixteen threads); absolute times move with the machine, so compare the ratios within a table. The method, every size in the sweeps and the reasoning behind each conclusion are in Benchmark method and results.

One call on the hot paths

Path Per call
Hermite ephemeris query 24 ns
TEME to Earth-fixed rotation 27 ns
Keplerian elements to a state 38 ns
Earth-fixed to geodetic 50 ns
Link budget C/N0 61 ns
SGP4 propagation, thirty minutes from epoch 249 ns
Access search, 120 samples 7.5 us
Planning 60 tasks on one resource 239 us

An ephemeris query is an order of magnitude cheaper than the propagation it stands in for, which is why a densely queried track is tabulated once and then interpolated.

Growth with size. A flat cost per unit is what shows the complexity is right:

  • A day of a constellation at a sixty second step is linear in the satellite count: 24 satellites in 8.7 ms, 2016 in 0.68 s, about 234 ns a sample, an exponent of 0.00.
  • Access search is flat at about 7.3 us a track from 8 to 128 tracks.
  • Topology construction is about O(n squared) and greedy planning eases from O(n squared) towards O(n); both are limited by their algorithm, and the design note says why.

Threads. The numerical kernels split by satellite. Propagating 2000 satellites over a day takes 671 ms on one thread and 85 ms on sixteen, a speedup of 7.9x; access search over 256 tracks reaches 5.3x. Efficiency is about eighty-five percent at eight threads, one per core; sixteen puts two on a core and returns about half of that. Every parallel result equals the serial one, so threading buys throughput and never changes a number.

Conventions

  • Toolchain stable, edition 2024, rust-version 1.85
  • No third-party crate by default, including dev-dependencies and build scripts
  • Library code uses no unwrap, expect or panic. A failure on external input is always a Result or an Option. An expect is allowed on an internal invariant that a check just above already guards, such as the element just pushed onto a vector, or a value whose absence the line above ruled out. There are thirteen such sites and each is listed with its guard in docs/design/errors.md.
  • Every public item carries a documentation comment, and the comment is self-contained
  • Tests live in each crate's tests/ directory, separate from src/. They are integration tests and reach the crate only through its public API; no inline #[cfg(test)] module is used
  • Documentation follows the X.md and X.zh.md convention, with English in X.md and Simplified Chinese in X.zh.md

Layout

apsis-rs/
├── Cargo.toml
├── crates/          # Rust workspace members
│   └── apsis-rs/
│       ├── examples/   # validation dumps and cross-layer checks
│       └── ...
├── scripts/         # repository check scripts
├── validation/      # Python cross-checks
└── docs/design/     # design notes

Tests live in each crate's tests/ directory. The cross-layer integration checks and the dependency-free benchmarks are examples of the facade:

# Cross-layer integration checks, which CI runs
cargo run -p apsis-rs --features network --example integrate_network
cargo run -p apsis-rs --features ops --example integrate_ops
cargo run -p apsis-rs --features sim --example integrate_sim

# Hot path benchmark, run in release
cargo run --release -p apsis-rs --features full --example bench

# Scale sweep: the input grows and the cost per unit of work shows the trend
cargo run --release -p apsis-rs --features full --example bench_scale

# Parallel sweep: the same work over 1, 2, 4, 8 and 16 threads
cargo run --release -p apsis-rs --features full --example bench_parallel

Documentation

The design notes record the cross-crate decisions that a reader of a single crate cannot infer:

The generated API documentation renders mathematical formulas offline. A copy of MathJax is vendored under docs/vendor/mathjax, docs/mathjax.html loads it, and crates/apsis-rs/build.rs copies it into the documentation root, so the generated pages need no network access. Build them with bash scripts/build-docs.sh --workspace --all-features --no-deps; a plain cargo doc also works, and the script only makes the copy reliable. A display formula is written as $$ ... $$ inside a <div>; the surrounding div keeps the markdown layer from rewriting the LaTeX.

Licence

GPL-3.0-or-later, see LICENSE.

The vendored MathJax under docs/vendor/mathjax is Apache-2.0; see the LICENSE in that directory.