Building a StratumOne clock with a simple GNSS module

Inspired by Kenneth Finnegan's awesome NTP server on a Raspberry Pi blog post, I wanted to build a StratumOne NTP server for my local network as well.

I just didn't know how, though.

I did know that I needed to provide PPS, pulse-per-second, via the GNSS module. Apparently plugging the PPS pin to the DCD (Data Carrier Detect) pin of the serial interface.

Sooo, what do I need?

A GNSS UART module with PPS output...

Obviously a GNSS antenna...

And of course, a Serial-to-USB module with a pinout that has a DCD pin.

After the parts have arrived, I have wired the GNSS module like below:

After verifying that the serial port working and the GNSS module printing data, I have proceeded to setup gpsd and the necessary things for PPS.

To note: I am using Ubuntu 21.04 (Hirsute Hippo). I'm also going to assume you have your Serial-to-USB module on /dev/ttyUSB0.

Setting up UART module for low latency data transmission and PPS support

Create a file under /etc/udev/rules.d, I'm going to use /etc/udev/rules.d/pps-sources.rules for setting up the perms for PPS and low latency mode on ttyUSB0:

KERNEL=="pps0", OWNER="root", GROUP="dialout", MODE="0660"
KERNEL=="ttyUSB0", RUN+="/bin/setserial -v /dev/%k low_latency irq 4"

Create a systemd template service under /etc/systemd/system, I'm going to use /etc/systemd/system/ldattach@.service for enabling the line discipline 18 for PPS support on ttyUSB0:

Description=Line Discipline for GNSS Timekeeping for %i
Before=ntpd.service chronyd.service chrony.service gpsd.service

ExecStart=/sbin/ldattach 18 /dev/%i


We now need to run sudo systemctl enable --now ldattach@ttyUSB0.

Aaand let's go to gpsd.

Setting up gpsd

sudo apt-get install gpsd gpsd-tools gpsd-clients for the full gpsd experience.

# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.

# Other options you want to pass to gpsd

# Automatically hot add/remove USB GPS devices via gpsdctl

Restart gpsd with sudo systemctl restart gpsd and wait for a short while. After that, launch ntpshmmon. You should see data coming from NTP0 and NTP1 (these are the GPS and PPS time data respectively).

Well, we have time tracking! Let's go to chrony.

Setting up chrony

sudo apt-get install chrony

My baseline config for /etc/chrony/chrony.conf:

# Fallback source server setup, change these based on your location

initstepslew 30

confdir /etc/chrony/conf.d

keyfile /etc/chrony/chrony.keys

driftfile /var/lib/chrony/chrony.drift

ntsdumpdir /var/lib/chrony

logdir /var/log/chrony

maxupdateskew 100.0


makestep 1 3

leapsectz right/UTC

allow all

And for using the PPS data as time source, I create a file on /etc/chrony/conf.d/pps.conf:

# set larger delay to allow the NMEA source to overlap with
# the other sources and avoid the falseticker status
refclock SHM 0                         refid GPS precision 1e-1 offset 0.130 delay 0.99
refclock SHM 1                         refid PPS precision 1e-7

# apparmor configs for both chrony and gpsd are borked for the SOCK protocol, ignored 
#refclock SOCK /run/chrony.ttyUSB0.sock refid GPS  precision 1e-1 offset 0.130 delay 0.99
#refclock SOCK /run/chrony.pps0.sock    refid PPS  precision 1e-7

After restarting chrony and waiting for a while, this can be seen on chronyc sources -v:

The asterisk shows the currently used source.

There we go!

(Update: 2022-08-09): Some portions of the config was taken from I've lost the URL at the time I wrote it so I've only just recently found it.

Copyright 2018-2023, linuxgemini (İlteriş Yağıztegin Eroğlu). Any and all opinions listed here are my own and not representative of my employers; future, past and present.