Rehearse the minute GNSS goes dark
Kshana replays jamming, spoofing and clock holdover when GNSS (Global Navigation Satellite System) signals fail, and tells you how long a system keeps time and position inside its budget, and which clock or sensor buys the most margin. It is one Rust engine. It runs in a terminal, a notebook, an IDE (integrated development environment), an AI (artificial intelligence) assistant, or in your browser.
Every run is reproducible bit for bit from the scenario, the seed and the engine version. Every capability carries one of three labels in a machine-checked ledger: VALIDATED (an independent external oracle agrees), MODELLED (internally consistent, and said out loud) or PARTNER (a hardware partner owns it).
Status: v0.38.0. A validated, reproducible simulation substrate for PNT resilience. Timing and holdover come first, because that domain is the best validated. Kshana is a study and trade-off instrument: not radio-frequency (RF) signal-generator hardware (it drives no radio and transmits nothing; its signal-level output is baseband IQ sample files for software receivers), not a hardware or certified GNSS receiver (its software receiver is an analysis tool) and not a flight product. New to the field? Start with the plain-language primer and the glossary.
What's new in 0.38
0.38 makes the neutral referee measurable: it scores receiver logs against a published test schedule, reads more receivers and sensors, tests timing systems, and ships a benchmark and an interference observatory. It stays advisory: nothing here is certified, no result is a type approval, and nothing transmits. Each row states what the ledger holds for it (how a label is earned); a status names only what was checked, and the ledger row and the linked page say what was not.
0.37's highlights: changelog. The full 0.38 list is in the changelog.
0.38 in depth
Each capability from the table above, in a few lines: what it does, one command to run it, one
picture drawn from a real run of the engine, and what the ledger holds for it. The pictures
are written by tools/gen_figures_038.py into
docs/figures/0.38/ (never drawn by hand): python3 tools/gen_figures_038.py
rewrites them from a built engine, and --check fails if one is stale or its caption here differs.
Where an input is a log, it is a made-up one the script writes, because the real third-party logs
are never vendored, and the caption says so. Everything below is advisory: not certified, not a
type approval, and nothing transmits. A status names what was checked and nothing more;
how a label is earned.
Jammertest scoring
kshana jammertest import turns the published NPRA test catalogue and transmission plan into
scheduled truth, and kshana jammertest score scores a receiver log against it: for each
scheduled test, whether it was detected, how late, and how long the receiver took to recover, plus
false alarms outside every slot. The catalogue is fetched, never vendored.
xval/jammertest-plan/fetch.sh ../jammertest-plan
kshana jammertest score session.toml --catalog ../jammertest-plan/testcatalog2025.json \
--plan ../jammertest-plan/plan-tuesday-2025-09-16.json --location 1 --json
Ledger status: MODELLED. The bars were fixed before any scored run; on the JammerTest 2024 data the minimum-data rule failed in both runs, so no bar is assessable and nothing is claimed. Jammertest.
The receiver's own flags
UBX-NAV-STATUS spoofDetState, UBX-SEC-SIG and NovAtel RXSTATUS are read into a per-epoch
self_report and set beside Kshana's alarms: both, receiver only, Kshana only, or neither. A
Septentrio SBF reader and a read-only --serial input feed the same comparison. Neither side is
ground truth.
kshana receiver-trust session.toml # [log] format = "ubx", path = "receiver.ubx"
Ledger status: MODELLED. Agreement between a receiver's detector and Kshana's is not validation. Receiver trust.
More receivers and sensors
RTKLIB .pos, NovAtel OEM7, RTCM 3 MSM4/MSM7 C/N0, GNSS-SDR monitor streams, an IMU log as a second
sensor, a ROS 2 package and a gnss_lib_py bridge all end in the same receiver-trust timeline.
imu-fit reads the sensor parameters the hybrid and fusion scenarios need off a stationary IMU
log, so a scenario can be run with the sensor you actually have.
kshana imu-fit still.csv --format csv --axis x --expected-mean 0 --id bench-imu
Ledger status: MODELLED. Self-written fixtures only; no capture from a real receiver, IMU, GNSS-SDR run or ROS graph was read. Machine inputs.
Timing and holdover tests
The holdover-test kind scores a holdover test (GNSS removed by the operator, holdover, recovery)
from a measured time-error record against ITU-T masks, and says so in those words: measured
against the mask. linuxptp and chrony log readers feed it; the hardware stages and the
lab-attestation field say who stated what.
kshana scenarios/holdover-test-example.toml
Ledger status: MODELLED. The times GNSS was removed and restored are the operator's statement, not verified. Timing HIL, Hardware safety.
Standards packs
Scored packs for the DHS Resilient PNT test profiles, IMO MSC.1/Circ.1575 and ICAO Doc 9849
Appendix F, plus a NIST IR 8323r1 crosswalk in compliance-report. Each test runs a bundled
scenario and compares a result field with a bar stated before the run; a test that needs a receiver
log is listed as needing an input, not scored. The wording is "supports evidence for".
kshana standards-pack list
kshana standards-pack run imo-circ-1575
Ledger status: MODELLED. A met bar says a modelled scenario met a stated bar, nothing about a product; every miss is kept and pinned by a test. Standards.
LunaNet AFS
The LunaNet Augmented Forward Signal code generator (Gold, Weil and tertiary codes, the message frame and its error coding) and a checker for a recorded or generated symbol stream, pinned to one edition of the specification.
kshana lunanet-afs-check spec
kshana lunanet-afs-check generate --frames 2 --out afs.sym
kshana lunanet-afs-check symbols afs.sym
Ledger status: VALIDATED for the code and synchronisation checks, against the values the specification prints (which it marks TBC); one check is NOT MET, kept and pinned (one printed tertiary-code column differs: the document prints the same value for two PRNs). The LDPC and CRC words of subframes 2 to 4 are not checked. The ledger rows say exactly which. LunaNet AFS.
Lab round trip
kshana.test-conditions/1 is a published description of what a laboratory did (events, times,
powers). kshana evidence round-trip links the scenario hash, the conditions hash, the receiver log
hash and the score into one signed pack, and refuses a pack whose conditions and scoring scenario
disagree about the events. Kshana never produces or replays a signal: how a scenario reached a
receiver is the operator's responsibility.
kshana evidence --help
The picture is the layout of a pack. Ledger status: MODELLED. Test conditions, Test bench.
kshana-bench
Fixed tasks, splits and metrics for interference detection, IQ detection, holdover prediction and the optimism gap, with a sealed holdout and a static leaderboard. It scores submissions; it is not a ranking of receivers.
kshana bench baseline --task d --split test --out results/
Ledger status: no ledger row of its own; the baselines are labelled MODELLED in the result files. Against the pre-registered bars the scorer, C1 to C3 and D2 are met and D1 is NOT MET (pinned by a test). Bench.
Interference observatory
One product per UTC day and region from an open aircraft-position archive, on a fixed coarse grid: each cell is degraded, not degraded, or not observed (which is not "clear"). Released no earlier than 24 h after the day ends, with small cells suppressed and no attribution of cause.
kshana interference-map daily /path/to/extracted/2026-09-15 \
--dataset adsb-lol --day 2026-09-15 --bbox 53.5,9.0,66.0,31.0 --out products/
The picture is the committed sample days, not an observatory product. Ledger status: MODELLED: no external oracle, no ground truth and no cause attribution; a cell that is not observed was not assessed, which is not the same as clear. Observatory method.
Exporters
Trust telemetry as Prometheus text, CEF, LEEF, OTLP and Home Assistant MQTT discovery and state messages. The command publishes only to a destination you give it and holds no credentials.
kshana trust-telemetry --result session.result.json --print-metrics
kshana trust-telemetry --result session.result.json --print-syslog --format cef
Ledger status: MODELLED. Trust telemetry.
Optical link margin against time
The optical-atmosphere kind can write one seeded realisation of a site's weather as a link-margin
series: cloud episodes, a liquid-water column for each, one scintillation sample per step. The
Studio's Optical PNT page opens the optical-atmosphere scenarios.
kshana scenarios/optical-atmosphere-network.toml
Ledger status: MODELLED. One draw from the model, not the climate. Optical atmosphere.
New Studio views
kshana.dev/studio gains a Bridge replay (two runs side by side on one
mission clock, at /studio/replay/), an Optical PNT page
(/studio/optical/), a Holdover test page (/studio/holdover/) and an observatory viewer
(/studio/observatory/). They run the same engine in the browser, nothing is uploaded, and they carry
the same advisory statement. The pictures above are drawn from the engine's own output by a script;
they are not screenshots of these pages.
Space-user navigation (gnss-ssv)
For a spacecraft above, around or far past the GNSS orbits: how many GNSS signals it can use, how strong they are, how good the geometry is, and what an onboard filter can do with them. One kind, four modes, chosen by the scenario's tables: a shell of users (the share of time with one and four signals, per constellation), a trajectory (signals, C/N0, dilution of precision and the limb-ray medium along an orbit), a navigation run (a nine-state filter on pseudoranges, to the Moon) and a formation (relative navigation of deputies about a chief). The public source for the availability comparison is the interoperable GNSS Space Service Volume booklet (UN OOSA ST/SPACE/75, from the International Committee on GNSS), which is itself a simulation; it is fetched, never vendored. Full page: docs/SSV.md.
kshana scenarios/gnss-ssv-shell.toml # shell: the upper space service volume, reduced size
kshana scenarios/gnss-ssv-heo.toml # trajectory: a high Earth orbit, limb medium
kshana scenarios/gnss-ssv-nav-lunar.toml # navigation: an onboard filter toward the Moon
kshana scenarios/gnss-ssv-formation-heo.toml # formation: relative navigation
Ledger status: MODELLED, and the comparison with the booklet mostly misses. The bars were fixed before the run. Of eight, only S0 (the inverse link budget, 13 of 13 values) and S6 (the lunar mission) are met. S1 to S5 and S7 are NOT MET: the upper and lower service volume geometry (43 of 52 and 48 of 52 cells), the 15/20/25 dB-Hz upper service volume, the six GEO missions, the HEO mission, and the parametric sidelobe pattern against measured GPS panel patterns. Almost every miss in S1 to S5 involves GLONASS, and the booklet prints two different reference angles for it; a diagnostic variant that uses the other pair brings S1, S2 and S4 to every cell, but it does not replace the run of record and no row is VALIDATED. The formation run's absolute filter is also overconfident (mean normalised error squared well above the value of about 3 that a consistent filter gives).
Space-user navigation, what the comparison shows.
Lunar infrastructure (lunar-infrastructure)
The Moon's orbit and surface as navigation and geodetic infrastructure, in five studies chosen by
study: od-consistency (an onboard Earth-GNSS orbit determination in a halo orbit against an
independent ground one), carrier-phase (integer ambiguity fixing between surface stations and an
orbiter), frame-coestimation (the lunar datum from Earth-station ranging with and without orbiter
links), space-tie (laser, VLBI and GNSS offsets on one spacecraft against the local-tie error) and
geodesy-sensitivity (which observable family sees the Love numbers h2, k2 and l2). The model is a
restricted three-body one with stated simplifications and illustrative inputs. Full page:
docs/LUNAR-INFRASTRUCTURE.md.
kshana scenarios/lunar-infrastructure-od-consistency.toml
kshana scenarios/lunar-infrastructure-carrier-phase.toml # quick; the scored run takes about eight minutes
kshana scenarios/lunar-infrastructure-frame-coestimation.toml
kshana scenarios/lunar-infrastructure-space-tie.toml
kshana scenarios/lunar-infrastructure-geodesy-sensitivity.toml
Ledger status: MODELLED, and three bars are NOT MET. The numerics are checked against independent numpy and scipy programs; nothing is compared with a measurement of a lunar quantity, and nothing is VALIDATED. Met: the two orbit solutions are consistent (mean q 5.81 against 6 expected), and orbiter links raise the smallest datum eigenvalue about 111-fold, assuming a known orbit. Not met: the injected 3e-10 m/s2 model error is not detected (C3: the onboard solution is known to about 350 m, so about ten metres hides in its covariance); integer fixing is not achieved (1 to 3 % success at 25 dBi, and at the registered 12 dBi the problem cannot be formed); and the trace half of G3 for adding an observable family (the trace rose in 12 of 28 additions, each time because the family raised the rank). Each miss is pinned by a test. Lunar infrastructure.
Kshana Studio
Open kshana.dev/studio and the whole engine runs in your browser, compiled to WebAssembly. Nothing to install, and nothing is uploaded. The Studio has two views of the same engine, and a switch in the header moves between them, keeping the scenario, the view and any settings you changed.
The Simple view is where the Studio opens. It starts from questions (how many satellites can I see, can I trust my position fix, how long can a clock keep time, what does a jammer do to my receiver) and from a map of everything the engine can do, each area with its share of validated methods. Open a scenario and the answer comes first: one plain sentence, written for what that scenario measures, then its key figures and a chart. At most five settings are on show, the ones that change the answer most; the rest wait under Advanced settings. How this is computed explains the method, and For researchers gives the engine's own figures, the scenario file, the code to run it from Python and the citation.
The Advanced view is the full dashboard: the scenario library, five numbered steps (Choose, Set, Run, Read results, Share or export), the key figures with PASS and FAIL chips where the run states a threshold, and every panel: overview, charts, maps, replay, the engine's report and exports. One search box reaches scenarios, domains and the fields inside them, and you can pin up to four runs and compare them side by side.
The Studio's source and its build notes are in web/README.md.
The site
The rest of kshana.dev is built from this repository's own files and engine runs: missions, capabilities, the evidence ledger, the published research and the editions.
Capabilities
One engine covers the whole failure chain, from the first jammed satellite to the last
nanosecond of clock holdover. The engine has 87 scenario kinds; kshana kinds lists
them and docs/SCENARIOS.md documents every field.
The export formats in full: SP3 is Standard Product 3; CCSDS OMM and OEM are the Orbit Mean-elements and Orbit Ephemeris Messages of the Consultative Committee for Space Data Systems; CZML and KML are the Cesium and Keyhole Markup Languages; STK is Systems Tool Kit; SigMF is the Signal Metadata Format.
Run any of them with kshana example <scenario> > s.toml && kshana s.toml: the scenarios
are l-band-waterfall-jamming, constellation-multi-gnss-coverage, solar-system-tour
and leo-pass-iridium.
All four are made-up data and advisory: they show the format and the behaviour of the software, not how any receiver or system performs against interference. Run the first with kshana receiver-trust examples/maritime-trust/session.toml, the second with kshana route-exposure on the samples in examples/interference-map/, the third with kshana nmea-scenario scenarios/training/coastal-drag-off.toml --out drag.nmea (training use only: never feed it to live navigation equipment), and the fourth as in docs/EVIDENCE-PACKS.md.
The full domain-by-domain detail follows; for a per-capability maturity ledger see
docs/CAPABILITY.md and docs/VALIDATION.md.