Tradução automática, não revista por um falante nativo. O inglês é a referência.

Confiança do receptor e marítimoEditar no GitHubFonte: docs/MARITIME-TRUST.md

Confiança marítima: a ponte de comando pode confiar nesta posição?

kshana receiver-trust pode calcular, para uma embarcação em movimento, uma pontuação de confiança de 0 a 100 em tempo real para cada posição, a partir do que qualquer receptor já coloca no barramento (primeiro NMEA 0183), e dizer quais verificações a reduziram. É uma camada de software que fica ao lado de um receptor. Não é um receptor, não substitui um e não torna uma posição mais precisa.

Software de caráter consultivo. O Kshana não é um equipamento de navegação homologado (IEC 61108, IEC 61162) e não foi avaliado em relação a nenhuma norma de desempenho. Sua pontuação e seu gate são auxílios ao julgamento do navegador. O operador continua responsável pela navegação segura da embarcação e por qualquer decisão de confiar, ou de deixar de confiar, em uma posição.

Esta página diz o que as verificações examinam, o que cada uma pode e não pode detectar, como executá-lo em tempo real e o que o gate faz. O mapeamento da pontuação e todos os valores padrão estão em RECEIVER-TRUST.md.

O que ele lê#

Uma embarcação é declarada no arquivo de sessão, com seus limites enunciados antes da execução:

[platform]
kind = "vessel"
max_speed_kn = 20.0        # includes any current you want to allow for: it limits speed over ground
max_accel_mps2 = 0.2
max_turn_rate_dps = 2.0
antenna_height_m = 18.0    # antenna above the waterline
heading_sensor = true      # the stream carries a gyro or compass heading
Sentença Lido
GGA posição, qualidade da posição (0 = sem posição), altitude acima do nível médio do mar, separação do geoide, HDOP
RMC status (A válido, V não), velocidade e rumo sobre o solo, data, modo
VTG velocidade e rumo sobre o solo, quando o RMC não traz
HDT, THS proa (THS apenas quando seu modo não é V)
VHW, VBW velocidade na água (VBW apenas quando seu status é A)
ZDA hora
GSV intensidade do sinal por satélite (dB-Hz), elevação e azimute (para a geometria)
GSA diluição de precisão e satélites usados (para a geometria, onde o GSV não dá céu; e o HDOP declarado que a salvaguarda compara)
$PKSOS,<A/F/N> um status OSNMA informado por um receptor, traduzido para esta sentença pelo adaptador que lê o receptor: A autenticado, F falhou, N sem resultado

Épocas que o próprio receptor diz não serem uma posição por satélite (qualidade GGA 0, ou de 6 a 8: estimada, manual, simulador; status RMC V, ou modo E, M, S, N) não são usadas nas verificações de posição e não carregam evidência em nenhum sentido: o receptor já o disse.

Os primeiros calibration_s segundos formam a linha de base e nunca são pontuados. Cada monitor olha apenas para a época atual e as anteriores, de modo que uma decisão em uma época não muda quando chegam dados posteriores, e um fluxo ao vivo dá a mesma pontuação que o mesmo texto lido como arquivo.

O que cada verificação detecta, e o que ela não consegue detectar#

As direções entre posições (o rumo da derrota usado pela verificação da taxa de giro, o rumo implicado por duas posições) são a direção da corda entre elas, que é o azimute médio da geodésica entre as posições, não o azimute direto na primeira posição; os dois diferem em cerca de metade da convergência dos meridianos entre as posições (0,001 grau para uma linha de base de 260 m a 59,5 graus de latitude).

Uma posição falsificada precisa concordar com a física de um navio e com os outros sensores do navio. Cada verificação compara a posição com um deles. Uma verificação é tão independente quanto sua outra fonte: uma proa de uma bússola GNSS, ou uma "velocidade na água" derivada do GNSS, não é independente do receptor sob teste e não acrescenta nada.

