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