Zaufanie do odbiornika: ocena rzeczywistego dziennika odbiornika GNSS
kshana receiver-trust odczytuje dziennik z rzeczywistego odbiornika globalnego systemu nawigacji satelitarnej (GNSS), przepuszcza go epoka po epoce przez monitory zaufania i mówi, kiedy i dlaczego pozycja odbiornika przestała być wiarygodna. Może też ocenić dziennik względem zdarzeń i predykcji podanych przed przebiegiem: czy każde zdarzenie wykryto na czas i czy modelowany spadek stosunku nośnej do gęstości szumu (C/N0) zgadza się z tym, co zmierzył odbiornik.
Obserwable pochodzą z samego odbiornika; monitory i ich progi pochodzą z silnika i od użytkownika. Każdy wynik niesie tę etykietę, SHA-256 dziennika i każdy próg, z którym był uruchomiony, więc przebieg można powtórzyć i poddać audytowi.
Dla statku w drodze, z bieżącym wskaźnikiem i opcjonalną bramką przed systemem map, zob. MARITIME-TRUST.md.
Uruchom#
kshana receiver-trust examples/receiver-trust/session.toml
To zapisuje session.result.json, session.trust.csv (jeden wiersz na epokę) i session.trust.svg (średnie C/N0 w czasie ze stanem zaufania pod spodem) obok scenariusza i wypisuje jednowierszowy werdykt. Przykładowy dziennik jest syntetyczny, stworzony, by pokazać format; path należy skierować na dziennik własnego odbiornika.
Powyższy rysunek to demo statku z MARITIME-TRUST.md: zmyślony dziennik, wskaźnik wyłącznie doradczy.
Ten sam scenariusz uruchamia się z Pythona (kshana.receiver_trust(toml)), z pakietu WebAssembly (receiver_trust(toml), z dziennikiem wstawionym w treści) oraz z narzędzia assess_receiver_log serwera Model Context Protocol (MCP).
Dzienniki, które odczytuje#
format |
Źródło | Co jest odczytywane |
|---|---|---|
ubx |
Binarne u-blox | NAV-SAT: C/N0, elewacja i azymut każdego satelity, NAV-PVT: pozycja, MON-RF: automatyczna regulacja wzmocnienia (AGC) i wskaźnik zagłuszania jamInd, NAV-TIMEGPS: tydzień oraz własne flagi odbiornika: NAV-STATUS spoofDetState i SEC-SIG (self_report, zob. RECEIVER-PROTOCOLS.md) |
rinex |
Plik obserwacji RINEX 3 | kody S (C/N0) dla każdego satelity i sygnału; z nadawanym plikiem nawigacyjnym (nav) także pseudoodległości do własnego rozwiązania pozycji silnika |
android |
CSV z Android GnssLogger | wiersze Raw: C/N0 i AgcDb dla każdego satelity; wiersze Fix: pozycja |
nmea |
Tekst NMEA 0183 | GSV: stosunek sygnału do szumu dla każdego satelity (dB-Hz, z identyfikatorem sygnału 4,10 lub bez) oraz jego elewacja i azymut, GSA: współczynnik DOP i użyte satelity, GGA i RMC: czas i pozycja |
sbf |
Binarne Septentrio (0,38, tylko odczyt) | PVTGeodetic: pozycja, MeasEpoch: C/N0 dla każdego satelity i sygnału, RFStatus: liczba pasm; zob. RECEIVER-PROTOCOLS.md |
rtklib-pos |
Plik rozwiązania RTKLIB (.pos) |
czas, pozycja (szerokość/długość geograficzna lub ECEF, po przeliczeniu) i użyte satelity dla każdej epoki; pliki ze stopniami, minutami i sekundami są odrzucane z komunikatem |
novatel |
Dzienniki ASCII lub binarne NovAtel OEM7 | BESTPOS: pozycja (tylko gdy rozwiązanie obliczono), RANGE: C/No dla każdego satelity i sygnału; flagi zagłuszania z RXSTATUS stają się polem self_report epoki (stan zagłuszania 2, gdy odbiornik zgłasza zagłuszacz, 1, gdy nie; w RXSTATUS nie ma flagi spoofingu), zestawianym z alarmami Kshana przez porównanie receiver-self-report |
gnss-sdr |
Strumienie Monitor i PVT-monitor z GNSS-SDR jako wiersze JSON | C/N0 dla każdego kanału, pozycja, użyte satelity, HDOP, azymut i elewacja; tworzone przez integrations/gnss-sdr/relay.py |
Szczegóły, źródła i ograniczenia tych trzech: MACHINE-INPUTS.md.
Każda epoka tworzona przez czytniki zawiera także source_span, przesunięcia bajtowe [start, end) w danych wejściowych: najmniejszy ciągły zakres obejmujący rekordy, które się na nią złożyły, dzięki czemu dokładny surowy wycinek można zahaszować dla okna zdarzenia. NMEA: każdy wiersz odczytany, gdy epoka jest bieżąca (nadzbiór tam, gdzie niezwiązany wiersz leży między jej zdaniami). UBX: ramki epoki. RINEX 3: rekordy epoki od każdego wiersza > do następnego. Android: wiersze Raw epoki (wiersz Fix tylko dla epoki bez wierszy Raw). Brak dla strumienia na żywo i wszędzie tam, gdzie nie jest to dobrze określone (RINEX 2).
Plik podaje się przez path (rozwiązywane względem własnego folderu scenariusza) lub wstawia w treści jako text albo base64 (jedyne możliwości w przeglądarce).
Dziennik IMU jako drugi czujnik#
[log.imu] dodaje dziennik inercjalny na tym samym zegarze:
[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
Prędkość odchylenia jest całkowana z heading0_deg i wypełnia kurs wszędzie tam, gdzie dziennik odbiornika go nie zawiera, więc monitor heading-course (który wymaga heading_sensor = true na platformie) porównuje kurs z żyroskopu z kursem nad dnem. Żyroskop dryfuje: kurs jest tak dobry, jak okno całkowania, a hdg_cog_tol_deg decyduje, co uznaje się za niezgodność. Układ csv opisano w MACHINE-INPUTS.md. Bajty IMU są częścią skrótu scenariusza i skrótu scenariusza w pakiecie dowodowym. Wstawienie w treści (text/base64) działa na każdej powierzchni; path jest odrzucane tam, gdzie pliki nie są odczytywane.
Platforma: nieruchoma lub statek#
Powyższe monitory zakładają nieruchomą antenę. Scenariusz podaje platformę przed przebiegiem w tabeli [platform] najwyższego poziomu; bez niej platformą jest static i nic się nie zmienia.
[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)
| Klucz | Wartość domyślna | Dlaczego ta wartość |
|---|---|---|
max_speed_kn |
30 | Powyżej prędkości służbowej kadłubów handlowych i większości jednostek pasażerskich, więc rzeczywisty statek nigdy jej nie osiąga; znacznie poniżej prędkości, jaką oznacza zrywanie pozycji. Wolniejszy statek powinien podać niższy próg: im niższy próg, tym wcześniej wykrywa się zrywanie. |
max_accel_mps2 |
0.5 | Duży statek zmienia prędkość o znacznie mniej niż 0,1 m/s²; mała szybka jednostka osiąga kilka dziesiątych przy gwałtownej zmianie przepustnicy. |
max_turn_rate_dps |
6 | Duży statek skręca z prędkością znacznie mniejszą niż 1 deg/s, a mała jednostka w ostrym zwrocie z kilkoma stopniami na sekundę. |
antenna_height_m |
brak | Właściwość instalacji; zgadywanie byłoby ukrytym progiem. |
heading_sensor |
false | Zadeklarowane true i nieobecne w dzienniku jest błędem, więc brakujący czujnik nie może uchodzić za czystą kontrolę. |
Klucze statku są odrzucane przy kind = "static". Dla statku monitor position-jump nie działa, ponieważ mierzy odległość od średniej kalibracyjnej, co jest błędne dla poruszającej się anteny; pole monitor_config wyniku zapisuje platformę i użyte limity.
Syntetyczne strumienie NMEA z kshana nmea-scenario, przygotowane do szkolenia załóg, są przyjmowane przez czytnik NMEA i można je przepuścić przez ten rodzaj jak każdy dziennik NMEA.
Monitory#
Każdy monitor potrzebuje swoich danych; monitor, którego danych nie ma w dzienniku, nie jest uruchamiany i nie jest wymieniony w monitors_run. Pierwsze calibration_s sekund (domyślnie 60) ustalają punkt odniesienia i nigdy nie alarmują.
| Monitor | Alarmuje, gdy | Wymaga |
|---|---|---|
cn0-drop |
średnie C/N0 po satelitach wspólnych z punktem odniesienia spada o cn0_drop_db lub więcej (domyślnie 6 dB) |
C/N0 |
loss-of-lock |
liczba śledzonych satelitów spada o sats_lost lub więcej poniżej mediany z punktu odniesienia (domyślnie 4) |
C/N0 |
agc |
odczyt AGC odbiega od swojego punktu odniesienia o więcej niż agc_k_sigma odchyleń standardowych (domyślnie 5) |
UBX MON-RF lub Android AgcDb |
jam-ind |
jamInd u-blox przekracza jam_ind_threshold (domyślnie 80 z 255) |
UBX MON-RF |
position-jump |
własna pozycja odbiornika jest oddalona od jego średniej kalibracyjnej o więcej niż position_jump_m (domyślnie 50 m) |
podana pozycja |
raim |
autonomiczne monitorowanie integralności przez odbiornik (RAIM) metodą parzystości na własnym jednopunktowym rozwiązaniu pozycji silnika odrzuca zestaw pomiarów | dziennik RINEX z nav |
solve-failure |
jednopunktowe rozwiązanie pozycji silnika zawodzi przy pięciu lub więcej satelitach | dziennik RINEX z nav |
clock |
zegar odbiornika odbiega od przewidywanej prostej o więcej niż minimalne wykrywalne przesunięcie wynikające z szumu jego kalibracji | dziennik RINEX z nav |
Skalibrowany profil (opcjonalny). [monitors] profile = "jammertest-2024-calibrated" zastępuje trzy progi zakłóceń wartościami skalibrowanymi na publicznym zbiorze danych JammerTest 2024 (jeden u-blox ZED-F9P na scenariusz; Zenodo 15911589): cn0_drop_db = 1.5, agc_k_sigma = 3.5, jam_ind_threshold = 29. Kalibrację zarejestrowano z góry (xval/jammertest-trust/PREREGISTRATION.md), a profil jest MODELLED, dopasowany na jednej publicznej kampanii: jego próg cn0_drop_db nie spełnił zarejestrowanego z góry progu fałszywych alarmów na zbiorze zapieczętowanym (0,0231 wobec 0,02); progi AGC i wskaźnika zagłuszania oraz próg dla sumy (fałszywe alarmy 0,033, detekcja 0,81) zostały spełnione (xval/jammertest-trust/RESULTS.md). Obowiązuje dla tego typu odbiornika w tym zdarzeniu, względem zaplanowanych slotów ataku, i został skalibrowany na dziennikach CSV zbioru (jego dzienniki UBX dają więcej fałszywych alarmów w dwóch folderach). Wynik uruchomiony z profilem powtarza zastosowane progi i zawiera profile_note mówiące o tym wszystkim. Bez profilu obowiązują powyższe wartości domyślne i każde wyjście pozostaje niezmienione.
Dla platformy vessel uruchamiane są także monitory platformy ruchomej (progi w tabeli [maritime] najwyższego poziomu, każda wartość domyślna opisana w src/receiver_trust/maritime.rs); każdy potrzebuje swoich danych i jest wymieniany w monitors_run tylko wtedy, gdy rozstrzygnął:
| Monitor | Alarmuje, gdy | Wymaga |
|---|---|---|
kinematic |
zmiana pozycji nie zgadza się z podaną prędkością i kursem albo oznacza prędkość, przyspieszenie lub prędkość zwrotu powyżej limitów statku | pozycje; prędkość i kurs (RMC lub VTG) dla części ze zliczania drogi |
heading-course |
kurs z żyroskopu i kurs nad dnem różnią się o więcej niż hdg_cog_tol_deg (prędkość powyżej min_speed_kn) |
kurs HDT, THS lub VHW, heading_sensor = true |
speed-log |
prędkość względem wody i prędkość nad dnem różnią się o więcej niż stw_sog_tol_kn |
VHW lub VBW oraz prędkość nad dnem |
sea-level |
wysokość z GGA różni się od antenna_height_m o więcej niż sea_level_tol_m |
wysokość z GGA, antenna_height_m |
cn0-spread |
rozrzut C/N0 między satelitami spada do cn0_spread_frac mediany kalibracyjnej |
GSV |
cn0-rise |
C/N0 satelitów wspólnych wzrosło średnio o cn0_rise_db, a cn0_rise_frac z nich wzrosło |
GSV |
time-consistency |
kolejne czasy epok zmieniają się nieregularnie, biegną wstecz albo opuszczają zegar hosta | czas NMEA; zegar hosta dla ostatniej części |
osnma |
odbiornik zgłasza niepowodzenie uwierzytelniania OSNMA (odczytywany jest zgłoszony status; nic nie jest weryfikowane; aby zweryfikować OSNMA na podstawie samych stron I/NAV, zob. docs/OSNMA.md) | zdanie statusowe $PKSOS |
Epoka jest niezaufana, gdy alarmuje RAIM, monitor zegara, niepowodzenie rozwiązania lub skok pozycji albo gdy spadek C/N0 osiąga dwukrotność swojego progu; zdegradowana przy każdym innym alarmie; i nominalna w pozostałych przypadkach.
Ścieżka silnika (raim, solve-failure, clock) to to samo obliczenie co wyrocznia spoofingu JammerTest 2024 w tests/jammertest_spoof_oracle_support: tests/receiver_trust_jammertest.rs sprawdza, że dla wszystkich ośmiu opublikowanych początków pierwszy alarm przypada na tę samą epokę i jest tego samego rodzaju.
Wskaźnik zaufania (platforma statku)#
Dla statku każda epoka po kalibracji dostaje także wskaźnik od 0 do 100 oraz powody, dla których nie wynosi on 100. Odwzorowanie jest deterministyczne i ustalone przed oceną jakiegokolwiek dziennika: bez uczenia, nic nie jest dopasowywane do zdarzeń. Każdy monitor sprowadza swoje dowody do ilorazu statistic / threshold (alarm przy 1 lub więcej); iloraz kosztuje wskaźnik
points = weight * clamp((ratio - onset_ratio) / (full_ratio - onset_ratio), 0, 1)
score = round_to_0.1( clamp(100 - sum of points, 0, 100) )
więc monitor nic nie kosztuje, dopóki jest poniżej onset_ratio swojego progu (domyślnie 0,5), połowę swojej wagi przy progu i całą wagę przy full_ratio razy progu (domyślnie 1,5); wskaźnik jest nierosnący względem każdej statystyki. Źródło, które nie zgłasza się w danej epoce, zachowuje swoją ostatnią statystykę przez evidence_hold_s (domyślnie 10 s: dwa cykle zdania C/N0 wysyłanego co 5 s), zarówno jako alarm, jak i jako odliczenie.
Zaokrąglenie jest w górę od połowy (floor(x * 10 + 0.5) / 10), pasmo jest pasmem wskaźnika zaokrąglonego, a ograniczenie następuje przed zaokrągleniem. Na liście odliczeń znajdują się tylko monitory, których punkty są powyżej zera, z ich ilorazem i niezaokrąglonymi punktami, najpierw największe; odliczenia o równych punktach są wymieniane w kolejności nazw monitorów 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. Monitorowi, którego test jest zaliczony lub niezaliczony (nieudane rozwiązanie, nieudane uwierzytelnianie), przypisuje się iloraz 1,5, czyli pełną wagę. Wymienione punkty to te sprzed ograniczenia przy 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
| Waga (punkty) | Monitory | Dlaczego |
|---|---|---|
| 70 | osnma |
niepowodzenie uwierzytelniania jest stwierdzeniem, a nie statystyką |
| 60 | kinematic, raim, clock, position-jump, time-consistency |
fałszywa pozycja musi przeczyć fizyce albo własnym kontrolom odbiornika, a czas nie może biec wstecz; jedno naruszenie z pełną siłą (1,5 razy próg: 60 punktów, wskaźnik 40) jest niezaufane, a przy progu (30 punktów, wskaźnik 70) zdegradowane |
| 40 | heading-course, speed-log, solve-failure |
jeden niezależny czujnik, który się nie zgadza, to stan zdegradowany; dwa z pełną siłą to stan niezaufany |
| 30 | sea-level, cn0-spread, cn0-rise, cn0-drop |
środowisko sygnału albo kontrola o słabej wartości informacyjnej |
| 25 | loss-of-lock, agc, jam-ind |
środowisko jest wrogie; samo to nie dowodzi, że pozycja jest błędna |
do 15, [geometry] |
geometry (0.36) |
nie jest wagą na tej rampie: nic, chyba że niebo jest słabe, wtedy do cap_points (15); sama geometria osiąga stan zdegradowany, nigdy niezaufany, i nie wywołuje alarmu (zob. „Geometria” poniżej) |
Granice pasm wynikają z wag: najlżejszy monitor, który alarmuje, kosztuje 12,5 punktu przy swoim progu, więc każdy alarm wyprowadza z pasma nominalnego; żaden pojedynczy monitor poniżej 60 punktów nie może samodzielnie osiągnąć pasma niezaufanego (kilka razem może). Wynik zapisuje model wskaźnika (score_model), a każda epoka zawiera score.deductions: które monitory odliczyły, ich ilorazy i ich punkty. Dla statku stan epoki to pasmo jej wskaźnika.
Geometria: naturalne pogorszenie odróżnione od niespójności przypominającej atak#
Odbiornik przy suwnicy nabrzeżnej, w śluzie lub pod wysokim kadłubem traci satelity, a jego pozycja się pogarsza z powodu, który nie jest atakiem. Od 0,36, zgodnie z notatką projektową geometrii (docs/design/2026-10-12-geometry-and-skyline-design.md, sekcja 4), każda epoka niesie swoją geometrię satelitów, wskaźnik statku ma monitor geometrii, a każde odliczenie niesie klasę: geometry (niebo), environment (środowisko sygnału) lub inconsistency (dowody przypominające atak).
Skąd pochodzi geometria. Elewacja i azymut każdego śledzonego satelity (NMEA GSV, UBX NAV-SAT), z których wyprowadza się współczynnik DOP (rozmycie dokładności) za pomocą jedynego jądra DOP silnika (orbit::dop, VALIDATED względem gnss_lib_py): source = derived. W razie braku takich danych brane są DOP i użyte satelity podane przez odbiornik (NMEA GSA): source = reported. Pozycje na niebie i GSA liczą się przez evidence_hold_s wskaźnika (domyślnie 10 s), ponieważ wiele magistral wysyła GSV co kilka sekund; zestaw GSV niekompletny w epoce (koniec fragmentu, zgubione zdanie) nie jest używany. Dziennik bez żadnego z nich, taki jak NMEA tylko z GGA i RMC, albo dzienniki Android i RINEX obecnie, dostaje status unknown z notatką „geometry unknown”: silnik nie zgaduje geometrii, której nie otrzymał, a monitor geometrii nie działa.
geometry.status |
Znaczenie |
|---|---|
good |
dość satelitów i DOP w granicach progów |
degraded |
jeden lub więcej powodów: few-satellites, high-pdop, high-hdop oraz (informacyjnie, nie we wskaźniku) sky-blocked, gdy wszystkie śledzone satelity znajdują się w jednej połowie horyzontu |
unknown |
dziennik nie podaje geometrii satelitów dla tej epoki |
Status jest opisowy, a dla wskaźnika liczy się etykieta (niżej): degraded obejmuje informacyjny przypadek sky-blocked, który może wystąpić przy dobrym lub przeciętnym DOP, a punktowana jest tylko etykieta poor.
Etykieta (zawsze podawana, nigdy punktowana). geometry.label każdej epoki to good (HDOP co najwyżej hdop_good 2, PDOP co najwyżej pdop_good 3 i co najmniej used_good 6 satelitów), poor (HDOP powyżej hdop_max 4, PDOP powyżej pdop_max 6 albo mniej niż min_used 5 satelitów), fair pomiędzy nimi albo unknown. Notatka epoki zawsze zaczyna się od etykiety i liczb, na przykład „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)”, a gdy to zachodzi, dodaje „, signal loss”.
Monitor geometrii (wskaźnik statku). Odlicza nic, chyba że geometria jest słaba: jego iloraz to max(hdop / hdop_max, pdop / pdop_max, min_used / n_used), a dopóki wynosi co najwyżej 1 (dobra lub przeciętna geometria, w tym otwarte niebo tylko z GPS i 8 satelitami), nic nie kosztuje, więc taki dziennik dostaje dokładnie ten sam wskaźnik co w 0,35. Powyżej 1 odliczenie rośnie liniowo do cap_points (15) przy full_ratio (1,5 razy progi: HDOP 6, PDOP 9, 3 satelity): 7,5 punktu przy ilorazie 1,25 (HDOP 5 lub 4 satelity), 15 od 1,5 wzwyż (wskaźnik 85, zdegradowany). Samo z siebie nigdy nie obniży wskaźnika poniżej floor (domyślnie: degraded_min wskaźnika, 55), więc sama geometria osiąga co najwyżej pasmo zdegradowane, a nigdy niezaufane: obowiązujące ograniczenie to min(cap_points, 100 - floor), więc tabela [score], która podnosi degraded_min (na przykład taka z 0,35 z degraded_min 90), obniża ograniczenie do 100 - degraded_min zamiast być odrzucana, a floor ustawione poniżej degraded_min jest odrzucane. Monitor nie wywołuje alarmu; jest wymieniany jako ostatni w monitors_run, gdy rozstrzygnął w jakiejkolwiek epoce (statek, którego dziennik podaje geometrię), i nie wlicza się do tożsamości z 0,35; nie jest wpisem [score.weights] (ustawienie takiego jest odrzucane), a obowiązujące ograniczenie i dolny próg są zapisywane w geometry_model.cap_in_force i geometry_model.floor_in_force. Notatka słabej epoki mówi dlaczego, na przykład „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”.
Klasy. Każde odliczenie (score.deductions[].class):
| Klasa | Monitory |
|---|---|
geometry |
geometry (słaba geometria); loss-of-lock, gdy każdy satelita utracony względem punktu odniesienia kalibracji znajdował się przy ostatnim raporcie poniżej low_elev_deg |
environment |
cn0-drop, cn0-spread, cn0-rise, agc, jam-ind, w pozostałych przypadkach loss-of-lock; geometry podczas utraty sygnału (niżej) |
inconsistency |
kinematic, heading-course, speed-log, sea-level, time-consistency, clock, raim, position-jump, osnma, solve-failure; oraz geometry, gdy geometria jest słaba, a podany HDOP nie zgadza się z podanym niebem (niżej) |
Zabezpieczenie przed sfałszowanym słabym niebem. Spoofer mógłby wysłać rzadkie GSV albo zgłosić wysoki HDOP, by kupić tolerancję. Silnik oznacza i nie wybacza: żadna inna kontrola nie jest rozszerzana z powodu słabej geometrii, a HDOP podany przez odbiornik (GSA w ramach przytrzymania, w przeciwnym razie GGA) jest porównywany z HDOP wyprowadzonym z podanego przez niego nieba. Gdy różnią się o więcej niż max(reported_vs_derived_tol, reported_vs_derived_rel × derived HDOP) (1,0 lub 25 % wyprowadzonego HDOP, zależnie od tego, które większe), epoka niesie reported_derived_mismatch: true, a gdy geometria jest słaba, jej odliczenie jest klasyfikowane jako inconsistency. Przy dobrej lub przeciętnej geometrii niezgodność jest zgłaszana i nie jest punktowana: nic nie jest odliczane, chyba że geometria jest słaba. To świadomy wybór (0,36): „zero, chyba że słaba” obowiązuje w każdej epoce, więc dobra i przeciętna geometria dostają dokładnie ten sam wskaźnik co w 0,35, a sfałszowany HDOP nigdy nie przechodzi po cichu. Flaga (reported_derived_mismatch) jest przy każdej epoce wyniku i każdym wierszu na żywo, notatka mówi „…beyond the tolerance: reported, not scored (the geometry is not poor)” albo „…: the geometry deduction is classed an inconsistency”, geometry_summary.hdop_mismatch zlicza epoki, a strona podsumowania dowodów pokazuje liczbę, także zero. Wartość 25 % jest REPRESENTATIVE wyborem inżynierskim, a nie wartością zmierzoną ani normatywną: odbiornik, który rozwiązuje jeden zegar na konstelację albo na podzbiorze śledzonych satelitów (maska elewacji, odcięcie C/N0, wykluczenie RAIM), podaje HDOP różniący się od HDOP z jednym zegarem dla wszystkich śledzonych satelitów o wartość rosnącą wraz z HDOP, więc sama stała tolerancja uznałaby naturalnie słabą geometrię za niespójność. Większa różnica nadal jest odczytywana jako niespójność. Odliczenie sklasyfikowane jako geometry jest stwierdzeniem o niebie, a nie ustaleniem, że atak nie nastąpił.
Utrata sygnału to nie niebo. Gdy satelity, które odbiornik wymienia jako widoczne (powyżej low_elev_deg), nie są śledzone, silnik wyprowadza także DOP dla wszystkich satelitów widocznych. Jeśli ten DOP widocznych mieści się w progach i albo nic nie jest śledzone, albo nieśledzone satelity są rozłożone wokół horyzontu (nie ograniczają się do jego jednej połowy), to niebo było, a sygnału nie było: epoka niesie signal_loss: true, notatkę „satellites in view but not tracked: signal environment, not geometry”, a odliczenie geometrii jest klasyfikowane jako environment. Nieśledzone satelity ograniczone do jednej połowy horyzontu nadal są odczytywane jako zasłonięte niebo (ściana, klif, nabrzeże). Przeszkoda dwustronna (kanion lub wąski fiord) jest odczytywana jako utrata sygnału, dopóki nie skonfiguruje się skyline. Znaczniki braku pozycji w odbiorniku nie są DOP: podany HDOP lub PDOP równy 99 lub więcej, jakość GGA 0 i tryb GSA 1 nie dają podanego DOP.
Klucz [geometry] |
Wartość domyślna | Pochodzenie |
|---|---|---|
hdop_max |
4 | wybrane (notatka projektowa 4,2): przy sigma_uere_m 1,5 m HDOP 4 daje 2drms równe 12 m, czyli kin_pos_tol_m kontroli kinematycznej. IEC 61108-1 jest powszechnie cytowana jako stawiająca wymagania dokładności dla HDOP ≤ 4 (lub PDOP ≤ 6): przywołane z wtórnej literatury o odbiornikach GNSS, bez sprawdzenia konkretnego źródła wtórnego i bez lektury normy; do sprawdzenia względem tekstu normy |
pdop_max |
6 | próg dostępności PDOP z scenarios/polar-arctic-leo-coverage.toml; GPS SPS Performance Standard, wyd. 5 (2020), PDOP ≤ 6, jak cytowano w tests/constellation_availability_published_spec_oracle.rs; do sprawdzenia względem tekstu normy |
min_used |
5 | o jeden więcej, niż potrzebuje rozwiązanie pozycji, więc pojedyncza utrata nie kończy rozwiązania; najmniej, przy których jest jeden pomiar nadmiarowy dla RAIM |
sigma_uere_m |
1.5 | podane wejście dla łącza 2drms z hdop_max; należy ustawić własną wartość odbiornika |
reported_vs_derived_tol |
1,0 HDOP | wybrane (notatka projektowa 4,2): część bezwzględna tolerancji; niezgodność powyżej tolerancji nie jest geometrią |
reported_vs_derived_rel |
0.25 | REPRESENTATIVE wybór inżynierski: część względna, dla odbiorników z zegarami dla poszczególnych konstelacji lub rozwiązaniami z podzbiorów |
low_elev_deg |
15 | notatka projektowa 4,3: satelity utracone poniżej tej wartości są utracone na rzecz nieba; skyline (0,36, pozycja 1) to rozszerzy |
azimuth_gap_deg |
180 | geometryczne, informacyjne: wszystkie satelity w jednej połowie horyzontu |
cap_points |
15 | notatka projektowa 4,2: sama geometria przesuwa wskaźnik z nominalnego do zdegradowanego i nigdy do niezaufanego; odliczane tylko wtedy, gdy geometria jest słaba (decyzja założyciela B1, 0,36) |
full_ratio |
1.5 | własne full_ratio wskaźnika: rampa osiąga cap_points przy 1,5 razy progach słabej geometrii |
floor |
degraded_min wskaźnika (55) |
sama geometria nigdy nie osiąga pasma niezaufanego; obowiązujące ograniczenie to min(cap_points, 100 - floor); dolny próg poniżej degraded_min jest odrzucany |
hdop_good |
2 | REPRESENTATIVE, tylko etykieta: połowa hdop_max (iloraz początkowy wskaźnika 0,35) |
pdop_good |
3 | REPRESENTATIVE, tylko etykieta: połowa pdop_max |
used_good |
6 | REPRESENTATIVE, tylko etykieta: o jeden więcej niż min_used, więc pojedyncza utrata nadal zostawia pomiar nadmiarowy |
Co zmieniło się w wynikach, a co nie. Żaden wskaźnik się nie zmienia, chyba że geometria jest słaba; etykieta jest zawsze podawana. Dołączone klucze: epochs[].geometry (status, reasons, source, n_used, hdop, pdop, vdop zaokrąglone do 0,001, open_fraction (null, dopóki nie skonfigurowano skyline), max_azimuth_gap_deg, age_s, reported_hdop, reported_derived_mismatch, note, in_view (satelity wymienione powyżej low_elev_deg), signal_loss, label), class przy każdym odliczeniu, a na końcu geometry_model (progi, przytrzymanie, obowiązujące ograniczenie i dolny próg, pochodzenie, reguły klas) oraz geometry_summary (epoki według statusu, etykiety i klasy odliczenia oraz epoki z niezgodnością podanego HDOP); score_model.weights jest taki jak w 0,35. Dwa testy wiążą to z bajtami punktu odniesienia „0,36 portable” po usunięciu tych kluczy (skróty z 0.35.0, ponownie ustalone, gdy silnik przeszedł na matematykę przenośną, i zachowane w testach jako zapis tego, co wydała wersja 0.35.0: docs/revisions/STUDIO15-portable-math-rebaseline.md). tests/geometry_trust_no_change.rs wiąże każde wyjście obu przykładowych sesji (wynikowy JSON, CSV, SVG, werdykt, odtworzone wiersze na żywo, $PKSHT, Prometheus, CEF, LEEF). tests/geometry_corpus_identity.rs wiąże wynik wsadowy, podsumowanie, wiersze na żywo i $PKSHT każdego zatwierdzonego korpusu NMEA statku (demo i referencyjne dane testowe morskie, każdy także jako otwarte niebo tylko z GPS i 8 satelitami, oraz scenariusze szkoleniowe generowane z ich ziaren); w dwóch scenariuszach szkoleniowych, w których zagłuszanie pogarsza geometrię, każda epoka, która nie jest słaba, jest identyczna, a każda słaba różni się wyłącznie odliczeniem geometrii. Dziennik ze słabą geometrią traci do 15 punktów, których nie tracił w 0,35; to zamierzona zmiana. W demo zrywanie odczytuje się jako inconsistency; tests/geometry_trust.rs edytuje dziennik, aby zasłonić niebo zachodnie (przeciętne: oznaczone, niepunktowane), zasłonić je i dwa satelity wschodnie (słabe: odliczenia geometrii, utrata zaczepienia environment, bo wysokie satelity też zniknęły), utracić tylko satelity poniżej 15° (utrata zaczepienia geometry) oraz podać HDOP, którego niebo nie wyjaśnia (odliczenie słabej geometrii staje się inconsistency). Progi są MODELLED: wybrane, a nie dopasowane do żadnego dziennika; nic tutaj nie twierdzi, jak często w rzeczywistych danych odróżnia się naturalne pogorszenie od ataków.
Podawanie zdarzeń i predykcji#
[[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
Dla każdego zdarzenia wynik podaje fałszywe alarmy między kalibracją a początkiem, pierwszy alarm i jego monitory, opóźnienie, rezultat (detected, late, missed lub not-evaluable) oraz obserwowany medianowy spadek C/N0 względem predykcji (agree, disagree, no-prediction lub not-evaluable). Niezgodność jest zgłaszana, nigdy dostrajana: tolerancje są w scenariuszu, a scenariusz jest w skrócie wyniku.
Czego to nie twierdzi#
Monitory zaufania są MODELLED: ich progi są danymi wejściowymi, a ich skuteczność detekcji dla rzeczywistych zakłóceń jest tym, co mierzy ocena zdarzeń, a nie czymś, co ten rodzaj stwierdza. W sesjach spoofingu JammerTest 2024 monitory ze ścieżki silnika wychwytują część początków w ciągu 10 s, a w innych są spóźnione; liczby podaje wiersz „Clock-aided χ², RAIM, AGC, SQM fused per-epoch security FoM” w VERIFICATION-MATRIX.md. Predykcja zgodna z jednym dziennikiem to jeden punkt danych, a nie walidacja.