Szybka transformata Fouriera - INFO-KAM
Transkrypt
Szybka transformata Fouriera - INFO-KAM
Andrzej Zeja e:mail [email protected] www.infokam.kielce.pl Kielce, dn. 30.11.2005 Szybka transformata Fouriera -FFT Większość metod poprawy skuteczności obliczania DTF wykorzystuje jedną lub obie z podanych właściwości: 1. WNk ( N −n ) = (WNkn ) ∗ 2. WNkn = WNk ( N + n ) = WN( k + N ) n gdzie: WNkn = e −j 2π kn Ν = cos( 2π 2π kn) + j sin( kn) N N Algorytm z podziałem czasu Algorytm z podziałem czasu wykorzystuje rozłoŜenie ciągu x(n) na coraz krótsze podciągi. Niech N będzie całkowitą potęgą liczby 2. PoniewaŜ N jest liczbą parzystą moŜna wyznaczyć dwa podciągi N/2-elementowe zawierające odpowiednio tylko parzyste lub nieparzyste próbki x(n). Stąd: ( 1) X (k ) = ∑ x(n)W kn N + n − parzyste ∑ x(n)W kn N n − nieparzyste dokonując podstawienia n=2r dla n -parzystych i n=2r+1 dla n -nieparzystych otrzymujemy: ( 2) X (k ) = N −1 2 ∑ x(2r )W 2 rk N r =0 = N −1 2 ∑ x(2r )(W r =0 Podstawiając dalej: N −1 2 + ∑ x (2r + 1)WN( 2 r +1) k = r =0 N −1 2 ) + WNk ∑ x ( 2r + 1)(WN2 ) 2 rk N r =0 rk W =e 2 N −2 j 2π N =e −j 2π N 2 otrzymujemy: ( 3) X (k ) = N −1 2 ∑ x (2r )W r =0 rk N 2 N −1 2 + WNk ∑ x ( 2r + 1)WNrk r =0 2 X ( k ) = G ( k ) + WNk H ( k ) W kaŜdej z sum w równaniu ( 3) znajdują się N/2-punktowe DTF Pomimo, Ŝe k=0..N-1 kaŜdą z sum wystarczy obliczyć w przedziale 0..(N/2)-1., poniewaŜ G(k) i H (k) są okresowe względem k i mają okres równy N/2. Rys. 1 przedstawia pierwszy krok do kompozycji grafu obliczania 8-punktowej DTF. Z powstałych dwóch 4-punkowych DTF naleŜy w analogiczny sposób utworzyć kolejne 4 2-punktowe DFT. G(0) x(0) x(2) X(0) 0 G(1) N/2 -punktowa DTF W X(1) 1 G(2) x(4) W X(2) G(3) W N X(3) 3 W x(1) x(5) N 2 x(6) x(3) N H(0) N/2 -punktowa DTF H(1) H(2) x(7) H(3) N 4 W N X(4) 5 W N X(5) 6 W N X(6) 7 W N X (7) Rys. 1 Graf przepływowy wyznaczania 8- punktowej DTF -krok 1 Algorytmy z podziałem częstotliwości Algorytm z podziałem częstotliwości, analogicznie do algorytmów z podziałem czasu, wykorzystuje podział ciągu na coraz to krótsze podciągi. W tym przypadku podziału dokonuje się dla X(k) . Standardowo N musi być potęgą liczby 2 Dokonanie podziału (4) N 2 −1 X ( k ) = ∑ x (n)W n=0 kn N N −1 + ∑ x (n)WNkn n = N2 (5) X (k ) = N −1 2 ∑ x(n)W 2 nk N n=0 N 2 + WN k N −1 2 ∑ x(n + n=0 N 2 )WNnk aby otrzymać dwie N/2 punktowe transformaty wykorzystuje się fakt, Ŝe: Wr(N/2)kN=(-1)k otrzymujemy: (6) N 2 −1 [ X ( k ) = ∑ x(n) + (−1) k x(n + n=0 N 2 )]WNnk Teraz moŜna rozpatrzyć osobno przypadki, gdy k jest parzyste (k=2r) i nieparzyste (k=2r+1). (7) N 2 −1 [ X (2r ) = ∑ x(n) + x(n + n=0 N 2 )]WN2 rn (8) N 2 −1 [ X (2r + 1) = ∑ x (n) − x(n + n=0 N 2 )]WN2rnWNn Łatwo udowodnić, Ŝe W2rnN=WrnN/2 zatem moŜna stwierdzić, Ŝe ( 7 ) i ( 8 ) odpowiadają N/2 - punktowym DTF. Przyjmując oznaczenia: g (n) = x(n) + x(n + N2 ) h(n) = x(n) − x(n + N2 ) MoŜna wyznaczyć DTF tworząc najpierw ciągi g(n) i h(n) , następnie wyznaczając h(n)WnN i obliczając N/2-punktowe transformaty obu ciągów. Transformaty te określają odpowiednio punkty ciągu wyjściowego o parzystych i nieparzystych numerach. Procedurę tą dla 8-punktowej DTF ilustruje Rys. 2. ` g(0) x(0) X(0) g(1) N/2 -punktowa DTF x(1) g(2) x(2) X(2) X(4) g(3) x(3) X(6) W h(1) W h(2) W h(3) W x(4) X(1) x(5) x(6) x(7) 0 N h(0) 1 N 2 N N/2 -punktowa DTF X(3) X(5) 3 N X (7) Rys. 2 Graf pierwszego kroku dla obliczenia N- punktowej transformaty-Podział na dwie N/2punktowe DFT Otrzymane N/2-punktowe DTF dzieli się na kolejne 4 N/4-punktowe transformaty. Współczynniki WrN Współczynniki mogą być wykorzystane albo w porządku naturalnym, albo z odwrócona kolejnością bitów. Wyznaczenie współczynników moŜe odbywać się na dwa sposoby: 1. przed rozpoczęciem obliczania FFT 2. na bieŜąco . Pierwsza ewentualność jest korzystna ze względu na szybkość obliczeń, wymaga jednak rezerwacji dodatkowej pamięci. Do obliczeń potrzebne są współczynniki: WrN dla r=0..(N/2)-1 czyli N/2 zespolone komórki pamięci do zapamiętania kompletu współczynników. BieŜące wyznaczanie współczynników jest oszczędne ze względu na objętość pamięci, lecz nie jest efektywne. Częściowe zwiększenie efektywności uzyskuje się wykorzystując do obliczeń WrN rekursywne zaleŜności. W kaŜdym etapie obliczeń współczynniki są potęgami określonej potęgi WN, a więc równe WqN przy czym q zaleŜy od algorytmu oraz etapu obliczeń. JeŜeli współczynniki są pobierane w naturalnym porządku, to moŜna zastosować następującą rekursywną zaleŜność: WqlN= WqN Wq(l-1)N Stosowanie takiej zaleŜności wiąŜe się z narastającym błędem podczas obliczania kolejnych współczynników, co jest związane ze skończoną dokładnością zapisu liczb oraz operacji arytmetycznych zapisanych w sposób cyfrowy. Jedną moŜliwością ograniczenia tych błędów jest korygowanie współczynników w zadanych, charakterystycznych miejscach np. dla WN/4N=-j. Przykładowe wartości dla N=8 W0 e[-(j2π/8)0] 1 W1 e[-(j2π/8)1] (1-j)/√2 W2 W4 e[-(j2π/8)2] e[-(j2π/8)4] -j -1 Porządkowanie wprowadzanych i wyprowadzanych danych RozwaŜając grafy przebiegu obliczania z Rys. 1i Rys. 2 oraz kolejne kroki, moŜna zauwaŜyć, Ŝe zmiana kolejności danych w wektorze wejściowym lub wyjściowym spowoduje moŜliwość wykorzystywania (rezerwacji pamięci) tylko jednego wektora do przechowywania obliczonych wartości przejściowych i końcowych. Wektor ten moŜe jednocześnie zawierać dane wejściowe, które w trakcie obliczeń ulegają zapisaniu. Sposób zmiany kolejności określany jest nazwą: „porządkiem odwrotnej kolejności bitów”. Przykład dla ciągu 8-elementowego Kol. Naturalna = Kol. odwrócona Xn(000)=x(000) Xn(001)=x(100) Xn(010)=x(010) Xn(011)=x(110) Xn(100)=x(001) Xn(101)=x(101) Xn(110)=x(011) Xn(111)=x(111) Przykład Przykład wykorzystujący algorytm z podziałem czasowym podanym przez Cooleya i Tukeya. Algorytm polega na wprowadzaniu ciągu wejściowego w naturalnym porządku, a ciąg współczynników transformaty otrzymywany jest z odwróconą kolejnością bitów. Algorytm ten jest modyfikacją grafu przebiegu obliczeń Rys. 1 poprzez odwrócenie kolejności ciągu wyjściowego, co zostało przedstawione na Rys. 3. x(0) X(0) 0 W x(1) W0N x(2) -1 0 W x(3) N W x(5) W W W N W -1 N -1 -1 N W2N -1 2 W N -1 X(6) 1 N 0 x(7) W X(1) -1 0 x(6) X(2) N 0 -1 X(4) 2 -1 0 x(4) N X(3) 3 W N -1 -1 X(5) N -1 X(7) Rys. 3 Graf przebiegu obliczeń dla danych wejściowych w naturalnym porządku i danych wyjściowych w odwróconej kolejności bitów Kod przykładowych funkcji obliczających FFT -------------------------------------------------------------------------------------------------------Procedura FFT complex The procedure FFTcomplex calculates complex coefficients F[j] (j=0,1,...,n-1) of the trigonometric polynomial F[0] + F[1]exp(ix) + ... + F[n-1]exp((n-1)ix), where i denotes the imaginary unit, by the fast Fourier transforms. Data: m - a positive integer such that n=2^m, f - an array of records containing the complex values of the polynomial at the points 2*pi*k/n (the element f[k].re should contain the real part, and the element f[k].im - the imaginary part of the adequate value; k=0,1,...,n-1). Result: F - an array of records containing complex coefficients of the polynomial (the element F[j].re contains the real part, and the element F[j].im - the imaginary part of the adequate coefficient; j=0,1,...,n-1). Other parameters: st - a variable which within the procedure FFTcomplex is assigned the value of: 1, if m<1, 0, otherwise. Note: If st=1, then the elements of array F are not calculated. Unlocal identifiers: vector - a type identifier of integer array [q0..qm], where q0<=0 and qm>=m, complex - a type identifier of the form: record re,im : Extended end; cplxvector - a type identifier of the form: array [q0..qn1] of complex; where q0<=0 and qn1>=n-1. procedure FFTcomplex (m : Integer; f : cplxvector; var F : cplxvector; var st : Integer); const sq=7.07106781186547524e-1; sk=3.82683432365089772e-1; ck=9.23879532511286756e-1; var i,j,jh,k,kh,kn,k0,k1,k2,k3,l,lim,mh,n,nk : Integer; c1,c2,c3,r,s1,s2,s3 : Extended; z0,z1,z2,z3 : complex; cond : Boolean; c,lst : vector; begin if m<1 then st:=1 else begin st:=0; n:=2 shl (m-1); c[m]:=n; mh:=(m shr 1)*2; kn:=0; for k:=m downto 1 do c[k-1]:=c[k] shr 1; r:=2*pi/n; nk:=m-5; repeat kh:=kn; kn:=kn+n; if mh<>m then begin k2:=kn; k0:=c[mh]+kh; repeat k2:=k2-1; k0:=k0-1; z2.re:=f[k2].re; z2.im:=-f[k2].im; z0.re:=f[k0].re; z0.im:=-f[k0].im; F[k0].re:=z0.re+z2.re; F[k0].im:=z0.im+z2.im; F[k2].re:=z0.re-z2.re; F[k2].im:=z0.im-z2.im until k0<=kh end; c1:=1; s1:=0; jh:=0; k:=mh-2; j:=3; if k>=0 then repeat l:=c[k]; if jh<>0 then begin c2:=jh*l*r; c1:=cos(c2); s1:=sin(c2); c2:=c1*c1-s1*s1; s2:=2*c1*s1; c3:=c2*c1-s2*s1; s3:=c2*s1+s2*c1 end; cond:=true; repeat for k0:=kh+l-1 downto kh do begin k1:=k0+l; k2:=k1+l; k3:=k2+l; z0.re:=F[k0].re; z0.im:=F[k0].im; if s1=0 then begin z1.re:=F[k1].re; z1.im:=F[k1].im; z2.re:=F[k2].re; z2.im:=F[k2].im; z3.re:=F[k3].re; z3.im:=F[k3].im end else begin z1.re:=F[k1].re*c1-F[k1].im*s1; z1.im:=F[k1].re*s1+F[k1].im*c1; z2.re:=F[k2].re*c2-F[k2].im*s2; z2.im:=F[k2].re*s2+F[k2].im*c2; z3.re:=F[k3].re*c3-F[k3].im*s3; z3.im:=F[k3].re*s3+F[k3].im*c3 end; F[k0].re:=z0.re+z1.re+z2.re+z3.re; F[k0].im:=z0.im+z1.im+z2.im+z3.im; F[k1].re:=z0.re-z1.re+z2.re-z3.re; F[k1].im:=z0.im-z1.im+z2.im-z3.im; F[k2].re:=z0.re-z1.im-z2.re+z3.im; F[k2].im:=z0.im+z1.re-z2.im-z3.re; F[k3].re:=z0.re+z1.im-z2.re-z3.im; F[k3].im:=z0.im-z1.re-z2.im+z3.re end; if k>0 then begin k:=k-2; cond:=false end else begin kh:=k3+l; if (kh<kn) and (j=0) then begin k:=2; j:=nk; while c[j]<=jh do begin jh:=jh-c[j]; j:=j-1; if c[j]<=jh then begin jh:=jh-c[j]; j:=j-1; k:=k+2 end end; jh:=c[j]+jh; j:=3; cond:=false end; if (kh<kn) and (j<>0) then begin j:=j-1; c2:=c1; if j=1 then begin c1:=c1*ck+s1*sk; s1:=s1*ck-c2*sk end else begin c1:=(c1-s1)*sq; s1:=(c2+s1)*sq end; c2:=c1*c1-s1*s1; s2:=2*c1*s1; c3:=c2*c1-s2*s1; s3:=c2*s1+s2*c1 end end until (kh>=kn) or not cond until kh>=kn until kn>=n; j:=m-1; l:=j; kh:=0; i:=0; m:=m-1; lim:=(m+2) shr 1; if l>0 then repeat k0:=c[j]+kh; kn:=k0; jh:=c[m-j]; k1:=kh+jh; repeat k:=k1+jh; repeat z1:=F[k1]; F[k1]:=F[k0]; F[k0]:=z1; k1:=k1+1; k0:=k0+1 until k1>=k; k1:=k1+jh; k0:=k0+jh until k1>=kn; cond:=true; if j>lim then begin j:=j-1; i:=i+1; lst[i]:=j; cond:=false end else begin kh:=k0; if i>0 then begin j:=lst[i]; i:=i-1; cond:=false end else if kh<n then begin j:=l; cond:=false end end until cond; r:=1/n; for k:=n-1 downto 0 do begin F[k].re:=F[k].re*r; F[k].im:=-F[k].im*r end end end; ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Procedura IFFTcomplex; - odwrotna FFT The procedure IFFTcomplex calculates at the points 2*pi*k/n (k=0,1,...,n-1) the complex values of the trigonometric polynomial F[0] + F[1]exp(ix) + ... + F[n-1]exp((n-1)ix), where i denotes the imaginary unit, by the fast Fourier transforms. Data: m - a positive integer such that n=2^m, F - an array of records containing complex coefficients of the polynomial (the element F[j].re should contains the real part, and the element F[j].im - the imaginary part of the adequate coefficient; j=0,1,...,n-1). Result: f - an array of records containing the complex values of the polynomial at the points 2*pi*k/n (the element f[k].re contains the real part, and the element f[k].im - the imaginary part of the adequate value; k=0,1,...,n-1). Other parameters: st - a variable which within the procedure IFFTcomplex is assigned the value of: 1, if m<1, 0, otherwise. Note: If st=1, then the elements of array f are not calculated. Unlocal identifiers: vector - a type identifier of integer array [q0..qm], where q0<=0 and qm>=m, complex - a type identifier of the form: record re,im : Extended end; cplxvector - a type identifier of the form: array [q0..qn1] of complex; where q0<=0 and qn1>=n-1. procedure IFFTcomplex (m : Integer; F : cplxvector; var f : cplxvector; var st : Integer); const sq=7.07106781186547524e-1; sk=3.82683432365089772e-1; ck=9.23879532511286756e-1; var i,j,jh,k,kh,kn,k0,k1,k2,k3,l,lim,mh,n,nk : Integer; c1,c2,c3,r,s1,s2,s3 : Extended; z0,z1,z2,z3 : complex; cond : Boolean; c,lst : vector; begin if m<1 then st:=1 else begin st:=0; n:=2 shl (m-1); c[m]:=n; mh:=(m shr 1)*2; kn:=0; for k:=m downto 1 do c[k-1]:=c[k] shr 1; r:=2*pi/n; nk:=m-5; repeat kh:=kn; kn:=kn+n; if mh<>m then begin k2:=kn; k0:=c[mh]+kh; repeat k2:=k2-1; k0:=k0-1; z2.re:=F[k2].re; z2.im:=F[k2].im; z0.re:=F[k0].re; z0.im:=F[k0].im; f[k0].re:=z0.re+z2.re; f[k0].im:=z0.im+z2.im; f[k2].re:=z0.re-z2.re; f[k2].im:=z0.im-z2.im until k0<=kh end; c1:=1; s1:=0; jh:=0; k:=mh-2; j:=3; if k>=0 then repeat l:=c[k]; if jh<>0 then begin c2:=jh*l*r; c1:=cos(c2); s1:=sin(c2); c2:=c1*c1-s1*s1; s2:=2*c1*s1; c3:=c2*c1-s2*s1; s3:=c2*s1+s2*c1 end; cond:=true; repeat for k0:=kh+l-1 downto kh do begin k1:=k0+l; k2:=k1+l; k3:=k2+l; z0.re:=f[k0].re; z0.im:=f[k0].im; if s1=0 then begin z1.re:=f[k1].re; z1.im:=f[k1].im; z2.re:=f[k2].re; z2.im:=f[k2].im; z3.re:=f[k3].re; z3.im:=f[k3].im end else begin z1.re:=f[k1].re*c1-f[k1].im*s1; z1.im:=f[k1].re*s1+f[k1].im*c1; z2.re:=f[k2].re*c2-f[k2].im*s2; z2.im:=f[k2].re*s2+f[k2].im*c2; z3.re:=f[k3].re*c3-f[k3].im*s3; z3.im:=f[k3].re*s3+f[k3].im*c3 end; f[k0].re:=z0.re+z1.re+z2.re+z3.re; f[k0].im:=z0.im+z1.im+z2.im+z3.im; f[k1].re:=z0.re-z1.re+z2.re-z3.re; f[k1].im:=z0.im-z1.im+z2.im-z3.im; f[k2].re:=z0.re-z1.im-z2.re+z3.im; f[k2].im:=z0.im+z1.re-z2.im-z3.re; f[k3].re:=z0.re+z1.im-z2.re-z3.im; f[k3].im:=z0.im-z1.re-z2.im+z3.re end; if k>0 then begin k:=k-2; cond:=false end else begin kh:=k3+l; if (kh<kn) and (j=0) then begin k:=2; j:=nk; while c[j]<=jh do begin jh:=jh-c[j]; j:=j-1; if c[j]<=jh then begin jh:=jh-c[j]; j:=j-1; k:=k+2 end end; jh:=c[j]+jh; j:=3; cond:=false end; if (kh<kn) and (j<>0) then begin j:=j-1; c2:=c1; if j=1 then begin c1:=c1*ck+s1*sk; s1:=s1*ck-c2*sk end else begin c1:=(c1-s1)*sq; s1:=(c2+s1)*sq end; c2:=c1*c1-s1*s1; s2:=2*c1*s1; c3:=c2*c1-s2*s1; s3:=c2*s1+s2*c1 end end until (kh>=kn) or not cond until kh>=kn until kn>=n; j:=m-1; l:=j; kh:=0; i:=0; m:=m-1; lim:=(m+2) shr 1; if l>0 then repeat k0:=c[j]+kh; kn:=k0; jh:=c[m-j]; k1:=kh+jh; repeat k:=k1+jh; repeat z1:=f[k1]; f[k1]:=f[k0]; f[k0]:=z1; k1:=k1+1; k0:=k0+1 until k1>=k; k1:=k1+jh; k0:=k0+jh until k1>=kn; cond:=true; if j>lim then begin j:=j-1; i:=i+1; lst[i]:=j; cond:=false end else begin kh:=k0; if i>0 then begin j:=lst[i]; i:=i-1; cond:=false end else if kh<n then begin j:=l; cond:=false end end until cond end end; Literatura 1. D.F.Elliott, K.R.Rao, Fast Transforms, Academic Press , INC 2. Oppenhaim, R.W. Shafer: „Cyfrowe przetwarzanie sygnałów.” ,WKiŁ Warszawa 1979