Морские интеграции: оценка доверия на мостике
Морская оценка доверия Kshana (MARITIME-TRUST.md) доступна из программ, которыми судоводители уже пользуются: плагин сервера Signal K и OpenCPN через NMEA в режиме gate по TCP. Эталонная сборка размещает её на небольшом компьютере рядом с приёмником.
Программное обеспечение консультативного характера. Kshana не является навигационным оборудованием с утверждённым типом (IEC 61108, IEC 61162) и не оценивалась по какому-либо стандарту характеристик. Всё на этой странице — вспомогательное средство для суждения штурмана. Ответственность за безопасное судовождение и за любое решение полагаться на определение места или перестать полагаться на него остаётся на операторе. Ничто здесь ничего не передаёт, и ничто здесь не делает определение места точнее.
| Компонент | Где | Состояние в 0,35 |
|---|---|---|
| Плагин Signal K | integrations/signalk/ |
поставляется, проверен модульными тестами на записанных синтетических выходных данных; не опубликован в npm |
| OpenCPN через NMEA в режиме gate по TCP | integrations/opencpn/ |
поставляется и проверен (воспроизведение на стороне потребителя); сам интерфейс OpenCPN не проверяется ни одним тестом |
| Нативный плагин панели оценки OpenCPN | integrations/opencpn/plugin/ |
создан, логика проверена модульными тестами и запущена в OpenCPN 5.8.4 на виртуальном дисплее (свидетельства в integrations/opencpn/evidence/); не упакован для менеджера плагинов; будет поставляться только после проверки |
| Эталонная сборка | deploy/reference-build/ |
детали, шаги для ОС, юниты systemd (проверены пробным запуском), вариант с контейнером; на судне не проверялась |
Всё это читает одно: выходные данные kshana receiver-trust live (строки JSON, предложение $PKSHT, шлюз). Форматы передачи описаны в MARITIME-TRUST.md. Плагин Signal K хранит знания о них в одном файле, integrations/signalk/lib/adapter.js; изменение формата — это правка одного файла.
Плагин Signal K#
Зависимостей npm нет. Сам плагин работает на Node 18 или новее; текущим выпускам signalk-server нужен Node 22 или новее, поэтому на практике используйте 22. Скопируйте или свяжите integrations/signalk/ с каталогом node_modules сервера (например, ~/.signalk/node_modules/signalk-kshana-trust), перезапустите сервер и включите Kshana GNSS trust в разделе Server, Plugin Config.
Откуда берутся эпохи (source)#
source |
Что делает плагин |
|---|---|
spawn-signalk-nmea (по умолчанию) |
Запускает kshana receiver-trust live <sessionFile> и записывает на его стандартный вход каждое необработанное предложение NMEA 0183, которое получает сервер (событие nmea0183 сервера). Перезапускает его с задержкой, нарастающей при повторах, если он завершается | Используйте его только на сервере с одним источником NMEA: он передаёт каждое предложение от каждого поставщика вперемешку, поэтому второй приёмник ГНСС или AIS выглядит как один приёмник, у которого скачет положение, и может быть оценён как спуфинг. При нескольких источниках используйте spawn-args с собственным потоком --tcp этого приёмника или tcp-pksht от шлюза. |
spawn-args |
Запускает kshana receiver-trust live <sessionFile> <inputArgs...>, например ["--tcp","192.0.2.10:10110"] или ["--udp","10110"] |
tcp-json |
Подключается к потоку строк JSON, которые записывает kshana (например, от ретранслятора в integrations/opencpn/) |
tcp-pksht |
Подключается к потоку NMEA (например, к выходу шлюза службы или контейнера в эталонной сборке, порт 10110) и читает только его предложения $PKSHT |
У потока, который уже подвергается спуфингу к моменту первого подключения монитора, нет чистого базового уровня (первые calibration_s с никогда не оцениваются). Запустите его до рейса.
Публикуемые пути#
Каждая эпоха обновляет (источник kshana-trust):
| Путь | Значение |
|---|---|
navigation.gnss.kshana.band |
calibrating, nominal, degraded или untrusted |
navigation.gnss.kshana.score |
от 0 до 100 или null во время калибровки |
navigation.gnss.kshana.reasons |
[{monitor, points}], сначала наибольшие: какие проверки снизили оценку |
navigation.gnss.kshana.alarms |
мониторы, которые сейчас превышают свой порог |
navigation.gnss.kshana.gate |
off, passed или withheld (когда шлюз работает в другом месте; null из $PKSHT, если его нет) |
navigation.gnss.kshana.reportedPosition |
{latitude, longitude, altitude}: положение, которое проверяемый приёмник сообщил в эту эпоху (схема JSON 1,1 или новее; не публикуется, если в потоке его нет, например от $PKSHT или более старого kshana). Только контекст доверия: плагин никогда не пишет navigation.position, поэтому собственный путь положения судна не затрагивается |
notifications.navigation.gnss.kshana.trust |
публикуется при смене состояния: {state, method, message} |
Плагин также один раз при запуске отправляет метаданные Signal K для этих путей. score — оценка доверия Kshana от 0 до 100: именно оценка, а не отношение и не вероятность, поэтому у неё нет единицы СИ; её метаданные задают диапазон, описание и зоны, следующие порогам ниже. reasons[].points — баллы оценки, вычтенные из этой оценки от 0 до 100.
Уведомление имеет состояние normal, alert, warn или alarm. Оно снимается публикацией состояния normal, а не null: потребитель, который считает снятым только null, должен считать снятым и normal. По умолчанию полоса degraded даёт warn, а полоса untrusted даёт alarm, с method: ["visual","sound"]. Оно понижается через clearAfterEpochs эпох при лучшем состоянии. Если эпохи не поступают в течение staleAfterS с, выдаётся предупреждение, что оценка не обновляется. Калибровка никогда не вызывает уведомления. Источник $PKSHT не содержит списка alarms (в предложении есть только полоса, оценка и две главные причины).
Конфигурация (каждый порог)#
| Ключ | По умолчанию | Значение |
|---|---|---|
source |
spawn-signalk-nmea |
см. выше |
command |
kshana |
исполняемый файл (режимы запуска). Он запускает любой исполняемый файл от имени пользователя сервера: изменять конфигурацию плагина должен мочь только администратор сервера Signal K |
sessionFile |
/etc/kshana/session.toml |
ограничения судна, пороги и веса оценки (режимы запуска) |
inputArgs |
[] |
аргументы, добавляемые к kshana receiver-trust live <sessionFile> в обоих режимах запуска: вход для spawn-args, дополнительные флаги, например --replay (сохранённый журнал подаётся быстрее реального времени), для spawn-signalk-nmea. Плагин отклоняет --gate, --listen, --json и --pksht (он читает JSON от kshana на стандартном выводе), а для spawn-signalk-nmea также входные флаги (--stdin, --file, --tcp, --udp, --follow, --from-end) |
host, port |
127.0.0.1, 10111 |
Режимы TCP (шлюз обслуживает порт 10110: задайте порт для tcp-pksht) |
thresholdMode |
band |
band: полоса, о которой сообщает Kshana, с границами в файле сессии. score: две оценки ниже |
warnBelowScore |
90 |
Режим score: ниже этого значения состояние degradedState (граница Kshana по умолчанию для nominal) |
alarmBelowScore |
55 |
Режим score: ниже этого значения состояние alarm (граница Kshana по умолчанию для degraded) |
degradedState |
warn |
состояние для полосы degraded: normal, alert или warn |
raiseAfterEpochs |
1 |
число последовательных эпох с худшим состоянием до выдачи уведомления |
clearAfterEpochs |
10 |
число последовательных эпох с лучшим состоянием до его понижения |
staleAfterS |
10 |
предупреждать, если эпохи не поступают столько времени; 0 отключает |
method |
["visual","sound"] |
способы уведомления |
Плагин не работает как шлюз: он никогда не изменяет определение места. Пометка определения места как недействительного для картографической системы — это шлюз, см. ниже.
Тесты#
cd integrations/signalk && npm test
node:test, без зависимостей, без сети, кроме 127.0.0.1. Тесты разбирают и воспроизводят фрагмент из 66 эпох строк JSON и предложений $PKSHT, которые kshana receiver-trust live записал для синтетической демонстрации в Балтийском море (вымышленные данные, только текст), проверяют согласие двух форматов, поведение удержания и снятия, режим оценки, устаревание и три пути ввода на локальном потоке и на подставном дочернем процессе. Эти тесты не запускают настоящий сервер Signal K; это делает отдельный сценарный запуск, см. ниже.
Запуск на настоящем сервере Signal K#
В integrations/signalk/evidence/ лежит скрипт, который загружает плагин в закреплённый пакет signalk-server 2.33.0 (в отдельном сетевом пространстве имён только с локальной петлёй) и записывает то, что показывают собственные API REST и WebSocket сервера. Пути доверия появляются, уведомление проходит warn, alarm, normal (после удержания) и снова warn, когда данные прекращаются, а при настоящем kshana, получающем собственный вход NMEA сервера, он также публиковал reportedPosition, но никогда navigation.position. Одна версия сервера, один прогон, настоящего судна нет; ограничения и воспроизведение описаны в README свидетельств.
OpenCPN#
OpenCPN читает NMEA из сети, а kshana receiver-trust live --gate может сам обслуживать отфильтрованный шлюзом поток на порту TCP.
Рекомендуется: прямое подключение.
some-nmea-source | kshana receiver-trust live session.toml --gate --listen tcp:10110
(some-nmea-source — последовательный порт приёмника, прочитанный через stty и cat, мультиплексор или входы --tcp/--udp самого kshana; kshana-gate.service эталонной сборки делает это под systemd.) --listen tcp:<port> привязывается к адресу петли; подключиться может любое число клиентов только для чтения, а тот, кто не успевает, отключается без задержки остальных.
Альтернатива: ретранслятор. integrations/opencpn/nmea-tcp-relay.mjs — разветвитель на Node без зависимостей, делающий то же для kshana без --listen или обслуживающий любой другой построчный поток (вариант с контейнером использует его для эпох JSON):
some-nmea-source | kshana receiver-trust live session.toml --gate --json trust.jsonl \
| node integrations/opencpn/nmea-tcp-relay.mjs --listen 10110
Он передаёт байты без изменений, только целые строки, и отключает клиента, который не читает. Предпочтительно прямое подключение: в цепочке одним процессом меньше.
В OpenCPN: Options, Connections, Add Connection, Network, протокол TCP, адрес 127.0.0.1, порт 10110, направление Input. Пока доверие не упало, поток — это собственный поток приёмника плюс одно предложение $PKSHT за цикл. Когда оно упало, пересылаемое GGA имеет качество 0, статус RMC V, а индикаторы режима сообщают об отсутствии определения места, с пересчитанными контрольными суммами: оборудование видит, как сам приёмник объявляет определение места недействительным, и поднимает собственную тревогу. Предложения $PKSHT видны в окне отладки NMEA в OpenCPN (Options, Connections, «NMEA debug window»). Поля положения остаются такими, как получены.
Прочтите «Gate mode» в MARITIME-TRUST.md, прежде чем на это полагаться: это включается по желанию, добавляет задержку (цикл удерживается, пока его эпоха не оценена), является единой точкой в цепочке и может задержать хорошее определение места так же, как пропустить подложное.
Тесты#
cd integrations/opencpn && npm test
test/relay.test.mjs воспроизводит записанный фрагмент синтетического потока, прошедшего шлюз (186 предложений вокруг момента, когда доверие падает, записанных kshana receiver-trust live --gate), через ретранслятор по настоящему сокету TCP и проверяет, что получает потребитель: поток байт в байт при произвольном разбиении на части; корректные контрольные суммы в каждом предложении, включая переписанные; согласие GGA и RMC, сначала корректных, затем некорректных без мерцания; одно $PKSHT за цикл с полем шлюза W ровно там, где определение места недействительно; два одинаковых потребителя; неполная строка никогда не пересылается. Это проверяет поток, который получает OpenCPN, а не собственный экран OpenCPN.
Сквозная проверка с настоящим исполняемым файлом, не входящая в тесты по умолчанию (нужен собранный kshana):
KSHANA_BIN=target/release/kshana npm run e2e # kshana --gate --listen | TCP consumer; the consumer's bytes must equal the gate's stdout
KSHANA_BIN=target/release/kshana npm run e2e -- relay # kshana --gate | relay | TCP consumer
Оба запускают часовую синтетическую демонстрацию и проверяют, что определение места становится сначала действительным, затем недействительным, с $PKSHT на каждую эпоху.
Нативный плагин#
integrations/opencpn/plugin/ — панель оценки для OpenCPN (оценка, полоса, шлюз, главные причины, предупреждение, когда доверие становится untrusted), читающая $PKSHT из собственного потока NMEA OpenCPN, поэтому ей не нужно никакого подключения, кроме описанного выше. Она собирается с помощью CMake против заголовка API плагинов OpenCPN (вендорного, с его исходным коммитом) и wxWidgets; ctest запускает тесты логики без wx и проверяет, что библиотека экспортирует точки входа, которые ищет OpenCPN. Она запускалась внутри OpenCPN 5.8.4 на виртуальном дисплее на синтетическом потоке, прошедшем шлюз (OpenCPN передал $PKSHT плагину, панель стала красной, а собственное положение OpenCPN замерло, когда включился шлюз; снимки экрана, журнал и скрипт в integrations/opencpn/evidence/), но не на настоящем рабочем столе, не в других версиях и не на других платформах, и она не упакована для менеджера плагинов OpenCPN. См. README.md плагина. Она распространяется под GPL-3.0-or-later, в отличие от остальной части репозитория (LICENSING.md).
Снимки экрана из этого прогона, в integrations/opencpn/evidence/ (один прогон, OpenCPN 5.8.4 на виртуальном дисплее, синтетические данные; ограничения указаны в README.md):
Это показывает прокладку каналов и то, что определение места после шлюза доходит до OpenCPN, но не то, что какая-либо тревога своевременна или верна для настоящего судна. Программное обеспечение консультативного характера, не оборудование с утверждённым типом.
Эталонная сборка#
deploy/reference-build/: общий перечень деталей, настройка ОС, два юнита systemd (консультативный монитор и включаемый по желанию шлюз), вариант с контейнером и подключение к Signal K. deploy/reference-build/check-units.sh запускает systemd-analyze verify на юнитах, ничего не запуская.
Ограничения#
- Оценка так же хороша, как проверки за ней, которые смотрят на физику судна и его другие датчики; самосогласованная подделка с совпадающими часами не видна мониторам, работающим только по NMEA (
MARITIME-TRUST.md). Отсутствие тревоги не является свидетельством хорошего определения места. - Ни одна величина на этой странице не является измеренной частотой обнаружения или ложной тревоги. Приведённые выше тесты проверяют прокладку каналов на синтетическом тексте; они ничего не говорят о том, как какой-либо монитор справляется с реальными помехами.
- Не является оборудованием с утверждённым типом; ответственность остаётся на операторе.