Raspberry Pi にGPSモジュールをつけてStratum 0で時刻同期(スタンドアロン)

[投稿日] 2015年9月30日
[最終更新] 2016年9月21日

2016年9月21日追記:Raspberry Pi 3 + Raspbian Jessieで動作を確認しました。疲れたー…。

43

本記事は、Raspberry PiでGPSを扱うための記事です。GPSを使って、スタンドアロンで時刻同期できることを目標とします。

スポンサーリンク

やりたいこと

やりたいことは以下です。

  • GPSモジュールから信号を受けて、時刻を同期
  • PPS信号で補正
  • インターネットに接続しないスタンドアロンを想定

ここで、PPS信号とはPulse Per Second、すなわち1秒に一回きっかりで送られる信号のことです。これをもちいて、時刻補正をすることができます。GPSモジュールにはAdafruitのUltimate GPS(チップはMTK3339)を使用します。スタンドアロンで使用することを想定するため、長期間にわたってRaspberry Piの電源を落とした場合、バッテリーのないRaspberry Piは時刻を保持できません。そこで、外部のRTCモジュールとしてDS1307を使用します。以上の部品を用いた手順をメモします。

今回非常に参考にさせていただいたのは、「GPSのPPS信号を使った Stratum-1 NTPサーバの作り方 | Nyanchew’s Digital Life – ja1」です。ただ、こちらは基本的にNICTのサーバー(Stratum-1)と同期し、PPS信号で補正という感じのようです(GPSとの同期の仕方も書かれていますが)。参考記事は精度の高いNTPサーバーを目的としていますが、本記事は、スタンドアロンで使うことを想定して、NTPでGPSと同期して、時刻補正をすることを目的とします。同様の目的の記事として、「Raspberry Pi で NTP Stratum-1 Server を作る. [tosihisa’s public notebook]」も参考にさせていただきました。

GPSの購入

Raspberry Piで使用できるGPSとして、AdafruitのUltimate GPS Breadkoutを使用します(「Adafruit Ultimate GPS Breakout – 66 channel w/10 Hz updates [Version 3] ID: 746 – $39.95 : Adafruit Industries, Unique & fun DIY electronics and kits」)。Raspberry Pi GPS Addon Boardなるものもあるようですが、通販サイト見たら最小1000個からって業者向けですねこれ(「Raspberry Pi GPS Addon Board」)。

モジュール

さて、Adafruitからも40ドルで購入できますが、送料などもあるので、スイッチサイエンスから購入するのがお手軽かと思います(「Adafruit Ultimate 66チャンネル10Hz GPSモジュール Version 3 – スイッチサイエンス」)。5,600円ほど。海外通販の煩わしさを思うと、大量注文するのでなければこちらがよいかと。

僕らのAmazonでも、スイッチサイエンスが取り扱いしています。また、このGPSはバッテリー付きでして、CR1220を装着できます。

HAT

数百円上乗せすると、ボードになっているタイプも購入できます(「Adafruit Ultimate GPS HAT (Raspberry Pi A+/B+/Pi 2用) – スイッチサイエンス」)。このタイプはHATと呼ばれます。見栄えがよいですね。こちらであれば、接続のためのジャンパワイヤも必要ありません。記事冒頭の写真は、このボードタイプのものを取り付けたものです。

しかし、このHATタイプには注意点があります。「Overview | Adafruit Ultimate GPS HAT for Raspberry Pi | Adafruit Learning System」には以下のような注意文が掲げられています(2016年9月現在)。

This does not yet work on the Pi 3 due to intense firmware/hardware changes that affected the hardware UART. Please stick to Pi 2 or ‘lower’ for now!

UART周り変更の影響で、Raspberry Pi 3では使えないよ、ということです。しかし、実際に使ってみると、多少設定は煩雑ですが、一応当記事の目的は達することができている…ように見えます。まぁとはいえ、正式には対応していないと考えてよいでしょう。

