pygps2 新GPS(NMEA)解析プログラム(ライブラリ) Ver.2 (PicoやPiに対応)

# 6/12 この情報は古いです。　後日更新します
# はじめに
micropygpsなどもっと便利なプログラムがありますが、自分なりのプログラムが欲しかったので作成しました。
少しだけ機能をつけて自分にカスタマイズしたので動かない環境が出てくると思います。
# リポジトリ(Git)

https://github.com/saradaabura/pygps2/tree/master

# 環境
**実行**
- Cpython(Windows 10)
Python 3.13.3 (tags/v3.13.3:6280bb5, Apr  8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)] on win32
- Raspberry Pi Pico 1/2
- Raspberry Pi 400(詳細は後述)
**開発**
- MicroPython v1.24.1 on 2024-11-29; Raspberry Pi Pico2 with
**GPSモジュール**
- Air530Z = AT6558R
- M5stack GPSモジュール V1.1 = AT6668
# 動機
手持ちのモジュールに最適なモジュールが必要だったからです。
(GSVやGSAをほぼ完璧に取得したい)
GPSロガーとして使いたかったので、自分なりの解釈ができるようにつくりました。
# 特徴
- 経度から現在の現地時間を出すことができます。日付をまたぐもできます
- 2周波のGNSSモジュールに対応しています
- NMEAからできる限りのデータを取得できます（開発中 詳細以下）
- エラーがでにくいです
- 自分自身の関数を作って実行できます
- 複数のモジュールを同時に使用できます
# 対応している解析センテンス
主要なセンテンスはカバーしています。
GPS受信機からのセンテンスが不足していても使用できます。
```
GGA:$GNGGA, $GPGGA, $BDGGA
GLL:$GNGLL, $GPGLL, $BDGLL
GSA:$GNGSA, $GPGSA, $BDGSA
GSV:$GPGSV, $BDGSV, $GQGSV, $GLGSV, $GAGSV, $GBGSV
RMC:$GNRMC, $GPRMC, $BDRMC
VTG:$GNVTG, $GPVTG, $BDVTG
GST:$GNGST, $GPGST, $BDGST
DHV:$GNDHV, $GPDHV, $BDDHV
ZDA:$GNZDA, $GPZDA, $BDZDA
TXT:$GNTXT, $GPTXT, $BDTXT
```
# 使用方法
リポジトリからpygps2.pyを取得し/libに配置
以下のようなプログラムでプログラムを実行できます!
### 使用方法(3.1以降)
GPSモジュールからデータを読み取ります。バッファは適度に調整してください。
```python
raw = gps.read(8192)
``` 
それをちょっと特殊なデコード処理をつけます。tryのプログラムです。
```python
raw = raw.replace(b'\r', b'').replace(b'\n', b'')
raw = raw.replace(b'/', b'')
data = raw.decode("utf-8", "ignore")
```
::: note info
受信したデータに、改行がないと処理ができません。（面倒）
主にしたのサンプルのプログラムを利用してください。
:::
次が肝です
```python
gps = pygps2.pygps2()
#インスタンスを作ります
gps.analyze(data)
```
ここに、デコードしたdataをいれて解析することができます。
# 解析オプション
## enable_type
オプションとして、必要なセンテンスのみを解析することができます。(この項目はなくてもOK)
リストとして入力してください。なにもない場合はすべて有効になります。
```
enable_type:
[0]: GGA
[1]: GLL
[2]: RMC
[3]: VTG
[4]: GST
[5]: DHV
[6]: ZDA
[7]: TXT
[8]: GSA
[9]: GSV
```
analyzed_data = pygps2.init()
:::

# プログラム 
```python:main.py
gps = pygps2.pygps2()
while True:
    raw = gps.read(8192)
    if raw is not None:
        try:
            raw = raw.replace(b'\r', b'').replace(b'\n', b'')
            raw = raw.replace(b'/', b'')
            data = raw.decode("utf-8", "ignore")
            del raw
        except Exception as e:
            print(f"error: {e}")
            continue
        gps.analyze(data)
```

# 機能の有効化
- GPGSV,GNGSVに含まれるQZSSを検出し、typeを"QZS"にする
```
DETECT_CONVERT_QZS=True
```
- GSVセンテンスの13番目からバンド情報を取得し、出力する
13番のフィールドがない場合はFalseに設定する
```
IN_BAND_DATA_INTO_GSV=True
```
- 同一のGNGSAに複数の衛星システムがある場合に、衛星システムを識別子を取得する
```
OBTAIN_IDENTIFLER_FROM_GSA=True
```



# 最後に
よければお気に入りして帰ってください
# 参考文献

https://skyrail.tech/archives/433

ライブラリ作成の際参考にさせていただきました。ありがとうございます

# 解析プログラムの拡張
自分で解析関数を作成して独自の解析プログラムが作りやすいようにしています。
- pygps2.pyに解析関数を作成します
- pygps2.pyのpatternsに解析したいセンテンスを設定します
:::note info
ここでは出力されるセンテンスをどの解析プログラムにいれるかを設定するところです。
使用するモジュールにあった設定をすることができます
:::
```詳細:patterns
patterns = {
    'GGA': re.compile(r'\$GNGGA,.*?\*..|\$GPGGA,.*?\*..|\$BDGGA,.*?\*..'),
    'GLL': re.compile(r'\$GNGLL,.*?\*..|\$GPGLL,.*?\*..|\$BDGLL,.*?\*..'),
    'GSA': re.compile(r'\$GNGSA,.*?\*..|\$GPGSA,.*?\*..|\$BDGSA,.*?\*..'),
    'GSV': re.compile(r'\$GPGSV,.*?\*..|\$BDGSV,.*?\*..|\$GQGSV,.*?\*..|\$GLGSV,.*?\*..|\$GAGSV,.*?\*..'),
    'RMC': re.compile(r'\$GNRMC,.*?\*..|\$GPRMC,.*?\*..|\$BDRMC,.*?\*..'),
    'VTG': re.compile(r'\$GNVTG,.*?\*..|\$GPVTG,.*?\*..|\$BDVTG,.*?\*..'),
    'GST': re.compile(r'\$GNGST,.*?\*..|\$GPGST,.*?\*..|\$BDGST,.*?\*..')
}
```
ここに自分の解析する**名前**を辞書形式で**追加**してください。以下のような形です
例
```
'DHV': re.compile(r'\$GNDHV,.*?\*..|\$GPDHV,.*?\*..|\$BDDHV,.*?\*..')
```
:::note alert
**,**　（カンマ）を忘れないこと!!
:::
- 次に解析実行をする関数であるanalyze_nmea_data関数内に移動し以下のようなプログラムを追記します
例
```
analyzed_data['DHV'] = [parse_gst(sentence) for sentence in parsed_data['GST']] if parsed_data['DHV'] else [parse_gst('')]
```
### これで独自の解析関数が登録、実行できるようになりました。

- **関数の作成方法**
センテンスは1行ずつ関数に入れられます。
```python:pygps2(add).py
def parse_dhv(sentence):
    fields = sentence.split(',')#カンマで区切り、リスト型にしたほうが処理しやすい
    data = {
        'timestamp': fields[1] if len(fields) > 1 and fields[1] else '000000.0',
        }
    return data #基本的に戻り値は辞書で返していいです。リストでもOK
```
# 質問等は遠慮なくしてください。
GithubのIssuesでも立てていいですよ