Skip to main content

PpRx as NTP Server

PpRx can be configured to provide a highly accurate GNSS-backed time source for NTP. When configured and while PpRx is running with a valid navigation solution, the system clock on the host computer will automatically be synchronized with GNSS time.

PpRx writes GNSS time into shared memory (SHM) segments. The NTP daemon on the host reads those segments and uses them as a reference clock. The exact NTP configuration syntax depends on which NTP implementation is installed.

Configuration for ntpsec (Ubuntu 22.04+)

Modern Ubuntu systems ship with ntpsec instead of classic ntpd. Edit /etc/ntpsec/ntp.conf and add:

refclock SHM unit 0 refid SHM0
refclock SHM unit 1 refid SHM1
refclock SHM unit 2 prefer refid SHM2
refclock SHM unit 3 refid SHM3
note

PpRx creates SHM units 0 and 1 with 600 permissions (root-readable only) and units 2 and 3 with 666 permissions (world-readable). Since ntpsec runs as the ntpsec user rather than root, only units 2 and 3 are accessible to it. The prefer keyword belongs on unit 2.

Then restart the NTP service:

sudo systemctl restart ntpsec
note

If the config file includes a tos minclock line (e.g., tos minclock 4 minsane 3), NTP will require at least 4 reachable clocks before disciplining the system clock. In a deployment without an internet connection, lower this value or remove the line so that the GNSS SHM source alone can be accepted:

tos minclock 1 minsane 1

Configuration for classic ntpd

On systems using classic ntpd, edit /etc/ntp.conf and add:

server 127.127.28.0
fudge 127.127.28.0 refid SHM0

server 127.127.28.1
fudge 127.127.28.1 refid SHM1

server 127.127.28.2 prefer
fudge 127.127.28.2 refid SHM2

server 127.127.28.3
fudge 127.127.28.3 refid SHM3

Then restart the NTP service:

sudo systemctl restart ntp

Configuration for chrony

On systems using chrony, add the following to /etc/chrony/chrony.conf:

refclock SHM 0 refid SHM0
refclock SHM 1 refid SHM1
refclock SHM 2 prefer refid SHM2
refclock SHM 3 refid SHM3

Then restart:

sudo systemctl restart chrony
note

SHM units 0 and 1 will show reach = 0 — PpRx does not write valid time data to those units. Unit 2 is the active GNSS time source.

Running PpRx with NTP

Add the --ntp option to the .opt file:

--ntp

Then run PpRx:

pprx -f ./autogen_01.opt

NTP will automatically reconfigure to select the time solution found by PpRx once a valid navigation solution is achieved.

Verifying GNSS Time is Active

After PpRx acquires a navigation solution, check which NTP implementation is running and use the appropriate command.

ntpsec / ntpd: run ntpq -p and look for:

  • *SHM(2) in the leftmost column. The * means it is selected as the primary source.
  • reach = 377 on the SHM(2) row. All 8 recent polls succeeded.
  • offset settling under ±1 ms.

A healthy steady-state ntpq -p output looks like:

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
SHM(0) .SHM0. 0 l - 64 0 0.000 0.000 0.000
SHM(1) .SHM1. 0 l - 64 0 0.000 0.000 0.000
*SHM(2) .SHM2. 0 l 49 64 377 0.000 -0.011 3.175
SHM(3) .SHM3. 0 l - 64 0 0.000 0.000 0.000

chrony: run chronyc sources -v and look for:

  • #* SHM2 The * means it is selected as the primary source.
  • Reach = 377 on the SHM2 row.
  • Last sample offset under ±1 ms.

A healthy steady-state chronyc sources -v output looks like:

MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#? SHM0 0 4 0 - +0ns[ +0ns] +/- 0ns
#? SHM1 0 4 0 - +0ns[ +0ns] +/- 0ns
#* SHM2 0 4 377 13 -22us[ -13us] +/- 42us
#? SHM3 0 4 0 - +0ns[ +0ns] +/- 0ns

Checking Shared Memory Status

To confirm the shared memory segments were created:

ipcs -m

Expected output includes four segments with keys starting at 0x4e545030:

------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x4e545030 ... root 600 96 1
0x4e545031 ... root 600 96 1
0x4e545032 ... root 666 96 2
0x4e545033 ... root 666 96 1

Units 0 and 1 have 600 permissions (root-readable only). ntpsec cannot attach to them (nattch = 1); chrony runs with sufficient privileges to attach (nattch = 2) but PpRx does not write valid data to those units so they show reach = 0 regardless. Unit 2 (666) is the active source and shows nattch = 2 (ntpsec) or nattch = 3 (chrony) when actively read.

If these segments are absent, PpRx is not running or was not started with --ntp.

Troubleshooting

SHM(2) never gets a * in ntpq -p

PpRx may not have a valid navigation solution yet. The shared memory clock source is only populated once PpRx begins producing navigation solutions. Check the PpRx display (pass --verbose to PpRx) and confirm a Standard Solution is shown with reasonable position values before diagnosing NTP further.

note

SHM(0) and SHM(1) will always show reach = 0. This is expected due to their root-only permissions and is not an error.

reach field stays at 0

NTP is not reading from the shared memory segments. Confirm PpRx was started with the --ntp option, and that the NTP config additions were saved and the NTP service was restarted afterward.

offset is large or oscillating

If PpRx has a valid solution but offset is large (>10 ms), check:

  • The host system clock was not recently set to a wrong time (a large initial offset can cause NTP to step-adjust rather than slew).
  • No other high-stratum NTP sources are competing with SHM(2) and winning. The prefer keyword on the SHM2 line (shown in the config above) should prevent this.

NTP is not installed

  • ntpsec (Ubuntu 22.04+): sudo apt install ntpsec
  • ntpd (older systems): sudo apt install ntp. On systems running systemd-timesyncd (which conflicts with ntpd), disable it first: sudo systemctl disable --now systemd-timesyncd.