Verificação Detecta Não consegue
kinematic um salto de posição; uma trajetória cujo movimento a velocidade e o rumo informados não explicam; uma velocidade, aceleração ou taxa de giro implícitas acima dos limites declarados da embarcação um arrasto cuja velocidade e cujo rumo informados seguem a trajetória falsa e permanece dentro dos limites (o receptor os deriva de sua própria solução, de modo que concordam entre si); um limite de velocidade declarado alto demais
heading-course uma trajetória que se afasta de onde o navio aponta em mais que o ângulo de deriva declarado um arrasto ao longo do rumo (o rumo não muda); um aproamento do mesmo receptor ou de uma bússola GNSS; uma deriva transversal acima da tolerância (alarme falso); velocidades abaixo de min_speed_kn
speed-log um arrasto que altera a velocidade sobre o fundo em relação à velocidade através da água um registro derivado de GNSS; uma corrente acima da margem (alarme falso); um arrasto mais lento que a margem
sea-level uma altitude que não está onde a superfície do mar e a altura de antena declarada a colocariam um falsificador que mantém a altitude plausível; a tolerância é larga porque o erro vertical isolado é grande; a separação do geoide é registrada, mas não verificada, porque o motor não tem modelo de geoide
cn0-spread, cn0-rise a assinatura de um único transmissor: C/N0 entre os satélites caindo em conjunto, ou subindo em conjunto, em relação à linha de base de calibração um falsificador que molda a potência por satélite; qualquer coisa enquanto a própria linha de base está falsificada; uma cadência de GSV lenta demais para acompanhar
time-consistency o tempo do receptor avançando de forma irregular ou retrocedendo; em relação ao relógio deste computador, o tempo do receptor se afastando dele (somente fluxos em tempo real) um tempo falso consistente e estável; um relógio errado desde o início
osnma uma falha de autenticação OSNMA informada o estado é lido, não verificado; um falsificador que suprime o relatório não é visto
cn0-drop, loss-of-lock, agc, jam-ind negação de potência e perda de satélites, como no caso estático indicam que o ambiente é hostil, não que a posição esteja errada

O que monitores apenas NMEA não conseguem fazer. Um falsificador que reproduz toda a constelação com uma posição autoconsistente, velocidade, rumo compatível com o aproamento, potência plausível por satélite e um relógio coincidente é invisível a todas as verificações acima: nada nas sentenças distingue sua posição de uma real. Tampouco uma repetição de sinais reais gravados. Autenticação (OSNMA, ou um receptor com recursos próprios de antifalsificação) e sensores de navegação independentes são o que fecha essa lacuna; esta camada só pode usar o estado que eles informam. Um fluxo que já está sendo falsificado enquanto a linha de base se forma não tem linha de base limpa. A ausência de um alarme não é evidência de uma boa posição.

Pontuação de confiança#

Cada época após a calibração recebe uma pontuação de 0 a 100 e os motivos de ela não ser 100: quais verificações descontaram, sua estatística contra o limiar e seus pontos. Faixas (limites declarados na sessão): nominal em 90 ou mais, degraded em 55 ou mais, untrusted abaixo disso. O mapeamento, cada peso e seu motivo estão em RECEIVER-TRUST.md; é determinístico e fixado antes de um registro ser pontuado, sem aprendizado e sem nada ajustado a eventos.

Toda saída de embarcação traz o aviso consultivo: a chave advisory do JSON de resultado e de cada linha ao vivo, uma primeira linha de comentário # do CSV (os leitores a pulam com sua opção de comentário), o <desc> e um rodapé do SVG. Saídas de uma execução em lote: session.result.json (com o modelo de pontuação e os descontos de cada época), session.trust.csv (com colunas score e score_reasons) e session.trust.svg, que para uma embarcação é a trajetória informada pelo receptor colorida pela faixa de confiança acima da pontuação ao longo do tempo.

Uma demonstração sintética#

examples/maritime-trust/ contém um registro NMEA fictício de uma balsa em uma rota de Tallinn a Helsinque (um trecho de 50 minutos, cerca de 17 nós, 1 Hz: GGA, RMC, VTG, um aproamento de giroscópio, um log de velocidade, GSV com um céu plausível) no qual um arrasto de posição afasta a posição informada da posição real da embarcação a partir de 1500 s. O receptor continua informando uma posição válida o tempo todo; é a pontuação de confiança que cai, passando pela faixa degradada até a não confiável. session.toml declara todo limiar, os limites da embarcação e todo peso de pontuação antes da execução.

kshana receiver-trust examples/maritime-trust/session.toml
kshana receiver-trust live examples/maritime-trust/session.toml \
  --file examples/maritime-trust/tallinn-helsinki.nmea --gate --json trust.jsonl > gated.nmea

