Servidor NTP Estrato 1 Autónomo en Raspberry Pi: chrony con DCF77 y GPS PPS en 2026
En Raspberry Pi OS Bookworm/Trixie, los tutoriales antiguos de servidores NTP fallan debido a libgpiod v2, el cambio a chrony y las modificaciones en los chips GPIO. Una implementación en C++20 utiliza un receptor DCF77 RC8000 en GPIO 17 y un módulo GPS ublox NEO-M8T con PPS en GPIO 18. El servidor logra una precisión <1 µs sin internet, con verificación independiente de fuentes de tiempo.
Problemas con Implementaciones Desactualizadas
libgpiod v2 Rompe el Código GPIO
libgpiod v2 eliminó la API v1 sin compatibilidad. El código antiguo con gpiod_chip_open_by_name y gpiod_chip_get_line no compila:
// libgpiod v1 — NO COMPILA con v2
struct gpiod_chip *chip = gpiod_chip_open_by_name("gpiochip0");
struct gpiod_line *line = gpiod_chip_get_line(chip, 17);
gpiod_line_request_both_edges_events(line, "dcf77");
La nueva API requiere gpiod_line_settings_new() y gpiod_line_config_new():
// libgpiod v2 — enfoque correcto
const SettingsPtr settings(gpiod_line_settings_new());
gpiod_line_settings_set_edge_detection(settings.get(), GPIOD_LINE_EDGE_BOTH);
gpiod_line_settings_set_event_clock(settings.get(), GPIOD_LINE_CLOCK_REALTIME);
const LineCfgPtr line_cfg(gpiod_line_config_new());
gpiod_line_config_add_line_settings(line_cfg.get(), &offset, 1, settings.get());
const RequestPtr request(gpiod_chip_request_lines(chip.get(), req_cfg.get(), line_cfg.get()));
Una ventaja clave es GPIOD_LINE_CLOCK_REALTIME: los eventos reciben marcas de tiempo del kernel en el momento de la interrupción, reduciendo el jitter de 40 ms a <5 ms.
Transición de ntpd a chrony
ntpd ha sido reemplazado por chrony en Bookworm. Comparación:
| Parámetro | ntpd | chrony |
|-----------|------|--------|
| Convergencia | minutos | segundos (makestep 0.5 3) |
| Pérdida de señal | degradación | retención (local stratum 10) |
| PPS | parches | nativo refclock PPS |
| Precisión PPS | ~100 µs | <1 µs |
Ambos admiten NTP SHM para intercambio de datos a través de memoria compartida.
Chip GPIO en RPi 5
gpiochip0 está desactualizado. En RPi 5/Trixie, usa gpiochip4 (verifica con gpiodetect).
Arquitectura del Sistema
Dos fuentes de tiempo:
- GPS NEO-M8T: PPS (/dev/pps0) + NMEA vía gpsd (NTP SHM #0)
- DCF77 RC8000: GPIO 17 → decodificador → NTP SHM #2
- chrony: estrato 1 con preferencia PPS
- Monitor web: chronyc + journald en :8080
Configuración de chrony:
refclock PPS /dev/pps0 refid PPS precision 1e-9 poll 0 dpoll -2 lock NMEA prefer
refclock SHM 0 refid NMEA precision 1e-3 poll 0 dpoll -2 offset 0.0 delay 0.2 noselect
refclock SHM 2 refid DCF7 precision 1e-2 offset 0.0 delay 0.008 poll 6 dpoll 5
local stratum 10
DCF77 sirve como respaldo y detector de suplantación GPS (discrepancia >varios segundos).
Decodificador DCF77
Marca de Tiempo del Kernel
GPIOD_LINE_CLOCK_REALTIME proporciona una marca de tiempo de interrupción, no del espacio de usuario. Desviación estándar en SHM: <5 ms.
NTP SHM para 64 bits
Estructura con relleno:
struct NtpShmTime {
int32_t mode;
int32_t count;
int64_t clockTimeStampSec;
int32_t clockTimeStampUSec;
int32_t _pad;
int64_t receiveTimeStampSec;
int32_t receiveTimeStampUSec;
int32_t leap;
int32_t precision;
int32_t nsamples;
int32_t valid;
};
volatile NtpShmTime *shm_ = nullptr;
Protocolo DCF77
59 bits/minuto en BCD con paridad:
- 100 ms ALTO → bit 0
- 200 ms ALTO → bit 1
- Pausa >1.5 s → marcador de minuto
Validación: 3 grupos de paridad + timegm(). Arranque: 2 tramas con diferencia de 60 s.
Configuración NEO-M8T
- Firmware TIM: RAIM, bajo jitter PPS
- dynModel=Stationary: jitter 5–10 ns
- antCableDelay=50 ns: compensación de cable
- 4 constelaciones: 20–30 satélites
- lpMode=Continuous: sin ahorro de energía
Monitor Web y Despliegue
dcf77_web: HTTP :8080, chronyc en JSON, UI en binario. API: /api/status, /api/prefer (solo localhost).
Compilación con vcpkg + CMake:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
Despliegue con Ansible y endurecimiento systemd.
Resultados
chronyc tracking:
- Estrato: 1
- Tiempo del sistema: +42 ns
- Desplazamiento RMS: 51 ns
- Dispersión raíz: 15 ns
DCF77: ~3 ms.
Conclusiones Clave
- libgpiod v2 requiere un nuevo modelo de objetos con
GPIOD_LINE_CLOCK_REALTIME - chrony supera a ntpd en precisión PPS (<1 µs) y retención
- NTP SHM para 64 bits necesita relleno explícito para
int64_t - NEO-M8T TIM + Stationary: jitter PPS 5–10 ns
- DCF77 valida GPS contra suplantación
— Editorial Team
Aún no hay comentarios.