NTP time from GPS
I found a blog post about Millisecond accurate Chrony NTP with a USB GPS for $12 USD.
Wow! I’ve had an interest in NTP since about 1994, when I had to prove that a system my (somewhat shady) employers were selling to an NSA cut-out could synchronize to an NTP server.
I actually run the chrony NTP client on my homelab server, and on the random machines I use around the house. $12 isn’t a lot, that’s an attractive hardware thing to fool around with.
Before futzing around, my server’s chrony
had settled like this:
chronyc> sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^- LAX.CALTICK.NET 2 10 377 109 +1211us[+1211us] +/- 69ms
^* time.cloudflare.com 3 10 377 992 -100us[ -107us] +/- 4708us
^- clock.nyc.he.net 2 10 377 1010 +3021us[+3014us] +/- 54ms
^- haka.ruselabs.com 2 9 377 24 -4990us[-4990us] +/- 26ms
I purchased a GT-U7 GPS receiver from Amazon for $10 and change. The GT-U7 u-blox GPS receiver with a micro-USB connector. This will be relevant later.
I followed Austin’s blog post,
but since I use and recommend Arch Linux,
I installed gpsd and pps-tools
on my server (and later my laptop)
like this:
$ pacman -Ss gpsd
$ pacman -Ss pps-tools
I followed Austin’s config file mods,
except that Arch Linux has /etc/chrony.conf
and /etc/default/gpsd
files.
I added the refclock
line to /etc/chrony.conf
:
pool 2.arch.pool.ntp.org iburst
refclock SHM 0 refid NMEA offset 0.000 precision 1e-3 poll 3 noselect
I changed /etc/default/gpsd
to look like this:
# Default settings for gpsd.
START_DAEMON="true"
USBAUTO="true"
GPSD_OPTIONS="-n"
DEVICES="/dev/ttyACM0 /dev/pps0"
USBAUTO="true"
Yeah, USBAUTO
set to “true” twice.
I plugged in the GT-U7, started gpsd
like this this: systemctl start gpsd
,
and restarted chrony
like this: systemctl restart chronyd
I got immediate success.
chronyc> sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#? NMEA 0 3 3 4 +59ms[ +59ms] +/- 1000us
^+ edge-dfw.txryan.com 2 6 17 8 +29us[ +68us] +/- 11ms
^- 66.220.10.2 2 6 17 9 +1384us[+1424us] +/- 29ms
^- 38.229.58.9 2 6 17 9 +1569us[+1608us] +/- 94ms
^* time.cloudflare.com 3 6 17 9 +35us[ +74us] +/- 4670us
The NMEA line is from the GT-U7, and it’s terrible. 59 milliseconds off, where the worst actual NTP servers is 1569 microseconds (1.569 milliseconds) off. That’s a whole order of magnitude worse.
I ran out of time, I had to go make supper.
The next morning, I tried to find out if 1PPS was working.
No dice. gpsmon
said PPS: N/A
.
ppscheck
timed out waiting for an answer from /dev/pps0
.
Then my server locked up. Nothing. No pings, no response on any network port. Even the USB keyboard and old, cheap monitor showed a login prompt, but the server didn’t respond. I had to find the power button to restart it.
A few minutes later, it locked up again.
Ian Fleming wrote that “Once is happenstance, twice is coincidence, three times is enemy action”, so I pulled the GT-U7, and held down the power button once again.
I decided that unless I got this figured out, my $10.67 GT-U7 purchase
was wasted, so I moved development to my laptop.
I got gpsd
installed and configured, and chrony.conf
fixed up.
I could not get gpsmon
or ppscheck
to say PPS was present no matter what.
Eventually, I had chrony.conf
set up like this:
refclock SHM 0 refid NMEA offset 0.000 precision 1e-3 poll 3 noselect
refclock SHM 1 refid PPS precision 1e-7
and /etc/default/gpsd
like this:
# Default settings for gpsd.
START_DAEMON="true"
GPSD_OPTIONS="-n"
DEVICES="/dev/ttyACM0 /dev/pps0"
USBAUTO="true"
Still no PPS. gpsmon -a /dev/ttyACM0
did not show anything PPS related.
Other informative, but not helpful commands:
$ ipcs -m # show the shared memory segments gpsd sets up
$ gpsctl --reset --device /dev/ttyACM0 # reset GT-U7
Look at ASCII character values for every 2-letter chunk of the IPCS shared memory segment key.
Through a combination of googling and reading PPS-related posts,
and deciding that since gpsmon
didn’t show any PPS-related communications
with the GT-U7,
it just wasn’t doing PPS,
I found these 2 commands:
$ ubxtool -e PPS --device /dev/ttyACM0
$ modprobe -v pps_ktimer
The ubxtool
invocation turns on PPS.
It’s possible you may have to do the gpsctl --reset
from above
before “enabling” PPS takes effect.
gpsmon -a
will show PPS “packets”.
The modload
has subtler effects.
If you’ve started gpsd
and plugged in the GT-U7 cable,
It will cause /dev/pps1
and/or /dev/pps2
to appear.
gpsd
will have created /dev/pps0
and it will be inert.
ppscheck /dev/pps1
will show KPPS entries every second after that.
Unplugging the GT-U7, restarting gpsd
and then re-plugging the GT-U7
will cause /dev/pps0
to appear, gpsmon -a
will show PPS-related communications,
and chronyc sources
will start to show a reference clock named “PPS”
that varies pretty wildly.
I’ve got Arch Linux kernel 6.2.11-zen1-1-zen running.
I believe the USB PPS, “KPPS” events, won’t show up without loading
the pps_ktimer
module.
All the older “get your time from GPS” web pages were at least a bit wrong because
at least the 6.2.11 kernel works differently, or maybe gpsd
doesn’t load
the right modules when the GT-U7 gets plugged in.
It looks like a serious investigation of PPS-over-USB shows that the whole process incurs delays, jitter and other artifacts that keep PPS-over-USB from working. I may try to set up a Raspberry Pi with wires from the GT-U7’s pins to GPIO pins, but I’m not going to go further with the all-USB experiment. Between locking up my server, which has been flawless for years, and delays and jittering, the GT-U7 won’t help my server keep better time than it already does.