Skip to content

Releases: ashfordeOU/kshana

v0.19.0

19 Jun 06:47

Choose a tag to compare

Changed

  • Relicensed from Apache-2.0 to the GNU AGPL-3.0-only, with a commercial licence
    available from Ashforde OÜ (dual-licensing).
    The open engine stays fully open and
    publicly verifiable; the AGPL's network-copyleft (§13) means a closed or hosted
    derivative must come back to open source — or take a commercial licence. This
    defends the validated core against fork-and-close while keeping the credibility of
    a public, runnable, auditable engine. See LICENSE (AGPL) and the new
    LICENSING.md (what each licence covers and when it applies).
    • LICENSE now contains the AGPL-3.0 text; SPDX headers across all sources updated
      to AGPL-3.0-only; NOTICE, README, GOVERNANCE, GLOSSARY, the website, and
      crate/package metadata (Cargo.toml, pyproject.toml, CITATION.cff, the MCP
      crate + image) updated accordingly.
    • Contributor terms (CONTRIBUTING.md) now license inbound
      under the AGPL and grant Ashforde OÜ the right to include contributions in the
      commercially-licensed edition, so the dual-licence keeps working.
    • Dependency policy unchanged but re-justified (deny.toml,
      GOVERNANCE): dependencies stay permissive (Apache/MIT/BSD/ISC). AGPL is allowed
      only for kshana's own crate — a copyleft dependency would taint the commercial
      edition and break dual-licensing.
    • Note for downstream: this is a copyleft relicence. Users who relied on
      Apache-2.0 permissive terms can continue using the last Apache-2.0 release
      (v0.18.0 and earlier, as published); v0.19.0 onward is AGPL-3.0 / commercial.

Security

  • Bumped pyo3 0.24 → 0.29 to clear RUSTSEC-2026-0176 / RUSTSEC-2026-0177
    (GHSA-36hh-v3qg-5jq4 / GHSA-chgr-c6px-7xpp) from external OSV/dependency scans.
    Both are function-level advisories whose affected functions
    (BoundList/TupleIterator::nth/nth_back, PyCFunction::new_closure) Kshana
    never calls, and pyo3 is an optional (python-feature) dependency — so the
    real exposure was nil — but the bump keeps a clean scan for downstream auditors.
    Migrated src/python.rs to the pyo3 0.29 API (Bound return type for
    scenario_kinds; explicit skip_from_py_object on the RunOutput pyclass).
    All 11 Python binding tests pass against the rebuilt extension.

Get this release

Download — attached below, prebuilt (no toolchain needed); each artifact carries
SLSA build-provenance (verify with gh attestation verify <file> --repo AshfordeOU/kshana):

  • kshana — the simulator CLI / engine (Linux x86-64)
  • kshana-mcp — the Model Context Protocol server (Linux x86-64)
  • kshana-sbom.cdx.json — CycloneDX SBOM
  • kshana-validation-summary.html — the per-release validation summary

On macOS or Windows, install from a registry below — the PyPI wheels, the npm/WASM
package, and the Docker image are all cross-platform.

Install from a package registry:

Channel Get it
crates.io cargo install kshana · cargo install kshana-mcp
PyPI pip install kshana
npm npm install kshana
ghcr.io docker run -i ghcr.io/ashfordeou/kshana-mcp:0.19.0
MCP registry io.github.ashfordeOU/kshana-mcp (auto-discovered by MCP clients)
JetBrains Marketplace search "Kshana" in your IDE → Plugins

No install: run it in your browser at kshana.dev · Cite: DOI 10.5281/zenodo.20528627


Full changelog: CHANGELOG.md · Docs: README

v0.18.0

17 Jun 09:39

Choose a tag to compare