また、I2Cなど他の機器を使いたいときには、HATはかえって邪魔かもしれません。今後のRaspberry Piのアップグレードによる影響も、モジュール単体より受けやすいものと考えられます。

HATタイプは、以上の注意点を踏まえたうえで使いましょう。今ならRaspberry Pi 3でしょうし、公式がアレな以上、推奨はしません。

なお、モジュールにせよHATにせよ、どちらもピンヘッダのハンダ付けが必要です。決して難しくありませんが(むしろ簡単)、一度もハンダ付けをしたことがない人が取り組む課題ではないかな、とも思います。まぁ練習すればよいことです。

外部アンテナ

このままでもよいですが、できれば外部アンテナを用いるとよりよいでしょう。アンテナを内蔵してもいるので、必須ではありません。「GPSアンテナ 28dB 5m SMAコネクタ型 – スイッチサイエンス」と「SMA/u.FLコネクタ変換ケーブル – スイッチサイエンス」を使います。もちろんAmazonにもあるのですが…購入したSMAU.fl変換がAmazonに置いてない…まぁでも、なんでも使えると思います。私の実績がないのでアフィリエイトリンクは張らず普通のリンクのみにしますが、「Amazon.co.jp: IPX-080-SMA (U.fl/IPX-SMAメス変換RFケーブル 8.0cm): パソコン・周辺機器」とか。

GPSの接続

GPSを購入し、ハンダ付けもしたら、いよいよRaspberry Piと接続します。接続には2種類あります。USB-TTL変換を行うか、GPIOピンを使うかです。簡単なのは前者なので、前者をオススメします。前者ならばGPSを適切に接続するだけで使えますが、後者の場合は設定ファイルをいじらなくてはいけないうえに、Raspberry Pi 2と3、あるいはRaspbian Wheezy以前とJessieでやり方が異なり、ちょっと混乱します。

UART経由(GPIOピン)の具体的な手順については「Raspberry Pi でシリアル通信(Macも同様) : 或る阿呆の記」を参照とします。USB-TTL変換を使えば、このあたりのややこしいところを丸々飛ばせるので、そちらのほうが楽なのがわかります(Adafruitのドキュメントでも変換を推奨しています)。GPSモジュールは3.3Vでも5VでもどちらでもOKなので、USB-TTL変換でも問題ありません(USB経由だと5V)。

ただし、HATタイプのものを使う場合は、UART経由なので、設定ファイルをいじるしかありません。

動作確認

GPSからの信号をRaspberry Piできちんと受けて利用することができるかまで、確認します。これまたRaspbian WheezyとJessieで違うのです。

catで直接叩く

catで直接叩いて、ダラダラとなにか表示されるかどうかを確認します。この時、USB変換をしている場合は/dev/ttyUSB0、UART経由の場合、Raspbian Pi 2では/dev/ttyAMA0、Raspberry Pi 3では/dev/ttyS0です。ここでは、/dev/ttyUSB0とします。

$ sudo cat /dev/ttyUSB0

だらだらと$GPGGAとか$GPGSAとかが表示されればOK。通信できています。

GPSDの利用

次に、以下のコマンドで、必要なソフトウェアgpsdをインストールします。

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

さて、ここで、Raspbian Jessieの場合、細工がいります。gpsdがインストールしたsystemdのサービスを無効化しないといけません(Raspbian Jessieでsystemdを採用した影響)。Raspbian Wheezy以前ならば、この手順はスキップしてOKです。具体的には、以下のコマンドを実行します。

$ sudo systemctl stop gpsd.socket
$ sudo systemctl disable gpsd.socket

そうしたら、次のコマンドを実行します。

$ sudo gpsd /dev/ttyUSB0 -F /var/run/gpsd.sock
$ cgps -s

Raspberry PiとGPSをきちんと接続し、実行できていれば、時刻や位置が表示されるようになります。

GPSDの自動起動

うまくいくことを確認したら、最初からgpsdが起動するように設定しなおします。

Raspbian Jessieでは、これまたシステム周りがsystemdになった影響で、操作が変わります。

Raspbian Wheezyの場合