O registro é texto gravado em um arquivo por cargo run --example gen_maritime_trust_demo: não modela nenhum sinal de rádio e não transmite nada. Não é uma medição e a rota é ilustrativa, não uma carta náutica. tests/receiver_trust_maritime_demo.rs fixa o registro e a saída esperada como guardas de regressão sobre os dados sintéticos; eles nada dizem sobre o desempenho de qualquer monitor diante de interferência real.

Pontuação de confiança ao longo de uma travessia sintética, a partir de uma execução real do motor v0.40.0 sobre examples/maritime-trust (um registro NMEA fictício de uma balsa em uma rota de Tallinn a Helsinque, 3001 épocas a 1 Hz). Três faixas compartilham um eixo de tempo. Em cima: a distância entre a posição que o receptor informa e a posição real da embarcação, que permanece dentro de poucos metros até cerca de 1576 s, quando o arrasto de posição roteirizado do registro a afasta, enquanto o receptor continua informando uma posição válida. No meio: a pontuação de confiança de 0 a 100, não calculada durante os primeiros 300 s de calibração, na faixa nominal (pelo menos 90) até o arrasto, depois caindo pela faixa degradada (pelo menos 55) até a faixa não confiável. Embaixo: a faixa de cada época como uma fita colorida com seu nome. A pontuação é consultiva. Software de caráter consultivo, não um equipamento de navegação homologado (IEC 61108, IEC 61162): o operador continua responsável pela navegação da embarcação. O registro é fictício para mostrar o formato e os monitores; não é uma medição e nada diz sobre o desempenho de qualquer receptor.Pontuação de confiança ao longo de uma travessia sintética, a partir de uma execução real do motor v0.40.0 sobre examples/maritime-trust (um registro NMEA fictício de uma balsa em uma rota de Tallinn a Helsinque, 3001 épocas a 1 Hz). Três faixas compartilham um eixo de tempo. Em cima: a distância entre a posição que o receptor informa e a posição real da embarcação, que permanece dentro de poucos metros até cerca de 1576 s, quando o arrasto de posição roteirizado do registro a afasta, enquanto o receptor continua informando uma posição válida. No meio: a pontuação de confiança de 0 a 100, não calculada durante os primeiros 300 s de calibração, na faixa nominal (pelo menos 90) até o arrasto, depois caindo pela faixa degradada (pelo menos 55) até a faixa não confiável. Embaixo: a faixa de cada época como uma fita colorida com seu nome. A pontuação é consultiva. Software de caráter consultivo, não um equipamento de navegação homologado (IEC 61108, IEC 61162): o operador continua responsável pela navegação da embarcação. O registro é fictício para mostrar o formato e os monitores; não é uma medição e nada diz sobre o desempenho de qualquer receptor.

A figura é desenhada por tools/gen_readme_assets.py a partir de uma execução real desta demonstração (kshana receiver-trust examples/maritime-trust/session.toml); a pontuação é consultiva e o registro é fictício.

Especificação da vista do Studio#

Para quem constrói a vista do Studio de uma execução de embarcação. Nada nesta seção está implementado ainda no Studio; o lado do motor (os dados abaixo) está.

Entradas. Qualquer uma destas:

  • o JSON de resultado de uma execução em lote (kshana receiver-trust <session.toml>, ou o mesmo por receiver_trust(toml) no pacote WebAssembly com o registro em linha): score_model (limites de faixa, rampa, retenção de evidência, todo peso), monitors_run, states e epochs[], cada um com t_s, state, alarms, score.{score,band,deductions[]}, marine.{position:[lat,lon], stats, ratios} e, a partir da 0,36, geometry e a class de cada desconto, com geometry_model e geometry_summary no nível superior; ou
  • as linhas JSON ao vivo (esquema acima), um objeto por época, acrescentadas à medida que o fluxo corre. Cada linha traz a position informada pelo receptor (esquema 1,1).

O conjunto de demonstração é examples/maritime-trust/: session.toml, tallinn-helsinki.nmea (o registro) e tallinn-helsinki.truth.csv (t_s,true_lat_deg,true_lon_deg: onde a embarcação realmente esteve, o que nenhum registro real tem). Uma vista deve executar a sessão e ler o arquivo de verdade ao lado.

