Como plotar manualmente a resposta de frequência de um filtro Butterworth passa-banda no MATLAB sem a função freqz?

15

Eu tenho o código abaixo que aplica um filtro passa-banda em um sinal. Eu sou um idiota no DSP e quero entender o que está acontecendo nos bastidores antes de prosseguir.

Para fazer isso, quero saber como plotar a resposta de frequência do filtro sem usar freqz.

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

Dadas as saídas, [b, a]como eu faria isso? Parece que seria uma tarefa simples, mas estou tendo dificuldades para encontrar o que preciso na documentação ou online.

Eu também gostaria de entender como fazer isso o mais rápido possível, por exemplo, usando um fftou outro algoritmo rápido.

William
fonte

Respostas:

25

Sabemos que, em geral, a função de transferência de um filtro é dada por:

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

Agora substitua z=ejω para avaliar a função de transferência no círculo unitário:

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

Assim, isso se torna apenas um problema de avaliação polinomial em um dado ω . Aqui estão os passos:

  • Crie um vetor de frequências angulares ω=[0,,π] para a primeira metade do espectro (não é necessário ir até 2π ) e salve-o w.
  • Pré-cálculo do expoente ejω em todos eles e armazene-o em variável ze.
  • Use a polyvalfunção para calcular os valores do numerador e denominador chamando polyval(b, ze):, divida-os e armazene-os H. Porque estamos interessados ​​em amplitude, então pegue o valor absoluto do resultado.
  • Converta para a escala dB usando: HdB=20log10H - neste caso 1 é o valor de referência.

Colocando tudo isso no código:

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

Saída original de freqz:

insira a descrição da imagem aqui

E a saída do meu script:

insira a descrição da imagem aqui

E comparação rápida em escala linear - parece ótimo!

[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'})

insira a descrição da imagem aqui

Agora você pode reescrevê-lo em alguma função e adicionar poucas condições para torná-lo mais útil.


Outra maneira (proposta anteriormente é mais confiável) seria usar a propriedade fundamental, que a resposta de frequência de um filtro é uma Transformada de Fourier de sua resposta de impulso:

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

Portanto, você deve alimentar o sinal δ(t) , calcular a resposta do seu filtro e obter a FFT dele:

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

Em comparação, isso produzirá o seguinte:

insira a descrição da imagem aqui

jojek
fonte
11
Explicação detalhada
partida
Eu estou pensando nesta linha a = [1 -0.5 -0.25]; % Some filter with lot's of static gain. Você pode me explicar a seleção desses parâmetros aqui, por favor. Estou lendo o manual do meu Matlab e ele diz [h,w] = freqz(hfilt,n)em uma parte da sinapse. Você está dando dois filtros (a, b) para freqz. Os dois filtros estão dentro hfilt? Ou um em n?
Léo Léopold Hertz,
Só para esclarecer para os outros: "Não há necessidade de subir até 2 pi" é quando os coeficientes são reais. Existem aplicações para filtros com coeficientes complexos e, nesse caso, o espectro não será mais simétrico e, nesse caso, desejaria estender a frequência para 2 pi.
Dan Boschen
14

este é apenas um adendo à resposta de jojek, que é mais geral e perfeitamente boa quando se usa matemática de dupla precisão. quando há menos precisão, ocorre um "problema de cosseno" que surge quando a frequência na resposta de frequência é muito baixa (muito menor que Nyquist) e também quando as frequências ressonantes do filtro são muito baixas.

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

considere esta identidade trigonométrica:

cos(ω) = 12sin2(ω2)

sin2(ω2)ω0 . tão pequeno que o termo e as informações de frequência desse termo se perdem quando o termo (ou é negativo) é adicionado a 1. esse é o caso mesmo do ponto flutuante, mas é menos um problema com flutuadores de precisão dupla. mas alguns de nós que colocamos essa função de resposta em frequência em marcha podem não ter o recurso de ponto flutuante de precisão dupla ou qualquer ponto flutuante.

sin2(ω2)

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

que tem resposta de frequência complexa

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

que tem magnitude ao quadrado:

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

robert bristow-johnson
fonte
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