Formatos de registos de relógio
src/clock_log.rs lê um registo de relógio medido para uma única série de fase normalizada: uma grelha uniforme de amostras (time_s, phase_s), com o intervalo de amostragem tau0 indicado ou encontrado, cada lacuna contada e as unidades do registo nomeadas. São lidos oito formatos, cada um um subconjunto documentado. Tudo o que fica fora do subconjunto é recusado com um erro que nomeia o que foi encontrado, em vez de ser lido de forma aproximada.
Abreviaturas. RINEX é o Receiver Independent Exchange Format. TICC é o contador de intervalos de tempo da TAPR (Tucson Amateur Packet Radio). ADEV é o desvio de Allan sobreposto, TDEV o desvio de tempo e MTIE o erro máximo de intervalo de tempo. tau0 é o intervalo de amostragem em segundos.
Onde um registo pode ser usado#
telecom-timing:[series.clock_log]em vez deseries.samplesouseries.csv_path. A fase do registo, em ns, é a série de erro de tempo verificada face às máscaras, e o relatório ganha um blococlock_loga indicar como foi lido.- O tipo de cenário do relógio: uma tabela
[clock_log]acrescenta à execução um blocomeasured: como o registo foi lido, o SHA-256 da tabela (input_sha256; oscenario_hashda execução não abrange a tabela) e das amostras de fase, e o ADEV sobreposto, o TDEV e o MTIE a fatores de média em oitavas. Requerruns = 1. O holdover simulado não se altera.
Sem a tabela, ambos os tipos de cenário são analisados, calculam o hash e reportam exatamente como antes.
[clock_log] # or [series.clock_log] in a telecom-timing scenario
format = "ticc" # "rinex-clk", "timelab", "ticc", "ptp4l", "phc2sys", "ts2phc",
# "chrony-tracking" or "chrony-statistics"
path = "logs/ticc.txt" # or text = "..." inline (the only choice in WebAssembly)
channel = "chA" # TICC: needed when the log holds more than one channel
gaps = "linear" # "refuse" (default), "longest" or "linear"
# clock = "G08" # RINEX, linuxptp, chrony: needed when the log holds more than one clock or source
# tau0_s = 1.0 # overrides a header and the detected spacing
# units = "ns" # TimeLab phase units: s (default), ms, us, ns, ps
# data = "phase" # TimeLab: phase (default) or frequency
Os formatos#
rinex-clk: ficheiros de relógio RINEX, versões 2,00 e 3,00 a 3,04 (incluindo o campo de nome de nove caracteres da 3,04). O registo RINEX VERSION / TYPE é verificado e qualquer outra versão é recusada, nomeando a versão encontrada: o IGS publica o formato de relógio (rinex_clock300.txt, 302, 304) até à 3,04, e os documentos RINEX 4,00, 4,01 e 4,02 definem apenas ficheiros de observação, de navegação e meteorológicos, portanto não existe versão 4,00 do ficheiro de relógio a ler. O cabeçalho é lido apenas para o seu TIME SYSTEM ID e é ignorado até END OF HEADER. Dos registos de dados, apenas os registos AS (satélite) e AR (recetor ou estação) de um relógio, selecionado pelo nome, são mantidos. O seu primeiro campo de valor, o viés do relógio em segundos, é a fase. As linhas de continuação e os registos CR, DR e MS são ignorados. As épocas são os campos de calendário do registo, em segundos a partir de 1980-01-06 00:00:00 no sistema de tempo do próprio ficheiro, sem conversão.
timelab: registos ASCII de fase ou frequência no estilo TimeLab, a forma em texto simples que o TimeLab exporta e que a sua importação ASCII e o Stable32 leem. Há uma leitura por linha: ou uma única coluna a um tau0 uniforme, ou duas colunas time value. As colunas podem ser separadas por espaços em branco, vírgulas ou pontos e vírgulas.
- As linhas que começam por
#,;ou%são comentários. Um comentário da formakey = value(oukey: value) definetau0(s),units(s,ms,us,nsoups, para fase),data(phaseoufrequency) outime_units(soumjd, para a primeira de duas colunas). - Uma linha inicial não numérica é tomada como cabeçalho de colunas.
- Os dados de frequência (frequência fracionária) só são aceites como coluna única. São integrados em fase como
x[i+1] = x[i] + y[i]·tau0(NIST SP 1065, 2008, §4,1). - Os ficheiros binários e de projeto
.timdo TimeLab não são lidos.
ticc: registos do contador TICC da TAPR (Manual de Operação do TICC, revisões de 23 de novembro de 2016 e 15 de dezembro de 2025, "modos de medição"). Todo campo de dados é em segundos com 12 casas decimais (11 por predefinição a partir do firmware 20251101: qualquer quantidade até 15 é lida). Um carimbo de tempo é lido a partir do seu texto decimal como um número inteiro de femtossegundos, e não como um número binário de ponto flutuante, de modo que a fase de um registo obtido dias após o início do contador mantém a resolução própria do contador: um carimbo de tempo f64 em 3.0e5 s tem um espaçamento de 5.8e-11 s, cerca dos 60 ps do contador.
- As linhas do modo de marca temporal (
<seconds> chA,<seconds> chB) e o canal de pseudomarca temporal do modo TimeLab (chC) são marcas temporais de eventos. A fase do canal selecionado é a sua marca temporal menos o tique nominal:x[k] = t[k] − t[0] − n[k]·tau0, em quen[k]é o número inteiro detau0desde o primeiro evento. Um evento perdido é uma lacuna. - As linhas do modo de intervalo (
<seconds> TI(A->B), ou qualquer etiqueta que comece porTI) são lidas diretamente como fase, uma leitura portau0. O cenário tem de indicartau0_s, porque a linha não transporta tempo. - O modo Paired Timestamp imprime as mesmas linhas na ordem dos canais e é lido como modo Timestamp; o modo 3-Corner Hat acrescenta o
chCsintetizado, lido como acima. - A saída dos modos Period e Debug é recusada. Um canal cujas leituras não crescem é comunicado como modo Period. O modo Binary Timestamp não é texto, e um ficheiro nesse modo é recusado com uma mensagem que o diz.
- As linhas que começam por
#, e as linhas que não são<number> <label>(como o menu de arranque), são ignoradas e contadas.
registos do linuxptp e do chrony#
Estes cinco formatos leem o que um daemon de tempo em execução escreve sobre o seu próprio relógio. Servem para verificar uma cadeia de temporização a posteriori (um relógio de fronteira, o PHC de um grandmaster, um cliente chrony) com os mesmos estimadores que os outros registos. Não leem a configuração de um daemon, e nada aqui comunica com um daemon.
ptp4l, phc2sys, ts2phc: as linhas de servo do linuxptp, cada formato a ler apenas linhas do seu próprio programa. Uma leitura é uma linha da forma
ptp4l[5003.000]: master offset 40 s2 freq +1480 path delay 497
phc2sys[7000.000]: CLOCK_REALTIME phc offset -30 s2 freq +100 delay 1200
ts2phc[8000.000]: /dev/ptp0 offset 3 s2 freq -2
- A marca temporal é o primeiro
[seconds.fraction]da linha (o relógio monótono do daemon). Também é lida após um prefixo de syslog, onde um identificador de processo comoptp4l[812]:não tem fração e não é tomado. - A fase é o
offsetregistado, em nanossegundos, convertido para segundos. É o erro de tempo medido pelo daemon do seu relógio escravo face à sua referência, tal como registado: positivo quando o escravo está adiantado. É o erro que resta sob controlo do servo, não a deriva de um relógio em marcha livre. - As palavras entre a marca temporal e
offsetnomeiam a fonte (master,eth0 masterpara um prefixo[eth0],CLOCK_REALTIME phc,/dev/ptp0). Um registo com mais do que uma fonte precisa declock = "<that text>"; o erro lista-as. - As leituras anteriores à primeira vez em que o servo reporta
s2(bloqueado) são excluídas e contadas emn_skipped_lines:s0es1são os estados desbloqueado e de salto, cujos desvios são transitórios de entrada. Um servo que nunca atinges2é recusado. Uma linha sem campo de estado é lida tal como está. - As linhas de resumo (
rms ... max ...), as mensagensdelayeselected best mastere as linhas de outros programas são ignoradas e contadas. - As marcas temporais que andam para trás (um daemon reiniciado) são recusadas; divida o registo.
chrony-tracking, chrony-statistics: os ficheiros log tracking e log statistics do chrony, lidos por posição de coluna. Uma leitura é uma linha com pelo menos 14 colunas (tracking) ou 13 (statistics) que começa por uma data e hora UTC YYYY-MM-DD HH:MM:SS. A linha de cabeçalho das colunas é ignorada e contada; a linha ==== por baixo dela é ignorada sem ser contada.
- Tracking: coluna 7, o desvio local na atualização do relógio, em segundos. O endereço IP ou o identificador de referência muda com a fonte selecionada, pelo que, sem
clock, cada linha é lida como um único relógio local.clock = "<id>"mantém um identificador de referência. - Statistics: coluna 5, o desvio estimado de uma fonte, em segundos. Há uma série por endereço de fonte, e o registo precisa de
clock = "<address>"quando contém mais do que uma. - As épocas são segundos desde 1980-01-06 00:00:00 UTC, sem tratamento de segundos intercalares. O chrony escreve uma linha por atualização ou por amostra de fonte, pelo que o espaçamento não é uniforme.
tau0é o espaçamento mediano; uma leitura a menos de meiotau0da anteriormente mantida é descartada e contada emn_duplicates, e um espaçamento maior é uma lacuna, quegaps = "refuse"reporta. Indiquetau0_segaps = "linear"para um registo com um intervalo de sondagem estável.
As colunas do chrony e as disposições de linha do linuxptp são as da documentação pública dos daemons. Os ficheiros de apoio em tests/fixtures/clock_log/linuxptp_chrony/ foram escritos para o Kshana com valores inventados (ver o seu NOTICE.md); nenhum registo real foi lido, pelo que uma disposição que difira entre versões do daemon ainda pode ser recusada ou mal lida. Uma linha que não corresponda é ignorada e contada, nunca adivinhada.
tau0 e lacunas#
Quando o registo transporta marcas temporais e não é indicado tau0, tau0 é o espaçamento mediano das marcas temporais consecutivas, arredondado a quatro algarismos significativos, e a fonte é reportada como median-spacing. O espaçamento de um contador difere do nominal pelo desvio de frequência da referência, partes em 10^6 ou menos. Um tau0_s no cenário prevalece sempre (fonte input), seguido de um comentário de cabeçalho tau0 (fonte header).
As amostras são colocadas na grelha t0 + i·tau0, e uma época em falta é uma lacuna. As leituras a mais de 1 % de tau0 fora da grelha são descartadas e contadas, tal como as duplicadas (mantém-se a primeira). A política de lacunas é uma das seguintes:
refuse(a predefinição): qualquer lacuna é um erro que conta as lacunas;longest: manter o troço sem lacunas mais longo;linear: preencher as lacunas interiores por interpolação linear e reportar o número preenchido.
Os estimadores precisam de um registo sem lacunas, razão pela qual as lacunas são resolvidas aqui.
O seu próprio relógio#
Uma tabela [clock_log] no tipo de cenário de relógio indica quão estável foi o seu próprio relógio, ao lado da simulação: scenarios/clock-measured-log.toml (kshana example clock-measured-log) é clock-holdover.toml com um excerto de 96 leituras de um relógio de satélite do IGS como um registo TimeLab embutido. Substitua text por path e format para o seu próprio ficheiro. O bloco measured no resultado contém como o registo foi lido (formato, tau0 e a origem, cada lacuna) e as suas ADEV, TDEV e MTIE; ele não altera a simulação.
Conduzir o holdover com o seu próprio relógio: [clock_fit]#
[clock_log] # the measured series, as above
format = "ticc"
path = "my-clock.txt"
channel = "chA"
[clock_fit]
replaces = "classical" # or "quantum": the simulated clock whose noise the log replaces
O registo é ajustado com o procedimento de cartão de dispositivo de src/clock_library/card.rs (condicionamento, remoção do termo quadrático, identificação do ruído de desfasamento 1, mínimos quadrados ponderados em PM branco, FM branco, FM de cintilação e FM de passeio aleatório nos tempos de média com pelo menos 30 graus de liberdade). O relógio nomeado passa então a usar q_wf = h_0 / 2, q_rw = 2 pi^2 h_-2, flicker_floor = sqrt(2 ln 2 h_-1) e o drift ajustado; y0 permanece como está no cenário, porque um registo traz o deslocamento da sua referência, e não o do relógio após a disciplina. O ruído de fase branco é ajustado e indicado, mas não simulado. O resultado ganha um bloco fitted (os coeficientes, os pontos de ajuste, os valores usados pela execução, o que foi deixado de fora, o SHA-256 do registo) e o relógio recebe o id measured:<12 hex of the phase hash>. Com runs > 1 o conjunto sorteia a partir do relógio ajustado (o bloco measured exige runs = 1). Um ajuste sem ruído de frequência, um registo curto demais para ajustar e um replaces diferente de quantum ou classical são erros que o dizem. scenarios/clock-fitted-log.toml é um exemplo (6 horas de um relógio de satélite do IGS).
O que a comparação com ruído conhecido (tests/fixtures/clock_fit/RESULTS.md) mostra e o que se deve ter em mente:
- Atingido: o nível de FM branco é recuperado dentro de 5 % em todo registo sintético que o tem; o cartão ajustado reproduz o desvio de Allan verdadeiro dentro de um fator de 1,35 em todo tempo de média em que foi ajustado; o expoente de ruído que o ajuste identifica é igual ao do allantools em todos os pontos (no registo 5071A, depois que o salto de fase que o condicionamento remove é removido também da entrada do allantools).
- Não atingido, e fixado pelo teste: o FM de passeio aleatório só é recuperado quando domina os maiores tempos de média do registo e, mesmo assim, num de três registos de uma série de 131 072 s com cruzamento em 3 000 s, o ajuste atribuiu-o ao FM de cintilação e à deriva. A divisão entre FM de passeio aleatório e FM de cintilação não é fiável a partir de um único registo. A execução reproduz a estabilidade do registo nos tempos de média que o registo cobre; além deles as duas escolhas divergem (o FM de cintilação é um piso constante, o FM de passeio aleatório cresce com a raiz quadrada do tempo de média).
- MODELLED: um cenário hipotético para o ruído desse relógio como o registo o mostra, não uma calibração do relógio e não uma previsão do seu comportamento futuro.
Um registo malformado é um erro que nomeia o que foi encontrado (a linha, a versão, o seletor ausente), nunca um resultado parcial: tests/clock_log_real_allantools.rs fixa 24 desses casos.
Evidências#
MODELLED. Este é um analisador, e a sua saída alimenta estimadores que são validados em outro local face ao allantools. Os testes unitários de src/clock_log.rs usam pequenos registos escritos à mão e, para os formatos linuxptp e chrony, os ficheiros de teste de tests/fixtures/clock_log/linuxptp_chrony/. tests/clock_log_allantools.rs executa a comparação fixada antecipadamente em tests/fixtures/clock_log/PREREGISTRATION.md: três registos sintéticos com semente, um por formato, relidos por um leitor independente escrito a partir desta página pelo mesmo autor, com as ADEV, TDEV e MTIE verificadas face ao allantools 2024,6; essa comparação cobre apenas os três primeiros formatos, e os leitores de linuxptp e chrony têm testes de analisador, mas nenhuma comparação com referência independente. Os registos dessa comparação são sintéticos, portanto nada ali descreve um relógio ou contador real; a comparação com séries medidas é descrita abaixo.
Com dados medidos#
tests/clock_log_real_allantools.rs executa a comparação fixada antecipadamente em tests/fixtures/clock_log_real/PREREGISTRATION.md (versionada antes de qualquer ficheiro de teste ou execução) em quatro séries medidas: o PHASE.DAT do Stable32, as primeiras 65 536 leituras de um relógio de césio 5071A contra um maser de hidrogénio, e os relógios do satélite G01 do IGS (30 s) e da estação ABMF (300 s) a partir do relógio final do IGS de 2026-08-02, como um recorte incorporado com o seu NOTICE. Os dois primeiros não têm concessão de redistribuição: os registos escritos a partir deles estão em realdata-cache/, ignorado pelo git (scripts/gen_clock_log_real_ref.py), e o teste di-lo e passa quando estão ausentes, a menos que KSHANA_REQUIRE_REALDATA=1. Cada registo é lido pelo motor e por um leitor independente, e ADEV, TDEV e MTIE são comparados com o allantools 2024,6. Registado em tests/fixtures/clock_log_real/RESULTS.md: no leitor distribuído, os registos TICC, a versão do cabeçalho e cinco mensagens de erro falharam nos limiares fixados antecipadamente; foram corrigidos e a comparação foi executada de novo sem alterações. Atingido, exceto um par: o MTIE de um registo de frequência difere do do allantools, porque o allantools integra a frequência menos a à sua média (fixado como NOT MET; a comparação em condições equivalentes é atingida). A linha permanece MODELLED: os registos TICC são dados de relógio medidos recodificados no formato do contador pelo nosso script (nenhuma captura TICC real foi encontrada), e o leitor de referência tem o mesmo autor que o do motor.