gpsdで推奨とされる、対話的な手法を用います。

$ sudo dpkg-reconfigure gpsd

いい加減に読みつつ返答をメモします。

  • 自動で起動してもいい?→Yes
  • USBのGPS Receiverを自動的に扱ってもいい?→Yes
  • GPSデバイスはどこにある?→/dev/ttyUSB0 # ここはUSB使いかUART使いかで適宜変更
  • gpsdのオプションは?→-n
  • gpsd control socketのパスは?→/var/run/gpsd.sock

終わったら、rebootしたあとcgpsして、gpsdが起動し受信できているかどうか確認します。

RASPBIAN Jessieの場合

Raspbian Jessieにおいては、dpkg-reconfigureが使えませんので、ファイル/etc/default/gpsdを直接編集します。/etc/default/gpsdの、DEVICES、GPSD_OPTIONS、GPSD_SOCKETを、以下のように設定します。

# 適宜適切なパスを指定(UARTならばRPi2 -> /dev/ttyAMA0 ,RPi3 -> /dev/ttyS0)
DEVICES="/dev/ttyUSB0"
GPSD_OPTIONS="-n"
GPSD_SOCKET="/var/run/gpsd.sock"

その後、以下のようにリンクを貼り付けることで、自動起動させられます(参考:「Quick-start Raspberry Pi NTP server」)。

sudo ln -s /lib/systemd/system/gpsd.service /etc/systemd/system/multi-user.target.wants/

終わったらrebootして、すぐにcgpsコマンドを使えるか、また正常にデータを受信できているかを確認します。

PPS

まず、カーネルが3.18以上であることを確認します。uname -r でどうぞ。それ以前のカーネルでは、PPSに対応しておらず、カーネルからビルドしなければならない、というたいへんな手間が必要とされます。カーネルのバージョンが古い場合は、rpi-updateで更新します。

次に、GPSからRaspberry PiにPPS信号が通るよう、GPSのPPSをRaspberry Piの任意のGPIOピンに接続します。ここでは、GPIO4ピンに接続するものとして進めます。HATタイプのものは、GPIO4ピンに繋がるように接続されているので、それにあわせました。

接続したら、以下のコマンドを実行し、設定ファイルの変更および必要なソフトウェアのインストールを行います。そして、ppstestコマンドによりtest。もし、GPIO4ピンではなく別のピンを使う場合は、下記のgpiopin=4のところを任意のピン番号に変更します。

$ sudo sh -c "echo 'pps-gpio' >> /etc/modules"
$ sudo sh -c "echo 'dtoverlay=pps-gpio,gpiopin=4' >> /boot/config.txt"
$ sudo apt-get install -y pps-tools
$ sudo reboot
.. 再起動
$ 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 1443513245.000003049, sequence: 111358 - clear  0.000000000, sequence: 0
PPS
PPS

ところで、このモジュールのPPSは、1秒間に100msの矩形波が出ています。「Raspberry Pi のGPIO経由で入力信号を連続的に取得 – 或る阿呆の記」の記事で用いた信号のグラフ(左図)がそれだったりします。

NTP

NTPはそのままではPPSに対応しておらず、PPSに対応させるにはビルドしてやる必要があります。また、設定ファイル /etc/ntp.conf も変更します。というわけで、適当なディレクトリで以下を実行します。いつもならプロンプトの$をつけるのですが、コピペのことを考えてつけませんでした。ntpdのバージョンは最新版を確認し、NTPVERのところは適宜変更してください。

# ソフトウェアのダウンロードとインストール
# ntpの最新バージョンは適宜確認
sudo apt-get install -y libcap-dev
NTPVER=ntp-4.2.8p8
wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/${NTPVER}.tar.gz
tar xfz ${NTPVER}.tar.gz
cd ${NTPVER}
sudo ./configure --enable-NMEA --enable-linuxcaps && sudo make &&sudo make install

