v0.13.0
This release closes the largest correctness gap in the engine: Earth-orientation
and reference-frame reduction are now done to reference-implementation grade
(validated bit-for-bit against ERFA/SOFA), the integrity stack gains
dual-constellation ARAIM on real GPS+Galileo TLEs, and the propagation,
quantum-sensor, lunar/cislunar, and geomagnetic layers all deepen — alongside a
typed Python API, a richer browser playground, and a three-OS reproducibility
matrix. Highlights:
- Reference frames, bit-for-bit. Full IAU 2000A and 2000B nutation, IAU 2006
precession, the CIO-based (X, Y, s) IAU 2006/2000A GCRS↔ITRS reduction, IERS
polar motion, and TEME→GCRS/ITRS output frames — each validated bit-for-bit
against ERFA/SOFA reference routines. - Dual-constellation ARAIM (GPS + Galileo) on real TLEs, with HPL/VPL,
Stanford-diagram output, and an opendocs/ARAIM_REFERENCE.md. - Cislunar PNT: an Earth–Moon CR3BP propagator, MCI↔MCMF frames, selenographic
coordinates, and a runnable LunaNet lunar-integrity scenario. - Quantum sensing: Coriolis and AC-Stark systematics for the cold-atom
interferometer, a drift sweep, and validation against the Freier (2016) budget. - IGRF-14 geomagnetic main-field model, self-contained and validated.
- Typed Python API (PyO3
RunOutput/ScenarioMeta,.data(),scenario_kinds,
validate_toml, type stubs) with a CI wheel build, plus first-class GCRS/ITRS
propagator output and CCSDS OMM export. - Credibility & reproducibility: a head-to-head SGP4 accuracy comparison against
the independentsgp4crate, a CI coverage gate (~97% line onsrc/), and a
three-OS (ubuntu/macos/windows) reproducibility matrix.
Changed
- Every playground chart now matches the site theme. All twelve SVG chart
generators — the result/holdover chart (src/report.rs+src/chart.rs), the
Allan-deviation chart (web/app.js), and every scenario chart (src/hybrid.rs,
jamming.rs,timetransfer.rs,spoof.rs,raim.rsStanford + availability,
lunar.rs,ensemble.rs,sweep.rs,gnss_sim.rs,fusion/pack.rs,
inertial/mod.rs) — used cool navy panels (#0e131b), cool-gray axes/text, and
a clashing red/blue/purple series palette. They now use the warm graphite palette
throughout:--bgpanels, warm--linegrid,--fglabels, with a consistent
series assignment — quantum = honey-gold (--accent-bright), classical = warm
amber (--partial), spec/limit =--crit. Safety-coded views keep their
meaning: the RAIM Stanford diagram stays green (available) / amber (misleading) /
red (hazardous) / muted steel (unavailable), and HPL/VPL read as gold/bronze. - Charts are now self-describing when saved/downloaded. Both charts bake their
title into the SVG (the Allan chart's title + "lower is better" subtitle were
previously only HTML around the image, so a saved image had no caption), and both
carry a provenance footer —Kshana v<version> · <scenario-hash> · kshana.dev—
so a downloaded chart stands on its own and stays reproducible. - Every chart now carries the provenance footer. The footer —
Kshana v<version> · scenario <hash> · kshana.dev— is stamped centrally for all
scenario kinds inapi::run_toml(it was previously only on the holdover and Allan
charts), so any saved or downloaded image — from the playground, the CLI's
.chart.svgexport, or the HTML scorecard — identifies its version, scenario
fingerprint, and source. The hash is labelledscenariofor clarity and comes from
the result'sscenario_hashwhere present, with a source-hash fallback for the
integrity/lunar reports. What the fingerprint is and why it's there is documented in
the README "Output" section anddocs/PROVENANCE.md.
Fixed
raim::chi2_quantileand the RAIM Stanford-noise sampler are now panic-free on
out-of-range / non-finite inputs (aread_dir-order-dependent fuzz finding from
the new ARAIM scenarios).chi2_quantilenow guardsp/klikenormal_quantile
(returning a boundary value instead ofassert!-panicking), and the availability
Stanford-noiseNormalclamps to a strictly-positive σ — so the integrity/ARAIM
stack never panics on mutated or mis-configured scenarios.
Added
- Cross-platform reproducibility CI matrix. A new
reproducibility-matrix
job runs the reproducibility tests on ubuntu-latest, macos-latest, and
windows-latest on every push. Because full result JSON is not byte-identical
across OSes (last-ULP libm divergence), it asserts the platform-invariant
projection exactly — the input fingerprint plus output shape, pinned per
scenario as SHA-256 goldens intests/golden/by the new
tests/cross_platform_golden.rs— alongside the numeric pins (golden.rs, to
1e-6), the SGP4 states (sgp4_verification.rs, to 2e-5 km), and same-process
determinism (determinism.rs). Together these prove cross-platform
reproducibility on three OSes without the brittleness of exact full-output byte
hashing.docs/REPRODUCIBILITY.mddocuments the split. - Code-coverage gate in CI. A new
coveragejob runscargo-tarpaulinwith
the LLVM source-based engine, publishes an lcov report as a build artifact, and
enforces a line-coverage floor onsrc/(generated data tables, the CLI
entrypoint, the tests, and web assets excluded). Measured line coverage is
~97% onsrc/(SGP4 and the clock modules ≥95%); the gate is set at 85% — above
the ≥80% target and clear of the measured value, so it catches regressions
without flaking. A coverage badge is published in the README. - SGP4/SDP4 head-to-head against the independent
sgp4crate. A new test
(tests/sgp4_crate_comparison.rs) cross-validates Kshana's propagator against
the most widely used Rust SGP4 library (neuromorphicsystems/sgp4, added as a
test-only dev-dependency) over the same 666 AIAA 2006-6753 vectors. With both
driven on the WGS72 gravity model the vectors use, the two independent
implementations agree to sub-micron on near-earth and resonant orbits and
4.12 mm worst-case across all regimes, both reproducing the reference
tcppver.outtable. The committed comparison table
(tests/fixtures/sgp4_comparison.md, regenerated viaKSHANA_REGEN_FIXTURES=1)
breaks the result out per regime (LEO/MEO, deep-space, ½-day and 1-day
resonance) and notes the four deliberately-pathological cases the crate rejects
at construction. The live assertions hold both within 2e-5 km of the reference
and agree to within 4e-5 km — a regression guard, not a one-off. This is
competitive pedigree: correctness against an independent codebase, not just a
static table. (The crate's defaultfrom_elementsuses WGS84 and so differs
from the WGS72 reference by ~km — surfaced honestly in the table prose.) - CCSDS OMM export is now reachable end-to-end. The OMM writer
(src/omm.rs) previously had no CLI/API path; anorbitscenario's mean
elements can now be published as a CCSDS 502.0-B-2 OMM catalogue — one OMM KVN
message per TLE-defined satellite — viakshana <orbit.toml> --export-omm out.omm, orexport_omm = truein the scenario auto-writes<scenario>.omm
(mirroring the existing--export-sp3). Each message carries the satellite's
real NORAD catalogue number, COSPAR international designator (YYYY-NNNP),
and epoch (CCSDS day-of-year form), parsed from the TLE line 1 by the new
tle::parse_tle_identity; the name line becomesOBJECT_NAME(elseOBJECT <id>).CREATION_DATEis the scenario epoch, not wall-clock, so the output is
reproducible. New API:api::export_omm/api::auto_export_omm,
OmmFile::from_tle_block,OrbitClockScenario::to_omm_string. A synthetic
Walker or RINEX scenario (no TLE mean elements) errors rather than emitting an
empty file. Validated against the bundled 30-satellitegps-opssnapshot
(tests/sp3_export_roundtrip.rs). - Interactive hover read-outs on the playground charts. Moving the cursor over
a chart snaps a crosshair to the nearest sample and shows a value tooltip
(web/hover.mjs, wired inweb/app.js). On the Allan chart it reads τ and each
clock's σ_y(τ); on the time-series scenario charts (clock holdover, dead-reckoning,
time-transfer, hybrid PNT, GNSS/INS) it reads the time and each suite's value in
the chart's own units (ns / m / ps / utilization), parsed from the result so the
read-out matches the curve. Specialised diagrams (RAIM, spoof, sweep) get no
overlay. The charts stay self-describing blob<img>s — the overlay is a
transparent crosshair + tooltip on top, so download/compare/export are untouched.
Coordinate math (nearest-sample, cursor→plot-fraction, polyline parsing) is
unit-tested (web/hover.test.mjs, run in CI). - A/B compare mode in the playground. Pin any run as a baseline A, run a
second scenario, and the two are shown side by side with a figure-of-merit
delta table (holdover, timing RMS/p95, availability) that colours the winner
per metric (web/compare.mjs, wired inweb/app.js; delta logic unit-tested
inweb/compare.test.mjs, run in CI). All values are inserted as text and all
charts via blob<img>, so nothing from a scenario string is ever injected as
markup. - Chart download buttons (SVG + PNG). Each playground chart now has a
theme-matched "Download SVG / PNG" toolbar (web/chartdl.mjs, wired in
web/app.js). SVG hands back the faithful, scalable original; PNG rasterises
that same self-describing image at 2x for slides and documents. Files are named
with their provenance —kshana-<chart>-v<version>-<scenario-hash>.<ext>— and
the filename/size logic is unit-tested (web/chartdl.test.mjs, run in CI). - IGRF-14 geomagnetic main-field model (
src/igrf.rs). The IAGA standard
spherical-harmonic field (degree/order 13, 2025.0 epoch + 2025–2030 secular
variation; coefficients machine-generated from the officialigrf14coeffs.txt
bytools/gen_igrf.pyintosrc/igrf_data.rs, bit-for-bit reproducible). A
Schmidt-normalised synthesis returns the field vector (north/east/down) and
derived elements (declination, inclination, horizontal/total intensity) at any
geodetic location/date, plus the geomagnetic pole and dipole strength — the
magnetic counterpart to the gravity-map matcher for alternative-PNT navigation.
Validated self-contained: the synthesis matches the exact closed-form tilted
dipole, the full degree-13 analytic field matches a finite-difference of the
scalar potential, the dipole axis reproduces the known geomagnetic pole
(~80.7°N, −72.7°E) and ~29.7 µT strength, and the global field is physical. - Typed Python bindings (
src/python.rs). Beyond the string-in/string-out
run/run_full, the module now exposes a typedRunOutputclass (.json,
.svg,.summary, and a.data()accessor that returns the result parsed into
a native Python dict — no JSON re-parsing, NumPy-wrappable), plusrun_typed,
scenario_kinds()(parsed list of metadata dicts), andvalidate_toml()(a
non-raising list of error messages). Ships a PEP 561 type stub (kshana.pyi+
py.typed) for mypy/pyright/editors and adocs/PYTHON_API.mdquickstart.
The existing functions are unchanged. - First-class output frames for propagators (
src/orbit.rsFrameenum +
position_in_frame+state_gcrs). AnyPropagator(Kepler, SGP4, RINEX,
GLONASS, SP3) can now emit its position in TEME, GCRS (≈ J2000), or ITRS
(Earth-fixed) — TEME native, GCRS via the validated TEME→GCRS reduction, ITRS by
chaining that into the IAU 2006/2000A CIOgcrs_to_itrsrotation — and its full
GCRS state (position + velocity) viastate_gcrs. The of-date inertial output is
no longer TEME-only. - CIO-based IAU 2006/2000A celestial-to-terrestrial reduction (
src/cio.rs).
The modern, equinox-free GCRS↔CIRS↔ITRS chain: CIP coordinatesX, Yread off
the IAU 2006/2000A bias-precession-nutation matrix (reusing the validated FW
precession + 2000A nutation with theeraNut06aP03 adjustment), the 66-term
CIO-locatorsseries (eraS06, machine-generated from the ERFA reference by
tools/gen_s06.pyintosrc/cio_s06_data.rs, bit-for-bit reproducible), the
GCRS→CIRS matrix (eraC2ixys), the Earth rotation angle (eraEra00), and the
full GCRS→ITRS rotation (eraC2tcio, composed with the existing IERS polar
motion). Validated bit-for-bit against the publishederaXys06a
(X=0.5791308482835292617e-3, Y=0.4020580099454020310e-4,
s=-0.1220032294164579896e-7 at JD_TT 2453736.5),eraC2ixys, anderaEra00
test vectors. The CIO chain and the legacy equinox/GMST-1982 TEME reduction are
shown to agree up to their documented ≈2·(equation of equinoxes) sidereal-time
convention difference. This is the rigorous reduction the equinox/GMST path
approximated. - Full IAU 2000A nutation series (
src/nutation.rsnutation_iau2000a,
nutation_matrix_2000a). The complete MHB2000 model — 678 luni-solar + 687
planetary terms — accurate to < 0.1 mas, alongside the existing 77-term 2000B
truncation. The tables are machine-generated from the IAU SOFA / ERFAnut00a
reference bytools/gen_nut00a.pyintosrc/nutation_iau2000a_data.rs(the
generator reproduces the committed file bit-for-bit), and the whole series —
both the IERS-2003 and MHB2000 fundamental-argument sets and the planetary
longitudes — is validated bit-for-bit against the publishederaNut00a
test vector (Δψ = −0.9630909107115518e-5, Δε = 0.4063239174001679e-4 at
JD_TT 2453736.5, to 1e-13 rad). The default TEME→GCRS reduction keeps the 2000B
series (~1 mas, below the chain's velocity-frame-rotation simplification);
nutation_matrix_2000aexposes the < 0.1 mas of-date matrix for callers that
need it. - Runnable lunar-integrity scenario (
kind = "lunar-integrity",scenarios/lunanet-araim.toml).
Wires the lunar south-pole protection-level pass (src/lunar.rsLunarScenario→
south_pole_hpl_pass) into the scenario runner with a JSONLunarReportand an SVG
HPL-vs-time chart, so the cislunar integrity case is reachable straight from the CLI.
It honestly surfaces the gap: with the 30 m LANS σ_URE the south-pole HPL (≈ 260–450 m)
exceeds a 50 m alert limit (0 % available) — lunar PNT integrity is not yet met. - Dual-constellation ARAIM availability on real GPS+Galileo TLEs, and scenario-runner
wiring (src/raim.rs,src/orbit.rs,scenarios/araim-gps-galileo.toml). Adds
araim_dual_constellation_availability(the advanced ARAIM engine —
single-satellite and constellation-wide faults — run over a time grid) and
visible_positions_labeled, and wires it into theIntegrityScenariorunner via an
araim_dualflag so a multi-GNSS ARAIM study is reachable straight from TOML. A
real-data test (tests/araim_dual_real_data.rs) on vendored 2026-06-07 Celestrak
GPS+Galileo snapshots shows pooling Galileo lifts ARAIM availability from 0.21 to
0.67 under a demanding 12 m VAL (10.5→21.8 satellites in view), while the
constellation-fault-robust mode is fundamentally limited with only two constellations
— the quantitative reason robust dual-constellation integrity drives toward a third
constellation or SBAS. Honest residual: the numerically exact EU ARAIM TN Table A-3
reproduction against a single version-locked epoch, and a Zenodo fixture record. - Circular restricted three-body problem (CR3BP) for the Earth–Moon system
(src/cr3bp.rs). A new cislunar-dynamics core the two-body/SGP4 propagators
cannot provide: rotating-frame equations of motion (cr3bp_accel), an RK4
propagator (propagate_cr3bp), the Jacobi-constant integral (jacobi_constant),
and the five Lagrange points (lagrange_points). Validated against closed-form /
published anchors: the Earth–Moon collinear points (L1 ≈ 0.83692, L2 ≈ 1.15568,
L3 ≈ −1.00506), the exact equilateral L4/L5 = (½−μ, ±√3/2, 0), all five confirmed
as field equilibria, Jacobi conserved to integrator precision under propagation,
and the out-of-plane restoring force that makes halo/NRHO orbits possible. This is
the foundation for representing a real NRHO. Honest residual: differential-corrected
periodic 9:2 NRHO initial conditions, the eccentric/ephemeris (DE) model, and the
de-normalised transform into the selenocentric frames ofsrc/lunar.rs. - IERS polar motion and the TEME→ITRF reduction (
src/frames.rs). Adds
polar_motion_matrix(SOFAiauPom00:W = Rx(−y_p)·Ry(−x_p)·Rz(s′)with the TIO
locators′),pef_to_itrf/itrf_to_pef, andteme_to_itrf— the GMST-based
TEME→PEF rotation followed by polar motion — completing an ITRF-precise Earth-fixed
position on top of the GMST-onlyteme_to_ecef(polar motion is a tens-of-metres
effect at orbital radius).x_p/y_pare observed IERS quantities the caller
supplies. Honest residual: a fully CIO-based (X, Y, s) chain and an ANISE/SPICE
<10 m numerical cross-check remain follow-ons. - Cold-atom-interferometer systematics, drift sweep, and a published-device
validation (src/inertial/quantum_imu.rs,docs/QUANTUM.md). Extends the
first-principles CAI accelerometer with the two leading deterministic systematics:
the Coriolis/rotation phaseΦ_cor = 2·k_eff·v_⊥·Ω·T²(coriolis_phase, with
the equivalent acceleration bias2·Ω×vviacoriolis_accel_bias) and the
AC-Stark light-shift phaseΦ_LS = (δ_LS,1 − δ_LS,3)/Ω_eff(ac_stark_phase,
which cancels by π/2–π–π/2 symmetry for a constant shift). Addscai_drift_sweep
(dead-reckoning position drift vs cycle time — the core of a quantum-vs-classical
comparison) and a validation test against the Freier et al. 2016 mobile gravimeter
(arXiv:1512.05660): the modelled quantum-projection-noise floor lies below, and
within ~2 orders of, the published 96 nm/s²/√Hz short-term noise.docs/QUANTUM.md
updated. Honest residual: wavefront/beam-pointing systematics, fringe-ambiguity
resolution, the exact CARIOQA-PMP / Boeing-AOSense flight-test reproduction (needs
published platform PSDs), and a JS playground preset. - Cislunar frame reduction and a lunar south-pole integrity pass (
src/lunar.rs).
Extends the lunar ARAIM engine with the MCI↔MCMF (Moon-centered inertial ↔
Moon-fixed) rotation (mci_to_mcmf/mcmf_to_mci, a simplified mean-rotation
model at the lunar sidereal rate), selenographic latitude/longitude/altitude
(mcmf_to_selenographic/selenographic_to_mcmf), andsouth_pole_hpl_pass—
a landed Artemis-region receiver against a representative LunaNet relay set over a
24 h pass, which honestly quantifies the integrity gap: with the nominal 30 m LANS
σ_URE the protection level is finite but exceeds a 50 m surface-ops alert limit.
Honest residual: the precise LANS NRHO ephemeris (a 3-body cislunar orbit), the
physical libration / precessing lunar pole (DE421/SPICE), and a LunaNet TOML
scenario remain follow-ons. - ARAIM integrity support message, Stanford-diagram SVG, and the open ARAIM
reference (src/raim.rs,docs/ARAIM_REFERENCE.md). Adds an explicit
IntegritySupportMessage(σ_URA / σ_URE / b_nom / P_sat / P_const, with the WG-C
GPS+Galileo reference values and.fault_priors()/.dual_fault_priors()
converters into the single-faultaraim_raimand constellation-wide
araim_dual_raimengines), a standalonestanford_svgrenderer of the Stanford
integrity diagram (the four zones, thePL = errorboundary, the alert-limit
guides, one colour-coded marker per epoch), anddocs/ARAIM_REFERENCE.md
documenting the algorithm, the ISM, the fault hypotheses, the protection-level
contract, and the dual-constellation benefit. Tests demonstrate the
geometry/redundancy gain (pooling a second constellation tightens the single-fault
HPL) and constellation-fault tolerance (the dual user survives losing a whole
constellation; a single-constellation user cannot). Honest residual: numerically
reproducing the EU ARAIM TN Table A-3 / the 15–25 % availability figure against a
version-locked real TLE snapshot, a Zenodo fixture record, and wiring
araim_dual_raiminto the scenario-file runner. - IAU 2000B nutation and the full TEME→GCRS/J2000 inertial reduction
(src/nutation.rs). Adds the second and third pieces of a true inertial frame
reduction on top of the shipped IAU 2006 precession: the 77-term luni-solar MHB2000
nutation series (nutation_iau2000b, the standard IAU 2000B truncation accurate to
~1 mas) with the Delaunay fundamental arguments and the SOFAiauNumatnutation
matrix, and the Vallado AIAA-2006-6980 chain TEME→TOD (equation of the equinoxes) →
TOD→MOD (nutation) → MOD→GCRS (bias-precession) exposed asteme_to_gcrs(r, v, jd_tt)
/gcrs_to_teme. The series, arguments and unit constants are transcribed from the
IAU SOFA / ERFAnut00breference and validated bit-for-bit against the published
eraNut00btest vector (Δψ, Δε to 1e-13 rad). Honest residual: the full IAU 2000A
678-term series (<0.1 mas), an ANISE/SPICE <10 m numerical cross-check, and polar
motion remain follow-ons (seeROADMAP.md).
Full changelog: CHANGELOG.md · Docs: README