NTPD Configuration

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 fuge 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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s