<img src="https://raw.githubusercontent.com/Empyrean-Dynamics/empyrean/main/docs/empyrean-dynamics-icon.png" width="140" alt="empyrean-sys">
# empyrean-sys
Low-level FFI bindings to the libempyrean astrodynamics C ABI
<a href="https://github.com/Empyrean-Dynamics/empyrean/actions/workflows/ci.yml"><img src="https://github.com/Empyrean-Dynamics/empyrean/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://crates.io/crates/empyrean-sys"><img src="https://img.shields.io/crates/v/empyrean-sys.svg?style=flat-square&label=crates.io" alt="crates.io"></a>
<a href="https://docs.rs/empyrean-sys"><img src="https://img.shields.io/docsrs/empyrean-sys?style=flat-square&label=docs.rs" alt="docs.rs"></a>
<br>
<a href="Cargo.toml"><img src="https://img.shields.io/badge/rustc-1.90%2B-orange?style=flat-square&logo=rust" alt="MSRV 1.90"></a>
<a href="https://github.com/Empyrean-Dynamics/empyrean/blob/main/LICENSE-BSD"><img src="https://img.shields.io/badge/source-BSD--3--Clause-blue.svg?style=flat-square" alt="Source license"></a>
<a href="https://github.com/Empyrean-Dynamics/empyrean/blob/main/LICENSE-BINARY"><img src="https://img.shields.io/badge/binary-proprietary-lightgrey.svg?style=flat-square" alt="Binary license"></a>
<a href="https://doi.org/10.5281/zenodo.21318471"><img src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.21318471-blue?style=flat-square" alt="DOI"></a>
<br>
<a href="https://claude.ai"><img src="https://img.shields.io/badge/Built%20with-Claude%20Code-D97757?logo=anthropic&logoColor=white&style=flat-square" alt="Built with Claude Code"></a>
<a href="https://www.empyrean-dynamics.com"><img src="https://img.shields.io/badge/Website-empyrean--dynamics.com-1a1a2e?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiLz48bGluZSB4MT0iMiIgeTE9IjEyIiB4Mj0iMjIiIHkyPSIxMiIvPjxwYXRoIGQ9Ik0xMiAyYTE1LjMgMTUuMyAwIDAgMSA0IDEwIDE1LjMgMTUuMyAwIDAgMS00IDEwIDE1LjMgMTUuMyAwIDAgMS00LTEwIDE1LjMgMTUuMyAwIDAgMSA0LTEweiIvPjwvc3ZnPg==&logoColor=white&style=flat-square" alt="Website"></a>
<a href="https://github.com/Empyrean-Dynamics"><img src="https://img.shields.io/badge/GitHub-Empyrean--Dynamics-1a1a2e?logo=github&logoColor=white&style=flat-square" alt="GitHub"></a>
---
empyrean-sys exposes the C ABI of `libempyrean` to Rust as raw,
`unsafe`, bindgen-generated declarations. It does not attempt to wrap,
type-check, or RAII-manage the underlying handles.
```toml
[dependencies]
empyrean-sys = "0.9.0"
```
```rust
use empyrean_sys::*;
// All entry points are unsafe; pointer ownership and lifetime are the
// caller's responsibility. See include/empyrean.h at the repository
// root for the authoritative C ABI documentation.
unsafe {
// Null data_dir = the platform default data directory; downloads
// any missing kernels. Returns null on error (see empyrean_last_error).
let ctx: *mut EmpyreanContext = empyrean_context_from_data_dir(std::ptr::null());
assert!(!ctx.is_null());
empyrean_context_free(ctx);
}
```
**Most users want the safe wrapper instead** — see the
[`empyrean`](https://crates.io/crates/empyrean) crate, which builds on
empyrean-sys to provide typed handles, `Result`-returning entry points,
and Rust-native lifetime management.
## What the bindings cover
The declarations track the full C ABI at `EMPYREAN_ABI_VERSION`
(currently `2`), including the v0.9.0 wide-parameter fitting surface
and the ABI-2 output surface below. ABI 2 grew existing struct shapes
by appending fields, so consumers must recompile against the matching
header. Each type below maps 1:1 onto a C struct; consult `include/empyrean.h`
at the repository root for field-level semantics.
- **Wide-parameter OD.** `empyrean_determine` / `empyrean_refine` solve
beyond the 6-parameter state and the Marsden A1/A2/A3 non-gravitational
block for the cometary outgassing time delay DT, the SRP area-to-mass
ratio AMRAT, and per-segment thrust Δv corrections on continuous-thrust
arcs — each partial produced analytically by the hyperdual integrator. The per-axis `EmpyreanSolveFor`
(`marsden` / `dt` / `amrat` / `thrust_segments`) is read under
`EMPYREAN_SOLVE_FOR_EXPLICIT`. DT / AMRAT / thrust are refine-path solves:
the input orbit must carry the corresponding prior (its declared variance)
to open the axis, and requesting an axis without its prior errors out
rather than returning a zeroed or defaulted column.
- **Tagged solved covariance.** `EmpyreanSolvedCovariance` carries the
fitted-parameter identities alongside the matrix: `marsden_slot`,
`dt_slot`, `amrat_slot`, and `thrust_slots` locate each parameter's
row/column, with `EMPYREAN_SLOT_NONE` marking an axis that was not solved.
Read a parameter's variance by its slot — the `width` alone is ambiguous.
- **Post-OD photometry.** With `EmpyreanODConfig::has_photometry` set,
`EmpyreanPhotometryConfig` requests a fit recovering absolute magnitude
`H` and the phase-function slopes from the observation magnitudes,
climbing the H-only → HG12 → HG1G2 ladder (Muinonen et al. 2010) to the
richest model the arc's phase coverage supports. `EmpyreanODPhotometryResult`
reports the fitted `h` / `slope1` / `slope2`, the `model_used`, a 3×3
`covariance` giving an honest 1σ on `h`, plus
`n_mags_dropped_unconvertible` and the distinct offending band codes
in `dropped_bands` when magnitudes could not be converted to V — the
observations' astrometry is unaffected.
- **Ephemeris covariances and warnings.** Each `EmpyreanEphemerisEntry`
carries a 6×6 sky-plane covariance over (rho, RA, Dec) and their rates
in (AU, deg) units, and the aberrated (light-time corrected)
barycentric ICRF Cartesian state at the photon-emission epoch with its
own 6×6 covariance — `has_covariance` / `has_aberrated_covariance`
gate each block (absent unless the input orbit carried a state
covariance). `EmpyreanEphemerisResult` returns run-level non-fatal
`warnings`, e.g. Earth-orientation kernel coverage gaps served by the
analytic IAU 2006 fallback, or rows whose sensitivity chain was
skipped.
- **Per-observation diagnostics.** `EmpyreanObservationResult` carries
radar delay/Doppler residuals (observed − predicted, seconds / hertz,
with `radar_chi2`, `radar_dof`, `radar_probability`, and the combined
`radar_variance`), the D-optimality `influence_information_loss` on
removal (+∞ marks an indispensable observation), and
`along_cross_covariance_arcsec2` completing the 2×2 along/cross-track
covariance.
- **Covariance trust verdict.** `EmpyreanODResult::covariance_trust`
reports an event-aware verdict on the delivered covariance
(`EMPYREAN_COVARIANCE_TRUST_*`): `TRUSTED`, `ENCOUNTER_INTERVENES`
(with the intervening close-approach or high-nonlinearity event and
whether a second-order state-only correction can recover it), or
`WEAKLY_DETERMINED_HIGH_N`. `NOT_EVALUATED` means no gate ran —
absence of a verdict is not trust.
- **Impact-probability detail.** Each `EmpyreanImpactProbability` row
carries the geodetic impact point (latitude / longitude / altitude on
the body's reference ellipsoid, when the surface projection is
available), the Monte-Carlo 95% binomial confidence half-width on
`ip_mc`, the second-order corrected mean miss distance with its 1σ
uncertainty and skewness, the closest-approach distance `gradient`
(6-vector) and `distance_hessian` (6×6) with respect to the initial
state, and the adaptive Gaussian-mixture component count.
- **Plan evaluation.** Radar candidates in `EmpyreanPlanCandidate`
carry the effective SNR (linear power ratio), one-way range (km),
link-budget provenance notes, and the measurement mode;
`EmpyreanPlanResult` carries the predicted optical ephemeris (epoch
MJD TDB, RA, Dec per optical candidate).
- **Basis-tagged mixture components.** Each `EmpyreanMixtureComponent`
is tagged with the reference `frame` and center-body `origin` (NAIF
id) its mean and covariance are expressed in.
## Runtime requirement
empyrean-sys opens `libempyrean.{dylib,so}` at run time via
`libloading` (dlopen). The library is distributed separately as a
binary release on
[GitHub](https://github.com/Empyrean-Dynamics/empyrean/releases) and
inside the published Python wheel. The path is resolved from the
`EMPYREAN_LIB` environment variable if set, else a `libempyrean.*`
sitting next to the loaded module, else a build-time location — an
`EMPYREAN_LIB_DIR` override, a sibling `../target/release` build, or
a checksum-pinned download from the GitHub release (in that order).
The FFI bindings are pre-generated and committed, so no C header,
libclang, or bindgen is needed to build.
Prebuilt engine binaries are currently published for four targets:
macOS arm64 (`macos-aarch64`), macOS x86_64 (`macos-x86_64`), Linux
x86_64 (`linux-x86_64`), and Linux aarch64 (`linux-aarch64`). On other
targets the build stops with an error unless `EMPYREAN_LIB_DIR` points
at an engine build.
## License
Source code in this crate is licensed under the
[BSD 3-Clause License](LICENSE). The closed-source `libempyrean`
runtime it loads at runtime is governed by a separate proprietary binary
license; see the main repository for the dual-license breakdown.
Copyright © 2024–2026 Joachim Moeyens. All rights reserved.