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
  • 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
  • Observe NMEA output:
    • Linux: cat < /dev/ttyS0
    • FreeBSD: cu -l /dev/cuau0 -s 4800

References