Exportação de animações
--animate transforma a série temporal de uma execução em algo que se move: um ficheiro Scalable Vector Graphics (SVG) animado, um único leitor HyperText Markup Language (HTML) autónomo, ou uma sequência numerada de fotogramas SVG para um codificador de vídeo. Uma campanha é reproduzida como as suas fases (jamming, spoofing, holdover, alarme de integridade) com os seus alarmes assinalados; uma execução de espetro reproduz a sua cascata linha a linha; uma passagem em órbita terrestre baixa (LEO) reproduz a sua ligação por banda e, com um spoofer, as estatísticas do monitor época a época.
kshana scenarios/campaign-jam-spoof-holdover-integrity.toml --animate all
kshana scenarios/l-band-waterfall-jamming.toml --animate html
kshana scenarios/clock-holdover.toml --animate frames --animate-fps 24 --animate-duration 10
Código: src/animation.rs. Testes: tests/animation.rs.
O que é, e o que não é#
O exportador lê o documento de resultados que a execução já escreveu. Não volta a executar a física, não reamostra, suaviza nem interpola entre amostras (exceto o único segmento de reta que termina um traço no cursor), e não acrescenta nenhum número próprio. Uma animação é, portanto, tão boa quanto a execução que está por trás dela. O seu nível é MODELLED, coerência interna: é uma representação, nunca evidência, e não leva nenhuma linha própria da matriz de verificação.
Saídas#
Cada formato é escrito junto do cenário, ao lado dos habituais result.json, chart.svg e report.html:
| Formato | Escrito como | O que contém |
|---|---|---|
svg |
<scenario>.animation.svg |
Um SVG animado com keyframes de Cascading Style Sheets (CSS) e sem script: os traços desenham-se por trás de um cursor de tempo em movimento, os eventos aparecem no seu instante, as linhas da cascata revelam-se por ordem temporal. Repete-se em ciclo, com uma pausa de 1,5 s na imagem final. |
html |
<scenario>.animation.html |
Um ficheiro com script e estilo em linha e sem recursos externos: reproduzir e pausar, um cursor de arrasto, velocidade (0.25x a 4x), todos os painéis num cursor sincronizado, um valor em tempo real por traço, a faixa de fases e uma lista de eventos que salta para o ponto ao clicar. A barra de espaços reproduz ou pausa, as teclas de seta avançam passo a passo, Home e End saltam. |
frames |
<scenario>.frames/ |
frame_0000.svg, frame_0001.svg, … e manifest.json. |
--animate pode ser repetido e aceita uma lista separada por vírgulas (--animate svg,html) ou all. --animate-fps (1 a 60, predefinição 12) e --animate-duration (0,5 s a 600 s, predefinição 8 s) definem a reprodução; a sequência de fotogramas está limitada a 7 200 fotogramas.
Quando --export é executado no mesmo comando, o leitor HTML lista os ficheiros de exportação escritos ao seu lado (CZML, KML, GeoJSON, STK, SigMF) como ligações relativas a esses ficheiros irmãos; só é escrita uma ligação para um nome de ficheiro simples, pelo que nenhuma pode sair da pasta nem alcançar a rede. O relatório avançado (report.html) incorpora o desenho animado sempre que o resultado tem uma série temporal, quer --animate tenha sido executado ou não.
Quando --animate é executado, result.json ganha um bloco animation: os formatos, as imagens por segundo (fps), a duração, o número de fotogramas, o intervalo de tempo, os títulos dos painéis, os caminhos JSON de onde cada traço foi lido (sources) e quaisquer séries encontradas mas não desenhadas (omitted). Sem --animate o resultado é idêntico byte a byte ao de uma execução simples.
A sequência de fotogramas#
A sequência tem exatamente round(duration × fps) fotogramas. O fotograma i mostra a cronologia até t_start + (t_end − t_start) × i / (n − 1), pelo que o primeiro fotograma é o início e o último é a imagem final. manifest.json regista fps, duration_s, frame_count, frame_pattern (frame_%04d.svg), o tamanho em píxeis, t_start, t_end, o tempo de missão de cada fotograma (frame_times), as fases, os eventos e as fontes. Uma nova exportação para o mesmo diretório remove primeiro todos os frame_NNNN.svg já aí existentes (e mais nada), pelo que uma sequência mais curta nunca deixa para trás fotogramas de uma mais longa para o codificador ler.
Os fotogramas são SVG, não Portable Network Graphics (PNG). Um ffmpeg compilado com a biblioteca librsvg lê-os diretamente, à taxa com que a sequência foi escrita (24 no exemplo acima; o fps do manifesto):
ffmpeg -framerate 24 -i clock-holdover.frames/frame_%04d.svg -pix_fmt yuv420p clock-holdover.mp4
Um ffmpeg sem librsvg para com "no decoder found for: svg". Rasterize primeiro os fotogramas, por exemplo com rsvg-convert, e codifique os PNG:
for f in clock-holdover.frames/frame_*.svg; do rsvg-convert "$f" -o "${f%.svg}.png"; done
ffmpeg -framerate 24 -i clock-holdover.frames/frame_%04d.png -pix_fmt yuv420p clock-holdover.mp4
Acessibilidade e tema#
- Esquema de cores. O leitor segue
prefers-color-schemee redesenha-se quando este muda; o SVG animado leva uma variante clara da sua paleta sob a mesma media query. Os fotogramas usam a paleta escura dos gráficos estáticos, porque um vídeo não tem esquema. - Movimento reduzido. Sob
prefers-reduced-motion: reduceo SVG animado desativa todas as animações e mostra a imagem final (os seus estilos de base são o estado final, pelo que nada fica oculto), e o leitor abre em pausa na imagem final em vez de reproduzir; o utilizador pode na mesma arrastar o cursor ou premir Play. - O SVG tem um
<title>e um<desc>; os controlos do leitor são botões rotulados, uma entrada de intervalo com um valor de tempo falado e um seletor.
O que conta como série temporal#
Os documentos de resultados diferem consoante o tipo de cenário, pelo que o exportador reconhece as formas que o motor emite e não uma lista de tipos de cenário:
- Uma matriz de registos com um campo de tempo (
t,t_s,t_hours,t_min,t_days, ouknuma matrizepochs) que aumenta estritamente ao longo da matriz. Cada um dos outros campos numéricos torna-se um traço; o mesmo campo sob duas raízes (quantum.series[].error_nseclassical.series[].error_ns) partilha um painel. - Um objeto com uma matriz de tempo estritamente crescente (
t_s,t,times_s, …) ao lado de matrizes do mesmo comprimento: matrizes numéricas irmãs, um mapachannelsde registos{unit, values}(a cronologia decampaign), uma matriz de registos que contêm tais matrizes (as bandas despectrum, as trajetórias de satélites de uma constelação) e uma matriz bidimensional cujas linhas correspondem ao eixo do tempo com um eixo de frequência*_hzirmão (a cascata despectrum). - Ao lado dessa matriz de tempo,
phases(name,t0_s,t1_s) tornam-se a faixa de fases eevents(t_s,label,alarm) tornam-se os marcadores. Os escalares*_t_sde uma banda (a sua primeira perda de bloqueio, a sua razão portadora-densidade de ruído mais baixa) também são assinalados.
Os traços com unidade partilham um painel quando as suas magnitudes estão dentro de um fator de 100, pelo que uma guarda de 50 ns não achata um erro de 2 ns. São desenhados no máximo 6 painéis de 6 traços; os restantes são listados em omitted. Os traços com mais de 2 400 amostras são reduzidos por um passo uniforme que mantém a última amostra, e uma cascata com mais de 96 × 120 células é reduzida por média (em potência, para células em decibéis, para que um jammer estreito não seja diluído).
Um tipo de cenário cujo resultado não contém nenhuma destas formas (um orçamento de enlace, uma geometria de época única, um resumo de Monte Carlo apenas estatístico, ou o holdover de telecomunicações, cujo resultado publica o hash do registo, mas não o registo) é recusado com no time series to animate, e nada é escrito. Com o kshana 0.40.0, 94 dos 193 ficheiros de cenário executáveis (todos, exceto uma suíte de estudo) geram animação e 99 são recusados. cargo test --test animation -- --nocapture imprime a contagem.
Entre os tipos de cenário LEO, todos os cenários leo-pass, leo-pnt-chain e leo-ppp incluídos animam. leo-pvt anima nos seus modos Doppler, de posicionamento conjunto e de temporização (uma execução de temporização precisa de trace = true, o que faz cada linha reportar o seu erro de tempo e o sigma previsto em cada época) e é recusado no seu modo de cobertura polar. leo-navmsg anima a sua atualização a meio da passagem e é recusado para as análises de codificação, de intervalo de ajuste e de comparação de modelos. Os resultados de leo-signal e ntn-positioning não têm eixo temporal e são recusados.
Determinismo#
A saída é uma função pura do documento de resultados e das opções: sem relógio, sem número aleatório, sem iteração de mapa de hashes, sem acesso a ficheiros ou à rede na biblioteca, e sem marca temporal em lado nenhum, pelo que o mesmo cenário, semente e opções dão ficheiros idênticos byte a byte. O módulo compila para WebAssembly sem alterações; apenas a interface de linha de comandos escreve ficheiros.
Utilização como biblioteca#
use kshana::animation::{AnimationFormat, AnimationOptions};
let anim = kshana::api::animate_toml(src, AnimationFormat::Html, &AnimationOptions::default())?;
std::fs::write("run.html", &anim.files[0].content)?;
kshana::animation::animate_result anima um documento de resultados que já tem, e extract_timeline devolve o que seria desenhado sem o renderizar.
Limites#
- O wheel de Python e o módulo WebAssembly ainda não expõem o exportador; é acessível a partir da linha de comandos, da biblioteca Rust e da ferramenta
animate_scenariodo servidor Model Context Protocol (MCP) (que devolve no máximo 120 fotogramas numa resposta). - Os eixos são lineares. Uma série que abrange muitas décadas (o crescimento do erro de um relógio de 0,1 ns a um microssegundo) parece plana até tarde na execução.
- O SVG animado baseia-se na animação CSS de um retângulo de recorte; os motores atuais do Chromium, Firefox e Safari reproduzem-no, enquanto um visualizador sem animação CSS mostra a imagem final.
- As campanhas de varrimento, Monte Carlo e composição não têm eixo temporal e são recusadas; as estatísticas de resumo de um varrimento ou de um conjunto não são uma série temporal. Uma campanha encadeada anima.