Traduction automatique, non relue par un locuteur natif. L'anglais fait foi.

Confiance du récepteur et maritimeModifier sur GitHubSource : docs/RECEIVER-TRUST.md

Confiance du récepteur : évaluer un véritable journal de récepteur GNSS

kshana receiver-trust lit un journal d'un véritable récepteur de système mondial de navigation par satellite (GNSS), exécute des moniteurs de confiance dessus époque par époque, et dit quand et pourquoi la position du récepteur a cessé d'être digne de confiance. Il peut aussi évaluer le journal par rapport à des événements et à des prédictions que vous énoncez avant l'exécution : si chaque événement a été détecté à temps, et si une chute modélisée de la densité de porteuse sur bruit (C/N0) concorde avec ce que le récepteur a mesuré.

Les observables sont ceux du récepteur lui-même ; les moniteurs et leurs seuils sont ceux du moteur et les vôtres. Chaque résultat porte cette étiquette, le SHA-256 du journal et chaque seuil avec lequel il a été exécuté, de sorte qu'une exécution peut être répétée et auditée.

Pour un navire en route, avec un score en direct et une passerelle facultative devant un système de cartographie, voir MARITIME-TRUST.md.

L'indicateur de leurrage propre du récepteur mis en regard des alarmes de Kshana, d'après une exécution réelle du moteur v0.40.0 sur un journal UBX fictif (200 époques à 1 Hz). Le C/N0 chute de 20 dB de 80 à 90 s et de 120 à 130 s ; le récepteur signale 120 à 130 s et 160 à 165 s. Sur 140 époques comparées après calibration, 11 avaient les deux en alerte, 6 le récepteur seul, 11 Kshana seul et 112 aucun. Aucun des deux côtés n'est la vérité terrain et l'accord n'est pas une validation. À titre consultatif : résultat d'analyse d'un outil de recherche et d'ingénierie, et non un équipement de navigation homologué ni un service de sauvegarde de la vie humaine.L'indicateur de leurrage propre du récepteur mis en regard des alarmes de Kshana, d'après une exécution réelle du moteur v0.40.0 sur un journal UBX fictif (200 époques à 1 Hz). Le C/N0 chute de 20 dB de 80 à 90 s et de 120 à 130 s ; le récepteur signale 120 à 130 s et 160 à 165 s. Sur 140 époques comparées après calibration, 11 avaient les deux en alerte, 6 le récepteur seul, 11 Kshana seul et 112 aucun. Aucun des deux côtés n'est la vérité terrain et l'accord n'est pas une validation. À titre consultatif : résultat d'analyse d'un outil de recherche et d'ingénierie, et non un équipement de navigation homologué ni un service de sauvegarde de la vie humaine.

L'exécuter#

kshana receiver-trust examples/receiver-trust/session.toml

Cela écrit session.result.json, session.trust.csv (une ligne par époque) et session.trust.svg (C/N0 moyen au fil du temps avec l'état de confiance en dessous) à côté du scénario, et affiche un verdict d'une ligne. Le journal d'exemple est synthétique, fait pour montrer le format ; pointez path vers le journal de votre propre récepteur.

Score de confiance sur un passage synthétique, issu d'une exécution réelle du moteur v0.40.0 sur examples/maritime-trust (un journal NMEA inventé d'un ferry sur une liaison Tallinn-Helsinki, 3001 époques à 1 Hz). Trois couloirs partagent un même axe des temps. En haut : la distance entre la position rapportée par le récepteur et la position réelle du navire, qui reste à quelques mètres près jusqu'à environ 1576 s, moment où l'entraînement de position scénarisé du journal l'écarte, tandis que le récepteur continue de rapporter un point de position valide. Au milieu : le score de confiance de 0 à 100, non calculé pendant les 300 premières secondes d'étalonnage, dans la bande nominal (au moins 90) jusqu'à l'entraînement, puis chutant à travers la bande degraded (au moins 55) pour atteindre la bande untrusted. En bas : la bande de chaque époque sous forme de ruban coloré portant son nom. Le score est à titre consultatif. Logiciel à titre consultatif, et non un équipement de navigation homologué (IEC 61108, IEC 61162) : l'opérateur reste responsable de la navigation du navire. Le journal est inventé pour montrer le format et les moniteurs ; ce n'est pas une mesure et il ne dit rien de la façon dont un récepteur se comporterait.Score de confiance sur un passage synthétique, issu d'une exécution réelle du moteur v0.40.0 sur examples/maritime-trust (un journal NMEA inventé d'un ferry sur une liaison Tallinn-Helsinki, 3001 époques à 1 Hz). Trois couloirs partagent un même axe des temps. En haut : la distance entre la position rapportée par le récepteur et la position réelle du navire, qui reste à quelques mètres près jusqu'à environ 1576 s, moment où l'entraînement de position scénarisé du journal l'écarte, tandis que le récepteur continue de rapporter un point de position valide. Au milieu : le score de confiance de 0 à 100, non calculé pendant les 300 premières secondes d'étalonnage, dans la bande nominal (au moins 90) jusqu'à l'entraînement, puis chutant à travers la bande degraded (au moins 55) pour atteindre la bande untrusted. En bas : la bande de chaque époque sous forme de ruban coloré portant son nom. Le score est à titre consultatif. Logiciel à titre consultatif, et non un équipement de navigation homologué (IEC 61108, IEC 61162) : l'opérateur reste responsable de la navigation du navire. Le journal est inventé pour montrer le format et les moniteurs ; ce n'est pas une mesure et il ne dit rien de la façon dont un récepteur se comporterait.

