Ottenere il valore di picco di un segnale se la sua frequenza si trova tra due centri bin


12

Si prega di supporre quanto segue:

  • La frequenza dei fondamentali di un segnale è stata stimata utilizzando FFT e alcuni metodi di stima della frequenza ed è compresa tra due centri bin
  • La frequenza di campionamento è fissa
  • Lo sforzo computazionale non è un problema

Conoscendo la frequenza, qual è il modo più accurato per stimare il corrispondente valore di picco dei segnali fondamentali?

Un modo potrebbe essere quello di azzerare il segnale orario per aumentare la risoluzione FFT in modo tale che il centro del contenitore sia più vicino alla frequenza stimata. In questo scenario, un punto di cui non sono sicuro è se posso zero-pad quanto voglio o se ci sono degli svantaggi nel farlo. Un altro è quale bin center dovrei selezionare dopo zero padding come quello da cui sto ottenendo il valore di picco (perché uno potrebbe non colpire esattamente la frequenza di interesse, anche dopo lo zero padding).

Tuttavia, mi chiedo anche se esiste un altro metodo che può fornire risultati migliori, ad esempio uno stimatore che utilizza i valori di picco dei due centri bin circostanti per stimare il valore di picco alla frequenza di interesse.


2
zero padding prima di FFT è a senso unico. Un altro è quello di applicare una funzione di finestra adatta per i tuoi nead. La finestra superiore piatta è stata progettata proprio per questo scopo. Naturalmente, se conosci già esattamente la frequenza e sei interessato a una sola ampiezza, probabilmente ci sono modi più economici per farlo rispetto a una FFT.
sellibitze,

1
nessuna imbottitura zero richiesta: semplice interpolazione parabolica (con 3 punti: imax-1, imax, imax + 1, dove si imaxtrova il picco FFT) ti darà risultati accurati
Basj

Assicurarsi che la funzione di interpolazione corrisponda alla funzione finestra. Il flat-top è banale, altrimenti si desidera una coppia corrispondente (ad es. Finestra rettangolare + interpolazione sinc, finestra gaussiana + interpolazione gaussiana ecc.)
Finlandia

@CedronDawg questa domanda e le sue risposte sono correlate (ma non uguali) alla tua esatta formula di frequenza. Magari lo trovi interessante.
Fat32,

Risposte:


5

Il primo algoritmo che mi viene in mente è l' algoritmo di Goertzel . Tale algoritmo di solito presuppone che la frequenza di interesse sia un multiplo intero della frequenza fondamentale. Tuttavia, questo documento applica l'algoritmo (generalizzato) al caso che ti interessa.


Un altro problema è che il modello del segnale non è corretto. Utilizza 2*%pi*(1:siglen)*(Fc/siglen). Dovrebbe essere usato 2*%pi*(0:siglen-1)*(Fc/siglen)per far uscire correttamente la fase.

Penso anche che ci sia un problema con la frequenza Fc=21.3molto bassa. I segnali a bassa frequenza con valori reali tendono a presentare distorsioni quando si tratta di problemi di stima di fase / frequenza.

Ho anche provato una ricerca di griglia approssimativa per la stima di fase, e dà la stessa risposta dell'algoritmo di Goertzel.

Di seguito è riportato un diagramma che mostra la distorsione in entrambe le stime (Goertzel: blu, grossolano: rosso) per due diverse frequenze: Fc=21.3(solido) e Fc=210.3(tratteggiato). Come puoi vedere, il bias per la frequenza più alta è molto meno.

L' asse trama è la fase iniziale che cambia da 0 a .2 πX2π

inserisci qui la descrizione dell'immagine


Ho appena testato il codice per l'algoritmo Goerzel basato sul documento. Utilizzando il valore DTFT in uscita, il picco può essere ottenuto in modo molto preciso. Tuttavia, esiste un fattore di ridimensionamento di esattamente 1000. Quindi, se il picco originale è 1.234, dopo Goerzel sarà 1234. Qualcuno sa da dove potrebbe provenire?
lR8n6i

