Lab 04a - Projektowanie filtrow FIR
Transformata Fouriera sygnałów dyskretnych- splot, modulacja, okna
Wstęp
Filtry FIR (Finite Impulse Response)
Jest to klasa filtrów nierekursywnych, czyli nie posiadających sprzężeń zwrotnych. Oznacza to że odpowiedź filtru wyznaczana jest wyłącznie jako kombinacja liniowa pewnej ilości próbek sygnału wejściowego. $$ y(n)=\sum^{\infty}_{m=-\infty}h(m)x(n-m) $$ gdzie \(x(n)\) jest sygnałem wejściowym, \(y(n)\) sygnałem wyjściowym, zaś ((h(n)\) odpowiedzią impulsową filtra.
Cechy filtrów FIR
- Z uwagi na skończoną długość filtra jego odpowiedź na jest zawsze stabilna.
- \(lim_{n\rightarrow\infty}y(n)=0\).
- Filtr może być zaprojektowany tak by mieć liniową charakterystykę fazową. Właściwość ta gwarantuje, że filtrowany sygnał nie będzie zniekształcany (będzie miał stałe opóźnienie grupowe)
Właściwości filtrów z liniową charakterystyką fazową
W ćwiczeniu ograniczymy się do jednego typu filtru mającego następujące właściwości:
- długość filtru \(N=2L+1\) - filtr ma nieparzystą liczbę próbek.
- pulsacja unormowana: \(\omega \in <0;2\pi\).
- charakterystyka amplitudowa filtru jest symetryczny względem osi \(0, \pi\).
- charakterystyka częstotliwościowa filtru posiada wyłącznie część rzeczywistą (\(Im(H(s))=0\)).
- charakterystyka czasowa filtru jest również symetryczna \(h(n)=h(N-1-n)\) warunek ten jest gwarantowany pośrednio przez symetrię charakterystyki w dziedzinie częstotliwości.
Filtr posiadający takie właściwości ma również liniową charakterystykę fazową (stałe opóźnienie grupowe). W tej klasie mogą istnieć filtry: LP, HP, BP, BS.
Okna i ich właściwości
Skończoną długość odpowiedzi impulsowej obiektu wykorzystywanego w procesie filtracji można opisać jako wycięcie nieskończonej odpowiedzi impulsowej filtru idealnego przez pewne okno. Szerokość oraz parametry okna determinują ostateczną jakość filtracji (szerokość pasma przejściowego oraz tłumienie w paśmie zaporowym). Z uwagi na listki boczne
w charakterystyce amplitudowej okna, użycie każdego z okien musi doprowadzić do ograniczenia tłumienia w paśmie zaporowym, ponadto z uwagi na niezerową szerokość listka głównego zmniejsza się stromość charakterystyki w paśmie przejściowym. Dalej zostały opisane 3 okna oraz ich właściwości.
Przyjęto następujące oznaczenia:
- \(w(n)\) - funkcja okno,
- \(N=2M+1\) - jest nieparzystą(!) długością okna,
- \(A_{sl}\) - względny poziom tłumienia
listków bocznych
w stosunku dolistka głównego
, - \(\Delta_{ml}=\frac{\Delta f}{f_s}\) unormowana szerokość
listka głównego
, gdzie \(\Delta f\) jest szerokością pasma przejściowego a \(f_s\) jest okresem próbkowania, - \(A_{stop}\) - maksymalne wzmocnienie filtra w paśmie zaporowym.
Okno prostokątne
- \(w(n)= 1 :n\in<-M;M>\)
- \(N>\frac{2}{\Delta_{ml}}\)
- \(A_{sl}=13,5dB\)
- \(A_{stop}=-21dB\)
Uwaga: Dla filtrów wyciętych za pomocą okna prostokątnego można wyłącznie regulować stromość charakterystyki w paśmie przejściowym.
Okno Hanninga
- \(w(n)= 0,5+0,5cos(2\pi n/(2M+1)) :n\in<-M;M>\)
- \(N>\frac{4}{\Delta_{ml}}\)
- \(A_{sl}=31dB\)
- \(A_{stop}=-44dB\)
Uwaga: Dla filtrów wyciętych za pomocą okna Hanninga można wyłącznie regulować stromość charakterystyki w paśmie przejściowym.
Okno Kaisera
Okno Kaisera w odróżnieniu od poprzednio wspomnianych umożliwia na dostosowanie tłumienia w paśmie zaporowym. Okno dane jest wzorem:
\(w(n) = I_0\left( \beta \sqrt{1-\frac{4n^2}{(M-1)^2}} \right)/I_0(\beta)\) z
\(\quad -\frac{M-1}{2} \leq n \leq \frac{M-1}{2},\) gdzie (I_0) jest zmodyfikowaną funkcją Bessela.
W Pythonie okno tworzone jest za pomocą:
= signal.kaiser(N, beta) window
gdzie N
jest liczbą próbek a beta
jest parametrem kształtu określającym pewien kompromis między szerokością listka głównego a tłumieniem dla listów bocznych.
Do wyznaczenia współczynnika beta, gdzie zadane jest tłumienie ripple
(w dB) zarówno w paśmie przepustowym jak i zaporowym oraz szerokości pasma przejściowego (width
) wyrażonego w pulsacji znormalizowanej (width=1 odpowiada szerokości pasma przejściowego fs/2
) można wykorzystać funkcję:
scipy.signal.kaiserord(ripple,width)
Więcej na ten temat możesz znaleźć w dokumentacji
Metody projektowania filtrów
Metody projektowania filtrów zostały przedstawione w materiałach z wykładów. Tutaj przedstawiono wyłączenie przykłady realizacji filtrów w języku Python. Zwróć uwagę na użycie funkcji firwin
oraz lfilter
. Przeanalizuj również działanie funkcji freqz
umożliwiającej wyświetlenie charakterystyki amplitudowej. Do uruchomienia skryptu wykorzystaj plik distorted.npy
"""Filtracja zakłóceń z sygnału."""
import numpy as np
import scipy.signal as sig
import matplotlib
import matplotlib.pyplot as plt
'figure.figsize'] = (8, 4)
matplotlib.rcParams[
= np.load('distorted.npy')
wav = wav / np.max(np.abs(wav))
wav = 48000
fs = np.arange(len(wav)) / fs
t
# oryginalny - postać czasowa
plt.figure()500], wav[:500])
plt.plot(t[:'Czas [s]')
plt.xlabel('Amplituda')
plt.ylabel('Sygnał oryginalny - postać czasowa')
plt.title(
# oryginalny - widmo
= np.fft.rfft(wav * np.hamming(2048))
spectrum = 20 * np.log10(np.abs(spectrum) / 1024)
spdb = np.fft.rfftfreq(2048, 1 / 48000)
f =(8, 4))
plt.figure(figsize
plt.plot(f, spdb)= np.zeros_like(spdb)
ideal > 3000] = -60
ideal[f # plt.plot(f, ideal, c='r')
0, 12000)
plt.xlim(=-60)
plt.ylim(bottom'Częstotliwość [Hz]')
plt.xlabel('Poziom widma [dB]')
plt.ylabel('Sygnał oryginalny - postać widmowa')
plt.title(
# filtr dolnoprzepustowy
= 1900 # częstotliwość graniczna
fc = 101 # długość filtru
N = sig.firwin(N, fc, pass_zero='lowpass', fs=48000)
h
# z, p, k = sig.tf2zpk(h, 1)
# print(len(z), len(p))
# odpowiedź impulsowa
=(10, 4))
plt.figure(figsize=True)
plt.stem(h, use_line_collection'Indeks')
plt.xlabel(# plt.ylabel('Amplituda')
'Odpowiedź impulsowa filtru')
plt.title(# plt.title('Współczynniki h')
# charakterystyka częstotliwościowa
= sig.freqz(h, worN=2048, fs=48000)
w, hf = 20 * np.log10(np.abs(hf))
hfdb = np.degrees(np.angle(hf))
phase
plt.figure()= plt.subplots(2, sharex=True, tight_layout=True, figsize=(8, 5))
fig1, ax1 0].plot(w, hfdb)
ax1[0].set_xlim(0, 12000)
ax1[0].set_ylim(bottom=-80)
ax1[0].set_xlabel('Częstotliwość [Hz]')
ax1[0].set_ylabel('Poziom widma [dB]')
ax1[0].set_title('Charakterystyka częstotliwościowa')
ax1[0].grid()
ax1[
1].axvline(3000, c='k', lw=1, ls='--')
ax1[1].plot(w, phase)
ax1[1].grid()
ax1[1].set_ylim(-180, 180)
ax1[1].set_xlabel('Częstotliwość [Hz]')
ax1[1].set_ylabel('Faza [°]')
ax1[1].set_title('Charakterystyka fazowa filtru DP 3 kHz')
ax1[
# charakterystyka częstotliwościowa + sygnał
plt.figure()
plt.plot(f, spdb)
plt.plot(w, hfdb)0, 12000)
plt.xlim(=-80)
plt.ylim(bottom'Częstotliwość [Hz]')
plt.xlabel('Poziom widma [dB]')
plt.ylabel('Charakterystyka częstotliwościowa')
plt.title(
plt.grid()
# filtracja sygnału
= sig.lfilter(h, 1, wav)
y = np.fft.rfft(y * np.hamming(2048))
ysp = 20 * np.log10(np.abs(ysp) / 1024)
yspdb
# widmo sygnału przed i po filtracji
plt.figure()='#a0a0a0', label='Oryginalny')
plt.plot(f, spdb, c='Po filtracji')
plt.plot(f, yspdb, label0, 12000)
plt.xlim(# plt.ylim(bottom=0)
'Częstotliwość [Hz]')
plt.xlabel('Poziom widma [dB]')
plt.ylabel(
plt.legend()
plt.grid()'Widmo sygnału przed i po filtracji')
plt.title(
# postać czasowa przed i po filtracji
= plt.subplots(2, figsize=(8, 5), sharex=True, tight_layout=True)
fig7, ax7 0].plot(t[:500], wav[:500], label='Originalny')
ax7[1].plot(t[:500], y[50:550], label='Przetworzony')
ax7[-1].set_xlabel('Czas [s]')
ax7[for a in ax7:
'Amplituda')
a.set_ylabel(='upper right')
a.legend(loc0].set_title('Sygnał oryginalny i przetworzony - postać czasowa')
ax7[
plt.show()
Opóźnienie grupowe
Każdy filtr wprowadza opóźnienie sygnału, które jest zależne m.in od długości filtra
import numpy as np
import scipy.signal as sig
import matplotlib
import matplotlib.pyplot as plt
'figure.figsize'] = (8, 4)
matplotlib.rcParams[
= 1000
n = 4800
fs = np.arange(n) / fs
t = np.array([500, 1000, 1500, 2000, 2500]).reshape(-1, 1)
fr = np.sum(np.sin(2 * np.pi * t * fr), axis=0)
x = x + 0.05 * np.random.randn(len(x))
x = x / np.max(np.abs(x))
x
= sig.firwin2(801, [0, 1250, 1300, fs/2], [1,1,0,0], window='hamming', fs=fs)
h = sig.lfilter(h, 1, x)
y
= plt.subplots(2, sharex=True, tight_layout=True, figsize=(8, 5))
fig, ax 0].plot(x[:n])
ax[1].plot(y[:n])
ax[for a in ax:
a.grid()'Amplituda')
a.set_ylabel(1].set_xlabel('Nr próbki')
ax[0].set_title('Sygnał wejściowy')
ax[1].set_title('Sygnał po filtracji (N=801)')
ax[
plt.show()
Zadania
Dla sygnału fs=48kHz, zaprojektuj 4 filtry o długości N=101 próbek z oknem Hamminga: górno przepustowy (fp=3kHz), dolno-przepustowy (fp=3kHz), pasmowo przepustowy (1-3kHz), pasmowo zaporowy (1-3kHz). Wyświetl charakterystyki fazowe oraz odpowiedzi impulsowe.
Zakładając, że \(f_s\)=500Hz, a okres obseracji wynosi T=2s przygotować wektor sygnału testowego o postaci:
$$ x(t)=sin(3\cdot 2\pi\cdot t)+cos(10\cdot 2\pi\cdot t)+cos(25\cdot 2\pi\cdot t)+sin(35\cdot 2\pi\cdot t)+sin(50\cdot 2\pi\cdot t)+sin(100\cdot 2\pi\cdot t) $$
Zaprojektować filtr dolnoprzepustowy nierekursywny, który umożliwi stłumienie składowych 50 i 100Hz. Dla okna Kaisera przyjąć, że tłumienie w paśmie zaporowym nie powinno być mniejsze niż 55dB, zaś oscylacje w paśmie przepustowym nie powinny przekraczać 0.1%. Szerokość pasma przejściowego ustalić na 5Hz. Do projektowania wykorzystać metodę
firwin
oraz okno Kaisera. Po zaprojektowaniu przefiltruj sygnał i wyznacz opóźnienie filtra.Zaprojektuj filtr pasmowo-zaporowy dla składowej 25Hz i 50Hz, załóż że pasmo zaporowe ma szerokość 1Hz, a pasmo przejściowe szerokośc taką jak w pkt 3. Wybierz metodę
firwin2
oraz założenia projektowe analogiczne do tych z pkt 3.Zaprojektuj filtry z zadania 3 i 4 metodą najmniejszych kwadratów (
firls
). Jakie jest najsłabsze tłumienie w paśmie zaporowym?W celu porównania skuteczności działania filtra zaprojektowanego metodą
firls
ifirwin
z zadania 4 i 5 wyznacz opóźnienie każdego z filtrów i przesuń sygnał przefiltrowany w taki sposób żeby pokrywał się z sygnałem oryginalnym, bez składowych, które miały zostać usunięte. Następnie w oknie o długości 1s (w przedziale 0.5-1.5s) wyznacz błąd średniokwadratowy liczony jako wartość średnia sumy kwadratów błędu.
Autorzy: Piotr Kaczmarek