Come posso tracciare manualmente la risposta in frequenza di un filtro Butterworth passa-banda in MATLAB senza la funzione freqz?


15

Ho un codice come quello sotto che applica un filtro passa-banda su un segnale. Sono un po 'rumoroso in DSP e voglio capire cosa sta succedendo dietro le quinte prima di procedere.

Per fare questo, voglio sapere come tracciare la risposta in frequenza del filtro senza usare freqz.

[b, a] = butter(order, [flo fhi]);
filtered_signal = filter(b, a, unfiltered_signal)

Dati gli output [b, a]come lo farei? Sembra che sarebbe un compito semplice, ma faccio fatica a trovare quello che mi serve nella documentazione o online.

Mi piacerebbe anche capire come farlo il più rapidamente possibile, ad esempio usando un ffto un altro algoritmo veloce.

Risposte:


25

Sappiamo che in generale la funzione di trasferimento di un filtro è data da:

H(z)=k=0Mbkzkk=0Nakzk

Ora sostituisci z=ejω per valutare la funzione di trasferimento sul cerchio unitario:

H(ejω)=k=0Mbkejωkk=0Nakejωk

Quindi questo diventa solo un problema di valutazione polinomiale in un dato ω . Ecco i passaggi:

  • Crea un vettore di frequenze angolari ω=[0,,π] per la prima metà dello spettro (non è necessario salire fino a 2π ) e salvalo in w.
  • Esponente pre-calcolo ejω in tutti e memorizzarlo in variabile ze.
  • Utilizzare la polyvalfunzione per calcolare i valori di numeratore e denominatore chiamando polyval(b, ze):, dividerli e archiviarli H. Poiché siamo interessati all'ampiezza, prendiamo il valore assoluto del risultato.
  • Converti in scala dB usando: HdB=20log10H - in questo caso 1 è il valore di riferimento.

Inserendo tutto ciò nel codice:

%% Filter definition
a = [1 -0.5 -0.25]; % Some filter with lot's of static gain
b = [1 3 2];

%% My freqz calculation
N = 1024; % Number of points to evaluate at
upp = pi; % Evaluate only up to fs/2
% Create the vector of angular frequencies at one more point.
% After that remove the last element (Nyquist frequency)
w = linspace(0, pi, N+1); 
w(end) = [];
ze = exp(-1j*w); % Pre-compute exponent
H = polyval(b, ze)./polyval(a, ze); % Evaluate transfer function and take the amplitude
Ha = abs(H);
Hdb  = 20*log10(Ha); % Convert to dB scale
wn   = w/pi;
% Plot and set axis limits
xlim = ([0 1]);
plot(wn, Hdb)
grid on

%% MATLAB freqz
figure
freqz(b,a)

Uscita originale di freqz:

inserisci qui la descrizione dell'immagine

E l'output del mio script:

inserisci qui la descrizione dell'immagine

E un rapido confronto in scala lineare: sembra fantastico!

[h_f, w_f] = freqz(b,a);
figure
xlim = ([0 1]);
plot(w, Ha) % mine
grid on
hold on
plot(w_f, abs(h_f), '--r') % MATLAB
legend({'my freqz','MATLAB freqz'})

inserisci qui la descrizione dell'immagine

Ora puoi riscriverlo in qualche funzione e aggiungere alcune condizioni per renderlo più utile.


Un altro modo (precedentemente proposto è più affidabile) sarebbe usare la proprietà fondamentale, che la risposta in frequenza di un filtro è una trasformata di Fourier della sua risposta all'impulso:

H(ω)=F{h(t)}

Pertanto è necessario immettere il segnale del tuo sistema δ(t) , calcolare la risposta del filtro e prenderne il FFT:

d = [zeros(1,length(w_f)) 1 zeros(1,length(w_f)-1)];
h = filter(b, a, d);
HH = abs(fft(h));
HH = HH(1:length(w_f));

In confronto ciò produrrà quanto segue:

inserisci qui la descrizione dell'immagine


1
Spiegazione dettagliata
Partida,

Sto pensando a questa linea a = [1 -0.5 -0.25]; % Some filter with lot's of static gain. Puoi spiegarmi qui la selezione di questi parametri, per favore. Sto leggendo il manuale di Matlab e lo dice [h,w] = freqz(hfilt,n)in una parte di Synapse. Stai dando due filtri (a, b) in freqz. Sono presenti entrambi i filtri hfilt? O uno dentro n?
Léo Léopold Hertz

Solo per chiarire per gli altri: "Non è necessario salire a 2 pi" è quando i coefficienti sono reali. Esistono applicazioni per filtri con coefficienti complessi e in tal caso lo spettro non sarà più simmetrico e in quel caso vorrebbe estendere la frequenza a 2 pi.
Dan Boschen,

14

