Since I now have a Meinberg Emulator in place the logical next step is to connect my clock with NTPD.
Since I am running on Ubuntu I describe the steps for Ubuntu. If someone would comment on how to do this for other operating systems I would be grateful.
Of course the “simplest” way to connect a DCF77 clock is to use a “raw clock driver”. However since I already have an Arduino connected to my clock I do not want to add any additional hardware. So I opted for the Meinberg clock driver.
The most helpful resource that I found (in Germany) was http://wiki.ubuntuusers.de/Systemzeit.
I basically followed the instructions there. First I installed ntpd with aptitude.
sudo apt-get install ntp
The next step was to configure ntpd by editing /etc/ntp.conf. Here is my current setup:
~$ cat /etc/ntp.conf # /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help driftfile /var/lib/ntp/ntp.drift logfile /var/log/ntp.log statsdir /var/log/ntpstats/ statistics loopstats peerstats clockstats filegen loopstats file loopstats type day enable filegen peerstats file peerstats type day enable filegen clockstats file clockstats type day enable # Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board # on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for # more information. server 0.de.pool.ntp.org server 1.de.pool.ntp.org server 2.de.pool.ntp.org # By default, exchange time with everybody, but don't allow configuration. restrict -4 default kod notrap nomodify nopeer noquery restrict -6 default kod notrap nomodify nopeer noquery # Local users may interrogate the ntp server more closely. restrict 127.0.0.1 restrict ::1 # entries for DIY DCF77 clock # 8 --> parse clock # 0 --> /dev/reflock-0 # mode 2 --> Meinberg Standard Time String 9600, 7E2 server 127.127.8.0 mode 2 fudge 127.127.8.0 stratum 1 fudge 127.127.8.0 time1 0.030 # gpsd #server 127.127.28.0 #fudge 127.127.28.0 refid GPSa
Notice the commented GPS entries. More on them later.
Also notice the use of the fudge command. After reading some tutorials I wondered why it is called fudge if it is always used to overwrite the stratum. Well, the documentation made it pretty clear. Fudge can fudge other values as well, it is only often used with the stratum. Here I use it to decrease the priority of my clock as well as tuning for the USB bus latency. Depending on how the clock is connected to the computer other values may be appropriate. In my setup I found 30 milliseconds to be most appropriate.
After running “sudo service ntp restart” and “ntpq -p” my clock did NOT show up. So I had to dig a little deeper. Obviously there are two pitfalls. First of all ntpd needs to know where to access my clock. So there must be a (sym)link from /dev/refclock-0 pointing to the serial interface of my clock. The other issue is that ntpd must be authorized to access this interface. Otherwise app-armor will stop it from doing so.
Of course you can create the symlink by means of “ln -S”. However this is not the best idea as this will be gone after each reboot. If you reboot often this is anoying. If you reboot seldom chances are that you will forget that you have to reestablish the link. udev to the rescue!
I told udev to grab my Blinkenlighty. Notice that the Blinkenlighty has an “old” FTDI serial converter which has the nice property that each and every one of them has a different ID. So I created a rule like so:
~$ cat /etc/udev/rules.d/90-map_usb_serial_to_ntp_refclock-0.rules # http://wirespeed.xs4all.nl/mediawiki/index.php/Figuring_out_udev_rules # http://reactivated.net/writing_udev_rules.html # IMPORT{builtin}="path_id" ATTRS{manufacturer}=="FTDI", ATTRS{serial}=="AE01J6GZ", SYMLINK+="refclock-0"
If you wonder how to determine the proper attributes for this (and other adapters) – try “lsusb -v”.
The next step was to tell apparmor to allow ntpd to access the serial interface. I was somehwat lazy and allow access to all serial devices that show up like /dev/ttyUSB*. This is because all FTDI chips will always be identified as such an interface.
~$ cat /etc/apparmor.d/tunables/ntpd @{NTPD_DEVICE}="/dev/ttyUSB*"
Again depending on your Arduino version this might be slightly different. The easiest way to figure out the proper pattern is to connect and disconnect your Arduino. After each step enter “ll /dev/tty*” and see what changes.
After this final step and a restart with “sudo service ntp restart” I finally got something like this.
~$ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== +ntp1.xxxxxxxxxx .DCFp. 1 u 192 256 377 21.177 -0.306 0.142 *time1.xxxxxxxxx .PPS. 1 u 236 256 377 16.450 0.043 0.299 -time2.xxxxxxxxx .PPS. 1 u 99 256 377 19.671 -1.645 0.266 +time3.xxxxxxxxx .PPS. 1 u 118 256 377 16.237 -0.024 58.258 xGENERIC(0) .DCFa. 1 l 5 64 377 0.000 -10.497 4.416
Actually the DCFa showed up immediately but it took several minutes till ntpd picked up the time. This is because an ntp restart will implicitly reset the Arduino. This in turn makes my clock resync which takes a while. If you want to avoid this you have to disable the autoreset feature.
Also notice the offset of -10 milliseconds. This is because I did not yet have the line “fudge 127.127.8.0 time1 0.030” in my configuration).
Another thing to keep in mind with this setup is that the Arduino is not accessible by the IDE while ntpd controls it. The easiest way to reprogram it is to stop ntpd with “sudo service ntp stop” and restart it once the new code is reflashed.
My next step was to configure GPS. The idea was obvious. GPS would provide a reliable and accurate reference. Then I would use this to benchmark and tune my DCF77 clock. So I ordered a uBlox NL-402U receiver. This is a state of the art but not a bleeding edge receiver. The idea was to take something that is hopefully already supported by Linux. As it turned out it is. The receiver performance is better than expected.
Configuration was slightly tricky though. But the big disappointment are the results.
~$ ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== *ntp1.xxxxxxxxxx .DCFp. 1 u 11 64 377 18.161 3.679 9.768 -time1.xxxxxxxxx .PPS. 1 u 58 64 367 16.421 25.690 24.733 +time2.xxxxxxxxx .PPS. 1 u 61 64 377 22.939 -0.574 9.870 +time3.xxxxxxxxx .PPS. 1 u 58 64 377 15.863 17.340 21.900 -GENERIC(0) .DCFa. 1 l 29 64 377 0.000 -13.614 18.706 xSHM(0) .GPSa. 0 l 29 64 377 0.000 -134.26 15.439
As soon as I added GPS jitter for the DCF module increased be more than 4 times. Also the GPS module shows jitter in excess of 10 milliseconds. My assumption is that the GPS module increases traffic on the USB bus. This in turn increases the jitter. The next drawback is that the GPS requires at least some view of the sky whereas the DCF77 receiver virtually works everywhere.
Thus I completely ditched the GPS approach. But still I wanted some idea on how much latency the USB bus introduces and how to get rid of this. Obviously I required access to some “nearby” stratum 0 timer servers. Fortunately I found some very good and very close servers. Which allowed me to determine a very reasonable fudge factor for the time1 parameter. I basically found that the offset is very close to -10 milliseconds. The default time1 for the Meinberg driver is 0.020 (seconds). Since the chain from my clock through FTDI and the USB bus to the kernel introduces an additional 10ms the total fudge must be 20ms + 10ms = 0.030 s.
/var/log # cat /etc/ntp.conf # /dev/refclock-0 meinberg 9600 7e2 server 127.127.8.0 mode 2 fudge 127.127.8.0 stratum 2 fudge 127.127.8.0 time1 0.030 server timehost restrict timehost nomodify notrap /var/log # ntpq ntpq> peers remote refid st t when poll reach delay offset jitter ============================================================================== -GENERIC(0) .DCFa. 2 l 48 64 377 0.000 -0.787 3.054 +timehost.xxxxxx .PPS. 1 u 22 64 377 1.252 -0.102 1.073 +datetime.xxxxxx .PPS. 1 u 15 64 377 0.905 0.051 0.064 *timehost.xxxxxx .PPS. 1 u 18 64 377 0.598 -0.425 0.366 ntpq> as ind assID status conf reach auth condition last_event cnt =========================================================== 1 52897 9354 yes yes none outlyer reachable 5 2 52898 9414 yes yes none candidat reachable 1 3 52899 9414 yes yes none candidat reachable 1 4 52900 9614 yes yes none sys.peer reachable 1 ntpq> ntpq> rv 52897 assID=52897 status=9354 reach, conf, sel_outlyer, 5 events, event_reach, srcadr=GENERIC(0), srcport=123, dstadr=127.0.0.1, dstport=123, leap=00, stratum=2, precision=-20, rootdelay=0.000, rootdispersion=0.000, refid=DCFa, reach=377, unreach=0, hmode=3, pmode=4, hpoll=6, ppoll=10, flash=00 ok, keyid=0, ttl=64, offset=-0.787, delay=0.000, dispersion=1.163, jitter=3.054, reftime=d6bfef70.00000000 Tue, Mar 4 2014 5:21:36.000, org=d6bfef70.07eca7ca Tue, Mar 4 2014 5:21:36.030, rec=d6bfef70.111bc1ce Tue, Mar 4 2014 5:21:36.066, xmt=d6bfef6f.1e968faa Tue, Mar 4 2014 5:21:35.119, filtdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00, filtoffset= -0.79 -1.44 3.53 2.98 -0.98 -0.86 -4.86 3.14, filtdisp= 0.01 1.56 2.51 2.93 3.92 4.89 5.85 6.80 ntpq> ntpq> rv 52900 assID=52900 status=9614 reach, conf, sel_sys.peer, 1 event, event_reach, srcadr=timehost.xxxxxxxxxx, srcport=123, dstadr=10.18.98.29, dstport=123, leap=00, stratum=1, precision=-18, rootdelay=0.000, rootdispersion=0.427, refid=PPS, reach=377, unreach=0, hmode=3, pmode=4, hpoll=6, ppoll=6, flash=00 ok, keyid=0, ttl=0, offset=-0.425, delay=0.598, dispersion=1.604, jitter=0.366, reftime=d6bfef84.ff1d70c5 Tue, Mar 4 2014 5:21:56.996, org=d6bfef8e.1ed2ec12 Tue, Mar 4 2014 5:22:06.120, rec=d6bfef8e.1f026a00 Tue, Mar 4 2014 5:22:06.121, xmt=d6bfef8e.1e99d45c Tue, Mar 4 2014 5:22:06.119, filtdelay= 0.60 1.37 1.00 1.29 1.31 1.33 1.02 1.43, filtoffset= -0.43 0.05 -0.05 -0.05 0.00 -0.18 -0.07 -0.19, filtdisp= 0.00 0.96 1.91 2.85 3.81 4.76 5.75 6.72 ntpq> ntpq> peers remote refid st t when poll reach delay offset jitter ============================================================================== -GENERIC(0) .DCFa. 2 l 28 64 377 0.000 6.972 5.506 *timehost.xxxxxx .PPS. 1 u 7 128 377 1.242 0.087 0.077 +datetime.xxxxxx .PPS. 1 u 65 128 367 0.968 0.112 0.064 +timehost1.xxxxx .PPS. 1 u 68 128 377 0.598 -0.425 0.506 ntpq> ntpq> rv 52897 assID=52897 status=9354 reach, conf, sel_outlyer, 5 events, event_reach, srcadr=GENERIC(0), srcport=123, dstadr=127.0.0.1, dstport=123, leap=00, stratum=2, precision=-20, rootdelay=0.000, rootdispersion=0.000, refid=DCFa, reach=377, unreach=0, hmode=3, pmode=4, hpoll=6, ppoll=10, flash=00 ok, keyid=0, ttl=64, offset=3.124, delay=0.000, dispersion=1.017, jitter=3.190, reftime=d6bff238.00000000 Tue, Mar 4 2014 5:33:28.000, org=d6bff238.06d815c6 Tue, Mar 4 2014 5:33:28.026, rec=d6bff238.0f169e36 Tue, Mar 4 2014 5:33:28.058, xmt=d6bff237.1e96375e Tue, Mar 4 2014 5:33:27.119, filtdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00, filtoffset= 3.12 3.08 -0.82 0.27 7.08 3.18 -0.92 -0.82, filtdisp= 0.01 1.00 1.96 3.89 3.90 4.88 5.83 6.76 ntpq> ntpq> rv 52898 assID=52898 status=9614 reach, conf, sel_sys.peer, 1 event, event_reach, srcadr=timehost.xxxxxxxxxx, srcport=123, dstadr=10.18.98.29, dstport=123, leap=00, stratum=1, precision=-18, rootdelay=0.000, rootdispersion=0.244, refid=PPS, reach=377, unreach=0, hmode=3, pmode=4, hpoll=7, ppoll=7, flash=00 ok, keyid=0, ttl=0, offset=0.087, delay=1.242, dispersion=5.152, jitter=0.077, reftime=d6bff28e.2c3f68af Tue, Mar 4 2014 5:34:54.172, org=d6bff28f.1ee1fba5 Tue, Mar 4 2014 5:34:55.120, rec=d6bff28f.1f049626 Tue, Mar 4 2014 5:34:55.121, xmt=d6bff28f.1e9a29a7 Tue, Mar 4 2014 5:34:55.119, filtdelay= 1.33 1.39 1.24 1.34 1.42 1.26 1.35 1.62, filtoffset= 0.14 0.03 0.09 0.07 0.09 0.00 -0.03 -0.03, filtdisp= 0.00 1.91 3.83 5.78 7.68 9.63 10.61 11.60
Finally success 🙂
One closing comment on the jitter. Take notice that my clock has only a resolution of 10ms. Thus it will have a discontinuity of ~10ms every 100-1000 seconds. This is mainly due to the low CPU power of the Arduino as well as the initial design goal to make the clock as noise resilent as possible. Thus jitter will always be around 5-10 milliseconds.
Although this looks worse than the network based time this is not always the case. The outstanding performance of the network based time servers was during times of very low network utilization. During peak hours my clock easily stays below the jitter of the network based clocks.
Hallo,
welche Einstellungen verwendest du für ttyUSB?
In Windows und mit PuTTY funktioniert alles wunderbar.
NTP bringt mir:
PARSE receiver #0: FAILED TIMECODE: “\x0d\x0aMeinberg Emulator V3.1.1\x0d\x0a(c)” (check receiver configuration / wiring)
Und auch in screen kommt nichts vernünftiges raus egal was ich mit stty -F setze.
Wie soll ich sowas beantworten? Ein paar mehr Details wären angebracht. Siehe auch http://www.catb.org/esr/faqs/smart-questions.html. Davon abgesehen kann es auch an der Hardware liegen. Genauer: der Seriell -> USB Konverter braucht auch die richtigen Treiber. Wenn es unter Windows funktioniert und unter Linux nicht, dann hast Du wohl ein Treiberproblem. Mit meiner Hardware klappt es jedenfalls bestens.