PTP-сервер дома. Часть 3

PTP-сервер дома. Часть 3

Семён сохраняет полезное_)

Подумал что стоит подробнее рассказать про взаимосвязь программных компонентов для PTP сервера. А именно про демон ts2phc и ptp4l.

ts2phc — это демон из пакета linuxptp, который синхронизирует PTP Hardware Clocks (PHC) (аппаратные часы на NIC / PTP-устройствах) по внешним сигналам метки времени: например по PPS/1PPS от GPS, по триггеру оборудованию или по другим внешним таймстемпам. Он может брать один внешний источник времени и распределять его на один или несколько PHC.

ts2phc — это демон, который читает внешние метки времени (PPS, hardware timestamp events, GPIO-based timestamps, SDPs от NIC и т.п.) и далкее преобразует их в “реальные” временные события(определяет точное время прихода PPS в системных координатах CLOCK_REALTIME или по PHC). Далее корректирует PHC через стандартный Linux интерфейс адаптации аппаратных часов. Программно он взаимодействует с ядром так же, как ptp4l и phc2sys через ioctl() на /dev/ptpX и через PPSAPI (/dev/ppsX).В моем случае PPS получается через карту Intel i210, этот метод называется - PTP Hardware Clock (PHC) timestamping subsystem.

Если внешний источник подключён к сетевой карте (i210 SDP0 → TS0), то NIC генерирует hardware timestamp events, которые ядро публикует через PTP интерфейс: /dev/ptpX

ts2phc использует:

  • ioctl(fd, PTP_ENABLE_PPS)
  • ioctl(fd, PTP_SYS_OFFSET_EXTENDED)
  • ioctl(fd, PTP_CLOCK_GETCAPS)
  • read() из event-fd, созданного через ioctl(fd, PTP_EXTTS_REQUEST)

EXTTS (External Timestamping) — главный механизм:

NIC выдаёт таймстемпы (EXTTS) на каждый приход внешнего импульса, а ts2phc подписывается на EXTTS события с помощью:

struct ptp_extts_request req;
req.index = 0;           // обычно TS0/SDP0
req.flags = PTP_ENABLE_FEATURE | PTP_RISING_EDGE;

ioctl(fd, PTP_EXTTS_REQUEST, &req);

После этого read(fd, &event, sizeof(event)) возвращает:

struct ptp_extts_event {
    struct ptp_clock_time t;
    unsigned int index;
    unsigned int flags;
};

Где t.sec / t.nsecвремя аппаратного таймстемпа PHC.

Когда ts2phc получил:

  • внешнюю метку времени T_ext
  • текущее время PHC T_phc

Он вычисляет offset = T_ext – T_phc

Это ровно тот же алгоритм, что используется в ptp4l но:

  • источник времени не сетевой (не message timestamp),
  • а EXTTS/PPS timestamp.

Внутри используется тот же класс servo (линейный, pi, ntp-похожий), что и в других компонентах LinuxPTP.

Расскажу как данные приходят к PTP4L

1. SDP0 → PTP Hardware Clock (внутри i210)

Когда на SDP0 приходит импульс (PPS от GPS):

  1. Внутренний блок timestamping i210 фиксирует
  2. время прихода сигнала в координатах PHC.
  3. i210 создаёт событие EXTTS (External Timestamp).
  4. Драйвер NIC (igb) передаёт его ядру Linux как:
struct ptp_extts_event
{
    struct ptp_clock_time t;
    unsigned int index;     // 0 = SDP0
    unsigned int flags;
};

2. Ядро Linux → /dev/ptpX → ts2phc

ts2phc подписывается на эти события:

struct ptp_extts_request req = {
    .index = 0, // SDP0
    .flags = PTP_ENABLE_FEATURE | PTP_RISING_EDGE,
};
ioctl(ptp_fd, PTP_EXTTS_REQUEST, &req);

После чего read(ptp_fd) возвращает аппаратные метки времени, например:

sec = 1700000000
nsec = 000000123

Это PHC timestamp.

3. ts2phc сравнивает внешнее время с PHC

ts2phc должен понять:

внешний PPS = точное время T_ref

PHC = текущее T_phc

Отсюда:

offset = T_ref - T_phc

Через внешний источник (обычно GNSS/PPS):

  • либо PPS → system time (CLOCK_REALTIME)
  • либо PPS → встроенное GPS API
  • либо конфигурация говорит что PPS = ровная секунда

ts2phc вычисляет этот offset.

4. ts2phc корректирует PHC через clock_adjtime()

После расчёта offset ts2phc делает:

clock_adjtime(clkid, &timex);

или:

ioctl(ptp_fd, PTP_CLOCK_SET_TIME)

(для больших шагов)

В результате:

PHC подгоняется под внешний PPS

Это единственный реальный «путь данных».

Т.е. ts2phc перезаписывает частоту / фазу PHC.

5. ptp4l читает PHC через ioctl и timestamp’ы пакетов

Теперь ptp4l использует этот же самый PHC как:

5.1. Источник timestamp’ов

Каждый приходящий PTP-пакет (Sync, Follow_Up, Delay_Resp) имеет hardware timestamp от PHC.

Драйвер NIC помещает timestamp в skb_hwtstamps → ptp4l читает через:

recvmsg()
 → cmsg(SCM_TIMESTAMPING)

5.2. Опорные часы

Когда ptp4l (в роли MASTER или SLAVE) нужно:

  • узнать текущее время PHC
  • пересчитать его в системные часы
  • вычислить смещение

Он вызывает:

ioctl(/dev/ptpX, PTP_SYS_OFFSET_EXTENDED)

или

clock_gettime(CLOCKID)

Таким образом ptp4l постоянно видит актуальное состояние PHC.


Report Page