dr inż. Grzegorz Szwoch [email protected] p. 732 … cyfrowe Filtry cyfrowe to algorytmy...
-
Upload
nguyenquynh -
Category
Documents
-
view
223 -
download
0
Transcript of dr inż. Grzegorz Szwoch [email protected] p. 732 … cyfrowe Filtry cyfrowe to algorytmy...
Filtry FIR i biblioteka DSPLIB
dr inż. Grzegorz Szwoch
p. 732 - Katedra Systemów Multimedialnych
Zastosowania Procesorów Sygnałowych
Wstęp
Na poprzednim wykładzie napisaliśmy algorytm liczący
średnią ruchomą. Jest to przykład filtru FIR.
Na tym wykładzie poruszymy następujące pojęcia.
� Podstawy filtrów FIR.
� Implementacja własnego kodu FIR w C.
� Wykorzystanie gotowego kodu napisanego
w Asemblerze z biblioteki DSPLIB.
Filtry cyfrowe
� Filtry cyfrowe to algorytmy przetwarzania sygnału
o wzmocnieniu zależnym od częstotliwości.
� Zadaniem filtrów jest wytłumienie wybranego
zakresu częstotliwości.
� Filtry cyfrowe to podstawowy element algorytmów
implementowanych na DSP.
� Dwa typy filtrów: FIR i IIR.
Filtry FIR
FIR – Finite Impulse Response
� Filtr cyfrowy o skończonej odpowiedzi impulsowej.
� Równanie różnicowe:
� N – rząd filtru – ile próbek wstecz
� bk – współczynniki filtru (N+1 współczynników)
� Transmitancja filtru FIR rzędu N:
)(...)2()1()()( 210 Nnxbnxbnxbnxbny N −++−+−+=
∑=
−=N
k
kk zbzH
0
)(
Filtry FIR
Schemat filtru typu FIR:
Charakterystyki filtrów
Parametry filtru
Parametry projektowe filtru:
� częstotliwości graniczne
� szerokość pasma przejściowego
� poziom zafalowań w paśmie:
� przepustowym
� zaporowym
Rząd filtru
� Większy rząd filtru to:
� węższe pasmo przejściowe,
� mniejsze zafalowania.
� Ale większy rząd to także:
� większa liczba mnożeń (dłuższe przetwarzanie),
� większa zajętość pamięci (dłuższy bufor).
� Jak zawsze, musimy znaleźć złoty środek.
Rząd filtru
Porównanie charakterystyk dla rzędu: 30, 60, 100
Projektowanie filtru FIR
� Projekt wykonujemy za pomocą oprogramowania
(Matlab, python + scipy, itp.).
� Metoda okienkowania – najprostsza:
� projektujemy pożądaną char. widmową
� obliczamy odpowiedź impulsową przez IFFT,
� wycinamy N próbek funkcją okna i przesuwamy.
� Metoda Parks-McClellan („Remez”) – minimalizuje
zafalowania. Standard w projektowaniu filtrów FIR.
Projektowanie filtru FIR
� Już na etapie projektowania filtru musimy myśleć
o ewentualnych problemach z przepełnieniem.
� Filtr powinien być zaprojektowany tak, aby jego
maksymalne wzmocnienie wynosiło 1.
Oprogramowanie powinno mieć taką funkcję.
� Jeżeli wzmocnienie filtru G jest większe od 1,
filtr trzeba unormować dzieląc współczynniki
przez G.
Wzmocnienie filtru
Wzór na wzmocnienie filtru FIR na częstotliwości f:
� Dla filtru dolnoprzepustowego: obliczamy dla f = 0.
Wzór upraszcza się do sumy współczynników.
� Dla f. górnoprzepustowego: liczymy dla
częstotliwości Nyquista: f = fs / 2
� Dla f. pasmowo-przepustowego: f ze środka pasma.
∑−
=
−=
1
0
2N
k
kfs
fj
k ebGπ
Kwantyzacja współczynników
� Z oprogramowania dostajemy współczynniki
w formie zmiennoprzecinkowej.
� Kwantyzacja współczynników – konwersja na Q15
(przemnożenie przez 32768). Następnie:
� albo obcięcie do wartości całkowitej – prostsze,
ale zwiększa błędy kwantyzacji,
� albo zaokrąglenie – mniejsze błędy, ale należy
sprawdzić czy nie powoduje przepełnienia
(maksymalna wartość nie może być > 32767).
� Dla bezpieczeństwa, można mnożyć przez 32767.
Kwantyzacja współczynników
Szum kwantyzacji – różnica między wynikiem filtracji
dla filtru zmiennoprzecinkowego i skwantyzowanego
(skala amplitudy ±0,0005)
Kwantyzacja współczynników
O czym należy pamiętać:
� Kwantyzacja współczynników powoduje,
że współczynniki filtru, a więc i jego charakterystyka,
różnią się od tej otrzymanej z projektu.
� Szum kwantyzacji powoduje zniekształcenie
wyników filtracji.
� Narażamy się na efekty przepełnienia zakresu.
Zapis współczynników w kodzie C
� W kodzie naszego programu zapisujemy
współczynniki w formie stałej tablicy typu int.
Przypominam: globalnie (nie w main!).
� Liczbę współczynników warto ustawić jako stałą.
� Dobrym pomysłem jest zamieszczanie
współczynników w osobnym pliku *.h.
#define N 30
const int filtr_dp[] = {-4, 39, 97, 149, 133, -23, -337, -701,
-883, -595, 360, 1955, 3883, 5634, 6677, 6677, 5634, 3883, 1955,
360, -595, -883, -701, -337, -23, 133, 149, 97, 39, -4};
Deklaracje zmiennych i buforów
� Bufor kołowy filtru deklarujemy globalnie.
� Bufor należy wypełnić zerami!
� Deklaracje potrzebnych zmiennych:
int bufor[N];
int i;
for (i = 0; i < N; i++)
bufor[i] = 0;
int indeks = 0; // indeks bufora kołowego
int poz; // pozycja odczytu z bufora
long wynik; // akumulator wyniku filtracji
Pętla przetwarzania
// wejscie – próbka odczytana z wejścia (int)
bufor[indeks] = wejscie;
poz = indeks;
wynik = 0;
// przefiltrowanie próbki wejscie
for (i = 0; i < N; i++) {
wynik = _smac(wynik, bufor[poz], filtr_dp[i]);
poz = _circ_incr(poz, -1, N);
}
// wyjscie – próbka wysyłana na wyjście (int)
wyjscie = (int)(_sround(wynik) >> 16);
// aktualizacja indeksu bufora kołowego
indeks = _circ_incr(indeks, 1, N);
Typy filtrów liniowofazowych
� Filtry FIR projektuje się jako układy o liniowej fazie.
� Są cztery typy liniowofazowych filtrów FIR:
� typ I: nieparzysta liczba współczynników,
symetryczna odpowiedź impulsowa – nadaje się
do wszystkich typów charakterystyki;
� typ II: parzysta / symetryczna – tylko DP i P-Z;
� typ III: nieparzysta / antysymetryczna – tylko P-P,
� typ IV: parzysta / antysymetryczna – nie dla DP
Symetryczne filtry FIR
� Symetria odpowiedzi impulsowej:
const int filtr_dp[] = {15, 58, 109, 135, 68, -151, -493, -796,
-788, -201, 1076, 2884, 4798, 6263, 6812, 6263, 4798, 2884,
1076, -201, -788, -796, -493, -151, 68, 135, 109, 58, 15};
Symetryczne filtry FIR
� Możemy wykorzystać symetrię filtru aby zmniejszyć
liczbę mnożeń o połowę. Np. dla filtru typu II
o 30 współczynnikach:
� b0 = b29 (symetria)
� b0 x(n) + b29 x(n–29) = b0 (x(n) + x(n–29))
� Znacząco skraca to czas obliczeń, więc należy
to wykorzystać.
� Dla typu I (nieparzysty) trzeba osobno dodać
współczynnik „bez pary”.
Symetryczne filtry FIR
Zmodyfikowany fragment
// ...
int poz1 = indeks; // najnowsza próbka
int poz2 = _circ_incr(poz1, 1, N); // najstarsza próbka
int sumapr;
for (i = 0; i < N/2; i++) {
sumapr = _sadd(bufor[poz1], bufor[poz2]);
wynik = _smac(wynik, sumapr, filtr_dp[i]);
poz1 = _circ_incr(poz1, -1, N);
poz2 = _circ_incr(poz2, 1, N);
}
// środkowy współczynnik (bez pary)
if (N & 1) // nieparzyste N
wynik = _smac(wynik, bufor[poz1], filtr_dp[i]);
Symetryczne filtry FIR
� Uwaga na sumowanie próbek:
� W tym miejscu niechybnie powstanie przepełnienie!
� Aby się przed nim uchronić, standardowo skaluje się
próbki wejściowe, dzieląc je przez 2 (>>1).
� Po filtracji, wynik można z powrotem pomnożyć x2.
� Tracimy w ten sposób na precyzji zapisu liczb.
int sumapr = _sadd(bufor[poz1], bufor[poz2]);
Symetryczne filtry FIR
� Można jeszcze szybciej. Nasz DSP ma specjalną
instrukcję FIRSADD, która w jednym cyklu
zegarowym wykonuje równolegle (naraz):
� mnożenie współczynnika przez sumę próbek
i dodanie wyniku do sumy, oraz
� sumowanie kolejnych próbek – przygotowanie
do kolejnego cyklu.
� W języku C: instrukcja wewnętrzna _firsadd.
� Ta sama uwaga co do przepełnienia.
Przetwarzanie blokowe
� Przetwarzanie „próbka po próbce” nie jest wydajne.
� W algorytmach DSP zwykle stosuje się przetwarzanie
blokowe:
� próbki z wejścia są zapisywane w buforze
wejściowym,
� próbki z bufora wyjściowego są wysyłane
na wyjście,
� jeżeli bufor wejściowy jest pełny – przetwarzamy
cały bufor naraz.
Przetwarzanie blokowe
Schemat przetwarzania blokowego
bufor_wejscie[indeks_buf] = wejscie; // próbka z wejścia
wyjscie = bufor_wyjscie[indeks_buf]; // próbka na wyjście
indeks_buf++;
if (indeks_buf == ROZMIAR_BUFORA) { // bufor zapełniony
indeks_buf = 0;
// przetwarzanie bufora wejściowego
for (i = 0; i < ROZMIAR_BUFORA; i++) {
// pobierz bufor_wejscie[i]
// przefiltruj tak jak poprzednio
// zapisz wynik do bufor_wyjscie[i]
}
}
Przetwarzanie blokowe
� Kiedy przetwarzanie „próbka po próbce”?
� Gdy zależy nam, aby przetworzona próbka jak
najszybciej trafiła na wyjście.
� Kiedy przetwarzanie blokowe?
� Gdy zależy nam na wydajności przetwarzania,
a małe opóźnienia nie są konieczne.
� Gdy algorytm wymaga obliczeń na bloku próbek
(FFT, splot, itp.).
Filtry FIR w Asemblerze
� Smutna prawda: nie napiszemy w C wydajnego
filtru FIR, który wykorzysta wszystkie możliwości
naszego procesora sygnałowego.
� Trzeba sięgnąć po Asembler.
� „Skoro filtry FIR to typowe algorytmy DSP,
to na pewno ktoś już to napisał, prawda?”
� Oczywiście, że tak. Odpowiedź brzmi: DSPLIB.
DSPLIB
Co to jest DSPLIB:
� Zbiór typowych procedur na DSP firmy Texas
Instruments – filtry, FFT, itp.
� Napisane w Asemblerze, zoptymalizowane.
� Dostępny kod źródłowy – możemy go modyfikować
do celów naszych projektów!
� Opis w dokumencie o nazwie SPRU422J
(można wpisać w Google).
Filtry FIR w DSPLIB
Mamy trzy funkcje:
� fir – standardowa implementacja,
� fir2 – zoptymalizowana dla szybszego trybu
dual MAC, wymaga spełnienia pewnych warunków
co do alokacji buforów,
� firs – szybsza implementacja filtrów symetrycznych,
niestety, tylko parzystych (typ II), więc filtry DP i PZ.
Osobno mamy specyficzne filtry FIR (Hilberta,
decymacyjne, interpolacyjne i drabinkowe).
Jak czytać dokumentację
Z dokumentacji funkcji fir:
� Typ DATA jest synonimem int (16-bit).
� Gwiazdka (*) oznacza, że potrzebny jest wskaźnik:
� dla tablic (buforów): nazwa jest wskaźnikiem,
� dla zmiennych „skalarnych”: wskaźnik trzeba
pobrać stawiając znak & przed nazwą.
FIR w DSPLIB
Z dokumentacji dowiadujemy się, że potrzebny jest
bufor roboczy:
Musimy go sami utworzyć (oczywiście poza main!)
#define N 30 // liczba współczynników
DATA bufor_fir[N + 2];
const int filtr_dp[] = {...} // współczynniki filtru
FIR w DSPLIB
Pierwszy przykład: przetwarzanie próbka po próbce.
� x – próbki do przefiltrowania. U nas jedna próbka,
więc &wejscie (wskaźnik!)
� h – wektor współczynników filtru. Tu uwaga: mamy
go jako const – trzeba rzutować: (DATA*)filtr_dp
� r – miejsce do zapisu przefiltrowanych próbek.
Tak samo jak dla x: &wyjscie
FIR w DSPLIB
Pierwszy przykład: przetwarzanie próbka po próbce.
� dbuffer – bufor roboczy, który utworzyliśmy
� nx – ile próbek przetwarzamy (tu: 1)
� nh – liczba współczynników.
Funkcja zwraca flagę informującą o tym, czy wystąpiło
przepełnienie. Nie przypisywać jej do próbki
wyjściowej!
FIR w DSPLIB
Wywołanie dla przetwarzania próbka po próbce:
Analogicznie, dla przetwarzania blokowego:
fir(&wejscie, (DATA*)filtr_dp, &wyjscie, bufor_fir, 1, N);
fir(bufor_wejscie, (DATA*)filtr_dp, bufor_wyjscie,
bufor_fir, ROZMIAR_BUFORA, N);
FIR w DSPLIB
� Mamy też w DSPLIB filtry zoptymalizowane, w tym
symetryczne. Musimy spełnić warunki określone
w dokumentacji.
� Niezależnie od typu, pamiętajmy że to na nas
spoczywa obowiązek zapewnienia, że nie wystąpi
przepełnienie (np. przez skalowanie próbek):
Podsumowanie
Cechy filtrów FIR:
� (+) łatwe w projektowaniu
� (+) bardzo proste w implementacji
� (+) mogą mieć liniową fazę (i tak je projektujemy)
� (+) nie ma problemów ze stabilnością
� (–) wymagają wysokiego rzędu, a więc dużej liczby
cykli oraz dużej pamięci, aby uzyskać pożądaną
charakterystykę (są dość wolne).
Podsumowanie
Co musimy wiedzieć i umieć:
� Jak działa filtr FIR. Pojęcia: charakterystyka, rząd,
współczynniki.
� Jak rząd filtru wpływa na kształt charakterystyki
i na czas obliczeń.
� Jak zaprojektować filtr FIR.
� Jak skwantyzować współczynniki.
� Co to jest DSPLIB i jak tego używać.
� Jak uruchomić filtr FIR za pomocą DSPLIB.