kshana 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <strong>क्षण</strong> — Sanskrit for <em>the precise instant</em>, the smallest measure of time.<br>
9
- Open, reproducible hybrid quantum / classical PNT performance simulation.
9
+ Open, reproducible PNT-resilience simulation with published quantum-sensor performance models.
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -16,34 +16,47 @@
16
16
  <a href="https://github.com/ashfordeOU/kshana/releases"><img src="https://img.shields.io/github/v/release/ashfordeOU/kshana?sort=semver" alt="Release"></a>
17
17
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache-2.0"></a>
18
18
  <a href="Cargo.toml"><img src="https://img.shields.io/badge/rust-1.75%2B-orange.svg" alt="Rust 1.75+"></a>
19
- <a href="https://doi.org/10.5281/zenodo.20528627"><img src="https://zenodo.org/badge/1256508460.svg" alt="DOI"></a>
19
+ <a href="https://doi.org/10.5281/zenodo.20528627"><img src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.20528627-blue.svg" alt="DOI 10.5281/zenodo.20528627"></a>
20
20
  </p>
21
21
 
22
22
  <p align="center">
23
23
  <strong>Kshana</strong> (क्षण, Sanskrit: <em>"the precise instant"</em>) is an open, reproducible
24
24
  <strong>PNT-resilience simulator with quantum-sensor performance models</strong> —
25
- positioning, navigation, and timing. It compares quantum and classical sensors using
26
- published Allan/noise-budget coefficients; it is not a first-principles quantum-physics
27
- simulator (see <a href="docs/QUANTUM-MODELS.md">docs/QUANTUM-MODELS.md</a>).
25
+ positioning, navigation, and timing. It compares quantum and classical sensors mostly
26
+ from published Allan/noise-budget coefficients, with a first-principles cold-atom-
27
+ interferometer accelerometer layer (Mach–Zehnder phase, quantum projection noise,
28
+ contrast decay, and vibration coupling) that <em>derives</em> the noise coefficient
29
+ rather than looking it up; it is not yet a full quantum-physics simulator (Coriolis and
30
+ light-shift systematics remain coefficient-level — see
31
+ <a href="docs/QUANTUM.md">docs/QUANTUM.md</a> and
32
+ <a href="docs/QUANTUM-MODELS.md">docs/QUANTUM-MODELS.md</a>).
28
33
  </p>
29
34
 
30
35
  It quantifies, in hard and reproducible numbers, what quantum clocks, quantum
31
36
  inertial sensors, and optical time-transfer buy a navigation system over classical
32
37
  PNT — scored against the operational figures of merit that matter for resilient
33
38
  navigation. Every result is reproducible from `scenario + seed + engine version`,
34
- and every sensor parameter is traceable to a published source.
39
+ and every sensor parameter is traceable to a published source — consolidated in one
40
+ citable table in [`docs/PROVENANCE.md`](docs/PROVENANCE.md).
35
41
 
36
42
  *Free and open source under Apache-2.0, professionally developed and maintained by
37
43
  Ashforde OÜ — commercial support, integration, and proprietary extensions available.*
38
44
 
