Azarashi supported DCX

ようやくできました。みちびきの災害・危機管理通報サービス(災危通報、DCR)にJ-AlertとL-Alertが追加されると聞いて、「2つ追加されるだけでしょ?チョロいチョロい」と思っていたら全然違いました。DCRのデコーダーをもう一度作るような手間がかかりました。

https://pypi.org/project/azarashi/

J-Alert、L-AlertはDCXというDCRとは異なるメッセージで配信され、CAMFというメッセージフォーマットを使っています。日本の災害・危機管理通報だけをターゲットにしたメッセージフォーマットではなく、様々な災害が定義されており、EUのGALILEOと共通で用いられることが想定されているようです。これはまた別の記事で書きます。

さて、azarashiはDCXをCAMFで定義されているビットフィールドに従ってこのように文字列化します。

>>> import azarashi
>>> msg = '$QZQSM,55,53B0604DE19524CDA305B2C1E355B57800000CCC000000000000001022A8188*7E' # l-alert
>>> report = azarashi.decode(msg, 'nmea')
>>> print(report)
### DCX Message - L-Alert ###
A1 - Message type: Alert
A2 - Country/region name: Japan
A3 - Provider identifier: Foundation for MultiMedia Communications
A4 - Hazard category and type: MET - Rainfall
A4 - Hazard definition: Rainfall greater than or equal to 50 mm in past 24 hours. Note: Precise threshold is according to each local standard.
A5 - Severity: Severe - Significant threat to life or property
A6A7 - Hazard onset: 2024-06-23 13:00:00
A8 - Hazard duration: 6H <= Duration < 12H
A11 - Guidance to react: Keep away from Water area.
A11 - Guidance to react (ja): 離れろ。水場。
A12 - Ellipse centre latitude: 35.688
A13 - Ellipse centre longitude: 139.691
A14 - Ellipse semi - major axis: 10.933
A15 - Ellipse semi - minor axis: 5.979
A16 - Ellipse azimuth: 45.0
A17 - Specific settings: B1 - Improved Resolution of Main Ellipse
C1 - Refined latitude of centre of main ellipse: 35.688
C2 - Refined longitude of centre of main ellipse: 139.691
C3 - Refined length of semi major axis: 10.933
C4 - Refined length of semi minor axis: 5.979

どのフィールドになんの情報が入るかをここで説明するのは無理です。細かいことはドキュメントを読んでください。メッセージによって使われるフィールドがかわるので、これまでのDCRと違ってちゃんと仕様を確認しないとDCXは使えないと思います。
https://qzss.go.jp/technical/download/ps-is-qzss.html
https://www.gsc-europa.eu/sites/default/files/sites/all/files/EWSS-CAMF_v1.0.pdf

実際に使うことになったときはたとえばA4フィールドの災害に関する情報は日本語に翻訳する必要がありそうですね。A4が0のメッセージが降ってくることもあるので、それも考慮して実装しましょう。
ちなみに、執筆時点で実際に降ってくるL-Alertのメッセージを見ているとA12~A16の位置情報は設定されておらず、そのときはEX1フィールドに市区町村コードが設定されます。

>>> from pprint import pprint
>>> pprint(report.get_params(), sort_dicts=False)

レポートクラスの中身もDCRと全く違います。