Nel frattempo ho fatto delle ricerche. Probabilmente ha a che fare con il ridimensionamento dell'ampiezza: ridimensionamento dell'ampiezza del dominio del tempo = coefficiente del dominio della frequenza * 2 / N, dove N è la lunghezza del segnale. È giusto questo presupposto?
lR8n6i


Ciao! Ho appena scoperto che usando l'algoritmo Goertzel, l'ampiezza del coefficiente complesso risultante è molto accurata, ma la fase è completamente sbagliata. Qualcuno ha un'idea da dove potrebbe venire? Per "fase" intendo il ritardo di fase specificato nel fondamentale del segnale originale.
lR8n6i,

1
peccato(ω0t+φ)j2[e-jφδ~(ω+ω0+2πK)-e+jφδ~(ω-ω0+2πK)]π/2

4

Se si desidera utilizzare più contenitori FFT adiacenti, non solo 2, l'interpolazione Sinc con finestra tra i risultati del contenitore complesso può produrre una stima molto accurata, a seconda della larghezza della finestra.

L'interpolazione di Windowed Sinc si trova comunemente in upsamplers audio di alta qualità, quindi i documenti sull'argomento avranno formule di interpolazione adeguate con analisi degli errori.


Grazie per il commento. Proverò anche questo approccio.
lR8n6i

4

peccato(πX)(πX)

[1] JL Flanagan e RM Golden, "Phase vocoder", Bell Systems Technical Journal, vol. 45, pagg. 1493–1509, 1966.

[2] K. Dressler, "Estrazione sinusoidale usando un'implementazione efficiente di una FFT multi-risoluzione", in Proc. 9th Int. Conf. sugli effetti audio digitali (DAFx-06), Montreal, Canada, settembre 2006, pagg. 247–252.


Ciao! Grazie mille per tutti i tuoi commenti. Ho esteso il mio codice (vedi sotto) per combinare il filtro Goertzel con l'interpolazione del picco parabolico per ottenere la fase. Tuttavia, i risultati non sono ancora precisi (+ - 3-4 gradi). È il più vicino possibile o ci sono errori nella comprensione o nella codifica?
lR8n6i


3

Ho avuto molte difficoltà con questo esatto problema un paio di anni fa.

Ho pubblicato questa domanda:

/programming/4633203/extracting-precise-frequencies-from-fft-bins-using-phase-change-between-frames

Ho finito per fare i calcoli da zero e ho pubblicato una risposta alla mia domanda.

Sono sorpreso di non essere riuscito a trovare una simile esposizione su Internet.

Invierò nuovamente la risposta qui; nota che il codice è progettato per uno scenario in cui sto sovrapponendo la mia finestra FFT di 4x.

π


Questo puzzle richiede due chiavi per sbloccarlo.

Grafico 3.3:

inserisci qui la descrizione dell'immagine

Grafico 3.4:

inserisci qui la descrizione dell'immagine

Codice:

for (int k = 0; k <= fftFrameSize/2; k++) 
{
    // compute magnitude and phase 
    bins[k].mag = 2.*sqrt(fftBins[k].real*fftBins[k].real + fftBins[k].imag*fftBins[k].imag);
    bins[k].phase = atan2(fftBins[k].imag, fftBins[k].real);

    // Compute phase difference Δϕ fo bin[k]
    double deltaPhase;
    {
        double measuredPhaseDiff = bins[k].phase - gLastPhase[k];
        gLastPhase[k] = bins[k].phase;

        // Subtract expected phase difference <-- FIRST KEY
        // Think of a single wave in a 1024 float frame, with osamp = 4
        //   if the first sample catches it at phase = 0, the next will 
        //   catch it at pi/2 ie 1/4 * 2pi
        double binPhaseExpectedDiscrepancy = M_TWOPI * (double)k / (double)osamp;
        deltaPhase = measuredPhaseDiff - binPhaseExpectedDiscrepancy;

        // Wrap delta phase into [-Pi, Pi) interval 
        deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);
    }

    // say sampleRate = 40K samps/sec, fftFrameSize = 1024 samps in FFT giving bin[0] thru bin[512]
    // then bin[1] holds one whole wave in the frame, ie 44 waves in 1s ie 44Hz ie sampleRate / fftFrameSize
    double bin1Freq = (double)sampleRate / (double)fftFrameSize;
    bins[k].idealFreq = (double)k * bin1Freq;

    // Consider Δϕ for bin[k] between hops.
    // write as 2π / m.
    // so after m hops, Δϕ = 2π, ie 1 extra cycle has occurred   <-- SECOND KEY
    double m = M_TWOPI / deltaPhase;

    // so, m hops should have bin[k].idealFreq * t_mHops cycles.  plus this extra 1.
    // 
    // bin[k].idealFreq * t_mHops + 1 cycles in t_mHops seconds 
    //   => bins[k].actualFreq = bin[k].idealFreq + 1 / t_mHops
    double tFrame = fftFrameSize / sampleRate;
    double tHop = tFrame / osamp;
    double t_mHops = m * tHop;

    bins[k].freq = bins[k].idealFreq + 1. / t_mHops;
}

Stai interpolando la frequenza, mentre l'OP conosce la frequenza e vuole interpolare l'ampiezza.
Finnw,

2

Questo codice Python ti darà un risultato molto accurato (l'ho usato per molte note musicali e ho ottenuto errori inferiori allo 0,01% di semitono) con interpolazione parabolica (metodo usato con successo da McAulay Quatieri, Serra, ecc. In armonico + residuo tecniche di separazione)

import matplotlib.pyplot as plt
import numpy as np
from scipy.io.wavfile import read
from scipy.fftpack import fft, ifft
import math

(fs, x) = read('test.wav')
if (len(x.shape) == 2):    # if stereo we keep left channel only
 x = x[:,1]

n=x.size
freq = np.arange(n)*1.0/n*fs 
xfft = abs(fft(x))

imax=np.argmax(xfft)  
p=1.0/2*(xfft[imax-1]/xfft[imax]-xfft[imax+1]/xfft[imax])/(xfft[imax-1]/xfft[imax]-2+xfft[imax+1]/xfft[imax])   # parabolic interpolation 
print 'Frequence detectee avec interpolation parabolique :',(imax+p)*1.0/n*fs, 'Hz'

1
clear all
clc

for phase_orig = 0:pi/18:pi,

%% Specify and generate signal
Amp = 1;                     % Amplitude of signal
Fs = 8000;                   % samples per second
dt = 1/Fs;                   % seconds per sample
Fc = 21.3;                   % Hz
StopTime = 0.25;             % seconds
t = (0:dt:StopTime-dt)';     % seconds

siglen = length(t);
sig = Amp * 1.5 * sin(2*pi*(0:siglen-1)*(Fc/siglen) + phase_orig) + 1.5 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 3) ...
  + 1.5 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 5)+ 0.3 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 7) ...
  + 1.3 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 9)+ 1.4 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 11);

%% Estimate the peak value of the signals fundamental using Goertzel algorithm
peak = 0;
indvec = [Fc-1 Fc Fc+1];

% Check the input data
if ~isvector(sig) || isempty(sig)
  error('X must be a nonempty vector')
end

if ~isvector(indvec) || isempty(indvec)
  error('INDVEC must be a nonempty vector')
end
if ~isreal(indvec)
  error('INDVEC must contain real numbers')
end

% forcing x to be column
sig = reshape(sig,siglen,1);

% initialization
no_freq = length(indvec); %number of frequencies to compute
y = zeros(no_freq,1); %memory allocation for the output coefficients