# 設定ファイル/etc/ntp.confの編集
# GPSとPPSを参照するように設定します
sudo sh -c "echo 'server 127.127.28.0 minpoll 4 maxpoll 4 prefer' >> /etc/ntp.conf"
sudo sh -c "echo 'fudge 127.127.28.0 time1 0.490 refid GPS' >> /etc/ntp.conf"
sudo sh -c "echo 'server 127.127.22.0 minpoll 4 maxpoll 4' >> /etc/ntp.conf"
sudo sh -c "echo 'fudge 127.127.22.0 flag3 1 refid PPS' >> /etc/ntp.conf"

# ntpをいったんとめます
# Raspbian Wheezyの場合
sudo service ntp stop
# Raspbian Jessieの場合
sudo systemctl stop ntp.service

# コンパイルしたntpをbin以下にコピー
sudo cp /usr/local/bin/ntp* /usr/bin/
sudo cp /usr/local/sbin/ntp* /usr/sbin/

実行後reboot(ntpサービスを起動させるだけでもよい)。

/etc/ntp.confの、IPアドレスっぽいものについて説明します。127.127.28.0はSHM、SHared Memoryで、要はいろんなソフトウェアで共有して使いたいものを置くところと考えてよいと思います。記事によっては127.127.20.0としているところもありますが、これはgpsdそのもので、GPSのデータをNTPのみで扱うならばそれでもよいです。しかし、汎用性を考えると、よほど高精度のNTPを求めているわけでもなければ、127.127.28.0としたほうがよいと思います。また、127.127.22.0はPPSのことです。PPSによる補正をするためには、同期元のサーバー(この場合は127.127.28.0)にpreferをつける必要があります。

以上、しばらく放置して同期した頃合いに(NTPの同期は時間がかかる)ntpq -pを実行します。

$ ntpq -p
remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*SHM(0)          .GPS.            0 l    1   16  377    0.000  -50.154  32.918
oPPS(0)          .PPS.            0 l   15   16  377    0.000    0.001   0.004
+ntp-a2.nict.go. .NICT.           1 u   13   64  377   11.317    0.396   0.227

こんな感じになればOKです。*がついているのが同期中のサーバー、oがついているのがPPSです。この例ではnictも参照するように設定しています。+はいつでも参照可能だよ、という意味です。

ntpqはインターネットに接続していることを前提に作られているので、スタンドアロンでは正常に動作しないと思いますが(「Raspberry Pi で ネットを切った時に ntpq -p で Name or service not known. : 或る阿呆の記」)、ネットから切り離しても同期は続きます。ひとまずはこれで目的は達せられたわけですが、運用していると、Raspberry Piはハードウェアクロックを持たない特性に起因すると思われるトラブルがあったので、その内容と対応について次節に書きます。

RTCモジュールの取り付け

Raspberry Piはバッテリーがありません。それはつまり、電源を落とすと時刻を保持できない、ということです。通常であれば、インターネットに接続しているため、即座にNTPサーバーに参照して正確な時刻となりますが、スタンドアロンではそうはいきません。スタンドアロンの場合、前回電源を落とした時刻になってしまいます。

しかしながら、GPSと同期しているのだから、それで問題は解決なのではないか。そう思っていたのですが、いざ運用してみると、問題がありました。少し間の電源遮断ならば問題なく再同期が始まるようなのですが、長時間(一日とか)電源を落とした後起動させると、いつまでたってもGPSと同期が始まりません。しかし、インターネットに接続するとすぐに同期が始まります。どうして同期元がGPSだとうまくいかないのか、ntpのアルゴリズムを理解していないためわからないのですが、現実としてそのような状況でした。

結局、外部のRTCモジュールを取り付け、ハードウェアクロックを実装することで対応しました(「Raspberry Pi で時刻をスタンドアロンでGPSと同期するなら、RTCモジュールとセットが良い : 或る阿呆の記」「Raspberry Pi 2 で RTC を使いスタンドアロンで時刻を保持 : 或る阿呆の記」)。

参考記事

ありがとうございました。

スポンサーリンク

コメントを残す

メールアドレスが公開されることはありません。