Kshana · क्षण · l'instant précis
Un simulateur open source pour la résilience PNT (positionnement, navigation et synchronisation temporelle).
Version v0.40.0 176 capacités sur 401 vérifiées par rapport à des références externes indépendantes Environ 95 % de couverture de lignes, imposée à 85 % en intégration continue Licence : AGPL-3.0-only, ou une licence commerciale DOI 10.5281/zenodo.20528627
Répétez la minute où le GNSS s'éteint#
Kshana rejoue le brouillage, le leurrage et le holdover d'horloge lorsque les signaux GNSS (Global Navigation Satellite System) tombent, et vous dit combien de temps un système garde la synchronisation temporelle et la position dans son budget, et quelle horloge ou quel capteur procure le plus de marge. Voici le paquet WebAssembly : tout le moteur en JavaScript, dans le navigateur ou dans Node.js. Chaque exécution est reproductible bit pour bit à partir du scénario, de la graine et de la version du moteur.
Essayez d'abord sans rien installer : Kshana Studio exécute tout le moteur dans votre navigateur, compilé en WebAssembly, et ne téléverse rien.
Preuves#
176 sur 401 capacités vérifiées par rapport à des références externes indépendantes ; 221 honnêtement étiquetées modélisées, 4 détenues par un partenaire. Chaque capacité porte une étiquette dans un registre vérifié par machine : VALIDATED (une référence externe indépendante concorde : données réelles, implémentation indépendante ou vecteurs de référence publiés), MODELLED (cohérent en interne, et dit à voix haute) ou PARTNER (un partenaire matériel la détient). Parmi les vérifications : les 666 vecteurs SGP4 (Simplified General Perturbations 4) de l'AIAA (American Institute of Aeronautics and Astronautics) 2006-6753 à 4,12 mm, et le modèle de forces de Cowell à 0,08 m près par rapport à Orekit 12,2.
Installation#
npm install kshana
Usage#
Le paquet est un module ES (ECMAScript, JavaScript standard) avec une charge WebAssembly, conçu pour le navigateur. Initialisez-le une fois, puis appelez le moteur de façon synchrone. La seule différence entre le navigateur et Node.js est la manière dont le binaire WebAssembly est chargé.
Dans le navigateur (via un bundler, ou une page qui sert node_modules/kshana/), init() récupère kshana_bg.wasm à côté du module :
import init, { run, summary, chart_svg, version } from "kshana";
await init(); // fetch and compile the WebAssembly binary
Dans Node.js, init() échoue avec TypeError: fetch failed (le fetch de Node ne peut pas lire un fichier local), il faut donc lire le binaire sur le disque et le transmettre à initSync :
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import { initSync, run, summary, chart_svg, version } from "kshana";
const wasmPath = createRequire(import.meta.url).resolve("kshana/kshana_bg.wasm");
initSync({ module: readFileSync(wasmPath) }); // compile the WebAssembly binary
Enregistrez la version Node.js comme fichier .mjs (ou définissez "type": "module" dans votre package.json) pour que import fonctionne. À partir de là, les deux sont identiques :
// A complete scenario, not a sketch: scenarios/clock-holdover.toml without its comment
// lines. It runs 2 h, 10 min of GNSS then about 1.8 h with GNSS denied, and asks how long
// a strontium optical clock and a chip-scale atomic clock (CSAC) each hold time to within
// 20 ns. sigma_y(1s) is a clock's Allan deviation at an averaging time of one second;
// q_wf, the white-frequency-noise intensity, is its square.
const toml = `
seed = 42
threshold_ns = 20.0
[time]
step_s = 10.0
duration_s = 7200.0
[gnss]
windows = [
{ t0 = 0.0, t1 = 600.0, state = "nominal" },
{ t0 = 600.0, t1 = 7200.0, state = "denied" },
]
[clock_quantum]
id = "optical-sr-lattice"
provenance = "Strontium optical lattice clock, space-oriented goal sigma_y(1s)=1e-15 (Origlia/Schiller/Bongs et al., arXiv:1503.08457); q_wf=sigma_y(1s)^2; ground-demonstrator maturity, not flown; flicker/aging not modeled"
y0 = 5.0e-17
q_wf = 1.0e-30
q_rw = 0.0
[clock_classical]
id = "csac-sa45s"
provenance = "Microchip SA65 / SA.45s CSAC datasheet sigma_y(1s)=3e-10; q_wf=sigma_y(1s)^2; flicker/aging not modeled"
y0 = 5.0e-10
q_wf = 9.0e-20
q_rw = 0.0
`;
const result = JSON.parse(run(toml)); // the full result document
// timing_p95_ns: the 95th-percentile timing error over the GNSS outage, in nanoseconds
console.log(version(), result.classical.fom.timing_p95_ns);
console.log(summary(toml)); // the one-line result string
const svg = chart_svg(toml); // the same chart the command-line interface (CLI) writes
TypeScript voit des résultats typés : run_typed(toml) renvoie un RunOutput (json, svg, summary, csv: string | null, data: ResultDocument), scenario_kinds() un KindMeta[] dont le name est l'union ScenarioKind de tous les types de scénario, et error_kind() un ErrorKind (ou "" en cas de succès). Les champs propres à chaque document de résultat varient selon le type de scénario ; ResultDocument type l'enveloppe commune (schema_version, engine_version, et le bloc units de FieldUnit).
import { run_typed, scenario_kinds, type ScenarioKind } from "kshana";
const out = run_typed(toml);
const units = out.data.units; // Record<string, FieldUnit>
const kinds: ScenarioKind[] = scenario_kinds().map((k) => k.name);
Quatre autres fonctions décrivent le moteur à une interface. kind_modules() renvoie un objet JSON allant du type de scénario aux modules que nomment les lignes de sa matrice de vérification, field_importance() un autre allant du type de scénario aux champs de résultat qui répondent à sa question (métadonnées d'affichage ; le moteur ne les lit jamais), iq_signals() les noms de signaux qu'accepte iq_run_short, et iq_run_short(fs_hz, duration_s, signal, ids, dopplers_hz, cn0_dbhz, seed) une courte scène IQ synthétique, acquise et poursuivie en mémoire (au plus 0,25 s, 1 000 000 échantillons et 8 satellites), avec la même réponse que la version native.
import { list_examples, get_example, export_scenario, animate_scenario } from "kshana";
const names = JSON.parse(list_examples("")).scenarios.map((s) => s.name); // the bundled scenarios
const toml = get_example("orbit-gnss-challenged"); // its TOML, byte for byte
const czml = JSON.parse(export_scenario(toml, "czml")).files[0].content; // a CZML file, in memory
const player = JSON.parse(animate_scenario(toml, "html", 0, NaN, 0)).files; // {name: html text}
Sur la face WebAssembly, chaque point d'entrée est un appel distinct : run (le document de résultat sous forme de chaîne JSON), summary, chart_svg, table_csv (la table CSV (valeurs séparées par des virgules) du scénario sous forme de chaîne, ou undefined pour les types de scénario qui ne publient aucune table ; elle lève une exception sur un scénario invalide), run_all (une exécution du moteur renvoyant {json, svg, summary, csv} sous forme de chaîne JSON — à utiliser lorsque vous voulez plus d'une sortie, puisque chaque autre appel réexécute le scénario à neuf), run_typed (les mêmes sorties sous forme d'un seul objet typé, avec le document déjà analysé comme data), version, list_kinds / scenario_kinds / error_kind (introspection ; scenario_kinds renvoie la liste analysée), encode_permalink / decode_permalink — le codec de lien partageable (URL, uniform resource locator) que Kshana Studio utilise pour faire l'aller-retour d'un scénario entier par le fragment de la barre d'adresse — et export_sp3 / export_omm / export_oem, les artefacts d'éphéméride SP3-c (révision c du format Standard Product 3) et CCSDS (Consultative Committee for Space Data Systems) — OMM, l'Orbit Mean-elements Message, et OEM, l'Orbit Ephemeris Message — que la ligne de commande écrit — et receiver_trust, qui évalue un journal de récepteur réel décrit par un scénario receiver-trust et renvoie {json, csv, svg, summary} sous forme de chaîne JSON (le navigateur n'a pas de système de fichiers, de sorte que le journal, et tout fichier de navigation, doivent figurer en ligne dans le scénario sous forme de text ou de base64 ; une table [platform] kind = "vessel" ajoute les moniteurs maritimes et le score de confiance de 0-100, à titre consultatif seulement), export_formats / export_scenario (quels formats d'interopérabilité un scénario prend en charge, sans l'exécuter, et une exportation en mémoire : czml, kml, geojson, stk ou sigmf comme JSON {format, spec_url, files}, identique octet pour octet pour le même scénario, rien n'est téléversé), import_route (un itinéraire GeoJSON écrit dans un scénario à trajectoire de points de cheminement, le --import-route de la ligne de commande), animate_scenario (la série temporelle de l'exécution sous forme de SVG animé, de lecteur HTML unique ou d'images SVG), list_examples / get_example (les scénarios de référence fournis et leur TOML), evidence_attach_timestamp (lie un jeton RFC 3161 à un paquet de preuves en mémoire ; le paquet doit toujours se vérifier, et la signature de l'autorité d'horodatage n'est pas vérifiée), osnma_verify (quelles données de navigation Galileo de quels satellites un fichier de pages I/NAV ou un flux UBX authentifie, avec la racine de Merkle ou les clés publiques de confiance et un temps de référence indépendant facultatif ; il authentifie des données et ne revendique aucune performance anti-leurrage ; rien n'est téléversé), receiver_trust_replay (un extrait NMEA borné évalué comme le mode actif l'évalue ; sans filtre), assess_vessel_log (le journal NMEA d'un navire comme exécution par lots), trust_telemetry (un flux ou résultat de confiance sous forme de texte Prometheus, de lignes syslog CEF ou LEEF ou d'une charge OTLP ; rien n'est envoyé), evidence_verify (vérifie un paquet de preuves signé ; rien n'est téléversé ; les paquets se font avec la CLI, Python ou MCP), bench_export (le mouvement et les événements d'un scénario sous forme de texte pour un simulateur de laboratoire ; aucun signal n'est écrit), compliance_report et compliance_mapping (quelles lignes d'exigences d'un cadre public les documents de résultat apportent des éléments de preuve en faveur de, avec l'écart de chaque ligne ; non un constat qu'un cadre est atteint), et interference_map (du texte CSV ADS-B ou AIS vers un GeoJSON kshana-interference-map/v1 par jour UTC ; agrégat seulement), route_exposure (la part d'un itinéraire traversant des cellules dégradées de ces cartes ; non une prévision), nmea_training (du NMEA de passerelle synthétique et un journal de l'instructeur pour l'entraînement des équipages ; texte seulement, jamais pour les systèmes de navigation actifs d'un navire) et les fonctions de géométrie du ciel sky_geometry, sky_monte_carlo, skyline_presets, skyline_profile et skyplot_svg (quels satellites un site voit lorsque des bâtiments, des quais ou le relief masquent une partie du ciel, avec la dilution de précision, l'ellipse d'erreur horizontale, un nuage de Monte-Carlo généré avec une graine et un diagramme du ciel en texte SVG ; la décision de visibilité et l'ellipse sont vérifiées par rapport à des bibliothèques indépendantes, les préréglages sont des géométries représentatives et non des relevés, et une ligne d'horizon retire les satellites en visibilité directe et rien de plus). Absents de la version navigateur : receiver-trust live avec son filtre et son écouteur TCP, le NMEA d'entraînement en flux continu, et les exportateurs de télémétrie (Prometheus, OpenTelemetry, syslog) sont des processus réseau de longue durée et restent dans l'outil en ligne de commande ; la création d'un paquet de preuves exige une clé de signature, qui relève de la CLI ou de Python. Il n'y a pas ici d'appel unique run_full ; cette liaison n'existe que sur la roue Python.
Chaque capacité de la matrice de vérification est étiquetée validée (vérifiée par rapport à une référence externe indépendante), modélisée ou détenue par un partenaire ; les valeurs des horloges optiques sont des objectifs spatiaux sur du matériel au sol (aucune horloge optique au strontium n'a volé). La maturité n'est pas uniforme selon les domaines — le PNT terrestre est validé par rapport à des données réelles ; la navigation en espace lointain et sur Mars est modélisée, seuls ses éléments constitutifs (temps de lumière, positions des planètes) étant validés par rapport à des références externes ; la détermination d'orbite (OD) de missions réelles en espace lointain figure sur la feuille de route.
Capacités#
Le moteur compte 92 types de scénario ; kshana kinds les liste avec leurs champs. Nouveaux dans la version 0.38.0 : gnss-ssv (signaux GNSS pour des véhicules spatiaux au-dessus, autour et au-delà des orbites GNSS) et lunar-infrastructure (orbite et surface lunaires comme infrastructure de navigation et géodésique), tous deux MODELLED, et holdover-test (un enregistrement d'erreur de temps comparé aux masques de l'ITU-T).
- Spectre : comment un brouilleur prend la bande L du GNSS, bande par bande.
- Horloges et synchronisation temporelle : holdover par rapport à un seuil, transfert de temps, et holdover télécom par rapport aux masques du secteur de la normalisation de l'Union internationale des télécommunications (UIT-T).
- Constellations autour de n'importe quel corps : couverture, dilution de précision et disponibilité autour de la Terre, de la Lune ou de Mars, et le système solaire à une époque.
- Navigation en orbite terrestre basse : signal, passage et liaison, message de navigation, et position fusionnée, étape par étape.
- Campagnes : une mission enchaînée, un balayage ou un ensemble de Monte-Carlo dans un seul scénario.
- Rapports, animation et exportations : chaque valeur avec son unité et son étiquette ; un dessin animé d'une exécution ; des fichiers SP3 (Standard Product 3), des messages d'orbite CCSDS (Consultative Committee for Space Data Systems), CZML (Cesium Language), KML (Keyhole Markup Language), GeoJSON, STK (Systems Tool Kit) et SigMF (Signal Metadata Format).
Architecture#
Le même fichier de scénario donne les mêmes octets sur chaque interface. MCP est le Model Context Protocol : kshana-mcp permet à un assistant d'IA (intelligence artificielle) d'exécuter le moteur.
Recherche#
Six articles sur arXiv reposent sur le moteur ouvert, chacun avec la commande qui le sous-tend : arXiv:2606,22054 (l'écart d'optimisme des détecteurs d'interférence), arXiv:2606,24210 (un niveau de protection conditionnel de la synchronisation temporelle), arXiv:2607,05415 (la stabilité d'un score de résilience PNT), arXiv:2607,02566 (l'observabilité des stations de surface lunaire par interférométrie à très longue base), arXiv:2607,06212 (géométrie polaire sud lunaire et balises de surface) et arXiv:2610,23871 (la navigation cislunaire comme problème d'observabilité : longueur d'arc, conditionnement du gramien et effet de levier Doppler). Voir Recherche.
Éditions#
Tout le moteur est libre sous l'AGPL-3.0. Kshana Pro, une surcouche propriétaire qui dépend du moteur ouvert et ne le fork jamais, et des études sur mesure sont disponibles sous contrat auprès d'Ashforde® OÜ : contact@ashforde.org. Il n'y a pas de tarifs ; voir docs/PRO.md.
En savoir plus#
- Le README complet → https://github.com/AshfordeOU/kshana
- Kshana Studio (le moteur dans votre navigateur, en WebAssembly) → https://kshana.dev
- Capacités → docs/CAPABILITY.md
- Validation et provenance → docs/VALIDATION.md · docs/PROVENANCE.md
- Matrice de vérification → docs/VERIFICATION-MATRIX.md
- Citer → CITATION.cff · DOI 10.5281/zenodo.20528627
Licence#
Libre et open source sous la GNU AGPL-3.0-only (la GNU Affero General Public License, version 3 seulement). Une licence commerciale est disponible auprès d'Ashforde® OÜ (une société estonienne à responsabilité limitée ; OÜ = osaühing) pour une intégration fermée ; voir LICENSING.md. Développé et maintenu de façon professionnelle par Ashforde® OÜ.