{'sentence': '$QZQSM,55,53B0604DE19524CDA305B2C1E355B57800000CCC000000000000001022A8188*7E',
 'timestamp': datetime.datetime(2024, 6, 21, 15, 9, 5, 433111),
 'message': b'S\xb0`M\xe1\x95$\xcd\xa3\x05\xb2\xc1\xe3U\xb5x\x00\x00\x0c\xcc'
            b'\x00\x00\x00\x00\x00\x00\x00\x10"\xa8\x18\x80',
 'nmea': '$QZQSM,55,53B0604DE19524CDA305B2C1E355B57800000CCC000000000000001022A8188*7E',
 'message_header': '$QZQSM',
 'satellite_id': 55,
 'satellite_prn': 183,
 'raw': b'M\xe1\x95$\xcd\xa3\x05\xb2\xc1\xe3U\xb5x\x00\x00\x0c\xcc\x00\x00\x00'
        b'\x00\x00\x00\x00\x10',
 'preamble': 'A',
 'message_type': 'DCX',
 'camf': <azarashi.qzss_dcr_lib.decoder.qzss_dcx_decoder.QzssDcxDecoder.decode.<locals>.CAMF object at 0x1023af6a0>,
 'ignore_a12_to_a16': False,
 'ignore_a17_to_a18': False,
 'ignore_ex1': True,
 'ignore_ex2_to_ex7': True,
 'ignore_ex8_to_ex9': True,
 'satellite_designation_mask_type': 'MT44 is for Japan or for use outside Japan',
 'satellite_designation_mask': ['For Japan',
                                'For Japan',
                                'For Japan',
                                'For Japan',
                                'For Japan',
                                'For use outside Japan',
                                'For use outside Japan',
                                'For Japan',
                                'For Japan'],
 'dcx_message_type': 'L-Alert',
 'a1_message_type': 'Alert',
 'a2_country_region_name': 'Japan',
 'a3_provider_identifier': 'Foundation for MultiMedia Communications',
 'a4_hazard_category': 'MET',
 'a4_hazard_type': 'Rainfall',
 'a4_hazard_definition': 'Rainfall greater than or equal to 50 mm in past 24 '
                         'hours. Note: Precise threshold is according to each '
                         'local standard.',
 'a5_severity': 'Severe - Significant threat to life or property',
 'a6_hazard_onset_week': 'Current',
 'a7_hazard_onset_time_of_week': 'SUNDAY - 01:00 PM',
 'a6a7_hazard_onset_datetime': datetime.datetime(2024, 6, 23, 13, 0),
 'a8_hazard_duration': '6H <= Duration < 12H',
 'a9_selection_of_library': 'Country/region guidance library',
 'a10_library_version': '#1',
 'a11_japanese_library': 'Keep away from Water area.',
 'a11_japanese_library_ja': '離れろ。水場。',
 'a12_ellipse_centre_latitude': 35.6882581826505,
 'a13_ellipse_centre_longitude': 139.69085457500137,
 'a14_ellipse_semi_major_axis': 10.932758602420911,
 'a15_ellipse_semi_minor_axis': 5.978542029422428,
 'a16_ellipse_azimuth': 45.0,
 'a17_main_subject_for_specific_settings': 'B1 - Improved Resolution of Main '
                                           'Ellipse',
 'c1_refined_latitude_of_centre_of_main_ellipse': 35.6882581826505,
 'c2_refined_longitude_of_centre_of_main_ellipse': 139.69085457500137,
 'c3_refined_length_of_semi_major_axis': 10.932758602420911,
 'c4_refined_length_of_semi_minor_axis': 5.978542029422428,
 'dcx_version': 1}

これまでのDCRをハンドリングするコードにDCXのレポートクラスを流し込むと想定しているメンバ変数が存在せずエラーになると思います。azarashi.decode_stream()メソッドにはignore_dcxオプションがあるので、DCXを流してよければFalseを設定してください。
後方互換性のためデフォルトではDCXは無視されます。

「自分で作ったライブラリを使うんじゃい」という方はreport.camfにビットフィールドの値が入っています。

>>> print(report.camf.get_params())
{'sdmt': 0, 'sdm': 96, 'a1': 1, 'a2': 111, 'a3': 1, 'a4': 74, 'a5': 2, 'a6': 0, 'a7': 9421, 'a8': 2, 'a9': 1, 'a10': 0, 'a11': 773, 'a12': 45761, 'a13': 116395, 'a14': 13, 'a15': 11, 'a16': 48, 'a17': 0, 'a18': 0, 'ex1': 13104, 'ex2': 0, 'ex3': 0, 'ex4': 0, 'ex5': 0, 'ex6': 0, 'ex7': 0, 'ex8': 0, 'ex9': 7376896189632872448, 'ex10': 0, 'vn': 1, 'c1': 0, 'c2': 0, 'c3': 0, 'c4': 0}

GitHubのレポジトリに星をくださった方、本当にありがとうございます。azarashiの半分はあなたの星でできています。
星を押さずに使っている方、いますぐ星を押すのです。星を押さないと開発が止まりますよ。星が足りません。星をもっとください。

では、よいハックを。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*