Bir işlev varsa , ve referans sinüs işlem hızlı algoritma olacağını ?
Goertzel algoritmasına bakıyordum , ama fazla ilgilenmiyor gibi görünüyor?
Bir işlev varsa , ve referans sinüs işlem hızlı algoritma olacağını ?
Goertzel algoritmasına bakıyordum , ama fazla ilgilenmiyor gibi görünüyor?
Yanıtlar:
Belirli bir frekansta bir DFT kullanın. Daha sonra gerçek / görüntü parçalarından genlik ve faz hesaplayın. Size örnekleme zamanının başlangıcına atıfta bulunulan fazı verir.
'Normal' bir FFT'de (veya tüm N harmonikleri için hesaplanan bir DFT'de) frekansı tipik olarak f = k * (örnek_ hızı) / N ile hesaplarsınız; burada k bir tamsayıdır. Her ne kadar kutsal görünebilir olsa da (özellikle Tamamen Tamsayı Kilisesi üyeleri için), tek bir DFT yaparken aslında tamsayı olmayan k değerlerini kullanabilirsiniz.
Örneğin, 27 Hz sinüs dalgasının N = 256 noktasını oluşturduğunuzu (veya kazandığınızı) varsayalım. (diyelim ki sample_rate = 200). 256 noktalı FFT (veya N noktası DFT) için 'normal' frekanslarınız aşağıdakilere karşılık gelir: f = k * (örnek_ hızı) / N = k * (200) / 256, burada k bir tamsayıdır. Fakat 34.56 tam sayı olmayan bir 'k', yukarıda listelenen parametreleri kullanarak 27 Hz'lik bir frekansa karşılık gelir. İlgilenilen frekansta (27 Hz.) Tam olarak merkezlenmiş bir DFT 'kutusu' oluşturmak gibidir. Bazı C ++ kodu (DevC ++ derleyicisi) aşağıdaki gibi görünebilir:
#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
(PS: Umarım yukarıdakiler stackoverflow'a iyi tercüme eder - bazıları etrafta dolanabilir)
Yukarıdakilerin sonucu, üretilen gerçek noktalarda gösterildiği gibi -twopi / 4'ün bir fazıdır (ve amp, negatif frekansı yansıtmak için amp iki katına çıkar).
Dikkat edilmesi gereken birkaç şey - Test dalga formunu oluşturmak ve sonuçları yorumlamak için kosinüs kullanıyorum - bu konuda dikkatli olmalısınız - faz, örneklemeye başladığınız zaman olan zaman = 0'a atıfta bulunur (yani: r [0] topladığınızda ) ve kosinüs doğru yorumdur).
Yukarıdaki kod ne zarif ne de etkilidir (ör. Sin / cos değerleri vb. İçin bir arama tabloları kullanın).
Daha büyük N kullandıkça sonuçlarınız daha doğru olacaktır ve yukarıdaki örnekleme hızı ve N'nin birbirinin katları olmaması nedeniyle küçük bir hata vardır.
Elbette, örnek oranınızı (N veya f) değiştirmek isterseniz, kodu ve k değerini değiştirmeniz gerekir. Bir DFT selesini sürekli frekans çizgisi üzerinde herhangi bir yere düşürebilirsiniz - sadece ilgili frekansa karşılık gelen bir k değeri kullandığınızdan emin olun.
Sorun, (doğrusal olmayan) en küçük kareler sorunu olarak formüle edilebilir:
Türev çok basit:
Açıkçası, yukarıdaki objektif fonksiyonun periyodiklik nedeniyle birden fazla minimasyonu vardır, bu nedenle diğer minimayı ayırt etmek için bir ceza süresi eklenebilir (örneğin, model denklemine eklenmesi ). Ancak, optimizasyonun sadece en yakın minima ile birleşeceğini düşünüyorum ve çıkararak sonucu güncelleyebilirsiniz .
Goertzel algoritmasının birkaç farklı formülasyonu vardır. Olası çıktılar olarak 2 durum değişkeni (dik veya yakın) veya karmaşık bir durum değişkeni sağlayanlar genellikle Goertzel penceresindeki orta gibi bir noktaya referansla fazı hesaplamak veya tahmin etmek için kullanılabilir. Tek başına tek bir skaler çıktı sağlayanlar genellikle yapamazlar.
Ayrıca Goertzel pencerenizin zaman ekseninize göre nerede olduğunu da bilmeniz gerekir.
Sinyaliniz Goertzel pencerenizde tam olarak periyodik değilse, pencerenin ortasındaki bir referans noktasının etrafındaki faz tahmini, fazı başlangıç veya bitişe göre daha doğru olabilir.
Sinyalinizin frekansını biliyorsanız, tam bir FFT aşırıya kaçar. Ayrıca bir Goertzel, FFT uzunluğunda periyodik olmayan bir frekansa ayarlanabilirken, FFT'nin penceresiz olmayan frekanslar için ek enterpolasyon veya sıfır dolguya ihtiyacı olacaktır.
Karmaşık bir Goertzel, kosinüs ve sinüs bazlı vektörler veya FFT twiddle faktörleri için bir nüks kullanan bir DFT'nin 1 bölmesine eşdeğerdir.
Bu, "hızlı" tanımınızın ne olduğuna, tahmininizin ne kadar doğru olmasını istediğinize, veya örneklemlerinizle ilgili faza bağlı olup olmadığınıza ve işlev ve referans sinüs dalgası üzerinde ne kadar gürültü olduğuna bağlıdır.
Bunu yapmanın bir yolu sadece nin FFT'sini almak ve en yakın bakmaktır . ω Ancak, bu çöp kutusu merkez frekansına yakın olmasına bağlı olacaktır .
Yani:
PS: yerine demek istediğinizi varsayıyorum .
Başlangıç noktası:
1) sinyalinizi ve referans sinüs dalgınızı çarpın: = A⋅sin (ωt + ϕ) ⋅sin (ωt) = 0.5⋅A⋅ (cos (ϕ) - cos (2⋅ωt + ϕ) )
2) dönemindeki integrali bul :
3) hesaplayabilirsiniz :
T = π / ω I ( ϕ ) = ∫ T 0 F ( t ) d t = 0.5 ⋅ A ⋅ c o s ( ϕ ) ⋅ T ϕ c o s ( ϕ ) = I ( t ) / ( 0,5 ⋅ A ⋅ T )
Bir düşünün:
A nasıl ölçülür?
belirlemek için olarak aralığı? ("referans cos dalgası" nı düşünün )0 .. ( 2 ⋅ π )
Ayrık sinyal için integrali toplamla değiştirin ve T'yi dikkatlice seçin!
Bu, Kevin McGee'nin kesirli bir çöp kutusu endeksi ile tek frekanslı DFT kullanma önerisindeki bir gelişmedir. Kevin'in algoritması harika sonuçlar vermez: yarım kutularda ve tüm kutularda çok hassas olsa da, wholes ve yarılara yakın da oldukça iyidir, ancak aksi takdirde hata% 5 içinde olabilir, bu muhtemelen çoğu görev için kabul edilemez .
Kevin'in algoritmasını , yani DFT penceresinin uzunluğunu ayarlayarak geliştirmeyi öneriyorum , böylece mümkün olduğunca bir bütüne yaklaşıyor. Bu, FFT'den farklı olarak çalışır, DFT, 2 gücü olmasını gerektirmez .
Aşağıdaki kod Swift'te, ancak sezgisel olarak açık olmalıdır:
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))