La figure ci-dessus est la démonstration pour navire de MARITIME-TRUST.md : un journal inventé, un score à titre consultatif.

Le même scénario s'exécute depuis Python (kshana.receiver_trust(toml)), depuis le paquet WebAssembly (receiver_trust(toml), avec le journal en ligne) et depuis l'outil assess_receiver_log du serveur Model Context Protocol (MCP).

Journaux qu'il lit#

format Source Ce qui est lu
ubx Binaire u-blox NAV-SAT : C/N0, élévation et azimut par satellite ; NAV-PVT : position ; MON-RF : commande automatique de gain (AGC) et indicateur de brouillage jamInd ; NAV-TIMEGPS : semaine ; et les indicateurs propres du récepteur : NAV-STATUS spoofDetState et SEC-SIG (self_report, voir RECEIVER-PROTOCOLS.md)
rinex Fichier d'observation RINEX 3 codes S (C/N0) par satellite et par signal ; avec un fichier de navigation diffusée (nav), les pseudodistances pour la position propre du moteur
android CSV d'Android GnssLogger lignes Raw : C/N0 et AgcDb par satellite ; lignes Fix : position
nmea Texte NMEA 0183 GSV : rapport signal sur bruit par satellite (dB-Hz, avec ou sans l'identifiant de signal 4,10) ainsi que son élévation et son azimut ; GSA : dilution de précision et satellites utilisés ; GGA et RMC : heure et position
sbf Binaire Septentrio (0,38, lecture seule) PVTGeodetic : position ; MeasEpoch : C/N0 par satellite et par signal ; RFStatus : nombre de bandes ; voir RECEIVER-PROTOCOLS.md
rtklib-pos Fichier de solution RTKLIB (.pos) heure, position (latitude/longitude ou ECEF, convertie) et satellites utilisés par époque ; les fichiers en degrés-minutes-secondes sont refusés avec un message
novatel Journaux ASCII ou binaires NovAtel OEM7 position BESTPOS (solution calculée seulement), C/No RANGE par satellite et par signal ; les indicateurs de brouilleur RXSTATUS deviennent le self_report de l'époque (état de brouillage 2 lorsque le récepteur signale un brouilleur, 1 sinon ; aucun indicateur de leurrage n'existe dans RXSTATUS), confrontés aux alarmes de Kshana par la comparaison receiver-self-report
gnss-sdr Flux GNSS-SDR Monitor et PVT-monitor sous forme de lignes JSON C/N0 par canal, position, satellites utilisés, HDOP, azimut et élévation ; produit par integrations/gnss-sdr/relay.py

Détails, sources et limites de ces trois : MACHINE-INPUTS.md.

