【MADOCA】SSR-RTCM仕様を“表”に固定して機械的に読む<MADOCA/RTCM SSRパーサの設計と実装メモ>

2週間の格闘からの学びまとめ。仕様=CSVカタログを唯一の真実にし、コードは機械的に読むだけにすると、LLMの“度忘れ”や実装ブレを構造的に潰せる。
●経緯:こんなに苦労しなくてもRTKLIBを使えばいいのですが、自作にこだわるので、ある程度自分の頭の中でなにをやっているか知りながらChatGPTに作らせることにこだわった結果2週間もかかってしまいました。将来的にGPTを活用していろいろな新規開発をしていきたいので、GPTの癖と扱い方のノウハウを開拓する意味もあります。
今回のGPTの課題;RTCM資料をPDFで渡しても、ヘッダをみつけてからの処理を延々とやっていくうちに完全に忘れて、GPTの過去学習の予測を優先してコードを組んでしまうため、途中からとんでもない仕様外れのコードを組んでしまう現象が多発しました。理由を聞くと、GNSS関係はマイナーなので、元々の学習ベースにないので、その場その場でユーザーの話しを聞きながらコードを想定しながら作っているそうで、仕様も確率的に考えているという恐ろしいことをやってました。これでは永遠にパースができないので、L6のCSSRをSSRにデコードするのは、複雑すぎてGPTでは無理だろうということで、MADOCA公式ライブラリのcssr2ssr.exeをPythonラッパーSubprocessにしてデコードしてSSRのRTCMを得ることにしました。次にRTCMのパースはGPTでやろうとして、今回の仕様を忘れる事件が発生して、対策に苦慮した上げく4人目のGPTに相談して
仕様の記憶が無しでもパースできる仕様カタログ表でのパース方式で
RTCMのパースを完成させた次第です
cssrssr.exe
https://github.com/QZSS-Strategy-Office/madocalib/tree/main/bin

cssr2ssr.exeをPythonで実行する

https://gist.github.com/dj1711572002/695e3cda92005023cf444ff94cdb1832
●全体の流れ
新テーマ、RTKのドップラー積分による補間テーマで、重要なのは
衛星の精密な位置速度がないと受信アンテナとの間のドップラー速度ベクトルの速度の精度がでないので、リアルタイム系で最高精度がでるMADOCA-L6 NTRIP接続のアカウントを内閣府宇宙戦略局様へ申請して、許諾していただきました。
団体に所属してない個人での申請は初めてだそうで、いい加減なことをすると失礼になるので、確実に成果をだせるように来シーズンのスキー計測に活用できるように準備を進めてます。

①最高精度はNASAのMGEX SP3衛星エフェメリスデータですが計測時刻から3日後に公開されるのでリアルタイムの計算にはつかえません。
②衛星エフェメリス精度をみてみると
・MGEX SP3がダントツですが3日後にしか入手できません、
・MADOCAは、JAXAが独自観測網+IGSの組み合わせで配信している補正データでリアルタイム系では精度が非常によいです、。精度的に1m以下はねらえます、
・BCEPは、通常のGPS受信機が衛星の放送として受信する放送暦ですがこれは
精度が悪くこれだけで地上の位置だと通常のGPS程度の数m誤差でます。

●処理の流れ

STEP アイテム 内容
補正データログ MADOCA NTRIP 内閣府管理のNTRIP接続
C#【MADOCANTRIP Logger】
L6フォーマットなので、

cssr2ssr.exeでSSRに変換する
【SubProcess_cssr2ssr.py】
SSRをRTCM csvにパース
【extract_cssr_from_rtcm.py】
放送暦ログ
(BCEP)
CDDIS BCEP NTRIP NASA CDDISのNTRIP接続
【ntrip_bcep_logger_v2.py】
RTCMをRINEX-NAV変換
【make_nav_from_rtcm3_fixed.py】
MADOCA
補正適応
BCEP+MADOCA SSR RTCM補正 補正してSP3ファイルを作成しテ完成
【madoca_merge_to_sp3_v1.6.2.py】
以後は、MGEX SP3と比較して精度の確認をおこなう
回転治具でのドップラー速度積分での精度計算に使って精度確認
を行います。
●参考資料
①MADOCA テストライブラリ cssr2ssr.exeがあるリンク

やったこと(要約)

  • **仕様カタログ(CSV)**で各MTのフィールド順・ビット長・型を定義
    message_type, field, bits, data_type の4列だけ

  • パーサはCSVを読み込んでビットを順に読むだけ(推論や“覚える”は不要)

  • RTCMフレーム抽出→カタログにあるMTは解析済みCSV、無いMTや失敗はraw CSVで必ず保存

  • **1057/1058/1062のスケール(m / m/s / m/s²)**を適用して “*_m” 列を出力
    (1059の入れ子繰り返しにも機械対応)

  • ファイル名に元ログのベース名+バージョンを付与して上書き回避

  • インデックスCSVで出現数と epoch 範囲を可視化

  • 仕様の取り違え(1058にSRDを入れてNo. of Satellitesが倍になった)を表側修正だけで瞬時に矯正

 

ディレクトリと主要ファイル

ここでの <stem> は入力 *.ssr のファイル名(拡張子抜き)。

