Formaty dzienników zegara
src/clock_log.rs wczytuje zmierzony dziennik zegara do jednego znormalizowanego szeregu fazy: jednorodnej siatki próbek (time_s, phase_s), z podanym lub znalezionym interwałem próbkowania tau0, każdą przerwą policzoną i nazwanymi jednostkami dziennika. Wczytywanych jest osiem formatów, każdy jako udokumentowany podzbiór. Wszystko poza podzbiorem jest odrzucane z błędem, który nazywa to, co znaleziono, zamiast być odczytywane w przybliżeniu.
Skróty. RINEX to Receiver Independent Exchange Format. TICC to licznik interwałów czasu TAPR (Tucson Amateur Packet Radio). ADEV to overlapping odchylenie Allana, TDEV to odchylenie czasu, a MTIE to maksymalny błąd interwału czasu. tau0 to interwał próbkowania w sekundach.
Gdzie można użyć dziennika#
telecom-timing:[series.clock_log]zamiastseries.sampleslubseries.csv_path. Faza dziennika, w ns, jest szeregiem błędu czasu sprawdzanym względem masek, a raport zyskuje blokclock_logmówiący, jak go odczytano.- Rodzaj clock: tabela
[clock_log]dodaje do przebiegu blokmeasured: jak odczytano dziennik, SHA-256 tabeli (input_sha256;scenario_hashprzebiegu nie obejmuje tabeli) i próbek fazy oraz overlapping ADEV, TDEV i MTIE przy oktawowych współczynnikach uśredniania. Wymagaruns = 1. Symulowany holdover pozostaje bez zmian.
Bez tabeli oba rodzaje są parsowane, haszowane i raportowane dokładnie tak jak dotąd.
[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
Formaty#
rinex-clk: pliki zegara RINEX, wersje 2,00 oraz od 3,00 do 3,04 (w tym dziewięcioznakowe pole nazwy z 3,04). Sprawdzany jest rekord RINEX VERSION / TYPE, a każda inna wersja jest odrzucana z podaniem znalezionej wersji: IGS publikuje format zegara (rinex_clock300.txt, 302, 304) do 3,04, a dokumenty RINEX 4,00, 4,01 i 4,02 definiują tylko pliki obserwacji, nawigacji i meteorologiczne, więc nie ma wersji 4,00 pliku zegara do odczytu. Nagłówek jest czytany tylko dla TIME SYSTEM ID i pomijany do END OF HEADER. Spośród rekordów danych zachowywane są tylko rekordy AS (satelita) i AR (odbiornik lub stacja) jednego zegara, wybranego po nazwie. Ich pierwsze pole wartości, bias zegara w sekundach, jest fazą. Linie kontynuacji oraz rekordy CR, DR i MS są pomijane. Epoki to pola kalendarzowe rekordu, jako sekundy od 1980-01-06 00:00:00 we własnym systemie czasu pliku, bez konwersji.
timelab: dzienniki fazy lub częstotliwości ASCII w stylu TimeLab, czyli zwykła postać tekstowa, którą eksportuje TimeLab i którą wczytują jego import ASCII oraz Stable32. W każdej linii jest jeden odczyt: albo pojedyncza kolumna przy jednorodnym tau0, albo dwie kolumny time value. Kolumny mogą być oddzielone białymi znakami, przecinkami lub średnikami.
- Linie zaczynające się od
#,;lub%są komentarzami. Komentarz w postacikey = value(lubkey: value) ustawiatau0(s),units(s,ms,us,nslubps, dla fazy),data(phaselubfrequency) albotime_units(slubmjd, dla pierwszej z dwóch kolumn). - Jedna początkowa linia nienumeryczna jest traktowana jako nagłówek kolumn.
- Dane częstotliwości (częstotliwość względna) są przyjmowane tylko jako pojedyncza kolumna. Są całkowane do fazy jako
x[i+1] = x[i] + y[i]·tau0(NIST SP 1065, 2008, §4,1). - Binarne pliki projektów TimeLab oraz
.timnie są wczytywane.
ticc: dzienniki licznika TAPR TICC (TICC Operation Manual, wersje z 23 listopada 2016 i 15 grudnia 2025, „measurement modes”). Każde pole danych to sekundy z 12 miejscami po przecinku (domyślnie 11 od oprogramowania sprzętowego 20251101: odczytywana jest każda liczba do 15). Znacznik czasu jest odczytywany z jego tekstu dziesiętnego jako całkowita liczba femtosekund, a nie jako liczba binarna zmiennoprzecinkowa, więc faza dziennika zebranego dni po uruchomieniu licznika zachowuje własną rozdzielczość licznika: znacznik czasu f64 przy 3.0e5 s ma rozstaw 5.8e-11 s, około 60 ps licznika.
- Linie trybu znaczników czasu (
<seconds> chA,<seconds> chB) oraz kanał pseudoznacznika czasu trybu TimeLab (chC) są znacznikami czasu zdarzeń. Fazą wybranego kanału jest jego znacznik czasu minus nominalny takt:x[k] = t[k] − t[0] − n[k]·tau0, gdzien[k]to liczba całkowitatau0od pierwszego zdarzenia. Pominięte zdarzenie jest przerwą. - Linie trybu interwału (
<seconds> TI(A->B)lub dowolna etykieta zaczynająca się odTI) są odczytywane bezpośrednio jako faza, jeden odczyt natau0. Scenariusz musi podaćtau0_s, ponieważ linia nie niesie czasu. - Tryb Paired Timestamp drukuje te same wiersze w kolejności kanałów i jest odczytywany jako tryb Timestamp; tryb 3-Corner Hat dodaje syntetyzowany
chC, odczytywany jak wyżej. - Wyjście trybu okresu i trybu debugowania jest odrzucane. Kanał, którego odczyty nie rosną, jest zgłaszany jako tryb okresu. Binarny tryb Timestamp nie jest tekstem, a jego plik jest odrzucany z komunikatem, który to mówi.
- Linie zaczynające się od
#oraz linie niebędące<number> <label>(takie jak menu startowe) są pomijane i liczone.
dzienniki linuxptp i chrony#
Te pięć formatów wczytuje to, co działający demon czasu zapisuje o własnym zegarze. Służą do sprawdzania łańcucha czasu po fakcie (zegar graniczny, PHC grandmastera, klient chrony) tymi samymi estymatorami co inne dzienniki. Nie wczytują konfiguracji demona i nic tu nie komunikuje się z demonem.
ptp4l, phc2sys, ts2phc: linie serwa linuxptp, przy czym każdy format wczytuje tylko linie własnego programu. Odczytem jest linia w postaci
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
- Znacznikiem czasu jest pierwsze
[seconds.fraction]w linii (zegar monotoniczny demona). Jest on odczytywany także po prefiksie syslog, gdzie identyfikator procesu, taki jakptp4l[812]:, nie ma części ułamkowej i nie jest brany. - Fazą jest zalogowane
offset, w nanosekundach, przeliczone na sekundy. Jest to zmierzony przez demona błąd czasu jego zegara podrzędnego względem wzorca, tak jak zalogowano: dodatni, gdy zegar podrzędny wyprzedza. Jest to błąd pozostały pod kontrolą serwa, a nie dryf zegara swobodnego. - Słowa między znacznikiem czasu a
offsetnazywają źródło (master,eth0 masterdla prefiksu[eth0],CLOCK_REALTIME phc,/dev/ptp0). Dziennik z więcej niż jednym źródłem wymagaclock = "<that text>"; błąd je wylicza. - Odczyty sprzed pierwszego zgłoszenia przez serwo
s2(zablokowany) są pomijane i liczone wn_skipped_lines:s0is1to stany niezablokowany i skokowy, których przesunięcia są przejściowymi procesami wejścia. Serwo, które nigdy nie osiągas2, jest odrzucane. Linia bez pola stanu jest odczytywana tak, jak jest. - Linie podsumowań (
rms ... max ...), komunikatydelayiselected best masteroraz linie innych programów są pomijane i liczone. - Znaczniki czasu cofające się (ponownie uruchomiony demon) są odrzucane; podziel dziennik.
chrony-tracking, chrony-statistics: pliki log tracking i log statistics chrony, odczytywane według położenia kolumn. Odczytem jest linia z co najmniej 14 kolumnami (tracking) lub 13 (statistics), zaczynająca się od daty i czasu UTC w postaci YYYY-MM-DD HH:MM:SS. Linia nagłówka kolumn jest pomijana i liczona; linia ==== pod nią jest pomijana bez liczenia.
- Tracking: kolumna 7, lokalne przesunięcie przy aktualizacji zegara, w sekundach. Adres IP lub identyfikator odniesienia zmienia się wraz z wybranym źródłem, więc bez
clockkażda linia jest odczytywana jako jeden zegar lokalny.clock = "<id>"zachowuje jeden identyfikator odniesienia. - Statistics: kolumna 5, estymowane przesunięcie źródła, w sekundach. Dla każdego adresu źródła jest jeden szereg, a dziennik wymaga
clock = "<address>", gdy zawiera więcej niż jeden. - Epoki to sekundy od 1980-01-06 00:00:00 UTC, bez obsługi sekund przestępnych. Chrony zapisuje linię na aktualizację lub na próbkę źródła, więc odstęp nie jest jednorodny.
tau0to mediana odstępu; odczyt wcześniejszy niż połowatau0po zachowanym poprzednim jest odrzucany i liczony wn_duplicates, a dłuższy odstęp jest przerwą, o czym informujegaps = "refuse". Podajtau0_sigaps = "linear"dla dziennika ze stałym interwałem odpytywania.
Kolumny chrony i układy linii linuxptp pochodzą z publicznej dokumentacji demonów. Zestawy danych w tests/fixtures/clock_log/linuxptp_chrony/ napisano dla Kshana ze zmyślonymi wartościami (zob. jego NOTICE.md); nie wczytano żadnego prawdziwego dziennika, więc układ różniący się między wersjami demona może nadal zostać odrzucony lub źle odczytany. Linia niepasująca jest pomijana i liczona, nigdy zgadywana.
tau0 i przerwy#
Gdy dziennik niesie znaczniki czasu i nie podano tau0, tau0 jest medianą odstępu kolejnych znaczników czasu, zaokrągloną do czterech cyfr znaczących, a źródło jest zgłaszane jako median-spacing. Odstęp licznika różni się od nominalnego o przesunięcie częstotliwości wzorca, rzędu 10^6 lub mniej. tau0_s w scenariuszu zawsze wygrywa (źródło input), następnie komentarz nagłówka tau0 (źródło header).
Próbki są umieszczane na siatce t0 + i·tau0, a brakująca epoka jest przerwą. Odczyty oddalone od siatki o więcej niż 1 % tau0 są odrzucane i liczone, podobnie jak duplikaty (pierwszy jest zachowywany). Polityka przerw jest jedną z:
refuse(domyślnie): każda przerwa jest błędem, który liczy przerwy;longest: zachowaj najdłuższy ciąg bez przerw;linear: wypełnij przerwy wewnętrzne interpolacją liniową i podaj liczbę wypełnionych.
Estymatory wymagają zapisu bez przerw, dlatego przerwy są tu rozwiązywane.
Twój własny zegar#
Tabela [clock_log] w rodzaju zegara podaje, jak stabilny był Twój własny zegar, obok symulacji: scenarios/clock-measured-log.toml (kshana example clock-measured-log) to clock-holdover.toml z 96-odczytowym wyciągiem zegara satelity IGS jako wbudowanym dziennikiem TimeLab. Zastąp text przez path i format dla własnego pliku. Blok measured w wyniku zawiera sposób odczytu dziennika (format, tau0 i skąd pochodzi, każda przerwa) oraz jego ADEV, TDEV i MTIE; nie zmienia on symulacji.
Sterowanie holdover własnym zegarem: [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
Dziennik jest dopasowywany procedurą karty urządzenia z src/clock_library/card.rs (kondycjonowanie, usunięcie składowej kwadratowej, identyfikacja szumu z opóźnieniem 1, ważona metoda najmniejszych kwadratów w szumie PM białym, FM białym, FM typu flicker i FM typu błądzenie losowe w czasach uśredniania z co najmniej 30 stopniami swobody). Wskazany zegar przyjmuje wtedy q_wf = h_0 / 2, q_rw = 2 pi^2 h_-2, flicker_floor = sqrt(2 ln 2 h_-1) oraz dopasowane drift; y0 pozostaje takie, jak w scenariuszu, ponieważ dziennik niesie przesunięcie swojego wzorca, a nie zegara po dyscyplinowaniu. Szum fazy białej jest dopasowywany i podawany, ale nie symulowany. Wynik zyskuje blok fitted (współczynniki, punkty dopasowania, wartości użyte przez przebieg, co pominięto, SHA-256 dziennika), a zegar nosi identyfikator measured:<12 hex of the phase hash>. Przy runs > 1 zespół losuje z dopasowanego zegara (blok measured wymaga runs = 1). Dopasowanie bez szumu częstotliwości, dziennik zbyt krótki do dopasowania oraz replaces inne niż quantum lub classical są błędami, które o tym mówią. scenarios/clock-fitted-log.toml to przykład (6 godzin zegara satelity IGS).
Co pokazuje porównanie na znanym szumie (tests/fixtures/clock_fit/RESULTS.md) i o czym warto pamiętać:
- Osiągnięte: poziom FM białego jest odzyskiwany z dokładnością do 5 % w każdym zapisie syntetycznym, który go ma; dopasowana karta odtwarza prawdziwe odchylenie Allana w granicach współczynnika 1,35 przy każdym czasie uśredniania, dla którego była dopasowana; wykładnik szumu identyfikowany przez dopasowanie równa się wykładnikowi allantools w każdym punkcie (w zapisie 5071A, po usunięciu także z wejścia allantools skoku fazy, który usuwa kondycjonowanie).
- Nieosiągnięte i przypięte przez test: FM typu błądzenie losowe jest odzyskiwany tylko wtedy, gdy dominuje przy najdłuższych czasach uśredniania zapisu, a nawet wtedy w jednym z trzech zapisów serii 131 072 s z przecięciem przy 3 000 s dopasowanie przypisało go do FM typu flicker i do dryfu. Podział między FM typu błądzenie losowe a FM typu flicker nie jest wiarygodny na podstawie jednego zapisu. Przebieg odtwarza stabilność dziennika w czasach uśredniania, które dziennik obejmuje; poza nimi oba wybory się rozchodzą (FM typu flicker to stała podłoga, FM typu błądzenie losowe rośnie jak pierwiastek kwadratowy czasu uśredniania).
- MODELLED: scenariusz „co by było, gdyby” dla szumu tego zegara, jaki pokazuje dziennik, a nie kalibracja zegara ani prognoza jego przyszłego zachowania.
Zniekształcony dziennik jest błędem, który nazywa to, co znaleziono (wiersz, wersję, brakujący selektor), a nigdy częściowym wynikiem: tests/clock_log_real_allantools.rs przypina 24 takie przypadki.
Dowody#
MODELLED. To jest parser, a jego wynik zasila estymatory sprawdzane gdzie indziej względem allantools. Testy jednostkowe w src/clock_log.rs używają małych ręcznie napisanych dzienników, a dla formatów linuxptp i chrony zestawów danych z tests/fixtures/clock_log/linuxptp_chrony/. tests/clock_log_allantools.rs uruchamia porównanie zarejestrowane wstępnie w tests/fixtures/clock_log/PREREGISTRATION.md: trzy syntetyczne dzienniki z ziarnem, po jednym na format, odczytane z powrotem przez niezależny czytnik napisany na podstawie tej strony przez tego samego autora, z ADEV, TDEV i MTIE sprawdzonymi względem allantools 2024,6; porównanie to obejmuje tylko pierwsze trzy formaty, a czytniki linuxptp i chrony mają testy parsera, lecz nie mają porównania z wyrocznią. Dzienniki tego porównania są syntetyczne, więc nic tam nie opisuje prawdziwego zegara ani licznika; porównanie na zmierzonych seriach opisano poniżej.
Na zmierzonych danych#
tests/clock_log_real_allantools.rs uruchamia porównanie zarejestrowane wstępnie w tests/fixtures/clock_log_real/PREREGISTRATION.md (zatwierdzone przed jakimkolwiek zestawem danych lub przebiegiem) na czterech zmierzonych seriach: PHASE.DAT ze Stable32, pierwsze 65 536 odczytów zegara cezowego 5071A względem masera wodorowego oraz zegary satelity IGS G01 (30 s) i stacji ABMF (300 s) z końcowego zegara IGS z 2026-08-02, jako dołączony wycinek z jego NOTICE. Dwa pierwsze nie mają przyznanego prawa redystrybucji: zapisane z nich dzienniki znajdują się w ignorowanym przez git realdata-cache/ (scripts/gen_clock_log_real_ref.py), a test o tym mówi i przechodzi, gdy ich brak, chyba że ustawiono KSHANA_REQUIRE_REALDATA=1. Każdy dziennik jest odczytywany przez silnik i przez niezależny czytnik, a ADEV, TDEV i MTIE są porównywane z allantools 2024,6. Zapisano w tests/fixtures/clock_log_real/RESULTS.md: w dostarczonym czytniku dzienniki TICC, wersja nagłówka i pięć komunikatów o błędach nie osiągnęły progów zarejestrowanych wstępnie; zostały naprawione, a porównanie uruchomiono ponownie bez zmian. Osiągnięte, z wyjątkiem jednej pary: MTIE dziennika częstotliwości różni się od MTIE allantools, ponieważ allantools całkuje częstotliwość pomniejszoną o jej średnią (przypięte jako NOT MET; porównanie jednakowe jest osiągnięte). Wiersz pozostaje MODELLED: dzienniki TICC to zmierzone dane zegara zakodowane ponownie w układzie licznika naszym skryptem (nie znaleziono żadnego rzeczywistego zapisu TICC), a czytnik wzorcowy ma tego samego autora co czytnik silnika.