questo è solo un addendum alla risposta di Jojek che è più generale e perfettamente valida quando si usa la matematica a doppia precisione. quando c'è meno precisione, c'è un "problema del coseno" che si manifesta quando la frequenza nella risposta in frequenza è molto bassa (molto più bassa di Nyquist) e anche quando le frequenze di risonanza del filtro sono molto basse.

|H(ejω)|2|H(ejω)|=|H(ejω)|

considera questa identità di trigono:

cos(ω) = 12sin2(ω2)

sin2(ω2)ω0

sin2(ω2)

H(z)=b0+b1z1+b2z2a0+a1z1+a2z2

which has complex frequency response

H(ejω)=b0+b1ejω+b2ej2ωa0+a1ejω+a2ej2ω

which has magnitude squared:

|H(ejω)|2=|b0+b1ejω+b2ej2ω|2|a0+a1ejω+a2ej2ω|2=(b0+b1cos(ω)+b2cos(2ω))2+(b1sin(ω)+b2sin(2ω))2(a0+a1cos(ω)+a2cos(2ω))2+(a1sin(ω)+a2sin(2ω))2=b02+b12+b22+2b1(b0+b2)cos(ω)+2b0b2cos(2ω)a02+a12+a22+2a1(a0+a2)cos(ω)+2a0a2cos(2ω)

so, one can see that the magnitude frequency response |H(ejω)| is an even symmetry function and depends only on the cosines cos(ω) and cos(2ω). for very low ω, the values of those cosines are so close to 1 that, with single-precision fixed or floating point, there are few bits remaining that differentiate those values from 1. that is the "cosine problem".

using the trig identity above, you get for magnitude squared:

|H(ejω)|2=b02+b12+b22+2b1(b0+b2)cos(ω)+2b0b2cos(2ω)a02+a12+a22+2a1(a0+a2)cos(ω)+2a0a2cos(2ω)=b02+b12+b22+2b1(b0+b2)(12sin2(ω2))+2b0b2(12sin2(ω))a02+a12+a22+2a1(a0+a2)(12sin2(ω2))+2a0a2(12sin2(ω))=b02+b12+b22+2b1(b0+b2)(12sin2(ω2))+2b0b2(2cos2(ω)1)a02+a12+a22+2a1(a0+a2)(12sin2(ω2))+2a0a2(2cos2(ω)1)=b02+b12+b22+2b1(b0+b2)(12sin2(ω2))+2b0b2(2(12sin2(ω2))21)a02+a12+a22+2a1(a0+a2)(12sin2(ω2))+2a0a2(2(12sin2(ω2))21)=b02+b12+b22+2b1(b0+b2)(12ϕ)+2b0b2(2(12ϕ)21)a02+a12+a22+2a1(a0+a2)(12ϕ)+2a0a2(2(12ϕ)21)=b02+b12+b22+2b1(b0+b2)(12ϕ)+2b0b2(18ϕ+8ϕ2)a02+a12+a22+2a1(a0+a2)(12ϕ)+2a0a2(18ϕ+8ϕ2)=b02+b12+b22+2b1b0+2b1b24(b1b0+b1b2)ϕ+2b0b216b0b2ϕ+16b0b2ϕ2a02+a12+a22+2a1a0+2a1a24(a1a0+a1a2)ϕ+2a0a216a0a2ϕ+16a0a2ϕ2=(b02+b12+b22+2b1b0+2b1b2+2b0b2)4(b1b0+b1b24b0b2)ϕ+16b0b2ϕ2(a02+a12+a22+2a1a0+2a1a2+2a0a2)4(a1a0+a1a24a0a2)ϕ+16a0a2ϕ2=14(b02+b12+b22+2b1b0+2b1b2+2b0b2)(b1b0+b1b24b0b2)ϕ+4b0b2ϕ214(a02+a12+a22+2a1a0+2a1a2+2a0a2)(a1a0+a1a24a0a2)ϕ+4a0a2ϕ2=(b0+b1+b22)2ϕ(4b0b2(1ϕ)+b1(b0+b2))(a0+a1+a22)2ϕ(4a0a2(1ϕ)+a1(a0+a2))

where ϕsin2(ω2)

if your gear is intending to plot this as dB, it comes out as

20log10|H(ejω)| = 10log10((b0+b1+b22)2ϕ(4b0b2(1ϕ)+b1(b0+b2)))10log10((a0+a1+a22)2ϕ(4a0a2(1ϕ)+a1(a0+a2)))

so your division turns into subtraction, but you have to be able to compute logarithms to some base or another. numerically, you will have much less trouble with this for low frequencies than doing it the apparent way.


2
That's really cool, thank you Robert! +1
jojek

@Robert I "believe" similar to my comment for Jojek above that this only applies as well when the coefficients are real (and therefore the spectrum is symmetric and thus the magnitude converts to cosines as you show)... Am I correct?
Dan Boschen

yes. that commitment is made when you go from the first line of |H(ejω)|2=... to the second line. no going back after that.
robert bristow-johnson
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.