GPSのPPS信号を使うと非常に高精度なNTPサーバが作れるそうなので、実際にRaspberry Pi 2を使ってやってみました。GPSモジュールは測位計算をする時に非常に正確な時刻を得ることができます。これを利用してGPSモジュールの中にはPPS信号を出力するものがあります。PPSはPulse Per Secondの略で、正確に1秒間隔で出力されるパルス信号のことです。

詳細な手順は後回しにして、まずはどれくらい正確な時刻が得られたのかをお見せします。Internet上のNTPサーバを使うと数ms程度の精度だったのがRaspberry Piに接続したGPSのPPSを使うと数μs程度の精度が得られました。

以下のグラフはRaspberry Pi と TS-109（NAS)のそれぞれでNTPサーバを動かし、上位のNTPサーバとの時刻オフセットをMRTGを使って表したグラフです。MRTGがマイナスの値を扱えないので、5ms のオフセットをかけてあります。つまり、中央の5msのラインより上がプラスのオフセット、下がマイナスのオフセットです。

まず、NTPサーバとして、ntp.nict.jp を指定した時の時刻オフセットです（TS-109はサーバとしてntp.nict.jpとRaspberry Piと両方を指定していました）。だいたい数ms程度のオフセットに収まっていることがわかります。

ネット上のNTPサーバを使っても百分の一秒程度の精度が得られるのですが、PPSを使うとさらに高精度な時刻同期をすることができます。

以下のグラフはNTPの参照クロックとしてNICTのサーバからPPSへと変更したときの時刻オフセットです。

22時30分頃にNTPサーバを入れ替えたり、ntp.conf を書き換えたりの作業をしたせいでオフセットが突出している箇所がありますが、それより右の平らな部分ではPPSを使った時刻同期に変わっています。

これはPPS使用時の時刻オフセットを別のスケールで測定したグラフです。

Raspberry Piの方は 20μsのオフセット、TSｰ109の方は500μsのオフセットをかけてあります。Raspberry Piは直接PPSを使用しているので数μsのオフセット値が得られています。TS-109の方はRaspberry Pi を上位サーバとして参照していますが、こちらは100μs程度の精度です。

GPSモジュールのPPS信号を使うと、ほぼ数μs程度の精度で時刻同期をすることができました。

以下が詳細な手順です。

概要

・使用したGPSモジュールは、Adafruit Ultimate GPS Breakoutというものです。これにGPS外部アンテナ を接続して使っています。以下のページを見て設定します｡

https://learn.adafruit.com/adafruit-ultimate-gps-on-the-raspberry-pi

・GPSモジュールのシリアル出力はUARTに入力しています。Raspberry Piでは/dev/ttyAMA0からデータを取り込みます。シリアルコンソールを無効にしてUARTを有効にする設定が必要です｡

・GPSモジュールのPPS信号はRaspberry PiのGPIO 18につなげています。

・以下のページを参考にしました。（書いてあることをほぼなぞるだけでできてしまいました）

http://www.satsignal.eu/ntp/Raspberry-Pi-NTP.html

GPS無しでNTPサーバを動かす

まず、GPS無しでNTPサーバを動かしてみます。ntp.conf を編集します。

$ sudo nano /etc/ntp.conf

次の1行を加えるだけです。

server ntp.nict.jp iburst

NTPサーバを再起動します。

$ sudo /etc/init.d/ntp restart

NTPサーバの動作をチェックします。

$ ntpq -p

remote refid st t when poll reach delay offset jitter

============================================

*ntp-a2.nict.go. .NICT. 1 u 17 64 377 6.547 0.940 0.894

こんなような出力がでればOK.

この状態で1日動かしてMRTGでデータをとりました。

PPS無しのGPSを使ってNTPサーバを動かす

次に、PPSは使わずにGPSを接続してNTPサーバを動かします。GPSモジュールは通常、NMEA0183というフォーマットで時刻や位置などの測位結果をテキストのメッセージとして出力しています。これを使ってNTPの同期をとることができます。

PPSは1秒おきのパルスに過ぎないのでPPSだけでは時刻はわかりません。GPS+PPS（またはインターネットのNTPサーバ＋PPS)という組合せによって、GPSで秒単位の時刻を取得し、それとの差分をPPSのパルスで知ることによって正確な時刻同期を行うことができます。

まず、GPS関連のツールをインストールします。

$ sudo apt-get install gpsd gpsd-clients python-gps

上述のように、私はGPSの出力をUARTに入力しているので、Raspberry PiではGPSの入力が /dev/ttyAMA0 になります。

gpsd を以下のように起動します。

$ sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock

GPSが動いているか確認します。

$ cgps -s

