Filtro FIR Passa baixa - Exercício de Processamento Digital de Sinais

By on 15 Jan 2022 | 12:30 .

Categories |

Tempo de leitura: #

Visualizações: #

Olá leitores, neste vídeo eu mostro um pouco para vocês a resolução de um exercício da disciplina de Processamento Digital de Sinais onde pede se a implementação de um filtro FIR passa baixa para realizar a filtragem de um sinal de áudio dado.

A avaliação propõe a implementação de um filtro passa baixas do tipo FIR, finite impulse response, para filtrar um sinal de áudio de voz com ruído de alta frequência, dado de forma a conseguir se um sinal de saída audível e também uma transcrição do conteúdo do áudio de voz.

O áudio dado possuí frequência de amostragem de \(24 KHz\). O conteúdo de frequência da voz humana é de \(125Hz\) a \(8KHz\), portanto irá ser utilizado uma frequência de corte empírica de \(6KHz\).

Um filtro FIR pode ser descrito pela equação diferença a seguir.

\[y(n) = \sum _{i=0}^{2M} h(n-M) \cdot x(n-i)\]

Onde \(M\) é relacionado ao tamanho da janela do filtro pela forma \(k=2M+1\), \(h(n)\) é a resposta ao impulso do filtro desejado, neste caso um passa baixa, e \(x(n)\) a entrada a ser filtrada.

Para um filtro passa baixa, a resposta ao impulso pode ser dada pela equação a seguir.

\[h(n) = \begin{cases} \frac{\Omega _c}{\pi} & n=0 \\ \frac{sin(\Omega _c \cdot n)}{n\cdot \pi} & \text{for } n \ne 0 \text{, } -M\le n \le M \end{cases}\]

Onde \(\Omega _c\) é a frequência de corte normalizada dada pela equação a seguir, onde \(\omega _c\) é a frequência de corte em \(rad/s\) e \(\omega _s\) a frequência de sampling.

\[\Omega _c = \frac{\omega _c\cdot 2\pi}{\omega _s}\]

Para implementação do filtro e filtragem, foi utilizado o software GNU Octave, compatível com Matlab.

Como visto na equação diferença acima, o filtro depende dos elementos de \(h(n)\), que podem ser dados em uma lista de coeficientes calculados usando a equação de resposta ao impulso.

"fir_lowpass.m":

% compute the coefficients for a FIR lowpass filter
% wc: cutoff frequency given in rad/2
% len: the discrete length of the filter
% return: list of coefficients

function filt = fir_lowpass(wc, ws, len)
    
    if (mod(len,2) == 0)
        error("The FIR filter len should be a odd number. Number passed: \"%i\".", len);
    end

    % normalized cutoff frequency
    Wc = 2*pi/ws * wc;

    for n = 1:((len-1)/2)
        hp(n) = sin(Wc*n)/(n*pi);
        hn((len-1)/2 - (n-1)) = hp(n);
    end

    h0 = Wc/pi;

    filt = [hn h0 hp];
end

O código acima apresenta a implementação para o cálculo da resposta ao impulso para o filtro passa baixa, aceitando um tamanho \(k\) ímpar, \(size\), frequência de corte \(\omega _c\) e frequência de amostragem \(\omega _s\).

Como o vetor de elementos é espelhado, calcula se os elementos positivos primeiro, os elementos negativos são calculados de forma espelhada, e ao final, linha 22, ambos são combinados com o valor de \(h(0)\) em um único vetor.

Para aplicação do filtro, pode se aplicar dois laços de repetição, onde um calcula o elemento \(y(n)\) e o outro laço aninhado calcula o somatório do produto entre \(h(n-M)\) e \(x(n-i)\).

Para o primeiro laço é necessário saber o tamanho da saída \(y\) a priori, que pode ser dado como:

\[y_{len} = x_{len} + h_{len} - 1\]

"fir_applyfilter.m":

% compute the output of a FIR filter from a input and a filter
% filter: array of filter coefficients
% input: the input array
% return: list of coefficients

function out = fir_applyfilter(filt, in)

    h = filt;
    x = in;
    h_len = length(h);
    x_len = length(x);
    out_len = (h_len+x_len-1);

    progress_bar = waitbar(0);

    % loop through the output shifiting the input by 'n-1'. The output convolution is the sum of the inputs lengths plus 1
    for n = (1:out_len)

        acc = 0;
        
        % loop through the filter coefficients 
        for i = (1:(h_len))
            n_sample = i + (x_len-1) - (n-1);

            if (n_sample < x_len && n_sample >= 1)
                acc += h(i) * x(n_sample);
            end

        end

        waitbar(n/out_len, progress_bar);

        out(n) = acc;
    end

    % i dunno why but the algorithm gives the output reversed, so i jsut reversed it again 
    out = fliplr(out);

end

A listagem de código acima apresenta a implementação, sendo necessário receber o vetor de filtro e o vetor de entrada.

Para obter se a saída basta aplicar as funções mostradas em ordem, observa se que o tamanho do filtro FIR, dado como \(k=251\) foi obtido de forma empírica por tentativa em erro, valores pequenos de \(k\) implementam um filtro muito longe do filtro ideal projetado enquanto que valores maiores aproximam o filtro ideal com maior fidelidade, ao custo de tempo de processamento.

"main.m":

% load audio
load dados_trabalho2_aluno.mat

% compute filter
filter = fir_lowpass(6000*2*pi, 24000*2*pi, 251);

% compute output, very slow!
out = fir_applyfilter(filter, sinal_trabalho_ruido);
save -mat audio.mat out;

% create playback
audio = audioplayer(out, 24000);
play(audio);