Машинний переклад, не перевірений носієм мови. Еталоном є англійська версія.

Синхронізація часуРедагувати на GitHubДжерело: docs/CLOCK-LOG-FORMATS.md

Формати журналів годинника

src/clock_log.rs читає виміряний журнал годинника в один нормалізований ряд фази: рівномірну сітку відліків (time_s, phase_s) з інтервалом відліків tau0, заданим або знайденим, з підрахованими всіма проміжками та названими одиницями журналу. Читається вісім форматів, кожен як задокументована підмножина. Усе поза підмножиною відхиляється з помилкою, що називає знайдене, а не читається приблизно.

Скорочення. RINEX — це Receiver Independent Exchange Format. TICC — лічильник часових інтервалів TAPR (Tucson Amateur Packet Radio). ADEV — перекривна девіація Аллана, TDEV — девіація часу, а MTIE — максимальна похибка часового інтервалу. tau0 — інтервал відліків у секундах.

Де можна використати журнал#

  • telecom-timing: [series.clock_log] замість series.samples або series.csv_path. Фаза журналу в нс — це ряд похибки часу, який перевіряється за масками, а звіт отримує блок clock_log, що пояснює, як його прочитано.
  • Вид clock: таблиця [clock_log] додає до прогону блок measured: як прочитано журнал, SHA-256 таблиці (input_sha256; scenario_hash прогону не охоплює таблицю) і відліків фази, а також перекривні ADEV, TDEV і MTIE за октавними коефіцієнтами усереднення. Потрібен runs = 1. Симульований holdover не змінюється.

Без таблиці обидва види розбираються, хешуються й повідомляються точно як раніше.

[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

Формати#

rinex-clk: файли годинників RINEX, версії 2,00 і від 3,00 до 3,04 (зокрема дев'ятисимвольне поле імені 3,04). Запис RINEX VERSION / TYPE перевіряється, а будь-яка інша версія відхиляється з назвою знайденої версії: IGS публікує формат годинника (rinex_clock300.txt, 302, 304) до 3,04, а документи RINEX 4,00, 4,01 і 4,02 визначають лише файли спостережень, навігації та метеорології, тож версії 4,00 файла годинника для читання немає. Заголовок читається лише заради TIME SYSTEM ID і пропускається до END OF HEADER. З записів даних зберігаються лише записи AS (супутник) і AR (приймач або станція) одного годинника, вибраного за іменем. Їхнє перше поле значення, зміщення годинника в секундах, є фазою. Рядки продовження та записи CR, DR і MS пропускаються. Епохи — це календарні поля запису, у секундах від 1980-01-06 00:00:00 у власній шкалі часу файла, без перетворення.

timelab: ASCII-журнали фази або частоти в стилі TimeLab, простий текстовий вигляд, який експортує TimeLab і читають його ASCII-імпорт та Stable32. Одне значення на рядок: або один стовпець за рівномірного tau0, або два стовпці time value. Стовпці можуть розділятися пробілами, комами або крапками з комою.

  • Рядки, що починаються з #, ; або %, є коментарями. Коментар у вигляді key = value (або key: value) задає tau0 (с), units (s, ms, us, ns або ps, для фази), data (phase або frequency) чи time_units (s або mjd, для першого з двох стовпців).
  • Один початковий нечисловий рядок сприймається як заголовок стовпців.
  • Дані частоти (відносної частоти) приймаються лише як один стовпець. Вони інтегруються у фазу як x[i+1] = x[i] + y[i]·tau0 (NIST SP 1065, 2008, §4,1).
  • Двійкові файли TimeLab і файли проєктів .tim не читаються.

ticc: журнали лічильника TAPR TICC (посібник з експлуатації TICC, редакції від 23 листопада 2016 і 15 грудня 2025, «режими вимірювання»). Кожне поле даних — це секунди з 12 десятковими розрядами (за замовчуванням 11 з прошивки 20251101: читається будь-яка кількість до 15). Позначка часу читається з десяткового тексту як ціле число фемтосекунд, а не як двійкове число з рухомою комою, тож фаза журналу, знятого через дні після запуску лічильника, зберігає власну роздільність лічильника: позначка часу f64 за 3.0e5 с має крок 5.8e-11 с, приблизно 60 пс лічильника.

  • Рядки режиму міток часу (<seconds> chA, <seconds> chB) і канал псевдомітки режиму TimeLab (chC) — це мітки часу подій. Фаза вибраного каналу — це його мітка часу мінус номінальний такт: x[k] = t[k] − t[0] − n[k]·tau0, де n[k] — ціле число tau0 від першої події. Пропущена подія є проміжком.
  • Рядки режиму інтервалів (<seconds> TI(A->B) або будь-яка мітка, що починається з TI) читаються безпосередньо як фаза, по одному значенню на tau0. Сценарій має вказувати tau0_s, бо рядок не містить часу.
  • Режим парних позначок часу друкує ті самі рядки в порядку каналів і читається як режим позначок часу; режим 3-Corner Hat додає синтезований chC, що читається як вище.
  • Вивід у режимах періоду та налагодження відхиляється. Канал, чиї показання не зростають, повідомляється як режим періоду. Двійковий режим позначок часу не є текстом, і файл у ньому відхиляється з повідомленням про це.
  • Рядки, що починаються з #, і рядки, які не мають вигляду <number> <label> (наприклад, меню запуску), пропускаються й підраховуються.

Журнали linuxptp і chrony#

Ці п'ять форматів читають те, що працюючий демон часу записує про власний годинник. Вони призначені для перевірки ланцюга часу постфактум (граничний годинник, PHC гросмейстера, клієнт chrony) тими самими оцінювачами, що й інші журнали. Вони не читають конфігурацію демона, і ніщо тут не спілкується з демоном.

ptp4l, phc2sys, ts2phc: рядки серво linuxptp, кожен формат читає лише рядки власної програми. Значення — це рядок вигляду

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
  • Мітка часу — це перше [seconds.fraction] у рядку (монотонний годинник демона). Її також читають після префікса syslog, де ідентифікатор процесу на кшталт ptp4l[812]: не має дробової частини й не береться.
  • Фаза — це записане offset у наносекундах, перетворене на секунди. Це виміряна демоном похибка часу його підлеглого годинника відносно еталона, як вона записана: додатна, коли підлеглий випереджає. Це похибка, що лишається під керуванням серво, а не дрейф вільно ходового годинника.
  • Слова між міткою часу та offset називають джерело (master, eth0 master для префікса [eth0], CLOCK_REALTIME phc, /dev/ptp0). Журнал з більш ніж одним джерелом потребує clock = "<that text>"; помилка їх перелічує.
  • Значення до першого повідомлення серво s2 (заблоковано) пропускаються й підраховуються в n_skipped_lines: s0 і s1 — це незаблокований стан і стан кроку, чиї зсуви є перехідними процесами втягування. Серво, яке ніколи не досягає s2, відхиляється. Рядок без поля стану читається як є.
  • Підсумкові рядки (rms ... max ...), повідомлення delay і selected best master, а також рядки інших програм пропускаються й підраховуються.
  • Мітки часу, що йдуть назад (перезапущений демон), відхиляються; розділіть журнал.

chrony-tracking, chrony-statistics: файли log tracking і log statistics chrony, що читаються за позицією стовпця. Значення — це рядок щонайменше з 14 стовпців (tracking) або 13 (statistics), що починається з дати й часу UTC YYYY-MM-DD HH:MM:SS. Рядок заголовка стовпців пропускається й підраховується; лінія ==== під ним пропускається без підрахунку.

  • Tracking: стовпець 7, локальний зсув під час оновлення годинника, у секундах. IP-адреса або ідентифікатор еталона змінюється разом із вибраним джерелом, тож без clock кожен рядок читається як один локальний годинник. clock = "<id>" зберігає один ідентифікатор еталона.
  • Statistics: стовпець 5, оцінений зсув джерела, у секундах. На кожну адресу джерела припадає один ряд, а журнал потребує clock = "<address>", якщо містить більше одного.
  • Епохи — це секунди від 1980-01-06 00:00:00 UTC, без обробки високосних секунд. Chrony записує рядок за кожне оновлення або вибірку джерела, тому інтервал нерівномірний. tau0 — це медіанний інтервал; значення менш ніж за половину tau0 після збереженого перед ним відкидається й підраховується в n_duplicates, а довший інтервал є проміжком, про який повідомляє gaps = "refuse". Задайте tau0_s і gaps = "linear" для журналу зі сталим інтервалом опитування.

Стовпці chrony і розкладки рядків linuxptp взято з публічної документації демонів. Фікстури в tests/fixtures/clock_log/linuxptp_chrony/ написано для Kshana з вигаданими значеннями (див. її NOTICE.md); жодного реального журналу не читали, тож розкладка, що різниться між версіями демонів, усе ще може бути відхилена або прочитана неправильно. Рядок, що не збігається, пропускається й підраховується, ніколи не вгадується.

tau0 і проміжки#

Коли журнал містить мітки часу, а tau0 не задано, tau0 — це медіанний інтервал між послідовними мітками часу, округлений до чотирьох значущих цифр, і джерело повідомляється як median-spacing. Інтервал лічильника відрізняється від номінального на зсув частоти еталона, частин у 10^6 або менше. tau0_s у сценарії завжди має перевагу (джерело input), далі йде коментар заголовка tau0 (джерело header).

Відліки розміщуються на сітці t0 + i·tau0, а відсутня епоха є проміжком. Значення, що відхиляються від сітки більш ніж на 1 % від tau0, відкидаються й підраховуються, як і дублікати (зберігається перший). Політика проміжків — одна з:

  • refuse (за замовчуванням): будь-який проміжок є помилкою, що підраховує проміжки;
  • longest: зберегти найдовший відрізок без проміжків;
  • linear: заповнити внутрішні проміжки лінійною інтерполяцією й повідомити кількість заповнених.

Оцінювачам потрібен запис без проміжків, тому проміжки розв'язуються тут.

Ваш власний годинник#

Таблиця [clock_log] на типі clock повідомляє, наскільки стабільним був ваш власний годинник, поряд із симуляцією: scenarios/clock-measured-log.toml (kshana example clock-measured-log) — це clock-holdover.toml із 96-показниковою вибіркою годинника супутника IGS як вбудованим журналом TimeLab. Замініть text на path і format для вашого власного файла. Блок measured у результаті містить, як прочитано журнал (формат, tau0 і звідки він, кожен пропуск), та його ADEV, TDEV і MTIE; він не змінює симуляцію.

Керування holdover вашим власним годинником: [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

Журнал підганяється за процедурою картки пристрою з src/clock_library/card.rs (кондиціонування, вилучення квадратичної складової, ідентифікація шуму за лагом 1, зважений метод найменших квадратів у білому PM, білому FM, flicker FM і FM випадкового блукання за часів усереднення щонайменше з 30 ступенями свободи). Названий годинник тоді бере q_wf = h_0 / 2, q_rw = 2 pi^2 h_-2, flicker_floor = sqrt(2 ln 2 h_-1) і підігнаний drift; y0 лишається таким, як у сценарії, бо журнал несе зміщення свого еталона, а не годинника після дисциплінування. Білий фазовий шум підганяється й повідомляється, але не симулюється. Результат отримує блок fitted (коефіцієнти, точки підгонки, значення, які використав прогін, що було відкинуто, SHA-256 журналу), а годинник несе ідентифікатор measured:<12 hex of the phase hash>. За runs > 1 ансамбль бере вибірки з підігнаного годинника (блок measured потребує runs = 1). Підгонка без частотного шуму, надто короткий для підгонки журнал і replaces, відмінний від quantum чи classical, є помилками, які про це кажуть. scenarios/clock-fitted-log.toml — приклад (6 годин годинника супутника IGS).

Що показує порівняння на відомому шумі (tests/fixtures/clock_fit/RESULTS.md) і що слід пам'ятати:

  • Досягнуто: рівень білого FM відновлюється в межах 5 % на кожному синтетичному записі, що його має; підігнана картка відтворює справжню девіацію Аллана в межах множника 1,35 за кожного часу усереднення, за якого її підігнано; показник шуму, який ідентифікує підгонка, дорівнює показнику allantools у кожній точці (на записі 5071A, коли крок фази, який вилучає кондиціонування, вилучено також із входу allantools).
  • Не досягнуто й закріплено тестом: FM випадкового блукання відновлюється лише тоді, коли воно домінує на найдовших часах усереднення запису, і навіть тоді в одному з трьох записів ряду з 131 072 с із перетином у 3 000 с підгонка віддала його flicker FM і дрейфу. Розподіл між FM випадкового блукання та flicker FM ненадійний за одним записом. Прогін відтворює стабільність журналу за часів усереднення, які журнал охоплює; за їхніми межами обидва вибори розходяться (flicker FM — стала підлога, FM випадкового блукання зростає як квадратний корінь часу усереднення).
  • MODELLED: сценарій «що, якщо» для шуму цього годинника, як його показує журнал, а не калібрування годинника й не передбачення його майбутньої поведінки.

Спотворений журнал є помилкою, що називає знайдене (рядок, версію, відсутній вибірник), ніколи не частковим результатом: tests/clock_log_real_allantools.rs закріплює 24 таких випадків.

Докази#

MODELLED. Це розбирач, і його вихід живить оцінювачі, перевірені деінде за allantools. Модульні тести в src/clock_log.rs використовують невеликі журнали, написані вручну, а для форматів linuxptp і chrony — фікстури в tests/fixtures/clock_log/linuxptp_chrony/. tests/clock_log_allantools.rs виконує порівняння, попередньо зареєстроване в tests/fixtures/clock_log/PREREGISTRATION.md: три синтетичні журнали із зерном, по одному на формат, перечитані незалежним читачем, написаним за цією сторінкою тим самим автором, з ADEV, TDEV і MTIE, перевіреними за allantools 2024,6; це порівняння охоплює лише перші три формати, а читачі linuxptp і chrony мають тести розбирача, але не порівняння з оракулом. Журнали цього порівняння синтетичні, тож ніщо там не описує справжній годинник чи лічильник; порівняння на виміряних рядах описано нижче.

На виміряних даних#

tests/clock_log_real_allantools.rs виконує порівняння, попередньо зареєстроване в tests/fixtures/clock_log_real/PREREGISTRATION.md (зафіксоване до будь-якої фікстури чи прогону), на чотирьох виміряних рядах: PHASE.DAT Stable32, перші 65 536 показань цезієвого годинника 5071A проти водневого мазера, а також годинники супутника IGS G01 (30 с) і станції ABMF (300 с) з фінального годинника IGS за 2026-08-02, як вендорений зріз із його NOTICE. Перші два не мають надання на поширення: журнали, записані з них, лежать у realdata-cache/, що ігнорується git (scripts/gen_clock_log_real_ref.py), і тест про це каже та проходить, коли їх немає, якщо не задано KSHANA_REQUIRE_REALDATA=1. Кожен журнал читається рушієм і незалежним читачем, а ADEV, TDEV і MTIE порівнюються з allantools 2024,6. Записано в tests/fixtures/clock_log_real/RESULTS.md: на поставленому читачі журнали TICC, версія заголовка та п'ять повідомлень про помилки не досягли попередньо зареєстрованих порогів; їх виправлено, а порівняння повторено без змін. Досягнуто, крім однієї пари: MTIE частотного журналу відрізняється від allantools, бо allantools інтегрує частоту мінус її середнє (закріплено як NOT MET; порівняння за рівних умов досягнуто). Рядок лишається MODELLED: журнали TICC — це виміряні дані годинника, перекодовані в розмітку лічильника нашим скриптом (жодного справжнього захоплення TICC не знайдено), а еталонний читач має того самого автора, що й читач рушія.