% Computation via second-order system
% loop over the particular frequencies
for cnt_freq = 1:no_freq
  %for a single frequency:
  %a/ precompute the constants
  pik_term = 2*pi*(indvec(cnt_freq))/(siglen);
  cos_pik_term2 = cos(pik_term) * 2;
  cc = exp(-1i*pik_term); % complex constant
  %b/ state variables
  s0 = 0;
  s1 = 0;
  s2 = 0;
  %c/ 'main' loop
  for ind = 1:siglen-1 %number of iterations is (by one) less than the length of signal
    %new state
    s0 = sig(ind) + cos_pik_term2 * s1 - s2;  % (*)
    %shifting the state variables
    s2 = s1;
    s1 = s0;
  end
  %d/ final computations
  s0 = sig(siglen) + cos_pik_term2 * s1 - s2; %correspond to one extra performing of (*)
  y(cnt_freq) = s0 - s1*cc; %resultant complex coefficient

  %complex multiplication substituting the last iterationA
  %and correcting the phase for (potentially) non-integer valued
  %frequencies at the same time
  y(cnt_freq) = y(cnt_freq) * exp(-1i*pik_term*(siglen-1));
end

  % perfom amplitude scaling
  peak = abs(y(2)) * 2 / siglen

% perform parabolic interpolation to get the phase estimate
phase_orig=phase_orig*180/pi
ym1 = angle(unwrap(y(1)));
y0 = angle(unwrap(y(2)));
yp1 = angle(unwrap(y(3)));

p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1)); 
phase = y0 - 0.25*(ym1-yp1)*p;
phase_est = phase * 180/pi + 90;
phase_est = mod(phase_est+180,360)-180
end

Le frequenze con cui hai a che fare (21.3Hz campionati a 8kHz) sono molto basse. Poiché si tratta di segnali a valore reale, esibiranno una distorsione nella stima di fase per ** qualsiasi ** frequenza.

Questa immagine mostra un diagramma del bias ( phase_est - phase_orig) per Fc = 210.3;(in rosso) rispetto al bias per Fc = 21.3;. Come puoi vedere, l'offset è molto più significativo per il 21.3caso.

Un'altra opzione è quella di ridurre la frequenza di campionamento. La curva verde mostra il bias per Fs = 800anziché 8000.

inserisci qui la descrizione dell'immagine


1
Grazie per l'aggiornamento! Vedi la mia trama; Penso ancora che qualsiasi stimatore di fase avrà una propensione per questa frequenza bassa. Un modo per aggirare il problema consiste nell'utilizzare la frequenza nota (se è nota!) Per correggere il bias della stima di fase attraverso una tabella di ricerca. Ma dovrai stare attento: il bias cambierà con la frequenza. Un altro modo per farlo sarà ridurre la frequenza di campionamento.
Peter K.

1
Grazie anche a te! Tuttavia, se si utilizza Fs = 8000 Hz e Fc = 210 anziché 210,3, il bias sembra ancora peggiore. Qualche idea da dove potrebbe venire?
lR8n6i

1
Erk! Nessuna idea. FWIW, lo stimatore Geortzel non ha problemi: goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;. :-) Scaverà un po 'di più. Guarda questo spazio.
Peter K.

1
L'interpolazione parabolica non sta facendo quello che pensi stia facendo. In particolare, se si sostituisce il calcolo di pcon, p2 = (abs(y(3)) - abs(y(1)))/(2*(2*abs(y(2)) - abs(y(3)) - abs(y(1)))); phase2 = y0 - 0.25*(ym1-yp1)*p2;si ottengono MOLTE risposte migliori --- anche per Fc=210. Non sono affatto sicuro che l'attuale versione di pti darà qualcosa di sensato. La formula di interpolazione è per l'interpolazione dell'AMPLITUDINE di una parabola, ma psta interpolando la fase che è solo ... dispari.
Peter K.

1
Tutto ciò è OK, TRANNE che la posizione di picco ( p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))) sarà errata qualche volta se si utilizza PHASES anziché le ampiezze. Questo perché le fasi possono saltare attorno al limite di +/- 180 gradi. Tutto ciò che serve per risolverlo per la fase è cambiare quella linea con il mio p2calcolo sopra.
Peter K.
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.