Chaque époque que produisent les lecteurs porte aussi source_span, des décalages d'octets [start, end) dans l'entrée : la plus petite plage contiguë contenant les enregistrements qui y ont contribué, de sorte que la tranche brute exacte puisse être hachée pour une fenêtre d'événement. NMEA : chaque ligne lue tant que l'époque est courante (un sur-ensemble lorsqu'une ligne sans rapport se glisse entre ses phrases). UBX : les trames de l'époque. RINEX 3 : les enregistrements d'époque de chaque ligne > jusqu'à la suivante. Android : les lignes Raw de l'époque (une ligne Fix seulement pour une époque sans lignes Raw). Absent pour un flux en direct et partout où cela n'est pas bien défini (RINEX 2).

Le fichier entre par path (résolu par rapport au dossier propre du scénario), ou en ligne sous la forme text ou base64 (les seuls choix dans un navigateur).

Un journal d'IMU comme second capteur#

[log.imu] ajoute un journal inertiel sur la même horloge :

[log.imu]
format = "csv"          # or "euroc"
path = "imu.csv"        # or text / base64
heading0_deg = 90.0     # heading at the first IMU sample
yaw_axis_down = true    # body z down (NED/FRD); false for z up (ENU/FLU)
time_offset_s = 0.0     # added to the IMU clock to reach the log's t_s

La vitesse de lacet est intégrée à partir de heading0_deg et renseigne le cap partout où le journal du récepteur n'en porte aucun, de sorte que le moniteur heading-course (qui exige heading_sensor = true sur la plateforme) compare le cap gyroscopique avec la route sur le fond. Un gyroscope dérive : le cap n'est aussi bon que la fenêtre d'intégration, et hdg_cog_tol_deg décide de ce qui compte comme désaccord. La disposition csv est documentée dans MACHINE-INPUTS.md. Les octets de l'IMU font partie du hachage du scénario et du hachage de scénario d'un paquet de preuves. En ligne (text/base64), cela fonctionne sur toutes les surfaces ; un path est refusé là où les fichiers ne sont pas lus.

Plateforme : statique ou navire#

Les moniteurs ci-dessus supposent une antenne fixe. Un scénario énonce la plateforme avant l'exécution dans une table [platform] de premier niveau ; sans elle, la plateforme est static et rien ne change.

[platform]
kind = "vessel"            # "static" (default) or "vessel"
max_speed_kn = 30.0        # default 30
max_accel_mps2 = 0.5       # default 0.5
max_turn_rate_dps = 6.0    # default 6
antenna_height_m = 18.0    # no default: without it the sea-level monitor does not run
heading_sensor = false     # true: the stream carries a gyro heading (HDT or THS)
Clé Valeur par défaut Pourquoi cette valeur
max_speed_kn 30 Au-dessus de la vitesse de service des coques marchandes et de la plupart des navires à passagers, de sorte qu'un navire réel ne l'atteint jamais ; très en dessous de la vitesse qu'implique un entraînement de position. Un navire plus lent doit déclarer une limite plus basse : plus la limite est basse, plus tôt un entraînement est détecté.
max_accel_mps2 0.5 Un grand navire change de vitesse de bien moins de 0,1 m/s² ; un petit engin rapide atteint quelques dixièmes lors d'un changement brusque de régime.
max_turn_rate_dps 6 Un grand navire tourne à bien moins de 1 deg/s et un petit engin dans un virage serré à quelques-uns.
antenna_height_m aucun Une propriété de l'installation ; une estimation serait un seuil caché.
heading_sensor false Déclaré true et absent du journal est une erreur, de sorte qu'un capteur manquant ne puisse pas passer pour un contrôle propre.

Les clés de navire sont rejetées sous kind = "static". Pour un navire, le moniteur position-jump ne s'exécute pas, car il mesure la distance à la moyenne d'étalonnage, ce qui est faux sur une antenne en mouvement ; le monitor_config du résultat consigne la plateforme et les limites utilisées.

Les flux NMEA synthétiques de kshana nmea-scenario, conçus pour la formation des équipages, sont acceptés par le lecteur NMEA et peuvent être exécutés avec ce type de scénario comme n'importe quel journal NMEA.

Moniteurs#

Chaque moniteur a besoin de ses données ; un moniteur dont les données ne figurent pas dans le journal n'est pas exécuté et n'est pas listé dans monitors_run. Les calibration_s premières secondes (par défaut 60) fixent la référence de base et ne déclenchent jamais d'alarme.

Moniteur Alarme quand Nécessite
cn0-drop le C/N0 moyen sur les satellites communs avec la référence de base baisse de cn0_drop_db ou plus (par défaut 6 dB) C/N0
loss-of-lock les satellites suivis tombent de sats_lost ou plus sous la médiane de la référence de base (par défaut 4) C/N0
agc la valeur de l'AGC s'écarte de sa référence de base de plus de agc_k_sigma écarts types (par défaut 5) UBX MON-RF ou Android AgcDb
jam-ind jamInd du u-blox dépasse jam_ind_threshold (par défaut 80 sur 255) UBX MON-RF
position-jump la position propre du récepteur est à plus de position_jump_m de sa moyenne de calibration (par défaut 50 m) une position rapportée
raim la surveillance autonome de l'intégrité par le récepteur (RAIM) de parité, appliquée à la position à point unique propre au moteur, rejette l'ensemble de mesures journal RINEX avec nav
solve-failure la position à point unique du moteur échoue avec cinq satellites ou plus journal RINEX avec nav
clock l'horloge du récepteur s'écarte de sa droite prédite de plus du décalage minimal détectable de son bruit de calibration journal RINEX avec nav

Profil calibré (sur option). [monitors] profile = "jammertest-2024-calibrated" remplace les trois seuils d'interférence par des valeurs calibrées sur le jeu de données public JammerTest 2024 (un u-blox ZED-F9P par scénario ; Zenodo 15911589) : cn0_drop_db = 1.5, agc_k_sigma = 3.5, jam_ind_threshold = 29. La calibration a été préenregistrée (xval/jammertest-trust/PREREGISTRATION.md) et le profil est MODELLED, ajusté sur une seule campagne publique : son seuil cn0_drop_db a manqué le seuil de fausses alarmes préenregistré sur le jeu réservé (0,0231 contre 0,02) ; les seuils de l'AGC et de l'indicateur de brouillage ainsi que le seuil de l'union (fausses alarmes 0,033, détection 0,81) ont été atteints (xval/jammertest-trust/RESULTS.md). Il vaut pour ce type de récepteur lors de cet événement, face à des créneaux d'attaque planifiés, et a été calibré sur les journaux CSV du jeu de données (ses journaux UBX donnent plus de fausses alarmes dans deux dossiers). Un résultat produit avec ce profil rappelle les seuils appliqués et porte une profile_note qui dit tout cela. Sans le profil, les valeurs par défaut ci-dessus s'appliquent et toutes les sorties sont inchangées.

Pour une plateforme vessel, les moniteurs de plateforme mobile s'exécutent aussi (seuils dans une table [maritime] de premier niveau, chaque valeur par défaut documentée dans src/receiver_trust/maritime.rs) ; chacun a besoin de ses données et n'est listé dans monitors_run que lorsqu'il a décidé :

Moniteur Alarme quand Nécessite
kinematic le changement de position ne concorde pas avec la vitesse et la route rapportées, ou implique une vitesse, une accélération ou une vitesse de giration supérieure aux limites du navire positions ; vitesse et route (RMC ou VTG) pour la partie à l'estime
heading-course le cap gyroscopique et la route fond diffèrent de plus de hdg_cog_tol_deg (vitesse supérieure à min_speed_kn) cap HDT, THS ou VHW, heading_sensor = true
speed-log la vitesse surface et la vitesse fond diffèrent de plus de stw_sog_tol_kn VHW ou VBW, et vitesse fond
sea-level l'altitude GGA diffère de antenna_height_m de plus de sea_level_tol_m altitude GGA, antenna_height_m
cn0-spread la dispersion du C/N0 entre satellites tombe à cn0_spread_frac de sa médiane de calibration GSV
cn0-rise le C/N0 des satellites communs a augmenté de cn0_rise_db en moyenne et cn0_rise_frac d'entre eux ont augmenté GSV
time-consistency les instants d'époques consécutives avancent de façon irrégulière, reculent ou s'écartent de l'horloge de l'hôte heure NMEA ; horloge de l'hôte pour la dernière partie
osnma le récepteur signale un échec d'authentification OSNMA (un état rapporté est lu ; rien n'est vérifié ; pour vérifier OSNMA à partir des pages I/NAV elles-mêmes, voir docs/OSNMA.md) phrase d'état $PKSOS