O que desenhar.

  1. Trajetória, norte para cima, uma escala nos dois eixos: as posições informadas pelo receptor como uma linha colorida pela faixa da época (cinza em calibração, verde nominal, âmbar degradado, vermelho não confiável), início e fim marcados, uma barra de escala. Para a demonstração, a trajetória verdadeira como uma linha fina neutra por baixo, de modo que o arrasto apareça como as duas se separando; uma chave para a trajetória verdadeira e uma leitura do afastamento em metros no instante do cursor.
  2. Linha do tempo da pontuação: a pontuação de 0 a 100 contra o tempo, os limites das faixas (nominal_min, degraded_min de score_model) como linhas tracejadas, a faixa como uma tira por baixo e o início de qualquer evento declarado como um marcador. O eixo do tempo é compartilhado com a trajetória.
  3. Motivos: para a época sob o cursor, os descontos (monitor, razão contra o limiar, pontos) como barras horizontais, o maior primeiro, com o peso que cada monitor carrega de score_model.weights; monitores que rodaram e nada custaram mostrados esmaecidos, monitores que não rodaram (fora de monitors_run) listados como "não executado", nunca como tendo passado. O monitor geometry não tem entrada em score_model.weights: seu teto é geometry_model.cap_in_force.
  4. Tiras de monitores: uma tira fina por monitor em monitors_run mostrando sua razão ao longo do tempo (alarme em 1), de modo que o leitor veja qual verificação se moveu primeiro.
  5. Estado do portão: em uma vista ao vivo, o gate de cada época (off, passed, withheld) como uma tira sob a tira de faixa, e o $PKSHT mais recente. Em uma vista em lote, as épocas que um portão teria retido são aquelas cuja faixa é não confiável, com a retenção de liberação aplicada.
  6. Faixa de geometria (0,36): uma tira sob a tira de faixa colorida por geometry.status (verde boa, âmbar degradada, cinza hachurada desconhecida), em uma família de cores distinta das faixas de inconsistência e de ambiente, com a legenda "geometria: o céu, não o sinal". Descontos coloridos por class (geometria, ambiente, inconsistência) no painel de motivos e nas tiras de monitores, e as contagens de épocas por classe de geometry_summary ao lado das contagens por faixa. Para a época sob o cursor: geometry.note, n_used, HDOP, PDOP, VDOP, source, age_s e um sinalizador quando reported_derived_mismatch está definido (o desconto de geometria é então uma inconsistência). unknown é mostrado como "geometria desconhecida: sem GSV nem GSA no registro", nunca como boa. Os limiares de [geometry] e sua proveniência de geometry_model ficam ao lado dos ajustes da pontuação. A nota consultiva permanece.
  7. Posição informada pelo receptor: o receptor da demonstração informa uma posição válida o tempo todo (qualidade GGA 1, estado RMC A); diga isso na legenda, porque esse é o ponto da vista.

Interações. Um cursor de tempo compartilhado por todos os painéis (arrastar, teclas de seta e um botão de reprodução que repete de 1x a 60x); passar o mouse sobre a trajetória ou sobre qualquer tira o move. Clicar em um desconto destaca as épocas em que aquele monitor esteve acima de seu início. Um filtro de faixa (mostrar apenas épocas degradadas e não confiáveis) e um salto para o primeiro desconto e para a primeira época não confiável. Texto de passagem do mouse em cada nome de monitor dando a descrição de uma linha desta página. Os limiares e pesos da sessão são mostrados, somente leitura, ao lado do gráfico, de modo que o que a pontuação usou fique na tela.

Redação a manter. A página deve dizer que é um software de caráter consultivo e não um equipamento de navegação homologado, que a demonstração é texto sintético com um arrasto fictício e que uma posição sem alarme não é por isso uma boa posição (veja os limites acima). Nenhum valor na página deve ser lido como medida do desempenho das verificações diante de interferência real.

A partir do código#

Para chamadores que mantêm a sessão e os bytes em memória (sem arquivos, sem relógio, sem sockets; seguro para WebAssembly), kshana::receiver_trust::assess tem:

  • assess_vessel_log(session_toml, log_bytes) -> Result<ReceiverTrustResult, String>: um registro inteiro, pontuado como uma execução em lote; o mesmo resultado de kshana receiver-trust <session.toml>, que se serializa no JSON de resultado (score_model, monitors_run, epochs[] com pontuações e descontos).
  • assess_stream_excerpt(session_toml, excerpt_bytes) -> Result<ExcerptAssessment, String>: um trecho limitado de um fluxo (no máximo 2 MiB e 20.000 épocas, e longo o bastante para conter a janela de calibração), pontuado como o modo ao vivo pontua e devolvido no esquema de linhas JSON ao vivo (epochs[], o último $PKSHT, um resumo das contagens por faixa, pontuação mais baixa e final, primeiro instante não confiável). O tempo do receptor não é comparado com um relógio do hospedeiro. O modo ao vivo de longa duração (sockets, um arquivo sendo acrescentado, o portão) é do binário.

