A couple of folks recommended I post this as a feature request. This is kind of half description, half how-to … let me know if any additional information is needed.
I think a number of assumptions could be made with a simple set
command that specifies the GPS and PPS interfaces, as well as some refclock
specific parameters for chrony, then stitches the data together in the background.
A lot of this was adapted from domschl/RaspberryNtpServer, which I’d previously used to set up a Raspberry Pi-based NTP server with an Adafruit GPS breakout board.
NOTE: I had a bunch of helpful references linked in this post, but there’s a limit of two links per post for new users - kind of silly.
Requirements
- GPS source.
- GPS source device drivers.
- PPS source.
- PPS source device drivers.
It’s more or less all via serial, it just depends on how your serial port is delivered:
- RS232 serial port, ex. DE-9, IDC 2x5 motherboard header, etc.
- Parallel port, ex. DB-25.
- USB to serial.
- PCIe to serial.
I have a Timebeat TimeCard Mini that delivers GPS via /dev/ttyS2
over an Exar XR17V3521 Dual PCIe UART and PPS via u.fl, which I connected to the motherboard’s IDC 2x5 serial header (dev/ttyS0
) on pins 1 (DCD) and 5 (GND).
GPS
The kernel module that supports my serial device - CONFIG_SERIAL_8250_EXAR
- was enabled by default.
PPS
Since my PPS source is a serial port, I needed to enable CONFIG_PPS_CLIENT_LDISC
. This is set to m
in the kernel config, so I used modprobe pps_ldisk
to enable it on my test bench. I plan to set CONFIG_PPS_CLIENT_LDISC
to y
via a custom kernel when I build the installer ISO.
Some other possible PPS drivers:
CONFIG_PPS_CLIENT_KTIMER
- Kernel timer, used for debug purposes.CONFIG_PPS_CLIENT_GPIO
- PPS via GPIO.CONFIG_PPS_CLIENT_PARPORT
- PPS via parallel port.
Software
Some software needs to be installed:
gpsd
- required to monitor and process GPS data and provide it tochrony
.setserial
- needed forudev
rules later.gpsd-tools
- forcgps
; not required, but used to test/debug.pps-tools
- forppstest
; not required, but used to test/debug.
I plan to install these via the ISO build process for now.
gpsd
Add/update the lines below to /etc/default/gpsd
, depending on your GPS source:
Serial:
GPSD_OPTIONS="-n -G"
DEVICES="/dev/GPS_INTERFACE"
USBAUTO="false"
USB:
GPSD_OPTIONS="-n -G"
DEVICES="/dev/GPS_INTERFACE"
USBAUTO="true"
Replace GPS_INTERFACE
with the name of the GPS source interface, ex. ttyS2
.
Enable/start the service, etc.
I plan to use a post-boot script to ensure this is configured and not overwritten.
PPS
How you set up your PPS source depends on the platform, source (serial, GPIO, etc.). In my case, I connected my PPS source direct to a serial port, /dev/ttyS0
, and needed to create the PPS interface.
ldattach 18 /dev/ttyS0
18
is the line discipline for PPS.
This created /dev/pps0
, which is used as the PPS source later. This device is owned by root, so udev rules are needed to allow chrony to access the device.
Create /etc/udev/rules.d/pps-sources.rules
and add these rules:
KERNEL=="PPS_INTERFACE", OWNER="root", GROUP="_chrony", MODE="0660"
KERNEL=="GPS_INTERFACE", RUN+="/bin/setserial -v /dev/%k low_latency irq 4"
Replace PPS_INTERFACE
with the name of the PPS interface, ex. pps0
, and GPS_INTERFACE
with the name of the GPS source interface, ex. ttyS2
.
Run sudo udevadm control --reload-rules && sudo udevadm trigger
to reload and trigger the new rules.
I plan to use a post-boot script to ensure the above is configured and not overwritten.
chrony
chrony needs two lines added to its configuration:
refclock PPS /dev/PPS_INTERFACE lock GPS
refclock SHM 0 refid GPS precision 1e-1 offset 0.01 delay 0.2 noselect
Replace PPS_INTERFACE
with the name of the PPS interface, ex. pps0
. The value of offset
may need to be adjusted based on the initial offset of the GPS source - it should be < 200ms. If it isn’t, the offset
needs to be adjusted to match. For example, if the GPS offset is +500ms, set offset
to 0.5
.
Restart the service.
Since /run/chrony/chrony.conf
is auto-generated by service_ntp.py
, I’ll probably set up a post-commit hook to re-add the refclock
lines if I ever need to mess with the NTP configuration after the initial setup.
Status
There are a couple of show
commands mapped to chrony/chronyc commands already that are useful:
sh ntp
= chronyc > sourcestatssh ntp system
= chronyc > tracking
The sources
command is also useful, but is not built-in. It’s close to sourcestats
, but provides a small amount of additional detail.
vbash-4.1# chronyc sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* PPS0 0 4 377 17 -13us[ -15us] +/- 32us
#? GPS 0 4 377 17 +16ms[ +16ms] +/- 200ms
^- ec2-34-206-168-146.compu> 2 8 377 171 +390us[ +399us] +/- 57ms
^- ec2-18-193-41-138.eu-cen> 2 8 377 145 +404us[ +416us] +/- 57ms
^- ec2-122-248-201-177.ap-s> 2 7 377 72 +391us[ +403us] +/- 58ms
Here’s what it looks like from vyos’ point of view:
vyos@vyos:~$ sh ntp
.- Number of sample points in measurement set.
/ .- Number of residual runs with same sign.
| / .- Length of measurement set (time).
| | / .- Est. clock freq error (ppm).
| | | / .- Est. error in freq.
| | | | / .- Est. offset.
| | | | | | On the -.
| | | | | | samples. \
| | | | | | |
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
PPS0 58 27 913 -0.000 0.037 -0ns 24us
GPS 27 13 416 +5.900 3.165 +16ms 550us
ec2-34-206-168-146.compu> 13 9 36m -0.040 0.022 +391us 10us
ec2-18-193-41-138.eu-cen> 6 3 21m -0.045 0.151 +370us 22us
ec2-122-248-201-177.ap-s> 11 5 21m -0.043 0.020 +391us 6050ns
vyos@vyos:~$ sh ntp system
Reference ID : 50505330 (PPS0)
Stratum : 1
Ref time (UTC) : Wed Feb 07 02:15:23 2024
System time : 0.000000120 seconds fast of NTP time
Last offset : -0.000002066 seconds
RMS offset : 0.000003705 seconds
Frequency : 19.267 ppm fast
Residual freq : -0.000 ppm
Skew : 0.039 ppm
Root delay : 0.000000001 seconds
Root dispersion : 0.000051959 seconds
Update interval : 16.0 seconds
Leap status : Normal
Debugging
cat
your GPS serial interface; you should see fast-scrolling lines comma-delimited text that start with$GPGGA
.- Use
cgps
to see the GNSS data; look forStatus: 3D fix (xx secs)
- this means you’ve connected to GPS satellites. - Run
ppstest /dev/pps0
to check for a valid PPS signal; you should see lines that look likesource 0 - assert <timestamp>, sequence: <seq_number> - clear <timestamp>, sequence: <seq_num>
Examples:
# cat /dev/ttyS2
$GPGGA,1413247.000,48432.1655,N,01342323.7322,E,1,06,1.340,2505.5,M,347.6,M,,*69
$GPGSA,A,3,123,303,248,205,037,415,,,,,,,1.59,1.340,0.90*049
# ppstest /dev/pps0
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1606833766.999998552, sequence: 341090 - clear 0.000000000, sequence: 0
source 0 - assert 1606833767.999998573, sequence: 341091 - clear 0.000000000, sequence: 0
source 0 - assert 1606833768.999998751, sequence: 341092 - clear 0.000000000, sequence: 0