Raspberry Pi: GPS レシーバーの接続

Takami Torao RPi3 Model B+ Raspbian 11 #RaspberryPi
  • このエントリーをはてなブックマークに追加

概要

Raspberry Pi を含む Linux ベースのシステムに USB 接続の GPS レシーバーを接続することで正確な位置情報を得ることができる。Linux ではシリアルまたは USB 接続された GPS とのインターフェースとなる gpsd というデーモンを利用する。gpsd で確認済みのハードウェアは Compatible Hardware 参照。

Table of Contents

  1. 概要
  2. GPS レシーバーを接続する
  3. Python プログラミング
  4. References

ネットワークに接続していない (または NTP サーバと時刻同期していない) Raspberry Pi で GPS レシーバーを使用するには注意が必要である。Raspberry Pi には RTC が搭載されておらず、シャットダウン時に時刻を記録し次の起動時にはその時刻から開始する。したがって電源が切断されてしばらく放置された後に起動するとシステム時刻が大きく遅れた状態となる。

そして、Raspberry Pi のシステム時刻が正しくない状況での GPS の測位は必ずコールドスタートとなり、起動のたびに正しい位置や時刻を計測できるようになるまで数十分かかることになる。オフライン環境でも起動後すぐに GPS 測位を開始するホットスタートを有効にするには、Raspberry Pi に RTC モジュールを追加するか、Garmin のハンディ GPS のように GPS レシーバー側で時刻とアルマナックを保持しているデバイスを使用する必要がある。

GPS レシーバーを接続する

GPS レシーバーは Raspberry Pi 本体と USB で接続すればシステムによってすぐに認識される。以下に lsusb コマンドで U-Blox AG と表示されているデバイスが GPS レシーバーである。

pi@pirite:~ $ lsusb
Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 006: ID 30de:6545 KIOXIA TransMemory
Bus 001 Device 008: ID 1546:01a7 U-Blox AG [u-blox 7]
Bus 001 Device 005: ID 0424:7800 Microchip Technology, Inc. (formerly SMSC)
Bus 001 Device 003: ID 0424:2514 Microchip Technology, Inc. (formerly SMSC) USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Microchip Technology, Inc. (formerly SMSC) USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

位置情報を取得するため、バックグラウンドで GPS デバイスと通信する gpsd とそのクライアントをインストールする。

pi@pirite:~ $ sudo apt-get install -y gpsd gpsd-clients

gpsd は USB やシリアルポートに接続した GPS デバイスからデータを収集して 2947 TCP ポート経由で複数のアプリケーションから使えるようにするデーモンである。NMEA 0183のようなプロトコルを JSON に変換している。

測位の状況は以下のコマンドで確認することができる。

pi@pirite:~ $ cgps -s -u m

ただし、測位を開始しても十分な衛星を補足するまでは Fig 3 のように n/a ばかりが表示されたり不正確な位置を示す。しばらくして測位が可能となると、左側に現在時刻と緯度、経度、標高、速度などが表示され、右側には補足している衛星とその電波状況が表示されるようになる。

Fig 1. cgps コマンドで見る GPS 情報。15 個の衛星を補足していて、そのうちの S/N 比の高い 5 個を使用している。
Fig 2. gpsd の構成。
Fig 3. 十分な衛星を補足していない状態。衛星を全く検出できない場合は再起動すると良いかもしれない。

衛星の位置情報を全く持たない状態での GPS デバイスの起動をコールドスタートと呼ぶ。これは購入後の初回起動や、飛行機等で長距離を移動した後の起動、長期間の間を空けて起動したような状態が該当する。コールドスタートは十分な数の衛星を捕捉するまで位置を特定できないため測位を開始するまでに非常に時間がかかる (Garmin の GPSMap60CS でも室内では30分~数時間かかることもある)。

これに対して、ある程度正確に衛星の位置情報が分かっている状態での起動をホットスタートと呼び、こちらは数秒程度で測位が可能となる。携帯電話やスマートフォンは通信回線を経由して衛星の位置情報 (A-GPS) をダウンロードするためホットスタートで起動できる[1]。

cgps 以外にも、gpsd を経由せず直接 GPS のパケットを監視する gpsmon コマンドを使うこともできるので、問題の切り分けなどで使い分けるとよいだろう。

Python プログラミング

cgps コマンドで正しく位置情報が取得できていることを確認したら次は位置情報を収集してみよう。Python では gps3 パッケージを使って gpsd から情報を取得することができる。

pi@pirite:~ $ sudo apt-get -y install python3-dev python3-pip
pi@pirite:~ $ pip3 install gps3
from gps3 import gps3
import tzlocal
import datetime
import time

TZ = tzlocal.get_localzone()

def parse_time(tm):
  if tm is None or tm == "n/a":
    return None
  tm = datetime.datetime.strptime(tm, "%Y-%m-%dT%H:%M:%S.%f%z")
  return tm.astimezone(TZ)

def parse_latlon(ll):
  if ll is None or ll == "n/a" or abs(float(ll)) <= 1e-5:
    return None
  else:
    return float(ll)

if __name__ == "__main__":
  socket = gps3.GPSDSocket()
  socket.connect()
  socket.watch()
  ds = gps3.DataStream()
  for new_data in socket:
    if new_data:
      # see: https://gpsd.gitlab.io/gpsd/gpsd_json.html
      ds.unpack(new_data)
      tm = parse_time(ds.TPV["time"])
      if tm is not None:
        lat = parse_latlon(ds.TPV["lat"])
        lon = parse_latlon(ds.TPV["lon"])
        print("{} {}/{}".format(tm, lat, lon))
        time.sleep(0.75)

実行結果。

pi@pirite:~ $ python3 gps.py
2022-02-07 22:16:27+09:00 35.699yyyyyy/139.812xxxxxx
2022-02-07 22:16:28+09:00 35.699yyyyyy/139.812xxxxxx
2022-02-07 22:16:29+09:00 35.699yyyyyy/139.812xxxxxx
...

References

  1. 第456回:A-GPS とは - ケータイ Watch