Ambos precisam de uma sessão com [platform] kind = "vessel"; sua tabela [log] é opcional.

Execute ao vivo#

# stdin: a serial port read by another tool, or a multiplexer's output
some-nmea-source | kshana receiver-trust live session.toml

# a log file that is being appended to (it starts at the beginning; --from-end skips what is there)
kshana receiver-trust live session.toml --file /var/log/nmea.log --follow

# a TCP server (a multiplexer or a terminal server) or UDP datagrams (a bridge network)
kshana receiver-trust live session.toml --tcp 192.0.2.10:10110
kshana receiver-trust live session.toml --udp 10110

A sessão é o mesmo session.toml de uma execução em lote, com uma tabela [platform] que declara kind = "vessel"; sua tabela [log] não é necessária. As portas seriais ainda não são abertas diretamente: leia a porta com a ferramenta que você já usa (stty e cat, um servidor de terminal, um multiplexador) e redirecione ou encaminhe. A entrada serial direta pode vir depois.

Cada época concluída grava uma linha JSON na saída padrão:

{"seq":941,"t_s":940.0,"time":"2025-06-14T08:15:40.000Z","state":"degraded","score":60.0,
 "deductions":[{"monitor":"heading-course","ratio":1.5,"points":40.0}],
 "alarms":["heading-course"],"gate":"off","note":null,
 "position":{"lat_deg":59.8123456,"lon_deg":24.9012345,"height_m":39.4}}

state é calibrating, nominal, degraded ou untrusted (a faixa da pontuação, com os limites na tabela [score] da sessão); score está ausente durante a calibração; deductions lista quais verificações tiraram pontos e quantos, o maior primeiro.

Esquema de linhas JSON (versão 1,1)#

Um objeto JSON por época concluída, chaves nesta ordem:

Chave Tipo Significado
seq inteiro contagem de épocas informadas, a partir de 1
t_s número segundos desde a primeira época do fluxo
time string ou nulo o tempo da época como o fluxo o declara (ISO-8601 UTC com data depois que um RMC foi visto, senão hh:mm:ss.mmm UTC (date not in log))
state string calibrating, nominal, degraded ou untrusted
score número ou null 0 a 100, uma casa decimal; null durante a calibração ou quando note diz por que nenhuma pôde ser produzida
deductions vetor {"monitor": string, "ratio": number, "points": number, "class": string}, os de mais pontos primeiro; ratio é a estatística sobre o limiar (alarme em 1 ou mais); só os monitores que custam pontos são listados; class (acrescentado na 1,3) é geometry, environment ou inconsistency
alarms vetor de strings monitores em seu limiar ou acima dele
gate string off, passed ou withheld
note string ou nulo por que nenhuma pontuação foi produzida (por exemplo, um sensor de aproamento declarado que está ausente)
position objeto ou null (acrescentado na 1,1) a posição que o receptor informou nesta época, {"lat_deg", "lon_deg", "height_m"} (altura elipsoidal quando a sentença dá a separação do geoide, senão acima do nível médio do mar); null quando o receptor não deu nenhuma, inclusive em épocas de calibração sem posição
advisory string (acrescentado na 1,2) o que este software é e não é: consultivo, não é um equipamento de navegação homologado, o operador continua responsável
geometry objeto (acrescentado na 1,3) a geometria dos satélites: status (good, degraded, unknown quando o fluxo não tem elevações e azimutes GSV nem GSA), reasons (few-satellites, high-pdop, high-hdop, sky-blocked), source (derived, reported, none), n_used, hdop, pdop, vdop, open_fraction, max_azimuth_gap_deg, age_s, reported_hdop, reported_derived_mismatch, note, in_view, signal_loss (satélites à vista mas não rastreados enquanto o DOP à vista é bom: o desconto de geometria é então environment), label (good, fair, poor, unknown: sempre informado, nunca pontuado; a pontuação desconta apenas por poor) (veja RECEIVER-TRUST.md)

