Yokko's Lab

Back

ARIB STD-T86防災無線をリアルタイム復号するまでBlur image

ARIB STD-T86方式の防災行政無線を実際にSDRで受信し、保存したIQデータから音声を復号しました。そこから処理をストリーミング化し、最終的にはSDRplay RSP1BとRTL-SDRをWindowsアプリから直接操作できるリアルタイム受信機にしています。

この記事の主役はGUIではなく、アンテナから入った複素IQがどのように16QAMのシンボルとなり、誤り訂正された音声ビットとなり、最後にPCMへ戻るのかという信号処理です。


公開資料で分かること、実測で確かめること#

ARIBのSTD-T86規格概要には、60MHz帯の市町村デジタル同報通信システムであり、狭帯域の16QAMとTDMA-TDDを採用することが記載されています。規格書はARIBの案内ページから入手できます。

音声層については、三菱電機の技報から、G.722.1と同じ系統の音声符号化に無線路向けの誤り訂正を組み合わせたS CODECが使われることが分かります。音声コーデック自体の仕様はITU-T G.722.1が基準になります。

アンリツの波形生成ソフトウェア取扱説明書も、S CODEC後のデータと、無線上で付加される同期・チャネル情報を区別する手掛かりになりました。適応多重の考え方は関連公開特許と照合しました。

そして、全体構造を理解するうえで大きな助けになったのが、BOOTHで頒布されている有償技術書「防災行政無線受信システムの開発 STD-T86編」です。ただし、書籍に記載された固有値や表を本記事へ転載することはしません。

公開資料だけで分かるのは、主に「何の処理が存在するか」です。「どのビットをどの順に処理するか」は、実測IQに対して仮説を当て、各段の検査結果が同時に成立するかで確定していきました。


完成したデコーダの全体像#

最終的な信号フローは次のようになりました。

flowchart TD
  A["SDRから複素IQを取得"] --> B["DC除去・周波数シフト<br/>チャネル抽出・レート変換"]
  B --> C["電力包絡で区間検出<br/>整合フィルター・シンボル同期"]
  C --> D["同期系列で<br/>周波数・位相・利得を補正"]
  D --> E["16QAM判定<br/>物理スロット復元"]
  E --> F{"物理チャネル"}
  F -->|"制御"| G["誤り訂正・CRC<br/>受信状態を表示"]
  F -->|"音声"| H["S CODEC・適応多重解除<br/>G.722.1音声復号"]
  H --> I["PCM再生・WAV録音"]
実測IQから音声と制御情報へ至る処理。各箱の出力を個別に検証できる構成にしました。

重要なのは、IQからいきなり音声を得ようとしないことです。周波数同期、時間同期、シンボル判定、物理チャネル、誤り訂正、音声多重という境界を明示し、それぞれに「正しいと言える検査」を置きました。


入力は実際に受信した複素IQ#

解析には、SDRunoで実際に受信・保存したステレオWAVを使いました。左チャンネルをI、右チャンネルをQとして複素数へ変換します。

x[n]=I[n]+jQ[n]x[n] = I[n] + jQ[n]

最初に確認するのは、目的信号が帯域内に存在すること、IとQが独立した信号として保存されていること、クリップや極端な直流成分がないことです。

実測IQから算出したスペクトル
SDRunoで実際に受信したIQのスペクトル。公開用画像では固有の周波数目盛を省略しています。

スペクトル上では狭帯域の信号が明確に確認できます。しかし、フィルターも同期もかけずにI/Q平面へ全サンプルを描くと、16個の点ではなく円盤状の雲になります。

同期前の実測IQ点群
同じ実測IQを同期前に描いたもの。搬送波位相とシンボル時刻が未確定なので、まだ16QAMの点には見えません。

これは失敗ではありません。シンボルの途中にあるサンプル、無信号区間、搬送波の位相回転、隣接サンプル間の遷移を全部重ねた結果です。ここから同期処理を一段ずつ追加します。


1. チャネルを切り出す#

まずIQ全体の電力スペクトル密度を求め、STD-T86の公開帯域幅に相当する窓を周波数方向へ動かします。窓内電力が最大になる場所を粗いチャネル中心候補としました。

