Skip to content

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 04 Jun 14:06
· 141 commits to main since this release

Changed

  • Real-data validation. The multi-GNSS RINEX navigation parser, the GLONASS
    RK4 propagator, and the SP3 reader are now exercised against genuine IGS/DLR
    files (a real RINEX 3 mixed broadcast nav file and an IGS SP3-c orbit product),
    not only self-authored samples — asserting non-empty satellite sets and finite,
    physically-sized ECEF positions. The fixtures are test-only (excluded from the
    published crate); see tests/fixtures/igs/NOTICE.
  • RAIM on real reference-orbit geometry. The snapshot, solution-separation
    (MHSS), and ARAIM protection-level cores are now validated against the real IGS
    precise-orbit (SP3) geometry, not synthetic constellations alone: the line-of-sight
    geometry is built from the first SP3 epoch at a real ground station, and the tests
    assert metre-level, APV-I-available protection levels, that a 60 m pseudorange bias
    trips the χ² monitor, that solution separation identifies the faulted satellite,
    and that ARAIM's levels meet the allocated P_HMI. Closes the
    validated-on-synthetic-geometry-only gap (receiver-domain gLAB parity over a full
    RINEX arc remains a roadmap item — it needs a pseudorange solution).

Added

  • Per-node confidence intervals for the N-D parameter sweep (sweep::nd_sweep_ensemble).
    Each grid node of the N-dimensional Cartesian-product sweep can now be evaluated as a
    Monte-Carlo ensemble of seeds, reporting the metric's mean, percentiles, and a
    percentile-bootstrap 95% CI per node (for both clocks) — a statistically honest sweep
    rather than one draw per node. Reuses the ensemble/bootstrap machinery (metric_stat);
    deterministic; runs = 1 reduces exactly to the single-seed nd_sweep. (Generalising
    the sweep across all packs, entangled with the typed-Scenario refactor, and parallel
    execution remain.)
  • NaveGo cross-validation of the IMU-noise pipeline (tests/navego_imu_crossval.rs).
    An external cross-check against NaveGo (R. Gonzalez's open-source INS/GNSS toolbox):
    reproduces the synthetic round-trip of navego_example_allan.m on its published
    Microstrain 3DM-GX3-35 reference profile, confirming our overlapping-ADEV estimator
    recovers NaveGo's velocity- and angle-random-walk coefficients (ADEV(1 s) = σ·√dt)
    to under 5% with the expected −1/2 white-noise slope. (The 40 MB recorded STIM300
    .mat log is not ingested — binary-format-gated.)
  • Tightly-coupled (pseudorange) GNSS/INS update. GnssInsEkf::update_tightly_coupled
    (and the ClosedLoopInsGnss::fuse_tightly_coupled wrapper) implement the
    previously-stubbed range-domain measurement: the innovation is the predicted
    range from the INS position to each satellite versus the measured pseudorange,
    with a line-of-sight Jacobian on the position error. Because each satellite is a
    scalar measurement, the filter keeps correcting with fewer than four
    satellites
    — where a loosely-coupled PVT fix does not exist. Five tests cover
    four-satellite nulling, two-satellite correction (no PVT possible), single-
    satellite along-line-of-sight observability, and input validation. Pseudorange-
    only; carrier phase and an explicit receiver-clock state remain roadmap. The
    unused tight_coupling cargo feature (which gated the old error stub) is removed.
  • Loosely-coupled GNSS/INS scenario pack (kind = "gnss-ins", src/fusion/pack.rs).
    Wires the three-axis strapdown navigator and the 15-state error-state EKF
    (closed_loop / gnss_ins_ekf) into a runnable scenario with a figure of merit —
    the EKF disciplines the mechanization against noisy GNSS fixes while coverage is
    up, then coasts through the outage, replacing the legacy 1-DOF scalar pack's
    truth-snap reset with genuine fusion. The result reports the fused horizontal
    error series, the scored position FoM (availability / outage RMS / holdover), and
    the open-loop free-INS RMS for comparison; a quantum/classical IMU pair differs
    only in true bias. Dispatched from the CLI/Python/wasm entry point with a
    scenarios/gnss-ins.toml example. Honest framing: loosely-coupled only, one
    deterministic trajectory, and the fused outage error is floor-limited by the
    hand-over attitude error (so it is not claimed to scale with bias) — the robust
    findings are that fusion beats unaided dead-reckoning for a biased sensor and that
    a lower-bias sensor has the better unaided coast.
  • Constellation design on the validated SGP4 core (src/walker.rs). A new
    walker module emits a designed Walker-delta pattern (i: T/P/F) as SGP4
    mean elements, so the synthetic constellation propagates through the same
    SGP4 path validated to 4.12 mm against the AIAA 2006-6753 vectors — not the
    analytic Keplerian generator. On top of it: pdop_sweep tabulates coverage and
    median/worst PDOP over a {planes × sats × inclination} design grid, and
    coverage_revisit reports the coverage fraction and revisit gaps (worst/mean)
    at a ground point. Validated by the physical monotonicities a trade must obey
    (more satellites ⇒ higher coverage, lower PDOP, shorter revisit). Separately, a
    genuine Celestrak gps-ops TLE snapshot (2021-07-28, 30 operational GPS
    satellites) is added as a test-only fixture and the real-TLE → SGP4 → ECEF
    geometry path validated against it (full MEO shell within 1%, nine-satellite
    all-in-view at PDOP 1.64), alongside the existing SP3 and RINEX real-data paths.
  • Noise-type-specific effective degrees of freedom for the Allan confidence
    intervals.
    allan::edf_overlapping_adev implements the NIST SP 1065 Table 5
    closed forms (the Stable32 simple set) for all five canonical power-law noise
    types — white/flicker PM, white/flicker FM, random-walk FM — replacing the
    conservative non-overlapping count as the χ² degrees of freedom. A new
    PowerLawNoise enum and classify_power_law identify the dominant type from
    the record's modified Allan-deviation slope (MDEV separates white from
    flicker PM where ADEV cannot), and overlapping_adev_curve now attaches the
    identified noise type, its edf, and a 95% confidence band to every point of the
    exported ADEV curve (AdevPoint gains noise/edf/ci_lo/ci_hi, additive
    with serde defaults). Validated two ways: the five formulas match hand-evaluated
    values to 1e-12, and a 4 000-record Monte-Carlo white-FM ensemble confirms the
    formula predicts the estimator's actual chi-squared edf within 20% (and that it
    materially beats the conservative count). Eight new tests.
  • Two-way time-transfer stochastic model. timetransfer::TwoWayLink replaces the
    white-only sampler with a physically-grounded model: the reciprocal (common-mode) path
    delay cancels in the (m_AB - m_BA)/2 estimate (two_way_offset_estimate, so two
    independent one-way measurements average to 1/sqrt(2)), and the residual is the
    non-reciprocal differential delay — modelled as a colored white-FM + random-walk-FM
    process (the validated ClockModel), giving the synchronization-error series a realistic
    Allan signature (sigma_y^2(tau) = q_rw*tau/3) instead of flat white noise. LinkCfg
    gains q_wf_s/q_rw_s (serde default 0 ⇒ the legacy white-only behaviour, bit-for-bit),
    the link FoM reports adev_tau0 (the model's Allan deviation at the base step), and the
    timetransfer scenario/CLI surface it. Golden FoM re-pinned. Six hand-derived tests
    (common-mode cancellation, the sqrt(2) two-way gain, the RWFM tau/3 law via the link's
    own step(), legacy-equivalence at q=0, determinism, and end-to-end FoM exposure).
  • Stable32 numeric parity for the Allan-family estimators (NBS14). tests/allan_reference.rs
    validates the overlapping ADEV, modified ADEV, time deviation, and overlapping Hadamard
    estimators against the Stable32 reference deviations for the canonical NBS14 dataset
    (W. J. Riley, Handbook of Frequency Stability Analysis, NIST SP 1065, ~p.107) at
    tau = 1, 2 to a 1e-4 relative tolerance — actual agreement ~1e-6. This pins the
    estimator mathematics against the de-facto reference implementation, not just against
    the estimators' own analytic self-consistency. Only the public reference numbers are
    used; no third-party code.
  • Vertical Stanford integrity diagram exported by the integrity scenario. The
    runnable integrity scenario kind now exports a vertical Stanford(-ESA) diagram
    alongside the HPL/VPL availability map: at each protected epoch a seeded, reproducible
    no-fault range-error draw is mapped through the geometry to an actual vertical position
    error and classified against the VPL and the vertical alert limit (Available /
    System-Unavailable / Misleading / Hazardously-Misleading). The diagram (per-epoch
    points + region counts) is carried in the result JSON and the integrity-event / HMI
    counts in the CLI summary, so the Stanford classifier — previously library-only — is
    reachable end-to-end. IntegrityScenario gains a seed field (default 0) controlling
    the error realization; the availability map itself remains geometry-only and seed
    independent.
  • ARAIM integrity-risk (P_HMI) budget for the protection levels. raim::araim_raim
    derives the horizontal and vertical protection levels from an explicit integrity-risk
    budget rather than a fixed K_md multiplier: for the all-in-view solution and every
    single-satellite exclusion sub-solution it builds the per-mode (prior, detection threshold, σ) on each axis, then araim_protection_level solves the smallest PL whose
    summed probability of hazardously-misleading information (araim_integrity_risk,
    P_HMI = Σ_k p_fault,k · Q((PL − T_k)/σ_k), Blanch et al. Baseline ARAIM) meets the
    allocated P_HMI. The result reports the integrity risk the levels actually achieve, so
    a user can trade integrity against the alert limit explicitly. Six hand-derived tests
    (fault-free and thresholded single-mode closed forms, multi-mode summation/monotonicity,
    end-to-end fault-free protection with a 10⁵× tighter budget raising the PL, fault
    detection/identification, and the six-satellite redundancy floor). Single-fault MHSS is
    the ARAIM baseline; simultaneous multi-SV-subset faults, the constellation-wide fault
    mode, and gLAB reference-dataset validation are documented extensions.
  • Two-speed coning/sculling compensation for the strapdown mechanization.
    inertial::mechanization::coning_sculling_compensate folds the high-rate coning
    (attitude) and rotation+sculling (velocity) terms out of a coarse update's
    ordered sub-interval IMU increments, so a moderate-rate NavState::step_increments
    reproduces vibration-rectified motion a coarse step over the raw sums misses. A
    validation test drives a 10 Hz coning+sculling environment for 60 s and compares
    fine-rate truth, naive coarse integration, and the folded coarse integration:
    the fold cuts the position error by ~18× (metres of naive drift → sub-decimetre),
    confirming the coning/sculling terms are load-bearing. A ScalarErrorBudget
    type alias names the legacy 1-DOF AccelModel for what it is, distinct from the
    three-axis NavState navigator.
  • RINEX observation-file parser. New rinex_obs module reads the RINEX 3.0x /
    4.00 observation file — the receiver's actual measurements — completing the
    RINEX pair alongside the existing navigation-message parser. parse_obs decodes
    the header (version/type, the per-system SYS / # / OBS TYPES lists with
    continuation lines, approximate position, interval, time of first observation)
    and each epoch's per-satellite records: pseudorange, carrier phase, Doppler, and
    signal strength, keyed by their RINEX 3 observation code (C1C, L1C, …) with
    the loss-of-lock (LLI) and signal-strength (SSI) flags, a blank field preserved
    as absent rather than zero. Honest scope: this is the standards-format ingest
    (a real RTKLIB/gLAB/IGS-station observation log in, typed measurements out), not
    a positioning engine — no pseudorange solution, PPP, or RTK here.
  • CCSDS OEM (Orbit Ephemeris Message) writer. New oem module exports a
    propagated constellation as a valid CCSDS 502.0-B OEM 2.0 message —
    the KVN ephemeris format GMAT, Orekit, STK, and most flight-dynamics tools
    ingest. OemFile::from_propagators samples each satellite's inertial
    (TEME) state — position and velocity, taken straight from the propagator
    with no Earth-fixed rotation, unlike the SP3 export — onto a time grid, and
    OemFile::to_oem_string serialises the CCSDS_OEM_VERS/CREATION_DATE/
    ORIGINATOR header plus one META_START … META_STOP segment per satellite
    (OBJECT_NAME/OBJECT_ID/CENTER_NAME/REF_FRAME = TEME/TIME_SYSTEM = GPS/
    START_TIME/STOP_TIME) followed by its epoch X Y Z X_DOT Y_DOT Z_DOT
    lines (km, km/s). The CREATION_DATE is caller-supplied, never wall-clock, so
    output is byte-identical across runs (the reproducibility contract). This is the
    spacecraft-ephemeris counterpart to the GNSS SP3 export: a Kshana orbit can now
    be handed to a flight-dynamics tool in a standard format.
  • SP3 precise ephemeris as a propagation source. Sp3File::interpolator
    builds a per-satellite Sp3Interpolator that fills the position between the
    tabulated SP3 epochs with a 9th-order Lagrange polynomial (standard IGS
    practice) and rotates it into the shared TEME frame, exposed as
    Propagator::Sp3Precise. An IGS/analysis-centre precise-orbit file can now drive
    the same geometry/visibility/integrity pipeline as the broadcast and analytic
    propagators. Validated round-trip: a Kepler orbit written to SP3 and re-read
    through the interpolator matches the original to sub-metre at the nodes and
    < 100 m mid-interval. Clock interpolation is next.
  • GLONASS broadcast ephemeris (completes multi-GNSS RINEX nav). New glonass
    module: GLONASS doesn't broadcast Keplerian elements but a PZ-90 Earth-fixed
    state vector (position, velocity, luni-solar acceleration). parse_glonass_nav
    reads the RINEX 3 R records, and the satellite position at any time is obtained
    by 4th-order Runge–Kutta integration of the GLONASS ICD equations of motion
    (central gravity + J2 + Earth-rotation Coriolis/centrifugal terms + the
    broadcast acceleration). Exposed as Propagator::Glonass, so GLONASS satellites
    flow through the constellation/visibility/integrity pipeline alongside the
    Keplerian systems; a single rinex constellation block can now mix GPS, Galileo,
    QZSS, BeiDou, and GLONASS.
  • Multi-GNSS RINEX navigation (GPS, Galileo, QZSS, BeiDou). The RINEX 3
    navigation parser now decodes Galileo (E), QZSS (J), and BeiDou (C,
    MEO/IGSO) records alongside GPS (G) — they share the Keplerian layout and user
    algorithm — each evaluated with its own gravitational constant and Earth-rotation
    rate (Galileo/BeiDou μ, BeiDou Ω̇ₑ). A mixed-constellation file yields all of
    them, flowing through the constellation/visibility/integrity pipeline as
    Propagator::Rinex. BeiDou geostationary satellites use a different coordinate
    rotation and are skipped pending a reference fixture to validate against. The
    record walker uses per-system line counts, fixing a latent bug where four-line
    GLONASS/SBAS records were skipped as if eight lines long. GLONASS (a state-vector
    model) is next.
  • SP3-c/d precise-ephemeris reader and writer. New sp3 module parses
    IGS/analysis-centre SP3 precise orbit files (parse_sp3) — the post-processed
    ECEF position/clock product that PPP engines (Ginan, RTKLIB, gLAB) treat as
    reference — into a structured Sp3File (header, epoch grid, per-satellite
    position km→m, clock µs, and velocity dm/s→m/s for V products), preserving the
    SP3 bad-value sentinels. The reverse direction is also covered:
    Sp3File::from_propagators builds an SP3 from a propagated constellation
    (TEME→ECEF per epoch) and to_sp3_string serialises it, so Kshana orbits can be
    exported in the format external PPP tools ingest — the read↔write round trip.
    Epoch interpolation and an SP3 propagator source are next.
  • RINEX broadcast ephemeris as a runnable constellation source. A
    constellation now accepts an inline rinex block (RINEX 3 GPS navigation
    text) alongside the existing tle option, so a real broadcast file drives a
    scenario end-to-end from the CLI, Python, or the in-browser playground — RINEX
    in, PNT geometry out. New scenarios/orbit-rinex.toml demonstrates GNSS
    availability and DOP from eight GPS satellites built straight from broadcast
    records. (GPS LNAV only; multi-GNSS and SP3 are next.)
  • RINEX broadcast ephemeris as a propagation source. A parsed
    RinexEphemeris now converts to an orbit::Propagator (Propagator::Rinex):
    position is the IS-GPS-200 broadcast orbit rotated from ECEF into the shared
    TEME inertial frame (sv_position_teme, with leap-second-correct GPS→UT1 time),
    velocity by central finite difference, and the Keplerian orbital period. Real
    GPS broadcast data can now drive the same geometry, visibility, and integrity
    (RAIM) pipeline as the analytic SGP4/Keplerian propagators. (Not yet exposed as
    a RINEX-file scenario kind — that and multi-GNSS/SP3 are next.)

Full changelog: CHANGELOG.md · Docs: README