Nomes de monitores (kebab-case; 17): 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 e, a partir da 1,3, geometry. Novos nomes podem ser acrescentados em versões posteriores; os consumidores devem tratar um nome de monitor desconhecido como um monitor. O leiaute $PKSHT abaixo leva uma versão de formato em seu primeiro campo; este esquema JSON é a versão 1,3: a 1,1 acrescentou position, a 1,2 advisory, a 1,3 geometry no final e class em cada desconto, e só muda acrescentando chaves no final.

Entrada de autenticação#

O monitor osnma recebe o estado de autenticação por satélite de uma fonte de autenticação por meio de MarineObs::sat_auth (uma lista de (satellite, status), estado Authenticated, Failed ou Unavailable), ao lado do MarineObs::osnma geral. Do lado NMEA, o mesmo é levado por $PKSOS,<A|F|N>[,<sat>:<A|F|N>...], por exemplo $PKSOS,A,E11:A,E19:F. Qualquer falha, geral ou em um satélite, aciona o alarme do monitor e custa todo o seu peso (recebe a razão 1,5); o sucesso não é evidência. O Kshana não verifica nada aqui: a fonte que o faz é responsável pelo estado que informa.

Uma época está completa quando chega a próxima sentença com marca de tempo; as sentenças de instrumentos mais lentos ou não sincronizados (um giroscópio alguns décimos de segundo atrás do receptor) que chegam antes disso pertencem a ela. Se o fluxo parar por [live] idle_flush_s (padrão 1,5 s, ajuste-o acima da maior lacuna entre as sentenças com marca de tempo do seu receptor), a época é concluída como está. Os primeiros calibration_s segundos do fluxo formam a linha de base e nunca são pontuados, de modo que um fluxo que já está sendo falsificado quando é conectado pela primeira vez não tem linha de base limpa: inicie a camada antes da viagem, ou enquanto se sabe que a posição é boa. A janela de calibração se fecha uma só vez, por chegada: uma época cujo tempo é anterior a um tempo já visto (um retrocesso, uma repetição do início do fluxo) nunca é calibração, qualquer que seja o seu tempo; é pontuada com um desconto cheio de time-consistency, que é não confiável. O motor ao vivo pontua cada época sobre as épocas de calibração e o último minuto, mais ou menos, do fluxo, de modo que seu custo por época cresce com calibration_s vezes a taxa de épocas; mantenha a janela curta em um receptor rápido. Seus buffers são limitados (uma enxurrada de linhas sem sentença com marca de tempo é concluída a cada 5.000 linhas, e a calibração e o histórico são limitados a 20.000 épocas).

O tempo do receptor também é comparado com o relógio monotônico deste computador, o que só tem sentido para um fluxo que chega em tempo real. Um registro armazenado alimentado em velocidade total por stdin, TCP ou UDP aciona essa verificação por construção: passe --replay. (--file sem --follow já a implica.) Um --file ... --follow é lido como repetição até ser lido até o fim, e o relógio do hospedeiro é acionado a partir daí, de modo que um atraso acumulado não seja lido como falha de relógio; a verificação então precisa que a janela de calibração ainda esteja aberta (caso contrário não tem linha de base e não é executada).

Término e falhas do fluxo: stdin e um arquivo lido até o fim terminam normalmente (saída 0). Uma conexão TCP fechada pelo par não é um término normal para um fluxo que deveria continuar: a camada sai com código diferente de zero depois de pontuar e gravar tudo o que havia recebido e não se reconecta, portanto execute-a sob um supervisor que a reinicie. Os datagramas UDP que dividem uma sentença são unidos; os leitores de stdin, de um arquivo e de TCP são sustentados por uma fila limitada que pressiona o emissor de volta (nada se perde), ao passo que o UDP, que não pode pressionar de volta, descarta os datagramas que o motor não consegue aceitar e avisa em stderr. --udp <port> faz o bind no endereço de loopback; dê --udp <addr>:<port> para escutar em outro lugar (um aviso é impresso).

$PKSHT: a sentença proprietária#

Cada época também produz uma sentença NMEA 0183, com soma de verificação válida, que --pksht <path|-> grava em um arquivo ou em stdout e que o portão insere no fluxo encaminhado:

