Fluxos NMEA de treinamento: kshana nmea-scenario
Academias marítimas e centros de simuladores podem alimentar um simulador de passadiço ou um plotter de cartas com NMEA 0183 gerado pelo Kshana para treinar tripulações a reconhecer jamming e spoofing de GNSS.
Somente treinamento e teste. Os fluxos gerados são sintéticos. Nunca devem ser alimentados aos sistemas de navegação em operação de uma embarcação. O Kshana é de caráter consultivo, não um equipamento de navegação homologado. Todo fluxo começa com uma sentença proprietária de marcação, que se repete a cada dez segundos,
$PKSHT,TRAINING,SYNTHETIC,NOT-FOR-NAVIGATION, a qual os receptores ignoram;--no-markera remove para um simulador que rejeita sentenças desconhecidas. Isto grava somente texto NMEA: não há saída de RF, de IQ nem de forma de onda, e nada transmite exceto o texto para o endereço que você indicar.
A figura é desenhada a partir do log do instrutor de scenarios/training/coastal-drag-off.toml.
Uso#
# 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
| Opção | Significado |
|---|---|
--out <file> |
grava o fluxo num arquivo; sem saída de rede o padrão é <name>.nmea |
--instructor-log <prefix> |
grava <prefix>.instructor.json e <prefix>.instructor.txt (padrão: ao lado do arquivo) |
--tcp <addr:port> ou <port> |
servidor TCP; a execução começa quando um cliente se conecta (--wait-clients <n>). Uma porta sozinha escuta apenas em 127.0.0.1 |
--udp <addr:port> ou <port> |
UDP, uma sentença por datagrama; --broadcast permite um endereço de broadcast. Uma porta sozinha envia para 127.0.0.1 |
--realtime, --speed <x> |
ritma o fluxo; --speed 0 é o mais rápido possível. A saída de rede usa tempo real por padrão, os arquivos o mais rápido possível |
--seed <n> |
substitui a semente do cenário |
--no-marker |
omite a sentença de marcação |
Não há autenticação. Um servidor TCP com um endereço não local serve o fluxo a qualquer pessoa que consiga alcançá-lo, e o UDP envia para qualquer endereço que você indicar. Um cliente TCP que não recebe seus dados em dois segundos é descartado, de modo que um consumidor parado não pode atrasar os demais.
A mesma semente dá os mesmos bytes numa plataforma, e o ritmo nunca altera os bytes. Entre plataformas a aritmética própria do gerador é idêntica bit a bit (ele usa a camada de matemática portátil), mas as posições dos satélites e os ângulos de visada vêm de funções do motor que chamam a biblioteca matemática do hospedeiro, de modo que, num caso limítrofe raro, um dígito arredondado de elevação, azimute ou C/N0 em GSV pode diferir entre plataformas.
O que é gravado#
Cada época (1, 2, 5 ou 10 por segundo) traz: RMC, GGA, GNS (solução, posição, tempo), GSA (satélites usados e DOP, um por constelação), GSV (satélites à vista com elevação, azimute e SNR em dB-Hz, um conjunto por constelação, ID de sinal NMEA 4,10), VTG (rumo e velocidade sobre o fundo), ZDA (hora e data), HDT (rumo do girocompasso) e VBW (velocidade na água e sobre o fundo a partir da barquilha). O talker é GN com várias constelações, caso contrário o da própria constelação. As linhas terminam em CRLF e têm checksum válido.
HDT e VBW representam sensores independentes (um girocompasso e uma barquilha Doppler). Nenhum evento os toca, o que é deliberado: a diferença entre o que o GNSS relata e o que o girocompasso e a barquilha dizem é uma pista para o debriefing.
Quando a solução é perdida, a qualidade do GGA é 0, o status do RMC é V, os campos de posição ficam vazios e os campos de tempo continuam (o relógio do receptor continua rodando), como nos receptores reais.
A embarcação#
[vessel] e [[waypoint]] dão uma posição inicial, uma velocidade e os pontos pelos quais governar. A embarcação é governada rumo ao próximo ponto de passagem sob um limite de taxa de guinada (max_rot_deg_per_min, padrão 30), um limite de quão rápido a taxa de guinada se constrói (max_rot_accel_deg_per_s2) e um limite de variação de velocidade (max_accel_kn_per_min). A velocidade de um trecho é o speed_kn do ponto de passagem. [environment] acrescenta uma corrente, de modo que a velocidade sobre o fundo difere da velocidade na água e o rumo difere do rumo sobre o fundo, e uma variação magnética. stop_at_end leva a embarcação ao repouso no último ponto de passagem.
Geometria dos satélites#
Os satélites vêm das próprias constelações nominais do motor (tabela de slots do GPS, padrões Walker MEO do Galileo e do BeiDou, ICD do GLONASS), propagados por constellation::satellite_positions_fixed (dois corpos mais J2 secular, rotação da Terra) e vistos da embarcação com os ângulos de visada WGS-84 do motor. A contagem visível, as elevações e os azimutes em GSV são plausíveis para o lugar e a hora do dia. Este é um céu nominal, não uma efeméride para a data do fluxo: os PRNs são atribuídos na ordem dos slots e cada constelação mantém o seu layout de slots publicado, propagado continuamente a partir da época da tabela de slots do GPS (2016-12-31) sob J2, de modo que o layout está intacto, mas a sua orientação não é a do céu real na data do fluxo. O C/N0 acompanha a elevação (um nível nominal de zênite, definido em [receiver]), com um deslocamento fixo por satélite, desvanecimento lento e ruído. Um satélite abaixo de track_threshold_dbhz tem o campo de SNR vazio; um abaixo de use_threshold_dbhz é rastreado, mas não usado. Até max_used (padrão 12, o teto do GGA do NMEA) satélites utilizáveis são usados, escolhidos pela geometria (primeiro o satélite mais alto em cada setor de azimute de 30 graus). Menos de quatro satélites utilizáveis perdem a solução, e ela volta após reacquire_s de sinal utilizável.
Eventos#
Cada [[event]] tem um kind, um início start_s, uma duration_s (do início até o começo da liberação), uma rampa de início ramp_s e uma rampa de liberação recovery_s. A intensidade sobe linearmente ao longo de ramp_s, se mantém e depois cai ao longo de recovery_s; 0 é um degrau. Todos os efeitos escalam com essa intensidade. Parâmetros que não pertencem ao tipo são recusados.
| tipo | parâmetros | efeito |
|---|---|---|
jamming |
cn0_drop_db, spread_db (padrão 6) |
o C/N0 de cada satélite cai em até cn0_drop_db (dispersão por satélite), os satélites saem primeiro do mais baixo, a solução é perdida quando menos de quatro são utilizáveis e se recupera após a liberação e reacquire_s |
drag-off |
final_offset_m, um de bearing_deg (verdadeiro) ou relative_bearing_deg (em relação ao rumo no início, depois mantido), opcional max_accel_mps2, counterfeit_cn0_dbhz |
o receptor mantém uma solução válida cuja posição se afasta da verdade; SOG e COG incluem o arrasto. O arrasto segue um smoothstep nas rampas, de modo que a trajetória falsa começa e para sem degrau de velocidade; seus picos são 1.5 D/T m/s e 6 D/T^2 m/s ao quadrado para uma rampa de T s sobre D m (ambos registrados), e um cenário que excedesse max_accel_mps2 é recusado. Um degrau (ramp_s = 0) move a posição sem tocar na velocidade |
time-spoof |
offset_s, counterfeit_cn0_dbhz |
o tempo de RMC, GGA, GNS e ZDA é deslocado do UTC verdadeiro; a posição permanece verdadeira |
replay-delay |
delay_s, affect_time (padrão verdadeiro), counterfeit_cn0_dbhz |
posição, velocidade (e tempo) são as da embarcação de delay_s atrás, com o atraso seguindo um smoothstep nas suas rampas e a velocidade relatada escalada por 1 - d(delay)/dt enquanto ele muda: uma abstração simplificada, no estilo de meaconing, não um modelo de propagação |
counterfeit_cn0_dbhz: enquanto um arrasto de posição, uma falsificação de tempo ou um atraso de repetição está ativo, todo satélite rastreado mostra esse único nível elevado, um sinal clássico. Deixe-o de fora para um evento sutil em que o padrão genuíno de C/N0 é mantido. Enquanto qualquer evento falsificado está ativo, o receptor segue o sinal falsificado, de modo que o jamming não o derruba; uma solução perdida por jamming anterior volta após reacquire_s sobre o sinal falsificado (ver combined-event). Uma mudança de tempo que faria o tempo relatado parar ou correr para trás (taxa de 1 s por segundo ou mais) é recusada, a menos que seja um degrau (ramp_s = 0).
Eventos podem se sobrepor; seus efeitos se somam (deslocamentos, mudanças de tempo, atrasos) ou prevalece o mais forte (jamming). O onset da linha do tempo é a primeira época em que o evento tem efeito, uma época após o start_s roteirizado quando o evento entra em rampa.
O log do instrutor#
Junto com o fluxo, <prefix>.instructor.json e <prefix>.instructor.txt trazem:
- os eventos como injetados, em linguagem simples, com todos os parâmetros, e o seu início em UTC;
- uma linha do tempo: início, efeito pleno, início da recuperação, recuperado, e os próprios
fix-lostefix-regaineddo receptor; - a trajetória verdadeira contra a relatada com espaçamento
log_interval_s: posição verdadeira e relatada, velocidade e rumo, erro de posição em metros, deslocamento de tempo, satélites usados e rastreados, C/N0 médio e quais eventos estavam ativos; - a velocidade e a aceleração de pico da trajetória verdadeira e da relatada;
- o
trainer_notedo cenário.
Mostre o log somente no debriefing.
Campos JSON (esquema kshana-nmea-training/1)#
| campo | tipo, unidade | significado |
|---|---|---|
schema |
string | kshana-nmea-training/1 |
warning |
string | a declaração de uso somente para treinamento |
scenario, description, trainer_note |
string | do cenário |
seed |
inteiro | semente usada |
start_utc |
UTC em ISO 8601 | início da execução |
duration_s |
s | duração da execução |
summary.true_peak_sog_kn, summary.reported_peak_sog_kn |
nós | maior velocidade sobre o fundo, verdadeira e relatada |
summary.true_peak_accel_mps2, summary.reported_peak_accel_mps2 |
m/s ao quadrado | maior variação de velocidade por segundo, verdadeira e entre soluções relatadas consecutivas |
events[] |
um por evento roteirizado | |
events[].id |
inteiro | base 1; usado por timeline[].event e track[].active_events |
events[].description |
string | descrição em linguagem simples com os parâmetros |
events[].start_utc |
UTC em ISO 8601 | início roteirizado |
events[].parameters |
objeto | o evento exatamente como escrito (kind, start_s, duration_s, ramp_s, recovery_s, e os parâmetros próprios do tipo; os ausentes são omitidos) |
events[].resolved_bearing_deg |
graus verdadeiros ou null | arrasto de posição: a direção aplicada (um rumo relativo é resolvido no início) |
events[].peak_drag_speed_mps, events[].peak_drag_accel_mps2 |
m/s, m/s ao quadrado ou null | arrasto de posição: picos que o arrasto acrescenta à trajetória falsa |
timeline[] |
momentos para o debriefing, em ordem de tempo | |
timeline[].t_s, .utc |
s desde o início, UTC em ISO 8601 | quando (tempo verdadeiro) |
timeline[].event |
inteiro ou null | o id do evento; null para mudanças de estado do receptor |
timeline[].what |
string | onset, full-effect, recovery-begins, recovered (eventos); fix-lost, fix-regained (receptor) |
timeline[].text |
string | explicação |
track[] |
uma linha por log_interval_s |
|
track[].t_s, .utc |
s, UTC em ISO 8601 | quando (tempo verdadeiro) |
track[].true_lat_deg, .true_lon_deg |
graus | posição verdadeira |
track[].true_sog_kn, .true_cog_deg, .true_heading_deg |
nós, graus verdadeiros | velocidade e rumo verdadeiros sobre o fundo, e rumo do girocompasso |
track[].fix_valid |
booleano | o receptor relatou uma solução |
track[].reported_lat_deg, .reported_lon_deg, .reported_sog_kn, .reported_cog_deg |
graus, nós, graus verdadeiros ou null | o que o receptor relatou; null enquanto a solução está perdida |
track[].position_error_m |
m ou null | distância horizontal da posição verdadeira à relatada |
track[].time_offset_s |
s | UTC relatado menos UTC verdadeiro |
track[].n_used, .n_tracked |
inteiros | satélites usados na solução e rastreados |
track[].mean_cn0_dbhz |
dB-Hz ou null | C/N0 médio dos satélites rastreados |
track[].active_events |
inteiros | ids dos eventos com efeito neste instante |
Acrescentar um campo não muda a etiqueta do esquema. Renomear ou remover um campo, ou mudar o significado ou a unidade de um campo, incrementa o número (/2).
A biblioteca de cenários#
scenarios/training/, cada um com alguns minutos de travessia e uma nota do instrutor no seu TOML:
| arquivo | o que mostra |
|---|---|
open-sea-jamming.toml |
o C/N0 cai em conjunto, os satélites saem, a solução é perdida e volta |
coastal-drag-off.toml |
uma solução válida, de aparência saudável, arrasta 1,4 km em direção à costa; C/N0 uniformemente elevado |
port-approach-time-spoof.toml |
posição correta, o UTC se adianta 45 s durante uma aproximação lenta ao porto |
combined-event.toml |
jamming, depois tomada falsificada com arrasto de posição e deslocamento de tempo, depois um atraso de repetição |
fjord-natural-degradation.toml |
um céu bloqueado, não interferência: as paredes de um fiorde (a predefinição de skyline REPRESENTATIVE fjord) escondem os satélites a leste e a oeste, depois o canal se abre de novo (evento natural-degradation, com uma tabela skyline) |
Posições, datas e trajetórias são inventadas; a biblioteca não traz nenhuma embarcação, operador ou gravação reais. Ela não faz nenhuma afirmação sobre como qualquer receptor ou sistema se comportaria diante de interferência.
Escolhas de modelagem#
O gerador é uma ferramenta de treinamento sem referência independente externa, e seus números são escolhas de modelagem, não medições de nenhum receptor. Eles são escolhidos para parecer um log de receptor comum: a inclinação de C/N0 de 14 dB do zênite ao horizonte abaixo do nível do zênite (padrão 47 dB-Hz) e o deslocamento por satélite de +/-1.5 dB, desvanecimento lento de 0,8 dB ao longo de 97 s e 0,5 dB ao longo de 31 s, jitter de 0,3 dB; os limiares de rastreamento (20 dB-Hz) e de uso (28 dB-Hz); o ruído de posição (1,5 m com HDOP 1, correlacionado ao longo de 30 s, o vertical o dobro); ruído de girocompasso de 0,05 grau e ruído de deriva de 0,03 nó; a constante de tempo de governo de 20 s e os raios de chegada aos pontos de passagem do modelo da embarcação; e a escolha de satélites por setor de azimute. Os padrões ficam em [receiver] e [vessel] e todos podem ser mudados no cenário.
Lendo de volta#
kshana receiver-trust pode ingerir esses arquivos (format = "nmea"). Os testes verificam cada sentença de cada cenário da biblioteca contra suas próprias regras de contagem de campos e de checksum, e passam os arquivos por esse leitor, que interpreta GGA, RMC e GSV e deixa passar os demais tipos sem pular nenhum. Os testes fixam um trecho golden por cenário (tests/fixtures/nmea_training/); regenere com KSHANA_BLESS_GOLDEN=1 após uma mudança pretendida.