Навчальні потоки NMEA: kshana nmea-scenario
Морські академії та центри тренажерів можуть подавати згенерований Kshana NMEA 0183 на місток-симулятор або картплотер, щоб навчати екіпажі розпізнавати глушіння та спуфінг GNSS.
Лише для навчання та тестування. Згенеровані потоки синтетичні. Їх ніколи не можна подавати на живі навігаційні системи судна. Kshana є консультативним засобом, а не навігаційним обладнанням із затвердженням типу. Кожен потік починається із власницького речення-маркера
$PKSHT,TRAINING,SYNTHETIC,NOT-FOR-NAVIGATIONі повторює його кожні десять секунд; приймачі його ігнорують;--no-markerвилучає його для симулятора, який відхиляє невідомі речення. Це записує лише текст NMEA: виходу RF, IQ чи сигналу немає, і ніщо не передається, окрім тексту на вказану вами адресу.
Рисунок побудовано за журналом інструктора scenarios/training/coastal-drag-off.toml.
Використання#
# 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
| Параметр | Значення |
|---|---|
--out <file> |
записати потік у файл; без мережевого виходу типовим є <name>.nmea |
--instructor-log <prefix> |
записати <prefix>.instructor.json і <prefix>.instructor.txt (типово: поруч із файлом) |
--tcp <addr:port> або <port> |
сервер TCP; прогін починається, коли під'єднався клієнт (--wait-clients <n>). Голий порт слухає лише 127.0.0.1 |
--udp <addr:port> або <port> |
UDP, одне речення на датаграму; --broadcast дозволяє широкомовну адресу. Голий порт надсилає на 127.0.0.1 |
--realtime, --speed <x> |
темп потоку; --speed 0 означає якнайшвидше. Мережевий вихід типово працює в реальному часі, файли — якнайшвидше |
--seed <n> |
замінити зерно сценарію |
--no-marker |
пропустити речення-маркер |
Автентифікації немає. Сервер TCP із нелокальною адресою віддає потік кожному, хто може до нього дістатися, а UDP надсилає на будь-яку вказану вами адресу. Клієнт TCP, який не забирає свої дані протягом двох секунд, відключається, тож один завислий споживач не може затримати інших.
Те саме зерно дає ті самі байти на одній платформі, а темп ніколи не змінює байти. Між платформами власна арифметика генератора побітово ідентична (використовується переносний математичний шар), але положення супутників і кути огляду надходять із функцій рушія, які викликають математичну бібліотеку хоста, тож у рідкісному межовому випадку округлена цифра висоти, азимута чи C/N0 у GSV може відрізнятися між платформами.
Що записується#
Кожна епоха (1, 2, 5 або 10 на секунду) містить: RMC, GGA, GNS (визначення, позиція, час), GSA (використані супутники та DOP, по одному на орбітальне угруповання), GSV (супутники в полі зору з висотою, азимутом і SNR у дБ-Гц, один набір на орбітальне угруповання, ідентифікатор сигналу NMEA 4,10), VTG (курс і швидкість відносно землі), ZDA (час і дата), HDT (курс за гіроскопом) і VBW (швидкість відносно води та землі за лагом). Передавач — GN для кількох орбітальних угруповань, інакше власний для угруповання. Рядки закінчуються CRLF і мають правильні контрольні суми.
HDT і VBW замінюють незалежні датчики (гірокомпас і доплерівський лаг). Жодна подія їх не зачіпає, і це свідомо: розбіжність між тим, що повідомляє GNSS, і тим, що кажуть гіроскоп і лаг, є підказкою для розбору.
Коли визначення втрачено, якість GGA дорівнює 0, статус RMC — V, поля позиції порожні, а поля часу тривають (годинник приймача продовжує йти), як у реальних приймачів.
Судно#
[vessel] і [[waypoint]] задають початкову позицію, швидкість і точки, через які веде судно. Судно веде до наступної шляхової точки з обмеженням швидкості повороту (max_rot_deg_per_min, типово 30), обмеженням того, як швидко наростає швидкість повороту (max_rot_accel_deg_per_s2) і обмеженням зміни швидкості (max_accel_kn_per_min). Швидкість ділянки — це speed_kn шляхової точки. [environment] додає течію, тому швидкість відносно землі відрізняється від швидкості відносно води, а курс відрізняється від курсу відносно землі, а також магнітне схилення. stop_at_end зупиняє судно в останній шляховій точці.
Геометрія супутників#
Супутники беруться з власних номінальних орбітальних угруповань рушія (таблиця слотів GPS, шаблони Волкера MEO для Galileo і BeiDou, ICD GLONASS), поширюються функцією constellation::satellite_positions_fixed (задача двох тіл плюс вікове J2, обертання Землі) і видимі з судна за кутами огляду WGS-84 рушія. Кількість видимих супутників, висоти й азимути в GSV правдоподібні для місця та часу доби. Це номінальне небо, а не ефемериди на дату потоку: PRN призначаються в порядку слотів, і кожне орбітальне угруповання зберігає свою опубліковану схему слотів, безперервно поширену від епохи таблиці слотів GPS (2016-12-31) під дією J2, тож схема цілісна, але її орієнтація не відповідає реальному небу на дату потоку. C/N0 залежить від висоти (номінальний рівень у зеніті задається в [receiver]) із фіксованим зсувом для кожного супутника, повільними завмираннями та шумом. Супутник нижче track_threshold_dbhz має порожнє поле SNR; нижче use_threshold_dbhz — відстежується, але не використовується. Використовується до max_used (типово 12, межа NMEA GGA) придатних супутників, обраних за геометрією (спершу найвищий супутник у кожному азимутному секторі 30 градусів). Якщо придатних супутників менше чотирьох, визначення втрачається, і воно повертається через reacquire_s придатного сигналу.
Події#
Кожна [[event]] має kind, початок start_s, duration_s (від початку до початку припинення), наростання на початку ramp_s і спадання при припиненні recovery_s. Інтенсивність лінійно зростає протягом ramp_s, утримується, потім спадає протягом recovery_s; 0 — це стрибок. Усі ефекти масштабуються цією інтенсивністю. Параметри, що не належать до цього виду, відхиляються.
| вид | параметри | ефект |
|---|---|---|
jamming |
cn0_drop_db, spread_db (типово 6) |
C/N0 кожного супутника падає щонайбільше на cn0_drop_db (розкид для кожного супутника), супутники випадають, починаючи з найнижчих, визначення втрачається, коли придатних менше чотирьох, і відновлюється після припинення та reacquire_s |
drag-off |
final_offset_m, одне з bearing_deg (істинний) або relative_bearing_deg (до курсу на початку, далі утримується), необов'язково max_accel_mps2, counterfeit_cn0_dbhz |
приймач зберігає дійсне визначення, позиція якого відходить від істини; SOG і COG містять зсув. Зсув слідує за smoothstep на наростаннях, тож хибний трек починається й закінчується без стрибка швидкості; його піки — 1.5 D/T м/с і 6 D/T^2 м/с на квадрат для наростання T с на D м (обидва записуються в журнал), а сценарій, що перевищив би max_accel_mps2, відхиляється. Стрибок (ramp_s = 0) зсуває позицію, не торкаючись швидкості |
time-spoof |
offset_s, counterfeit_cn0_dbhz |
час RMC, GGA, GNS і ZDA зсувається від істинного UTC; позиція залишається істинною |
replay-delay |
delay_s, affect_time (типово true), counterfeit_cn0_dbhz |
позиція, швидкість (і час) — такі, якими були в судна delay_s тому, із затримкою за smoothstep на наростаннях і швидкістю, що повідомляється, помноженою на 1 - d(delay)/dt, поки вона змінюється: спрощена абстракція в стилі міконінгу, а не модель поширення |
counterfeit_cn0_dbhz: поки активні зсув, спуфінг часу чи затримка відтворення, кожен відстежуваний супутник показує цей єдиний піднятий рівень — класична ознака. Пропустіть його для непомітної події, у якій зберігається справжній розподіл C/N0. Поки активна будь-яка підроблена подія, приймач слідує за підробленим сигналом, тож глушіння його не скидає; визначення, втрачене через раніше глушіння, повертається через reacquire_s на підробленому сигналі (див. combined-event). Зміна часу, через яку повідомлений час зупинився б або йшов назад (швидкість 1 с за секунду чи більше), відхиляється, якщо це не стрибок (ramp_s = 0).
Події можуть перекриватися; їхні ефекти додаються (зсуви, зміни часу, затримки) або береться найсильніший (глушіння). onset на часовій шкалі — це перша епоха, в якій подія має ефект, на одну епоху пізніше за сценарний start_s, коли подія наростає.
Журнал інструктора#
Разом із потоком <prefix>.instructor.json і <prefix>.instructor.txt містять:
- події, як їх уведено, простою мовою з кожним параметром, і їхній початок у UTC;
- часову шкалу: початок, повний ефект, початок відновлення, відновлено, а також власні
fix-lostіfix-regainedприймача; - істинний трек порівняно з повідомленим з інтервалом
log_interval_s: істинна та повідомлена позиція, швидкість і курс, похибка позиції в метрах, зсув часу, використані та відстежувані супутники, середній C/N0 і які події були активні; - пікову швидкість і прискорення істинного та повідомленого треку;
trainer_noteсценарію.
Показуйте журнал лише під час розбору.
поля JSON (схема kshana-nmea-training/1)#
| поле | тип, одиниця | значення |
|---|---|---|
schema |
рядок | kshana-nmea-training/1 |
warning |
рядок | твердження про призначення лише для навчання |
scenario, description, trainer_note |
рядок | зі сценарію |
seed |
ціле число | використане зерно |
start_utc |
ISO 8601 UTC | початок прогону |
duration_s |
s | тривалість прогону |
summary.true_peak_sog_kn, summary.reported_peak_sog_kn |
вузли | найвища істинна та повідомлена швидкість відносно землі |
summary.true_peak_accel_mps2, summary.reported_peak_accel_mps2 |
м/с на квадрат | найбільша зміна швидкості за секунду, істинна та між послідовними повідомленими визначеннями |
events[] |
по одному на кожну сценарну подію | |
events[].id |
ціле число | нумерація з 1; використовується в timeline[].event і track[].active_events |
events[].description |
рядок | опис простою мовою з параметрами |
events[].start_utc |
ISO 8601 UTC | сценарний початок |
events[].parameters |
об'єкт | подія точно так, як записано (kind, start_s, duration_s, ramp_s, recovery_s і власні параметри виду; відсутні пропускаються) |
events[].resolved_bearing_deg |
градуси істинні або null | зсув: застосований напрямок (відносний пеленг визначається на початку) |
events[].peak_drag_speed_mps, events[].peak_drag_accel_mps2 |
м/с, м/с на квадрат або null | зсув: піки, які зсув додає до хибного треку |
timeline[] |
моменти для розбору, у порядку часу | |
timeline[].t_s, .utc |
с від початку, ISO 8601 UTC | коли (істинний час) |
timeline[].event |
ціле число або null | ідентифікатор події; null для змін стану приймача |
timeline[].what |
рядок | onset, full-effect, recovery-begins, recovered (події); fix-lost, fix-regained (приймач) |
timeline[].text |
рядок | пояснення |
track[] |
один рядок на log_interval_s |
|
track[].t_s, .utc |
с, ISO 8601 UTC | коли (істинний час) |
track[].true_lat_deg, .true_lon_deg |
градуси | істинна позиція |
track[].true_sog_kn, .true_cog_deg, .true_heading_deg |
вузли, градуси істинні | істинна швидкість і курс відносно землі та курс за гіроскопом |
track[].fix_valid |
логічне значення | приймач повідомив про визначення |
track[].reported_lat_deg, .reported_lon_deg, .reported_sog_kn, .reported_cog_deg |
градуси, вузли, градуси істинні або null | що повідомив приймач; null, поки визначення втрачене |
track[].position_error_m |
м або null | горизонтальна відстань від істинної до повідомленої позиції |
track[].time_offset_s |
s | повідомлений UTC мінус істинний UTC |
track[].n_used, .n_tracked |
цілі числа | супутники, використані у визначенні, і відстежувані |
track[].mean_cn0_dbhz |
дБ-Гц або null | середній C/N0 відстежуваних супутників |
track[].active_events |
цілі числа | ідентифікатори подій, що діють у цей момент |
Додавання поля не змінює тег схеми. Перейменування чи вилучення поля або зміна його значення чи одиниці збільшує номер (/2).
Бібліотека сценаріїв#
scenarios/training/, кожен — кілька хвилин переходу з приміткою тренера в його TOML:
| файл | що він показує |
|---|---|
open-sea-jamming.toml |
C/N0 падає в усіх разом, супутники випадають, визначення втрачається й повертається |
coastal-drag-off.toml |
дійсне, здорове на вигляд визначення зсувається на 1,4 км до берега; рівномірно піднятий C/N0 |
port-approach-time-spoof.toml |
позиція правильна, UTC випереджає на 45 с під час повільного заходу в порт |
combined-event.toml |
глушіння, потім підроблене перехоплення зі зсувом і зсувом часу, потім затримка відтворення |
fjord-natural-degradation.toml |
заблоковане небо, а не завади: стіни фіорду (REPRESENTATIVE шаблон обрію fjord) ховають супутники на сході та заході, потім канал знову відкривається (подія natural-degradation з таблицею skyline) |
Позиції, дати та треки вигадані; бібліотека не містить жодного реального судна, оператора чи запису. Вона нічого не стверджує про те, як будь-який приймач чи система поводилися б за наявності завад.
Вибори моделювання#
Генератор — це навчальний інструмент без зовнішнього оракула, а його числа — вибори моделювання, а не вимірювання будь-якого приймача. Їх обрано так, щоб вони виглядали як звичайний журнал приймача: нахил C/N0 у 14 дБ від зеніту до горизонту нижче рівня зеніту (типово 47 дБ-Гц) і зсув для кожного супутника ±1,5 дБ, повільні завмирання 0,8 дБ за 97 с і 0,5 дБ за 31 с, тремтіння 0,3 дБ; пороги відстеження (20 дБ-Гц) і використання (28 дБ-Гц); шум позиції (1,5 м при HDOP 1, корельований за 30 с, вертикальний удвічі більший); шум гіроскопа 0,05 градуса та шум дрейфу 0,03 вуз.; стала часу керування 20 с і радіуси прибуття до шляхових точок моделі судна; а також вибір супутників за азимутними секторами. Типові значення містяться в [receiver] і [vessel] і всі можуть бути змінені в сценарії.
Зчитування назад#
kshana receiver-trust може приймати ці файли (format = "nmea"). Тести перевіряють кожне речення кожного сценарію бібліотеки за його власними правилами кількості полів і контрольної суми та пропускають файли через цей читач, який інтерпретує GGA, RMC і GSV та пропускає інші типи, не відкидаючи жодного. Тести фіксують еталонний уривок для кожного сценарію (tests/fixtures/nmea_training/); після навмисної зміни перегенеруйте за допомогою KSHANA_BLESS_GOLDEN=1.