Une époque est non fiable lorsque RAIM, le moniteur d'horloge, un échec de résolution ou un saut de position déclenche une alarme, ou lorsque la baisse de C/N0 atteint le double de son seuil ; dégradée pour toute autre alarme ; et nominale dans le cas contraire.

Le chemin du moteur (raim, solve-failure, clock) est le même calcul que la référence indépendante de leurrage JammerTest 2024 dans tests/jammertest_spoof_oracle_support : tests/receiver_trust_jammertest.rs vérifie que, sur les huit débuts publiés, la première alarme tombe sur la même époque et est de même nature.

Score de confiance (plateforme navire)#

Pour un navire, chaque époque après la calibration reçoit aussi un score de 0 à 100 et les raisons pour lesquelles il n'est pas de 100. L'application est déterministe et fixée avant qu'aucun journal ne soit noté : pas d'apprentissage, rien d'ajusté sur des événements. Chaque moniteur ramène ses éléments de preuve à un rapport statistic / threshold (une alarme à 1 ou plus) ; un rapport coûte au score

points = weight * clamp((ratio - onset_ratio) / (full_ratio - onset_ratio), 0, 1)
score  = round_to_0.1( clamp(100 - sum of points, 0, 100) )

de sorte qu'un moniteur ne coûte rien tant qu'il est sous onset_ratio de son seuil (par défaut 0,5), la moitié de son poids à son seuil, et la totalité à full_ratio fois le seuil (par défaut 1,5) ; le score est non croissant en chaque statistique. Une source qui ne rapporte rien à une époque conserve sa dernière statistique pendant evidence_hold_s (par défaut 10 s : deux cycles d'une phrase C/N0 envoyée toutes les 5 s), tant comme alarme que comme déduction.

L'arrondi se fait à la valeur supérieure en cas d'égalité (floor(x * 10 + 0.5) / 10), la bande est celle du score arrondi, et le plafonnement précède l'arrondi. Seuls les moniteurs dont les points sont supérieurs à zéro figurent dans les déductions, avec leur rapport et leurs points non arrondis, les plus gros points d'abord ; les déductions de points égaux sont listées dans l'ordre des noms de moniteurs cn0-drop, agc, jam-ind, loss-of-lock, position-jump, raim, clock, solve-failure, kinematic, heading-course, speed-log, sea-level, cn0-spread, cn0-rise, time-consistency, osnma, geometry. Un moniteur dont le test est réussi ou échoué (une résolution échouée, une authentification échouée) reçoit le rapport 1,5, le poids plein. Les points listés sont ceux d'avant le plafonnement à 0.

[score]
nominal_min = 90.0         # score >= 90: nominal
degraded_min = 55.0        # 55 <= score < 90: degraded; below 55: untrusted
onset_ratio = 0.5
full_ratio = 1.5
evidence_hold_s = 10.0
[score.weights]            # overrides; a monitor not listed keeps its default
sea-level = 30.0
Poids (points) Moniteurs Pourquoi
70 osnma un échec d'authentification est un constat, non une statistique
60 kinematic, raim, clock, position-jump, time-consistency une fausse position doit contredire la physique ou les propres contrôles du récepteur, et le temps ne peut pas remonter ; une violation à pleine force (1,5 fois son seuil : 60 points, score 40) est non fiable, et à son seuil (30 points, score 70) dégradée
40 heading-course, speed-log, solve-failure un seul capteur indépendant en désaccord donne un état dégradé ; deux à pleine force donnent un état non fiable
30 sea-level, cn0-spread, cn0-rise, cn0-drop l'environnement du signal, ou un contrôle peu informatif
25 loss-of-lock, agc, jam-ind l'environnement est hostile ; cela seul ne montre pas que la position est fausse
jusqu'à 15, [geometry] geometry (0.36) pas un poids sur cette rampe : rien à moins que le ciel ne soit pauvre, puis jusqu'à cap_points (15) ; la géométrie seule atteint l'état dégradé, jamais non fiable, et ne déclenche aucune alarme (voir « Géométrie » ci-dessous)

Les bornes des bandes découlent des poids : le moniteur le plus léger qui déclenche une alarme coûte 12,5 points à son seuil, de sorte que toute alarme fait quitter la bande nominale ; aucun moniteur seul sous 60 points ne peut atteindre seul la bande non fiable (plusieurs ensemble le peuvent). Le résultat consigne le modèle de score (score_model) et chaque époque porte score.deductions : quels moniteurs ont déduit, leurs rapports et leurs points. Pour un navire, l'état de l'époque est la bande de son score.

Géométrie : dégradation naturelle distincte d'une incohérence de type attaque#

Un récepteur à côté d'une grue de quai, dans une écluse ou sous une coque haute perd des satellites et sa position se dégrade pour une raison qui n'est pas une attaque. À partir de 0,36, selon la note de conception de la géométrie (docs/design/2026-10-12-geometry-and-skyline-design.md, section 4), chaque époque porte sa géométrie satellitaire, le score d'un navire a un moniteur de géométrie, et chaque déduction porte une classe : geometry (le ciel), environment (l'environnement du signal) ou inconsistency (éléments de type attaque).

