Eigenständiger Stratum-1-NTP-Server auf Raspberry Pi: chrony mit DCF77 und GPS PPS im Jahr 2026
Auf Raspberry Pi OS Bookworm/Trixie scheitern alte NTP-Server-Tutorials aufgrund von libgpiod v2, dem Wechsel zu chrony und Änderungen bei GPIO-Chips. Eine C++20-Implementierung nutzt einen DCF77-Empfänger RC8000 an GPIO 17 und ein ublox NEO-M8T-GPS-Modul mit PPS an GPIO 18. Der Server erreicht eine Genauigkeit von <1 µs ohne Internet, mit unabhängiger Zeitquellenverifizierung.
Probleme mit veralteten Implementierungen
libgpiod v2 macht GPIO-Code unbrauchbar
libgpiod v2 hat die v1-API ohne Kompatibilität entfernt. Alter Code mit gpiod_chip_open_by_name und gpiod_chip_get_line lässt sich nicht kompilieren:
// libgpiod v1 — KOMPILIERT NICHT mit 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");
Die neue API erfordert gpiod_line_settings_new() und gpiod_line_config_new():
// libgpiod v2 — korrekter Ansatz
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()));
Ein entscheidender Vorteil ist GPIOD_LINE_CLOCK_REALTIME: Ereignisse erhalten Zeitstempel vom Kernel zum Interrupt-Zeitpunkt, was Jitter von 40 ms auf <5 ms reduziert.
Übergang von ntpd zu chrony
ntpd wurde in Bookworm durch chrony ersetzt. Vergleich:
| Parameter | ntpd | chrony |
|-----------|------|--------|
| Konvergenz | Minuten | Sekunden (makestep 0.5 3) |
| Signalverlust | Verschlechterung | Haltebetrieb (local stratum 10) |
| PPS | Patches | native refclock PPS |
| PPS-Genauigkeit | ~100 µs | <1 µs |
Beide unterstützen NTP SHM für Datenaustausch über Shared Memory.
GPIO-Chip auf RPi 5
gpiochip0 ist veraltet. Auf RPi 5/Trixie verwenden Sie gpiochip4 (prüfen mit gpiodetect).
Systemarchitektur
Zwei Zeitquellen:
- GPS NEO-M8T: PPS (/dev/pps0) + NMEA über gpsd (NTP SHM #0)
- DCF77 RC8000: GPIO 17 → Decoder → NTP SHM #2
- chrony: Stratum 1 mit prefer PPS
- Web-Monitor: chronyc + journald auf :8080
chrony-Konfiguration:
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 dient als Fallback und GPS-Spoofing-Detektor (Abweichung >mehrere Sekunden).
DCF77-Decoder
Kernel-Zeitstempel
GPIOD_LINE_CLOCK_REALTIME liefert einen Interrupt-Zeitstempel, nicht aus dem Userspace. StdDev in SHM: <5 ms.
NTP SHM für 64-Bit
Struktur mit Padding:
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;
DCF77-Protokoll
59 Bits/Minute in BCD mit Parität:
- 100 ms HIGH → Bit 0
- 200 ms HIGH → Bit 1
- Pause >1,5 s → Minutenmarker
Validierung: 3 Paritätsgruppen + timegm(). Bootstrap: 2 Frames mit 60 s Differenz.
NEO-M8T-Konfiguration
- TIM-Firmware: RAIM, niedriger PPS-Jitter
- dynModel=Stationary: Jitter 5–10 ns
- antCableDelay=50 ns: Kabelkompensation
- 4 Konstellationen: 20–30 Satelliten
- lpMode=Continuous: kein Energiesparmodus
Web-Monitor und Deployment
dcf77_web: HTTP :8080, chronyc in JSON, UI im Binärformat. API: /api/status, /api/prefer (nur localhost).
Build mit vcpkg + CMake:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
Ansible-Deployment mit systemd-Härtung.
Ergebnisse
chronyc tracking:
- Stratum: 1
- Systemzeit: +42 ns
- RMS-Offset: 51 ns
- Root-Dispersion: 15 ns
DCF77: ~3 ms.
Wichtigste Erkenntnisse
- libgpiod v2 erfordert ein neues Objektmodell mit
GPIOD_LINE_CLOCK_REALTIME - chrony übertrifft ntpd in PPS-Genauigkeit (<1 µs) und Haltebetrieb
- NTP SHM für 64-Bit benötigt explizites Padding für
int64_t - NEO-M8T TIM + Stationary: PPS-Jitter 5–10 ns
- DCF77 validiert GPS gegen Spoofing
— Editorial Team
Noch keine Kommentare.