Raspberry PiにGPS受信機を接続する

ロボット

秋月電子で販売されているGPS受信モジュールを購入した。

これがなかなか難物でした。

ハードウェア

まず、ピンを見ると次のとおり。

データ入力、データ出力、1PPSがある。

1PPSを知らなかったのですが、1 pulse per secondといい、データが出るらしい。これとシリアル通信は別物。
ところが巷の体験記によると1PPSのほうが正確だとかいう話がある。

さらに気をつけなければいけないのが、この受信機はアンテナがプアなので(アンテナは別売)窓際でしばらく待たないと衛星と同機が取れません。同機が取れるとLEDが点滅します。

1PPSを選んだ理由

さて、以下では1PPSを使うための設定をメモしていきます。
普通のシリアル通信を避ける大きな理由があります。
1PPSを使うパッケージのインプリがgpsdというデーモンを使っています。
これは素晴らしいことで、クライアント側プログラムは問い合わせさえすれば最新の情報が入手できます。
一方、シリアル通信でやるならば自分でマルチスレッドを作ることになります。
どこかで書くつもりですが、PythonはThreadingを避けることが推奨されています。GIL(Global Interpreter Lock)により必ずしもコーダーの意思どおりにマルチスレッドが動かないからです。
せいぜい、asyncioとawaitを使う程度にするべきのようです。

Raspberry PIとの接続

さて、1PPSを使うにしろ配線は以下のように、いい感じでできます。
(以下、図、手順はhttps://denor.jp/さんに頼っています。オリジナルはこちらだそうです)

 

シリアル通信(UART)のためにTXD0, TXD0としてすでにアサインされているため、これを外す必要があります。

  1. /boot/cmdline.txtを編集し、console=serial0,115200を削除します。おそらくconsole=tty1というのがあると思いますが、それは残します。
  2. 次にシリアルコンソールを無効化します。
    sudo systemctl stop serial-getty@ttyS0.service
    sudo systemctl disable serial-getty@ttyS0.service

    systemctlはサービスをコントロールするコマンドです。

  3. /boot/config.txtに次の1行を追加します。
    enable_uart=1

1PPSの出力を受け取る定義

  1. /boot/config.txtを編集し、次の1行を追加。
    dtoverlay=pps-gpio,gpiopin=18,,assert_falling_edge=true
  2. /etc/modulesに次の1行を追加します。
    pps-gpio

再起動します。
次のコマンドでpps-gpioが読み込まれていることを確認しましょう。

lsmod|grep pps

gpsデーモンと周辺のツールインストール

  1. gpsdとpps-toolsをインストール
    sudo apt install gpsd gpsd-clients pps-tools
  2. /etc/default/gpsdに以下を書き込みます。
    STAERT_DAEMON="true"
    DEVICES="/dev/ttyS0 /dev/pps0"
    GPSD_OPTIONS="-n"

    オプションの-nはポーリングしないということらしいです?

  3. systemdの設定を変更し、gpsd.socketを有効にします。
    sudo systemctl enable gpsd.socket

再起動しすると、/dev/ttyS0と/dev/pps0をターゲットデバイスとしてgpsdが起動します。(dmesgを確認)

GPS信号の受信

GPSモジュールは点滅していますか?

次のコマンドで一秒おきにデータが出力されます。

sudo ppstest /dev/pps0

gpsmonを起動してみます。フルスクリーンで出ます。

gpsdのAPI

gps3をpythonに追加します。

pip3 install gps3

a


from gps3 import gps3

gps_socket = gps3.GPSDSocket()
data_stream = gps3.DataStream()
gps_socket.connect()
gps_socket.watch()

for new_data in gps_socket:
  if new_data:
    data_stream.unpack(new_data)
    print('time : ', data_stream.TPV['time'])
    print('lat : ', data_stream.TPV['lat'])
    print('lon : ', data_stream.TPV['lon'])
    print('alt : ', data_stream.TPV['alt'])