Esiste un algoritmo per calcolare la fase per una singola frequenza?


17

Se hai una funzione f(t)=Asin(ωt+ϕ) e riferimento sin wave sin(ωx) quale sarebbe un algoritmo veloce da calcolare ϕ ?

Stavo guardando l' algoritmo di Goertzel , ma non sembra affrontare la fase?

Risposte:


5

Utilizzare un DFT alla frequenza specifica. Quindi calcola l'ampiezza e la fase dalle parti real / imag. Ti dà la fase riferita all'inizio del tempo di campionamento.

In una FFT "normale" (o una DFT calcolata per tutte le armoniche N), in genere si calcola la frequenza con f = k * (frequenza_campione) / N, dove k è un numero intero. Sebbene possa sembrare sacrilego (specialmente ai membri della Chiesa del Numero intero), puoi effettivamente utilizzare valori non interi di k quando esegui un singolo DFT.

Ad esempio, supponiamo di aver generato (o ottenuto) N = 256 punti di un'onda sinusoidale di 27 Hz. (diciamo, sample_rate = 200). Le frequenze "normali" per un FFT a 256 punti (o N punto DFT) corrisponderebbero a: f = k * (sample_rate) / N = k * (200) / 256, dove k è un numero intero. Ma una "k" non intera di 34,56 corrisponderebbe a una frequenza di 27 Hz., Utilizzando i parametri sopra elencati. È come creare un 'bin' DFT esattamente centrato sulla frequenza di interesse (27 Hz.). Alcuni codici C ++ (compilatore DevC ++) potrebbero apparire come segue:

#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: spero che quanto sopra si traduca bene in StackOverflow - alcuni potrebbero andare in giro)

Il risultato di quanto sopra è una fase di -twopi / 4, come mostrato nei punti reali generati (e l'amplificatore viene raddoppiato per riflettere la frequenza pos / neg).

Alcune cose da notare - Uso il coseno per generare la forma d'onda di prova e interpretare i risultati - Devi stare attento a questo - la fase è riferita al tempo = 0, che è quando hai iniziato il campionamento (cioè: quando hai raccolto r [0] ) e il coseno è l'interpretazione corretta).

Il codice sopra riportato non è né elegante né efficiente (ad esempio: utilizzare una tabella di ricerca per i valori sin / cos, ecc.).

I tuoi risultati diventeranno più precisi quando usi N più grande e c'è un po 'di errore dovuto al fatto che la frequenza di campionamento e N sopra non sono multipli l'una dell'altra.

Naturalmente, se si desidera modificare la frequenza di campionamento, N o f, è necessario modificare il codice e il valore di k. Puoi inserire un bin DFT in qualsiasi punto della linea di frequenza continua: assicurati di utilizzare un valore di k che corrisponda alla frequenza di interesse.


Questo approccio può essere migliorato regolando N per rendere k più vicino al tutto. Ho pubblicato una risposta separata che imprime l'accuratezza di questo algoritmo.
Mojuba,

10

Il problema può essere formulato come problema dei minimi quadrati (non lineari):

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

F(ϕ)ϕ

La derivata è molto semplice:

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

F(ϕ)

Ovviamente, la funzione obiettivo sopra ha più minimi a causa della periodicità, quindi un certo termine di penalità può essere aggiunto per discriminare altri minimi (ad esempio, aggiungendo all'equazione del modello). Ma penso che l'ottimizzazione sarà solo convergere i minimi più vicino e si può aggiornare il risultato sottraendo . 2 π k , k Nϕ22πk,kN


Non penso che devi penalizzare a causa della periodicità no? Puoi semplicemente prendere qualsiasi minimo nello spazio delle fasi a cui converge e fare un modulu , no? 2π
Spacey,

@Mohammad Sì, ma alcune tecniche di ottimizzazione possono utilizzare più punti di partenza che dovrebbero convergere allo stesso valore o assumere una funzione convessa con un minimizzatore globale singolo che può essere approssimato bene con un quadratico. L'altro vantaggio è che finiamo con lo stesso risultato per qualsiasi punto di partenza . ϕ0
Libor,

Interessante. Potrei invitarti a fare anche una pausa a questa domanda correlata ? :-)
Spacey,

@Mohammad OK, ho contribuito un po 'lì :)
Libor

Dove va la funzione fi (w)? fi (w) non è una costante, quindi quando prendi una derivata di una non costante come diventa zero?
SamFisher83,

5

Esistono diverse formulazioni dell'algoritmo di Goertzel. Quelle che forniscono 2 variabili di stato (ortogonali o vicine), o una variabile di stato complessa, come possibili uscite spesso possono essere utilizzate per calcolare o stimare la fase con riferimento a un punto della finestra di Goertzel, come il centro. Quelli che forniscono un singolo output scalare da soli di solito non possono.

Dovrai anche sapere dove si trova la finestra di Goertzel in relazione al tuo asse temporale.

Se il segnale non è esattamente periodico intero nella finestra di Goertzel, la stima della fase attorno a un punto di riferimento al centro della finestra potrebbe essere più accurata della fase di riferimento all'inizio o alla fine.

Un FFT completo è eccessivo se si conosce la frequenza del segnale. Inoltre un Goertzel può essere sintonizzato su una frequenza non periodica nella lunghezza FFT, mentre un FFT avrà bisogno di interpolazione aggiuntiva o riempimento di zero per frequenze non periodiche nella finestra.

