TGIF の無音ゲートを見つけた話 ─ MD-619 が固まる本当の理由
カテゴリ: ハマりどころ / OpenCCVoice / DVSwitch タグ: DMR, TGIF, MD-619, MMDVM_Bridge, Analog_Bridge, tcpdump
Radioddity の MD-619 を使っている運用者から、こんな報告をいただきました。
「OpenCCVoice の応答を受信した後、無線機が受信状態のまま固まる。電源も切れなくなる。」
しかも決定的な情報がいくつもありました。
- 他の局(アナログ音声)の受信では固まらない
- OCV の応答を受信したときだけ、確実に固まる
- 他のメーカーの中継経路でも同じ症状が出る
- 途中で信号から抜けると、稀に固まらないことがある
「OCV だけ」「必ず」「途中から」──この3つが揃った症状は、OCV のストリームに、他のクライアントとは違う何か固有の異常があることを強く示唆します。しかし、送信ログを見ても、ダッシュボードを見ても、ボットは正常に応答しているようにしか見えません。ダッシュボードでは Callsign=JJ2YYK、BER 0.0%、ロス 0%。数値上は完璧です。
順に潰していく
まず疑うべきは「音声データそのもの」ですが、これは棄却できます。OpenCCVoice の音声は Open JTalk が生成した PCM を Analog_Bridge が AMBE に再エンコードして送るので、RF に乗るビット構造は普通の DMR 音声と同じです。音の中身では無線機は固まりません。
次に疑ったのは、USRP プロトコルの「終端パケット」の不足でした。ボットは音声送信の最後に keyup=0 のパケットを1発だけ送ってソケットを閉じています。この1発が取りこぼされると、Analog_Bridge がストリームを閉じられないのでは、と考えました。
そこで V1.82 として EOT を3連打に変えました。実機テスト、ハング再発。棄却です。
次は「USRP メタデータの欠落」を疑いました。DVSwitch 公式クライアントの pyUC は、送信前に必ず自局のコールサインと DMR ID を SET_INFO パケットで通知します。OCV は送っていない。そこで V1.83 で pyUC とバイト単位で同じ SET_INFO を実装しました。実機テスト、ハング再発。これも棄却。
3日かけて主要な仮説を潰しましたが、進捗はありませんでした。
tcpdump で電波の中身を見る
ここで方向転換しました。RF 経由で本当に何が届いているのか、パケットレベルで見よう。
Pi 上で tcpdump を回し、DMR ネットワーク(Homebrew プロトコル、UDP 62031)のパケットをキャプチャします。
sudo tcpdump -i any -Z root -w /tmp/dmr.pcap udp port 62031
そして自作の解析器 dmrd_analyze.py で読み解きます。
その結果、決定的な発見がありました。通常局 JJ2ZAR の折り返し(ネットから Pi に届く方向)は、こう見えます:
ヘッダ:3 終端:1 総フレーム:42 並び: VVVAAAbbbcccdddeeefff...T
きれいに VoiceLCHeader が3発、その後に音声、最後に Terminator。正常な DMR ストリームです。
一方、OpenCCVoice の折り返しはこう:
ヘッダ:0 終端:3 総フレーム:259 ⚠️ ヘッダ無し! 並び: AbcdefAbcdefAbcdef...
VoiceLCHeader が完全にゼロ。 送信側では確かに2発送出しているのに、TGIF から戻ってくるストリームには1発も残っていません。
さらに衝撃的だったのは、消えているフレーム数です。
- 1回目:送信 275 → 配送 224。差 51 フレーム
- 2回目:送信 281 → 配送 230。差 51 フレーム
51 × 20ms = 3.06 秒。何の数字か、お分かりでしょうか。
OCV の応答の頭には、前パディング1.5秒 + 焼き込み頭無音1.5秒 = ちょうど 3.0 秒の無音があります。
TGIF は無音を転送しない
つまり TGIF は先頭の無音区間を配送していない。 音が始まった所からストリームを配送し始めているのです。ヘッダは無音区間の前にあるので、無音と一緒に捨てられます。
これが MD-619 の受信スタックの直接原因でした。ヘッダなしのストリームを受け取った受信機は「途中参加(late entry)」処理を強要され、これに弱いファームが破綻する。
「アナログ音声では起きない」の説明もつきます。人間の声にはノイズフロアがあってビット的にゼロになりません。合成音声の無音は数学的にゼロ。TGIF はゼロを捨てる。「他のメーカーの経路でも起きる」の説明もつきます。TGIF 側の挙動なので、どの経路を通っても同じ結果になります。
推測ですが、TGIF はネットワーク帯域を節約するため「無音は転送しない」というエネルギー閾値のゲート機構を持っていると考えられます。ノイズフロアのある実音声はこのゲートを開けられるが、デジタル完全無音は永遠に開けられない。これは TGIF の仕様上の挙動で、ボット側の実装ミスではありません。しかし、ボット側で対処する必要はあります。
対策:無音をノイズに
対策の方針は明快でした。送出する PCM の完全ゼロを、聞こえないほど微小なノイズに置き換える。 人の声のノイズフロアと同じく AMBE が「非無音」として符号化し、TGIF ゲートは開く。
V1.84 でこれを実装。実機テスト結果、MD-619 のハングが完全に解消しました。
原因を突き止めるのに、送信側の実装を疑ってから3日、パケットキャプチャを取ってから15分でした。
🏛 JJ2YYK あいちデジタルコミュニケーションハムクラブ
教訓: 症状の原因が分からないときは、電波レベルまで降りるのが最速です。ダッシュボード、ログ、ボット側の実装──どれも「送ろうとしたもの」を教えてくれますが、「実際に受信側に届いているもの」は教えてくれません。ダッシュボード表示は途中で加工されていることを覚えておくべきです。
次回は、この決定的発見をもたらしたパケット解析器 dmrd_analyze.py の作り方と使い方を紹介します。DVSwitch 系で「なんで俺の局だけ変な動きをするんだ」と思ったときの一手として、たいへん役立ちます。
続きの話(音質のシャー音・ゲロゲロ音との戦い、Talker Alias の謎、パケット解析器の実装)は次回以降で。
TYT md-619,retevis P1 でTG43912(立山山頂)を受信すると同様の症状で固まり電源OFFも受け付けなくなります
バッテリー脱しか対処できない状態でした
他機種やVoxDMR ではこんな症状はありません
現在立山山頂はJARLAnalogレピーターとDMRがリンクされているようで是非利用したいポイントなんですが
AI診断ではパケットフローのフリーズの回答でしたので受信パスしてます