GPSの動作確認は gpsmon でもOK。

ntp.conf の編集をします。

$ sudo nano /etc/ntp.conf

以下の2行を追加します。

server 127.127.28.0 minpoll 4 maxpoll 4

fudge 127.127.28.0 refid GPS stratum 15

NTPを再起動して、確認します。

$ sudo /etc/init.d/ntp restart

$ ntpq -p

remote refid st t when poll reach delay offset jitter

=============================================

SHM(0) .GPS. 15 l 4 16 377 0.000 330.130 354.492

*ntp-a2.nict.go. .NICT. 1 u 58 64 377 6.504 0.867 0.404

こんなような出力ならOK.

SHMはShared Memoryの略です。127.127.28.0 というのはNTPの参照クロックとしてPPSを使用するという意味です。他にどのような参照クロックがあるのかは以下のページでわかります。ちなみに後でPPSを使う時にType22を追加します。

http://www.eecis.udel.edu/~mills/ntp/html/refclock.html

ついでに gpsd の設定をしておきます。

$ sudo nano /etc/default/gpsd

以下のようにします。

START_DAEMON="true"

USBAUTO="true"

DEVICES=""

GPSD_OPTIONS="/dev/ttyAMA0"

GPSD_SOCKET="/var/run/gpsd.sock"

PPSを使ったNTPサーバを動かす

/etc/modules に次の1行を追加します。

pps-gpio

/boot/config.txt に次の1行を加えます。

dtoverlay=pps-gpio,gpiopin=18

Raspberry Pi を再起動します。

モジュールがロードされたことを確認します。

$ lsmod | grep pps

pps_gpio 2993 1

pps_core 8756 2 pps_gpio

PPSが有効になったかどうか確認します。

$ dmesg |grep pps

[ 4.384297] pps_core: LinuxPPS API ver. 1 registered

[ 4.386474] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti

[ 4.416234] pps pps0: new PPS source pps.-1

[ 4.418431] pps pps0: Registered IRQ 178 as PPS source

pps tools をインストールします。

$ sudo apt-get install pps-tools

PPS をテストします。

$ sudo ppstest /dev/pps0

trying PPS source "/dev/pps0"

found PPS source "/dev/pps0"

ok, found 1 source(s), now start fetching data...

source 0 - assert 1388146614.000084899, sequence: 381 - clear 0.000000000, sequence: 0

source 0 - assert 1388146615.000082106, sequence: 382 - clear 0.000000000, sequence: 0

source 0 - assert 1388146616.000084314, sequence: 383 - clear 0.000000000, sequence: 0

source 0 - assert 1388146617.000081521, sequence: 384 - clear 0.000000000, sequence: 0

source 0 - assert 1388146618.000081728, sequence: 385 - clear 0.000000000, sequence: 0

source 0 - assert 1388146619.000080936, sequence: 386 - clear 0.000000000, sequence: 0

source 0 - assert 1388146620.000081143, sequence: 387 - clear 0.000000000, sequence: 0

...

/etc/ntp.conf に以下の2行を追加します。

server 127.127.22.0 minpoll 4 maxpoll 4

fudge 127.127.22.0 flag3 1 refid PPS

それから、PPSを使うためには他のサーバーに prefer 指定が必要とのことなので、NICTのサーバの後ろに prefer を付けます。

server ntp.nict.jp iburst prefer

NTPサーバを再起動して ntpq -p してみるとこんなふうになっているはずです。

remote refid st t when poll reach delay offset jitter

============================================

SHM(0) .GPS. 15 l 15 16 377 0.000 11.207 21.121

oPPS(0) .PPS. 0 l 14 16 377 0.000 -0.002 0.002

*ntp-a2.nict.go. .NICT. 1 u 21 64 377 6.501 0.258 0.914

さらに、ntpq -c rv の出力はこんな感じ。

associd=0 status=0115 leap_none, sync_pps, 1 event, clock_sync,

version="ntpd 4.2.8p3@1.3265 Mon Jul 13 11:28:54 UTC 2015 (3)",

processor="armv6l", system="Linux/3.18.11+", leap=00, stratum=1,

precision=-18, rootdelay=0.000, rootdisp=1.075, refid=PPS,

reftime=d94e2d5d.88677330 Mon, Jul 13 2015 21:28:13.532,

clock=d94e2d62.c11182ca Mon, Jul 13 2015 21:28:18.754, peer=19590, tc=4,

mintc=3, offset=0.008924, frequency=-19.734, sys_jitter=0.003815,

clk_jitter=0.319, clk_wander=0.017

この場合、オフセットは 約9µs です。PPSを参照しているので、stratum=1 になっています。