D'où vient la géométrie. L'élévation et l'azimut de chaque satellite suivi (NMEA GSV, UBX NAV-SAT), dont est dérivée la dilution de précision (DOP) avec l'unique noyau DOP du moteur (orbit::dop, VALIDATED par rapport à gnss_lib_py) : source = derived. À défaut, le DOP et les satellites utilisés que le récepteur indique (NMEA GSA) : source = reported. Les positions dans le ciel et GSA comptent pour le evidence_hold_s du score (par défaut 10 s), car de nombreux bus envoient GSV toutes les quelques secondes ; un ensemble GSV incomplet à une époque (la fin d'un extrait, une phrase perdue) n'est pas utilisé. Un journal sans l'un ni l'autre, comme un NMEA avec seulement GGA et RMC, ou les journaux Android et RINEX aujourd'hui, reçoit l'état unknown avec la note « geometry unknown » : le moteur ne devine pas une géométrie qu'on ne lui a pas donnée, et le moniteur de géométrie ne s'exécute pas.

geometry.status Signification
good assez de satellites et DOP dans les seuils
degraded une ou plusieurs raisons : few-satellites, high-pdop, high-hdop, et (à titre informatif, hors du score) sky-blocked lorsque tous les satellites suivis se trouvent dans une moitié de l'horizon
unknown le journal ne donne aucune géométrie satellitaire pour cette époque

L'état est descriptif et c'est l'étiquette (ci-dessous) qui compte pour le score : degraded inclut le cas informatif sky-blocked, qui peut s'accompagner d'un DOP bon ou moyen, et seule une étiquette poor est notée.

L'étiquette (toujours rapportée, jamais notée). Le geometry.label de chaque époque vaut good (HDOP d'au plus hdop_good 2, PDOP d'au plus pdop_good 3 et au moins used_good 6 satellites), poor (HDOP supérieur à hdop_max 4, PDOP supérieur à pdop_max 6 ou moins de min_used 5 satellites), fair entre les deux, ou unknown. La note de l'époque s'ouvre toujours par l'étiquette et les chiffres, par exemple « geometry fair: HDOP 1,105, PDOP 1,831, VDOP 1,410, 8 satellites; not scored (the score deducts only above HDOP 4, PDOP 6 or below 5 satellites) », et ajoute « , signal loss » lorsque c'est le cas.

Le moniteur de géométrie (score du navire). Il ne déduit rien tant que la géométrie n'est pas mauvaise : son rapport est max(hdop / hdop_max, pdop / pdop_max, min_used / n_used), et tant qu'il vaut au plus 1 (géométrie bonne ou moyenne, y compris un ciel dégagé GPS seul de 8 satellites) il ne coûte rien, de sorte qu'un tel journal obtient exactement le même score qu'en 0,35. Au-dessus de 1, la déduction monte linéairement jusqu'à cap_points (15) à full_ratio (1,5 fois les seuils : HDOP 6, PDOP 9, 3 satellites) : 7,5 points au rapport 1,25 (HDOP 5, ou 4 satellites), 15 à partir de 1,5 (score 85, dégradé). À lui seul, il ne peut jamais faire passer le score sous floor (par défaut : le degraded_min du score, 55), de sorte que la géométrie seule atteint au plus la bande dégradée et jamais la bande non fiable : le plafond en vigueur est min(cap_points, 100 - floor), si bien qu'une table [score] qui relève degraded_min (une table 0,35 avec degraded_min 90, par exemple) abaisse le plafond à 100 - degraded_min au lieu d'être refusée, et un floor fixé sous degraded_min est refusé. Le moniteur ne déclenche aucune alarme ; il est listé en dernier dans monitors_run lorsqu'il a décidé à une époque quelconque (un navire dont le journal donne la géométrie), et n'est pas compté dans l'identité avec 0,35 ; ce n'est pas une entrée de [score.weights] (en définir une est refusé), et le plafond et le plancher en vigueur sont consignés dans geometry_model.cap_in_force et geometry_model.floor_in_force. La note d'une époque mauvaise dit pourquoi, par exemple « geometry poor: HDOP 5,200, PDOP 7,900, VDOP 4,100, 7 satellites; degraded by geometry: HDOP 5,200 above 4 (derived from 7 logged satellites); not an inconsistency ».

Classes. Chaque déduction (score.deductions[].class) :

Classe Moniteurs
geometry geometry (mauvaise géométrie) ; loss-of-lock lorsque chaque satellite perdu par rapport à la référence de base de calibration était sous low_elev_deg à son dernier rapport
environment cn0-drop, cn0-spread, cn0-rise, agc, jam-ind, loss-of-lock dans les autres cas ; geometry pendant une perte de signal (ci-dessous)
inconsistency kinematic, heading-course, speed-log, sea-level, time-consistency, clock, raim, position-jump, osnma, solve-failure ; et geometry lorsque la géométrie est mauvaise et que le HDOP indiqué est en désaccord avec le ciel indiqué (ci-dessous)

La protection contre un faux ciel pauvre. Un leurreur pourrait envoyer un GSV maigre, ou rapporter un HDOP élevé, pour acheter de la tolérance. Le moteur étiquette et ne pardonne pas : aucun autre contrôle n'est élargi par une mauvaise géométrie, et le HDOP indiqué par le récepteur (GSA dans la durée de maintien, sinon GGA) est comparé au HDOP dérivé du ciel qu'il indique. Lorsqu'ils diffèrent de plus de max(reported_vs_derived_tol, reported_vs_derived_rel × derived HDOP) (1,0, ou 25 % du HDOP dérivé, la plus grande des deux valeurs), l'époque porte reported_derived_mismatch: true et, lorsque la géométrie est mauvaise, sa déduction est classée inconsistency. Avec une géométrie bonne ou moyenne, le désaccord est rapporté et non noté : rien n'est déduit tant que la géométrie n'est pas mauvaise. C'est un choix délibéré (0,36) : « zéro sauf si mauvaise » vaut pour chaque époque, de sorte qu'une géométrie bonne ou moyenne obtient exactement le même score qu'en 0,35, et qu'un HDOP falsifié n'est jamais passé sous silence. Le drapeau (reported_derived_mismatch) figure sur chaque époque du résultat et chaque ligne en direct, la note dit « …beyond the tolerance: reported, not scored (the geometry is not poor) » ou « …: the geometry deduction is classed an inconsistency », geometry_summary.hdop_mismatch compte les époques, et la page de synthèse des preuves affiche le décompte, zéro compris. Les 25 % sont un choix d'ingénierie REPRESENTATIVE, non un chiffre mesuré ou normalisé : un récepteur qui résout une horloge par constellation, ou avec un sous-ensemble des satellites qu'il suit (masque d'élévation, seuil de C/N0, exclusion RAIM), indique un HDOP qui diffère du HDOP à horloge unique de tous les satellites suivis d'un écart qui croît avec le HDOP, de sorte qu'une tolérance fixe seule lirait une mauvaise géométrie naturelle comme une incohérence. Un écart plus grand se lit toujours comme tel. Une déduction classée geometry est un constat sur le ciel, non la conclusion qu'aucune attaque n'a eu lieu.

