というのをちょっと前に作ったけど日記に書いていなかった。
デモ (音アリじゃないとよくわからない):
- 音ナシ: http://lowreal.net/2013/Morse/decoder.html#dummy
- 音アリ: http://lowreal.net/2013/Morse/decoder.html#dummy-output
デフォルトだと、信号がありそうなところを適当に追跡してデコードする。上のスペクトラムをクリックで、その周辺の周波数領域のデコードだけをするようになる。一度に1つのデコーダーだけが動く。
何もハッシュをつけない場合マイク入力からになる。あと Chrome でしか見てない。
Web Audio で信号処理
Web Audio を使って、マイク入力を信号処理しようと思うといくつか躓くところがあった。
- サンプリング周波数を指定できない
- AnalyserNode を任意のサンプリングタイミングで呼ぶことができない?
- あとサンプリング周波数が指定できないので分解能に限界がある
モールスのデコードに高いサンプリング周波数は必要ない。しかしサンプリング周波数は Web Audio 側で固定になっているので、自力でダウンサンプリングしている。これは ScriptProcessorNode の onaudioprocess を使い、Float32Array にリングバッファ状に落としこんでる。なんかもっといい方法ありそうだけど、わからなかった。
まだ onaudioprocess の挙動が不安定で、データがこなくなったりすることがある。毎回 onaudioprocess に対してコールバックを代入しなおしたりいろいろやったけど、最近直ったような気がしないでもない。
FFT も AnalyserNode のを使うのではなく、このダウンサンプリングした信号に対し、JS レベルで実行してる。これは dsp.js を使ってる。
モールスデコード部
それなりに工夫して作った。最初のころ Description of RSCW's algorithms というのを見つけてよく読んでみたけどよくわからないことも多くて、僕でも実装できる程度に落としこんで結局以下のようになってる。
- 適当にデコードしたい周波数を決める
- 直近で信号強度が強い周波数
- または手動 (スペクトラムをクリック)
- その周波数に対し、2位相ロックインアンプ相当の処理をする
- 本当に単純にその周波数の矩形波を90°ずらして合成してローパスフィルタにかけて、、というのをナイーブにやってる
- 適当に閾値を決めて2値化する (むずかしい……)
- ここまでで 0/1 になる
- 0 の連続または 1 の連続のうち、最小の長さを見つける (モールスの符号単位)
- 符号単位の2倍以上なら長点、そうでなければ短点として表から符号をデコードする
デモのようなホワイトノイズ + それなりの強さの信号でかつ、機械的に綺麗な符号なら、結構いい感じにデコードできるけど、実際の交信だと思ったより厳しい。
- SN比がもっと悪い。フェージングで信号強度がよく変化する
- 符号が綺麗なことが少ない
なので、なんらかの統計的な、機械学習のような要素を入れこんで (隠れマルコフモデルとか?) やりたいけど、そのような技術力がない。あと、別に全域常時 FFT して全チャンネル同時デコードとかも、ギジュツリョクがあればできるだろうけど、できてない。