empyrean 0.10.0

Uncertainty-first orbit propagation, ephemeris, orbit determination, and event detection for asteroids and comets, powered by automatic differentiation
Documentation
//! File I/O for orbits, ephemerides, events, and residuals.
//!
//! Three file formats are supported per data type:
//!
//! - **Parquet** — round-trips covariance and is the canonical batch
//!   format. Recommended for data that flows back into the propagator
//!   (parquet's nullable covariance columns are the only way to
//!   round-trip uncertainty).
//! - **JSON** — human-readable, retains structure (covariance arrays
//!   when present), good for small batches and config-style use.
//! - **CSV** — flat row-per-record, easiest to feed into spreadsheets or
//!   shell pipelines. Carries the same columns as parquet, covariance
//!   included; the two differ only in how a non-computable number is
//!   spelled (CSV writes a literal `NaN`, JSON writes `null`, since
//!   JSON has no NaN literal).
//!
//! Orbits support read + write in all three formats; ephemeris,
//! events, residuals, and the per-object fit summary are write-only —
//! the propagator / OD pipeline is the canonical producer.
//!
//! All conversion happens at the FFI boundary; the wrapper returns
//! Rust-friendly types ([`OrbitBatch`], [`EphemerisEntry`](crate::EphemerisEntry),
//! …) instead of raw C arrays.

mod ephemeris;
mod events;
mod fit_summary;
mod orbits;
mod residuals;

pub use ephemeris::{write_ephemeris_csv, write_ephemeris_json, write_ephemeris_parquet};
pub use events::{write_events_csv, write_events_json, write_events_parquet};
pub use fit_summary::{
    FitSummaryRow, write_fit_summary_csv, write_fit_summary_json, write_fit_summary_parquet,
};
pub use orbits::{
    OrbitBatch, read_orbits_csv, read_orbits_json, read_orbits_parquet, write_orbits_csv,
    write_orbits_json, write_orbits_parquet,
};
pub use residuals::{write_residuals_csv, write_residuals_json, write_residuals_parquet};

use std::ffi::CString;
use std::path::Path;

use crate::error::{Error, Result};

/// Path → CString helper shared across the I/O sub-modules.
pub(super) fn path_to_cstring(path: &Path) -> Result<CString> {
    let bytes = path
        .to_str()
        .ok_or_else(|| Error::invalid_input("path is not valid UTF-8"))?
        .as_bytes();
    CString::new(bytes).map_err(|_| Error::invalid_input("path contains a NUL byte"))
}