M5StackでGPS衛星の現在位置を表示する


M5StackとGPSモジュールを使って、GPS衛星の現在位置を調べ、LCDに表示しました。

![M5Stack画面](https://ambidata.io/wp/wp-content/uploads/2018/10/66e091bc2d19932de79e174ad215915d-1-768x606.jpg)

# GPSモジュール

M5Stackの[GPSモジュール](https://www.switch-science.com/catalog/3861/)はu-blox社のNEO-M8Nチップを搭載したもので、現在販売されているV2は外部アンテナと内蔵アンテナが使えます。マイコンとはシリアルで通信します。GPSモジュールの送信データがGPIO16に、受信データがGPIO17に接続されているので、M5StackのUART2を使い、GPIO16を受信、GPIO17を送信に設定します。

GPSモジュールからはNMEA 0183フォーマットのデータが送られてきます。NMEA 0183フォーマットについては「[M5Stack+心拍センサー+GPSで移動中の心拍数を記録する](https://ambidata.io/samples/m5stack/m5stack_pulse_gps/)」に少し書いたので、合わせてご覧ください。

## GSVメッセージ

NMEA 0183フォーマットにはGSV(Satellites in view)という次の形式のメッセージがあります。

```
$GPGSV,メッセージ数,メッセージ番号,全捕捉衛星数,{衛星番号,仰角,方位角,SN比,}x4*チェックサム
```

GPSモジュールが捕捉した衛星の数と各衛星の仰角(0〜90度)、方位角(0〜359度)、SN比(00-99 dB)の情報が分かります。1メッセージには最大4つの衛星の情報が含まれるので、補足した衛星の数が4以上の場合は複数行のGSVメッセージが送られてきます。

この情報を使い、どの位置にGPS衛星がいるかという天球の地図を描きます。

Arduino の「[TinyGPS++](http://arduiniana.org/libraries/tinygpsplus/)」というライブラリーはこのGSVメッセージの衛星の位置データを解析してくれません。MicroPythonの「[micropyGPS](https://github.com/inmcm/micropyGPS)」はGSVメッセージを解析し、衛星の位置データを返すので、MicroPythonでプログラミングします。

# MicroPythonでGPSモジュールのデータを読む

「[micropyGPS](https://github.com/inmcm/micropyGPS)」サイトにいき、ページ右上の「Clone or download」ボタンをクリックしてZIPファイルをダウンロードし、展開します。micropyGPS.pyファイルをampyコマンドでM5Stackに転送すればインストール完了です。

ampyコマンドのインストールと使い方は「[ampy: MicroPythonマイコンとPCとのファイル転送ツール](https://ambidata.io/blog/2018/03/15/ampy/)」をご覧ください。

```
 $ export AMPY_PORT=/dev/tty.SLAB_USBtoUART
 $ ampy put micropyGPS.py
```
 
次のようなプログラムを書きました。

<script src="https://gist.github.com/TakehikoShimojima/e47e6e8ee9d1a57a20d2bca588b59b03.js"></script>

GPSモジュールからデータを読んでmicropyGPSに渡して解析するGPSwatch()関数をスレッドにして実行しています。またこの関数の中では100m秒のsleepを入れています。MicroPythonの開発者によると、これらは「Guru Meditation Error」と「Task watchdog got triggered」というエラーを回避するために必要とのことです。

## GPS衛星の位置を表示する

micropyGPSはGSVメッセージを解析し、捕捉したGPS衛星の位置(仰角と方位角)をgps.satellite_dataに次のような辞書形式のデータとしてセットします。

```
{衛星番号: (仰角, 方位角, SN比), ...}
```

実際には次のようなデータが得られます。

```
{72: (63, 342, None), 73: (8, 71, None), 80: (0, 19, None), 87: (72, 296, 19), 65: (40, 264, 11), 74: (2, 115, None), 86: (39, 168, 40), 71: (25, 39, None)}
```

このデータを使ってGPS衛星の位置を表示します。GSVメッセージはある時は10機分のデータが得られ、次に6機分のデータが得られるなど、衛星のデータが一定しません。

そこで、次のような辞書形式のデータを作り、micropyGPSから得たデータに更新時刻をつけて保存(update)しました。更新時刻が古くなった衛星データを削除するようにしました。

```
{衛星番号: ((仰角, 方位角, SN比), 更新時刻), ...}
```

衛星位置の表示は、天球を円で表し、中心が天頂、つまり仰角90度、円の周辺が仰角0度としました。円の中心の座標を(X0, Y0)、円の半径をrピクセルとすると、仰角e、方位角aの衛星の座標は次のようになります。

```
(X0 + sin(a) * (90 - e) / 90 * r, Y0 - cos(a) * (90 - e) / 90 * r)
drawSatellites()で衛星の位置をLCDに表示しています。
```

## 測位に利用しているGPS衛星の色を変えて表示する

GVSメッセージで得られるのは信号を受信しているGPS衛星ですが、そのすべてを測位に利用しているわけではありません。測位に利用している衛星のリストはGSA (GPS DOP and active satellites)メッセージで得られます。

そこでGSAメッセージで測位に利用している衛星のリストを取得し、衛星を表示するときに、測位に利用している衛星と利用していない衛星とで色を変えて表示しました。

GPSモジュールから送られてくる実際のメッセージを調べると、GSAメッセージはトーカIDが「GN」で送られてきますが、micropyGPSは「GNGSA」を解析しません。解析するメッセージ種類はPythonの辞書で管理されているので、「GNGSA」を追加しました。

プログラムファイルをampyでM5Stackに転送し、CoolTermなどの通信プログラム、あるいはcuコマンドなどでM5Stackにつなぎ、プログラムをインポートして、起動します。

```
 $ ampy put GPSwatch.py
 $ sudo cu -s 115200 -l /dev/tty.SLAB_USBtoUART

 >>> import GPSwatch
```

インポートすると、プログラムが動き、GPS衛星のデータを取得し始めます。プログラムを起動してからGPS衛星のデータが揃うまでに数分かかる場合がありますが、しばらくするとGPS衛星の現在地がLCDに表示されます。