In this tutorial I will show how to set up a Raspberry Pi with a DCF77 receiver as an NTP server. Since the external radio clock via DCF77 is a stratum 0 source, the NTP server itself is stratum 1. I am showing how to connect the DCF77 module and I am listing all relevant commands as a step by step guide to install the NTP things. With this tutorial you will be able to operate your own stratum 1 NTP server. Nice DIY project. ;) However, keep in mind that you should only use it on a private playground and not on an enterprise network that should consist of high reliable NTP servers rather than DIY Raspberry Pis. Anyway, let’s go:
At the time of writing (Nov 2018) I am using a Raspberry Pi 1 B (yes, the old one), kernel 4.14.71+ and Raspbian GNU/Linux 9 (stretch). I installed a few relevant packages and gave it a static IPv6 address. Legacy IP (IPv4) is not used at all, only IPv6.
1) Connecting the DCF77 Module
At first you need a DCF77 module in order to receive the radio clock from Germany. I used the “Conrad” module. It needs a pull up resistor with 3-10 kOhm between ports 2 and 4 (VCC and DCF-). Have a look at the PDFs from the Conrad store that show the port assignment, as well as this two german pages that give several hints. Long story short: use three cables:
- DCF77 module port 1 (GND): Pi pin 9, GND
- DCF77 module port 2 (VCC): Pi pin 1, +3.3 V
DCF77 module port 3 (DCF+): not used- DCF77 module port 4 (DCF-): Pi pin 10, RXD0 (UART)
I first soldered the cables directly to the Pi, while I later added a 3,5 mm jack to the housing of the Pi with a 2 meter cable on the DCF77 module:
2) Installing NTP w/ DCF77 Support
The first step is to install NTP with the support of DCF77. This is not the case if you’re simply doing a
sudo apt-get install ntp. Anyway, please install the NTP server with this command first in order to have the startup scripts in place, etc. After that you need to build NTP from source (note the “–enable-RAWDCF” option) which involves the following steps:
sudo nano /etc/apt/sources.list # uncomment the line with "deb-src" deb-src http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi sudo apt-get update sudo apt-get -y build-dep ntp # download the latest ntp: http://ntp.org/downloads.html # in this example it was 4.2.8p12 wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.8p12.tar.gz cd ntp-4.2.8p12/ # note: the following step takes some time: ./configure --enable-RAWDCF --prefix=/usr # note: the following "make" takes even longer, especially on an old Pi 1 B make sudo service ntp stop sudo make install # to disable updating ntp via apt-get, echo the following: echo "ntp hold" | sudo dpkg --set-selections sudo service ntp start
(Fun fact: Since I am running my NTP server via IPv6-only meanwhile the NTP download page is IPv4-only, I had to download and copy the ntp package from another machine to the Raspberry Pi. Sigh. Yes, I know, DNS64/NAT64 would solve the problem.)
You have just installed the latest version of NTP, while “holding” the ntp package within dpkg to not overriding/downgrading it with future “apt-get update”s. You can verify this with the following: (Note the “h” in the very first column which indicates that the ntp package is on hold.)
pi@ntp1-dcf77:~ $ dpkg -l | grep ntp hi ntp 1:4.2.8p10+dfsg-3+deb9u2 armhf Network Time Protocol daemon and utility programs
Similarly you can verify the running ntp version with:
pi@ntp1-dcf77:~ $ ntpq --version ntpq 4.2.8p12@1.3728-o Thu Nov 8 11:51:22 UTC 2018 (1)
3) Leveraging the Serial Port
At first you must disable the serial port of the Pi while keeping the port hardware enabled. Open
sudo raspi-config, navigate to “5 Interface Options” -> “P6 Serial” and:
- Would you like a login shell to be accessible over serial? –> No
- Would you like the serial port hardware to be enabled? –> Yes
followed by a reboot when exiting raspi-config.
A very basic test with “screen” should indicate some output, though completely useless:
pi@ntp1-dcf77:~ $ sudo screen /dev/ttyAMA0 9600 ���� Ctrl+a \ y
Add the user “ntp” (which runs the ntp daemon) to the “tty” group in order to have access to the tty console. Verify it with the second command shown below:
pi@ntp1-dcf77:~ $ sudo adduser ntp tty Adding user `ntp' to group `tty' ... Adding user ntp to group tty Done. pi@ntp1-dcf77:~ $ cat /etc/group | grep tty tty:x:5:ntp
In order to have a symbolic link to /dev/refclock-0 (which is needed by NTP later), add the following three lines to the init script from NTP, while commenting out the latter three lines
sudo nano /etc/init.d/ntp:
if [ ! -L /dev/refclock-0 ]; then ln -s /dev/ttyAMA0 /dev/refclock-0 fi #if [ -e /var/lib/ntp/ntp.conf.dhcp ]; then # NTPD_OPTS="$NTPD_OPTS -c /var/lib/ntp/ntp.conf.dhcp" #fi
Reload the systemctl and restart the NTP process:
sudo systemctl daemon-reload sudo service ntp restart
Verify that the symbolic link is present:
pi@ntp1-dcf77:/etc/init.d $ ls /dev/ref* /dev/refclock-0
Now you need to use driver number 8 from the NTP software in “mode 5” for the Conrad DCF77 module. That is: Open the ntp configuration file:
sudo nano /etc/ntp.confand add your “server” aka DCF77 module in the following way:
server 127.127.8.0 mode 5 prefer
I have commented out the default “pool 0.debian.pool.ntp.org iburst” lines but entered a few IPv6 enabled servers as well, though without the “prefer” option:
#http://support.ntp.org/bin/view/Servers/PublicTimeServer000388 server ntp.probe-networks.de #http://support.ntp.org/bin/view/Servers/PublicTimeServer001352 server time.hueske-edv.de #http://support.ntp.org/bin/view/Servers/PublicTimeServer000840 server ntp2.301-moved.de #http://support.ntp.org/bin/view/Servers/PublicTimeServer001363 server ntp.fanlin.de
Finally, restart the NTP daemon:
sudo service ntp restart.
Now, in theory, the DCF77 receiver should work, but only if you have a good radio signal and quality and correct angle of the antenna, which was not the case in my scenario at the first time. Use “ntpq -p” to print a list of NTP servers that are in use. As you can see in this example, the “GENERIC(0) .DCFa.” server (first line) was never reached so far:
pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== GENERIC(0) .DCFa. 0 l - 64 0 0.000 0.000 0.000 ptbtime2.ptb.de .PTB. 1 u 57 64 377 9.207 4.270 4.483 *ns2.probe-netwo 192.53.103.108 2 u 2 64 377 2.553 3.727 4.777 -2a01:4f8:201:41 131.188.3.221 2 u 57 64 377 16.104 -2.650 2.955 +2a02:a00:1009:6 40.179.132.91 2 u 9 64 377 6.683 -0.115 2.329 +2001:4ba0:ffa4: 51.254.155.97 2 u 11 64 377 5.818 -4.302 3.673
Have a look at the syslog messages. If they list something like this, you don’t have a good signal quality, while the receiver at least works in general: ;)
pi@ntp1-dcf77:~ $ tail /var/log/syslog | grep ntpd Nov 8 16:05:59 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 3 bits Nov 8 16:06:04 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 6 bits Nov 8 16:06:08 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 5 bits Nov 8 16:07:00 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 12 bits Nov 8 16:07:04 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 3 bits Nov 8 16:07:07 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 4 bits Nov 8 16:07:18 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 12 bits Nov 8 16:07:24 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 7 bits Nov 8 16:07:55 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 32 bits Nov 8 16:08:00 ntp1-dcf77 ntpd[500]: parse: convert_rawdcf: INCOMPLETE DATA - time code only has 6 bits
Note that the DCF77 receiver must point orthogonal with its longer side to Mainflingen, Germany, which is south-east of Frankfurt. (Fun fact: Since I am living just a few kilometers in the north of Frankfurt, my actual direction of the receiver must not show to Frankfurt directly, but more precisely to Mainflingen. However, for almost everyone else the receiver must simply “look” into Frankfurt.) Furthermore, you must omit any interfering/disturbing sources such as switching power supplies or the like in the room where your receiver resides. At its best, the receiver is placed outside a building (waterproof, lightning protection!), which is not that easy at all. In addition I came across some weird observations, since my signal quality was way better as I turned the antenna just a few degrees in one direction. TL;DR: If you’re lacking signal quality, try a few different positions of your antenna. Be creative. ;)
If you have a good signal strength and quality, the ntpq -p output should look like this, in which the “GENERIC(0) .DCFa.” source has a reach of 377 while the * symbol indicates, that it is used as the system peer:
pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== *GENERIC(0) .DCFa. 0 l 35 64 377 0.000 -0.019 0.346 +2003:de:2016:33 .PPS. 1 s 34 64 376 6.445 0.496 5.006 ptbtime2.ptb.de .PTB. 1 u 23 64 377 16.437 1.195 3.226 +ns2.probe-netwo 124.216.164.14 2 u 6 64 377 9.554 1.594 2.322 -2a01:4f8:201:41 192.53.103.108 2 u 30 64 377 23.610 -3.326 4.884 -2a02:a00:1009:6 192.53.103.108 2 u 9 64 377 13.279 1.295 5.938 -2001:4ba0:ffa4: 130.149.17.21 2 u 47 64 377 17.065 1.689 7.871
4) Adjusting the Fudgetime
You need to adjust the fudgetime of the DCF77 receiver which is the compensation of the offset from the radio clock to the “real” time. That is: You need to compare the time received from your DCF77 module to some other working NTP sources. The offset should be minimal after that.
Without any “fudgetime” options in your ntp.conf file, the “RAW DCF77 CODE (Conrad DCF77 receiver module)”, as it is called by NTP, has a built-in fudgetime1 of 292.000 ms. You can see this with “ntpq -c cv”, since it “displays a list of clock variables for those associations supporting a reference clock”:
pi@ntp1-dcf77:~ $ ntpq -c cv associd=0 status=0020 2 events, clk_unspec, device="RAW DCF77 CODE (Conrad DCF77 receiver module)", timecode="-####----####-#---M-S1-----4p-2---2p-2---2--41---1---81---P", poll=33, noreply=0, badformat=2, baddata=0, fudgetime1=292.000, stratum=0, refid=DCFa, flags=0, refclock_time="dfa1a0fa.00000000 Thu, Nov 22 2018 21:41:14.000", refclock_status="TIME CODE; (LEAP INDICATION; CALLBIT)", refclock_format="RAW DCF77 Timecode", refclock_states="*NOMINAL: 00:29:02 (88.06%); BAD FORMAT: 00:03:56 (11.93%); running time: 00:32:58"
After some time running NTP you will see either this, in which all other NTP servers have a comparable but huge offset (in this situation about 590 ms, while the DCFa receiver is not used due to the “x” in the first column):
pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== xGENERIC(0) .DCFa. 0 l 52 64 37 0.000 -0.792 2.046 -2003:de:2016:33 .PPS. 1 s 48 64 36 7.255 591.525 3.128 ptbtime2.ptb.de .PTB. 1 u 41 64 37 15.637 590.647 1.982 +ns2.probe-netwo 124.216.164.14 2 u 43 64 37 9.333 591.296 1.737 +2a01:4f8:201:41 131.188.3.221 2 u 45 64 37 24.371 586.926 3.137 *2a02:a00:1009:6 205.46.178.169 2 u 44 64 37 12.479 592.152 7.343 2001:4ba0:ffa4: .STEP. 16 u - 64 0 0.000 0.000 0.000
or this, in which all other NTP servers have a low offset, while the DCFa receiver has one with a much higher negative value (while still not used):
pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== xGENERIC(0) .DCFa. 0 l 5 64 77 0.000 -593.24 1.561 -2003:de:2016:33 .PPS. 1 s 60 64 73 7.738 -0.359 0.778 ptbtime2.ptb.de .PTB. 1 u 64 64 77 16.143 0.222 1.091 +ns2.probe-netwo 124.216.164.14 2 u 61 64 77 9.862 0.471 0.862 +2a01:4f8:201:41 192.53.103.108 2 u 61 64 77 23.879 -4.579 0.615 *2a02:a00:1009:6 205.46.178.169 2 u 63 64 77 12.489 -0.316 1.477 2001:4ba0:ffa4: .STEP. 16 u - 256 0 0.000 0.000 0.000
–> Now you need to adjust the fudgetime1 to compensate this difference. In my case, since the pre-configured fudgetime was 292 ms while my offset of the DCF77 receiver was about -592 ms (hence needed an even higher compensation), I had to add a fudgetime1 of 292 + 592 = 884. Open the configuration file again:
sudo nano /etc/ntp.confand add the following below your “server 127.127.8.0 […]” line:
fudge 127.127.8.0 time1 0.884
Followed by a restart of NTP:
sudo service ntp restart.
A couple of minutes later you should have very small offsets among all NTP servers, the external ones as well as your DCFa receiver:
pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== *GENERIC(0) .DCFa. 0 l 63 64 377 0.000 -0.451 0.857 -2003:de:2016:33 .PPS. 1 s 47 64 175 7.557 0.539 2.272 ptbtime2.ptb.de .PTB. 1 u 49 64 377 15.739 1.224 0.850 +ns2.probe-netwo 124.216.164.14 2 u 42 64 377 8.975 1.550 0.757 -2a01:4f8:201:41 192.53.103.108 2 u 52 64 377 24.224 -4.928 2.822 +2a02:a00:1009:6 40.33.41.76 2 u 49 64 377 12.530 -0.400 2.792 2001:4ba0:ffa4: .INIT. 16 u - 128 0 0.000 0.000 0.000
Very good! You’re almost done.
5) Reducing Ethernet Latency
One hint from David Taylor about reducing the Ethernet latency on a Pi: Adding an option to the single line in
sudo nano /boot/cmdline.txtthat states:
smsc95xx.turbo_mode=N(reboot needed). This reduces the “delay” as shown in the “ntpq -p” output. I used two Raspberry Pis connected via a single switch, first time without the option, second with the option set by both. The delay between those NTP servers decreased from about 0.916 ms to 0.632 ms. Nice.
# BEFORE pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== GENERIC(0) .DCFa. 0 l - 64 0 0.000 0.000 0.000 *2003:de:2016:33 .PPS. 1 s 28 64 377 0.916 1.125 0.759 ptbtime2.ptb.de .PTB. 1 u 26 64 377 9.782 1.081 0.419 # AFTER pi@ntp1-dcf77:~ $ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== GENERIC(0) .DCFa. 0 l - 64 0 0.000 0.000 0.000 *2003:de:2016:33 .PPS. 1 s 24 64 377 0.632 0.120 0.737 ptbtime2.ptb.de .PTB. 1 u 35 64 377 9.444 1.592 0.554
That’s it for now. Cheers! If you have any suggestions, please write a comment.
Links
This post used several information from a couple of (german) articles and posts:
- (de) Raspberry Pi Geek – Raspberry Pi als Funkuhr und NTP-Stratum-1-Zeitserver
- (de) debuglevel. – Raspberry Pi und DCF77 Empfänger von Conrad
- (de) Netzmafia – Raspberry Pi: DCF77-Empfang und Decodierung
- NTP – Generic Reference Driver (driver 8)
Featured image “dcf77 module-stapel” by ledmaster33 is licensed under CC BY-NC 2.0.