Added

  • Eleven new runnable scenario kinds — two-tender demonstrators, a CCSDS interop
    bridge, and a first-order mission-analysis / environment suite (all MODELLED,
    additive; existing reproducibility goldens unchanged).
    Each is CLI / Python /
    WASM / MCP dispatchable, ships a scenarios/<kind>.toml, and carries an explicit
    MODELLED label in its result JSON and one-line summary (a per-kind test in
    tests/dominance_demonstrators.rs asserts the label is present and that the output
    never contains the string VALIDATED):

    • impairment-eval (src/impairment_eval.rs) — AI/ML RF-impairment detection
      evaluation testbed: a labelled synthetic corpus + a detector-agnostic
      ROC/AUC harness + an in/out-of-distribution optimism-gap report (operating
      characteristics only — never field/IQ data).
    • quantum-trade (src/quantum_trade.rs) — quantum-vs-classical PNT trade with
      measured-ADEV ingestion and a GNSS-denied resilience envelope.
    • space-weather (src/space_weather.rs) — solar/geomagnetic indices (Kp↔ap IAGA
      table, daily Ap, centred 81-day F10.7a), Jacchia-71 exospheric temperature, and
      an activity-driven thermospheric-density coupling over a static atmosphere (NOT an
      NRLMSISE absolute-density model).
    • oem-interop (src/oem.rs) — CCSDS 502.0 OEM import + round-trip bridge for
      GMAT / Orekit / STK ephemerides (the parse_oem reader, exact inverse of the writer).
    • launch-window (src/launch.rs) — two-body launch azimuth (sin Az = cos i/cos lat),
      plane-change Δv, site-rotation bonus, and daily-opportunity geometry.
    • reentry (src/reentry.rs) — Allen-Eggers ballistic re-entry corridor:
      peak deceleration, peak-g and peak-heating velocities, peak-g altitude.
    • eo-coverage (src/eo_payload.rs) — EO swath / GSD / access / revisit geometry
      (SMAD space-triangle).
    • space-packet (src/space_packet.rs) — CCSDS 133.0-B Space Packet primary-header
      encode/decode with bit-exact round-trip.
    • attitude-budget (src/attitude_budget.rs) — 3-DOF gravity-gradient torque +
      RSS pointing-error budget (scalar pre-hardware budget, not a control loop).
    • passes (src/passes.rs) — ground-station rise/set pass prediction (AOS/TCA/LOS,
      max elevation, access).
    • link-budget (src/linkbudget.rs) — one-way CCSDS-401 / DSN-810-005 link equation
      (C/N₀ = EIRP − FSPL − L_other + G/T − k, FSPL / Eb·N₀ / margin / closure).
  • 17-state hybrid quantum + classical tightly-coupled UKF — surfaced as a runnable
    scenario (MODELLED).
    A new hybrid-ukf scenario kind (src/fusion/hybrid_ukf.rs,
    scenarios/hybrid-ukf.toml) that turns the previously API-only 17-state tightly-coupled
    GNSS/INS unscented filter (src/fusion/tightly_coupled17.rs) into a scenario the
    CLI/Python/WASM/MCP bindings can dispatch. The 17-state error vector is the 15 INS error
    states
    (position, velocity, attitude misalignment, accel + gyro bias) augmented with the
    CAI-derived accelerometer-bias correction — the cold-atom interferometer
    (src/inertial/quantum_imu.rs) sets the velocity-random-walk floor q_va, so the long-term
    coast drift is the quantum-sensor-limited one — and a 2-state phase + frequency clock
    whose process noise comes from the q-parameter clock engine
    (clock_state::q_from_allan, mapping a clock's Allan-deviation profile to the q_wf/q_rw
    PSDs, scaled to range units). The platform is GNSS-aided for a lead-in (the filter learns
    the biases, velocity and clock), then coasts through a GNSS outage on the CAI IMU + clock
    alone, so the run demonstrates classical-IMU short-term + quantum long-term
    hybridisation. The figure of merit is filter self-consistency: pooled NEES
    (Normalised Estimation Error Squared, over the estimable position/velocity/clock subset) and
    innovation-whiteness NIS (Normalised Innovation Squared) over a Monte-Carlo ensemble,
    checked against their 95% χ² bands (Bar-Shalom §5.4). The matched filter lands inside the
    bands; a deliberately mistuned filter (the q_factor / r_factor knobs) is flagged — an
    objectively checkable, discriminating gate, not a rubber stamp. The NEES/innovation-
    whiteness check is the STATISTICAL ORACLE: a self-consistency statement (the filter's
    reported covariance honestly matches the spread of its own errors under the modelled noise),
    NOT a real-world accuracy guarantee.
    Honest scope: everything is modelled / simulation
    — the CAI and clock inputs are bracketed, literature-representative values, not measured
    hardware; the CAI hardware and its Key-Person stay partner-owned; nothing here implies
    TRL > 3, flight heritage, or external validation (the result JSON and one-line summary carry
    these labels explicitly). The single constant-velocity, level trajectory leaves attitude and
    IMU-bias states only weakly observable, so NEES is assessed over the 8 estimable states; a
    manoeuvring trajectory for full-17 observability is roadmap. Also adds Ukf::update_stats
    (returns the per-update NIS) and TightlyCoupled17::{update_gnss_nis, nees, nees_subset}
    consistency instrumentation. All existing scenarios are unaffected (additive; reproducibility
    goldens unchanged).

  • Sequential (recursive) terrain-referenced navigation — SITAN as a running filter.
    A new terrain-slam scenario kind (src/altpnt/sequential.rs,
    scenarios/terrain-slam.toml) that runs the existing altimeter-vs-DEM measurement
    model epoch by epoch through the particle_filter SIR engine, rather than the
    batch coarse-to-fine search terrain-nav uses to recover a single constant INS
    offset. At each waypoint the cloud is propagated by the INS-reported increment (itself
    corrupted by the per-step drift growth), reweighted by the terrain match, and
    resampled on degeneracy — so a time-varying INS drift is tracked along the
    track, which a constant-offset fit structurally cannot do. On the synthetic DEM the
    recursive estimate stays bounded and re-converges (final ≈ 70 m) while the unaided
    inertial solution diverges unbounded to ≈ 5 km; per-epoch error follows terrain
    distinctiveness (it coasts on the biased INS over flat saddles and re-locks over
    distinctive relief), and the effective-sample-size monitor confirms a healthy cloud.
    Honest scope: the map is known and fixed — recursive localization against a
    stored DEM (the localization half of terrain SLAM), not joint map estimation;
    non-circular by construction (the injected drift ramp is the independent truth). All
    existing scenarios are unaffected (additive; reproducibility goldens unchanged).

  • GNSS-denied resilience spine + FutureNAV demonstrator slices (src/holdover.rs,
    and resilience-envelope foundations under src/impairment_eval.rs, src/quantum_trade.rs,
    src/navsignal.rs, src/inertial/quantum_imu.rs, with FutureNAV verification slices in
    src/verification.rs). Composes the alternative-PNT building blocks — clock holdover,
    signal tracking, inertial coast, terrain — into a single GNSS-outage resilience narrative.
    MODELLED; additive.

  • Kshana Interchange Format (KIF) — a versioned, self-describing artifact envelope
    (src/interchange.rs). A schema-tagged wrapper around scenario results so a stored
    artifact carries its kind, schema version, and MODELLED/VALIDATED labels with it, and
    older envelopes stay forward-compatibly readable. Additive; existing result JSON unchanged.

  • Navigation-signal modulation / tracking + CR3BP halo/NRHO differential corrector
    (src/navsignal.rs, src/cr3bp.rs). A first-order nav-signal modulation & tracking model,
    and a circular-restricted three-body differential corrector for halo / near-rectilinear
    halo orbits, surfaced on the existing deep-space capability axis. MODELLED.

  • Distribution-shift evaluation mode + corpus severity-scale knob for impairment-eval
    (src/impairment_eval.rs). Adds an explicit in/out-of-distribution split and a tunable
    corpus severity scale to the ROC/AUC optimism-gap harness (operating characteristics only —
    never field/IQ data).

  • Cost-per-coverage ROI + detection-miss integrity-impact mapping (src/frugal.rs,
    src/integrity_impact.rs). A frugal-engineering ROI lens (cost per unit coverage) and a
    mapping from detection-miss rate to integrity impact. MODELLED; additive.

  • Cited cold-atom-interferometer (CAI) error-model parameter sheet
    (src/inertial/quantum_imu.rs). A literature-referenced, bracketed parameter sheet for the
    CAI inertial model — inputs are cited, not measured hardware (no TRL / flight claim); it
    feeds the hybrid-ukf velocity-random-walk floor.

  • Project governance. GOVERNANCE.md documenting the decision model and the open/closed
    boundary; the capability map's community/governance row moves none → partial to reflect it.