受信機の中心に目的信号を置くと、SDR固有のDCスパイクと重なる場合があります。そのため実機では少し離して受信し、ソフトウェア側のNCOで目的信号をベースバンド中心へ移します。

y[n]=x[n]ej2πf0n/fsy[n] = x[n] e^{-j2\pi f_0 n / f_s}

ここでのf0f_0はIQから推定した周波数差です。具体的な運用値を固定するのではなく、受信ごとに残留ずれを再推定します。

周波数移動後は、目的チャネルだけを残す低域フィルターを通し、以後の復調器が扱いやすいサンプルレートへ変換します。この段で大切なのは、フィルターの過渡応答を送信区間の先頭へ持ち込まないことと、サンプル欠落時に古いフィルター状態を使い続けないことでした。


2. 電力包絡から送信の時間構造を見つける#

チャネル抽出後の複素電力を短い区間で平均すると、送信区間と無信号区間が見えてきます。

実測IQの正規化電力包絡
実測IQの電力包絡。具体的な時間値は省略していますが、送信区間が周期的に現れることが分かります。

単純なしきい値だけでは、フェージングやAGCの変動で区間が分裂します。そこで次の順で処理しました。

  1. 複素電力を平滑化する
  2. 雑音床に対する相対しきい値で候補区間を作る
  3. 短い空白を結合し、明らかに短い候補を捨てる
  4. 候補間隔の自己相関を取り、周期グリッドを作る
  5. グリッド上の複数区間が同期系列と整合するか確認する

最後の確認が重要です。強いノイズが周期的に現れても、既知系列との相関までは一致しません。電力包絡は「どこを詳しく探すか」を決める索引であり、それ自体を同期判定にはしていません。


3. シンボル時刻と搬送波を同時に合わせる#

16QAMの受信波形は、概念的には次のモデルで表せます。

r[n]=as[nτ]ej(ϕ+ωn)+w[n]r[n] = a\,s[n-\tau]e^{j(\phi + \omega n)} + w[n]

τ\tauがシンボル時刻、ω\omegaが残留周波数ずれ、ϕ\phiが位相、aaが複素利得です。どれか一つでも外れると、コンスタレーションは円弧や雲になります。

最初に送信側のパルス整形に対応する整合フィルターを通します。その後、1シンボル内の候補サンプル位相を走査し、既知の同期系列と最もよく一致する位相を探します。

同期系列の比較は、単純なユークリッド距離ではなく、候補ごとに複素利得を最小二乗で当てはめてから残差を評価しました。これにより、受信レベルと絶対位相が異なっても同じ尺度で比較できます。

さらに、同期系列の先頭から末尾にかけて位相が一定方向へ回る場合は、その傾きから残留周波数ずれを推定します。粗い周波数補正をNCOで行い、細かな傾きをブロック内の位相補正で吸収する二段構成です。

I/Qの向きが逆転した場合や複素共役になった場合も候補として評価します。既知系列に対する残差が最も小さくなる向きを採用することで、入力デバイスごとのI/Q極性差を吸収できます。


4. 16QAMをビットへ戻す#

周波数、位相、利得を補正したシンボルは、理想16QAM点の近くへ集まります。各シンボルを最も近い理想点へ割り当て、ニブル列へ変換します。

ただし「16個のクラスタが見えた」だけでは復調成功とは言えません。次の条件を同時に確認しました。

  • 同期系列のシンボル誤りが小さい
  • AGC用系列とパイロットが期待位置にある
  • 連続する物理スロットの境界が周期グリッドと一致する
  • 制御チャネルと音声チャネルの識別が継続して成立する
  • 別の送信区間でも同じマッピングが使える

同じ実測IQを復調器へ通し、同期が成立した物理ブロックの補正後シンボルだけを描くと、16個のクラスタを確認できます。青緑の点が受信シンボル、橙色の十字が理想点です。

実測IQを同期した16QAMコンスタレーション
実測IQから復調した同期後16QAMコンスタレーション。同期前の円盤状の点群が、周波数・シンボル時刻・位相・利得の補正後には16個の理想点付近へ集まります。

5. 途中受信や一時的な崩れに耐える同期状態機械#

録音ファイルでは先頭から解析できますが、リアルタイム受信では放送途中にアプリを起動することがあります。また、一度ロックしてもUSB欠落やフェージングで同期が崩れます。

