confd: add gps support to ietf-hardware and ietf-ntp

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-02-10 16:49:40 +01:00
parent 867239c7b8
commit eb1763d49f
7 changed files with 386 additions and 4 deletions
+6
View File
@@ -150,6 +150,12 @@ static int hardware_cand_infer_class(json_t *root, sr_session_ctx_t *session, co
inferred.data.string_val = "infix-hardware:wifi";
err = srx_set_item(session, &inferred, 0, "%s/class", xpath);
}
if (!fnmatch("gps+([0-9])", name, FNM_EXTMATCH)) {
inferred.data.string_val = "infix-hardware:gps";
err = srx_set_item(session, &inferred, 0, "%s/class", xpath);
}
out_free_name:
free(name);
out_free_xpath:
+75 -2
View File
@@ -134,12 +134,85 @@ static int change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd
}
fprintf(fp, "\n");
/* Reference clock (local stratum) - fallback time source */
/* Reference clock configuration */
refclock = lydx_get_child(ntp, "refclock-master");
if (refclock) {
struct lyd_node *source;
int stratum = atoi(lydx_get_cattr(refclock, "master-stratum"));
bool has_gps_sources = false;
/* Only configure local clock if stratum is valid (1-15) */
/*
* GPS/GNSS reference clock sources via gpsd SHM
*
* Each GPS hardware component (gps0, gps1, ...) maps to:
* - SHM unit 2*N for GPS time (coarse, ~100ms)
* - SHM unit 2*N+1 for PPS time (precise, ~1us) if enabled
*
* The mapping is based on gpsd's convention.
*/
LYX_LIST_FOR_EACH(lyd_child(refclock), source, "source") {
const char *receiver, *refid, *poll_str, *precision_str;
const char *offset_str, *delay_str;
bool prefer, pps;
int unit;
receiver = lydx_get_cattr(source, "receiver");
if (!receiver)
continue;
/* Extract unit number from receiver name (gps0 -> 0, gps1 -> 1, ...) */
if (sscanf(receiver, "gps%d", &unit) != 1) {
ERROR("Invalid GPS receiver name: %s (expected gpsN)", receiver);
continue;
}
refid = lydx_get_cattr(source, "refid");
poll_str = lydx_get_cattr(source, "poll");
precision_str = lydx_get_cattr(source, "precision");
offset_str = lydx_get_cattr(source, "offset");
delay_str = lydx_get_cattr(source, "delay");
prefer = lydx_get_bool(source, "prefer");
pps = lydx_get_bool(source, "pps");
if (!has_gps_sources) {
fprintf(fp, "# GPS/GNSS reference clock sources (via gpsd SHM)\n");
has_gps_sources = true;
}
/* GPS time source: SHM unit 2*N */
fprintf(fp, "refclock SHM %d", unit * 2);
if (poll_str)
fprintf(fp, " poll %s", poll_str);
if (refid)
fprintf(fp, " refid %s", refid);
if (precision_str)
fprintf(fp, " precision %s", precision_str);
if (offset_str && atof(offset_str) != 0.0)
fprintf(fp, " offset %s", offset_str);
if (delay_str && atof(delay_str) != 0.0)
fprintf(fp, " delay %s", delay_str);
if (prefer)
fprintf(fp, " prefer");
fprintf(fp, "\n");
/* PPS time source: SHM unit 2*N+1 (if enabled) */
if (pps) {
fprintf(fp, "refclock SHM %d", unit * 2 + 1);
fprintf(fp, " poll %s", poll_str ? poll_str : "-6");
fprintf(fp, " refid PPS");
fprintf(fp, " precision 0.000001");
if (prefer)
fprintf(fp, " prefer");
/* PPS needs a time source for initial lock */
fprintf(fp, " lock %s", refid ? refid : "GPS");
fprintf(fp, "\n");
}
}
if (has_gps_sources)
fprintf(fp, "\n");
/* Local clock fallback - only if stratum is valid (1-15) */
if (stratum >= 1 && stratum <= 15) {
fprintf(fp, "# Local reference clock - fallback stratum %d source\n", stratum);
fprintf(fp, "local stratum %d orphan\n\n", stratum);
+2 -2
View File
@@ -27,7 +27,7 @@ MODULES=(
"infix-syslog@2025-11-17.yang"
"iana-hardware@2018-03-13.yang"
"ietf-hardware@2018-03-13.yang -e hardware-state -e hardware-sensor"
"infix-hardware@2026-01-19.yang"
"infix-hardware@2026-02-08.yang"
"ieee802-dot1q-types@2022-10-29.yang"
"infix-ip@2025-11-02.yang"
"infix-if-type@2026-01-07.yang"
@@ -51,6 +51,6 @@ MODULES=(
"ietf-crypto-types -e cleartext-symmetric-keys"
"infix-crypto-types@2025-11-09.yang"
"ietf-keystore -e symmetric-keys"
"infix-ntp@2025-12-03.yang"
"infix-ntp@2026-02-08.yang"
"infix-keystore@2025-12-17.yang"
)
+139
View File
@@ -21,6 +21,10 @@ module infix-hardware {
contact "kernelkit@googlegroups.com";
description "Vital Product Data augmentation of ieee-hardware and deviations.";
revision 2026-02-08 {
description "Add GPS/GNSS receiver hardware class and container for time sync.";
reference "internal";
}
revision 2026-01-19 {
description "Add probe-timeout for slow USB WiFi dongles.";
reference "internal";
@@ -63,6 +67,15 @@ module infix-hardware {
WiFi radios are hardware components with class 'ih:wifi'.";
}
typedef gps-receiver-ref {
type leafref {
path "/iehw:hardware/iehw:component/iehw:name";
}
description
"Reference to a GPS/GNSS receiver hardware component.
GPS receivers are hardware components with class 'ih:gps'.";
}
typedef wifi-band {
type enumeration {
enum "2.4GHz" {
@@ -95,6 +108,11 @@ module infix-hardware {
description "This identity is used to describe a WiFi radio/PHY";
}
identity gps {
base iahw:hardware-class;
description "GPS/GNSS receiver for time synchronization";
}
deviation "/iehw:hardware/iehw:component/iehw:state/iehw:admin-state" {
deviate add {
must ". = 'locked' or . = 'unlocked'" {
@@ -510,5 +528,126 @@ module infix-hardware {
}
}
}
/*
* GPS/GNSS Receiver configuration (when class = 'ih:gps')
*/
container gps-receiver {
when "derived-from-or-self(../iehw:class, 'ih:gps')";
presence "GPS receiver configuration";
description
"GPS/GNSS receiver configuration and operational data.
This container is present when the hardware component represents
a GPS/GNSS receiver (class 'ih:gps'). GPS receivers provide
precision time via gpsd, which chronyd can use as a reference
clock through shared memory (SHM).
The hardware component name (e.g., 'gps0') determines:
- Device path: /dev/gps0 (symlink managed by udev)
- SHM unit: 0 for GPS time, 1 for PPS (derived from gpsN index)
Supported GNSS constellations depend on the hardware:
- GPS (US)
- GLONASS (Russia)
- Galileo (EU)
- BeiDou (China)";
/*
* Operational state from gpsd
*/
leaf device {
config false;
type string;
description
"Actual device path being used (e.g., /dev/ttyUSB0, /dev/ttyACM0).
This is the physical device that /dev/gpsN symlinks to.";
}
leaf driver {
config false;
type string;
description
"GPS driver/protocol in use (e.g., 'u-blox', 'NMEA', 'SiRF').";
}
leaf activated {
config false;
type boolean;
description
"Whether gpsd has successfully activated this GPS device.";
}
leaf satellites-visible {
config false;
type uint8;
description
"Number of satellites currently visible to the receiver.";
}
leaf satellites-used {
config false;
type uint8;
description
"Number of satellites being used for the fix.";
}
leaf fix-mode {
config false;
type enumeration {
enum "none" {
description "No fix available";
}
enum "2d" {
description "2D fix (latitude/longitude only)";
}
enum "3d" {
description "3D fix (latitude/longitude/altitude)";
}
}
description
"Current GPS fix mode. A 3D fix is required for accurate time.";
}
leaf latitude {
config false;
type decimal64 {
fraction-digits 6;
}
units "degrees";
description
"Current latitude in decimal degrees. Negative values are south.";
}
leaf longitude {
config false;
type decimal64 {
fraction-digits 6;
}
units "degrees";
description
"Current longitude in decimal degrees. Negative values are west.";
}
leaf altitude {
config false;
type decimal64 {
fraction-digits 1;
}
units "meters";
description
"Current altitude above mean sea level in meters.";
}
leaf pps-available {
config false;
type boolean;
description
"Whether this GPS device provides a PPS (Pulse Per Second) signal.
PPS provides microsecond-level timing accuracy.";
}
}
}
}
+164
View File
@@ -15,10 +15,30 @@ module infix-ntp {
"RFC 6991: Common YANG Data Types";
}
import ietf-hardware {
prefix iehw;
reference
"RFC 8348: A YANG Data Model for Hardware Management";
}
import infix-hardware {
prefix ih;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Infix deviations and augments to ietf-ntp.";
revision 2026-02-08 {
description
"Add GPS/GNSS reference clock support.
Augments refclock-master with a list of hardware GPS sources that
chronyd can use via gpsd's shared memory (SHM) interface. Supports
multiple GNSS receivers for redundancy (GPS, GLONASS, Galileo, BeiDou).";
reference "internal";
}
revision 2025-12-03 {
description
"Initial release - NTP server support.
@@ -70,6 +90,150 @@ module infix-ntp {
}
}
/*
* GPS/GNSS reference clock sources
*
* Augments refclock-master with hardware GPS sources that chronyd
* can use via gpsd's shared memory (SHM) interface.
*/
augment "/ntp:ntp/ntp:refclock-master" {
list source {
key "receiver";
max-elements 2;
description
"GPS/GNSS hardware reference clock sources.
Each source references a GPS receiver hardware component and
configures how chronyd should use it. The hardware component
name (e.g., 'gps0') determines the SHM unit number:
- gps0 -> SHM 0 (GPS time), SHM 1 (PPS if available)
- gps1 -> SHM 2 (GPS time), SHM 3 (PPS if available)
Two sources can be configured for redundancy against jamming
or hardware failure. The NTP server will evaluate all sources
and select the best one.";
leaf receiver {
type leafref {
path "/iehw:hardware/iehw:component/iehw:name";
}
must "derived-from-or-self(/iehw:hardware/iehw:component[iehw:name=current()]/iehw:class, 'ih:gps')" {
error-message "Referenced hardware component must be a GPS receiver (class 'ih:gps')";
}
description
"Reference to a GPS receiver hardware component.
The component name determines the device path (/dev/gpsN)
and the SHM unit number for chronyd.";
}
leaf poll {
type int8 {
range "-6..10";
}
default "2";
description
"Polling interval in log2 seconds.
Common values:
- -6 = 1/64 second (for hardware with PPS)
- 0 = 1 second
- 2 = 4 seconds (default, good for GPS without PPS)
- 4 = 16 seconds
Lower values provide faster response but higher CPU usage.";
}
leaf precision {
type decimal64 {
fraction-digits 9;
range "0.000000001..1.0";
}
default "0.1";
units "seconds";
description
"Assumed precision of the reference clock in seconds.
Typical values:
- 0.1 (100ms) - GPS without PPS, default
- 0.001 (1ms) - GPS with software PPS processing
- 0.000001 (1us) - GPS with hardware PPS";
}
leaf offset {
type decimal64 {
fraction-digits 9;
}
default "0.0";
units "seconds";
description
"Constant offset correction in seconds.
Used to compensate for known systematic delays in the
GPS receiver or signal path. Positive values mean the
GPS time is ahead of true time.";
}
leaf delay {
type decimal64 {
fraction-digits 9;
range "0.0..1.0";
}
default "0.0";
units "seconds";
description
"Assumed maximum delay from the GPS receiver in seconds.
This accounts for processing delays in gpsd and the
GPS receiver. Default is 0 (let chronyd estimate).";
}
leaf refid {
type string {
length "1..4";
pattern "[A-Z0-9]+";
}
default "GPS";
description
"Reference identifier (refid) shown in chronyc tracking.
Common values:
- GPS - Generic GPS
- GLO - GLONASS
- GAL - Galileo
- BDS - BeiDou
- PPS - Pulse Per Second (for PPS-only sources)
- GNSS - Multi-constellation receiver";
}
leaf prefer {
type boolean;
default false;
description
"Prefer this source over others when multiple are available.
When multiple reference clocks have similar quality, the
preferred source will be selected. Useful for designating
a primary GPS receiver.";
}
leaf pps {
type boolean;
default false;
description
"Use PPS (Pulse Per Second) signal from this source.
When enabled, chronyd will also configure the PPS SHM
unit (2*N+1) for microsecond-level accuracy. The GPS
receiver must provide PPS and gpsd must be configured
to expose it.
Note: PPS requires the GPS source for initial time lock,
then provides precise time edges.";
}
}
}
/*
* Additional operational state fields from chronyd
* that are not part of the standard ietf-ntp model