This page documents my project to build a stratum 1 NTP time server at my house. Its hostname is gauss.kenyonralph.com and it is part of the NTP pool.
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
I first tried using the GPS with an old Pentium III computer running Ubuntu 11.04 natty running kernel 2.6.38-11-generic-pae. I could see the NMEA sentences with gpsd and gpspipe -r, but a few seconds after I did sudo ldattach PPS /dev/ttyS0 the system would lock up hard due to some kernel bug in some driver for my hardware. I saw BUG: scheduling while atomic in the syslog, but I wasn't able to capture the whole bug output. The same bug would occur if I just did cat /dev/ttyS0. So I just gave up with that old hardware and moved the GPS to my newer desktop computer running Debian squeeze and kernel 2.6.39-bpo.2-amd64. No lockups on this machine.
You need to have /usr/include/timepps.h to compile ntpd with proper PPS support. This header does not exist in Debian yet, so I used the one from Alexander Gordeev's git repository.
The ntpd in Debian squeeze does not have debugging enabled, which makes it difficult to see what is going on with your local reference clock. I rebuilt the squeeze package with debugging enabled, but I still wasn't getting very good results. It would not select the GPS as system peer or PPS peer, and jitter was high, in the tens of milliseconds. So I compiled my own ntp version 4.2.6p4 straight from the tarball, starting with this configure line: ./configure --prefix=/usr --sysconfdir=/var/lib/ntp The output below is from this build of ntp, not the Debian package, which was version 4.2.6p2.
When you do sudo ldattach PPS /dev/ttyS0, the PPS modules will be loaded automatically and the device /dev/pps0 will be created. I have some udev rules to create device symlinks and run ldattach:
KERNEL=="ttyS0" SYMLINK+="gps0"
KERNEL=="ttyS0", RUN+="/usr/sbin/ldattach pps /dev/%k"
KERNEL=="pps0" SYMLINK+="gpspps0"
Here is my working Debian squeeze ntp.conf:
driftfile /var/lib/ntp/ntp.drift
server 127.127.20.0
fudge 127.127.20.0 flag1 1 flag2 0 flag3 1 time2 0.600
server voodoo.kenyonralph.com iburst
server darwin.kenyonralph.com iburst
pool 2.us.pool.ntp.org iburst
Example output after letting ntpd run for about 8 hours:
ntpq -p -c clockvar; ntptime; ntpdc -c kerninfo
remote refid st t when poll reach delay offset jitter
==============================================================================
oGPS_NMEA(0) .GPS. 0 l 3 8 377 0.000 0.001 0.001
*voodoo.kenyonra 72.29.161.5 2 u 46 64 377 0.243 2.454 0.146
+darwin.kenyonra 127.67.113.92 2 u 14 64 377 20.122 2.467 3.413
associd=0 status=0012 , 1 event, clk_bad_format,
device="NMEA GPS Clock",
timecode="$GPRMC,212627,A,1111.1111,N,11111.1111,W,000.0,171.9,011011,012.1,E*61",
poll=3876, noreply=0, badformat=1, baddata=0, fudgetime1=0.000,
stratum=0, refid=GPS, flags=5
ntp_gettime() returns code 0 (OK)
time d2320403.a6e5ab84 Sat, Oct 1 2011 14:26:27.651, (.651942165),
maximum error 1617 us, estimated error 0 us
ntp_adjtime() returns code 0 (OK)
modes 0x0 (),
offset 0.530 us, frequency 4.205 ppm, interval 1 s,
maximum error 1617 us, estimated error 0 us,
status 0x2007 (PLL,PPSFREQ,PPSTIME,NANO),
time constant 3, precision 0.001 us, tolerance 500 ppm,
pll offset: 5.3e-07 s
pll frequency: 4.205 ppm
maximum error: 0.001617 s
estimated error: 0 s
status: 2007 pll ppsfreq ppstime nano
pll time constant: 3
precision: 1e-09 s
frequency tolerance: 500 ppm
I'm going to try setting up a time server with FreeBSD next to see if it's any easier or better.
FreeBSD setup
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.
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
cucan 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
- LinuxPPS installation and ntpd support
- NTP server using PC gnu/linux and freebsd by Steven Bjork
- NTP on Gentoo Wiki
- 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
- Ubuntu bug 805661
- Using a Garmin GPS 18 LVC as NTP stratum-0 on Linux 2.6 by Philip M. White