39
- > **Status: v0.10.0 · a simulation substrate, not yet a product.** A validated,
40
- > fully reproducible engine spanning the PNT stack — orbit geometry, inertial
41
- > navigation, GNSS/INS fusion, integrity, clocks, and timing. Honest by design:
42
- > every figure of merit is labelled *validated* or *not-modeled*, and optical-clock
43
- > figures are space goals on ground hardware (no strontium optical clock has flown).
45
+ > **Status: v0.12.0 · a simulation substrate, not yet a product.** A validated,
46
+ > fully reproducible engine spanning the PNT stack — orbit geometry and constellation
47
+ > design, a numerical (Cowell) propagator with a six-perturbation force model, maneuver
48
+ > and trajectory design, time systems, inertial navigation (incl. map-aided and
49
+ > gravity-map-matching alt-PNT), GNSS/INS fusion (loose, tight, UKF, coupled
50
+ > clock+position, 17-state), orbit determination, ARAIM integrity, clocks, advanced
51
+ > time-and-frequency transfer, the GNSS measurement domain, and resilience (jamming +
52
+ > multi-layer spoofing).
53
+ > Honest by design: every figure of merit is labelled *validated* or *not-modeled*, and
54
+ > optical-clock figures are space goals on ground hardware (no strontium optical clock has flown).
44
55
  > See **[Capabilities](#capabilities)** for what it does, **[What it is / is not](#what-it-is--is-not)**
45
56
  > for scope, and [`docs/CAPABILITY.md`](docs/CAPABILITY.md) / [`docs/VALIDATION.md`](docs/VALIDATION.md)
46
- > for per-capability maturity and the claims table.
57
+ > for per-capability maturity. The overclaim closure ledger
58
+ > [`docs/CLAIMS-VS-REALITY.md`](docs/CLAIMS-VS-REALITY.md) tracks every historical overclaim,
59
+ > how it was resolved, and a CI guard (`tests/no_overclaims.rs`) that keeps it resolved.
47
60
 
48
61
  > **Try it in your browser:** the [playground](web/) runs the engine client-side as
49
62
  > WebAssembly — pick a scenario, edit the parameters, and see the result, with nothing
@@ -97,17 +110,34 @@ receiver, or a certified avionics product. Quantum-hardware fidelity comes from
97
110
  published error models, not from this tool. The granular maturity of each
98
111
  capability is documented in [`docs/CAPABILITY.md`](docs/CAPABILITY.md).
99
112
 
113
+ **It is not (yet):** a *full* atom-interferometry physics engine (most quantum sensors
114
+ consume published Allan/noise-budget coefficients; the CAI accelerometer has a
115
+ first-principles layer — Mach–Zehnder phase, projection noise, contrast decay, and
116
+ vibration coupling — but Coriolis and light-shift systematics remain a **P2** roadmap
117
+ layer, see [`ROADMAP.md`](ROADMAP.md) and [`docs/QUANTUM-MODELS.md`](docs/QUANTUM-MODELS.md));
118
+ a GNSS receiver or PVT solver (it models the measurement domain and resilience, not
119
+ signal acquisition or a least-squares fix); or a mission-design / orbit-determination
120
+ tool. Owning this scope is deliberate. If you need first-principles cold-atom
121
+ interferometer error budgets (e.g. CARIOQA-PMP-grade or X-37B-style validation), see
122
+ the P2 roadmap and [get in touch](#support--professional-services) to collaborate.
123
+
100
124
  ## Capabilities
101
125
 
102
126
  | Domain | Capability |
103
127
  |--------|------------|
104
- | **Orbit & geometry** | SGP4/SDP4 propagation (validated to 4.12 mm against all 666 AIAA 2006-6753 vectors), real-TLE or synthetic Walker constellations, multi-constellation visibility, dilution of precision, and GNSS availability. |
105
- | **Inertial** | Three-axis strapdown INS quaternion attitude, WGS-84 NED mechanization, coning/sculling compensation, and a deterministic IMU error model (scale-factor, misalignment, g-sensitivity, quantization, drift). |
106
- | **Fusion** | Loosely-coupled GNSS/INS error-state EKF (15-state) with closed-loop feedback that coasts through GNSS outages on a calibrated inertial solution. |
107
- | **Integrity** | Snapshot and solution-separation (ARAIM-style) RAIM with horizontal/vertical protection levels (HPL/VPL) including levels solved from an explicit ARAIM integrity-risk (P_HMI) budgetfault detection and identification, and Stanford integrity diagrams. |
108
- | **Clock & timing** | Two-state Kalman holdover, Allan-family stability (ADEV/MDEV/TDEV/HDEV) with confidence intervals, and optical/RF two-way time transfer. |
109
- | **Interoperability** | RINEX-3 multi-GNSS broadcast-ephemeris ingestion (GPS, Galileo, QZSS, BeiDou MEO/IGSO via IS-GPS-200; GLONASS via PZ-90 state-vector RK4 propagation), usable as a constellation source that drives a scenario directly (RINEX in, PNT geometry out); RINEX-3/4 observation-file parser (pseudorange, carrier phase, Doppler, and signal strength by observation code); SP3-c/d precise-ephemeris reader/writer for IGS/analysis-centre orbit products, with 9th-order Lagrange interpolation that turns a precise-orbit file into a propagation source; CCSDS OEM 2.0 (Orbit Ephemeris Message) writer that exports a propagated constellation in the standard format flight-dynamics tools (GMAT, Orekit, STK) ingest. |
110
- | **Resilience** | Clock-aided spoof-detectability analysis against a configurable time-spoof attack. |
128
+ | **Orbit & geometry** | SGP4/SDP4 propagation (validated to 4.12 mm against all 666 AIAA 2006-6753 vectors); real two-line elements (a committed, date-stamped Celestrak `gps-ops` snapshot) or synthetic Walker-delta constellations whose mean elements realise the `i:T/P/F` formula to under 1 km over a 24 h propagation; multi-constellation visibility, dilution of precision, and GNSS availability; a gradient-free constellation-design optimiser, streets-of-coverage minimum-satellite sizing, a multi-constellation comparison tool, and a Walker **design sweep** that tabulates coverage / PDOP / revisit-time over a planes × satellites grid and reports the Pareto-optimal designs. |
129
+ | **Numerical propagator** | A **Cowell** numerical propagator (`src/propagator.rs`) complementing the analytic SGP4/SDP4 path, with a hierarchical **six-perturbation** force model (`src/forces.rs`): two-body + the full **J2–J6 zonal** field (the exact analytic gradient of its disturbing potential), **epoch-driven Sun and Moon third-body** gravity (a built-in low-precision ephemeris, no DE/SPK kernel), **solar-radiation pressure** (cannonball model with a conical umbra+penumbra shadow), **atmospheric drag** (Vallado piecewise-exponential density, co-rotating atmosphere), and the **post-Newtonian Schwarzschild relativistic correction** — driven by a choice of two adaptive integrators (RK4 step-doubling or the **Dormand–Prince RK5(4)** embedded pair). Validated against analytic truth stronger than a cross-tool would give: the unperturbed orbit matches the exact universal-variable Kepler solution to **sub-metre over 24 h**, energy/angular-momentum conserve to ~1e-9, and each perturbation matches a hand-derived closed-form signature. |
130
+ | **Maneuvers & trajectory design** | Impulsive ΔV nodes with 6×6 covariance propagation (ECI / LVLH execution-error frames), finite-burn integration checked against the closed-form **Tsiolkovsky** rocket equation to < 0.01 %, an **Izzo-2015** single-revolution **Lambert** solver, an exact universal-variable **Kepler** propagator, and a **porkchop** (launch × arrival) C3 / arrival-V∞ sweep emitted as a JSON contour grid — the performance-simulation layer above GMAT/Orekit, with every Lambert output round-tripped against two-body truth and the porkchop minimum checked against the analytic Hohmann floor. |
131
+ | **Time systems** | IERS leap-second **UTC / TAI / TT / UT1** scales, a Julian-date API, the IAU-2000 **Earth Rotation Angle**, and GMST-based **TEME ECEF** with WGS-84 geodetic frames plus IAU 2006 precession (Fukushima–Williams) toward an ITRF-precise reduction. |
132
+ | **Inertial** | Three-axis strapdown INS — quaternion attitude, WGS-84 NED mechanization, coning/sculling compensation, and a deterministic IMU error model (scale-factor, misalignment, g-sensitivity, quantization, drift); a **first-principles cold-atom-interferometer accelerometer** (Mach–Zehnder phase, quantum projection noise, contrast decay, vibration coupling) that *derives* the velocity-random-walk coefficient; and a sequential-importance-resampling **particle filter** for map-aided (terrain-/gravity-referenced) GPS-denied navigation. |
133
+ | **Gravity-map / alt-PNT** | A cold-atom **gravimeter measurement model** whose white-noise floor ( = ASD/√τ`) is derived from the CAI accelerometer physics; a low-degree, fully-normalised **spherical-harmonic gravity-anomaly field** (validated against the closed-form Legendre functions and a hand-derived single-term anomaly) plus synthetic mascons; and a **gravity-map-matching particle filter** that recovers a GPS-denied track from the anomaly sequence it flies through. A **60-minute GPS-denied benchmark** (a ~700 km / one-hour outage where the inertial solution drifts to ~70 km) is recovered to **~145 m (< 500 m)** by a hierarchical coarse-to-fine matcher the ESA NAVISP *Quantum Wayfarer* target. |
134
+ | **Fusion** | Loosely-coupled 15-state GNSS/INS error-state EKF with closed-loop feedback (the `gnss-ins` pack); a **tightly-coupled** pseudorange update that keeps correcting with fewer than four satellites; a coupled **clock + position** filter; a general **unscented (sigma-point) Kalman** estimator for strongly nonlinear measurements; a tightly-coupled GNSS/INS **UKF navigator** (pseudorange + Doppler) whose force-model orbital coast is validated to **0.77 m RMS** over a 30-minute curving LEO pass that includes a 120-second GNSS outage; and a full **17-state tightly-coupled GNSS/INS UKF** (position, velocity, attitude error, accelerometer and gyro biases, clock bias and drift) whose **quantum-CAI dead-reckoning** coasts a 120-second outage on the cold-atom accelerometer's derived velocity-random-walk. |
135
+ | **Orbit determination** | Recovery of an orbital state `[r, v]` from ground-station range tracking, composing the two-body + J2 force model and RK4 integrator with a **Gauss–Newton batch** corrector (`determine_orbit_batch`, sub-metre / mm·s⁻¹ from noiseless ranges, ~2 m at a 5 m noise floor) and a **sequential** unscented-filter variant (`determine_orbit_sequential`). |
136
+ | **Integrity** | Snapshot and solution-separation (ARAIM-style) RAIM with horizontal/vertical protection levels (HPL/VPL), fault detection & exclusion, and Stanford integrity diagrams; an explicit integrity-risk-budget (**MHSS**) protection level, including the **dual-/multi-constellation constellation-wide fault mode** (EU ARAIM / DO-316). |
137
+ | **Clock & timing** | Two-state Kalman holdover (Joseph-form covariance, NIS/NEES consistency health); Allan-family stability (ADEV / MDEV / TDEV / HDEV) with noise-type-specific confidence intervals and a full **IEEE-1139 five-coefficient power-law fit**; geometric corrections (Sagnac, GNSS common-view); and the operational transfer methods — **TWSTFT** with the BIPM Sagnac closed form, **GNSS common-view**, **PPP** ionosphere-free time transfer, a free-space **optical** link with turbulence scintillation, and an inverse-variance **clock-ensemble (paper) timescale** below the best contributing clock. |
138
+ | **GNSS measurement domain** | Forward pseudorange / Doppler synthesis with **Klobuchar** (broadcast) and **IONEX / TEC-grid** (measured) ionosphere — including an IONEX file parser, time interpolation between maps, and the thin-shell slant-obliquity mapping — **Saastamoinen + Niell** troposphere, and snapshot RAIM (HPL/VPL). |
139
+ | **Resilience** | Link-budget **jamming** (J/S → effective C/N₀ → loss of lock); a stochastic **time-spoof detector** (Neyman–Pearson / χ²₁ energy test with closed-form and Monte-Carlo P_fa/P_md and a Security FoM of 1 − P_md); and a **multi-layer spoof detector** fusing a RAIM-consistency parity test (with the common-mode blind spot modelled honestly), an RF AGC-power monitor, and a signal-quality (SQM early-minus-late) monitor. |
140
+ | **Interoperability** | **RINEX-3** multi-GNSS broadcast-ephemeris ingestion (GPS, Galileo, QZSS, BeiDou MEO/IGSO via IS-GPS-200; GLONASS via PZ-90 state-vector RK4) usable as a constellation source (RINEX in, PNT geometry out); a **RINEX-3/4** observation parser (pseudorange, carrier phase, Doppler, signal strength); an **SP3-c/d** precise-ephemeris reader/writer with 9th-order Lagrange interpolation; and **CCSDS OEM 2.0 + OMM** (mean-elements) export for flight-dynamics tools (GMAT, Orekit, STK). |
111
141
 
112
142
  Each capability is reachable as a Rust API, a runnable scenario `kind`, or both.
113
143
  Maturity per capability — *validated*, *runnable*, or *library* — is tracked in
@@ -148,7 +178,12 @@ The constellation can also be given as real two-line element sets. A *full* TLE
148
178
  (line 1 + line 2) is propagated with the full **SGP4/SDP4** model — including
149
179
  atmospheric drag and the deep-space lunar-solar and 12 h / 24 h resonance terms that
150
180
  matter for ~12 h GNSS orbits — validated against the official AIAA 2006-6753 vectors
151
- to a worst-case ≈ 4 mm (`scenarios/orbit-sgp4-gps.toml`). A line-2-only block keeps
181
+ to a worst-case ≈ 4 mm. `scenarios/orbit-sgp4-gps.toml` ships a **real Celestrak
182
+ `gps-ops` snapshot** of the operational GPS constellation (2021-07-28, 30 satellites)
183
+ and requires valid TLE checksums — two-line element sets are open data from the US
184
+ Space Force / 18th Space Defense Squadron catalogue, redistributed by Celestrak
185
+ (Dr T. S. Kelso, [celestrak.org](https://celestrak.org)); refresh with
186
+ `scripts/fetch_tles.sh`. A line-2-only block keeps
152
187
  the analytic two-body propagation (`scenarios/orbit-real-tle.toml`); the two forms can
153
188
  be mixed in one constellation. A constellation can equally be built from a block of
154
189
  **RINEX-3 GPS broadcast-ephemeris** records — the format a receiver decodes —
@@ -180,8 +215,19 @@ cargo run -- scenarios/orbit-gnss-challenged.toml
180
215
  cargo run -- scenarios/orbit-sgp4-gps.toml
181
216
  cargo run -- scenarios/orbit-rinex.toml
182
217
  cargo run -- scenarios/integrity-raim.toml
218
+
219
+ # Export a propagated constellation to an SP3-c precise-ephemeris file:
220
+ cargo run -- scenarios/orbit-sgp4-gps.toml --export-sp3 gps.sp3
183
221
  ```
184
222
 
223
+ **Interoperability role.** Kshana is the *performance-simulation* layer that sits
224
+ alongside the post-processing toolchain, not a replacement for it: feed its **RINEX**
225
+ output into RTKLIB or gLAB for a position solution, and use its **SP3** output as a
226
+ precise-orbit product for tools like Ginan — Kshana answers *what resilience a given
227
+ PNT architecture buys* before you have real signals, in formats those tools already
228
+ ingest (`--export-sp3`, or `export_sp3 = true` in an `orbit` scenario, writes
229
+ `<scenario>.sp3`).
230
+
185
231
  Example output (clock holdover — note the Integrity and Security figures of merit):
186
232
 
187
233
  ```
@@ -250,8 +296,9 @@ console.log(version(), result.classical.fom.timing_p95_ns);
250
296
 
251
297
  ## Scenario format
252
298
 
253
- Scenarios are declarative TOML. A top-level `kind` selects the pack
254
- (`clock` is the default if omitted; `inertial`, `timetransfer`, `hybrid`, `orbit`).
299
+ Scenarios are declarative TOML. A top-level `kind` selects the pack (`clock` is
300
+ the default if omitted; `inertial`, `timetransfer`, `hybrid`, `fusion`,
301
+ `gnss-ins`, `orbit`, `gnss-sim`, `integrity`, `spoof`, `jamming`, `sweep`, `sweep-nd`).
255
302
  Common fields: `seed`, a `[time]` grid, a `[gnss]` availability timeline (the outage
256
303
  driver), and per-sensor blocks with `provenance` strings citing the source of every
257
304
  figure. Example (clock):
@@ -294,9 +341,13 @@ quaternion attitude with coning/sculling compensation, a full NED mechanization
294
341
  (Earth-rate and transport-rate terms, WGS-84 Somigliana gravity), and a
295
342
  deterministic IMU error model in which **scale-factor, misalignment,
296
343
  g-sensitivity, quantization, and rate-ramp are modelled** (IEEE Std 952-1997
297
- §A.2; Groves 2013 §4.3). That 3-axis path is **not yet wired into the scenario
298
- pack/FoM** switching the pack over, with a loosely-coupled GNSS/INS filter, is
299
- the next inertial milestone. A
344
+ §A.2; Groves 2013 §4.3). That 3-axis path is now **wired into a runnable
345
+ loosely-coupled GNSS/INS pack** (`kind = "gnss-ins"`): a 15-state error-state EKF
346
+ disciplines the strapdown solution against noisy fixes while GNSS is up, then
347
+ coasts through the outage, reporting the fused horizontal error against the
348
+ open-loop free-INS coast. A **tightly-coupled pseudorange** update is also
349
+ available (it forms the innovation in the range domain, so it keeps correcting
350
+ with fewer than four satellites). A
300
351
  clock-holdover scenario may add `runs` (> 1) to run a **Monte Carlo ensemble** — each
301
352
  figure of merit is then reported as a mean with a 5th–95th-percentile spread and the
302
353
  chart shades the error confidence band (see `scenarios/clock-ensemble.toml`).
@@ -308,10 +359,39 @@ cross-covariance: this is a stacked pair of error budgets, **not** a true couple
308
359
  clock+position joint filter (cross-block covariance is a roadmap item). See
309
360
  `scenarios/fusion-pnt.toml`.
310
361
 
311
- A `spoof` scenario injects a ramping false-time spoof (an `[attack]` block with
312
- `start_s` and `rate_ns_per_s`) and runs each clock's integrity monitor, reporting
313
- whether and when the spoof is detected and whether it reaches the spec undetected a
314
- concrete demonstration of the Security figure of merit (see `scenarios/spoof-attack.toml`).
362
+ A `spoof` scenario injects a time-spoof one of four `[attack.shape]` kinds
363
+ (`linear_ramp`, `step_jump`, `meaconing`, `replay`; a bare `rate_ns_per_s` is still
364
+ accepted as a linear ramp) and runs each clock's spoof detector. The detector is a
365
+ two-sided **χ²₁ energy / Neyman–Pearson test** on the clock-aided monitor statistic:
366
+ the threshold is set from a target false-alarm budget `target_pfa`, and the
367
+ **missed-detection probability `P_md`** is reported both closed-form and by
368
+ Monte-Carlo (`mc_runs` trials per hypothesis — the two agree to a few ×1/√N). The
369
+ **Security figure of merit is `1 − P_md`** at the operationally-harmful (spec)
370
+ magnitude, so a quiet clock that catches a spec-sized spoof scores ≈ 1 and a noisy
371
+ one that often misses it scores lower (see `scenarios/spoof-attack.toml`,
372
+ `scenarios/spoof-meaconing.toml`).
373
+
374
+ A `gnss-sim` scenario is a **measurement-domain** simulation: for each visible
375
+ satellite it synthesises the pseudorange `ρ = geometric range + c·δt_rx − c·δt_sv +
376
+ I + T + noise + multipath` and the L1 Doppler, with the **Klobuchar** single-frequency
377
+ ionosphere (`[iono]`, IS-GPS-200 §20.3.3.5.2.5) and the **Saastamoinen** zenith
378
+ troposphere projected by the **Niell (1996)** mapping function (`[tropo]`). The
379
+ residuals feed **snapshot RAIM** for per-epoch HPL/VPL, and every satellite's
380
+ pseudorange, Doppler, C/N₀, and iono/tropo corrections are emitted in the JSON
381
+ `gnss_measurements` array. It is a forward simulator (it generates measurements from
382
+ a known truth), not a receiver/solver — a zero-noise run reproduces geometry plus the
383
+ corrections to sub-millimetre (see `scenarios/gnss-sim-raim.toml`).
384
+
385
+ A `jamming` scenario models RF interference as a **link budget**: a `[jammer]`
386
+ (ECEF position, transmit `power_dbw`, type) raises the jammer-to-signal ratio at a
387
+ `[receiver]` watching a Walker `[constellation]`. From the geometry (free-space
388
+ path loss and the per-direction receive-antenna gain) it computes each satellite's
389
+ `J/S`, the **effective C/N₀** via the standard anti-jam equation (despreading
390
+ processing gain × the spectral-separation factor `Q`; Kaplan & Hegarty §9.4), and
391
+ flags loss of lock below a configurable tracking threshold — reporting an
392
+ `availability_under_jamming` figure of merit. A 10 W broadband jammer at 1 km
393
+ denies the receiver entirely (J/S ≈ 72 dB); the same jammer at 100 km only
394
+ degrades the links (see `scenarios/jamming-demo.toml`).
315
395
 
316
396
  A `sweep` scenario runs a **trade study**: it varies one `parameter` (`threshold_ns`,
317
397
  `duration_s`, `quantum_q_wf`, or `classical_q_wf`) from `start` to `stop` over `steps`
@@ -319,6 +399,14 @@ points on a `lin` or `log` `scale`, records a `metric` (e.g. `holdover_s`) for b
319
399
  clocks, and charts the two curves. The base scenario goes under `[base]` (see
320
400
  `scenarios/sweep-clock-stability.toml`).
321
401
 
402
+ A `sweep-nd` scenario generalises this to **any pack and any number of axes**: it
403
+ varies dotted TOML keys of a `[base]` scenario (of any `kind`) over the Cartesian
404
+ product of `[[axes]]`, re-runs each grid node, and records `metrics` given as
405
+ dotted JSON paths into the result (e.g. `classical.fom.holdover_s`). It works for
406
+ every pack because it operates at the TOML/result boundary; native runs evaluate
407
+ the grid in parallel (no extra dependency, wasm falls back to sequential) and the
408
+ output is deterministic and row-major (see `scenarios/sweep-nd-inertial.toml`).
409
+
322
410
  An `orbit` scenario derives the `[gnss]` timeline from geometry instead of authoring
323
411
  it — give a `[user]` orbit, a `[constellation]`, an elevation `mask_deg`, and the two
324
412
  clock blocks. It also reports position accuracy from the satellite geometry; the
@@ -360,10 +448,13 @@ See `scenarios/` for one example of every kind.
360
448
  The result artifact is versioned, self-describing JSON: per-step time series, the
361
449
  scored figures of merit, the active model specs (with provenance), the seed, a
362
450
  **scenario hash** — so any chart can be reproduced from the file — and, for each clock,
363
- an `adev_curve` (`[{tau_s, adev, n_samples}]`): the overlapping Allan deviation across
364
- octave-spaced averaging times, the standard way to read a clock's stability. The
365
- browser playground renders it as a log-log "Clock stability (ADEV)" chart. (MDEV/TDEV/HDEV
366
- and confidence intervals are not yet computed roadmap.) Every field, with units and a
451
+ an `adev_curve` (`[{tau_s, adev, n_samples, noise, edf, ci_lo, ci_hi}]`): the overlapping
452
+ Allan deviation across octave-spaced averaging times the standard way to read a clock's
453
+ stability now with a **noise-type-specific 95% confidence band** per point (the record's
454
+ power-law type is identified from its modified-Allan slope, and the χ² interval uses the
455
+ matching NIST SP 1065 effective degrees of freedom). The browser playground renders it as a
456
+ log-log "Clock stability (ADEV)" chart. (MDEV, TDEV, and HDEV are available as library
457
+ estimators; the exported result curve is the overlapping ADEV.) Every field, with units and a
367
458
  source pointer, is documented in [`docs/SCHEMA.md`](docs/SCHEMA.md). The figures of
368
459
  merit follow the standard operational PNT figures of merit:
369
460
 
@@ -467,6 +558,7 @@ kshana/
467
558
  | [Glossary](docs/GLOSSARY.md) | everyone | plain-language definitions of every term |
468
559
  | [Architecture](docs/ARCHITECTURE.md) | developers / reviewers | module map, engine pipeline, dispatch, and diagrams |
469
560
  | [Validation status](docs/VALIDATION.md) | reviewers / citers | what is `validated` vs `not modeled`, with evidence |
561
+ | [Provenance](docs/PROVENANCE.md) | reviewers / citers | every sensor parameter, model, and dataset traced to its published source, in one citable table |
470
562
  | [Reproducibility &amp; provenance](docs/REPRODUCIBILITY.md) | reviewers / packagers | determinism guarantees, golden-pinning, SBOM, build provenance |
471
563
  | [Positioning](docs/POSITIONING.md) | evaluators | where Kshana sits vs RTKLIB/gLAB (complementary), and the zero-install browser tier |
472
564
  | [SGP4 validation](docs/SGP4-VALIDATION.md) | reviewers / citers | agreement with the AIAA 2006-6753 reference (666 states, ~4 mm) |
@@ -542,15 +634,25 @@ CPython versions).
542
634
 
543
635
  ## Roadmap
544
636
 
545
- See [`CHANGELOG.md`](CHANGELOG.md) for released history and the `[Unreleased]`
546
- section for what's next (Earth-fixed frame reduction — TEME&rarr;ECEF/ITRF and
547
- explicit time systems: UTC/UT1/TAI/TT with leap seconds). SGP4/SDP4 orbit
548
- propagation has **shipped** (v0.7.0, validated against the AIAA 2006-6753 vectors),
549
- and its inertial velocity is now exposed downstream. An active
550
- spoofing-attack demonstrator, multi-constellation availability, a single-axis (1-DOF)
637
+ See [`ROADMAP.md`](ROADMAP.md) for the phased roadmap, [`CHANGELOG.md`](CHANGELOG.md)
638
+ for released history, and [`docs/CAPABILITY.md`](docs/CAPABILITY.md) for the
639
+ per-capability roadmap. Near-term items include **ITRF-precise frame reduction**
640
+ (polar motion and sub-arcsecond nutation on top of the shipped GMST-based
641
+ TEME&harr;ECEF), two-part Julian dates, tightly-coupled carrier-phase fusion, and
642
+ surfacing the loosely-/tightly-coupled GNSS/INS navigator across more packs. The
643
+ **quantum physics layer** is a **P2** item: the CAI accelerometer is now simulated from
644
+ first principles (Mach–Zehnder phase, projection noise, contrast decay, vibration
645
+ coupling), while the clock/time-transfer sensors are still driven by published
646
+ Allan/noise-budget coefficients. GMST-based TEME&harr;ECEF, the IERS
647
+ leap-second time systems (UTC/TAI/TT/UT1), SGP4/SDP4 orbit propagation (v0.7.0,
648
+ validated against the AIAA 2006-6753 vectors), and the runnable `gnss-ins` fusion
649
+ pack have all **shipped**, and the inertial velocity is exposed downstream. An active
650
+ stochastic time-spoof detector (Neyman–Pearson / χ²₁ energy test with Monte-Carlo
651
+ P_fa/P_md and a Security FoM of 1−P_md), a link-budget jamming model (J/S → effective
652
+ C/N₀ → loss of lock), multi-constellation availability, a single-axis (1-DOF)
551
653
  IMU error budget, two independent (clock + position) Kalman estimators reported as a
552
- combined FoM, real constellation geometry from TLEs, an HTML scorecard report, a
553
- clock-stability-based spoof-detectability bound, geometry-derived GNSS availability
654
+ combined FoM, real constellation geometry from TLEs, an HTML scorecard report,
655
+ geometry-derived GNSS availability
554
656
  *and* dilution of precision from Keplerian orbits with eccentricity and J2 drift,
555
657
  Monte Carlo confidence bands, trade-study parameter sweeps, an in-browser WebAssembly
556
658
  playground, and optional Python (PyO3) and WebAssembly (wasm-bindgen) bindings have
@@ -568,12 +670,12 @@ entry for every user-visible change. Participation is governed by our
568
670
 
569
671
  If you use Kshana in academic or technical work, please cite it. Machine-readable
570
672
  metadata is in [`CITATION.cff`](CITATION.cff) (GitHub renders a "Cite this repository"
571
- button from it); cite the version you used (e.g. `v0.10.0`) together with the
673
+ button from it); cite the version you used (e.g. `v0.12.0`) together with the
572
674
  scenario and seed for full reproducibility. Every release is archived on Zenodo with
573
675
  a citable DOI — the concept DOI [10.5281/zenodo.20528627](https://doi.org/10.5281/zenodo.20528627)
574
676
  always resolves to the latest version.
575
677
 
576
- > Baweja, C. (2026). *Kshana — hybrid quantum/classical PNT performance simulator*. Ashforde OÜ. https://doi.org/10.5281/zenodo.20528627
678
+ > Baweja, C. (2026). *Kshana — a PNT-resilience simulator with quantum-sensor performance models*. Ashforde OÜ. https://doi.org/10.5281/zenodo.20528627
577
679
 
578
680
  ## Versioning & releases
579
681
 
package/kshana.d.ts CHANGED
@@ -6,6 +6,30 @@
6
6
  */
7
7
  export function chart_svg(toml: string): string;
8
8
 
9
+ /**
10
+ * Decode a permalink token back into the scenario TOML; returns an empty string if the
11
+ * token is not valid Base64 or not valid UTF-8.
12
+ */
13
+ export function decode_permalink(token: string): string;
14
+
15
+ /**
16
+ * Encode a scenario TOML into a URL-safe permalink token for a `?s=` query parameter.
17
+ */
18
+ export function encode_permalink(toml: string): string;
19
+
20
+ /**
21
+ * Run a scenario; on failure return the structured error *kind* tag
22
+ * (`invalid_input`, `unsupported`, …) so the caller can branch on the failure
23
+ * category rather than parse the message. Returns an empty string on success.
24
+ */
25
+ export function error_kind(toml: string): string;
26
+
27
+ /**
28
+ * List the available scenario kinds and their metadata as a JSON array (name,
29
+ * description, required and optional fields), for programmatic introspection.
30
+ */
31
+ export function list_kinds(): string;
32
+
9
33
  /**
10
34
  * Run a scenario given as a TOML string; returns the result document as a JSON
11
35
  * string. Throws a JS error if the scenario is invalid.
@@ -27,6 +51,10 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
27
51
  export interface InitOutput {
28
52
  readonly memory: WebAssembly.Memory;
29
53
  readonly chart_svg: (a: number, b: number) => [number, number, number, number];
54
+ readonly decode_permalink: (a: number, b: number) => [number, number];
55
+ readonly encode_permalink: (a: number, b: number) => [number, number];
56
+ readonly error_kind: (a: number, b: number) => [number, number];
57
+ readonly list_kinds: () => [number, number];
30
58
  readonly run: (a: number, b: number) => [number, number, number, number];
31
59
  readonly summary: (a: number, b: number) => [number, number, number, number];
32
60
  readonly version: () => [number, number];
package/kshana.js CHANGED
@@ -26,6 +26,87 @@ export function chart_svg(toml) {
26
26
  }
27
27
  }
28
28
 
29
+ /**
30
+ * Decode a permalink token back into the scenario TOML; returns an empty string if the
31
+ * token is not valid Base64 or not valid UTF-8.
32
+ * @param {string} token
33
+ * @returns {string}
34
+ */
35
+ export function decode_permalink(token) {
36
+ let deferred2_0;
37
+ let deferred2_1;
38
+ try {
39
+ const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
40
+ const len0 = WASM_VECTOR_LEN;
41
+ const ret = wasm.decode_permalink(ptr0, len0);
42
+ deferred2_0 = ret[0];
43
+ deferred2_1 = ret[1];
44
+ return getStringFromWasm0(ret[0], ret[1]);
45
+ } finally {
46
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Encode a scenario TOML into a URL-safe permalink token for a `?s=` query parameter.
52
+ * @param {string} toml
53
+ * @returns {string}
54
+ */
55
+ export function encode_permalink(toml) {
56
+ let deferred2_0;
57
+ let deferred2_1;
58
+ try {
59
+ const ptr0 = passStringToWasm0(toml, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
60
+ const len0 = WASM_VECTOR_LEN;
61
+ const ret = wasm.encode_permalink(ptr0, len0);
62
+ deferred2_0 = ret[0];
63
+ deferred2_1 = ret[1];
64
+ return getStringFromWasm0(ret[0], ret[1]);
65
+ } finally {
66
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Run a scenario; on failure return the structured error *kind* tag
72
+ * (`invalid_input`, `unsupported`, …) so the caller can branch on the failure
73
+ * category rather than parse the message. Returns an empty string on success.
74
+ * @param {string} toml
75
+ * @returns {string}
76
+ */
77
+ export function error_kind(toml) {
78
+ let deferred2_0;
79
+ let deferred2_1;
80
+ try {
81
+ const ptr0 = passStringToWasm0(toml, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
82
+ const len0 = WASM_VECTOR_LEN;
83
+ const ret = wasm.error_kind(ptr0, len0);
84
+ deferred2_0 = ret[0];
85
+ deferred2_1 = ret[1];
86
+ return getStringFromWasm0(ret[0], ret[1]);
87
+ } finally {
88
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * List the available scenario kinds and their metadata as a JSON array (name,
94
+ * description, required and optional fields), for programmatic introspection.
95
+ * @returns {string}
96
+ */
97
+ export function list_kinds() {
98
+ let deferred1_0;
99
+ let deferred1_1;
100
+ try {
101
+ const ret = wasm.list_kinds();
102
+ deferred1_0 = ret[0];
103
+ deferred1_1 = ret[1];
104
+ return getStringFromWasm0(ret[0], ret[1]);
105
+ } finally {
106
+ wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
107
+ }
108
+ }
109
+
29
110
  /**
30
111
  * Run a scenario given as a TOML string; returns the result document as a JSON
31
112
  * string. Throws a JS error if the scenario is invalid.
package/kshana_bg.wasm CHANGED
Binary file
package/package.json CHANGED
@@ -4,8 +4,8 @@
4
4
  "collaborators": [
5
5
  "Chakshu Baweja <contact@ashforde.org>"
6
6
  ],
7
- "description": "Open hybrid quantum/classical PNT performance simulator",
8
- "version": "0.10.0",
7
+ "description": "Open, reproducible PNT-resilience simulator with quantum-sensor performance models",
8
+ "version": "0.12.0",
9
9
  "license": "Apache-2.0",
10
10
  "repository": {
11
11
  "type": "git",