そこで同期を一発判定ではなく、状態機械にしました。

flowchart TD
  A["探索"] -->|"候補検出"| B["検証"]
  B -->|"連続成功"| C["ロック"]
  B -->|"不成立"| A
  C -->|"追尾"| C
  C -->|"品質悪化"| D["再探索"]
  D -->|"新候補"| B
  D -->|"候補なし"| A
ライブ受信の同期状態。ロック前に検証を挟み、品質悪化時は古い状態を捨てて探索し直します。

ロック中は次のブロック位置を予測するので、毎回全探索する必要がありません。一方、品質検査に連続して失敗した場合は、NCO、フィルター、同期器、音声デコーダの状態をまとめて初期化します。壊れた状態を引きずらないことが、長時間受信では重要でした。


6. 制御チャネルを復号する#

物理層から取り出した制御ペイロードには、スクランブルと誤り訂正が残っています。候補となるスクランブル系列を解除し、誤り訂正後のCRCが成立するかで正しい候補を選びます。

CRCは単なる最終エラー表示ではなく、未知パラメータを探索するためのオラクルとして使えます。「見た目がもっともらしい」候補ではなく、複数の実測フレームでCRCまで一貫して成立する候補だけを採用しました。

制御情報から得られる識別情報はGUIの自動選択に利用します。文字情報については、入手できた実測フレームだけでは全種類のフレーミングを確定できなかったため、確定済み項目と推定表示を分離しています。


7. 音声ペイロードはG.722.1へ直接渡せない#

ここからが解析の中心です。

公開資料からG.722.1系の音声であることは分かっていたため、最初は物理スロットから取り出したビットをそのままG.722.1デコーダへ渡しました。一部のフレームは形式上受理されても、連続した音声にはなりません。

原因は、G.722.1の前にS CODEC固有の伝送路処理があるためです。音声情報のうち重要な部分を誤り訂正で保護し、重要度の低い部分と組み合わせ、無線スロットへ再配置しています。

完成した音声側の処理は次の通りです。

flowchart TD
  A["TCH物理ペイロード"] --> B["スクランブル解除<br/>チャネル配置を復元"]
  B --> C{"領域を分離"}
  C -->|"保護領域"| D["消去位置を復元 → Viterbi復号<br/>CRC検査 → ビット順序を復元"]
  C -->|"非保護領域"| E["そのまま保持"]
  D --> F["両領域を結合<br/>適応多重を解除"]
  E --> F
  F --> G["G.722.1パケットへ再構成"]
  G --> H["Sirenデコーダ → PCM"]
S CODEC音声層の信号フロー。具体的な係数、位置、ビット数は省略しています。

8. 多数の実測フレームから保護領域を見つける#

有償技術書から、音声フレームが「誤り訂正される領域」と「そのまま伝送される領域」を持つことは分かりました。しかし、実装に必要な無線上の配置すべてがそのまま得られるわけではありません。

そこで、実測した多数の音声フレームをGF(2)上のベクトルとして扱いました。ビット列を0と1のベクトルと考え、フレーム集合に共通して成立する線形関係を行基本変形で探します。

手順は次の通りです。

  1. 物理スロットの境界を揃え、スクランブルを解除する
  2. 各音声フレームをGF(2)ベクトルとして行列へ並べる
  3. フレーム集合の張る部分空間と、その直交補空間を求める
  4. パリティ検査式に現れる座標と、現れない座標を分離する
  5. 別の受信区間でも同じ分離が成立するか確認する

誤り訂正符号の影響を受ける座標は、フレーム間に線形制約を持ちます。一方、非保護データの座標はその制約へ現れません。この性質を利用すると、内容の正解が分からなくても「どの座標が符号語に属するか」を推定できます。

ここで一部のフレームだけに過学習しないよう、推定へ使わなかった区間で検査式を再評価しました。未知区間でも同じ線形関係が成立することを採用条件にしています。


9. 畳み込み符号とパンクチャを推定する#

保護座標を切り出した後は、座標間の局所的な線形依存を調べました。畳み込み符号では、入力ビットの影響が有限の時間範囲に広がるため、出力座標の依存関係をたどると時間方向の鎖が見えます。

