Skip to content

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 05 Jun 15:01
· 99 commits to main since this release

Changed

  • Honest framing for the quantum positioning. The headline descriptor is now a
    "PNT-resilience simulator with quantum-sensor performance models" consistently
    across the README tagline, citation line, CITATION.cff (title + abstract), and
    the banner artwork — replacing the looser "hybrid quantum/classical PNT simulator"
    marketing phrasing. The README's What it is / is not section gains an explicit
    "It is not (yet)" scope statement (not a first-principles atom-interferometry
    physics engine, not a GNSS receiver/PVT solver, not a mission-design tool), and a
    new top-level ROADMAP.md makes the Quantum physics layer a P2
    item
    (Mach–Zehnder CAI phase, projection noise, vibration tensor) so readers know
    the first-principles physics is scoped-and-coming, not abandoned. No behaviour or
    API change.

Added

  • Constellation-design trade study: Walker design sweep with a Pareto front, revisit-time
    JSON, and a sub-kilometre Walker-formula validation.
    src/walker.rs gains
    walker_design_sweep, which runs a planes × sats_per_plane grid (e.g. a 3×3 trade) at a
    fixed inclination and tabulates, per design, the coverage fraction, worst-case PDOP, and the
    max/mean revisit gap; pareto_front flags the non-dominated designs (fewer satellites, more
    coverage, lower PDOP, shorter revisit), and WalkerDesignReport::to_json serialises the cells
    and Pareto front — revisit-time fields included — as JSON. New validation pins the generator to
    the Walker i:T/P/F formula: same-slot satellites in adjacent planes are shown to map onto one
    another by an exact R_z(2π/P) rotation to under 1 km over a full 24 h of SGP4 propagation
    (the J2 short-period breathing is common-mode and cancels), and the in-plane slots are confirmed
    spaced 2π/S in the mean. Builds on the committed real Celestrak gps-ops 2021-07-28 snapshot
    (scenarios/orbit-sgp4-gps.toml, exercised by the scenario-coverage and SP3 round-trip tests).
  • Advanced time-and-frequency transfer: TWSTFT, GNSS common-view, PPP, optical, IEEE-1139
    power-law fit, and a clock ensemble.
    New src/timetransfer_adv.rs builds the operational
    transfer methods on the shipped Sagnac/common-view closed forms and the Allan-stability tools.
    twstft_sagnac gives the Two-Way Satellite Time and Frequency Transfer Sagnac correction as the
    three-hop loop sum, equal to the BIPM closed form Δt = 2·A·ω_E/c² exactly (cross-checked by the
    independent twstft_sagnac_bipm); run_twstft emits a one-day T_A − T_B series and its TDEV.
    gnss_common_view_series single-differences two synthetic ground stations so the satellite clock
    cancels. iono_free_combination + ppp_receiver_clock are the PPP ionosphere-free combination and
    receiver-clock solve against an SP3-grade (synthetic) truth, cancelling the first-order ionosphere
    exactly. rytov_variance, fried_parameter, and the unit-mean lognormal_fading model a free-space
    optical link's turbulence-induced scintillation. fit_power_law_psd is a full IEEE-1139 five-coefficient
    h_α least-squares fit of the Allan-variance curve (all five canonical noise processes at once) with the
    dominant process reported per τ-decade. ensemble_timescale forms an inverse-variance-weighted paper
    timescale whose Allan deviation falls strictly below the best contributing clock. 31 unit tests;
    validation targets are closed forms and synthetic truth — a real BIPM Circular-T / IGS SP3 ingest remains.
  • IONEX ionosphere maps: file parser, time interpolation, and slant obliquity mapping. src/ionex.rs
    gains parse_ionex, which reads the IONEX file format (header grid definition + START/END OF TEC MAP
    blocks) into a sequence of IonexMaps — normalising the file's north-to-south latitude ordering into a
    positive-step TecGrid and scaling values by 10^EXPONENT. interpolate_tec_in_time blends two
    successive maps to a query epoch, and obliquity_factor / slant_tec map the vertical TEC onto a slant
    ray via the single-layer thin-shell factor M(z) = 1/cos z′ (sin z′ = (Rₑ/(Rₑ+H))·sin z). Together
    with the shipped grid model these turn a measured IGS global ionosphere map into a usable slant delay.
  • Constellation design: streets-of-coverage sizing + multi-constellation comparison. src/walker.rs
    gains min_satellites_streets_of_coverage, an idealised streets-of-coverage minimum-satellite solver —
    from the shipped coverage half-angle λ and street half-width c it sizes the near-polar constellation
    for continuous single global coverage as p = ⌈π/(2c)⌉ planes (e.g. a GPS-altitude 4-satellite plane
    needs 2 planes, 8 satellites), and reports None when the satellites are too sparse to form a continuous
    street. compare_constellations is the multi-constellation comparison tool: it scores each named Walker
    design on the same station/window via pdop_sweep and returns their coverage / PDOP / size side by side.
    Honest scope: the seam-exact Rider correction at the counter-rotating plane boundary and a 3-D coverage
    globe are follow-ons.
  • Multi-layer spoof detection: RAIM-consistency parity detector + layer fusion. src/spoof_monitors.rs
    gains the third and final detection layer and the fusion stage: parity_raim_test least-squares-fits
    the position/clock solution to a redundant pseudorange set and tests the leftover weighted residual
    sum-of-squares against its χ²(m−4) threshold — flagging a biased subset of satellites while
    correctly leaving a common-mode bias (absorbed by the receiver clock) RAIM-invisible, not papered
    over. fuse_spoof_layers combines the parity, AGC and SQM layers into one weighted decision that
    records which layers fired. A Monte-Carlo characterises the detector: empirical P_fa ≈ 0.068
    against a 0.05 design point, with missed-detection falling from 0.885 at a 2σ spoof bias to 0.16 at
    . Honest scope: cross-validation against specific published (Spirent / ION GNSS+) spoofing test
    vectors needs those external datasets and remains a follow-on.
  • Coupled-vs-decoupled Kalman validation ensemble. A 100-trial Monte-Carlo in
    src/fusion/coupled.rs quantifies the value of carrying the position↔clock cross-covariance: a
    faithful inline decoupled baseline (validated bit-for-bit against the shipped CoupledPntFilter)
    processes the same data with the cross blocks zeroed, and after near-degenerate pseudoranges plus a
    clock-only fix the coupled filter recovers position to 2.97 m RMS versus the decoupled filter's
    48.8 m, winning 97 of 100 trials
    — the clock fix sharpens position only through the correlation
    the decoupled pack discards. This completes the Kalman-correctness validation suite (Joseph form,
    PSD safety, NEES/NIS consistency, and now the coupled-filter ensemble).
  • Orbit determination pipeline (batch + sequential). A new src/orbit_determination.rs recovers
    a satellite's orbital state [r, v] from ground-station range tracking, composing three shipped
    pieces: the two-body + J2 force model (src/forces.rs) and RK4 integrator (src/integrator.rs)
    propagate a candidate state across the arc, a range measurement model predicts each station range,
    and the Gauss–Newton batch corrector (src/batch_ls.rs) drives the candidate onto the best-fit
    state (determine_orbit_batch). The same dynamics and range model also drive a sequential
    recursive determination on the shipped unscented filter (determine_orbit_sequential). Four tests
    validate it: range prediction across the arc; batch recovery to sub-metre / mm·s⁻¹ from noiseless
    ranges
    ; batch recovery to ~2 m with a post-fit residual at the 5 m noise floor (the signature
    of a consistent least-squares fit); and sequential recovery to within tens of metres. Honest scope:
    range-rate/Doppler and angle measurements, an analytic J2 state-transition matrix, and station
    visibility masking are follow-ons.
  • Tightly-coupled GNSS/INS UKF navigator. A new src/fusion/tightly_coupled.rs wires the
    shipped unscented Kalman core (src/fusion/ukf.rs) into a working tightly-coupled navigator over
    the eight-state [px,py,pz,vx,vy,vz,b,d] (ECEF position/velocity plus receiver clock bias and
    drift in range units). It ingests the raw satellite measurementspseudorange
    (ρ = |p − sᵢ| + b) and range_rate/Doppler (ρ̇ = (p − sᵢ)·(v − ṡᵢ)/|p − sᵢ| + d) — rather
    than a pre-formed position fix, so TightlyCoupled (with propagate/propagate_orbital/
    update_gnss) keeps correcting with fewer than four satellites and coasts through GNSS
    outages on its propagated dynamics. Five tests validate it end-to-end, including the milestone
    acceptance scenarios: the pseudorange/Doppler geometry against hand values; noiseless convergence
    to sub-metre on five satellites; a three-satellite case converging from ~212 m to ~13 m
    where a snapshot PVT cannot even be formed; a constant-velocity 120-second outage within 50 m;
    and — the headline acceptance — a 30-minute curving LEO pass (real two-body + J2 orbit) with a
    120-second GNSS outage, held to 0.77 m pass RMS and 2.9 m worst-case through the
    outage
    . That orbital coast composes the shipped gravity force model (src/forces.rs) and RK4
    integrator (src/integrator.rs) into the UKF process model (propagate_orbital), so the filter
    follows the orbit's curvature — which a constant-velocity coast cannot (curvature alone is ~58 km
    over 120 s at LEO). Honest scope: the orbital coast uses the two-body + J2 force model rather than
    raw IMU specific-force (for an unpowered orbital platform these coincide); folding in a
    strapdown-IMU error state and in-loop iono/tropo corrections remain follow-ons.
  • Map-matching measurement model (terrain-/gravity-referenced navigation). A new
    src/mapmatch.rs supplies the measurement model that turns the shipped
    sequential-importance-resampling particle filter (src/particle_filter.rs) into a working
    GPS-denied navigator: field_likelihood (a Gaussian field-match likelihood) and
    map_match_likelihood, which samples any georeferenced reference field — terrain elevation
    (TRN) or a gravity anomaly — at a particle's position and weights it by agreement with the
    vehicle's measured value. The field is any Fn(lat, lon) -> value sampler, so it composes
    with the bilinear grid in src/ionex.rs or a closure. Two tests anchor it — the likelihood
    peaks (=1) at a perfect match and falls to e^(−½) at one sigma, and a particle filter over
    a distinctive synthetic-terrain patch recovers the true position to within 0.1. Honest scope:
    the real reference maps (SRTM elevation, EGM/EIGEN gravity anomaly) and their loaders are
    follow-ons.
  • Cislunar PNT integrity (lunar ARAIM). A new src/lunar.rs applies the Earth-side
    MHSS ARAIM engine to a LunaNet-style lunar navigation service with the lunar parameters
    (σ_URE ≈ 30 m vs GPS 0.6 m, P_sat ≈ 1e-4): lunar constants, a selenocentric
    East/North/Up basis and sky-geometry helper, and lunar_araim (HPL/VPL). Three tests
    anchor it — the orthonormal selenocentric basis, the slant-range geometry, and the exact
    linear protection-level scaling with σ_URE (lunar 30 m gives a 50× larger protection
    level than the same geometry at the GPS 0.6 m — the quantitative reason lunar PNT
    integrity is hard). Honest scope: the precise LANS NRHO ephemeris, the signal-in-space
    error budget, and the MCI↔MCMF frame reduction are follow-ons.
  • Two-part (high-precision) Julian dates. A new src/jd2.rs adds Jd2, a Julian date
    split into an integer day and a fractional frac in [0,1) (the SOFA/hifitime
    convention), with new/from_parts/add_seconds/diff_seconds/total. Differences of
    nearby epochs stay exact to the f64 floor where a single-f64 JD loses ~50 µs near
    J2000. Four tests anchor it: the round-trip, fraction normalisation, exact microsecond
    recovery (with the single-f64 failure demonstrated alongside), and additive/reversible
    second arithmetic.
  • CCSDS OMM (Orbit Mean-Elements Message) writer. A new src/omm.rs complements the
    oem ephemeris writer with the mean-elements message: OmmFile::from_tle maps SGP4/TLE
    mean elements into the OMM units (mean motion in rev/day, angles in degrees, plus
    BSTAR), and to_omm_kvn serialises the standards-track CCSDS 502.0-B-2 KVN form — so a
    Kshana orbit can be consumed by any OMM-aware tool instead of as a bespoke TLE. Two tests
    anchor the TLE→OMM unit conversion (≈ 15.5 rev/day, 51.6° inclination, etc.) and the
    presence of the required KVN keywords. Honest scope: the KVN form and TLE mapping ship
    here; the XML (ndm/omm) rendering and a reference-parser round-trip are follow-ons.
  • Sequential-importance-resampling particle filter. A new src/particle_filter.rs
    adds the nonlinear, non-Gaussian estimator behind map-aided, GPS-denied navigation
    (terrain-referenced or gravity-map matching): predict (propagate particles through the
    dynamics + Gaussian process noise), update (reweight by a per-particle measurement
    likelihood), systematic resample, the effective_sample_size degeneracy monitor, and
    the weighted-mean estimate. Six tests anchor the deterministic core exactly — ESS spanning
    1…N, systematic resampling picking indices in proportion to weight, the weighted-mean
    convex combination, a Gaussian likelihood pulling the estimate onto the measurement,
    resample-to-uniform behaviour, and seeded predict determinism. Honest scope: the engine
    ships here; the reference maps (SRTM elevation, EGM gravity anomaly) and the map
    measurement model are follow-ons (the ionex grid+bilinear sampler would serve a
    gravity/terrain map equally).
  • IONEX-style TEC ionosphere maps. A new src/ionex.rs adds the measured-ionosphere
    alternative to the broadcast Klobuchar model: a TecGrid (a regular lat/lon grid of
    vertical TEC, an IGS global ionosphere map) with bilinear interpolation at a pierce point
    (vtec_at, clamped outside the grid) and the first-order delay Δ = 40.3·TEC/f²
    (vtec_to_delay_m, delay_at). Four tests anchor it: 1 TECU ≈ 0.162 m at L1 with the
    1/f² scaling, node-exact interpolation, bilinear midpoints averaging the corners, and
    edge-clamped out-of-grid queries. Honest scope: the grid and interpolation ship here;
    parsing the IONEX file format, time interpolation between maps, and the slant mapping
    function are follow-ons.
  • Geometric time-transfer corrections (Sagnac + GNSS common-view). A new
    src/timegeo.rs adds the two deterministic effects a real clock comparison must account
    for, complementing the stochastic two-way model in timetransfer: sagnac_correction
    (Δt = (ω_E/c²)·(x₁y₂ − x₂y₁), the rotating-Earth delay — tens of ns for continental
    baselines) and common_view_offset, the GNSS common-view single difference that cancels
    the satellite-clock error exactly and recovers the inter-station offset. Three tests
    anchor them on exact references: the ≈ 33 ns Sagnac of an equatorial quarter-turn,
    antisymmetry and the zero radial/polar cases, and the exact satellite-clock cancellation.
    Honest scope: a full TWSTFT transponder/hardware-delay budget and a PPP ionosphere-free
    time-transfer solution are follow-ons.
  • Orbital force model (two-body + J2). A new src/forces.rs adds the acceleration
    model a numerical propagator integrates: two_body_accel (−μ·r/|r|³), the j2_accel
    oblateness perturbation (the ECI closed form), and gravity_accel summing them — pair
    it with src/integrator.rs as f(t,[r;v]) = [v; a(r)]. It also exposes the analytic J2
    secular rates (j2_secular_rates): the nodal regression Ω̇, apsidal rotation ω̇,
    and mean-anomaly drift . Six tests anchor the physics on exact references: μ/r² for
    the two-body term, the J2 closed form at the equator (~10⁻³ of the two-body magnitude),
    the critical inclination (63.4349°) that freezes the perigee (ω̇ = 0), the ISS
    nodal regression (Ω̇ ≈ −5°/day), and the eastward drift of a retrograde sun-synchronous
    orbit. Honest scope: two-body + J2 only; J3–J6, drag, SRP, and third-body are follow-ons.
  • Shareable scenario permalinks. A new src/permalink.rs adds a dependency-free
    RFC 4648 Base64 codec (standard +/ alphabet with padding, and a URL-safe -_
    unpadded alphabet) and encode_scenario / decode_scenario wrappers, so a playground
    TOML can be encoded into a ?s= query parameter and shared as a URL. Exposed to the
    browser as encode_permalink / decode_permalink wasm bindings. Four tests anchor it
    on the canonical RFC 4648 vectors ("foobar""Zm9vYmFy", etc.), a URL-safe scenario
    round trip (no +///= to escape), invalid-symbol rejection, and an all-256-byte
    round trip. Honest scope: the codec and bindings ship here; the playground Share-button
    UI, the Plotly/D3 multi-series chart, and the A/B comparison mode are follow-ons.
  • Gauss–Newton batch least squares (the batch differential corrector). A new
    src/batch_ls.rs adds the estimation core a batch orbit determination (or any
    parameter fit) rests on: gauss_newton linearises a user-supplied model h(x) with a
    central finite-difference Jacobian, forms and solves the weighted normal equations
    (HᵀWH)·Δx = HᵀW·(z − h(x)) (reusing the tested matrix inverse), and iterates to
    convergence with per-measurement weights. Four tests anchor it: a linear line fit reaching
    the exact weighted-least-squares solution, a nonlinear a·exp(b·t) fit recovering the true
    parameters, a 3-D range-multilateration that recovers a known position from noise-free
    ranges (the orbit-determination flavour), and rejection of under-determined/mismatched
    inputs. Honest scope: this is the generic corrector engine; the orbit-specific
    range/range-rate/azimuth-elevation measurement model, the analytic J2 state-transition
    matrix, and the published-case validation are follow-ons.
  • RF-layer spoofing monitors (AGC power and SQM). A new src/spoof_monitors.rs adds
    two independent receiver-front-end spoof detectors that complement the clock-aided
    time-spoof monitor in spoof: an AGC power monitor (combine_power_dbm incoherent
    power sum + AgcMonitor) that flags the excess received power a spoof transmitter adds
    beyond a configurable dB margin, and a signal-quality monitor (bpsk_autocorr
    triangular code autocorrelation + SqmMonitor) that flags the Early-minus-Late
    correlator imbalance multipath/meaconing/replay introduces. Four tests anchor the exact
    closed forms (3.01 dB for a doubling of power, the 10·log10(N) aggregate, the
    triangular R(τ)=1−|τ|, and the 10 % Early/Late alert threshold). Honest scope: the
    full RAIM-consistency parity spoof detector, the multi-layer fusion of the monitor
    outputs, and validation against published Spirent/ION GNSS+ spoofing vectors are
    follow-ons.
  • Adaptive numerical ODE integrator. A new src/integrator.rs adds the first piece
    of a numerical propagator (Kshana's orbit propagation is otherwise analytic SGP4/SDP4):
    a generic fourth-order Runge–Kutta step (rk4_step) over any first-order system
    y' = f(t, y), and an adaptive driver (integrate) that controls local error by
    step doubling (Richardson extrapolation) with the standard 0.9·(tol/err)^(1/5)
    step controller and accept/reject logic. Six tests anchor it on exact solutions: the
    y' = y → e exponential to < 1e-9, the ~16× error reduction per halved step that
    proves fourth-order convergence, energy/return conservation of the harmonic oscillator
    over a full period, and the adaptive driver meeting a tight tolerance with variable
    steps. Honest scope: this is the integrator core and its error control; the
    Dormand–Prince RK5(4)/RKF7(8) embedded tableaux and the hierarchical orbit force model
    (two-body + J2–J6 + drag + SRP + third-body) that make it a NumericalPropagator are
    follow-ons.
  • Unscented (sigma-point) Kalman filter. A new src/fusion/ukf.rs adds the
    scaled unscented Kalman filter (Julier & Uhlmann; Wan & van der Merwe) as a general
    n-state estimator over user-supplied process and measurement functions — the
    sigma-point estimator a tightly-coupled GNSS/INS navigator uses when the
    pseudorange/Doppler model is strongly nonlinear and an EKF's Jacobian degrades. It
    includes the supporting dense linear algebra (Cholesky factor for the sigma-point
    spread, Gauss–Jordan inverse for the innovation covariance) and a Joseph-free
    P⁺ = P⁻ − K S Kᵀ update. Six tests pin it down, the key ones exploiting the exact
    property that for a linear model the unscented transform reproduces the Kalman
    filter to numerical precision (predict, update, and a full predict+update cycle all
    matched against a hand-run linear KF, plus a 1-D analytic Bayesian-posterior check
    and the Cholesky/inverse identities). Honest scope: this is the estimator engine; the
    17-state tightly-coupled GNSS/INS navigator, pseudorange/Doppler measurement model,
    and outage-validation scenario remain follow-ons.
  • Dual-constellation ARAIM protection levels. A new araim_dual_raim extends the
    single-fault Advanced RAIM (araim_raim) with the constellation-wide fault mode of
    EU ARAIM / DO-316: alongside the fault-free and per-satellite hypotheses, each
    constellation (labelled per satellite) contributes one hypothesis that removes all of its
    satellites at once, with prior P_const (a new DualFaultPriors { p_sat, p_const }). Every
    hypothesis adds a term to the same MHSS integrity sum, so VPL/HPL are the smallest bounds
    whose total P_HMI meets the budget over fault-free + single-SV + per-constellation faults
    (the Bonferroni false-alert split is over all N + C hypotheses). With P_const = 0 the
    result is bit-for-bit araim_raim; a single-constellation user returns None against its
    own constellation fault (it cannot be excluded) — which is exactly why dual-constellation
    coverage matters. Four tests cover the equivalence, the protection-level widening, the
    single-constellation unavailability, and input validation, reusing the existing
    solution-separation sub-solution machinery.
  • IAU 2006 precession (Fukushima–Williams angles and bias-precession matrix). A new
    src/precession.rs implements the IAU 2006 (P03; Capitaine, Wallace & Chapront 2003)
    precession: the four Fukushima–Williams angles (γ̄, φ̄, ψ̄, ε̄_A) as polynomials in TT
    Julian centuries (fw_angles), and the GCRS→mean-of-date bias-precession rotation matrix
    built from them via the SOFA iauFw2m construction (precession_matrix, with
    gcrs_to_mod / mod_to_gcrs helpers). This is the first inertial-frame piece on top of
    the existing GMST-based frames reduction. Eight tests validate against closed-form
    anchors — the J2000 mean obliquity ε̄ = 84381.406″ = 23.4392794°, the published angle
    constant terms, the ψ̄ ≈ 5039.998″ general-precession accumulation over a century,
    matrix orthonormality and det = +1, the near-identity (frame-bias-only) value at J2000,
    and the ≈ 1.40°/century net rotation angle. Honest scope (ROADMAP.md): precession
    only — the IAU 2000A 678-term nutation, the full TEME→GCRS chain, and a SOFA/ANISE µas/<10 m
    numerical cross-check are follow-ons.
  • First-principles cold-atom-interferometer (CAI) accelerometer physics.
    src/inertial/quantum_imu.rs models a three-pulse Mach–Zehnder atom interferometer
    from first principles instead of a datasheet: effective wavevector k_eff = 4π/λ,
    interferometer phase Φ = k_eff·a·T², quantum projection (shot) noise σ_Φ = 1/(C·√N),
    per-shot acceleration sensitivity, contrast decay C(t) = C₀·e^(−t/τ), and — the
    point — CaiAccelerometer::q_va(), which derives the white-acceleration PSD the
    classical AccelModel already consumes from the atom number, interrogation time, and
    contrast. The model now also covers vibration coupling — the dominant real-device
    term: the interferometer acceleration→phase transfer function |H(ω)| = (4/ω²)sin²(ωT/2) (accel_transfer_function), the white-PSD phase variance
    σ_Φ² = k_eff²·S_a·T³/3 (vibration_phase_variance_white, with a numeric band-integral
    cross-check vibration_phase_variance_band), the rank-1 along-beam beam_axis_projection,
    and CaiAccelerometer::vibration_phase_noise / vibration_limited_accel (the latter
    reducing to the k_eff-independent √(S_a/(3T)) floor). Eleven tests hand-verify the
    physics (Rb-87 k_eff ≈ 1.61×10⁷, Φ(1 g) ≈ 1.58×10⁴ rad, σ_a ≈ 0.13 µg/shot shot-noise
    floor vs ≈ 5.9 µg vibration floor, the 1/T², 1/√N, and scaling laws). Honest
    scope in docs/QUANTUM.md: this spans the projection-noise floor and the vibration-limited
    regime above it; laser-phase noise, Coriolis and light-shift systematics, and the
    PHARAO/CARIOQA validation scenarios remain follow-ons.
  • Quantum-CAI accelerometer wired into the inertial scenario. An accelerometer in an
    inertial dead-reckoning scenario now resolves to a new ImuKindClassical (the
    existing datasheet-coefficient sensor) or QuantumCai when it carries an optional [cai]
    block (CaiCfg: wavelength, pulse separation, atom number, contrast, cycle time, and an
    optional platform vibration_psd). A quantum_cai sensor's velocity-random-walk PSD
    q_va is derived from the interferometer physics — the shot-noise floor plus, when a
    vibration PSD is given, the vibration-limited contribution in quadrature — instead of a
    supplied coefficient, and the run's provenance records that the noise is physics-derived.
    The cai field is skip_serializing_if = "Option::is_none", so existing scenarios omit it
    and serialize byte-identically (the scenario hash is unchanged). Five tests cover the
    derivation, the quadrature vibration sum, the Classical/QuantumCai selection, hash-stable
    serialization, and an end-to-end CAI-driven run.
  • Constellation-design optimiser and streets-of-coverage geometry. src/walker.rs
    gains optimize_walker_design, a gradient-free grid optimiser that searches the
    {planes × sats × inclination} design space and returns the best Walker design under
    a chosen DesignObjectiveMinSatellitesForCoverage, MaxCoverage, or
    MinWorstPdop — over the already-validated PDOP sweep (a test confirms it returns the
    brute-force winner). Plus the analytical streets-of-coverage closed forms
    coverage_half_angle_rad (λ = arccos(Re/r·cos ε) − ε) and street_half_width_rad
    (cos c = cos λ / cos(π/s), Rider/Beste), hand-verified against textbook geometry and
    detecting the under-population gap. The full Rider minimum-satellite global-coverage
    solver, a 3-D playground globe, and an external-tool DOP cross-check remain follow-ons.
  • SP3 precise-ephemeris export from the CLI. A propagated orbit/constellation
    scenario can now be written to an SP3-c file: kshana <orbit.toml> --export-sp3 out.sp3, or export_sp3 = true in the scenario auto-writes <scenario>.sp3
    (api::export_sp3 / auto_export_sp3, OrbitClockScenario::to_sp3_string, optional
    epoch). A round-trip test (tests/sp3_export_roundtrip.rs) propagates the real
    Celestrak gps-ops snapshot, exports it, re-parses it, and confirms the recovered
    ECEF positions match the SGP4 truth over 24 h to < 0.5 m (well inside the 10 m
    TLE-grade tolerance). README documents the interoperability role (RINEX → RTKLIB/gLAB,
    SP3 → Ginan/precise-orbit products).
  • Coupled clock+position Kalman filter (cross-block covariance). src/fusion/coupled.rs
    CoupledPntFilter is a single stacked [pos, vel, phase, freq] filter (Joseph-form
    updates) whose pseudorange measurement ρ = g·pos + c·phase + noise genuinely
    couples the position and clock blocks — unlike the legacy fusion pack's two
    independent two-state filters, which keep the cross-block covariance exactly zero.
    Validated: a shared pseudorange drives P[pos,phase] non-zero; two distinct
    geometries jointly resolve injected position+clock offsets a single range cannot
    separate; a clock-only fix sharpens the position through the cross-covariance
    (the payoff decoupled filters cannot provide); and the Monte-Carlo NEES is
    χ²(4)-consistent. This is the 1-DOF realization (the fusion pack's
    dimensionality); the 3-D 8-state extension and wiring into the runnable pack are
    tracked as follow-ons.
  • Kalman filter-consistency health monitoring (NIS/NEES). The two-state clock
    filter's covariance update is now in Joseph stabilised form `P⁺ = (I−KH)P(I−KH)ᵀ
    • KRKᵀ, which stays positive-semidefinite under extreme Q/R ratios (Cholesky-checked in CI at R=1e-26 / Q≈1e-30). A new src/filter_health.rsruns a Monte-Carlo consistency assessment (Bar-Shalom §5.4): pooled **NIS** (normalised innovation², target 1) and **NEES** (normalised estimation error², target 2) against 95% χ² bands, surfaced as afilter_health { nis_mean, nis_chi2_lower_95, nis_chi2_upper_95,
      nees_mean, nees_chi2_lower_95, nees_chi2_upper_95, consistent } block in the clock result JSON and as a green/amber card in the playground. A Q/R-mismatch sweep test proves the monitor flips to inconsistent when the process noise is mistuned by ×0.1–×10. Adds a general χ² quantile (detection::chi2_inv_cdf`, Wilson–Hilferty,
      table-checked).
  • docs/PROVENANCE.md — one citable provenance table. Consolidates every sensor
    parameter (clocks, inertial, time-transfer), physical/algorithmic model (orbit, time
    systems, frames, iono/tropo, integrity, detection, jamming, Allan), and validation
    dataset (AIAA 2006-6753, Celestrak gps-ops) with its published source — datasheet,
    paper, ICD, or standard — and an honest maturity label (flight-qualified /
    ground-lab / space-goal-on-ground-hardware). Linked from the README intro and
    Documentation table; complements the per-run provenance strings that already travel
    in the result JSON.
  • Typed scenario API. Dispatch is now on a typed ScenarioKind enum instead
    of a raw kind string match (ScenarioKind::classify + exhaustive dispatch), so
    adding a pack is compile-checked. New typed surfaces alongside the unchanged
    string-returning run_toml: run_scenario(src) -> Result<RunOutput, KshanaError>
    with a structured error taxonomy (InvalidInput / NonConvergence /
    Unsupported / IoError, each with a stable kind_tag()); a Scenario trait
    and ExternalPack extension point (the jamming pack is wired through it as the
    worked example); and list_scenario_kinds() introspection (name, description,
    required/optional fields per kind). The Python and WebAssembly bindings gain
    list_kinds() and error_kind(). Documented in docs/ARCHITECTURE.md.
  • Real GPS constellation + operating-envelope coverage.
    scenarios/orbit-sgp4-gps.toml now ships a real Celestrak gps-ops snapshot
    (2021-07-28, 30 satellites) instead of synthetic Walker TLEs, with
    strict_checksum = true so it only loads when every TLE checksum is valid;
    scripts/fetch_tles.sh documents reproducible refresh and the README credits
    the open-data source. New tests/scenario_coverage.rs exercises each pack across
    ≥5 envelope variants asserting finite/bounded output, confirms the flicker-FM
    floor measurably degrades a clock's coast
    when enabled (now set in three shipped
    scenarios), and confirms the fusion filter converges with a realistic non-zero
    accelerometer bias
    (within 3× the zeroed-bias case), closing the "fusion only
    works with zeroed biases" realism gap. docs/VALIDATION.md gains an Operating
    Envelope table.
  • Measurement-domain GNSS simulation (gnss-sim kind). A pseudorange-level
    forward model: per visible satellite it synthesises ρ = geometric range + c·δt_rx − c·δt_sv + I + T + noise + multipath and the L1 Doppler, with the
    Klobuchar single-frequency ionosphere (IS-GPS-200 §20.3.3.5.2.5) and the
    Saastamoinen zenith troposphere projected by the Niell (1996) mapping
    function — exposed as [iono] and [tropo] TOML blocks. The residuals feed
    snapshot RAIM for per-epoch HPL/VPL, and a gnss_measurements[] JSON array
    carries each SV's pseudorange, Doppler, C/N₀, and iono/tropo corrections. A
    zero-noise run reproduces geometry + corrections to sub-millimetre (CI test).
    New src/gnss_sim.rs and scenarios/gnss-sim-raim.toml.
  • Stochastic time-spoof detector (spoof kind). The spoof pack now runs a real
    detector instead of a deterministic ramp-vs-bound comparison: four injection
    shapes (linear_ramp, step_jump, meaconing, replay), a two-sided χ²₁
    energy / Neyman–Pearson test on the clock-aided monitor statistic with the
    threshold set from a target false-alarm budget target_pfa, and the
    missed-detection probability P_md reported both closed-form and by Monte-Carlo
    (mc_runs trials per hypothesis — the two agree to a few ×1/√N). The Security
    figure of merit is now 1 − P_md at the operationally-harmful (spec) magnitude.
    New src/detection.rs (Gaussian tail functions, NP/energy test, Monte-Carlo
    P_fa/P_md) and scenarios/spoof-meaconing.toml. Backward compatible: a bare
    [attack] rate_ns_per_s is still accepted as a linear ramp.

Changed

  • Security FoM definition (spoof kind): from the analytic detectability
    bound 1 − min_detectable/threshold to the stochastic detector's 1 − P_md.
    The clock pack's security field remains the faster analytic proxy.

Added (continued)

  • RF jamming model (jamming kind). A link-budget interference model that
    turns a jammer's power and geometry into per-satellite loss of lock: the
    jammer-to-signal ratio from free-space path loss and the per-direction
    receive-antenna gain, the effective C/N₀ via the standard anti-jam equation
    (despreading processing gain × the spectral-separation factor Q; Kaplan &
    Hegarty §9.4), and a configurable tracking threshold, scored over a Walker
    constellation as an availability_under_jamming figure of merit. New
    src/jamming.rs and scenarios/jamming-demo.toml. Honest scope (no multipath,
    terrain shadowing, AGC, or adaptive nulling) is documented in
    docs/CAPABILITY.md / docs/VALIDATION.md.
  • Generic N-D parameter sweep over any scenario kind (sweep-nd). The
    previous N-D sweep was clock-pack only. sweep-nd varies dotted TOML keys of a
    [base] scenario over the Cartesian product of its axes, re-dispatches each
    grid node through the normal run path, and reads one or more metrics out of the
    result by dotted JSON path — so it works for every pack (inertial, gnss-ins,
    integrity, spoof, …) without coupling to each pack's Rust type. Grid nodes are
    evaluated in parallel across OS threads on native targets (no added
    dependency); wasm falls back to sequential. Deterministic and row-major
    regardless of thread count. New scenarios/sweep-nd-inertial.toml example.
  • TOML-configurable deterministic IMU error model in the gnss-ins pack. The
    three-axis strapdown error chain (scale-factor, misalignment, g-sensitivity,
    quantization, rate-ramp; IEEE Std 952-1997 §A.2, Groves 2013 §4.3) is now
    reachable per sensor from a scenario file via an optional [imu_*.error_model]
    block, layered on top of the constant turn-on biases. Omitting the block leaves
    each sensor a pure constant-bias source, so existing gnss-ins runs are
    unchanged. This wires the previously library-only error model into a runnable
    pack and figure of merit.

Full changelog: CHANGELOG.md · Docs: README