Apakah ada algoritma untuk menghitung fase untuk frekuensi tunggal?

17

Jika Anda memiliki fungsi , dan referensi dosa gelombang dosa ( ω x ) apa yang akan menjadi algoritma cepat untuk menghitung ϕ ?f(t)=Asin(ωt+ϕ)sin(ωx)ϕ

Saya melihat algoritma Goertzel , tetapi sepertinya tidak berurusan dengan fase?

SamFisher83
sumber

Jawaban:

5

Gunakan DFT pada frekuensi tertentu. Kemudian hitung amplitudo dan fase dari bagian real / imag. Ini memberi Anda fase yang dirujuk ke awal waktu pengambilan sampel.

Dalam FFT 'normal' (atau DFT dihitung untuk semua harmonik N), Anda biasanya menghitung frekuensi dengan f = k * (sample_rate) / N, di mana k adalah bilangan bulat. Meskipun mungkin tampak asusila (terutama untuk anggota Gereja Wholly Integer), Anda sebenarnya dapat menggunakan nilai-nilai non-integer k ketika melakukan DFT tunggal.

Sebagai contoh, misalkan Anda telah menghasilkan (atau memperoleh) N = 256 poin dari gelombang sinus 27 Hz. (katakanlah, sample_rate = 200). Frekuensi 'normal' Anda untuk 256 titik FFT (atau N titik DFT) akan sesuai dengan: f = k * (sample_rate) / N = k * (200) / 256, di mana k adalah bilangan bulat. Tetapi 'k' non-integer dari 34,56 akan sesuai dengan frekuensi 27 Hz., Menggunakan parameter yang tercantum di atas. Ini seperti membuat 'bin' DFT yang persisnya berpusat pada frekuensi yang diinginkan (27 Hz.). Beberapa kode C ++ (kompilator DevC ++) mungkin terlihat sebagai berikut:

#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: Saya harap di atas diterjemahkan dengan baik untuk stackoverflow - beberapa mungkin membungkus)

Hasil di atas adalah fase -twopi / 4, seperti yang ditunjukkan pada titik nyata yang dihasilkan (dan amp digandakan untuk mencerminkan frekuensi pos / neg).

Beberapa hal yang perlu diperhatikan - saya menggunakan cosinus untuk menghasilkan bentuk gelombang tes dan menginterpretasikan hasil - Anda harus berhati-hati tentang itu - fase direferensikan ke waktu = 0, yaitu ketika Anda memulai pengambilan sampel (yaitu: ketika Anda mengumpulkan r [0] ), dan cosinus adalah interpretasi yang benar).

Kode di atas tidak elegan atau efisien (misalnya: gunakan tabel pencarian untuk nilai dosa / cos, dll.).

Hasil Anda akan menjadi lebih akurat karena Anda menggunakan N yang lebih besar, dan ada sedikit kesalahan karena fakta bahwa laju sampel dan N di atas tidak kelipatan satu sama lain.

Tentu saja, jika Anda ingin mengubah laju sampel, N, atau f, Anda harus mengubah kode dan nilai k. Anda dapat memasukkan nampan DFT ke mana saja di jalur frekuensi kontinu - pastikan Anda menggunakan nilai k yang sesuai dengan frekuensi yang diinginkan.

Kevin McGee
sumber
Pendekatan ini dapat ditingkatkan dengan menyesuaikan N untuk membuat k lebih dekat dengan keseluruhan. Saya memposting jawaban terpisah yang melanggar keakuratan algoritma ini.
mojuba
10

Masalahnya dapat dirumuskan sebagai masalah (nonlinier) kuadrat-terkecil:

F(ϕ)=12i=1n[Asin(ωi+ϕ)fi(ω)]2

F(ϕ)ϕ

Turunannya sangat sederhana:

F(ϕ)=i=1nAcos(ωi+ϕ)[Asin(ωi+ϕ)fi(ω)]

F(ϕ)

ϕ22πk,kN

Libor
sumber
2π
ϕ0
Menarik. Mungkinkah saya mengundang Anda untuk mengambil celah pada pertanyaan terkait ini ? :-)
Spacey
@Mohammad OK, saya telah berkontribusi sedikit di sana :)
Libor
Kemana fungsi fi (w) pergi? fi (w) bukan konstanta jadi ketika Anda mengambil turunan dari konstanta, bagaimana ia menjadi nol?
SamFisher83
5

Ada beberapa formulasi berbeda dari algoritma Goertzel. Yang menyediakan 2 variabel keadaan (ortogonal atau dekat dengan), atau variabel keadaan kompleks, karena keluaran yang mungkin sering dapat digunakan untuk menghitung atau memperkirakan fase dengan merujuk ke beberapa titik di jendela Goertzel, seperti tengah. Yang menyediakan output skalar tunggal saja biasanya tidak bisa.

Anda juga perlu tahu di mana jendela Goertzel Anda terkait dengan sumbu waktu Anda.

Jika sinyal Anda bukan bilangan bulat periodik di jendela Goertzel Anda, perkiraan fase di sekitar titik referensi di tengah jendela mungkin lebih akurat daripada merujuk fase ke awal atau akhir.

