関数と参照正弦波がある場合、を計算するための高速アルゴリズムは何でしょうか?罪(ω X )φ
Goertzelアルゴリズムを見ていましたが、フェーズを処理していないようです。
関数と参照正弦波がある場合、を計算するための高速アルゴリズムは何でしょうか?罪(ω X )φ
Goertzelアルゴリズムを見ていましたが、フェーズを処理していないようです。
回答:
特定の周波数でDFTを使用します。次に、実数/画像部分から振幅と位相を計算します。これにより、サンプリング時間の開始を基準とした位相が得られます。
「通常の」FFT(またはN個のすべての高調波に対して計算されたDFT)では、通常、f = k *(sample_rate)/ Nで周波数を計算します。ここで、kは整数です。(特に教会のWholly Integerのメンバーにとっては)見苦しいように見えるかもしれませんが、実際には、単一のDFTを行うときにkの非整数値を使用できます。
たとえば、27 Hzの正弦波のN = 256ポイントを生成(または取得)したとします。(たとえば、sample_rate = 200)。256ポイントFFT(またはNポイントDFT)の「通常の」周波数は、f = k *(sample_rate)/ N = k *(200)/ 256に対応します。ここで、kは整数です。ただし、34.56の非整数「k」は、上記のパラメーターを使用して、27 Hzの周波数に対応します。これは、対象の周波数(27 Hz)を正確に中心とするDFTの「ビン」を作成するようなものです。一部のC ++コード(DevC ++コンパイラ)は次のようになります。
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
// arguments in main needed for Dev-C++ I/O
int main (int nNumberofArgs, char* pszArgs[ ] ) {
const long N = 256 ;
double sample_rate = 200., amp, phase, t, C, S, twopi = 6.2831853071795865;
double r[N] = {0.}, i[N] = {0.}, R = 0., I = 0. ;
long n ;
// k need not be integer
double k = 34.56;
// generate real points
for (n = 0; n < N; n++) {
t = n/sample_rate;
r[n] = 10.*cos(twopi*27.*t - twopi/4.);
} // end for
// compute one DFT
for (n = 0; n < N; n++) {
C = cos(twopi*n*k/N); S = sin(twopi*n*k/N);
R = R + r[n]*C + i[n]*S;
I = I + i[n]*C - r[n]*S;
} // end for
cout<<"\n\ndft results for N = " << N << "\n";
cout<<"\nindex k real imaginary amplitude phase\n";
amp = 2*sqrt( (R/N)*(R/N) + (I/N)*(I/N) ) ;
phase = atan2( I, R ) ;
// printed R and I are scaled
printf("%4.2f\t%11.8f\t%11.8f\t%11.8f\t%11.8f\n",k,R/N,I/N,amp,phase);
cout << "\n\n";
system ("PAUSE");
return 0;
} // end main
//**** end program
(追記:上記がStackoverflowにうまく変換されることを願っています–一部はラップアラウンドするかもしれません)
上記の結果は、生成された実際のポイントに示されているように-twopi / 4の位相になります(およびアンプは、pos / neg周波数を反映するために2倍になります)。
注意点–コサインを使用してテスト波形を生成し、結果を解釈します–注意する必要があります–位相は、サンプリングを開始したとき(つまり、r [0]を収集したとき)に参照されます。 )、コサインが正しい解釈です)。
上記のコードはエレガントでも効率的でもありません(例:sin / cos値にルックアップテーブルを使用するなど)。
より大きなNを使用すると、結果はより正確になります。サンプルレートと上記のNは互いに倍数ではないため、少し誤差があります。
もちろん、サンプルレート、N、またはfを変更する場合は、コードとkの値を変更する必要があります。DFTビンは、連続周波数ライン上のどこにでもプランクできます。目的の周波数に対応するkの値を使用していることを確認してください。
問題は(非線形)最小二乗問題として定式化できます。
ここで、に対して最小限にする目的関数ですφ。
導関数は非常に単純です:
明らかに、上記の目的関数は周期性のために複数の最小値を持っているため、他の最小値を区別するためにペナルティ項を追加できます(たとえば、モデル方程式にを追加します)。しかし、最適化は最も近い最小値に収束するだけで、減算した結果を更新できます。 2 πのK 、K ∈ N
Goertzelアルゴリズムには、いくつかの異なる公式があります。多くの場合、可能な出力として2つの状態変数(直交または近似)または複素状態変数を提供するものを使用して、中央などのGoertzelウィンドウ内のある点を参照して位相を計算または推定できます。通常、単一のスカラー出力のみを提供するものはできません。
また、Goertzelウィンドウが時間軸との関係でどこにあるかを知る必要があります。
信号がGoertzelウィンドウで正確に整数周期的でない場合、ウィンドウの中央の基準点の周りの位相推定は、位相を最初または最後に参照するよりも正確になる可能性があります。
信号の周波数がわかっている場合、完全なFFTはやりすぎです。さらに、GoertzelはFFT長で周期的でない周波数に調整できますが、FFTは非周期的なウィンドウ周波数に対して追加の補間またはゼロパディングを必要とします。
複雑なGoertzelは、コサインとサインの基底ベクトルまたはFFT回転因子に再帰を使用するDFTの1ビンに相当します。
これは、フラクショナルビンインデックスで単一周波数DFTを使用するという@Kevin McGeeの提案を改善したものです。ケビンのアルゴリズムは素晴らしい結果を生み出しません:ハーフビンとホールビンは非常に正確ですが、全体と半分に近く、それもかなり良いですが、それ以外の場合、エラーは5%以内になる可能性があり、ほとんどのタスクで許容できない可能性があります。
以下のコードはSwiftにありますが、直感的に明確でなければなりません。
let f = 27.0 // frequency of the sinusoid we are going to generate
let S = 200.0 // sampling rate
let Nmax = 512 // max DFT window length
let twopi = 2 * Double.pi
// First, calculate k for Nmax, and then round it
var k = round(f * Double(Nmax) / S)
// The magic part: recalculate N to make k as close to whole as possible
// We also need to recalculate k once again due to rounding of N. This is important.
let N = Int(k * S / f)
k = f * Double(N) / S
// Generate the sinusoid
var r: [Double] = []
for i in 0..<N {
let t = Double(i) / S
r.append(sin(twopi * f * t))
}
// Compute single-frequency DFT
var R = 0.0, I = 0.0
let twopikn = twopi * k / Double(N)
for i in 0..<N {
let x = Double(i) * twopikn
R += r[i] * cos(x)
I += r[i] * sin(x)
}
R /= Double(N)
I /= Double(N)
let amp = 2 * sqrt(R * R + I * I)
let phase = atan2(I, R) / twopi
print(String(format: "k = %.2f R = %.8f I = %.8f A = %.8f φ/2π = %.8f", k, R, I, amp, phase))