Releases: ashfordeOU/kshana
v0.19.0
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. SeeLICENSE(AGPL) and the new
LICENSING.md(what each licence covers and when it applies).LICENSEnow contains the AGPL-3.0 text; SPDX headers across all sources updated
toAGPL-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.0and earlier, as published);v0.19.0onward is AGPL-3.0 / commercial.
Security
- Bumped
pyo30.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, andpyo3is an optional (python-feature) dependency — so the
real exposure was nil — but the bump keeps a clean scan for downstream auditors.
Migratedsrc/python.rsto the pyo3 0.29 API (Bound return type for
scenario_kinds; explicitskip_from_py_objecton theRunOutputpyclass).
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 SBOMkshana-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
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 ascenarios/<kind>.toml, and carries an explicit
MODELLED label in its result JSON and one-line summary (a per-kind test in
tests/dominance_demonstrators.rsasserts the label is present and that the output
never contains the stringVALIDATED):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 (theparse_oemreader, 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 newhybrid-ukfscenario 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 floorq_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 theq_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 (theq_factor/r_factorknobs) 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 addsUkf::update_stats
(returns the per-update NIS) andTightlyCoupled17::{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 newterrain-slamscenario kind (src/altpnt/sequential.rs,
scenarios/terrain-slam.toml) that runs the existing altimeter-vs-DEM measurement
model epoch by epoch through theparticle_filterSIR engine, rather than the
batch coarse-to-fine searchterrain-navuses 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 undersrc/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 thehybrid-ukfvelocity-random-walk floor. -
Project governance.
GOVERNANCE.mddocumenting the decision model and the open/closed
boundary; the capability map's community/governance row movesnone → partialto 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_nomper 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_mfields, so inputs that leave them unset retain prior behaviour. See
docs/ARAIM_REFERENCE.mdfor the restoredb_kformula and the honest implementation note. -
Spoof-monitor χ² consistency.
parity_raim_testnow uses the shared
raim::chi2_quantileinverse-χ² path, removing a second divergent χ² implementation so RAIM
and the parity spoof monitor agree on their thresholds. -
**
gravity-map-navwired into the CLI...
v0.17.0
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 pluggableEphemerisProviderseam, 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; themars-pntscenario 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
thecargo denylicense gate and the MSRV-1.75 job are untouched. - Agency-accurate ground tracks from real IERS Earth orientation. The
ephemerisscenario takes an optionaleop_finals2000afield — the inlined body
of a real IERSfinals2000Afile — and reduces the ground track through the
per-epoch UT1−UTC and polar motion interpolated from it (the sameEopSeries
precise_oduses), overriding the nominaldut1_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). Akshana --eop <finals2000A>flag folds a real file into the scenario from the CLI. Closes the
asymmetry where onlyprecise_odconsumed 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_hzis 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 existingstep_sguard.
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 SBOMkshana-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
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 IERSfinals2000AEarth-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-detectscenario — 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.ephemerisscenario — 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
withhifitimeto < 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 ≈ 0approximation at the GMST call site (a ≤ ~13″
rotation error, well inside SGP4's own model error) and refreshedCAPABILITY.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 SBOMkshana-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
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.jsondescriptionwas 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 to0.1.1, and the idempotency guard now also treats an "already contains
version" response as a no-op success. kshana.devcache-busting: the version-stampedstyle.css/app.jsquery 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 SBOMkshana-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
Added
kshana-mcp— Kshana as a Model Context Protocol (MCP) server (mcp/kshana-mcp/).
A standalone, workspace-excluded crate (thermcpSDK 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 overkshana::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-mcpto crates.io (cargo install kshana-mcp) viapublish.yml.kshana-mcpas a multi-arch OCI image onghcr.io
(docker run ghcr.io/ashfordeou/kshana-mcp) via a newmcp-publish.yml.kshana-mcpto 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 SBOMkshana-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
Added
- Independent ANISE/SPICE reference-frame cross-validation (
xval/anise-frames/).
A standalone, workspace-excluded crate cross-checkskshana'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 IERSfinals2000AEarth-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 becauseanise+hifitimeare MPL-2.0 / edition-2024 and must never enter
the publishedkshanadependency graph, itsCargo.lock, thecargo denylicense
gate, or the MSRV build; ANISE is pinneddefault-features = false. Includes a
frame-xvalbinary (fetches the ~5 MB BPC, prints a table, writesreport.{json,md}),
a kernel/network-self-skipping test gate, and an optionalworkflow_dispatch-only CI
job (never blocksmain). Documented indocs/VALIDATION.md(CIO row) andROADMAP.md.
Fixed
- Mobile-friendly playground. Fixed horizontal overflow of the playground
.panel
on phones (a CSS-grid item defaulting tomin-width: autorendered ~100 px wider than
the viewport) viamin-width: 0and 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
Fixed
- Robustness hardening from an adversarial battle-test pass. (1)
sbas_protection_levelnow
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 validSome— 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_angleazimuth is held strictly in[0, 360).
(5)SphericalHarmonicField::from_gfcrejects non-physical (NaN / non-positive)GM/radius.
Added
validation_reportbinary + 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.htmland 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 anauditwheel showverification step in the wheel build. - Tutorials & education:
docs/tutorials/(three worked tutorials, per-domain annotated
scenarios, Tier-1/2/3 exercises) withtests/tutorials.rspinning 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, plusFUNDING.yml— staging the external steps for submission. - Terrain-referenced & combined alt-PNT navigation (
altpntmodule): a TERCOM/SITAN
terrain-matching navigator over a DEM (.hgtloader + synthetic-fixture generator) and a
combined gravity + magnetic (IGRF) + terrain GPS-denied navigator, exposed asterrain-navand
combined-altpntscenario 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 additiveeci_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
(sbasmodule).sbas_protection_levelforms the weighted geometry matrix from each
satellite's elevation/azimuth and UDRE/GIVE/airborne/tropo error budget, inverts the normal
matrix (sharedorbit::invert4), and projects the variances into HPL/VPL via the DO-229E
K-factors (PA 6.0/5.33, NPA 6.18).iono_free_l1l5adds 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_maptraces 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 indocs/COMPLIANCE.md. - Full tesseral spherical-harmonic gravity — the EGM2008 field to degree/order 70.
A newgravity_sh::SphericalHarmonicFieldevaluates the geopotential and its acceleration
in the Earth-fixed frame from fully-normalizedC̄_nm, S̄_nmcoefficients, 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 inegm2008_data.rsand reproduced bit-for-bit bytools/gen_egm2008.pyfrom the
committedtools/egm2008_to70.gfc; any ICGEM.gfcmodel loads viafrom_gfc. Validated
against three independent oracles: point-mass collapse (C̄00-only =−μr/|r|³), a zonal-only
field reproducing the existingforces::zonal_accelto ~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
Propagatortrait unifying the analytic and numerical orbit propagators. The
numerical Cowell force-model propagator is now a first-class peer of SGP4: a new
NumericalPropagatortype (initial state +ForceModel+Tolerance+ choice of
step-doubling or Dormand–PrinceIntegrator) andSgp4both implement
propagator::Propagator, whosestate_at(t_seconds) -> StateVectorreturns 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 aPropagatorError
surfaces the underlying SGP4 code.
Full changelog: CHANGELOG.md · Docs: README
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 ...
v0.12.0
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β = γ = 1forma = (μ/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 form3μ²/(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 radiia = asin(R☉/d☉),b = asin(Rₑ/|r|)
with apparent centre separationc, 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 forc ≥ a+b, total umbra
forc ≤ b−a, annular forc ≤ a−b, and a continuous penumbra in between.srp_accelnow uses
it, so the SRP force tapers smoothly through eclipse instead of switching on/off. Adds the IAU
nominalforces::SOLAR_RADIUS. Validated self-contained:ν = 1in full sun andν = 0deep in
the umbra (exact), a smooth monotonic penumbra (νrises 0 → ~½ atc = 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 is0 < ν < 1for the cone). The simplercylindrical_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 and0.9·(1/err)^(1/5)step controller, so it is a drop-in
alternative;propagator::propagate_dopriexposes it on the orbit force model. Validated
self-contained: the embedded error estimate is O(h⁵) (halving the step cuts it ~32×); DP5(4)
integratesy' = ytoeand 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); andpropagate_dopriclears 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_relagainst the co-rotating atmosphere
v_rel = v − ωₑ ẑ × r(forces::EARTH_ROTATION_RATE = 7.2921151467e-5). Because drag depends on
velocity,ForceModelgains a newaccel_rv(t, r, v)and the integrator RHS now passes velocity
(f(t,[r;v]) = [v; a(t,r,v)]); the position-onlyaccel_atis 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 forC_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
modela = ν · P☉ · cᵣ · (A/m) · (AU/d)² · d̂with a cylindrical-shadow eclipse factor
(forces::cylindrical_shadow, ν ∈ {0,1}): the radiation pressureP☉ = Φ☉/cfrom 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 theephemSun once at the advanced epochepoch_jd_tt + t/86400shared 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 theephemSun/Moon positions at the advanced epoch
epoch_jd_tt + t/86400(reusingprecession::julian_centuries_ttfor 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 tothird_body_accelevaluated at the ephemeris position for
that instant at botht = 0andt = 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-agnosticforces::third_body_accelcan 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...