私が見つけた 1 つのアプローチは、90 度離れた 2 つの基準波 (私はそれらを「正弦波」と「余弦波」と呼んでいます) を生成し、入力波形とこれらの基準波の内積をかなり短い間 (たとえば 1 /60 秒) 入力のストレッチ。これにより、参照波に関して同相または逆相である入力周波数の量のややノイズの多い指標が得られます(2つの参照波を使用して生成された値の2乗和の平方根は、振幅です)。ウィンドウ サイズが小さいと、出力にノイズが多いことがわかりますが、単純な FIR または IIR フィルターなどで出力をフィルター処理すると、かなり合理的な結果が得られるはずです。
優れたトリックの 1 つは、2 つの振幅数を生成することです。最初の数値については、正弦波と余弦波の振幅を 2 回のフィルター処理で実行し、平方和を計算します。2 つ目は、1 ラウンドのフィルタリングで振幅を実行し、次に平方和を計算してから、別のラウンドのフィルタリングで実行します。
どちらの振幅測定でも同じ遅延が発生しますが、最初のものは 2 番目のものよりもはるかに選択的です。したがって、周波数が「正しい」か「少しずれている」かを非常に明確に判断できます。このアプローチを使用すると、DTMF トーンをすばやく検出しながら、数 Hz ずれているトーンも拒否できます (オフピッチ トーンは、タイトな検出器よりも「ルーズな」検出器ではるかに強くピックアップされます)。
サンプルコード:
ダブル sine_phase,sine_freq;
void process_some_waves(double *input, int16 len,
double *sine_phase、double sine_freq、
double *sine_result, double *cosine_result)
{
int i;
二相、sin_tot、cos_tot;
位相 = *sine_phase;
sin_tot = cos_tot = 0;
for (i=0; 長さ > i; i++)
{
sin_tot += 入力[i] * sin(フェーズ);
cos_tot += 入力[i] * cos(フェーズ);
位相 += sine_freq;
}
*sine_result = sin_tot;
*cosine_result = cos_tot;
*sine_phase = 位相;
}
/* バッファ内の最初の要素を取得し、単純なガウス応答でバッファを「塗りつぶす」。*/
void simple_fir_filter(double *buff, int buffsize)
{
int i;
for (i=buffsize-1; i>=2; i--)
buff[i] = (buff[i-1] + buff[i-2])/2;
}
#define FILTER_SIZE1 10
#define FILTER_SIZE2 8
#SECTION_LENGTH 128 を定義
#なんでも FREQ を定義
double sine_buff1[FILTER_SIZE1], sine_buff2[FILTER_SIZE2];
double cos_buff1[FILTER_SIZE1], cos_buff2[FILTER_SIZE2];
double combined_buff[FILTER_SIZE2];
ダブル tight_amplitude、loose_amplitude;
ダブルref_phase;
void handle_some_data(double *input)
{
/* フィルター バッファーの最初の要素に結果を格納します */
process_some_waves(input, SECTION_LENGTH, &ref_phase, FREQ, sine_buff1, cos_buff1);
/* フィルタリングの最初の段階を実行します */
simple_fir_filter(sine_buff1, FILTER_SIZE1);
simple_fir_filter(cosine_buff1, FILTER_SIZE1);
/* 各配列の最後の要素は、フィルタリングの結果を保持します。*/
/* 第 2 段階を実行します */
sine_buff2[0] = sine_buff1[FILTER_SIZE1-1];
cosine_buff2[0] = cosine_buff1[FILTER_SIZE1-1];
結合された_buff[0] = sine_buff2[0]*sine_buff2[0] + cosine_buff2[0]*cosine_buff2[0];
simple_fir_filter(sine_buff2, FILTER_SIZE2);
simple_fir_filter(cosine_buff2, FILTER_SIZE2);
simple_fir_filter(combined_buff, FILTER_SIZE2);
タイトな振幅 = sine_buff2[FILTER_SIZE2-1]*sine_buff2[FILTER_SIZE2-1] +
cosine_buff2[FILTER_SIZE2-1]*cosine_buff2[FILTER_SIZE2-1];
Loose_amplitude = combined_buff2[FILTER_SIZE2-1];
}
ここのコードでは、配列の添え字以外のすべての計算に「double」を使用しています。実際には、数学の一部を整数演算に置き換える方がほぼ確実に高速です。浮動小数点を使用するマシンでは、位相を 32 ビット整数として保持し、〜 4096 の「単一」サイン値のテーブルを使用するのが最善の方法だと思います (RAM 内のテーブル サイズが小さいほど、キャッシュの一貫性が向上します)。パフォーマンス)。上記のようなコードを固定小数点 (整数) DSP で使用すると、大きな成功を収めました。process_some_waves の正弦計算と余弦計算は別々の「ループ」で行われ、各「ループ」は「繰り返し」プレフィックスを持つ単一の命令として実現されました。