This page documents my project to build a stratum 1 NTP time server at my house. Its hostname is ntp.kenyonralph.com.
- GPS selection
- Cable Construction
- GPS preparation
- Debian GNU/Linux setup
- FreeBSD setup
- Conclusion
- Notes
- References
GPS selection
I selected the Garmin GPS 18x LVC for the following reasons:
- Plenty of documentation exists by people who have used this unit as an NTP timing source.
- The receiver and antenna are integrated into one enclosure.
- Minimal assembly and soldering necessary, compared to bare-board receivers.
- Available on Amazon for a decent price.
I ordered the 18x (and a serial cable) on Amazon on 2011-09-05. UPS delivered it on 2011-09-09.
Cable Construction
I simply spliced the GPS 18x LVC wires onto serial and USB wires. This matches what’s documented in Garmin’s GPS 18x LVC Technical Specifications.
GPS preparation
I used a Windows Vista computer with Garmin’s SNSRXCFG_270.exe to upgrade the firmware from version 3.60 to the latest, 3.70. I also used SNSRXCFG_270.exe to set the PPS pulse width to 200 ms, and disable all NMEA sentences except GPGGA.
Debian GNU/Linux setup
March 2022 update: I’m now running my stratum 1 time server on a Shuttle DS10U running Debian 11 bullseye. The Garmin GPS 18x LVC is connected to the serial port. gpsd reads the NMEA sentences and pulse per second (PPS), and chrony provides NTP service. I basically followed this documentation and configured everything with puppet. This commit and the two parent commits are the implementation.
Old setups:
Here are the steps in summary (written for Debian wheezy in February 2015):
- Install pps-tools
- Create udev rules
- Create systemd service unit for ldattach
Some details:
When you do sudo ldattach PPS /dev/ttyS0
, the PPS modules will be loaded automatically and the device /dev/pps0
will be created. Place some udev rules in /etc/udev/rules.d/77-local.rules
to create device symlinks and run ldattach automatically:
SUBSYSTEM=="pps", MODE="0664" GROUP="dialout"
KERNEL=="ttyS2", RUN+="/bin/stty --file=/dev/ttyS2 9600"
KERNEL=="ttyS2" SYMLINK+="gps0"
KERNEL=="pps0" SYMLINK+="gpspps0"
systemd unit to run ldattach
(enable with systemctl enable ldattach@ttyS2
):
[Unit]
Description=PPS Line Discipline for GPS Timekeeping for %i
Before=ntpd.service
[Service]
ExecStart=/usr/sbin/ldattach pps /dev/%i
Type=forking
[Install]
WantedBy=multi-user.target
Here is my working ntp.conf:
rlimit memlock -1
driftfile /var/lib/ntp/ntp.drift
restrict localhost
restrict default limited noquery
server darwin.kenyonralph.com iburst
pool 2.pool.ntp.org iburst
server 127.127.20.0 mode 16 minpoll 3 iburst
fudge 127.127.20.0 flag1 1 flag2 0 flag3 1 time2 0.545
leapfile /etc/ntp/leap-seconds.list
Example output after letting ntpd run for about 8 hours:
kenyon@einstein ~ % ntpq -ccv -p -crv -ckern -csysinfo
associd=0 status=0000 no events, clk_unspec,
device="NMEA GPS Clock",
timecode="$GPGGA,172617,1111.3839,N,11111.4417,W,2,11,0.8,126.3,M,-35.5,M,,*71",
poll=3418, noreply=0, badformat=0, baddata=0, fudgetime2=545.000,
stratum=0, refid=GPS, flags=5
remote refid st t when poll reach delay offset jitter
==============================================================================
2.us.pool.ntp.o .POOL. 16 p - 64 0 0.000 0.000 0.000
oGPS_NMEA(0) .GPS. 0 l - 8 377 0.000 0.000 0.004
+darwin.kenyonra 127.67.113.92 2 u 50 64 377 28.478 4.232 0.953
-2001:470:c:4a1: 131.188.3.220 2 u 45 64 377 200.027 11.259 0.586
-2604:a880:800:1 184.105.182.7 3 u 39 64 377 82.776 4.196 1.805
-ntp.jtsage.com 216.218.254.202 2 u 24 64 377 69.561 -1.286 0.490
-2604:a880:800:1 184.105.182.7 3 u 46 64 377 82.421 3.827 0.603
*yurizoku.tk 209.51.161.238 2 u 59 64 377 80.916 1.554 0.619
+198.110.48.12 ( 204.9.54.119 2 u 35 64 377 100.437 1.796 1.330
-108.61.194.85 ( 201.198.247.252 2 u 53 64 377 45.230 -5.521 1.041
associd=0 status=041d leap_none, sync_uhf_radio, 1 event, kern,
version="ntpd 4.2.8p1@1.3265-o Mon Feb 9 09:59:02 UTC 2015 (1)",
processor="x86_64", system="Linux/3.16.7-ckt2", leap=00, stratum=1,
precision=-23, rootdelay=0.000, rootdisp=1.015, refid=GPS,
reftime=d884bdb9.bc1f69e3 Tue, Feb 10 2015 9:26:17.734,
clock=d884bdba.e1639551 Tue, Feb 10 2015 9:26:18.880, peer=27173, tc=3,
mintc=3, offset=-0.000130, frequency=-13.457, sys_jitter=0.004033,
clk_jitter=0.051, clk_wander=0.031, tai=35, leapsec=201507010000,
expire=201512280000
associd=0 status=041d leap_none, sync_uhf_radio, 1 event, kern,
pll offset: 0
pll frequency: -13.4565
maximum error: 0.0015
estimated error: 3.4e-05
kernel status: pll ppsfreq ppstime ppssignal nano
pll time constant: 3
precision: 1e-06
frequency tolerance: 500
pps frequency: -13.4874
pps stability: 0.280609
pps jitter: 0.050
calibration interval 256
calibration cycles: 152
jitter exceeded: 45
stability exceeded: 2
calibration errors: 0
associd=0 status=041d leap_none, sync_uhf_radio, 1 event, kern,
system peer: GPS_NMEA(0)
system peer mode: client
leap indicator: 00
stratum: 1
log2 precision: -23
root delay: 0.000
root dispersion: 1.015
reference ID: GPS
reference time: d884bdb9.bc1f69e3 Tue, Feb 10 2015 9:26:17.734
system jitter: 0.004033
clock jitter: 0.051
clock wander: 0.031
broadcast delay: 0.000
symm. auth. delay: 0.000
I also tried setting up a time server with FreeBSD to see if it’s any easier or better.
FreeBSD setup
This section was written in October 2011. I no longer run this FreeBSD system. I’m running the NTP server on the Linux machine described above.
I am using ntp-devel from ports.
/etc/rc.conf
ntpd_enable="YES"
ntpd_flags="-N -p /var/run/ntpd.pid -f /var/db/ntpd.drift"
ntpd_program="/usr/local/sbin/ntpd"
ntpd_sync_on_start="YES"
/etc/devfs.conf
link cuau0 gps0
/etc/ttys
Commented out ttyu*.
Kernel configuration
Put this in /usr/src/sys/amd64/conf/PPS-GENERIC
:
#
# PPS -- Generic kernel configuration file for FreeBSD/amd64 PPS
#
include GENERIC
ident PPS-GENERIC
options PPS_SYNC
/etc/make.conf
KERNCONF= PPS-GENERIC GENERIC
/etc/ntp.conf
server 127.127.20.0 minpoll 3
fudge 127.127.20.0 flag1 1 flag2 0 flag3 1 time2 0.600
server darwin.kenyonralph.com iburst
server voodoo.kenyonralph.com iburst
server grunt.kenyonralph.com iburst
pool 2.us.pool.ntp.org iburst
Example output
Example output after running for a few minutes and while doing buildworld and buildkernel:
kenyon@gauss ~ % ntpq -p -c clockvar -c readvar; ntpdc -c kerninfo
remote refid st t when poll reach delay offset jitter
==============================================================================
oGPS_NMEA(0) .GPS. 0 l 38 64 377 0.000 0.648 0.249
2.us.pool.ntp.o .POOL. 16 p - 64 0 0.000 0.000 0.002
*darwin.kenyonra 127.67.113.92 2 u 63 64 377 20.431 3.263 3.354
-voodoo.kenyonra 106.61.18.129 3 u 61 64 377 0.243 4.485 0.124
+grunt.kenyonral 106.61.18.129 3 u 64 64 377 0.182 3.521 0.225
-conquest.kjsl.c 69.36.224.15 2 u 62 64 377 26.374 5.768 2.522
-bindcat.fhsu.ed 128.138.140.44 2 u 63 64 377 75.219 4.115 1.846
-peachesgeldof.k 204.9.54.119 2 u 64 64 377 94.141 3.787 2.844
-ip-173-201-38-8 198.153.152.52 2 u 58 64 377 24.752 6.104 1.801
-druid.storyinme 130.207.244.240 2 u 56 64 377 65.657 -2.773 4.542
+cheezum.mattnor 129.7.1.66 2 u 65 64 377 45.797 2.972 2.286
associd=0 status=0000 no events, clk_unspec,
device="NMEA GPS Clock",
timecode="$GPGGA,085740,1111.1111,N,11111.1111,W,1,09,0.9,103.2,M,-35.3,M,,*77",
poll=14, noreply=0, badformat=0, baddata=0, fudgetime2=600.000,
stratum=0, refid=GPS, flags=5
associd=0 status=04ad leap_none, sync_uhf_radio, 10 events, kern,
version="ntpd 4.2.7p225@1.2483-o Fri Oct 21 04:59:50 UTC 2011 (1)",
processor="amd64", system="FreeBSD/9.0-RC1", leap=00, stratum=1,
precision=-19, rootdelay=0.000, rootdisp=2.380, refid=GPS,
reftime=d24d03df.edabdc26 Sat, Oct 22 2011 1:57:03.928,
clock=d24d0405.3f696dad Sat, Oct 22 2011 1:57:41.247, peer=38673, tc=6,
mintc=3, offset=0.648, frequency=-4.048, sys_jitter=0.249,
clk_jitter=0.000, clk_wander=0.005
pll offset: 0.000624065 s
pll frequency: -4.048 ppm
maximum error: 2.0824e-05 s
estimated error: 1.7e-08 s
status: 2007 pll ppsfreq ppstime nano
pll time constant: 6
precision: 1e-09 s
frequency tolerance: 496 ppm
kenyon@gauss ~ % ntpq -c "rv 38673"
associd=38673 status=973a conf, reach, sel_pps.peer, 3 events, sys_peer,
srcadr=GPS_NMEA(0), srcport=123, dstadr=127.0.0.1, dstport=123, leap=00,
stratum=0, precision=-20, rootdelay=0.000, rootdisp=0.000, refid=GPS,
reftime=d24d03de.ffd6d0b6 Sat, Oct 22 2011 1:57:02.999,
rec=d24d03df.edabdc26 Sat, Oct 22 2011 1:57:03.928, reach=377,
unreach=0, hmode=3, pmode=4, hpoll=6, ppoll=6, headway=0, flash=00 ok,
keyid=0, offset=0.648, delay=0.000, dispersion=0.928, jitter=0.249,
filtdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,
filtoffset= 0.65 0.70 0.76 0.80 0.86 0.91 0.99 1.06,
filtdisp= 0.00 0.96 1.92 2.88 3.84 4.80 5.76 6.72
Here is what kerninfo looks like with the PPS_SYNC kernel option:
pll offset: 0.000123674 s
pll frequency: -3.918 ppm
maximum error: 0.000111415 s
estimated error: 1.51e-07 s
status: 2107 pll ppsfreq ppstime ppssignal nano
pll time constant: 6
precision: 1e-09 s
frequency tolerance: 496 ppm
pps frequency: -3.918 ppm
pps stability: 0.033 ppm
pps jitter: 4.729e-06 s
calibration interval: 64 s
calibration cycles: 27
jitter exceeded: 5
stability exceeded: 0
calibration errors: 10
Conclusion
FreeBSD is much nicer than Linux (as of late 2011) at being a stratum 1 NTP server using a NMEA GPS with PPS reference clock.
As of early 2015, I would say it’s about an equal amount of effort between FreeBSD and Debian Linux. Debian Linux is a little more effort if you want to create a proper Debian package of NTP.
Notes
- Show serial port settings:
- Linux:
stty --all --file=/dev/ttyS0
- FreeBSD:
stty -a -f /dev/cuau0
- Linux:
- Set serial port baud rate to 4800:
- Linux:
stty --file=/dev/ttyS0 4800
- FreeBSD (but not really necessary since
cu
can do it, and doesn’t seem to take effect anyway):stty -f /dev/cuau0 4800
- Linux:
- Observe NMEA output:
- Linux:
cat /dev/ttyS0
- FreeBSD:
cu -l /dev/cuau0 -s 4800
- Linux:
References
- NTP documentation: Reference Clock Support
- NTP support wiki: Configuring Garmin Refclocks
- NTP support wiki: Configuring NMEA Refclocks
- NTP support wiki: Garmin Refclock Users
- Adding a FreeBSD NTP server based on an GPS 18 LVC device by David Taylor
- Enabling ntpd PPS support for Debian Lenny Linux by World Time Solutions
- Garmin GPS 18x OEM
- NTP server using PC gnu/linux and freebsd by Steven Bjork
- Stratum 1 NTP, Garmin GPS 18 LVC on FreeBSD 8.0 by Ryan Doyle
- Synchronising to a Garmin GPS 18 LVC by R.J. van der Putten
- Synchronizing an NTP server to GPS/PPS by Pela-Suros
- Synchronizing ntpd to a Garmin GPS 18 LVC via gpsd by Jaap Winius