FFT penuh berlebihan jika Anda tahu frekuensi sinyal Anda. Plus Goertzel dapat disetel ke frekuensi yang tidak periodik dalam panjang FFT, sedangkan FFT akan membutuhkan interpolasi tambahan atau zero padding untuk frekuensi non-periodik-dalam-jendela.

Goertzel kompleks setara dengan 1 nampan DFT yang menggunakan pengulangan untuk vektor-vektor berbasis cosinus dan sinus atau faktor-faktor FFT.

hotpaw2
sumber
ωkkk=0
Tidak, karena menambahkan wk menghasilkan fase yang berbeda di ujung jendela daripada di awal untuk sinusoid non-integer-periodik-dalam-bukaan. Tapi DFT 1-bin menghitung fase sirkular tunggal pada titik yang sama. Dengan demikian 3 nilai semuanya akan berbeda. Tetapi fase tengah selalu terkait dengan rasio fungsi ganjil / genap, tidak peduli apa f0.
hotpaw2
Mencoba, tetapi saya tidak mengerti.
Olli Niemitalo
Gunakan cosinus (fase nol pada k = 0), sesuaikan frekuensi sedikit (dengan bilangan irasional kecil, tetapi tanpa mengubah fase pada k = 0). DFT melaporkan fase telah berubah! Coba hal yang sama dengan cosinus yang tepat berpusat di k = N / 2. Tidak ada perubahan pada k = N / 2 untuk setiap df. Sama untuk dosa atau campuran apa pun. Titik tengah titik referensi fase menunjukkan lebih sedikit perubahan dalam fase yang diukur dengan perubahan dalam f0. mis. frekuensi kesalahan tidak berkontribusi pada kesalahan pengukuran fase yang meningkat.
hotpaw2
1
Ya, kesalahan estimasi fase yang kurang di tengah jendela masuk akal jika sinusoid dan filter Goertzel berada pada frekuensi yang berbeda. Dalam hal ini, perkiraan fase mengatakan di ujung jendela bias oleh konstanta yang merupakan produk jarak antara pusat dan ujung jendela dan perbedaan antara frekuensi filter sinusoid dan Goertzel. Mengurangi bias ini memberikan kesalahan ukuran yang sama dengan estimasi pusat, tetapi membutuhkan mengetahui frekuensi sinusoid.
Olli Niemitalo
4

Jika sinyal Anda bebas noise, Anda dapat mengidentifikasi nol penyilangan di keduanya dan menentukan frekuensi dan fase relatif.

Juancho
sumber
3

ϕ

f(t)ωω

Begitu:

  • Apa yang Anda maksud dengan "puasa"?
  • Seberapa akurat Anda membutuhkan perkiraan?
  • ϕ
  • Berapa tingkat kebisingan pada setiap sinyal?

f(t)=Asin(ωt+ϕ)f(t)=Asin(ωx+ϕ)

Peter K.
sumber
2



F(t)
T=π/ω
I(ϕ)=0TF(t)dt =0.5Acos(ϕ)T
ϕ
cos(ϕ)=I(t)/(0.5AT)



ϕ0..(2π)

Untuk mengubah sinyal diskrit integral ke jumlah dan hati-hati memilih T!

SergV
sumber
1

Anda juga bisa melakukan ini (dalam notasi numpy):

np.arctan( (signal*cos).sum() / (signal*sin).sum() ))

di mana sinyal adalah sinyal fase-shifted Anda, cos dan sin adalah sinyal referensi, dan Anda menghasilkan perkiraan integral selama waktu tertentu melalui penjumlahan dari dua produk.

M529
sumber
0

Ini merupakan peningkatan atas saran @Kevin McGee untuk menggunakan DFT frekuensi tunggal dengan indeks bin fraksional. Algoritma Kevin tidak membuahkan hasil yang luar biasa: sementara di setengah bins dan seluruh bins sangat tepat, juga dekat dengan keseluruhan dan membagi dua itu juga cukup bagus, tetapi jika tidak error bisa dalam 5%, yang mungkin tidak dapat diterima untuk sebagian besar tugas .

NkN

Kode di bawah ini dalam Swift, tetapi harus jelas secara intuitif:

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))
Mojuba
sumber
FFT hanyalah cara untuk menghitung DFT secara efisien. Dengan perpustakaan modern, kekuatan dua pembatasan tidak lagi ada. Jika Anda hanya membutuhkan satu atau dua nilai nampan, maka lebih baik untuk menghitungnya secara langsung seperti sebelumnya. Untuk nada murni tunggal (nyata atau kompleks), hanya dua nilai nampan yang diperlukan untuk menghitung frekuensi, fase, dan amplitudo dengan tepat. Lihat dsprelated.com/showarticle/1284.php . Matematikanya cukup canggih, tetapi ada tautan ke artikel di mana derivasi dijelaskan. Aljabar Linier merupakan prasyarat untuk pemahaman yang benar.
Cedron Dawg