Strumienie szkoleniowe NMEA: kshana nmea-scenario
Akademie morskie i ośrodki symulatorowe mogą podawać wygenerowane przez Kshana NMEA 0183 do symulatora mostka lub plotera map, aby szkolić załogi w rozpoznawaniu zagłuszania i spoofingu GNSS.
Wyłącznie do szkoleń i testów. Wygenerowane strumienie są syntetyczne. Nigdy nie wolno ich podawać do żywych systemów nawigacyjnych statku. Kshana ma charakter doradczy i nie jest sprzętem nawigacyjnym z zatwierdzeniem typu. Każdy strumień zaczyna się od zdania znacznika, które powtarza co dziesięć sekund, własnościowego zdania
$PKSHT,TRAINING,SYNTHETIC,NOT-FOR-NAVIGATION, które odbiorniki ignorują;--no-markerusuwa je dla symulatora odrzucającego nieznane zdania. Zapisuje to wyłącznie tekst NMEA: nie ma żadnego wyjścia RF, IQ ani przebiegu, a nic nie jest nadawane poza tekstem na wskazany adres.
Rysunek pochodzi z dziennika instruktora scenarios/training/coastal-drag-off.toml.
Zastosowanie#
# write a file (default <name>.nmea) and the instructor log next to it
kshana nmea-scenario scenarios/training/coastal-drag-off.toml --out drag.nmea
# serve over TCP in real time on this machine only (waits for the simulator to connect)
kshana nmea-scenario scenarios/training/open-sea-jamming.toml --tcp 10110
# reachable from the bridge-simulator host: name the address in full
kshana nmea-scenario scenarios/training/open-sea-jamming.toml --tcp 192.168.1.20:10110
# UDP, one sentence per datagram, ten times faster than real time
kshana nmea-scenario scenarios/training/combined-event.toml --udp 192.168.1.50:10110 --speed 10
# UDP broadcast
kshana nmea-scenario scenarios/training/combined-event.toml --udp 192.168.1.255:10110 --broadcast
| Opcja | Znaczenie |
|---|---|
--out <file> |
zapisz strumień do pliku; bez wyjścia sieciowego domyślnie <name>.nmea |
--instructor-log <prefix> |
zapisz <prefix>.instructor.json i <prefix>.instructor.txt (domyślnie: obok pliku) |
--tcp <addr:port> lub <port> |
serwer TCP; przebieg zaczyna się, gdy połączy się klient (--wait-clients <n>). Sam port nasłuchuje tylko na 127.0.0.1 |
--udp <addr:port> lub <port> |
UDP, jedno zdanie na datagram; --broadcast zezwala na adres rozgłoszeniowy. Sam port wysyła na 127.0.0.1 |
--realtime, --speed <x> |
nadaj tempo strumieniowi; --speed 0 oznacza możliwie najszybciej. Wyjście sieciowe domyślnie działa w czasie rzeczywistym, pliki możliwie najszybciej |
--seed <n> |
zastąp ziarno scenariusza |
--no-marker |
pomiń zdanie znacznika |
Nie ma uwierzytelniania. Serwer TCP z adresem nielokalnym udostępnia strumień każdemu, kto może się z nim połączyć, a UDP wysyła na dowolny wskazany adres. Klient TCP, który nie odbierze swoich danych w ciągu dwóch sekund, jest odłączany, więc jeden zablokowany odbiorca nie może wstrzymać pozostałych.
To samo ziarno daje te same bajty na jednej platformie, a tempo nigdy nie zmienia bajtów. Między platformami własna arytmetyka generatora jest identyczna bit w bit (używa przenośnej warstwy matematycznej), ale położenia satelitów i kąty widzenia pochodzą z funkcji silnika wywołujących bibliotekę matematyczną hosta, więc w rzadkim przypadku granicznym zaokrąglona cyfra elewacji, azymutu lub C/N0 w GSV może różnić się między platformami.
Co jest zapisywane#
Każda epoka (1, 2, 5 lub 10 na sekundę) zawiera: RMC, GGA, GNS (rozwiązanie, położenie, czas), GSA (używane satelity i DOP, po jednym na konstelację), GSV (satelity w widoku z elewacją, azymutem i SNR w dB-Hz, po jednym zestawie na konstelację, identyfikator sygnału NMEA 4,10), VTG (kurs i prędkość nad dnem), ZDA (czas i data), HDT (kurs z żyrokompasu) i VBW (prędkość względem wody i nad dnem z logu). Nadawcą jest GN przy wielu konstelacjach, w przeciwnym razie własny identyfikator konstelacji. Wiersze kończą się CRLF i mają poprawne sumy kontrolne.
HDT i VBW zastępują niezależne czujniki (żyrokompas i log Dopplera). Żadne zdarzenie ich nie dotyka, co jest zamierzone: rozbieżność między tym, co podaje GNSS, a tym, co mówią żyrokompas i log, jest wskazówką do omówienia.
Gdy rozwiązanie zostaje utracone, jakość GGA wynosi 0, stan RMC to V, pola położenia są puste, a pola czasu trwają dalej (zegar odbiornika nadal chodzi), tak jak w rzeczywistych odbiornikach.
Statek#
[vessel] i [[waypoint]] podają pozycję początkową, prędkość i punkty, przez które należy sterować. Statek jest kierowany do następnego punktu trasy z ograniczeniem prędkości zwrotu (max_rot_deg_per_min, domyślnie 30), ograniczeniem tego, jak szybko narasta prędkość zwrotu (max_rot_accel_deg_per_s2) oraz ograniczeniem zmiany prędkości (max_accel_kn_per_min). Prędkość odcinka to speed_kn punktu trasy. [environment] dodaje prąd, więc prędkość nad dnem różni się od prędkości względem wody, a kurs różni się od kursu nad dnem, oraz zboczenie magnetyczne. stop_at_end zatrzymuje statek w ostatnim punkcie trasy.
Geometria satelitów#
Satelity pochodzą z własnych nominalnych konstelacji silnika (tablica slotów GPS, wzorce Walkera MEO Galileo i BeiDou, GLONASS ICD), propagowanych przez constellation::satellite_positions_fixed (dwa ciała plus wiekowe J2, obrót Ziemi) i widziane ze statku przy użyciu kątów widzenia WGS-84 silnika. Liczba widocznych satelitów, elewacje i azymuty w GSV są wiarygodne dla miejsca i pory dnia. To jest niebo nominalne, a nie efemerydy dla daty strumienia: PRN-y są przydzielane w kolejności slotów, a każda konstelacja zachowuje swój opublikowany układ slotów, propagowany w sposób ciągły od epoki tablicy slotów GPS (2016-12-31) pod wpływem J2, więc układ jest nienaruszony, ale jego orientacja nie jest orientacją rzeczywistego nieba w dniu strumienia. C/N0 zależy od elewacji (nominalny poziom w zenicie, ustawiany w [receiver]), ze stałym przesunięciem na satelitę, powolnym zanikaniem i szumem. Satelita poniżej track_threshold_dbhz ma puste pole SNR; ten poniżej use_threshold_dbhz jest śledzony, ale nieużywany. Do max_used (domyślnie 12, pułap NMEA GGA) użytecznych satelitów jest używanych, dobieranych pod kątem geometrii (najpierw najwyższy satelita w każdym sektorze azymutu o rozwartości 30 stopni). Mniej niż cztery użyteczne satelity powoduje utratę rozwiązania, a wraca ono po reacquire_s użytecznego sygnału.
Zdarzenia#
Każde [[event]] ma kind, moment początku start_s, duration_s (od początku do rozpoczęcia zwolnienia), czas narastania ramp_s i czas zwalniania recovery_s. Siła rośnie liniowo przez ramp_s, utrzymuje się, a następnie maleje przez recovery_s; 0 oznacza skok. Wszystkie efekty skalują się z tą siłą. Parametry, które nie należą do danego rodzaju, są odrzucane.
| rodzaj | parametry | efekt |
|---|---|---|
jamming |
cn0_drop_db, spread_db (domyślnie 6) |
C/N0 każdego satelity spada nawet o cn0_drop_db (z rozrzutem na satelitę), satelity wypadają w kolejności od najsłabszego, pozycja zostaje utracona, gdy użytecznych jest mniej niż cztery, i wraca po zwolnieniu oraz po reacquire_s |
drag-off |
final_offset_m, jedno z: bearing_deg (rzeczywisty) lub relative_bearing_deg (względem kursu w chwili początku, a potem utrzymywany), opcjonalnie max_accel_mps2, counterfeit_cn0_dbhz |
odbiornik utrzymuje poprawną pozycję, której położenie oddala się od prawdziwego; SOG i COG uwzględniają to znoszenie. Znoszenie przebiega według funkcji smoothstep na czasach narastania i zwalniania, więc fałszywy tor zaczyna się i kończy bez skoku prędkości; jego szczyty to 1.5 D/T m/s i 6 D/T^2 m/s kwadrat dla czasu narastania T s na dystansie D m (oba są rejestrowane), a scenariusz, który przekroczyłby max_accel_mps2, jest odrzucany. Skok (ramp_s = 0) przesuwa pozycję, nie ruszając prędkości |
time-spoof |
offset_s, counterfeit_cn0_dbhz |
czas RMC, GGA, GNS i ZDA jest przesunięty względem prawdziwego UTC; pozycja pozostaje prawdziwa |
replay-delay |
delay_s, affect_time (domyślnie true), counterfeit_cn0_dbhz |
pozycja, prędkość (i czas) są takie, jakie jednostka miała delay_s temu, przy czym opóźnienie zmienia się według funkcji smoothstep na czasach narastania, a raportowana prędkość jest skalowana przez 1 - d(delay)/dt, gdy opóźnienie się zmienia: uproszczona abstrakcja w stylu meaconingu, a nie model propagacji |
counterfeit_cn0_dbhz: gdy aktywne jest znoszenie, fałszowanie czasu lub opóźnione odtwarzanie, każdy śledzony satelita pokazuje ten sam, podniesiony poziom, co jest klasycznym znakiem. Pomiń go przy subtelnym zdarzeniu, w którym zachowany jest prawdziwy wzorzec C/N0. Gdy aktywne jest jakiekolwiek zdarzenie z fałszywym sygnałem, odbiornik podąża za fałszywym sygnałem, więc zagłuszanie go nie wyłącza; pozycja utracona wskutek wcześniejszego zagłuszania wraca po reacquire_s na fałszywym sygnale (zob. combined-event). Zmiana czasu, która sprawiłaby, że raportowany czas stanęłby lub biegłby wstecz (tempo 1 s na sekundę lub więcej), jest odrzucana, chyba że jest skokiem (ramp_s = 0).
Zdarzenia mogą się nakładać; ich efekty sumują się (przesunięcia, zmiany czasu, opóźnienia) albo obowiązuje najsilniejszy (zagłuszanie). onset na osi czasu to pierwsza epoka, w której zdarzenie ma efekt, czyli jedna epoka po zaplanowanym start_s, gdy zdarzenie narasta stopniowo.
Dziennik instruktora#
Obok strumienia <prefix>.instructor.json i <prefix>.instructor.txt podają:
- zdarzenia w postaci wprowadzonej, zwykłym językiem, z każdym parametrem i momentem początku w UTC;
- oś czasu: początek, pełny efekt, początek powrotu, powrót oraz własne
fix-lostifix-regainedodbiornika; - prawdziwy tor obok raportowanego w odstępach
log_interval_s: prawdziwa i raportowana pozycja, prędkość i kurs, błąd pozycji w metrach, przesunięcie czasu, liczba satelitów użytych i śledzonych, średnie C/N0 oraz informacja, które zdarzenia były aktywne; - szczytowa prędkość i przyspieszenie prawdziwego oraz raportowanego toru;
trainer_notescenariusza.
Pokazuj dziennik dopiero podczas omówienia.
pola JSON (schemat kshana-nmea-training/1)#
| pole | typ, jednostka | znaczenie |
|---|---|---|
schema |
ciąg znaków | kshana-nmea-training/1 |
warning |
ciąg znaków | oświadczenie dotyczące wyłącznie szkolenia |
scenario, description, trainer_note |
ciąg znaków | ze scenariusza |
seed |
liczba całkowita | użyte ziarno (seed) |
start_utc |
ISO 8601 UTC | początek przebiegu |
duration_s |
s | długość przebiegu |
summary.true_peak_sog_kn, summary.reported_peak_sog_kn |
węzły | najwyższa prawdziwa i raportowana prędkość względem dna |
summary.true_peak_accel_mps2, summary.reported_peak_accel_mps2 |
m/s kwadrat | największa zmiana prędkości na sekundę, prawdziwa oraz między kolejnymi raportowanymi pozycjami |
events[] |
po jednym na każde zaplanowane zdarzenie | |
events[].id |
liczba całkowita | numeracja od 1; używane przez timeline[].event i track[].active_events |
events[].description |
ciąg znaków | opis zwykłym językiem wraz z parametrami |
events[].start_utc |
ISO 8601 UTC | zaplanowany początek |
events[].parameters |
obiekt | zdarzenie dokładnie tak, jak zapisano (kind, start_s, duration_s, ramp_s, recovery_s oraz parametry właściwe dla danego rodzaju; nieobecne są pomijane) |
events[].resolved_bearing_deg |
stopnie względem północy rzeczywistej lub null | znoszenie: zastosowany kierunek (kierunek względny jest rozstrzygany w chwili początku) |
events[].peak_drag_speed_mps, events[].peak_drag_accel_mps2 |
m/s, m/s kwadrat lub null | znoszenie: szczyty, jakie znoszenie dodaje do fałszywego toru |
timeline[] |
momenty do omówienia, w kolejności czasowej | |
timeline[].t_s, .utc |
s od początku, ISO 8601 UTC | kiedy (czas prawdziwy) |
timeline[].event |
liczba całkowita lub null | identyfikator zdarzenia; null dla zmian stanu odbiornika |
timeline[].what |
ciąg znaków | onset, full-effect, recovery-begins, recovered (zdarzenia); fix-lost, fix-regained (odbiornik) |
timeline[].text |
ciąg znaków | objaśnienie |
track[] |
jeden wiersz na log_interval_s |
|
track[].t_s, .utc |
s, ISO 8601 UTC | kiedy (czas prawdziwy) |
track[].true_lat_deg, .true_lon_deg |
stopnie | prawdziwa pozycja |
track[].true_sog_kn, .true_cog_deg, .true_heading_deg |
węzły, stopnie względem północy rzeczywistej | prawdziwa prędkość i kurs względem dna oraz kurs żyrokompasu |
track[].fix_valid |
wartość logiczna | odbiornik zgłosił pozycję |
track[].reported_lat_deg, .reported_lon_deg, .reported_sog_kn, .reported_cog_deg |
stopnie, węzły, stopnie względem północy rzeczywistej lub null | to, co zgłosił odbiornik; null, gdy pozycja jest utracona |
track[].position_error_m |
m lub null | odległość pozioma między pozycją prawdziwą a raportowaną |
track[].time_offset_s |
s | raportowane UTC minus prawdziwe UTC |
track[].n_used, .n_tracked |
liczby całkowite | satelity użyte w pozycji oraz śledzone |
track[].mean_cn0_dbhz |
dB-Hz lub null | średnie C/N0 śledzonych satelitów |
track[].active_events |
liczby całkowite | identyfikatory zdarzeń mających efekt w tej chwili |
Dodanie pola nie zmienia znacznika schematu. Zmiana nazwy lub usunięcie pola albo zmiana jego znaczenia lub jednostki podnosi numer (/2).
Biblioteka scenariuszy#
scenarios/training/, każdy to kilkuminutowy rejs z notatką trenera w pliku TOML:
| plik | co pokazuje |
|---|---|
open-sea-jamming.toml |
C/N0 spada jednocześnie, satelity wypadają, pozycja zostaje utracona i wraca |
coastal-drag-off.toml |
poprawna, zdrowo wyglądająca pozycja znosi się o 1,4 km w stronę brzegu; jednolicie podniesione C/N0 |
port-approach-time-spoof.toml |
pozycja poprawna, UTC dryfuje o 45 s do przodu podczas powolnego podejścia do portu |
combined-event.toml |
zagłuszanie, następnie przejęcie przez fałszywy sygnał ze znoszeniem i przesunięciem czasu, a potem opóźnione odtwarzanie |
fjord-natural-degradation.toml |
zasłonięte niebo, a nie zakłócenia: ściany fiordu (zestaw horyzontu REPRESENTATIVE fjord) zasłaniają satelity na wschodzie i zachodzie, po czym kanał znów się otwiera (zdarzenie natural-degradation, z tabelą skyline) |
Pozycje, daty i tory są zmyślone; biblioteka nie zawiera żadnego prawdziwego statku, operatora ani nagrania. Nie zawiera żadnego stwierdzenia o tym, jak jakikolwiek odbiornik lub system zachowałby się wobec zakłóceń.
Wybory modelowe#
Generator jest narzędziem szkoleniowym bez zewnętrznej wyroczni, a jego liczby są wyborami modelowymi, a nie pomiarami żadnego odbiornika. Dobrano je tak, aby wyglądały jak zwykły dziennik odbiornika: nachylenie C/N0 o 14 dB od zenitu do horyzontu poniżej poziomu w zenicie (domyślnie 47 dB-Hz) oraz przesunięcie na satelitę +/-1.5 dB, powolne zaniki o 0,8 dB w ciągu 97 s i o 0,5 dB w ciągu 31 s, drżenie 0,3 dB; progi śledzenia (20 dB-Hz) i użycia (28 dB-Hz); szum pozycji (1,5 m przy HDOP 1, skorelowany w ciągu 30 s, w pionie dwukrotnie większy); szum żyroskopu 0,05 stopnia i szum znoszenia 0,03 kn; stała czasowa sterowania 20 s i promienie dojścia do punktów trasy w modelu statku; oraz wybór satelitów według sektorów azymutu. Wartości domyślne znajdują się w [receiver] i [vessel] i wszystkie można zmienić w scenariuszu.
Odczyt zwrotny#
kshana receiver-trust potrafi wczytać te pliki (format = "nmea"). Testy sprawdzają każde zdanie każdego scenariusza z biblioteki względem jego własnych reguł liczby pól i sumy kontrolnej oraz przepuszczają pliki przez ten czytnik, który interpretuje GGA, RMC i GSV, a pozostałe typy pomija, niczego nie przeskakując. Testy przypinają fragment wzorcowy dla każdego scenariusza (tests/fixtures/nmea_training/); po zamierzonej zmianie wygeneruj go ponownie przez KSHANA_BLESS_GOLDEN=1.