GoertzelアルゴリズムでDTMF検出
最初に、この翻訳は前半部分しかありません。後半の電子工作の話は、「今年こそArduinoとかやろぞ〜」といって数年経った人間ですので、訳語とかで迷惑をかけること必至で訳しませんでした。さて内容はプッシュ式電話機などのボタンを押したときに発せられるピ、ポ、パ音の周波数を検出するというもの。毎度ですが訳の精度は高くないので怪しい時は原文でご確認ください。おまけ、実際にコードを書いてみたい人はここを参考にされてはどうでしょう。
>>>それでは、はりきってどうぞ。
Goertzel algorithm DTMF Detection(日本語訳)
GoertzelアルゴリズムはDSPドメイン(訳注:時間ドメインや周波数ドメインなど使って解析する分野)でよくしられたものだ。それは周波数の存在やそれらがいくつあるのかを検出するのに使われる。少ない周波数を検出するつもりなら、DFT(FFT)のような他のテクニックに比べて実装するのに必要な計算が少なくてすむので、このアルゴリズムは人気がある。
わたしが今まで見たことがあるGoertzelアルゴリズムを実装しているプロジェクトをあげてみる:-
1.DTMF検出
2.ギターの弦のチューニング
3.オーディオ信号の特定の周波数を検出する
私のプロジェクトは、DTMFの周波数を検出するためにPIC18F4520とGoertzelアルゴリズムを使う。コードはすべてC言語で書かれている。 たとえ私のプロジェクトの実施が良好だとしても、それが商用アプリケーションで使用できることを保証するものではない。
Goertzelアルゴリズムを理解する
Goertzelアルゴリズムの理解にはz平面上で単位円の線図(下図参照)を使って行うことができる。
DTMF検出には普通、サンプルリング周波数は8000Hzにするように選択される。しかし、私はサンプリング周波数を5908Hzとして使用するよう選択した。この特定の値を選択した理由については後述する。
我々が単位円上の2つの共役極(効果的に複雑な項を削除し、現実世界のマイクロコントローラを使用して実装を可能にするための共役極)を持つシステムを想定した場合、そのようなシステムの出力は、その周波数で鋭いピークを持つ。
5908Hzのサンプリング周波数は、z平面の単位円上の2πラジアンに当てる。また、πを超える周波数はサンプリング周波数の2分の1より多くなり、したがって、その後のナイキストレートを超えて当てた、そこを考慮しなくていい。
今、我々はDTMF上に次の周波数を持っている:-
697Hz 770Hz 852Hz 941Hz 1209Hz 1336Hz 1477Hz
z平面上に697Hzを配置するために、我々は、サンプリング周波数でこれを正規化しよう:-
5908Hz ---> 2π だから 697Hz = (2π/5908) * 697 = 0.74126ラジアン
同様の変換に続いて、各周波数の値は次のとおりです:-
697Hz = 0.74126ラジアン 770Hz = 0.81889ラジアン 852Hz = 0.9061ラジアン 941Hz = 1.00713ラジアン 1209Hz = 1.28577ラジアン 1336Hz = 1.4208ラジアン 1477Hz = 1.57079ラジアン
我々は697Hzの周波数でのシステムのために2つの共役極、そして原点に2のゼロを配置する場合、z平面の図はこのようになる:-
だから、システムの伝達関数は次のようになる:-
私たちの周波数を一般化するために、項0.74126を"θ"に置き換えよう:-
次にオイラーの公式を使って代入
そして
等式は以下のようになる:-
"Z-2"で分子と分母を乗算:-
"Z-1"を1つ目の遅延として、そして"Z-2"を2つ目の遅延に代入:-
Input = Output - 2 * Cosθ * Prev_Output + Prev_Prev_Output Output = Input + 2 * Cosθ * Prev_Output - Prev_Prev_Output
上記の式は、Goertzelアルゴリズムの実装です。項(2 *cosθ)は"係数"と呼ばれ、各周波数に対して事前に計算されます: -
697Hz = 0.74126ラジアン = 1.4752 770Hz = 0.81889ラジアン = 1.3660 852Hz = 0.9061ラジアン = 1.2336 941Hz = 1.00713ラジアン = 1.0685 1209Hz = 1.28577ラジアン = 0.5623 1336Hz = 1.4208ラジアン = 0.2988 1477Hz = 1.57079ラジアン = 0
"1477Hz"の係数がゼロであることから確認できるように、基本的には1477Hzの検出時の計算を大幅に節約する。このような所以で私は5908Hzのサンプリング周波数を選択した。
これらの係数は、私のコードの"DTMF.h"ファイルに"#define"で定数として配置されている。 PICの計算をスピードアップするために、私は浮動小数点計算を避ける。したがって、それを整数に変換するために係数は256で乗算されている。後で、ほんとうの値を知るために結果を256で割る。この手で、私の計算は浮動小数点の実装よりも早い手法だといえる。
Goertzelの計算は、多くの取得したADC(デジタル変換回路)のサンプルで実行される。私は計算を実行するために100のサンプル数を選択している。これは周波数の検出分解能と処理時間のバランス。
計算は100のサンプルすべてに対して実行され、検出ピークの大きさは次のように計算される:-
Magnitude = Prev_Prev_Output 2 + Prev_Output2 - (Coefficient * Prev_Prev_Output * Prev_Output)
この大きさが閾値より>なら、その時、周波数が存在する。そうでなければ、周波数は存在しない。
if(magnitude > THRESHOLD) { // 周波数が存在する }