候補となる符号器について、次の評価を行いました。

  • 実測符号語を再符号化したときに一致するか
  • 欠けた出力を消去として戻した後、Viterbi経路距離が小さくなるか
  • 復号後のCRCが複数フレームで成立するか
  • 受信誤りがあるフレームで距離が自然に増えるか
  • 未使用区間でも同じ候補が最良になるか

パンクチャされた位置は、値を0または1と決め打ちせず「観測されなかった枝」としてViterbiへ渡します。こうすることで、欠けた出力を誤った確定値として扱わずに済みます。

符号係数、拘束長、消去位置の具体値は本記事では公開しません。大切なのは、書籍の図を写すのではなく、再符号化、経路距離、CRCという独立した検査が同時に成立する構造だけを残したことです。


10. CRCが通っても音声にならない問題#

Viterbi復号とCRCが通るようになっても、まだG.722.1の音声にはなりませんでした。これは「無線路上の誤り訂正は正しく戻せたが、S CODECが期待する元のビット順ではない」ことを意味します。

ここで、保護データが複数のレーンへ分けて送られている可能性を検討しました。候補となる順序を戻し、次の3段階で評価します。

  1. 保護領域のCRCが成立する
  2. 適応多重ヘッダーと未使用領域の規則が成立する
  3. 音声デコーダの出力が時間方向に連続する

CRCだけを見ると、レーン内部の並びが正しくてもレーン間の順序までは判断できません。逆に、音が出たかどうかだけでは偶然の受理を排除できません。ビットレベルのCRC、パケット構文、音声の時間連続性を組み合わせることで、正しい順序を絞り込みました。

このレーン数、長さ、読み出し順は、今回の解析で得た核心的な具体値なので掲載しません。


11. S CODECの適応多重をほどく#

S CODECの内部では、G.722.1のビットが単純な周波数順では送られていません。フレーム先頭のヘッダーから各周波数リージョンの量子化カテゴリを求め、重要度に応じた順序で可変長符号を多重化します。

復号器側では、概ね次の処理を行います。

  1. ヘッダーからリージョンごとの包絡情報を読む
  2. ビット予算から各リージョンのカテゴリを再計算する
  3. カテゴリとリージョン番号から読み出し優先順を作る
  4. フレームの前方・後方から可変長符号を読み出す
  5. 読み出したリージョンをG.722.1の標準順へ並べ直す
  6. 未使用領域の規則を検査する
  7. ヘッダーなしのG.722.1パケットとしてSirenデコーダへ渡す

前方と後方の両側から読むという構造は、関連特許の記述とも整合します。ただし、保護境界や具体的な読み出し規則は有償資料と実測解析に関わるため省略します。

この段を通したことで、断片的なノイズではなく、連続した放送音声をPCMとして復元できました。


12. 非保護領域が壊れたフレームをどう扱うか#

誤り訂正されない領域にビット誤りが入ると、可変長符号の境界がずれ、後続リージョンまで読めなくなることがあります。壊れたフレームをそのままG.722.1へ渡すと、鋭いノイズや不自然な突発音になります。

そこで、適応多重を完全に検証できないフレームでは、保護領域内で最後まで読めた優先度の高いリージョンだけを残し、残りをゼロベクトルで補うコンシールメントを実装しました。

これは元の音を捏造する処理ではありません。訂正できた情報だけを使い、復元できない高域成分を抑える方法です。帯域は狭くなりますが、時間軸と主要な音声成分を保ったまま連続再生できます。


13. 各段に検査を置く#

この開発では、最終的に音が出るかどうかだけを成功条件にしませんでした。処理段ごとに次の検査を置いています。

段階検査するもの失敗時に疑う場所
IQ入力クリップ、DC、I/Q相関、欠落SDR設定、USB、入力形式
送信区間電力包絡と周期性同調、ゲイン、チャネル抽出
シンボル同期既知系列残差、EVM、位相傾きタイミング、周波数、I/Q方向
物理スロット同期系列、識別子、パイロットスロット境界、16QAMマッピング
誤り訂正Viterbi経路距離、再符号化配置、消去位置、符号候補
保護データCRCレーン順、ビット順、スクランブル
適応多重ヘッダー、ビット予算、未使用領域リージョン順、前後読み出し
音声デコーダ受理、PCM連続性G.722.1再構成、コンシールメント