Fixed

  • ARAIM integrity protection level — nominal bias (b_nom) and σ_URA now applied. The
    MHSS protection level now subtracts the one-sided nominal-bias projection
    b_k = Σ_i |s_i|·b_nom per fault mode, and uses the integrity sigma σ_URA (distinct
    from the accuracy sigma σ_URE) carried on the Integrity Support Message. This makes the
    protection level more conservative and standards-correct, and therefore changes the PL
    values reported by existing integrity scenarios
    . The ISM/scenario gains #[serde(default)]
    sigma_ura_m / b_nom_m fields, so inputs that leave them unset retain prior behaviour. See
    docs/ARAIM_REFERENCE.md for the restored b_k formula and the honest implementation note.

  • Spoof-monitor χ² consistency. parity_raim_test now uses the shared
    raim::chi2_quantile inverse-χ² path, removing a second divergent χ² implementation so RAIM
    and the parity spoof monitor agree on their thresholds.

  • **gravity-map-nav wired into the CLI...

Read more

v0.17.0

14 Jun 18:12

Choose a tag to compare

Added

  • Deep-space & Mars PNT — open radiometric navigation engine + GSE simulation.
    A new, fully additive capability axis on top of the Earth-validated core (every
    existing Earth scenario is byte-identical — the reproducibility goldens pass
    unchanged with no regeneration). Adds: a multi-body dynamics core (Body{mu, re, zonals, gravity, rotation, IAU-pole} with Mars GMM-3 tesseral gravity, an IAU
    body-fixed Mars frame, a pluggable EphemerisProvider seam, sub-microsecond
    two-part Julian dates and TT↔TDB); radiometric observables (iterative light-time +
    Shapiro delay, two-/one-/three-way Doppler & range via the Moyer two-leg solve,
    coherent transponder turnaround, regenerative/PN ranging per CCSDS 414, Δ-DOR per
    CCSDS 506, solar-plasma/tropo/iono media); CCSDS-TDM (503) parse + emit; a
    reduced-dynamic Square-Root Information Filter with RTN empirical accelerations, a
    three-state onboard clock, and a Mars-drag model; a joint one-way + two-way fusion
    estimator; the mars-pnt scenario surface (relay constellation + LMO/transfer/
    surface) across CLI, Python, WASM, MCP, and the playground; and an end-to-end GSE
    performance simulator (geometry → link budget → observables → SRIF → covariance).
    Validation tier — simulation-validated: synthetic closed-loop OD (Mars-LMO ≈
    0.2 m) and analytic self-consistency, with the Sun-central Mars dynamics
    independently cross-checked against JPL DE440 (xval/anise-mars-od, kernel-gated:
    137 m @ 1-day arc, the honest unmodelled-n-body residual). Reported deep-space
    accuracies are simulation / covariance figures of merit, not real-mission
    results; real DSN/ESTRACK tracking-data validation remains on the roadmap. ANISE
    (MPL-2.0, edition-2024) is confined to a workspace-excluded cross-check crate, so
    the cargo deny license gate and the MSRV-1.75 job are untouched.
  • Agency-accurate ground tracks from real IERS Earth orientation. The
    ephemeris scenario takes an optional eop_finals2000a field — the inlined body
    of a real IERS finals2000A file — and reduces the ground track through the
    per-epoch UT1−UTC and polar motion interpolated from it (the same EopSeries
    precise_od uses), overriding the nominal dut1_s/xp_arcsec/yp_arcsec
    scalars. The data travels in the scenario, so the run stays reproducible and
    needs no filesystem (it works in the WASM playground). A kshana --eop <finals2000A> flag folds a real file into the scenario from the CLI. Closes the
    asymmetry where only precise_od consumed real Earth-orientation data.

Fixed

  • Range-rate frame consistency. The ground station is now mapped into the
    inertial frame through the exact inverse of the satellite's reduction
    (frames::itrf_to_teme, undoing polar motion and the sidereal rotation)
    instead of a polar-motion-blind GMST rotation, so both endpoints share one
    frame. The effect on the reported Doppler is below the validation floor, but it
    removes a real frame mismatch and an "exact" overclaim in the source.
  • carrier_hz is validated. A zero or non-finite carrier frequency now returns
    an error instead of silently producing zero Doppler (it had made λ = c/carrier
    infinite), matching the existing step_s guard.

Get this release

Download — attached below, prebuilt (no toolchain needed); each artifact carries
SLSA build-provenance (verify with gh attestation verify <file> --repo AshfordeOU/kshana):

  • kshana — the simulator CLI / engine (Linux x86-64)
  • kshana-mcp — the Model Context Protocol server (Linux x86-64)
  • kshana-sbom.cdx.json — CycloneDX SBOM
  • kshana-validation-summary.html — the per-release validation summary

On macOS or Windows, install from a registry below — the PyPI wheels, the npm/WASM
package, and the Docker image are all cross-platform.

Install from a package registry:

Channel Get it
crates.io cargo install kshana · cargo install kshana-mcp
PyPI pip install kshana
npm npm install kshana
ghcr.io docker run -i ghcr.io/ashfordeou/kshana-mcp:0.17.0
MCP registry io.github.ashfordeOU/kshana-mcp (auto-discovered by MCP clients)
JetBrains Marketplace search "Kshana" in your IDE → Plugins

No install: run it in your browser at kshana.dev · Cite: DOI 10.5281/zenodo.20528627


Full changelog: CHANGELOG.md · Docs: README

v0.16.0

11 Jun 22:13

Choose a tag to compare

