Includes building the source to reconfigure the use of the Pi serial port, besides building a time server with the pi.
One of the things I’ve always loved to tinker with is time sources
and synchronization. Typically this has been tied to sensible things
like the Network Time Protocol and designing and maintaining time
distribution systems for broadcast networks. Lately though I’ve been
toying with ‘real’ time sources – GPS and MSF broadcasts. This is a
quick tutorial on how to set up a Raspberry Pi, which at only a few
watts makes for an economical time server, to talk to a Venus 638FLPx
GPS receiver (available from Sparkfun on a suitable breakout board
here).
It’s worth noting that the GPS chip in question is not really ideal
for timing applications. There are nicer boards for this purpose. But
the great thing is that because most of these things are pretty
standard, almost all this guide will still work. Venus do make an IC,
the 638LPx-T, which is a variant of this chip designed for timing with
an accuracy of plus/minus 30us, but I can’t find that on a breakout
board and I had the FLPx board spare from a UAS platform.
The basic requirements for a GPS time source/server are:
- GPS module which outputs NMEA strings on serial and has a 1PPS output
- Computer with GPI pin for 1PPS and serial connectivity running Linux
- GPS antenna in a place that can see a good chunk of sky with a feed back to the module
The pinout on the Venus board has serial RX/TX, +3.3V, GND and PPS.
For my timeserver I’m using the Raspberry Pi – the GPI to use is pin 23
on the headers, RX goes to TX and TX goes to RX also on the header, and
the header power for 3V3 works perfectly for powering the module. I used
an Adafruit Pi Cobbler to breadboard this, but will use a Ciseco Slice
of Pi board to make something a bit more solid later. It’s pretty simple
to hook up, in any case.
The tricky bit comes with the software. Let’s talk about how GPS time sourcing works.
The GPS satellites rely on highly accurate timing, and broadcast UTC
time information as part of their signals. The difference in the
timestamps is what, largely, allows your receiver to figure out where
you are. If you know where you are, though, you can use those timestamps
to come up with a very accurate fix for time.
The NMEA strings that come off the GPS describe all sorts of things. They look a bit like this:
pi@ntpi ~ $ cat /dev/gps0
$GPGGA,201705.000,5644.4848,N,00142.3529,W,1,10,0.9,64.4,M,49.0,M,,0000*75
$GPGLL,5644.4848,N,00142.3529,W,201705.000,A,A*43
$GPGSA,A,3,07,26,08,19,15,05,09,21,18,24,,,1.5,0.9,1.2*3B
$GPZDA,201705.000,29,12,2012,00,00*5E
They’re a bit obscure but they have the time encoded into them along
with things like how many satellites are used in the solution,
velocity/heading, and so on. The first chunk, $GPGGA for instance, is a
preamble for each string – the last three characters are used to refer
to a string of that type, eg GGA.
Sadly this won’t get us a very accurate reference. Let’s look at what the PPS looks like, for starters, with an oscilloscope:
We
can see quite clearly these are small, 5ms pulses exactly once a
second. Great! So how about those NMEA strings being sent over serial?
We can see in the top half of this trace a full NMEA transmission
occurring just after (1.18ms) the PPS pulse – but the NMEA transmission
is huge! And if we follow it, we see it move around, too.
This
is a zoomed in view of a PPS pulse against the NMEA transmission (1.6ms
after the pulse, this time – plenty of variance). This was taken with
the GPS in ‘send NMEA in sync with PPS’ mode!
So if we want to get our device accurate we need to use PPS. But if we just use PPS, we don’t know
which second we’re in! So we have to use both. Fortunately, ntpd will manage this for us.
Diving back to the software, then – we need a kernel that can deal with the PPS pulses on our GPI port. Let’s install one:
git clone https://github.com/davidk/adafruit-raspberrypi-linux-pps.git
cd adafruit-raspberrypi-linux-pps/
sudo mv /boot/kernel.img /boot/kernel.img.orig
sudo cp kernel.img /boot
sudo mv modules/* /lib/modules
sudo sh -c "echo 'pps-gpio' >> /etc/modules"
We need to disable the default usage of the Pi’s serial, which is to
provide a tty. Open /boot/cmdline.txt and remove all elements referring
to ttyAMA0. Mine looks like this:
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
Next, open /etc/inittab and comment this line out:
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
We also want to configure udev to put our GPS module and PPS where ntpd will expect it to be:
sudo nano /etc/udev/rules.d/80-gps-to-ntp.rules
# Change MODE of ttyAMA0 so it is readable by NTP and provide a symlink to
# /dev/gps0
KERNEL=="ttyAMA0", SUBSYSTEM=="tty", DRIVER=="", SYMLINK+="gps0", MODE="0666"
# Symlink /dev/pps0 to /dev/gpspps0
KERNEL=="pps0", SUBSYSTEM=="pps", DRIVER=="", SYMLINK+="gpspps0", MODE="0666"
Next we’re going to install then remove ntpd – we’ll be back with an updated version.
sudo apt-get install ntp
sudo apt-get remove ntp
sudo apt-get install libcap-dev
Time to install and compile a PPS-enabled ntpd.
wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-dev/ntp-dev-4.2.7p340.tar.gz
tar zxf ntp-dev-4.2.7p340.tar.gz
cd ntp-dev-4.2.7p340/
./configure --prefix=/usr --enable-all-clocks --enable-parse-clocks --enable-SHM --enable-debugging --sysconfdir=/var/lib/ntp --with-sntp=no --with-lineeditlibs=edit --without-ntpsnmpd --disable-local-libopts --disable-dependency-tracking --enable-ipv6 && make && sudo make install
Now we’ve got all this we need to reboot to load the new kernel:
sudo reboot
First, we can verify that we can see the NMEA sentences:
cat /dev/gps0
You should see NMEA strings! Ctrl+C to kill it. Next, let’s test the PPS.
pi@ntpi ~ $ dmesg | grep pps
[ 0.000000] Linux version 3.1.9adafruit-pps+ (davidk@bender) (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #21 PREEMPT Sun Sep 2 10:57:58 PDT 2012
[ 1.148909] usb usb1: Manufacturer: Linux 3.1.9adafruit-pps+ dwc_otg_hcd
[ 14.707851] pps_core: LinuxPPS API ver. 1 registered
[ 14.712724] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[ 14.728437] pps pps0: new PPS source pps-gpio.-1
[ 14.731832] pps pps0: Registered IRQ 108 as PPS source
Looks like we’re good to go. Check the pps-utils package if you need to verify more completely.
Next up: telling ntp about this shiny new local clock!
We need to do one thing first – if your local network has a DHCP
server that announces time servers, you want to use your local NTP
config despite this. You need to edit /etc/init.d/ntp and comment the
following lines out:
#if [ -e /var/lib/ntp/ntp.conf.dhcp ]; then
# NTPD_OPTS="$NTPD_OPTS -c /var/lib/ntp/ntp.conf.dhcp"
#fi
Open up /etc/ntp.conf and adjust it to taste. The following stanza should get you up and running:
server 127.127.20.0 mode 24 minpoll 3 maxpoll 4 iburst true prefer
fudge 127.127.20.0 flag1 1 flag2 0 flag3 1 flag4 1 time2 0.093
The settings here are documented
here
and can be configured to suit your module. The time2 value may need to
be larger (0.4 or so), and you may want to keep or remove the existing
servers configured in ntp.conf – or just reconfigure them to be
geographically local. You can get an idea of the time2 offset by adding
128 to whatever mode value you’re using, then looking at the timings in
/var/log/ntpstats/clockstats after a ntpd restart. Pick the time offset
for the first NMEA string seen and there’s your number.
Restart ntpd, wait a few minutes for it all to settle down, and verify things are working:
pi@ntpi ~ $ ntptime
ntp_gettime() returns code 0 (OK)
time d489d596.2b0cfe14 Sat, Dec 29 2012 20:48:22.168, (.168167784),
maximum error 2000 us, estimated error 1 us, TAI offset 0
ntp_adjtime() returns code 0 (OK)
modes 0x0 (),
offset 2.518 us, frequency 79.473 ppm, interval 1 s,
maximum error 2000 us, estimated error 1 us,
status 0x2007 (PLL,PPSFREQ,PPSTIME,NANO),
time constant 3, precision 0.001 us, tolerance 500 ppm,
pi@ntpi ~ $ ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
oGPS_NMEA(0) .GPS. 0 l 8 8 377 0.000 0.002 0.004
+ns0.luns.net.uk 157.44.176.4 2 u 63 64 17 44.161 3.389 0.709
-ntp.oceanmediag 192.93.2.20 2 u 5 64 37 34.806 2.048 0.585
+dns1.rmplc.co.u 193.62.22.74 2 u 63 64 17 36.375 2.488 0.282
*mail1.itdojo.or 10.10.120.2 2 u 56 64 17 51.280 3.977 0.270
pi@ntpi ~ $ ntptrace
localhost: stratum 1, offset 0.000001, synch distance 0.001090, refid 'GPS'
Note the o before GPS_NMEA(0) in ntpq -p – this indicates the PPS is
being used. If you don’t have the PPS enabled your time will kinda suck,
as we saw a bit with the scope traces. To illustrate this, here’s a
comparison of system performance between NMEA-only and PPS.
You really, really want to make sure your system is using the PPS, is what I’m getting at.
Another thing I ran into was the GPS reporting the wrong time or not
reporting its timing offset – just ensure you have enabled the GPZDA
statement to avoid this. Symptoms of this can include huge offsets and
being marked as a falseticker (x) in peer lists of other peers. Just
make sure your module is configured properly. This is what I did to
configure my module:
- Enabled position pinning
- Disabled all NMEA strings except for GGA, GSA, GLL and ZDA
- Set update rate to 1Hz and output sync to UTC
- Set baud rate to 9600 (higher is not better!)
This seems to work quite well, and I’m very happy with the
performance given the low cost of the solution – it just needs popping
in a box and it’ll be a nice stable time reference for my home network.
I’m still interested in sourcing time from MSF with a receiver but we
have quite poor reception of the MSF signal here, so we’ll see if that’s
even feasible with the Pi. Given how important it is to maintain
accurate time in broadcast networks, especially with external programme
sources, this is a really nice thing to have gotten working and
documented. Kudos to the guys over at
this thread on the Raspberry Pi forums who did a lot of the legwork figuring this all out!