「送信中なのに割り込んでしまう」── 受信ビジー検知を後付けで組み込んだ話
OpenCCVoice の音声アナウンス機能を運用していて、地味だが厄介な問題に当たりました。デジピーターが受信中(誰かが交信中)でも、時報やアナウンスを構わず送信してしまい、進行中の通信に被せてしまうのです。
本記事は、その「受信ビジー検知」を後付けで組み込んだときの試行錯誤の記録です。同じシリーズの設計判断や GPIO まわりの話は別記事にまとめてあります。
何が問題だったか
初期実装では、設定した時刻になったら無条件でアナウンス音声を再生し、PTT を ON にして送信していました。誰も使っていない時間帯ならこれで困りません。ところが実際の運用では、交信が続いている最中に時報が割り込み、相手局の音声にアナウンスが重なる、という事故が起きました。
- 送信タイミングが固定時刻なので、運用状況をまったく見ていない
- 受信中かどうかを判定する仕組みが存在しなかった
- 結果として「人が話している上に機械が喋る」状態になった
受信状態をどう取るか
無線機側には受信中を示す信号(スケルチが開いているかどうか)が出ています。これを Raspberry Pi の GPIO に引き込んで読めば、ビジー判定ができるはずでした。理屈は単純です。
# 擬似コード
if gpio_busy_pin == HIGH:
# 受信中 → 送信を見送る
skip_announcement()
else:
# 空き → アナウンス送信
transmit_announcement()ところが、ここで素直に動かないのが現場でした。
瞬間値で見ると誤判定する
GPIO をその瞬間に一度だけ読むと、スケルチのチャタリングや短いノイズで HIGH/LOW がばたつき、「空いている」と誤判定して送信してしまうことがありました。逆に、ほんの一瞬のノイズで「ビジー」と誤判定し、本来送るべきアナウンスを延々と見送ってしまうこともありました。
そこで、瞬間値ではなく一定時間ぶんの状態を見るようにしました。
# 一定時間サンプリングしてから判定
WINDOW = 1.0 # 秒
INTERVAL = 0.05 # サンプリング間隔
busy_count = 0
samples = 0
t_end = time.time() + WINDOW
while time.time() < t_end:
if read_busy() == HIGH:
busy_count += 1
samples += 1
time.sleep(INTERVAL)
busy_ratio = busy_count / samples
is_busy = busy_ratio > 0.3 # 3割以上HIGHならビジー扱い一定時間サンプリングして HIGH の割合で判定するようにしたところ、瞬間ノイズに振り回されることがほぼなくなりました。
「いつまで待つか」という別の問題
ビジーなら送信を見送る、までは良いのですが、ではその見送ったアナウンスをどうするかという判断が次に出てきました。
- 完全に破棄する(その回はアナウンスしない)
- 空くまで待って、空いた瞬間に送る
- 一定時間だけリトライして、それでもビジーなら諦める
時報のように「その時刻であること」に意味がある放送は、何分も遅れて流れると逆に違和感があります。最終的には、短いリトライ猶予(数十秒)を設けて、その間に空けば送る、空かなければその回は破棄する、という折衷にしました。
運用してみての感想
後付けでビジー検知を入れた結果、進行中の交信に被せる事故はなくなりました。最初から運用状況を見る設計にしておけば、と思わなくもないですが、固定時刻送信のシンプルさで一度動かしてしまったものを、現場の不都合に合わせて少しずつ直していく、というのは小規模な自作運用ではよくある流れだと思います。
瞬間値ではなく時間窓で見る、という判定の作り方は、GPIO まわりの他のデバッグでも繰り返し使うことになりました。その話は別記事にまとめています。
📝 執筆:JJ2TAB(尾張旭 DMR デジピーター 管理人)
🏛 JJ2YYK あいちデジタルコミュニケーションハムクラブ