Beaglebone Black NTP/GPS server

Goals

I wanted to experiment with using temperature measurement to augment a NTP stratum 1 server. I started with a very similiar setup to this one. [Edit: the8thlayerof.net is gone, link changed to archive.org]. This will cover just getting NTP+PPS working.

Hardware

GPS receiver: Adafruit Ultimate
Pins:

  • GPS PPS -> BBB P8.7
  • GPS TX -> BBB P9.11 (UART4)
  • GPS RX -> BBB P9.13 (UART4)
  • GPS Vin -> BBB +3.3V
  • GPS GND -> BBB GND

DTS overlay file for GPS (slightly modified to fix the 0-based vs 1-based resource reservations)

Temperature Sensor: DS18B20
Pins:

  • DQ -> BBB P8.11 (including a pullup resistor to +3.3V)
  • Vdd -> BBB +3.3V
  • GND -> BBB GND

DTS overlay file for Temp

Device Tree/pinmux

These instructions are dependant upon what kernel and distribution you are running. This is for the 3.8.13-bone* kernel and Debian.

First thing that needs to happen is to tell the pinmux system about the pins needed. Take the DTS overlay files above and compile them to .dtbo files:

dtc -@ -I dts -O dtb -o DD-TEMP-00A0.dtbo DD-TEMP-00A0.dts
dtc -@ -I dts -O dtb -o DD-GPS-00A0.dtbo DD-GPS-00A0.dts

Put those .dtbo files in /lib/firmware. Next, tell capemgr about these two shields:

echo DD-TEMP >/sys/devices/bone_capemgr.9/slots
echo DD-GPS >/sys/devices/bone_capemgr.9/slots

dmesg should have added a bunch of new lines, the main ones you're looking for are:

[  341.544360] bone-capemgr bone_capemgr.9: slot #7: dtbo 'DD-GPS-00A0.dtbo' loaded; converting to live tree
[  341.555818] 481a8000.serial: ttyO4 at MMIO 0x481a8000 (irq = 45) is a OMAP UART4
[  341.563682] pps pps0: new PPS source pps.15.-1
[  345.295338] bone-capemgr bone_capemgr.9: slot #8: dtbo 'DD-TEMP-00A0.dtbo' loaded; converting to live tree

The DD-TEMP cape will complain that of_get_named_gpio_flags "can't parse gpios property", but this doesn't seem to be a problem. ttyO4 is the new serial device, and pps0 is the new pps device.

To make your changes permanent, edit the file /etc/default/capemgr and change CAPE= to CAPE=DD-TEMP,DD-GPS.

GPSd

I installed gpsd and modified /etc/systemd/system/gpsd.service to contain:

[Unit]
Description=GPS (Global Positioning System) Daemon
Requires=gpsd.socket

[Service]
ExecStart=/usr/sbin/gpsd -n -N /dev/ttyO4

[Install]
Also=gpsd.socket

The -n flag tells gpsd to run without a client connected.

Chrony

I then installed chrony from source (to make sure the pps driver was enabled). To configure it, I put the following in /etc/chrony/chrony.conf:

# change the clock slower for less jitter
minsamples 10
# get NMEA data from gpsd over shared memory
refclock SHM 0 offset 0.395 delay 0.2 refid NMEA noselect
# get PPS data from pps-gpio, summarize every 16 seconds, prefer it
refclock PPS /dev/pps0 refid PPS poll 4 prefer
keyfile /etc/chrony/chrony.keys
commandkey 1
driftfile /var/lib/chrony/chrony.drift
log tracking measurements statistics
logdir /var/log/chrony
maxupdateskew 100.0
dumponexit
dumpdir /var/lib/chrony
# if local stratum is configured and there's a local clock issue, the PPS changes stratum to match this stratum
#local stratum 10
allow
logchange 0.5
rtconutc
# change this to the proper country code
pool us.pool.ntp.org iburst offline

Enabling the overlays via the capemgr and starting gpsd+chrony gives this result after running for a few hours:

# chronyc sources
210 Number of sources = 2
MS Name/IP address Stratum Poll Reach LastRx Last sample
=====================================================================
#? NMEA                0   4   377    12    +41ms[  +41ms] +/-  108ms
#* PPS                 0   4   377    12   -159ns[ -181ns] +/-  997ns

The PPS jitter is nice and low (just under 1us) and has a 377 reach history, which is good. The sample was last updated 12 seconds ago.

Some things to try: "nohz=off" kernel command line in /boot/uboot/uEnv.txt at the end of mmcargs. This should work around possible jitter introduced CONFIG_NO_HZ.

For the temperature compensation, see Part 2

For pictures, see cape construction