この検査の連鎖があるため、音が出ないときも「RFが悪い」「同期が悪い」「FEC後が悪い」「音声再構成が悪い」を切り分けられます。


14. ファイル処理をリアルタイム化する#

オフライン版では、入力全体を読んでから最良の同期候補を探せます。リアルタイム版では未来のサンプルを待てず、音声出力も一定速度で消費されます。

そこで受信、復調、音声出力を分離しました。

flowchart TD
  A["SDRコールバック"] --> B["IQリングバッファ<br/>連続性・欠落を監視"]
  B --> C["DSPワーカー<br/>同期・S CODEC復号"]
  C --> D["PCMリングバッファ"]
  D --> E["音声コールバック"]
  C --> F["ペイロード基準のWAV録音"]
  B -->|"欠落・APIリセット"| G["DSP状態を初期化"]
  G --> C
リアルタイム版のスレッド境界。音声デバイスの待ち時間をIQ受信へ波及させません。

IQコールバックでは重い復調をせず、受信データをリングバッファへ積むだけにします。DSPワーカーが一定単位で取り出して復調し、生成したPCMは別のリングバッファへ送ります。音声デバイスはそこから自分のペースで読み出します。

USB/APIのリセット、サンプル番号の飛び、処理キューの欠落を検出した場合は、NCO、FIR、同期器、S CODEC、PCMバッファを一括して初期化します。時間連続性が失われたのに、以前の位相や畳み込み状態を継続すると、見かけ上ロックしているのに音が壊れ続けるためです。

録音済みIQを実時間と同じ速度で流すリプレイ試験を行い、入力より十分短い処理時間で継続復号できることを確認しました。


Windows GUIとSDR直接入力#

最後に、復号コアをWindowsアプリへまとめました。入力は次の3系統です。

SDRplayとRTL-SDRはいずれも、外部SDRソフトや仮想オーディオを介さず、GUIからデバイス選択、同調、ゲイン設定、受信開始まで行えます。Nooelec NESDR SMArt v5の仕様確認には公式データシートを使いました。

STD-T86 Windows Decoderの受信画面
完成したWindows受信機。SDR入力の設定、同期後16QAMコンスタレーション、復号状態を一画面にまとめています。公開用画像では内部コード、生ペイロード、ログの具体値をマスクしています。

画面にはコンスタレーション、同期状態、CRC、受信ブロック、処理負荷を表示します。RSP1BのBias-Tは誤給電を避けるため既定OFFです。WAV録音はPCMの音量ではなく、有効な音声ペイロードを受信したかどうかで開始・終了を判断します。

既存製品の状態表示はMagistol STD-T86 Decoderも参考にしましたが、今回のアプリは復調コアからSDR入力まで独立して実装しています。


公開しない具体値#

復号の処理順と検証方法は説明しましたが、次の値は掲載していません。

  • 有償技術書に掲載された固有の数値、表、図、式
  • 同期語とチャネル識別子のビット列
  • スクランブル系列の生成式、初期値、識別コードとの対応
  • 誤り訂正符号の係数、拘束長、パンクチャ位置
  • 保護・非保護領域のビット数と物理座標
  • デインターリーブ行列、レーン数、レーンの読み出し順
  • CRC生成式、初期値、ビット順
  • 適応多重の保護境界と前後読み出しの具体的な切り替え規則
  • 実装で調整した周波数オフセット、フィルター係数、品質しきい値

参考資料#

実測IQの円盤状の点群から始まり、周波数とタイミングを合わせて16QAMを復元し、物理チャネル、誤り訂正、適応多重、G.722.1を順にほどくことで、最終的に連続した音声へ到達しました。

いちばん大きな学びは、未知のデジタル信号を一気に解こうとせず、各段に独立した検査を置くことです。同期系列、EVM、再符号化、Viterbi距離、CRC、パケット構文、PCM連続性という複数の証拠が同時に成立して初めて、次の段へ進めます。この積み重ねが、オフライン解析を実用的なリアルタイム受信機へ変えてくれました。

ARIB STD-T86防災無線をリアルタイム復号するまで
https://blog.yokko-jp.com/blog/arib-std-t86-realtime-decoder
Author よっこー
Published at 2026年8月15日
Comment seems to stuck. Try to refresh?✨