メインコンテンツまでスキップ

災危通報の解析

メッセージフォーマット

災危通報のメッセージは、0xB5 0x62 から始まるUBXプロトコルのバイナリ形式で出力され、必ず以下の形で始まります。

b5 -> UBX Preamble sync character 1
62 -> UBX Preamble sync character 2
02 -> Message Class (RBX)
13 -> Message ID (SFRBX)
2c 00 -> Payload Length (Little Endian, 44 bytes)
05 -> GNSS ID (5=QZSS)
ヒント

NMEAセンテンスは \r\n の改行コードで終わりますが、UBXプロトコルのバイナリ形式メッセージはヘッダの Payload Length を読んでメッセージの終端を得る必要があります。

上記のヘッダに続き、メッセージごとのデータおよびチェックサムが入っています。

07 -> Satellite ID (7+182=189=QZS03)
01 -> Signal ID (1=L1S)
00 -> Frequency ID (Only used for GLONASS)
09 -> The number of data words contained in this message (8+9*4=44)
45 -> Tracking channel number
02 -> Message version (0x02)
00 -> Reserved 0
55f5ad9a170580110000008e00000000000000000000000010000000b1aa5aebff9483b2 -> 災危通報データ
e2 -> ck_a (Checksum)
cd -> ck_b (Checksum)

UBXプロトコルに関する詳細については、以下のu-blox社のドキュメントを参照してください。

M10 firmware 5.10 interface description (PDF, 2.28MiB)

備考

災危通報メッセージは衛星航法データの出力 UBX-RXM-SFRBX を有効にすると出力されます。
出荷時は有効にする設定を書き込んでいるため、設定作業は不要です。

センテンスへの変換

以下はPython3で災危通報のセンテンスを出力するサンプルコードです。

UBXプロトコルのバイナリ形式で出力される災危通報メッセージを ユーザインタフェース仕様書(災害・危機管理通報サービス, IS-QZSS-DCR-010)の 4.3.1. Sentence format で示されている $QZQSM から始まるNMEA形式のセンテンスに変換して出力します。

read.py
import sys
import argparse
import operator
from functools import reduce
import serial

satellite_id = {
184: '56',
185: '57',
189: '61',
183: '55',
186: '58',
}

def nmea_checksum(sentence):
data = sentence.strip("$").split('*', 1)[0]
cksum = reduce(operator.xor, (ord(s) for s in data), 0)
return cksum

def ubx_checksum(message):
ck_a = 0
ck_b = 0
i = 0
while i < len(message):
ck_a = (ck_a + message[i]) & 0xff
ck_b = (ck_b + ck_a) & 0xff
i += 1
return ck_a, ck_b

def ubx2qzqsm(line):
if line[:7] == b'\xB5\x62\x02\x13\x2C\x00\x05': # UBX-RXM-SFRBX, 44 bytes, QZSS
satId = satellite_id[line[7] + 182] # PRN -> Satellite ID
data = b''
for i in range(9):
data += bytes((line[14+3+i*4], line[14+2+i*4], line[14+1+i*4], line[14+0+i*4]))
if data[1] >> 2 == 43 or data[1] >> 2 == 44: # Message Type 43=JMA-DC Report, 44=Other
dcr_message = (data[:31] + bytes((data[31] & 0xC0,))).hex()[:-1] # 256-4=252 bit
sentence = '$QZQSM,' + satId + ',' + dcr_message + '*'
return sentence + format(nmea_checksum(sentence), 'x')


if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Print QZQSM NMEA format sentence')
parser.add_argument('port', help='serial port. ex: /dev/ttyUSB0')
parser.add_argument('baudrate', help='baudrate. ex: 115200')
parser.add_argument('-n', '--nmea', help='print other standard NMEA sentence', action='store_true')
args = parser.parse_args()

with serial.Serial(args.port, args.baudrate) as ser:
while True:
line = b''
nmea_flag = False
ubx_flag = False
count = 0
payload_length = 0
while True:
if ubx_flag:
if count > 4 and payload_length == 0:
payload_length = int.from_bytes(line[4:5], "little")
if payload_length > 0 and count == payload_length + 8: # header 6 bytes + checksum 2 bytes
break
b = ser.read()
if b == b'$' and not ubx_flag:
nmea_flag = True
if b == b'\x62' and line == b'\xB5':
ubx_flag = True
if b == b'\n':
if line.endswith(b'\r'):
line += b
break
else:
line += b
else:
line += b
count += 1

if args.nmea and nmea_flag:
sentence = line.decode().strip('\r\n')
ck = nmea_checksum(sentence)
if format(ck, 'x') == sentence.split('*', 1)[1]:
print(sentence)

if ubx_flag:
ck_a, ck_b = ubx_checksum(line[2:payload_length+6])
if line[-2] == ck_a and line[-1] == ck_b:
sentence = ubx2qzqsm(line)
if sentence:
print(sentence)

実行

PySerialをインストールしていない場合はpipでインストールしてください。

pip install pyserial

以下のように、シリアルポートとボーレートを指定すると $QZQSM から始まるセンテンスを出力します。

python read.py /dev/tty.usbserial1410 115200
$QZQSM,57,9aadf5b1118002c3f2587f8b101962082c41a588acb1181623500012b979380*20

また、-n オプションでNMEA形式の他のセンテンスも出力します。

usage: read.py [-h] [-n] port baudrate

Print QZQSM NMEA format sentence

positional arguments:
port serial port. ex: /dev/ttyUSB0
baudrate baudrate. ex: 115200

options:
-h, --help show this help message and exit
-n, --nmea print other standard NMEA sentence

センテンスの解析

以下のライブラリを使用することで、センテンスを文字情報に変換することができます。

nbtk/azarashi: QZSS DCR Decoder

上記ライブラリにあるいくつかのバグを修正したフォーク版を提供しています。

9SQ/azarashi: QZSS DCR Decoder

インストール

git clone https://github.com/9SQ/azarashi.git
cd azarashi
python setup.py install

使用方法

sentenceに上記サンプルコードで得た $QZQSM から始まるセンテンスを入れてください。

import azarashi

sentence = '$QZQSM,57,9aadf5b1118002c3f2587f8b101962082c41a588acb1181623500012b979380*20'
report = azarashi.decode(sentence, msg_type='spresense')
print(report)
防災気象情報(海上)(発表)(通常)
海上警報が発表されました。

発表時刻: 11月12日17時35分

警報等情報要素: 海上濃霧警報
サハリン東方海上

警報等情報要素: 海上濃霧警報
サハリン西方海上

警報等情報要素: 海上濃霧警報
網走沖

警報等情報要素: 海上濃霧警報
宗谷海峡

警報等情報要素: 海上濃霧警報
北海道西方海上

警報等情報要素: 海上濃霧警報
北海道東方海上

警報等情報要素: 海上濃霧警報
釧路沖

警報等情報要素: 海上濃霧警報
日高沖

過去の災危通報データ

過去の配信データは以下に保存しています。ライブラリや自作プログラムの出力テストにご利用ください。

みちびき災危通報 過去配信データ

みちびき災危通報 試験用データ 2022年9月6日