Integrações marítimas: a pontuação de confiança na ponte
A pontuação de confiança marítima do Kshana (MARITIME-TRUST.md) é acessível a partir de software que os navegadores de recreio já usam: um plugin de servidor Signal K e o OpenCPN através de NMEA em modo gate sobre TCP. Uma compilação de referência coloca-a num computador pequeno junto do recetor.
Software de caráter consultivo. O Kshana não é um equipamento de navegação homologado (IEC 61108, IEC 61162) e não foi avaliado face a nenhuma norma de desempenho. Tudo nesta página é um auxiliar do juízo do navegador. O operador continua a ser responsável pela navegação segura da embarcação e por qualquer decisão de confiar, ou de deixar de confiar, numa solução de posição. Nada aqui transmite, e nada aqui torna uma solução de posição mais exata.
| Componente | Onde | Estado em 0,35 |
|---|---|---|
| Extensão para o Signal K | integrations/signalk/ |
entregue, com testes unitários sobre saída sintética registada; não publicado no npm |
| OpenCPN através de NMEA em modo gate sobre TCP | integrations/opencpn/ |
entregue e testado (repetição do lado do consumidor); a própria interface de utilizador do OpenCPN não é acionada por nenhum teste |
| Plugin nativo de painel de pontuação para o OpenCPN | integrations/opencpn/plugin/ |
construído, com a lógica testada unitariamente, e executado dentro do OpenCPN 5.8.4 sob um ecrã virtual (evidências em integrations/opencpn/evidence/); não empacotado para o gestor de plugins; só é entregue após revisão |
| Compilação de referência | deploy/reference-build/ |
peças, passos do sistema operativo, unidades systemd (verificadas em simulação), uma opção de contentor; não testada numa embarcação |
Tudo isto lê uma só coisa: a saída de kshana receiver-trust live (linhas JSON, a sentença $PKSHT, o gate). Os formatos de transmissão constam de MARITIME-TRUST.md. O plugin Signal K guarda o seu conhecimento deles num só ficheiro, integrations/signalk/lib/adapter.js; uma alteração de formato é uma edição de um só ficheiro.
Extensão para o Signal K#
Sem dependências npm. O próprio plugin corre em Node 18 ou posterior; as versões atuais de signalk-server exigem Node 22 ou posterior, pelo que, na prática, se usa 22. Copie ou ligue integrations/signalk/ para a pasta node_modules do servidor (por exemplo ~/.signalk/node_modules/signalk-kshana-trust), reinicie o servidor e ative Kshana GNSS trust em Server, Plugin Config.
De onde vêm as épocas (source)#
source |
O que o plugin faz |
|---|---|
spawn-signalk-nmea (predefinição) |
Executa kshana receiver-trust live <sessionFile> e escreve na sua entrada padrão cada sentença NMEA 0183 em bruto que o servidor recebe (o evento nmea0183 do servidor). Reinicia-o, com recuo progressivo, se terminar | Use-o apenas num servidor com uma única fonte NMEA: alimenta todas as sentenças de todos os fornecedores, intercaladas, pelo que um segundo recetor GNSS ou o AIS parece um só recetor cuja posição salta e pode ser pontuado como spoofing. Com mais de uma fonte, use spawn-args com o fluxo --tcp do próprio recetor, ou tcp-pksht a partir de um gate. |
spawn-args |
Executa kshana receiver-trust live <sessionFile> <inputArgs...>, por exemplo ["--tcp","192.0.2.10:10110"] ou ["--udp","10110"] |
tcp-json |
Liga-se a uma alimentação das linhas JSON que kshana escreve (por exemplo, a partir do retransmissor em integrations/opencpn/) |
tcp-pksht |
Liga-se a um fluxo NMEA (como a saída do gate do serviço ou do contentor na compilação de referência, porta 10110) e lê apenas as suas sentenças $PKSHT |
Um fluxo que já esteja a ser alvo de spoofing quando o monitor se liga pela primeira vez não tem linha de base limpa (os primeiros calibration_s segundos nunca são pontuados). Inicie-o antes da viagem.
Caminhos publicados#
Cada época atualiza (fonte kshana-trust):
| Caminho | Valor |
|---|---|
navigation.gnss.kshana.band |
calibrating, nominal, degraded ou untrusted |
navigation.gnss.kshana.score |
0 a 100, ou null durante a calibração |
navigation.gnss.kshana.reasons |
[{monitor, points}], do maior para o menor: que verificações reduziram a pontuação |
navigation.gnss.kshana.alarms |
os monitores que estão neste momento acima do seu limiar |
navigation.gnss.kshana.gate |
off, passed ou withheld (quando um gate está em execução noutro local; null a partir de $PKSHT quando ausente) |
navigation.gnss.kshana.reportedPosition |
{latitude, longitude, altitude}: a posição que o recetor em teste indicou nessa época (esquema JSON 1,1 ou posterior; não publicada quando o fluxo não a traz, por exemplo a partir de $PKSHT ou de um kshana mais antigo). Apenas contexto de confiança: o plugin nunca escreve navigation.position, pelo que o caminho de posição da própria embarcação não é tocado |
notifications.navigation.gnss.kshana.trust |
emitida quando o estado muda: {state, method, message} |
O plugin envia também, uma vez no arranque, metadados Signal K para estes caminhos. score é a pontuação de confiança de 0 a 100 do Kshana, uma pontuação e não uma razão nem uma probabilidade, pelo que não tem unidade SI; os seus metadados dão o intervalo, uma descrição e zonas que seguem os limiares abaixo. reasons[].points são pontos de pontuação retirados a essa pontuação de 0 a 100.
A notificação é normal, alert, warn ou alarm. É limpa publicando o estado normal, não null: um consumidor que só trate null como limpo deve tratar normal também como limpo. Por predefinição, a faixa degraded dá warn e a faixa untrusted dá alarm, com method: ["visual","sound"]. É reduzida após clearAfterEpochs épocas num estado melhor. Se não chegar nenhuma época durante staleAfterS segundos, avisa que a pontuação não está a ser atualizada. A calibração nunca gera uma notificação. A fonte $PKSHT não traz lista alarms (a sentença tem apenas a faixa, a pontuação e os dois principais motivos).
Configuração (todos os limiares)#
| Chave | Predefinição | Significado |
|---|---|---|
source |
spawn-signalk-nmea |
ver acima |
command |
kshana |
o executável (modos spawn). Executa qualquer executável como utilizador do servidor: só um administrador do servidor Signal K deve poder alterar a configuração do plugin |
sessionFile |
/etc/kshana/session.toml |
os limites, limiares e pesos de pontuação da embarcação (modos spawn) |
inputArgs |
[] |
argumentos acrescentados a kshana receiver-trust live <sessionFile> nos dois modos spawn: a entrada para spawn-args, sinalizadores adicionais como --replay (um registo guardado alimentado mais depressa do que em tempo real) para spawn-signalk-nmea. O plugin recusa --gate, --listen, --json e --pksht (lê o JSON de kshana na saída padrão) e, para spawn-signalk-nmea, também os sinalizadores de entrada (--stdin, --file, --tcp, --udp, --follow, --from-end) |
host, port |
127.0.0.1, 10111 |
Modos TCP (o gate serve em 10110: defina a porta para tcp-pksht) |
thresholdMode |
band |
band: a faixa que o Kshana indica, com os limites no ficheiro de sessão. score: as duas pontuações abaixo |
warnBelowScore |
90 |
modo score: abaixo deste valor o estado é degradedState (limite predefinido do Kshana para nominal) |
alarmBelowScore |
55 |
modo score: abaixo deste valor o estado é alarm (limite predefinido do Kshana para degraded) |
degradedState |
warn |
estado para a faixa degraded: normal, alert ou warn |
raiseAfterEpochs |
1 |
épocas consecutivas num estado pior antes de a notificação ser emitida |
clearAfterEpochs |
10 |
épocas consecutivas num estado melhor antes de ser reduzida |
staleAfterS |
10 |
avisar quando não chega nenhuma época durante este tempo; 0 desativa-o |
method |
["visual","sound"] |
métodos de notificação |
O plugin não faz de gate: nunca altera uma solução de posição. Marcar uma solução de posição como inválida para um sistema de cartas é o gate, abaixo.
Testes#
cd integrations/signalk && npm test
node:test, sem dependências, sem rede para além de 127.0.0.1. Os testes interpretam e repetem um excerto de 66 épocas das linhas JSON e das sentenças $PKSHT que kshana receiver-trust live escreveu para a demonstração sintética do Báltico (dados fictícios, só texto), verificam a concordância entre os dois formatos, o comportamento de retenção e de limpeza, o modo de pontuação, a obsolescência e os três caminhos de entrada face a uma alimentação local e a um processo-filho de substituição. Esses testes não iniciam um servidor Signal K real; uma execução separada com script fá-lo, ver abaixo.
Execução num servidor Signal K real#
integrations/signalk/evidence/ contém um script que carrega o plugin no pacote signalk-server 2.33.0 fixado (num espaço de nomes de rede privado, só de loopback) e regista o que as próprias APIs REST e WebSocket do servidor mostram. Os caminhos de confiança aparecem, a notificação passa por warn, alarm, normal (após a retenção) e warn de novo quando os dados cessam, e com o kshana real alimentado pela própria entrada NMEA do servidor publicou também reportedPosition, nunca navigation.position. Uma versão do servidor, uma execução, nenhuma embarcação real; limites e reprodução no README das evidências.
OpenCPN#
O OpenCPN lê NMEA a partir da rede, e kshana receiver-trust live --gate pode servir o fluxo filtrado por gate numa porta TCP por si próprio.
Recomendado: a ligação direta.
some-nmea-source | kshana receiver-trust live session.toml --gate --listen tcp:10110
(some-nmea-source é a porta série do recetor lida com stty e cat, um multiplexador, ou as entradas --tcp/--udp do próprio kshana; o kshana-gate.service da compilação de referência faz isto sob o systemd.) --listen tcp:<port> liga-se ao endereço de loopback; qualquer número de clientes só de leitura se pode ligar, e aquele que não consiga acompanhar é descartado sem atrasar os outros.
Alternativa: o retransmissor. integrations/opencpn/nmea-tcp-relay.mjs é um distribuidor Node sem dependências que faz o mesmo para um kshana sem --listen, ou serve qualquer outro fluxo de linhas (a opção de contentor usa-o para as épocas JSON):
some-nmea-source | kshana receiver-trust live session.toml --gate --json trust.jsonl \
| node integrations/opencpn/nmea-tcp-relay.mjs --listen 10110
Encaminha os bytes sem alterações, apenas linhas inteiras, e descarta um cliente que não leia. Prefira a ligação direta: menos um processo na cadeia.
No OpenCPN: Options, Connections, Add Connection, Network, protocolo TCP, endereço 127.0.0.1, porta 10110, direção Input. Enquanto a confiança não está colapsada, o fluxo é o do próprio recetor, mais uma sentença $PKSHT por ciclo. Enquanto está colapsada, a GGA encaminhada tem qualidade 0, a RMC tem estado V e os indicadores de modo dizem sem posição, com os checksums recalculados: o equipamento vê o próprio recetor declarar a solução de posição inválida e emite o seu próprio alarme. As sentenças $PKSHT são visíveis na janela de depuração NMEA do OpenCPN (Options, Connections, "NMEA debug window"). Os campos de posição ficam como foram recebidos.
Leia "Gate mode" em MARITIME-TRUST.md antes de confiar nisto: é facultativo, acrescenta latência (um ciclo fica retido até a sua época ser pontuada), é um ponto único na cadeia e pode reter uma boa solução de posição, além de deixar passar uma falsificada.
Testes#
cd integrations/opencpn && npm test
test/relay.test.mjs repete um excerto registado do fluxo sintético filtrado por gate (186 sentenças em torno do momento em que a confiança colapsa, escritas por kshana receiver-trust live --gate) através do retransmissor sobre um socket TCP real e verifica o que um consumidor recebe: o fluxo byte a byte sob fragmentação arbitrária; checksums válidos em todas as sentenças, incluindo as reescritas; GGA e RMC concordantes, válidas e depois inválidas sem oscilação; uma $PKSHT por ciclo com o seu campo de gate W exatamente onde a solução de posição é inválida; dois consumidores idênticos; uma linha parcial nunca encaminhada. Isto verifica o fluxo que é entregue ao OpenCPN, não o ecrã do próprio OpenCPN.
Uma verificação de ponta a ponta com o binário real, que não faz parte dos testes predefinidos (precisa de um kshana compilado):
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
Ambos executam a demonstração sintética de uma hora e verificam que a solução de posição passa a válida e depois a inválida, com uma $PKSHT por época.
Plugin nativo#
integrations/opencpn/plugin/ é um painel de pontuação para o OpenCPN (pontuação, faixa, gate, principais motivos, um alerta quando a confiança passa a untrusted) que lê $PKSHT do próprio fluxo NMEA do OpenCPN, pelo que não precisa de nenhuma ligação para além da acima. É compilado com CMake contra o cabeçalho da API de plugins do OpenCPN (incluído, com o seu commit de origem) e o wxWidgets; ctest executa os testes de lógica sem wx e verifica que a biblioteca exporta os pontos de entrada que o OpenCPN procura. Foi executado dentro do OpenCPN 5.8.4 sob um ecrã virtual no fluxo sintético filtrado por gate (o OpenCPN entregou $PKSHT ao plugin, o painel ficou vermelho e a própria posição do OpenCPN congelou quando o gate entrou em ação; capturas de ecrã, registo e script em integrations/opencpn/evidence/), mas não num ambiente de trabalho real, noutras versões ou plataformas, e não está empacotado para o gestor de plugins do OpenCPN. Ver o README.md do plugin. Está licenciado sob GPL-3.0-or-later, ao contrário do resto do repositório (LICENSING.md).
Capturas de ecrã dessa execução, em integrations/opencpn/evidence/ (uma execução, OpenCPN 5.8.4 sob um ecrã virtual, dados sintéticos; README.md indica os limites):
Estas mostram a canalização e a solução de posição filtrada por gate a chegar ao OpenCPN, não que algum alarme seja atempado ou correto para uma embarcação real. Software de caráter consultivo, não um equipamento homologado.
Compilação de referência#
deploy/reference-build/: uma lista genérica de peças, a configuração do sistema operativo, duas unidades systemd (o monitor consultivo e o gate facultativo), uma opção de contentor e a ligação ao Signal K. deploy/reference-build/check-units.sh executa systemd-analyze verify nas unidades sem iniciar nada.
Limites#
- A pontuação vale apenas o que valem as verificações que a sustentam, que observam a física do navio e os seus outros sensores; uma falsificação autoconsistente com um relógio coincidente não é vista por monitores apenas NMEA (
MARITIME-TRUST.md). A ausência de alarme não é evidência de uma boa solução de posição. - Nenhum valor nesta página é uma taxa de deteção ou de falso alarme medida. Os testes acima verificam a canalização em texto sintético; nada dizem sobre o desempenho de qualquer monitor perante interferência real.
- Não homologado; o operador continua a ser responsável.