Une perte de signal n'est pas le ciel. Lorsque des satellites que le récepteur liste en vue (au-dessus de low_elev_deg) ne sont pas suivis, le moteur dérive aussi le DOP de tous les satellites en vue. Si ce DOP en vue est dans les seuils et que soit rien n'est suivi, soit les satellites non suivis sont répartis autour de l'horizon (non confinés à une moitié), le ciel était là et le signal non : l'époque porte signal_loss: true, la note « satellites in view but not tracked: signal environment, not geometry », et la déduction de géométrie est classée environment. Des satellites non suivis confinés à une moitié de l'horizon se lisent toujours comme un ciel bloqué (un mur, une falaise, un quai). Une obstruction des deux côtés (un canyon ou un fjord étroit) se lit comme une perte de signal tant qu'aucun profil d'horizon n'est configuré. Les valeurs sentinelles d'absence de position du récepteur ne sont pas un DOP : un HDOP ou PDOP indiqué de 99 ou plus, une qualité GGA de 0 et un mode GSA de 1 ne donnent aucun DOP indiqué.

clé [geometry] Valeur par défaut Provenance
hdop_max 4 choisi (note de conception 4,2) : à sigma_uere_m 1,5 m, un HDOP de 4 correspond à un 2drms de 12 m, le kin_pos_tol_m du contrôle cinématique. IEC 61108-1 est couramment citée comme énonçant ses exigences de précision pour HDOP ≤ 4 (ou PDOP ≤ 6) : rappelé d'après la littérature secondaire sur les récepteurs GNSS, sans source secondaire précise consultée et sans que la norme ait été lue ; à vérifier par rapport au texte de la norme
pdop_max 6 le seuil de disponibilité du PDOP de scenarios/polar-arctic-leo-coverage.toml ; GPS SPS Performance Standard, 5e éd. (2020), PDOP ≤ 6, tel que cité dans tests/constellation_availability_published_spec_oracle.rs ; à vérifier par rapport au texte de la norme
min_used 5 un de plus que ce qu'exige une position, de sorte qu'une seule perte ne met pas fin à la position ; le minimum avec une mesure redondante pour RAIM
sigma_uere_m 1.5 une entrée indiquée pour le lien 2drms de hdop_max ; indiquez la valeur propre au récepteur
reported_vs_derived_tol 1,0 HDOP choisi (note de conception 4,2) : la partie absolue de la tolérance ; un désaccord au-delà de la tolérance n'est pas de la géométrie
reported_vs_derived_rel 0.25 choix d'ingénierie REPRESENTATIVE : la partie relative, pour les récepteurs à horloges par constellation ou à solutions sur sous-ensemble
low_elev_deg 15 note de conception 4,3 : les satellites perdus sous ce seuil sont perdus pour le ciel ; un profil d'horizon (élément 1 de 0,36) l'étendra
azimuth_gap_deg 180 géométrique, à titre informatif : tous les satellites dans une moitié de l'horizon
cap_points 15 note de conception 4,2 : la géométrie seule fait passer le score de nominal à dégradé et jamais à non fiable ; déduit seulement lorsque la géométrie est mauvaise (décision du fondateur B1, 0,36)
full_ratio 1.5 le full_ratio propre au score : la rampe atteint cap_points à 1,5 fois les seuils de mauvaise géométrie
floor le degraded_min du score (55) la géométrie seule n'atteint jamais la bande non fiable ; le plafond en vigueur est min(cap_points, 100 - floor) ; un plancher sous degraded_min est refusé
hdop_good 2 REPRESENTATIVE, pour l'étiquette seulement : la moitié de hdop_max (le rapport de début du score 0,35)
pdop_good 3 REPRESENTATIVE, pour l'étiquette seulement : la moitié de pdop_max
used_good 6 REPRESENTATIVE, pour l'étiquette seulement : un de plus que min_used, de sorte qu'une seule perte laisse encore une mesure redondante