Added

  • Reference-grade precise orbit determination, cross-validated against real
    agency products.
    A new batch least-squares estimator (src/precise_od.rs)
    with a variational state-transition matrix and outlier editing, driven by a
    full force model (PreciseForceModel: EGM2008 geopotential, third bodies,
    solid + ocean + atmospheric tides, and empirical CPR/2-per-rev accelerations),
    fed by a real IERS finals2000A Earth-orientation parser. Validated against
    published reference orbits: Galileo MEO to 13 cm post-fit, ESA Swarm-A
    LEO
    , and LRO lunar (selenocentric, GRAIL gravity, IAU-2015 body frame —
    reduced-dynamic 6.6 m, honestly above the 5 m target on the open path; the
    DE-grade ANISE/DE440 path that reaches it is a workspace-excluded crate).
  • spoof-detect scenario — an integrated multi-layer spoofing detector
    combining per-epoch RAIM parity, AGC and signal-quality (SQM) monitors and a
    fused decision, validated against the published TEXBAT scenario parameters
    (Humphreys et al., ION GNSS 2012), including the carrier-aligned hard case.
  • ephemeris scenario — state, frames, ground track and Doppler. Propagate
    one satellite (TLE→SGP4 or an analytic orbit) and emit, per step, the inertial
    TEME and GCRS state (position and velocity), the Earth-fixed ITRF/ECEF
    position, the WGS-84 sub-satellite ground track (latitude / longitude /
    altitude), and the topocentric azimuth / elevation / range with range-rate and
    Doppler from a ground station. Reachable from the CLI, Python, WASM and the
    MCP server, and shipped as the "Ground track" preset in the web playground,
    where the track is drawn over a real world map (Natural Earth 1:110m
    coastlines, embedded — no network or external dependency).
  • CCSDS OEM export (--export-oem / export_oem = true) — the
    velocity-carrying Orbit Ephemeris Message consumed by GMAT / Orekit / STK, at
    parity with the existing SP3 and OMM exporters.
  • Solid, ocean (FES2004) and atmospheric (Ray 2001 S2) Earth tides on the
    geopotential (IERS Conventions Ch. 6), wired into the force model.
  • ARM64 wheels — the PyPI build now also produces Linux aarch64
    (manylinux_2_28), macOS arm64 and Windows arm64 wheels.
  • Extended technical report (preprint) linked from the README, and the JOSS
    paper made submittable (author ORCID, compiled to PDF in CI on every change).