Un Goertzel complesso equivale a 1 bin di un DFT che utilizza una ricorrenza per i vettori di coseno e seno base o i fattori di twiddle FFT.


La stima di fase all'interno della finestra non ha esattamente la stessa accuratezza, perché aggiungerebbe alla stima di fase all'inizio della finestra per calcolare la stima di fase nel campione all'interno della finestra ( essendo l'inizio della finestra)? k k = 0ωkkk=0
Olli Niemitalo

No, perché l'aggiunta di wk provoca una fase diversa alla fine della finestra rispetto all'inizio per una sinusoide non intera-periodica in-apertura. Ma un DFT a 1 bin calcola una singola fase circolare nello stesso punto. Quindi i 3 valori saranno tutti diversi. Ma la fase centrale è sempre correlata al rapporto della funzione pari / dispari, non importa quale f0.
hotpaw2,

Sto provando, ma non capisco.
Olli Niemitalo

Usa un coseno (fase zero a k = 0), modifica leggermente la frequenza (con un piccolo numero irrazionale, ma senza cambiare la fase a k = 0). Un DFT segnala che la fase è cambiata! Prova lo stesso con un coseno esattamente centrato su k = N / 2. Nessuna modifica a k = N / 2 per qualsiasi df. Lo stesso per il peccato o qualsiasi mix. La centratura del punto di riferimento di fase mostra meno cambiamenti nella fase misurata con cambiamenti in f0. ad es. l'errore di frequenza non contribuisce ad aumentare gli errori di misurazione di fase.
hotpaw2,

1
Sì, l'errore di stima della fase essendo inferiore al centro della finestra ha senso se la sinusoide e il filtro Goertzel sono a frequenze diverse. In tal caso, la stima di fase che dice alla fine della finestra è distorta da una costante che è il prodotto della distanza tra il centro e la fine della finestra e la differenza tra le frequenze del filtro sinusoide e Goertzel. Sottraendo questo bias si ottiene lo stesso errore di dimensione della stima centrale, ma è necessario conoscere la frequenza della sinusoide.
Olli Niemitalo

4

Se i segnali sono privi di rumore, è possibile identificare zero incroci in entrambi e determinare la frequenza e la fase relativa.


3

Dipende da quale sia la tua definizione di "veloce", quanto preciso vuoi la tua stima, se vuoi o la fase relativa ai tuoi campionamenti, e quanto rumore c'è sulla tua funzione e sull'onda sinusoidale di riferimento.ϕ

Un modo per farlo è semplicemente prendere la FFT di e guardare il cestino più vicino a . ωf(t)ω Tuttavia, ciò dipenderà dal fatto che sia vicino alla frequenza centrale del contenitore.ω

Così:

  • Cosa intendi con "veloce"?
  • Quanto preciso hai bisogno del preventivo?
  • Vuoi (fase relativa al riferimento) o fase relativa all'inizio del campionamento? Importa?ϕ
  • Qual è il livello di rumore su ciascun segnale?

PS: che intendevi , piuttosto che .f(t)=Asin(ωt+ϕ)f(t)=Asin(ωx+ϕ)


2

Punto iniziale:
1) moltiplica il segnale e l'onda sin di riferimento: = A⋅sin (ωt + ϕ) ⋅sin (ωt) = 0.5⋅A⋅ (cos (ϕ) - cos (2⋅ωt + ϕ) ) 2) trova integrale sul periodo : 3) puoi calcolare :
F(t)
T=π/ω
I(ϕ)=0TF(t)dt =0.5Acos(ϕ)T
ϕ
cos(ϕ)=I(t)/(0.5AT)

Pensa a:
come misurare A?
come determinare nell'intervallo ? (pensa a "riferimento cos wave")ϕ0..(2π)

Per un segnale discreto, cambiare l'integrale da sommare e scegliere attentamente T!


1

Potresti anche farlo (in notazione numpy):

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

dove il segnale è il segnale sfasato, cos e sin sono i segnali di riferimento e si genera un'approssimazione di un integrale per un certo tempo sommando i due prodotti.


0

Questo è un miglioramento rispetto al suggerimento di @Kevin McGee di utilizzare un DFT a singola frequenza con un indice bin binario frazionario. L'algoritmo di Kevin non produce grandi risultati: mentre a metà bin e interi bin è molto preciso, anche vicino agli interi e alle metà è anche abbastanza buono, ma altrimenti l'errore può essere all'interno del 5%, il che probabilmente non è accettabile per la maggior parte delle attività .

Suggerisco di migliorare l'algoritmo di Kevin regolando , cioè la lunghezza della finestra DFT in modo che si avvicini il più possibile all'intero. Questo funziona poiché, diversamente da FFT, DFT non richiede che abbia una potenza di 2.NkN

Il codice seguente è in Swift, ma dovrebbe essere intuitivamente chiaro:

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))

La FFT è semplicemente un modo per calcolare in modo efficiente una DFT. Con le biblioteche moderne, il potere di due restrizioni non è più lì. Se hai bisogno solo di uno o due valori bin, allora è meglio calcolarli direttamente come hai fatto tu. Per un singolo tono puro (reale o complesso), sono necessari solo due valori bin per calcolare esattamente la frequenza, la fase e l'ampiezza. Vedi dsprelated.com/showarticle/1284.php . La matematica è piuttosto sofisticata, ma ci sono collegamenti agli articoli in cui sono spiegate le derivazioni. L'algebra lineare è un prerequisito per una vera comprensione.
Cedron Dawg,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.