$PKSHT,1,080140.25,30.0,U,W,heading-course:40.0/cn0-spread:30.0*2F
Campo Significado
1 1: a versão do formato
2 hora UTC da época, hhmmss.ss; vazio se o fluxo não deu nenhuma
3 pontuação de confiança 0 a 100, uma casa decimal; vazio durante a calibração ou quando nenhuma pôde ser produzida
4 faixa: C em calibração, N nominal, D degradada, U não confiável
5 portão: - desligado, P passou, W retido (a posição foi marcada como inválida no ciclo encaminhado)
6 até dois motivos, monitor:points unidos por /; vazio quando nada foi descontado

O endereço é P + o mnemônico do fabricante KSH + T. Equipamentos que não conhecem uma sentença proprietária a ignoram. O Kshana ignora o seu próprio $PKSHT na entrada.

Modo portão#

--gate coloca o Kshana entre o receptor e o equipamento que usa sua posição (um sistema de cartas, um sistema de controle de rota). O fluxo NMEA entra, sai em stdout e é:

  • repassado sem alteração enquanto a época não é não confiável (e durante a calibração), com um $PKSHT após cada ciclo de sentenças; e
  • marcado como inválido enquanto é não confiável: qualidade GGA 0; estado RMC V (e seu indicador de modo N e estado de navegação V onde presentes); modo GNS N em toda posição; estado GLL V e modo N; modo VTG N onde houver. Os campos de posição são deixados como estavam: o objetivo é que o equipamento veja o próprio receptor declarar a posição inválida e acione seu próprio alarme. Toda sentença reescrita tem sua soma de verificação recalculada.

Uma vez retida, a posição só é liberada depois que as épocas ficarem fora da faixa não confiável por [live] gate_release_s (padrão 30 s), de modo que uma pontuação no limite da faixa não possa fazer a posição oscilar. As linhas JSON vão apenas para onde --json <path> indicar (por padrão, para lugar nenhum, porque o fluxo ocupa stdout).

receiver-nmea-source | kshana receiver-trust live session.toml --gate --json trust.jsonl | downstream-equipment

Para deixar um plotter de cartas se conectar diretamente, sirva o fluxo filtrado por TCP em vez de stdout:

receiver-nmea-source | kshana receiver-trust live session.toml --gate --listen tcp:10110

--listen tcp:<port> escuta no endereço de loopback; tcp:<addr>:<port> escuta em outro lugar (um aviso é impresso, porque quem alcançar o endereço pode ler o fluxo). Qualquer número de clientes pode se conectar; cada um recebe o mesmo fluxo a partir do instante em que se conecta, e os clientes só leem. Cada cliente tem sua própria fila limitada e seu próprio escritor, de modo que um que não consegue acompanhar é descartado (sua conexão é fechada, uma linha em stderr) e nunca atrasa os outros nem a entrada. --listen precisa de --gate; stdout então não leva nada do fluxo, portanto --json - fica livre.

O que o portão não faz e o que saber antes de usá-lo:

  • É opcional. Nada marca uma posição como inválida a menos que --gate seja dado.
  • Ele acrescenta latência: um ciclo fica retido até que sua época seja pontuada, o que leva o tempo até a próxima sentença com marca de tempo (cerca de um ciclo: um segundo para um receptor de 1 Hz), ou idle_flush_s após a última quando o fluxo trava. Equipamentos que precisam de uma sentença a poucas dezenas de milissegundos da saída do receptor não devem ficar atrás dele.
  • É um ponto único na cadeia: se ele para, o fluxo para. Execute-o sob supervisão e decida de antemão o que o equipamento a jusante faz sem um fluxo.
  • Ele falha em modo fechado diante de suas próprias falhas: se os monitores não conseguem produzir uma pontuação após a calibração (um sensor de aproamento declarado que não envia nada, épocas de calibração insuficientes), a época é não confiável, leva uma note e é retida; a retenção de liberação é contada pelo relógio deste computador, não pelo tempo do receptor, que é o que um falsificador controla (para uma repetição só há o tempo do receptor).
  • Ele pode reter uma boa posição (um alarme falso) assim como deixar passar uma falsificada (uma omissão). Os monitores conferem uma posição com a física de um navio e seus outros sensores; uma falsificação que seja consistente com todos eles não é vista (veja os limites abaixo).
  • Ele espera um fluxo de texto NMEA 0183 puro. Quadros binários misturados ao fluxo são encaminhados sem alteração, mas um $PKSHT pode então cair entre os bytes de um quadro.
  • Ele não é homologado e nada nele foi avaliado segundo a IEC 61108 ou a IEC 61162. O operador continua responsável pela navegação da embarcação.