Jeff Epler's blog2019-06-06T12:19:03ZPhotos, electronics, cnc, and moreJeff Eplerjepler@unpythonic.netYet another Raspberry Pi Stratum 1 NTP server2019-06-06T12:19:03Z2019-06-06T12:19:03Zhttps://emergent.unpythonic.net/01559823543
There are lots of instructions for setting up a Stratum 1 NTP server
on your Raspberry Pi. A lot. After much research, I found a simple
configuration that uses a single ntp reference clock and does not involve
gpsd, but uses both NMEA and PPS for the most accurate timekeeping possible.
NMEA+PPS is a mode of NTP's "<a href="http://doc.ntp.org/4.2.8p10/drivers/driver20.html">Generic NMEA GPS Receiver</a>".
<p>Here's what worked for me:
<p>Start with a pi3 and raspbian stretch. Add a GPS with TTL-level PPS and 9600
baud NMEA outputs.
<p>Hook up you GPS: GND and +3.3V or +5V (according to the specifiics of your GPS)
to any matching pin, the GPS's TX output to the Pi's RX input on pin 10
of the header, and the GPS's PPS output to the Pi's pin 12. (You can hook
up your GPS's RX input to the Pi's TX output on pin 8 if you like, but I don't
think this is necessary)
<p>Using raspi-config, disable the serial console and enable the serial port
hardware.
<p>Manually edit /boot/config.txt to add: <tt>dtoverlay=pps-gpio</tt> —
If you have to (or prefer to) use a different pin for pps, you can apparently
specify it as <tt>dtoverlay=pps-gpio,gpiopin=##</tt>, where ## is the
internal numbering of the GPIO pins, not the number on the 40-pin header.
<p>Manually edit /etc/modules and add a line that reads <tt>pps-gpio</tt> — According to some sources, this step is not necessary.
<p>Install ntpd with apt-get.
<p>Delete all the content in /etc/dhcp/dhclient-exit-hooks.d/ntp, leaving an
empty file.
<p>Remove the file /run/ntp.conf.dhcp if it exists.
<p>Edit /etc/udev/rules.d/99-com.rules. On each of the two lines that ends
<tt>, SYMLINK+="serial%c"</tt>, append <tt>, SYMLINK+="gps%c"</tt>
<p>Create /etc/udev/rules.d/99-ppsd.rules with the content <tt>SUBSYSTEM=="pps", GROUP="dialout", MODE="0660", SYMLINK+="gpspps%n"</tt>
<p>In /etc/ntp.conf, add a stanza to access the GPS:
<pre>
# gps clock via serial /dev/gps0 and /dev/gpspps0
server 127.127.20.0 minpoll 3 maxpoll 3 mode 16 burst iburst prefer
fudge 127.127.20.0 refid GPS time2 +.250 flag1 1
</pre>
(leave the "pool" line(s) or other ntp server lines; if your pi doesn't
have a battery-backed RTC, you need a way to get the correct time initially,
before a GPS fix may be available!)
<p>"time2" doesn't seem critical with this setup, because the PPS time is
preferred over the serial reception time. "minpoll", "maxpoll", "burst"
and "iburst" may be superstitious and unnecessary.
<p>Reboot now and have a look at `ntpq -c peers`. You should see something
like this:
<pre>
remote refid st t when poll reach delay offset jitter
==============================================================================
oGPS_NMEA(0) .GPS. 0 l 1 8 377 0.000 -0.001 0.003
+ntp.u .GPS. 1 u 2 8 377 1.104 0.013 0.064
</pre>
<p>"GPS_NMEA" is selected as peer and is using PPS (this is what "o" in the
first column means). delay, offset, and jitter should all be extremely small
(Here, .001 is 1 microsecond). "ntp.u" is my other local stratum-1 NTP server.
The "+" indicates it's in good agreement with the local GPS. If you use pool,
you will see multiple lines here; some may have "+" and some may have "-".
<p>If something's not working, you will get, " " (blank), "-" or "x" next to
GPS_NMEA. If it's got "*" then NMEA is working but PPS isn't. Now you get to do things like debug whether the PPS signal is working properly according to ppstest, whether NMEA messages are actually coming in
at 9600 baud, etc. Or you can follow one of those other guides. :wink:
<p>Here's how the local time to GPS offset has looked over the last
10 hours or so — I find it awesome that my computer appears to be
synchronized to GPS to within ±5 microseconds almost all of the time:
<p><img src="https://media.unpythonic.net/emergent-files/01559823543/ntp-offset.png">
<p>Precision vs Accuracy: A Clock2019-06-03T01:17:09Z2019-06-03T01:17:09Zhttps://emergent.unpythonic.net/01559524629
<div style="float:right;clear:right"><!-- IMG_20190602_205835.jpg--><div class=albumouter style=width:306px id=><div class=albumimage style="width:306px;margin-left:0.0px;"><a href="https://media.unpythonic.net/emergent-files/01559524629/IMG_20190602_205835-medium.jpg" class="thickbox" rel="album" title="The clock, in all its glory"><img src="https://media.unpythonic.net/emergent-files/01559524629/IMG_20190602_205835-small.jpg" width=300 height=198></a><div ><div style="float: right" ><a href="https://media.unpythonic.net/emergent-files/01559524629/IMG_20190602_205835-medium.jpg">(M)</a><a href="https://media.unpythonic.net/emergent-files/01559524629/IMG_20190602_205835.jpg">(L)</a></div><a href="https://media.unpythonic.net/emergent-files/01559524629/IMG_20190602_205835.jpg">The clock, in all its glory</a></div></div></div>
</div>
<p>I was inspired by <a href="https://hop-picker.tumblr.com/post/51065757240">this watch face design</a> (I think that's the original version) and by the arrival
of a "OCXO", a very reliable time keeping circuit, to finally make an
electronic clock.
<p>Accuracy: Between hardware and software tuning, the OCXO keeps time with
an accuracy of possibly better than 100 microseconds per day (loses or gains
well less than a half second per year)
(Yes, I'm deliberately ignoring a lot about crystal aging here!)
<p>Precision: The time displayed is to the nearest minute, and the touchscreen
setting mechanism is (deliberately?) poor, making it hard to set the time
closer than +- 2 minutes or so. Oh, and it takes a good fraction of a second
to update the screen anytime it changes. (The best way to set it seems to be
to wait until a few seconds before 6AM/6PM and plug it in, since it boots with
that time showing)
<p><div style="float:right;clear:right"><!-- timelog.png--><div class=albumouter style=width:306px id=><div class=albumimage style="width:306px;margin-left:0.0px;"><a href="https://media.unpythonic.net/emergent-files/01559524629/timelog-medium.jpg" class="thickbox" rel="album" title="Estimated accuracy of the OCXO vs GPS: 60 microseconds per day?"><img src="https://media.unpythonic.net/emergent-files/01559524629/timelog-small.jpg" width=300 height=187></a><div ><div style="float: right" ><a href="https://media.unpythonic.net/emergent-files/01559524629/timelog-medium.jpg">(M)</a><a href="https://media.unpythonic.net/emergent-files/01559524629/timelog.png">(L)</a></div><a href="https://media.unpythonic.net/emergent-files/01559524629/timelog.png">Estimated accuracy of the OCXO vs GPS: 60 microseconds per day?</a></div></div></div>
</div>
The clock consists of:
<ul>
<li> A <a href="https://www.adafruit.com/product/2390">PyBoard</a> running micropython
<li> A <a href="https://www.adafruit.com/product/1591">touchscreen LCD</a> and controller
<li> A <a href="https://www.tindie.com/products/deBug67/10mhz-frequency-reference-ocxo-t/">10MHz OCXO frequency reference</a>
</ul>
.. all in a 3d printed enclosure that's not quite the right size.
<p><div style="float:right;clear:right"><!-- clock.gif--><div class=albumouter style=width:306px id=><div class=albumimage style="width:278px;margin-left:14.0px;"><img src="https://media.unpythonic.net/emergent-files/01559524629/clock.gif"><br><span>The dial as an animation (1 revolution = 12 hours)</span></div></div>
</div>
Along the way, I added an even more accurate time source to a Raspberry
PI (GPS with PPS) so that I could even measure the accuracy of the OCXO, and
discovered I even have a GPS module which was negatively affected by the GPS
rollover that occurred in April of this year (the second 1024-week rollover).
This leads to a surprising sequence of clock arithmetic, and finally gpsd
decides the GPS is returning a date sometime back in 1963.
SNTP from Python: getting server's esimate of time quality2013-11-23T18:19:15Z2013-11-23T18:19:15Zhttps://emergent.unpythonic.net/01385230755
<p>Anders Wallin <a href="http://www.anderswallin.net/2013/11/ntp-failure-detection/">recently discussed</a>
generating graphs of server time error using a timestamp from the Date:
header of an http request.
<p>NTP servers themselves have estimates of time error, and it's possible to
get the local server's idea of the error by sending SNTP packets to the
local machine. You can also see the last time this NTP server successfully
communicated with a peer or upstream, and see whether the server considers
itself desynchronized by checking the stratum and leap second fields.
<p>Based on my tinkering, the root_dispersion field or the derived LAMBDA value is
the easiest to look at for estimating how bad the NTP time is. Between
communications with a peer, root_dispersion and LAMBA increase at a rate of
15µs/s, ntp's arbitrary estimate of the accuracy of the local undisciplined
clock. (YMMV and you can appaerntly tweak CLOCK_PHI in your NTP configuration
file)
<p>The attached program is a basic Python SNTP client which by default polls the local server every 5 seconds and prints some information. License: GPLv2+
<p>Typical output:
<pre >
12:25:58 0.06233 228.95875 ST-SYNCH LI-SYNCH
12:26:03 0.06241 233.96404 ST-SYNCH LI-SYNCH
12:26:08 0.06247 238.96745 ST-SYNCH LI-SYNCH
12:26:13 0.06255 243.97141 ST-SYNCH LI-SYNCH
12:26:18 0.06262 248.97673 ST-SYNCH LI-SYNCH
12:26:23 0.06270 253.98213 ST-SYNCH LI-SYNCH
12:26:28 0.06277 258.98746 ST-SYNCH LI-SYNCH
</pre>
<p><b>Files currently attached to this page:</b>
<table cellpadding=5 style="width:auto!important; clear:none!important"><col><col style="text-align: right"><tr bgcolor=#eeeeee><td><a href="https://media.unpythonic.net/emergent-files/01385230755/ntpsynch.py">ntpsynch.py</a></td><td>4.9kB</td></tr></table><p>