Ce qui a changé dans les sorties, et ce qui n'a pas changé. Aucun score ne change tant que la géométrie n'est pas mauvaise ; l'étiquette est toujours rapportée. Clés ajoutées : epochs[].geometry (status, reasons, source, n_used, hdop, pdop, vdop arrondi à 0,001, open_fraction (null tant qu'aucun profil d'horizon n'est configuré), max_azimuth_gap_deg, age_s, reported_hdop, reported_derived_mismatch, note, in_view (satellites listés au-dessus de low_elev_deg), signal_loss, label), class sur chaque déduction, et à la fin geometry_model (seuils, durée de maintien, plafond et plancher en vigueur, provenance, règles de classe) et geometry_summary (époques par état, par étiquette et par classe de déduction, et époques avec un écart de HDOP indiqué) ; score_model.weights est comme en 0,35. Deux tests rattachent cela, à l'octet près, à la référence de base « 0,36 portable » une fois ces clés retirées (les empreintes 0.35.0, rebasées lorsque le moteur est passé aux mathématiques portables et conservées dans les tests comme trace de ce que 0.35.0 a publié : docs/revisions/STUDIO15-portable-math-rebaseline.md). tests/geometry_trust_no_change.rs couvre chaque sortie des deux sessions d'exemple (JSON de résultat, CSV, SVG, verdict, lignes en direct rejouées, $PKSHT, Prometheus, CEF, LEEF). tests/geometry_corpus_identity.rs couvre le résultat par lot, la synthèse, les lignes en direct et $PKSHT de chaque corpus NMEA de navire versionné (la démonstration et les jeux de référence maritimes, chacun aussi en ciel dégagé GPS seul avec 8 satellites, et les scénarios de formation d'équipage générés à partir de leurs graines) ; dans les deux scénarios de formation dont le brouillage rend la géométrie mauvaise, chaque époque qui n'est pas mauvaise est identique et chaque époque mauvaise diffère par la seule déduction de géométrie. Un journal dont la géométrie est mauvaise perd jusqu'à 15 points qu'il ne perdait pas en 0,35 ; c'est le changement voulu. Sur la démonstration, l'entraînement progressif se lit inconsistency ; tests/geometry_trust.rs modifie le journal pour masquer le ciel ouest (moyen : étiqueté, non noté), pour le masquer ainsi que deux satellites à l'est (mauvais : déductions de géométrie, perte d'accrochage environment parce que des satellites hauts ont aussi disparu), pour ne perdre que des satellites sous 15° (perte d'accrochage geometry), et pour indiquer un HDOP que le ciel n'explique pas (la déduction de mauvaise géométrie devient inconsistency). Les seuils sont MODELLED : choisis, non ajustés sur un journal quelconque ; rien ici n'affirme à quelle fréquence la dégradation naturelle et les attaques sont distinguées sur des données réelles.

Énoncer les événements et les prédictions#

[[events]]
label = "jammer on"
kind = "jamming"            # jamming, spoofing or other
onset_s = 120.0             # seconds since the first epoch of the log
end_s = 240.0               # optional; otherwise onset_s + compare.horizon_s
predicted_cn0_drop_db = 12.0  # optional, e.g. from a `jamming` scenario's link budget

[compare]
detect_tol_s = 10.0         # an alarm this soon after onset counts as a detection
cn0_tol_db = 3.0            # predicted and observed drops agree within this

Pour chaque événement, le résultat donne les fausses alarmes entre la calibration et le début, la première alarme et ses moniteurs, la latence, l'issue (detected, late, missed ou not-evaluable), et la baisse médiane observée de C/N0 par rapport à la prédiction (agree, disagree, no-prediction ou not-evaluable). Le désaccord est rapporté, jamais effacé par réglage : les tolérances sont dans le scénario, et le scénario est dans l'empreinte du résultat.

Ce que cela n'affirme pas#

Les moniteurs de confiance sont MODELLED : leurs seuils sont des entrées, et leur performance de détection sur de véritables interférences est ce que mesure la notation des événements, non quelque chose qu'affirme ce type de scénario. Sur les sessions de leurrage JammerTest 2024, les moniteurs du chemin du moteur détectent certains débuts en 10 s et sont en retard sur d'autres ; voir la ligne « Clock-aided χ², RAIM, AGC, SQM fused per-epoch security FoM » dans VERIFICATION-MATRIX.md pour les chiffres. Une prédiction qui concorde avec un journal est un point de donnée, non une validation.