Raspberry Pi + GPSでNTPサーバー

どうも、萌乃です。
Raspberry PiとGPSモジュールを組み合わせてNTPサーバーを構築してみました。
GPSからの信号を利用することでUTCに対して数百ナノ秒の精度で時刻を保持できます。
以前から高精度の時刻保持は研究していたのですが、Raspberry Piを使うとわりと簡単に作れるので良いなと思う次第です。

■もくじ

1:材料
2:raspbianの設定
3:ひとまずGPSモジュールからのデータを見てみる
4:PPSの確認
3:カーネルのリビルド
4:NTPのビルド
5:うるう秒への対応
6:NTPの設定(gpsd編)
7:NTPの設定(ntpd編)
8:Raspberry Pi自体の設定
9:ちなみに

■材料

・Raspberry Pi B+
おなじみのrpiです。秋月電子で購入。

・4GB以上のmicroSDカード
これはお好みで。
4GBだとカーネルリビルド時にギリギリだったので、8GB以上が良いかもしれません。

・U-blox NEO-6M GPSモジュール
aitendoで購入(http://www.aitendo.com/product/10255
アンテナも付属していますので窓際ならこれだけで受信環境が揃います。

動作電圧3-5v、信号がTTLレベルで出て使いやすいですが、PPSがLEDに接続されているので
3番ピンにリード線をハンダ付けして信号が取り出せるようにしておきます。

gps

こんな感じで。
ピンヘッダを付けておくと便利です。

・GPSアクティブアンテナ
aitendoで購入(http://www.aitendo.com/product/2421
ベランダにアンテナを出したかったので購入。
上記アクティブアンテナの場合、モジュール側がU.FL、アンテナ側がMMCXなので変換する必要があります。

case

自分はケースを加工して搭載できるようにしました。

■raspbianの設定

今回はrpi標準のraspbianを使っています。(2014/12/24版で動作確認しています)
http://www.raspberrypi.org/downloads/からシステムイメージを入手してmicroSDに書き込んでおいてください。

キーボードやロケールなど初期設定はお好みで
apt-get update
apt-get upgrade
rpi-update

で内容を最新にしておきます。

GPSモジュールはrpiのttyAMA0に接続することになります。
標準ではttyAMA0でシリアルコンソールが使えるようになっているため、これを無効にします。

/boot/cmdline.txtからconsole=ttyAMA0,115200 の部分を削除
/etc/inittabのT0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100 の行を無効に
sudo insserv -r triggerhappyでtriggerhappyを無効に

他の用途でもrpiを使うなら最低限このくらいですが、自分のようにntp専用にしたいなら
サウンド関連は無効にしても良いかもしれません。
/etc/modulesにsnd-bcm2835がいます。

ここまでやったら一度シャットダウンしてGPSモジュールを接続。

connect

自分はこんな感じに入力しています。
電源投入後、GPSを捕捉するとPPS LEDが点滅します。

■ひとまずGPSモジュールからのデータを見てみる

PPSは一旦置いておいて、GPSモジュールから出てるデータを見てみます。

NMEAデータを管理するgpsdとクライアントをインストール
sudo apt-get install gpsd gpsd-clients

今のところはgpsdを自動起動せずに手動で立ち上げます。
理由は後述。
sudo gpsd /dev/ttyAMA0 -n -F /var/run/gpsd.sock
cgps -sするとGPSの捕捉状況が見られます。

cgps

こんな感じに
衛星が3つか4つ以上捕捉できていると3D FIXできますが、
時刻同期だけなら多分もっと少なくてもいいです。

余談として、gpsdを経由すれば簡単にPPSに同期したGPSロガーも作れそうですね。
実用に耐えうるかはともかく、そのうち作ってみます。

■PPSの設定

※2014/12/24公開のraspbianカーネルは標準でPPSに対応しています。

PPSの信号が利用できるようにします。

sudo apt-get install pps-tools
でppsツールをインストール後、
/boot/cmdline.txtの行末に
bcm2708.pps_gpio_pin=18 を追加(ピン番号は環境で変更してください)、

/etc/modulesに
pps-gpio
を追加します。

再起動後、
sudo ppstest /dev/pps0
でppsが拾えているか確認。

pps

延々表示し続けるのでCtrl+Cで止めます。

■カーネルのリビルド

※2014/12/24公開のraspbianカーネルは標準でPPSに対応しています。
カーネルリビルドしなくても数マイクロ秒の精度は出ます。

ナノ秒オーダーの精度を出すにはdyntickが邪魔になるので、tickの方法を変更してカーネルをリビルドします。

必要なものをインストール。
apt-get install wget git libncurses5-dev bc screen

今動いているカーネルコンフィグを取得するとき、sudoでもエラーが出たのでrootログインできるようにしておきます。
sudo passwd rootでパスワード作成。
再起動しrootでログインして作業再開。

mkdir pps
cd pps
git clone --depth 1 git://github.com/raspberrypi/linux.git
cd linux
zcat /proc/config.gz > .config ←今動いているカーネルコンフィグを保存、ここでsudoだとエラーが出ました
make menuconfig

カーネルメニューにて、まずは現在の設定をロードします。
最下部のloadから.configをロード。

PPSとtick周りの設定をします。
Device Drivers → PPS supportでPPS supportを有効に(Y)、
PPS kernel consumer supportとPPS client using GPIOも有効にしておきます。

メニューのトップに戻って、
General setup → Timers subsystemと入り
Old Idle dynticks configを無効、
High Resolution Timer Supportを有効に。
その後Timer tick handlingを有効にすると別画面に遷移するので
Periodic timer ticksを選択します。

ここまで終わったら再度メニューのトップに戻り、
saveから.configへ再度書き込みます。

私はこれをやり忘れて1日無駄にしました…

さてビルドします。
make
make modules_install

makeに10時間くらいかかります。
ext4のジャーナル破損が起こることがありますが、その際にはmicroSDの故障ないし相性なので
別のカードに変えてみてください。

カーネルイメージ化します。
git clone --depth 1 git://github.com/raspberrypi/tools.git
cd tools/mkimage/
./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage

これでkernel.imgが出来上がります。

rpiにカーネルをインストールし、起動カーネルを変更します。
mv kernel.img /boot/kernel_tick.img
/boot/config.txtに
kernel=kernel_tick.img
を追記します。

再起動すると作成したカーネルで起動します。

■NTPのビルド

標準でインストールされているntpはPPSに対応していないので、ソースからビルドします。
sudo打つのが面倒だったのでrootでログインしています。

mkdir ntp
cd ntp
apt-get install libcap-dev
wget http://archive.ntp.org/ntp4/ntp-4.2.8.tar.gz

1/31時点ではntp-4.2.8p1-RC2.tar.gzが最新です。
必要に応じてhttp://archive.ntp.org/ntp4/を確認してください。

tar xvfz ntp-4.2.8.tar.gz
cd ntp-4.2.8
./configure
--enable-ATOMはなくても対応するようです。
--enable-linuxcaps含めて入れたほうが精神衛生上良さそうな気がします。

make
make install

これで/usr/local/bin/と/usr/local/sbin/にバイナリが生成されます。

このバイナリを今まで動いていたntpと入れ替えます。
/etc/init.d/ntp stop
cp /usr/local/bin/ntp* /usr/bin/ && cp /usr/local/sbin/ntp* /usr/sbin/
/etc/init.d/ntp start

この方法だと、今までのからntp.confや起動スクリプトが流用できるので便利です。

■うるう秒への対応

今年の7/1にうるう秒の挿入があります。
GPSから送られる情報の中にうるう秒がいつプラス、マイナスされるかがありますので、
本来であればその情報だけで対応できます。
しかし、GPSからのその情報を使うかはクライアントごとにまちまちなので、別のアプローチで対応しておきます。

アメリカのNISTでうるう秒のリストが公開されていますのでそちらを使います。
ftp://time.nist.gov/pub/leap-seconds.list
(実体のファイルはleap-seconds.3629404800ですが、leap-seconds.listでシンボリックリンクされているようです)

cd ~
wget ftp://time.nist.gov/pub/leap-seconds.list
sudo cp leap-seconds.list /etc/

/etc/はntp.confと同じところに置きたいと思ったからなので、コピー先はお好みで。

/etc/ntp.confの中に
leapfile /etc/leap-seconds.list
を追記します。
ntpdを再起動すると適用されます。

ここからNTPの設定に入りますが、NMEAのデータgpsd経由で送るか、ntpdで直接管理するかによって設定が異なります。
gpsdを経由せずにntpdで直接管理したほうが精度が良いですが、他のソフトから扱えなくなります。

■NTPの設定(gpsd編)

NMEAデータをロギングしたいなど、他にも使う際にはこちらです。
共有メモリを経由するので、NMEA側のジッターが大きめな気がします。

まずはgpsdの自動起動を設定します。
sudo dpkg-reconfigure gpsdから出てくるメニューで
自動起動をオン、参照ポートを/dev/ttyAMA0、ソケットを/var/run/gpsd.sock(標準)に。

/etc/ntp.confの現在のserver行をコメントアウトしてから、下記設定を投入します。
server 127.127.22.0 maxpoll 4
fudge 127.127.22.0 flag3 1 refid PPS
server 127.127.28.0 maxpoll 4 prefer
fudge 127.127.28.0 time1 0.141 refid GPS

他の設定はお好みで。

time1はPPSに対するNMEAの遅延時間で、単位は秒です。
私の環境では141ミリ秒くらいでしたが、一旦0で動かしてオフセットを見ながら適宜調整したほうがいいかもしれません。

この設定で動かすと、下のようになります。

gpsd

■NTPの設定(ntpd編)

私のようにNTP専用機にするぜ!という人はこちらです。
ntpdが直接NMEAデータをつかむので安定しますが、他で使えません。

/etc/udev/rules.d/10.pps.rulesというファイルを作成します。
内容は以下2行

KERNEL=="ttyAMA0", SYMLINK+="gps0"
KERNEL=="pps0", OWNER="root", GROUP="dialout", MODE="0660", SYMLINK+="gpspps0"

ntpdはユーザー”ntp”で動作するので、ユーザー”ntp”をdialoutグループに追加しておきます(標準ではグループに属していません)。
sudo usermod -G dialout ntp

再起動すると/dev/gps0と/dev/gpspps0という2つのデバイスができます。
このデバイスをntpdで読んでいきます。

/etc/ntp.confの現在のserver行をコメントアウトしてから、下記設定を投入します。
server 127.127.20.0 mode 17 maxpoll 4 prefer
fudge 127.127.20.0 flag1 1 flag3 1 refid GPS

flag1はPPSを処理するかのフラグ、flag3はPPSの管理をntpdでするかカーネルでするか(0がntpd、1がカーネル)のフラグです。
あと、入れなくても精度は出ますが気になる方はtime2も入れてください。
time2はPPSに対するNMEAの遅延時間で、単位は秒です。

この設定で動かすと、下のようになります。

ntpd

■Raspberry Pi自体の設定

上記でGPSモジュールを参照してntpdが動作するようになりました。
より時刻精度を出したい、参照時の精度を出したいといった場合には下記設定をすると良いでしょう。

・CPU周波数の固定
rpiのCPU周波数は、アイドル時には低く負荷がかかると高くなります。
周波数変動時の遅延が時刻に影響をするので、それを固定します。
raspi-configでオーバークロックをするとリセットされてしまうので注意。

sudo echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

・シリアルのレイテンシ設定
シリアル(NMEA)はオフセット指定できるので標準のままでも問題ないのですが、
レイテンシを低減しておくとなお良いかと思います。

sudo apt-get install setserial
setserial /dev/ttyAMA0 low_latency

この設定を入れる前後で遅延時間がいくぶん変化しますので、必要に応じてntp.confに修正を加えてください。

・LANコントローラーの設定
rpiに搭載されているLANコントローラーはSMSC9512というもので、USBハブと一緒になっています。
ターボモードというものがあり複数のパケットを同時送出できますが、カーネルクラッシュの危険性がありますので
無効にしたほうが良いのかも。

/boot/cmdline.txtに下記設定を入れます。
smsc95xx.turbo_mode=0
ppsの設定が行末になるように注意してください。

・メモリの設定
rpiに搭載のメモリにはリフレッシュレート調整の機能がありますが、調整時にはポーズがかかって影響が出ますので無効にします。
メモリの温度によって調整しているみたいですね。

/boot/cmdline.txtに下記設定を入れます。
disable_pvt=1
ppsの設定が行末になるように注意してください。

■ちなみに

自宅で運用しているrpiのオフセット値などをmuninで収集して公開しています。
http://ns.lowfreq.info/munin/pintp/pintp/
このくらいの精度が出る、という参考にしてもらえれば。
プラグインの一部は自作したものですが、これについては別の機会に公開します。

Posted: 2015-01-31
at 17:00 by 入江 萌乃


Categories: サーバー,技術メモ

Comments: 2 comments