message_type field bits data_type
1057 Message Number 12 uint12
1057 GPS Epoch Time 1s 20 uint20
1057 SSR Update Interval 4 bit(4)
1057 Multiple Message Indicator 1 bit(1)
1057 Satellite Reference Datum 1 bit(1)
1057 IOD SSR 4 uint4
1057 SSR Provider ID 16 uint16
1057 SSR Solution ID 4 uint4
1057 No. of Satellites 6 uint6
1057 GPS Satellite ID 6 uint6
1057 GPS IODE 8 uint8
1057 Delta Radial 22 int22
1057 Delta Along-Track 20 int20
1057 Delta Cross-Track 20 int20
1057 Dot Delta Radial 21 int21
1057 Dot Delta Along-Track 19 int19
1057 Dot Delta Cross-Track 19 int19
  • 行順=ビット並び順。これが唯一の真実

  • 符号付き/無しは data_type(例:int22, uint20, bit(4))で判定

  • 「No. of Satellites」を検出したら 以降を衛星ブロックとして n_sat 回読む

    • 1059のように衛星内でさらに可変繰り返しがある場合は、
      No. of Code Biases Processed を読んで直後の [Signal, Bias]N 回繰り返すだけ

    • スケールの扱い(m / m/s / m/s²)

      • 表の“名前一致”で係数を適用し、*_m / *_mps 列を追加

      • 代表例(RTCM 10403.2ベース)

        • 1057(軌道)

          • Delta Radial:0.0001 m/LSB(0.1 mm)

          • Delta Along/Cross:0.0004 m/LSB(0.4 mm)

          • Dot項:0.000001 / 0.000004 m/s

        • 1058(時計)

          • C0:0.0001 m/LSB(0.1 mm)

          • C1:1e-6 m/s/LSB(適用時にΔtを掛ける)

          • C2:2e-8 m/s²/LSB(適用時にΔt²を掛ける)

        • 1062(高レート時計)

          • 0.0001 m/LSB

      ブログでは “C0が1〜1.3 mでも正常、適用値は C0 + C1·Δt + C2·Δt²” と一言補足しておくと誤解が減ります。


      出力設計(落とさない・上書かない・追跡できる)

      • 必ずどちらかを出力

        • 解析成功 → <stem>_v2_MTxxxx.csv

        • 仕様外/失敗 → <stem>_v2_MTxxxx_raw.csvpayload_hexframe_hex を保存)

      • バージョン列・タグ

        • 各CSVの先頭に version 列(例:v2

        • ファイル名にも _v2_ を組み込み → 将来v3と共存できる

      • インデックス

        • <stem>_v2_mt_index.csvcount / mode(parsed|raw) / epoch_min,max

        • indexにあるのにCSVが無いという穴を構造的に排除


      実装の要点(コードの読み方)

      • BitReader:RTCM payload(frameの0xD3ヘッダ+len+payload+CRCのうちpayload)から先頭12bitでMT抽出

      • spec_table.load_spec:CSVを FieldDef の配列へ。MTごとに行順を保持

      • CssrParser.parse

        1. ヘッダを順に読む

        2. 「No. of Satellites」以降=衛星ブロックn_sat

        3. 1059は衛星内で No. of Code Biases Processed を読んで [Signal, Bias]×N

        4. スケールがあれば *_m / *_mps を追加

        5. 余りビット(パディング)は厳密チェックしない(実装差分に強くするため)

      • extract_cssr_from_rtcm_v2.py

        • CRC-24Q を検証(VERIFY_CRC=True

        • TARGET_MESSAGE_TYPES=None見つかった全MTを対象

        • 既知MT→解析、未知/失敗→rawにフォールバック

        • ファイル名=<stem>_<version>_MT<mt>.csv で上書き回避


      トラブル事例と対処(実録)

      • 1058に“Satellite Reference Datum”を入れてしまい、No. of Satellites が60に倍化
        CSVを修正(1058はSRD無し)だけで復旧。コードには一切触れずに正しい列数・値へ

      • MADOCA仕様と実データのズレ(1062 vs 1058)
        → データ側に1058が流れていると判明。1058をカタログに追加して即対応

      • 出力ファイルが不足
        raw出力の標準化で“取りこぼしゼロ”。indexの mode で一目で判別


      運用チェックリスト

      • ssr_df_catalog_madoca.csv が正しいか(1058にSRD無し / 1057ドット項の型 等)

      • VERIFY_CRC=True*_mt_index.csvcount が期待どおりか

      • parsed で出るMTが増えているか(raw が残ればカタログ未整備のサイン)

      • 1057の _m 列が1 m未満に収束、1058の C0 が数 m以内で自然か

      • バージョンを上げる時は PARSER_VERSION / VERSION_TAG を更新し併存させる


      伸びしろ(必要になったら)

      • 適用後の時計補正Clock(t)=C0+C1·Δt+C2·Δt² をCSV列で出力

      • 縦長(1衛星=1行)出力:長大列を避けたい用途向け

      • カタログに version/hash 列を追加し、実行時に乖離検出(安全運用)

      • サブフォルダ分割cssr_out/v2/<stem>/...(大量ログ運用)


      まとめ

      • 仕様を“表”に固定して、コードは表を読むだけにする——たったこれだけで、
        実装ブレ/勘違い/度忘れが構造的に起きにくいパイプラインになりました。

      • 解析側の変更はCSVに追記・修正するだけ。コードは安定・再現性が高い。

      • “catalog外”でも必ずrawを残す設計で、取りこぼしゼロ後追い修正を両立。

      コードはGistに公開(推奨):「bitreader.py / spec_table.py / cssr_parser.py / extract_cssr_from_rtcm_v2.py / (任意)audit_catalog_vs_data.py」。
      記事にはこの方針と運用の勘所だけを書けば、将来の自分の助けになります。

 

コメントを残す

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