라즈베리 파이 독립형 Stratum 1 NTP 서버 구축: 2026년 DCF77 및 GPS PPS를 활용한 chrony 설정
라즈베리 파이 OS Bookworm/Trixie에서는 libgpiod v2 도입, chrony로의 전환, GPIO 칩 변경으로 인해 기존 NTP 서버 튜토리얼이 작동하지 않습니다. C++20 구현으로 GPIO 17에 DCF77 수신기 RC8000, GPIO 18에 PPS 기능을 갖춘 ublox NEO-M8T GPS 모듈을 연결합니다. 이 서버는 인터넷 없이도 <1 µs 정확도를 달성하며 독립적인 시간 소스 검증을 제공합니다.
구형 구현 방식의 문제점
libgpiod v2로 인한 GPIO 코드 호환성 문제
libgpiod v2는 이전 버전과의 호환성 없이 v1 API를 제거했습니다. gpiod_chip_open_by_name 및 gpiod_chip_get_line을 사용하는 기존 코드는 컴파일되지 않습니다:
// libgpiod v1 — 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");
새로운 API는 gpiod_line_settings_new() 및 gpiod_line_config_new()를 요구합니다:
// libgpiod v2 — 올바른 접근 방식
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()));
주요 장점은 GPIOD_LINE_CLOCK_REALTIME입니다: 이벤트는 인터럽트 발생 시 커널로부터 타임스탬프를 받아 지터를 40 ms에서 <5 ms로 줄입니다.
ntpd에서 chrony로의 전환
Bookworm에서는 ntpd가 chrony로 대체되었습니다. 비교:
| 매개변수 | ntpd | chrony |
|-----------|------|--------|
| 수렴 속도 | 분 단위 | 초 단위 (makestep 0.5 3) |
| 신호 손실 | 성능 저하 | 홀드오버 (local stratum 10) |
| PPS | 패치 필요 | 기본 지원 refclock PPS |
| PPS 정확도 | ~100 µs | <1 µs |
둘 다 공유 메모리를 통한 데이터 교환을 위한 NTP SHM을 지원합니다.
RPi 5의 GPIO 칩
gpiochip0은 더 이상 사용되지 않습니다. RPi 5/Trixie에서는 gpiochip4를 사용하세요 (gpiodetect로 확인).
시스템 아키텍처
두 가지 시간 소스:
- GPS NEO-M8T: PPS (/dev/pps0) + gpsd를 통한 NMEA (NTP SHM #0)
- DCF77 RC8000: GPIO 17 → 디코더 → NTP SHM #2
- chrony: PPS 우선 선택을 통한 stratum 1
- 웹 모니터: :8080 포트에서 chronyc + journald
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는 백업 및 GPS 스푸핑 탐지기 역할을 합니다 (몇 초 이상의 불일치 시).
DCF77 디코더
커널 타임스탬프
GPIOD_LINE_CLOCK_REALTIME은 사용자 공간이 아닌 인터럽트 타임스탬프를 제공합니다. SHM의 표준 편차: <5 ms.
64비트를 위한 NTP SHM
패딩이 포함된 구조체:
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 프로토콜
패리티가 포함된 BCD 형식의 59비트/분:
- 100 ms HIGH → 비트 0
- 200 ms HIGH → 비트 1
- >1.5 s 일시 정지 → 분 마커
검증: 3개의 패리티 그룹 + timegm(). 부트스트랩: 60초 차이를 가진 2개의 프레임.
NEO-M8T 구성
- TIM 펌웨어: RAIM, 낮은 PPS 지터
- dynModel=Stationary: 지터 5–10 ns
- antCableDelay=50 ns: 케이블 보상
- 4개 별자리: 20–30개 위성
- lpMode=Continuous: 절전 모드 없음
웹 모니터 및 배포
dcf77_web: HTTP :8080, JSON 형식의 chronyc, 바이너리 UI. API: /api/status, /api/prefer (로컬호스트 전용).
vcpkg + CMake로 빌드:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
Ansible 배포 및 systemd 강화.
결과
chronyc tracking:
- Stratum: 1
- 시스템 시간: +42 ns
- RMS 오프셋: 51 ns
- 루트 분산: 15 ns
DCF77: ~3 ms.
핵심 요약
- libgpiod v2는
GPIOD_LINE_CLOCK_REALTIME을 포함한 새로운 객체 모델을 요구합니다 - chrony는 PPS 정확도 (<1 µs)와 홀드오버 측면에서 ntpd를 능가합니다
- 64비트 NTP SHM은
int64_t를 위한 명시적 패딩이 필요합니다 - NEO-M8T TIM + Stationary: PPS 지터 5–10 ns
- DCF77는 GPS 스푸핑에 대한 검증을 제공합니다
— Editorial Team
아직 댓글이 없습니다.