Formate für Uhren-Logs
src/clock_log.rs liest ein gemessenes Uhren-Log in eine einzige normierte Phasenreihe ein: ein gleichmäßiges Raster von Abtastwerten (time_s, phase_s), mit dem angegebenen oder ermittelten Abtastintervall tau0, wobei jede Lücke gezählt und die Einheiten des Logs benannt werden. Acht Formate werden gelesen, jeweils eine dokumentierte Teilmenge. Alles außerhalb der Teilmenge wird mit einem Fehler zurückgewiesen, der benennt, was gefunden wurde, statt es näherungsweise zu lesen.
Abkürzungen. RINEX ist das Receiver Independent Exchange Format. TICC ist der Zeitintervallzähler von TAPR (Tucson Amateur Packet Radio). ADEV ist die überlappende Allan-Abweichung, TDEV die Zeitabweichung und MTIE der maximale Zeitintervallfehler. tau0 ist das Abtastintervall in Sekunden.
Wo ein Log verwendet werden kann#
telecom-timing:[series.clock_log]anstelle vonseries.samplesoderseries.csv_path. Die Phase des Logs in ns ist die gegen die Masken geprüfte Reihe des Zeitfehlers, und der Bericht erhält einen Blockclock_log, der angibt, wie es gelesen wurde.- Der Typ Uhr: Eine Tabelle
[clock_log]fügt dem Lauf einen Blockmeasuredhinzu: wie das Log gelesen wurde, den SHA-256 der Tabelle (input_sha256; derscenario_hashdes Laufs deckt die Tabelle nicht ab) und der Phasenabtastwerte sowie die überlappende ADEV, TDEV und MTIE bei oktavweisen Mittelungsfaktoren. Er benötigtruns = 1. Das simulierte Holdover ist unverändert.
Ohne die Tabelle werden beide Typen exakt wie zuvor geparst, gehasht und ausgewiesen.
[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
Die Formate#
rinex-clk: RINEX-Clock-Dateien, Versionen 2,00 und 3,00 bis 3,04 (einschließlich des Namensfelds mit neun Zeichen aus 3,04). Der Eintrag RINEX VERSION / TYPE wird geprüft, und jede andere Version wird abgelehnt, wobei die gefundene Version genannt wird: IGS veröffentlicht das Clock-Format (rinex_clock300.txt, 302, 304) bis 3,04, und die RINEX-Dokumente 4,00, 4,01 und 4,02 definieren nur Beobachtungs-, Navigations- und Meteorologiedateien, sodass es keine Version 4,00 der Clock-Datei zu lesen gibt. Der Kopf wird nur auf sein TIME SYSTEM ID gelesen und bis END OF HEADER übersprungen. Von den Datensätzen werden nur die Sätze AS (Satellit) und AR (Empfänger oder Station) einer nach Namen gewählten Uhr behalten. Ihr erstes Wertefeld, der Uhrenbias in Sekunden, ist die Phase. Fortsetzungszeilen und die Sätze CR, DR und MS werden übersprungen. Epochen sind die Kalenderfelder des Satzes, als Sekunden ab 1980-01-06 00:00:00 im eigenen Zeitsystem der Datei, ohne Umrechnung.
timelab: ASCII-Phasen- oder Frequenz-Logs im Stil von TimeLab, die Klartextform, die TimeLab exportiert und die sein ASCII-Import und Stable32 lesen. Pro Zeile steht ein Messwert: entweder eine einzelne Spalte mit gleichmäßigem tau0 oder zwei Spalten time value. Spalten dürfen durch Leerraum, Kommas oder Semikola getrennt sein.
- Zeilen, die mit
#,;oder%beginnen, sind Kommentare. Ein Kommentar der Formkey = value(oderkey: value) setzttau0(s),units(s,ms,us,nsoderps, für die Phase),data(phaseoderfrequency) odertime_units(sodermjd, für die erste von zwei Spalten). - Eine führende nichtnumerische Zeile wird als Spaltenkopf genommen.
- Frequenzdaten (relative Frequenz) werden nur als einzelne Spalte akzeptiert. Sie werden zur Phase als
x[i+1] = x[i] + y[i]·tau0integriert (NIST SP 1065, 2008, §4,1). - Binäre Projektdateien von TimeLab und Projektdateien
.timwerden nicht gelesen.
ticc: Logs des TAPR-TICC-Zählers (TICC Operation Manual, Fassungen vom 23. November 2016 und vom 15. Dezember 2025, „measurement modes“). Jedes Datenfeld ist in Sekunden mit 12 Dezimalstellen (11 standardmäßig ab Firmware 20251101: jede Anzahl bis 15 wird gelesen). Ein Zeitstempel wird aus seinem Dezimaltext als ganze Zahl von Femtosekunden gelesen, nicht als binäre Gleitkommazahl, sodass die Phase eines Logs, das Tage nach dem Start des Zählers aufgenommen wurde, die eigene Auflösung des Zählers behält: Ein Zeitstempel als f64 bei 3.0e5 s hat einen Abstand von 5.8e-11 s, etwa die 60 ps des Zählers.
- Zeilen im Timestamp-Modus (
<seconds> chA,<seconds> chB) und der Pseudo-Zeitstempelkanal des TimeLab-Modus (chC) sind Zeitstempel von Ereignissen. Die Phase des gewählten Kanals ist sein Zeitstempel minus des nominellen Takts:x[k] = t[k] − t[0] − n[k]·tau0, wobein[k]die ganze Anzahl vontau0seit dem ersten Ereignis ist. Ein verpasstes Ereignis ist eine Lücke. - Zeilen im Intervall-Modus (
<seconds> TI(A->B)oder jede mitTIbeginnende Bezeichnung) werden direkt als Phase gelesen, ein Messwert jetau0. Das Szenario musstau0_sangeben, weil die Zeile keine Zeit enthält. - Der Modus Paired Timestamp gibt dieselben Zeilen in Kanalreihenfolge aus und wird als Modus Timestamp gelesen; der Modus 3-Corner Hat ergänzt das synthetisierte
chC, gelesen wie oben. - Ausgaben im Modus Period und im Modus Debug werden abgelehnt. Ein Kanal, dessen Messwerte nicht ansteigen, wird als Modus Period gemeldet. Der binäre Modus Timestamp ist kein Text, und eine Datei davon wird mit einer Meldung abgelehnt, die das sagt.
- Zeilen, die mit
#beginnen, und Zeilen, die nicht<number> <label>entsprechen (etwa das Startmenü), werden übersprungen und gezählt.
Logs von linuxptp und chrony#
Diese fünf Formate lesen, was ein laufender Zeit-Daemon über seine eigene Uhr schreibt. Sie dienen dazu, eine Zeitkette nachträglich zu prüfen (eine Boundary Clock, die PHC eines Grandmasters, einen chrony-Client) und zwar mit denselben Schätzern wie die anderen Logs. Sie lesen nicht die Konfiguration eines Daemons, und nichts hier spricht mit einem Daemon.
ptp4l, phc2sys, ts2phc: die Servo-Zeilen von linuxptp, wobei jedes Format nur Zeilen seines eigenen Programms liest. Ein Messwert ist eine Zeile der Form
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
- Der Zeitstempel ist das erste
[seconds.fraction]in der Zeile (die monotone Uhr des Daemons). Er wird auch nach einem Syslog-Präfix gelesen, wobei eine Prozess-ID wieptp4l[812]:keinen Bruchteil hat und nicht übernommen wird. - Die Phase ist der protokollierte Wert
offsetin Nanosekunden, in Sekunden umgerechnet. Es ist der vom Daemon gemessene Zeitfehler seiner Slave-Uhr gegenüber seiner Referenz, wie protokolliert: positiv, wenn die Slave-Uhr vorgeht. Es ist der unter Servo-Regelung verbleibende Fehler, nicht die Drift einer freilaufenden Uhr. - Die Wörter zwischen dem Zeitstempel und
offsetbenennen die Quelle (master,eth0 masterbei einem Präfix[eth0],CLOCK_REALTIME phc,/dev/ptp0). Ein Log mit mehr als einer Quelle brauchtclock = "<that text>"; der Fehler listet sie auf. - Messwerte, bevor das Servo erstmals
s2(locked) meldet, werden ausgelassen und inn_skipped_linesgezählt:s0unds1sind die Zustände ungesperrt und Sprung, deren Offsets Einschwingvorgänge sind. Ein Servo, dass2nie erreicht, wird zurückgewiesen. Eine Zeile ohne Zustandsfeld wird so gelesen, wie sie ist. - Zusammenfassungszeilen (
rms ... max ...), Meldungendelayundselected best mastersowie Zeilen anderer Programme werden übersprungen und gezählt. - Zeitstempel, die rückwärts laufen (ein neu gestarteter Daemon), werden zurückgewiesen; das Log ist zu teilen.
chrony-tracking, chrony-statistics: die Dateien log tracking und log statistics von chrony, nach Spaltenposition gelesen. Ein Messwert ist eine Zeile mit mindestens 14 Spalten (tracking) oder 13 (statistics), die mit einem UTC-Datum und einer Uhrzeit YYYY-MM-DD HH:MM:SS beginnt. Die Spaltenkopfzeile wird übersprungen und gezählt; die Trennlinie ==== darunter wird übersprungen, ohne gezählt zu werden.
- Tracking: Spalte 7, der lokale Offset bei der Aktualisierung der Uhr, in Sekunden. Die IP-Adresse oder Referenz-ID ändert sich mit der gewählten Quelle, sodass ohne
clockjede Zeile als eine lokale Uhr gelesen wird.clock = "<id>"behält eine Referenz-ID. - Statistics: Spalte 5, der geschätzte Offset einer Quelle, in Sekunden. Es gibt eine Reihe je Quelladresse, und das Log braucht
clock = "<address>", wenn es mehr als eine enthält. - Die Epochen sind Sekunden ab 1980-01-06 00:00:00 UTC, ohne Behandlung von Schaltsekunden. Chrony schreibt eine Zeile je Aktualisierung oder je Quellmesswert, sodass der Abstand nicht gleichmäßig ist.
tau0ist der mediane Abstand; ein Messwert unter einem halbentau0nach dem davor behaltenen wird verworfen und inn_duplicatesgezählt, und ein größerer Abstand ist eine Lücke, diegaps = "refuse"meldet. Geben Sietau0_sundgaps = "linear"für ein Log mit gleichmäßigem Abfrageintervall an.
Die Spalten von chrony und die Zeilenlayouts von linuxptp entsprechen der öffentlichen Dokumentation der Daemons. Die Fixtures in tests/fixtures/clock_log/linuxptp_chrony/ sind für Kshana mit erfundenen Werten geschrieben (siehe dessen NOTICE.md); es wurde kein reales Log gelesen, sodass ein Layout, das sich zwischen Versionen eines Daemons unterscheidet, weiterhin zurückgewiesen oder falsch gelesen werden kann. Eine Zeile, die nicht passt, wird übersprungen und gezählt, nie erraten.
tau0 und Lücken#
Enthält das Log Zeitstempel und ist kein tau0 angegeben, ist tau0 der mediane Abstand aufeinanderfolgender Zeitstempel, auf vier signifikante Stellen gerundet, und die Quelle wird als median-spacing gemeldet. Der Abstand eines Zählers weicht vom nominellen um den Frequenzversatz der Referenz ab, Teile in 10^6 oder weniger. Ein tau0_s im Szenario hat immer Vorrang (Quelle input), danach ein Kommentar im Kopf tau0 (Quelle header).
Die Messwerte werden auf das Raster t0 + i·tau0 gelegt, und eine fehlende Epoche ist eine Lücke. Messwerte, die um mehr als 1 % von tau0 vom Raster abweichen, werden verworfen und gezählt, ebenso Duplikate (das erste bleibt erhalten). Die Regel für Lücken ist eine der folgenden:
refuse(der Standard): Jede Lücke ist ein Fehler, der die Lücken zählt;longest: den längsten lückenlosen Abschnitt behalten;linear: innere Lücken durch lineare Interpolation füllen und die Zahl der gefüllten ausweisen.
Die Schätzer brauchen eine lückenlose Aufzeichnung, weshalb Lücken hier aufgelöst werden.
Ihre eigene Uhr#
Eine Tabelle [clock_log] bei der Uhrenart gibt neben der Simulation an, wie stabil Ihre eigene Uhr war: scenarios/clock-measured-log.toml (kshana example clock-measured-log) ist clock-holdover.toml mit einem Auszug von 96 Messwerten einer IGS-Satellitenuhr als eingebettetes TimeLab-Log. Ersetzen Sie text durch path und format für Ihre eigene Datei. Der Block measured im Ergebnis hält fest, wie das Log gelesen wurde (Format, tau0 und woher es stammt, jede Lücke) sowie seine ADEV, TDEV und MTIE; er verändert die Simulation nicht.
Den holdover mit Ihrer eigenen Uhr antreiben: [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
Das Log wird mit dem Geräteprofil-Verfahren aus src/clock_library/card.rs angepasst (Konditionierung, Entfernung des quadratischen Anteils, Rauschidentifikation mit Lag 1, gewichtete kleinste Quadrate in White PM, White FM, Flicker FM und Random-Walk FM bei den Mittelungszeiten mit mindestens 30 Freiheitsgraden). Die benannte Uhr übernimmt dann q_wf = h_0 / 2, q_rw = 2 pi^2 h_-2, flicker_floor = sqrt(2 ln 2 h_-1) und den angepassten drift; y0 bleibt so, wie das Szenario es hat, weil ein Log den Versatz seiner Referenz trägt, nicht den der Uhr nach der Disziplinierung. Weißes Phasenrauschen wird angepasst und angegeben, aber nicht simuliert. Das Ergebnis erhält einen Block fitted (die Koeffizienten, die Anpassungspunkte, die vom Lauf verwendeten Werte, was ausgelassen wurde, der SHA-256 des Logs), und die Uhr trägt die Kennung measured:<12 hex of the phase hash>. Mit runs > 1 zieht das Ensemble aus der angepassten Uhr (der Block measured braucht runs = 1). Eine Anpassung ohne Frequenzrauschen, ein zu kurzes Log für eine Anpassung und ein anderes replaces als quantum oder classical sind Fehler, die das sagen. scenarios/clock-fitted-log.toml ist ein Beispiel (6 Stunden einer IGS-Satellitenuhr).
Was der Vergleich an bekanntem Rauschen (tests/fixtures/clock_fit/RESULTS.md) zeigt und was zu beachten ist:
- Erreicht: Das White-FM-Niveau wird innerhalb von 5 % bei jeder synthetischen Aufzeichnung wiedergewonnen, die es hat; das angepasste Profil reproduziert die wahre Allan-Abweichung innerhalb eines Faktors von 1,35 bei jeder Mittelungszeit, bei der angepasst wurde; der von der Anpassung identifizierte Rauschexponent ist bei jedem Punkt gleich dem von allantools (bei der Aufzeichnung 5071A, sobald der Phasensprung, den die Konditionierung entfernt, auch aus der Eingabe von allantools entfernt ist).
- Nicht erreicht und durch den Test festgeschrieben: Random-Walk-FM wird nur wiedergewonnen, wenn es die längsten Mittelungszeiten der Aufzeichnung dominiert, und selbst dann nur bei einer von drei Aufzeichnungen einer Reihe von 131.072 s mit einem Übergang bei 3.000 s; die Anpassung schrieb es Flicker-FM und der Drift zu. Die Aufteilung zwischen Random-Walk-FM und Flicker-FM ist aus einer einzelnen Aufzeichnung nicht verlässlich. Der Lauf reproduziert die Stabilität des Logs über die Mittelungszeiten, die das Log abdeckt; darüber hinaus laufen die beiden Möglichkeiten auseinander (Flicker-FM ist eine konstante Untergrenze, Random-Walk-FM wächst mit der Quadratwurzel der Mittelungszeit).
- MODELLED: ein Was-wäre-wenn für das Rauschen dieser Uhr, wie das Log es zeigt, keine Kalibrierung der Uhr und keine Vorhersage ihres künftigen Verhaltens.
Ein fehlerhaftes Log ist ein Fehler, der nennt, was gefunden wurde (die Zeile, die Version, die fehlende Auswahl), nie ein Teilergebnis: tests/clock_log_real_allantools.rs legt 24 solcher Fälle fest.
Nachweise#
MODELLED. Dies ist ein Parser, und seine Ausgabe speist Schätzer, die anderswo gegen allantools validiert werden. Die Unit-Tests in src/clock_log.rs verwenden kleine handgeschriebene Logs und für die Formate linuxptp und chrony die Fixtures in tests/fixtures/clock_log/linuxptp_chrony/. tests/clock_log_allantools.rs führt den in tests/fixtures/clock_log/PREREGISTRATION.md vorab festgelegten Vergleich aus: drei mit festem Seed erzeugte synthetische Logs, eines je Format, zurückgelesen von einem unabhängigen Reader, den derselbe Autor anhand dieser Seite geschrieben hat, wobei ADEV, TDEV und MTIE gegen allantools 2024,6 geprüft werden; dieser Vergleich deckt nur die ersten drei Formate ab, und die Reader für linuxptp und chrony haben Parser-Tests, aber keinen Vergleich mit einer unabhängigen Referenz. Die Logs dieses Vergleichs sind synthetisch, daher beschreibt nichts davon eine reale Uhr oder einen realen Zähler; der Vergleich an gemessenen Reihen wird unten beschrieben.
An gemessenen Daten#
tests/clock_log_real_allantools.rs führt den in tests/fixtures/clock_log_real/PREREGISTRATION.md vorab festgelegten Vergleich aus (vor jeder Fixture und jedem Lauf eingecheckt) an vier gemessenen Reihen: der Stable32-Datei PHASE.DAT, den ersten 65.536 Messwerten einer Cäsiumuhr 5071A gegen einen Wasserstoffmaser und den Uhren des IGS-Satelliten G01 (30 s) und der Station ABMF (300 s) aus der finalen IGS-Uhr vom 2026-08-02, als mitgeführter Ausschnitt mit seiner NOTICE. Die ersten beiden tragen keine Weitergabeerlaubnis: Die daraus geschriebenen Logs liegen in dem von git ignorierten realdata-cache/ (scripts/gen_clock_log_real_ref.py), und der Test sagt das und besteht, wenn sie fehlen, sofern nicht KSHANA_REQUIRE_REALDATA=1 gesetzt ist. Jedes Log wird von der Engine und von einem unabhängigen Reader gelesen, und ADEV, TDEV und MTIE werden mit allantools 2024,6 verglichen. Festgehalten in tests/fixtures/clock_log_real/RESULTS.md: Beim ausgelieferten Reader verfehlten die TICC-Logs, die Versionskennung im Kopf und fünf Fehlermeldungen die vorab festgelegten Zielwerte; sie wurden korrigiert, und der Vergleich wurde unverändert wiederholt. Erreicht bis auf ein Paar: Das MTIE eines Frequenzlogs weicht von dem von allantools ab, weil allantools die Frequenz minus ihren Mittelwert integriert (festgeschrieben als NOT MET; der Vergleich unter gleichen Bedingungen ist erreicht). Die Zeile bleibt MODELLED: Die TICC-Logs sind gemessene Uhrendaten, die von unserem Skript in das Layout des Zählers umkodiert wurden (es wurde keine echte TICC-Aufzeichnung gefunden), und der Referenz-Reader hat denselben Autor wie der der Engine.