Changed

  • Frames validated to the millimetre against published Vallado vectors. The
    TEME→PEF/ITRF reduction, the full CIO IAU 2006/2000A GCRS→ITRS chain and the
    ECEF→geodetic WGS-84 conversion are now pinned to the worked example in Vallado
    et al. (AIAA 2006-6753) with its IERS EOP, not just to internal self-consistency.
  • Independent time-scale cross-checks. ERA and the UTC/TAI/TT scales agree
    with hifitime to < 1 µs, and the DE440 planetary ephemeris agrees with JPL
    Horizons (Moon/Sun geocentric positions), both as always-on CI gates.
  • The web playground hides the figures-of-merit tab when a result carries no
    figure-of-merit rows, so chart-only packs (ephemeris, RAIM, spoof) open on
    their chart — for ephemeris, the ground track — instead of an empty table.
  • The playground's guided sliders and parameter sweep now work for the
    ephemeris / ground-track scenario
    : its knobs (station latitude / longitude,
    time step, duration, UT1−UTC) are tunable, and a sweep can plot pass geometry
    (max elevation, peak Doppler, altitude, speed) against any of them — e.g. max
    elevation vs station latitude. The Sweep tab is now shown only when a scenario
    is actually sweepable, so no pack offers a control that plots nothing.
  • Documented the SGP4 DUT1 ≈ 0 approximation at the GMST call site (a ≤ ~13″
    rotation error, well inside SGP4's own model error) and refreshed CAPABILITY.md.

Get this release

Download — attached below, prebuilt (no toolchain needed); each artifact carries
SLSA build-provenance (verify with gh attestation verify <file> --repo AshfordeOU/kshana):

  • kshana — the simulator CLI / engine (Linux x86-64)
  • kshana-mcp — the Model Context Protocol server (Linux x86-64)
  • kshana-sbom.cdx.json — CycloneDX SBOM
  • kshana-validation-summary.html — the per-release validation summary

On macOS or Windows, install from a registry below — the PyPI wheels, the npm/WASM
package, and the Docker image are all cross-platform.

Install from a package registry:

Channel Get it
crates.io cargo install kshana · cargo install kshana-mcp
PyPI pip install kshana
npm npm install kshana
ghcr.io docker run -i ghcr.io/ashfordeou/kshana-mcp:0.16.0
MCP registry io.github.ashfordeOU/kshana-mcp (auto-discovered by MCP clients)
JetBrains Marketplace search "Kshana" in your IDE → Plugins

No install: run it in your browser at kshana.dev · Cite: DOI 10.5281/zenodo.20528627


Full changelog: CHANGELOG.md · Docs: README

v0.15.1

09 Jun 15:02

Choose a tag to compare

Added

  • The Kshana — PNT simulator JetBrains plugin is now published and approved on the
    JetBrains Marketplace.
    README, kshana.dev, and the distribution docs link to it directly.

Fixed

  • MCP registry publish now succeeds: the server.json description was over the
    registry's 100-character limit (HTTP 422). Shortened it, and aligned the server name
    (and the image's ownership label) to the canonical namespace
    io.github.ashfordeOU/kshana-mcp.
  • JetBrains Marketplace publish now succeeds: the CI re-uploaded the same plugin
    version that was listed manually, which the Marketplace rejects. The plugin version is
    bumped to 0.1.1, and the idempotency guard now also treats an "already contains
    version" response as a no-op success.
  • kshana.dev cache-busting: the version-stamped style.css/app.js query strings
    track the release, so returning visitors always get the current build.

Get this release

Download — attached below, prebuilt (no toolchain needed); each artifact carries
SLSA build-provenance (verify with gh attestation verify <file> --repo AshfordeOU/kshana):

  • kshana — the simulator CLI / engine (Linux x86-64)
  • kshana-mcp — the Model Context Protocol server (Linux x86-64)
  • kshana-sbom.cdx.json — CycloneDX SBOM
  • kshana-validation-summary.html — the per-release validation summary

On macOS or Windows, install from a registry below — the PyPI wheels, the npm/WASM
package, and the Docker image are all cross-platform.

Install from a package registry:

Channel Get it
crates.io cargo install kshana · cargo install kshana-mcp
PyPI pip install kshana
npm npm install kshana
ghcr.io docker run -i ghcr.io/ashfordeou/kshana-mcp:0.15.1
MCP registry io.github.ashfordeOU/kshana-mcp (auto-discovered by MCP clients)
JetBrains Marketplace search "Kshana" in your IDE → Plugins

No install: run it in your browser at kshana.dev · Cite: DOI 10.5281/zenodo.20528627


Full changelog: CHANGELOG.md · Docs: README

v0.15.0

08 Jun 21:07

Choose a tag to compare

Added

  • kshana-mcp — Kshana as a Model Context Protocol (MCP) server (mcp/kshana-mcp/).
    A standalone, workspace-excluded crate (the rmcp SDK is edition 2024) that exposes the
    validated engine to AI agents and assistants — Cursor, JetBrains AI Assistant / Junie,
    and any MCP client — over stdio. Tools: run_scenario, list_scenario_kinds,
    validate_scenario, export_sp3, export_omm, each a thin wrapper over kshana::api.
  • JetBrains IDE plugin (ide/jetbrains/). Right-click a scenario .toml
    Run Kshana Scenario; figures of merit and result JSON stream into a Kshana tool
    window. Pure-platform Kotlin plugin, compatible with every JetBrains IDE 2024.3+.
  • Public distribution + per-release auto-publish for both:
    • kshana-mcp to crates.io (cargo install kshana-mcp) via publish.yml.
    • kshana-mcp as a multi-arch OCI image on ghcr.io
      (docker run ghcr.io/ashfordeou/kshana-mcp) via a new mcp-publish.yml.
    • kshana-mcp to the official MCP registry via GitHub OIDC (zero secrets); the
      registry entry (server.json) uses the OCI package type with a label-verified owner.
    • the IDE plugin to the JetBrains Marketplace via publishPlugin (token-gated,
      optional developer signing) on each release tag.

Get this release

Download — attached below, prebuilt (no toolchain needed); each artifact carries
SLSA build-provenance (verify with gh attestation verify <file> --repo AshfordeOU/kshana):

  • kshana — the simulator CLI / engine (Linux x86-64)
  • kshana-mcp — the Model Context Protocol server (Linux x86-64)
  • kshana-sbom.cdx.json — CycloneDX SBOM
  • kshana-validation-summary.html — the per-release validation summary

On macOS or Windows, install from a registry below — the PyPI wheels, the npm/WASM
package, and the Docker image are all cross-platform.

Install from a package registry:

Channel Get it
crates.io cargo install kshana · cargo install kshana-mcp
PyPI pip install kshana
npm npm install kshana
ghcr.io docker run -i ghcr.io/ashfordeou/kshana-mcp:0.15.0
MCP registry io.github.ashfordeOU/kshana-mcp (auto-discovered by MCP clients)
JetBrains Marketplace search "Kshana" in your IDE → Plugins

No install: run it in your browser at kshana.dev · Cite: DOI 10.5281/zenodo.20528627


Full changelog: CHANGELOG.md · Docs: README

v0.14.1

08 Jun 16:39

Choose a tag to compare

Added

  • Independent ANISE/SPICE reference-frame cross-validation (xval/anise-frames/).
    A standalone, workspace-excluded crate cross-checks kshana's IAU 2006/2000A CIO
    reduction (kshana::cio::gcrs_to_itrs_matrix, GCRS→ITRS) against ANISE (the
    pure-Rust NAIF/SPICE reimplementation) rotating GCRF→ITRF93 from JPL's
    earth_latest_high_prec.bpc, with the same IERS finals2000A Earth-orientation
    parameters fed to both sides
    , over eight quarterly epochs 2020–2023. The two
    independent frame realizations agree to a maximum relative rotation of 0.028″ —
    ≤ 0.86 m on the ground, ≤ 0.93 m at LEO, ≤ 3.6 m at GNSS orbit
    , meeting the
    long-standing ROADMAP "< 10 m" frame cross-check with large margin (it complements,
    and does not replace, the existing bit-for-bit SOFA/ERFA anchors). The crate is
    isolated because anise + hifitime are MPL-2.0 / edition-2024 and must never enter
    the published kshana dependency graph, its Cargo.lock, the cargo deny license
    gate, or the MSRV build; ANISE is pinned default-features = false. Includes a
    frame-xval binary (fetches the ~5 MB BPC, prints a table, writes report.{json,md}),
    a kernel/network-self-skipping test gate, and an optional workflow_dispatch-only CI
    job (never blocks main). Documented in docs/VALIDATION.md (CIO row) and ROADMAP.md.

Fixed

  • Mobile-friendly playground. Fixed horizontal overflow of the playground .panel
    on phones (a CSS-grid item defaulting to min-width: auto rendered ~100 px wider than
    the viewport) via min-width: 0 and width-guarded controls; verified clean at 360 /
    390 / 414 / 768 px. Aligned the "Pin to compare" / "Download report" action buttons
    (equal margin boxes in the flex row). Enlarged run buttons, sliders, selects and nav
    links to the ~44 px WCAG 2.5.5 / Apple-HIG touch-target minimum on phones and touch
    devices, with desktop sizing unchanged.

Full changelog: CHANGELOG.md · Docs: README

v0.14.0

08 Jun 13:28

Choose a tag to compare

Fixed

  • Robustness hardening from an adversarial battle-test pass. (1) sbas_protection_level now
    rejects non-finite elevation/azimuth/variance and negative or non-finite covariance diagonals
    (a near-singular geometry scaled up by small σ could previously slip the absolute-pivot gate and
    return a NaN VPL / absurd HPL as a valid Some — a silent integrity failure). (2) The
    numerical propagator (propagate/propagate_dopri) fails closed on a non-finite initial state
    instead of spinning the adaptive controller forever. (3) The DEM cell helper no longer panics on
    a single-sample (1×N) grid. (4) lunar_look_angle azimuth is held strictly in [0, 360).
    (5) SphericalHarmonicField::from_gfc rejects non-physical (NaN / non-positive) GM/radius.

Added

  • validation_report binary + release artifact: a dependency-free generator that emits a
    one-page, print-ready HTML validation summary indexing every CI-enforced validation (SGP4
    666/666, EGM2008, bit-for-bit frames, NIST Allan, IMU datasheets, ARAIM/SBAS, 3-OS
    reproducibility, coverage) to its test and external oracle. The release workflow generates
    kshana-validation-summary.html and attaches it (with SLSA provenance) to each tagged release.
  • numpy-interop pytest + wheel hardening: tests/python/test_numpy_interop.py (run in CI)
    plus a pinned manylinux container and an auditwheel show verification step in the wheel build.
  • Tutorials & education: docs/tutorials/ (three worked tutorials, per-domain annotated
    scenarios, Tier-1/2/3 exercises) with tests/tutorials.rs pinning every quoted number to live
    engine output.
  • External submission artifacts (paper/, notebooks/, submissions/): a JOSS paper
    draft, a quantum-vs-classical notebook, and ready-to-file kits for awesome-gnss / ESA Navipedia
    / NASA ASCL / ESA ESSR / ION/IAC, plus FUNDING.yml — staging the external steps for submission.
  • Terrain-referenced & combined alt-PNT navigation (altpnt module): a TERCOM/SITAN
    terrain-matching navigator over a DEM (.hgt loader + synthetic-fixture generator) and a
    combined gravity + magnetic (IGRF) + terrain GPS-denied navigator, exposed as terrain-nav and
    combined-altpnt scenario kinds. Validated by terrain-match convergence (a known injected
    offset recovered) and a bounded combined-filter error over a GPS-denied window.
  • LunaNet LANS geometry (lunar): named lunar surface sites (Apollo 11/15/16, Shackleton
    rim) with authoritative selenographic coordinates, surface look angles (az/el/range),
    visibility/coverage, and site DOP, validated against the Moon radius, the published site
    coordinates, and the radial-overhead 90° elevation identity.
  • Guided browser playground: guided-mode sliders, a tabbed output panel, a first-run tour
    overlay, parameter-sweep and multi-run-overlay modes, a dependency-free canvas/SVG 3D orbit
    view (the orbit pack now emits an additive eci_track), an embed/iframe mode, and
    download-as-HTML-report — each with node unit tests in CI.
  • Datasheet-validated IMU error model (tests/imu_allan_spec.rs): ADIS16465/16488/16460
    ARW/VRW/bias-instability recovered from the synthesised Allan deviation and checked against the
    manufacturer specs (NIST SP1065 / IEEE 952 identification).
  • NIST SP1065 Allan-estimator validation (tests/allan_nist_sp1065_1000point.rs): the four
    estimators reproduce the published 1000-point reference deviations and Table-32 confidence
    bounds.
  • SBAS / DO-229E protection levels, L1/L5 ionosphere-free, and a DO-316 compliance map
    (sbas module). sbas_protection_level forms the weighted geometry matrix from each
    satellite's elevation/azimuth and UDRE/GIVE/airborne/tropo error budget, inverts the normal
    matrix (shared orbit::invert4), and projects the variances into HPL/VPL via the DO-229E
    K-factors (PA 6.0/5.33, NPA 6.18). iono_free_l1l5 adds the GPS L1/L5 ionosphere-free
    pseudorange (IS-GPS-705, γ₁₅ = 1.79327), validated to cancel the engine's independent
    first-order ionospheric delay. do316_compliance_map traces DO-316/DO-229E requirements to
    the implementing functions. Validated against closed-form K-factor definitions, the numpy
    inv(GᵀG) reference geometry, and the two-route covariance identity; the published-PL
    RTKLIB/gLAB conformance cross-check is documented as founder-gated in docs/COMPLIANCE.md.
  • Full tesseral spherical-harmonic gravity — the EGM2008 field to degree/order 70.
    A new gravity_sh::SphericalHarmonicField evaluates the geopotential and its acceleration
    in the Earth-fixed frame from fully-normalized C̄_nm, S̄_nm coefficients, using the stable
    Holmes–Featherstone normalized Legendre recurrence (de-normalizing would overflow at this
    degree). The shipped coefficients are the NGA EGM2008 product (public domain, via ICGEM),
    bundled in egm2008_data.rs and reproduced bit-for-bit by tools/gen_egm2008.py from the
    committed tools/egm2008_to70.gfc; any ICGEM .gfc model loads via from_gfc. Validated
    against three independent oracles: point-mass collapse (C̄00-only = −μr/|r|³), a zonal-only
    field reproducing the existing forces::zonal_accel to ~1e-9, and the analytic acceleration
    matching the finite-difference gradient of the directly-summed potential to <1e-6.
  • General-relativistic Lense–Thirring (frame-dragging) acceleration
    (forces::lense_thirring_accel, IERS 2010 Eq. 10.12), the gravitomagnetic term beyond the
    existing Schwarzschild correction, wired into the numerical propagator via a new
    ForceModel::lense_thirring() flag. Validated as linear in the Earth's angular momentum and
    1–2 orders of magnitude below the Schwarzschild term, the regime of the LAGEOS / Gravity
    Probe B measurements.
  • A Propagator trait unifying the analytic and numerical orbit propagators. The
    numerical Cowell force-model propagator is now a first-class peer of SGP4: a new
    NumericalPropagator type (initial state + ForceModel + Tolerance + choice of
    step-doubling or Dormand–Prince Integrator) and Sgp4 both implement
    propagator::Propagator, whose state_at(t_seconds) -> StateVector returns the inertial
    TEME state in SI units (m, m/s) so the two are interchangeable behind a
    Box<dyn Propagator>. The SGP4 impl is the exact km/min→SI conversion of the inherent
    method (verified by an equality test); the numerical impl clears the same sub-metre
    exact-Kepler gate through the trait, the two adaptive drivers agree, and a PropagatorError
    surfaces the underlying SGP4 code.

Full changelog: CHANGELOG.md · Docs: README

v0.13.0

08 Jun 08:23

Choose a tag to compare

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 open docs/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 independent sgp4 crate, a CI coverage gate (~97% line on src/), 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.rs Stanford + 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: --bg panels, warm --line grid, --fg labels, 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 in api::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.svg export, or the HTML scorecard — identifies its version, scenario
    fingerprint, and source. The hash is labelled scenario for clarity and comes from
    the result's scenario_hash where 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 and docs/PROVENANCE.md.

Fixed

  • raim::chi2_quantile and the RAIM Stanford-noise sampler are now panic-free on
    out-of-range / non-finite inputs
    (a read_dir-order-dependent fuzz finding from
    the new ARAIM scenarios). chi2_quantile now guards p/k like normal_quantile
    (returning a boundary value instead of assert!-panicking), and the availability
    Stanford-noise Normal clamps 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 in tests/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.md documents the split.
  • Code-coverage gate in CI. A new coverage job runs cargo-tarpaulin with
    the LLVM source-based engine, publishes an lcov report as a build artifact, and
    enforces a line-coverage floor on src/ (generated data tables, the CLI
    entrypoint, the tests, and web assets excluded). Measured line coverage is
    ~97% on src/ (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 sgp4 crate. 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.out table. The committed comparison table
    (tests/fixtures/sgp4_comparison.md, regenerated via KSHANA_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 default from_elements uses 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; an orbit scenario's mean
    elements can now be published as a CCSDS 502.0-B-2 OMM catalogue — one OMM KVN
    message per TLE-defined satellite — via kshana <orbit.toml> --export-omm out.omm, or export_omm = true in 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 becomes OBJECT_NAME (else OBJECT <id>). CREATION_DATE is 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-satellite gps-ops snapshot
    (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 in web/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 in web/app.js; delta logic unit-tested
    in web/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 ...
Read more

v0.12.0

06 Jun 19:31

Choose a tag to compare

This release lands Kshana's first non-analytic orbit propagator — a Cowell
integrator with a hierarchical six-perturbation force model (two-body + J2–J6 zonal +
epoch-driven Sun/Moon third body + solar-radiation pressure with a conical
umbra/penumbra shadow + atmospheric drag + the post-Newtonian Schwarzschild relativistic
correction) driven by a choice of two adaptive integrators (RK4 step-doubling and the
Dormand–Prince RK5(4) embedded pair) — alongside a maneuver / trajectory-design layer
(impulsive and finite burns, an Izzo Lambert solver, and a porkchop sweep), a
gravity-map-matching alt-PNT layer that recovers a 60-minute GPS-denied track to under
500 m, a batch + sequential orbit-determination pipeline, and a full 17-state
tightly-coupled GNSS/INS UKF with quantum-CAI dead-reckoning. Every numerical capability
is pinned against analytic truth or a hand-derived closed form; the off-by-default
perturbations leave the released goldens untouched.

Added

  • Post-Newtonian (Schwarzschild) relativistic correction (forces::relativistic_accel +
    propagator::ForceModel::relativity).
    Adds the dominant general-relativistic perturbation on a
    near-Earth orbit — the leading driver of the relativistic perigee advance — in the IERS /
    Montenbruck–Gill β = γ = 1 form a = (μ/c²r³)·{[4μ/r − v²]·r + 4(r·v)·v}. Like atmospheric
    drag it is velocity-dependent, so it rides the (r, v) integrator RHS via
    [accel_rv], opt-in and off by default. Validated self-contained: on a circular orbit it
    collapses to the closed form 3μ²/(c²r³)·r̂ (purely radial and outward, off-axis components
    exactly zero); its ratio to two-body is the textbook ≈1.9·10⁻⁹ at LEO (the μ/(c²r)
    signature); a radial-velocity case matches the hand-simplified μ(4μ + 3v²r)/(c²r³); and in the
    propagator it perturbs the orbit without dissipating it — the semi-major axis is conserved to
    well under a metre/day, the structural opposite of drag's monotonic decay. Because it is off by
    default the two-body/J2/zonal goldens are untouched. PPN-parameter (β,γ) tuning and the
    Lense–Thirring frame-dragging term remain follow-ons.
  • Conical umbra+penumbra shadow model (forces::conical_shadow), now used by solar-radiation
    pressure.
    Upgrades the binary umbral-cylinder eclipse to a smooth ν ∈ [0,1] factor: the Sun
    and Earth are modelled as disks of apparent angular radii a = asin(R☉/d☉), b = asin(Rₑ/|r|)
    with apparent centre separation c, and ν is one minus the fraction of the Sun's disk occulted
    by the Earth's disk (the circle–circle lens-overlap area) — full sun for c ≥ a+b, total umbra
    for c ≤ b−a, annular for c ≤ a−b, and a continuous penumbra in between. srp_accel now uses
    it, so the SRP force tapers smoothly through eclipse instead of switching on/off. Adds the IAU
    nominal forces::SOLAR_RADIUS. Validated self-contained: ν = 1 in full sun and ν = 0 deep in
    the umbra (exact), a smooth monotonic penumbra (ν rises 0 → ~½ at c = b → 1 across the
    [b−a, b+a] band), and the conical penumbra extends beyond the umbral cylinder (a point the
    binary cylinder calls fully lit is 0 < ν < 1 for the cone). The simpler cylindrical_shadow
    remains available; solar limb darkening and the oblate-Earth shadow remain follow-ons.
  • Dormand–Prince RK5(4) embedded integrator (integrator::dopri54_step /
    integrator::integrate_dopri + propagator::propagate_dopri).
    Adds the standard
    Dormand–Prince (1980) embedded Butcher-tableau pair alongside the existing RK4 step-doubling
    driver: seven FSAL stages yield a 5th-order solution and a 4th-order error estimate from one set
    of evaluations (7 vs 11 function calls per step), a cheaper local-error estimate. The adaptive
    driver reuses the same RMS-error norm and 0.9·(1/err)^(1/5) step controller, so it is a drop-in
    alternative; propagator::propagate_dopri exposes it on the orbit force model. Validated
    self-contained: the embedded error estimate is O(h⁵) (halving the step cuts it ~32×); DP5(4)
    integrates y' = y to e and the harmonic oscillator over 50 periods conserving energy to
    <1e-6; it reaches the same endpoint at the same tolerance in fewer function evaluations than
    step doubling (without sacrificing accuracy); and propagate_dopri clears the same analytic-truth
    gate as the RK4 path — sub-metre against the exact universal-variable Kepler solution over a
    24 h LEO orbit
    — while the two drivers agree to <1 m on a J2..J6 orbit (no closed form). Higher
    embedded pairs (RKF7(8) / DOP853) remain a follow-on.
  • Atmospheric drag wired into the propagator as its first velocity-dependent force
    (forces::atmospheric_density + forces::drag_accel + propagator::ForceModel::drag).
    Adds
    the Vallado Table 8-4 piecewise-exponential atmosphere ρ = ρ0·exp(−(h−h0)/H) (28 bands from
    sea level past 1000 km, clamped below the surface) and the quadratic drag
    a = −½ · ρ(h) · (C_D·A/m) · |v_rel| · v_rel against the co-rotating atmosphere
    v_rel = v − ωₑ ẑ × r (forces::EARTH_ROTATION_RATE = 7.2921151467e-5). Because drag depends on
    velocity, ForceModel gains a new accel_rv(t, r, v) and the integrator RHS now passes velocity
    (f(t,[r;v]) = [v; a(t,r,v)]); the position-only accel_at is unchanged, so the conservative
    terms and goldens are untouched. Validated self-contained: the density anchors at the
    1.225 kg/m³ sea-level value
    , clamps below the surface, decreases monotonically through LEO,
    sits in the solar-mean ~1e-12 kg/m³ band at 400 km, and its recovered local scale height
    (≈ 58 km at 400 km)
    is physical; drag opposes the co-rotating relative velocity at the
    ~2e-6 m/s² LEO magnitude for C_D·A/m = 0.02 m²/kg; and — the key signature — drag is
    dissipative: a 300 km orbit loses specific energy monotonically and its semi-major axis
    decays a bounded ~km/day, where the vacuum baseline conserves energy to <1e-9. The
    NRLMSISE-00 thermospheric density (the < 5 % drag-density clause) remains a follow-on.
  • Solar-radiation pressure wired epoch-driven into the propagator force model
    (forces::srp_accel + propagator::ForceModel::solar_radiation).
    Adds the cannonball SRP
    model
    a = ν · P☉ · cᵣ · (A/m) · (AU/d)² · d̂ with a cylindrical-shadow eclipse factor
    (forces::cylindrical_shadow, ν ∈ {0,1}): the radiation pressure P☉ = Φ☉/c from the modern
    1361 W/m² total solar irradiance (≈ 4.5398·10⁻⁶ N/m²), the inverse-square (AU/d)² flux fall-off,
    and the radial push away from the Sun. It rides the same epoch-driven RHS as the third
    body, sampling the ephem Sun once at the advanced epoch epoch_jd_tt + t/86400 shared between
    the Sun third body and SRP. Composable:
    with_zonals_j2_j6().third_body(true, true, epoch).solar_radiation(1.5, 0.02). Validated
    self-contained against hand-derived signatures: the 1-AU radiation pressure pins to its textbook
    ≈ 4.5398·10⁻⁶ N/m²
    ; a fully-lit LEO sat's SRP is bit-identical to the cannonball formula,
    points away from the Sun, and sits in the ~1.36·10⁻⁷ m/s² band for cᵣ = 1.5, A/m = 0.02
    m²/kg; doubling the Sun distance quarters the magnitude (inverse-square); the cylindrical
    shadow eclipses only the umbral cylinder
    (anti-sunward and within one Earth radius of the
    Earth–Sun line) and yields exactly zero SRP in eclipse; and in the propagator SRP perturbs
    a LEO orbit by a small bounded amount that scales ~linearly with A/m
    — while a model with no
    perturbations stays bit-for-bit time-independent, leaving the two-body/J2/zonal goldens untouched.
    The conical umbra/penumbra (smooth ν ∈ [0,1]), atmospheric drag, and external GMAT/Orekit
    cross-validation remain follow-ons.
  • Epoch-driven Sun/Moon third body wired into the time-varying propagator RHS
    (propagator::ForceModel::third_body / accel_at).
    The third-body perturbation is no longer a
    standalone force term — it is now integrated by the Cowell propagator as a genuinely time-varying
    force: each RHS evaluation samples the ephem Sun/Moon positions at the advanced epoch
    epoch_jd_tt + t/86400
    (reusing precession::julian_centuries_tt for the day↔century
    conversion), so the perturbers move along their orbits during the integration rather than being
    frozen at the start. Composable with any gravity model
    (ForceModel::with_zonals_j2_j6().third_body(true, true, epoch)). Validated self-contained:
    the RHS Sun term is bit-identical to third_body_accel evaluated at the ephemeris position for
    that instant at both t = 0 and t = 1 day (proving the 86400 s ↔ 1 day ↔ 1/36525 century
    wiring exactly), the perturber advances ~2.6·10⁹ m/day between samples (not frozen), the
    instantaneous LEO tidal magnitudes hit the textbook ~5·10⁻⁷ m/s² (Sun) and ~1.1·10⁻⁶ m/s²
    (Moon, ≈ 2× the Sun) bands, each body measurably perturbs the day-long trajectory while staying
    bounded
    , and the same initial state propagated at epochs a quarter-year apart yields a
    different trajectory
    (the tidal axis rotates 90°) — while a model with neither body enabled is
    bit-for-bit time-independent, leaving the two-body/J2/zonal goldens untouched. DE-grade ephemeris
    accuracy and external GMAT/Orekit cross-validation remain follow-ons.
  • Low-precision Moon ephemeris (ephem::moon_position), completing the Sun/Moon third-body pair.
    Adds the Montenbruck & Gill low-precision lunar series (§3.3.2) alongside the Sun model, so the
    body-agnostic forces::third_body_accel can now be driven by either luminary with no external
    DE/SPK kernel
    . Validated self-contained against hand-derived lunar signatures: the geocentric
    distance stays inside the real perigee/apogee envelope (~356 500–406 700 km) over a month and its
    monthly mean recovers the ~384 400 km semi-major axis; the **ecliptic latitude never exce...
Read more