モールスリーダー Kraftwerk の Radioactivity を読む

Kraftwerk 好きの皆さんなら、モールス信号といえば、Radioactivity ですよね。はい実はあれのモールス信号が読みたくてこのプロジェクトをやったんですが。ちゃちゃっとはできなくて足元には深い沼が見え始めてます。ここまで使ってきたテスト信号はノイズが乗っているといってもせいぜい定常なホワイトノイズぐらい、それほど信号読み取りの邪魔にはなりませんでした。ですが Radioactivity は音楽です。この冒頭のところ、クリック音が入ってるし、コーラス風のコードが流れているし途中からベースも入ってくるし、モールス信号を拾うのは格段に難しそうです。

はいさて、冒頭からモールス部分までを切り出してきて、FFT 出力を見てみます。縦に見える軸が時間方向、横の軸が周波数方向、高さはパワーです。5 とか 6 とかあたりになんかそれらしいいパタンが見えてる?

ここでちょっとずるして、拾う周波数をプログラムに教えて、FFT 出力のうちその周波数成分だけ拾ってみましょう。本来この周波数も自動検出しないといけないんですけれども。

ん-なんか。2500 のあたりが - - - の O かな?そこからさかのぼって .. の I、 - .. の D、次が、.- の A がかろうじて見える?次が .-. の R?ええどこ?曲もよく聴くとモールスはフェードインしてくるので最初はノイズに埋もれがちなようです。これは読める気がしない。そもそも冒頭の「タッタッタッタッ」というガイガーカウンターを模した音をばっちり拾っていてこれは誤動作の種になりそうです。まずは、冒頭のクリック音の除去を試みてみましょう。この部分を拡大して

はっきり出ています。これはクリック音で、周波数領域に広がっているので、周波数方向に平たい信号をキャンセルするフィルタをかけてみてはどうでしょうか?具体的には FFT 出力をこんな風にすると

value = -0.1 * data[c - 2] - 0.3 * data[c - 1] + data[c] - 0.3 * data[c + 1] - 0.1 * data[c + 2]; 
Bash

c は中央周波数で、その両側にも信号が広がっている場合打ち消されてレベルが下がります。正弦波のように周波数領域でとがっていると、両側の信号は弱いので打ち消しが起こらずレベルは高いままです。結果はこのような感じ

勢いあまってマイナスまで行っちゃってますが良い感じです。マイナス値はただ無視すれば良いので問題ありません。これで行ってみましょう。ではモールス信号の冒頭はどうなっているでしょうか?

なんかこう、時間軸上に大暴れしています。まだ厳しいかな?少し時間軸方向を平滑してみましょう。具体的には単純に3フレーム分の移動平均を取りました。

value = (current_value + prev_value + prev_prev_value) / 3;
Bash

大分良くなったようには感じます。モールス信号の区間はこんな具合

D I O は何とかなりそうですが、R (.-.) と A (.-) は、目視しても言われなければそれとわからない気がします。特に R。目で見てわからないものはソフトではもっと厳しい気がします。耳で聴くとはっきりわかるんですが。聴覚の性能高すぎどうなってんの?色々試行錯誤しているうち、まさに錯誤してステレオ信号をモノラルとして入力してしまいました。その混じった信号は...ヒゲ的なノイズがありますけど全体的には形がよりはっきりしている?信号が立ち上がっている間に掘れる谷間がやや浅い気がします。理屈はわからないし思い切り乱暴ですけど、これなら拾えそうな気がしてきました。読み取りをしてみましょう。

結論から書くと、ただレベルだけで取るとうまく信号が拾えませんでした。そこで以下のようなルールを追加しました

  • 立ち上がり直後は信号を拾わない(チャタリング防止の感覚で)
  • レベルが下がってもゆっくり少しだけ下がる程度なら信号の立下りとして拾わない
  • レベルが高くても急激にレベルが低下したら信号の立下りとして拾う

この動作は何か、立ち上がりに鋭くて減衰部分は鈍くなる人間の聴覚とどことなく似ている気がします。

結果だいぶんうまく拾えています。R もきちんと拾えているのが驚きです。

これで OK と思いきや、この後ろで拾い間違いが見つかりました。

ここの信号はこのような割り振り。I (..) が明確に拾い間違っています。どうやら I と同じタイミングに別の音が混ざっている雰囲気です。ここの誤認識はパラメータを調整してもどうにもならず。

でもこうやってパタンを見ても耳で音を聴いてもここは明確に二音なのです。どうやっているのか?おそらく、後半に鋭いアタックが来ているのを視覚や聴覚は検知して、後付けで「振り返ってみるとこれは二音だった」と判断しているんじゃないかと疑われます(しらんけど。聴覚の仕組みなんて頭を開けてみてもわからない)

そういうわけで、後出しジャンケン機構を付けましょう。モールス信号の検出結果をすぐに出力せずに「まだ未確定」と保持します。もし「信号は上がっている」と判断中に鋭いアタックが入ったら、「誤認しました。今アタックが入ったので、その前の信号は下がっていました」と、出力の遅延時間が許す限り検出結果に修正をかけます。検出アルゴリズムがどんどん人間臭くなってきます。結果は

なんか危なっかしいですがうまく行きました。

よっし本番です行ってみましょう!

うっひょーできた!

ここからさらに中央周波数を自動検出するアルゴリズムを加える必要がありますが、もうちょっと勉強しないと厳しいかもしれません。というわけでこのプロジェクトは一旦ここで一区切りとします。

モールス信号の読み取りは機械学習の題材として取り上げられることがよくあるようです。人の手で打つからぶれがあること、周波数も速度も特に決まっていないので、読み取りながらそれらを検知しなくてはいけないこと、ノイズに埋もれがちであることなど、パタン認識の妨げになる問題が入っているので良いのでしょう。一方でモールス符号の仕様は非常に単純で、ITU-T の勧告としてのモールス信号の仕様書は数ページ分しかありません。なのでこのプロジェクトのように強引に機構的に読むのも可能だろうと考えていました。実際やってみると、可能は可能ですがやはり変動には弱そうで、それを補う機構を入れていくとどんどんあいまいさを許すアルゴリズムになって行き、柔軟なパタン認識手法として浸透しているニューラルネットワークと比較してみたいなと度々思いました。

このプロジェクトで書いたソースコードは github で公開しています。

https://github.com/naokiiwakami/morse-code-reader/tree/master

Comments

No comments yet. Why don’t you start the discussion?

コメントを残す

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

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください