triển khai FFT radix-4


8

Tôi đã triển khai FFT radix-4 FFT và thấy rằng tôi cần thực hiện một số thao tác với các điều khoản đầu ra để làm cho nó khớp với một dft.

Mã của tôi là một triển khai khá trực tiếp của công thức ma trận nên tôi không rõ vấn đề là gì

//                                | 
// radix-4 butterfly matrix form  |  complex multiplication
//                                | 
//        +-          -+ +-  -+   |    a+ib
// X[0] = | 1  1  1  1 | |x[0]|   |  * c+id
// X[1] = | 1 -i -1  i | |x[1]|   |    -------
// X[2] = | 1 -1  1 -1 | |x[2]|   |    ac + ibc
// X[3] = | 1  i -1 -i | |x[3]|   |         iad - bd
//        +-          -+ +-  -+   |    ------------------
//                                |    (ac-bd) + i(bc+ad)  
//                                | 

Bất cứ ai có thể phát hiện ra nơi tôi đã đi sai?

Cảm ơn,

-David

typedef double fp; // base floating-point type


// naiive N-point DFT implementation as reference to check fft implementation against
//
void dft(int inv, struct cfp *x, struct cfp *y, int N) {

  long int i, j;
  struct cfp w;
  fp ang;

  for(i=0; i<N; i++) { // do N-point FFT/IFFT
    y[i].r = y[i].i = 0;
    if (inv) ang =  2*PI*(fp)i/(fp)N;
    else     ang = -2*PI*(fp)i/(fp)N;
    for (j=0; j<N; j++) {
      w.r = cos(j*ang);
      w.i = sin(j*ang);
      y[i].r += (x[j].r * w.r - x[j].i * w.i);
      y[i].i += (x[j].r * w.i + x[j].i * w.r);
    }
  }

  // scale output in the case of an IFFT
  if (inv) {  
    for (i=0; i<N; i++) {
      y[i].r = y[i].r/(fp)N;
      y[i].i = y[i].i/(fp)N;
    }
  }

} // dft()


void r4fft4(int inv, int reorder, struct cfp *x, struct cfp *y) {
  struct cfp x1[4], w[4];
  fp         ang, temp;
  int        i;

  //                                | 
  // radix-4 butterfly matrix form  |  complex multiplication
  //                                | 
  //        +-          -+ +-  -+   |    a+ib
  // y[0] = | 1  1  1  1 | |x[0]|   |  * c+id
  // y[1] = | 1 -i -1  i | |x[1]|   |    -------
  // y[2] = | 1 -1  1 -1 | |x[2]|   |    ac + ibc
  // y[3] = | 1  i -1 -i | |x[3]|   |         iad - bd
  //        +-          -+ +-  -+   |    ------------------
  //                                |    (ac-bd) + i(bc+ad)  
  //                                | 

  if (inv) ang =  2*PI/(fp)4; // invert sign for IFFT
  else     ang = -2*PI/(fp)4;
  //
  w[1].r = cos(ang*1); w[1].i = sin(ang*1); // twiddle1 = exp(-2*pi/4 * 1);
  w[2].r = cos(ang*2); w[2].i = sin(ang*2); // twiddle2 = exp(-2*pi/4 * 2);
  w[3].r = cos(ang*3); w[3].i = sin(ang*3); // twiddle3 = exp(-2*pi/4 * 3);

  //         *1       *1       *1       *1
  y[0].r  = x[0].r + x[1].r + x[2].r + x[3].r;
  y[0].i  = x[0].i + x[1].i + x[2].i + x[3].i;
  //         *1       *-i      *-1      *i
  x1[1].r = x[0].r + x[1].i - x[2].r - x[3].i;               
  x1[1].i = x[0].i - x[1].r - x[2].i + x[3].r;               
  //         *1       *-1      *1       *-1
  x1[2].r = x[0].r - x[1].r + x[2].r - x[3].r;
  x1[2].i = x[0].i - x[1].i + x[2].i - x[3].i;
  //         *1       *i       *-1      *-i
  x1[3].r = x[0].r - x[1].i - x[2].r + x[3].i;
  x1[3].i = x[0].i + x[1].r - x[2].i - x[3].r;
  //
  y[1].r = x1[1].r*w[1].r - x1[1].i*w[1].i; // scale radix-4 output
  y[1].i = x1[1].i*w[1].r + x1[1].r*w[1].i;
  //
  y[2].r = x1[2].r*w[2].r - x1[2].i*w[2].i; // scale radix-4 output
  y[2].i = x1[2].i*w[2].r + x1[2].r*w[2].i;
  //
  y[3].r = x1[3].r*w[3].r - x1[3].i*w[3].i; // scale radix-4 output
  y[3].i = x1[3].i*w[3].r + x1[3].r*w[3].i;

  // reorder output stage ... mystery as to why I need this
  if (reorder) {
    temp = y[1].r; 
    y[1].r = -1*y[1].i; 
    y[1].i = temp;
    //
    y[2].r = -1*y[2].r; 
    //
    temp = y[3].r; 
    y[3].r = y[3].i; 
    y[3].i = -1*temp;
  }

  // scale output for inverse FFT
  if (inv) {
    for (i=0; i<4; i++) { // scale output by 1/N for IFFT
      y[i].r = y[i].r/(fp)4;
      y[i].i = y[i].i/(fp)4;
    }
  }

} // r4fft4()

1
Bạn cũng có thể chỉ cho chúng tôi một số dữ liệu đầu vào và đầu ra mẫu cho từng loại không?
Paul R

1
Ngoài vấn đề thứ tự đảo ngược bit, còn có sự khác biệt

Đây không phải là vấn đề đặt hàng lại vì việc đặt hàng lại cho phép các mục của y như tôi hiểu. Tôi có thể khắc phục sự cố nếu tôi thay đổi ang = -2 * PI; thay vì ang = -2 * PI / (fp) 4; Tôi không cần phải sắp xếp lại các điều khoản và kiểm tra tính nhất quán của tôi so với các lỗi vượt qua với 0 lỗi. Tôi nghĩ rằng điều này tương đương với một sự thay đổi pha 90deg cho các yếu tố twiddle. Tuy nhiên, điều này dường như không phù hợp với toán học ... tôi còn thiếu gì?

Câu trả lời:


2

Tôi vừa chuyển một fft DIF radix-4 từ mã S. Burrus Fortran sang Java. Trên thực tế, nó thiếu một số tối ưu hóa, trước hết là yếu tố twiddle điều khiển bảng (các yếu tố sin và cos nên được tính toán trước). Điều này sẽ tăng tốc fft hơn một chút (có thể 50%). Tôi đã hack một chút cho điều đó nhưng nếu ai đó có câu trả lời đúng tôi sẽ rất vui và biết ơn. Tôi sẽ đăng mã càng tối ưu càng tốt, tôi hy vọng có thể với một số bài kiểm tra tốc độ so với thuật toán radix-2.

Hơn nữa, phép nhân với 1 và sqrt (-1) không bị xóa. Loại bỏ chúng sẽ tăng tốc hơn một chút. Nhưng tổng thể IMHO radix-4 dường như không nhanh hơn 25% so với radix-2, vì vậy tôi không biết liệu tỷ lệ tốc độ / độ phức tạp có thực sự đáng giá hay không. Hãy nhớ rằng các thư viện được tối ưu hóa như FFTW phần lớn có sẵn và được sử dụng, vì vậy nỗ lực này có thể chỉ là một 'phân kỳ' cá nhân!

Đây là mã java. Chuyển nó sang C, C ++ hoặc C # nên rất dễ dàng.

public static void FFTR4(double[] X, double[] Y, int N, int M) {
    // N = 4 ^ M
    int N1,N2;
    int I1, I2, I3;
    double CO1,CO2,CO3,SI1,SI2,SI3;
    double A,B,C,E;
    double R1,R2,R3,R4;
    double S1,S2,S3,S4;
    // N = 1 << (M+M);
    N2 = N;
    I2 = 0; I3 = 0;
    for (int K=0; K<M; ++K) {
        N1 = N2;
        N2 = N2 / 4;
        E = PI2 / (double)N1;
        A = 0.0;
        for (int J=0; J < N2; ++J) {
            A = J*E;
            B = A + A;
            C = A + B;
            //Should be pre-calculated for optimization
            CO1 = Math.cos(A);
            CO2 = Math.cos(B);
            CO3 = Math.cos(C);
            SI1 = Math.sin(A);
            SI2 = Math.sin(B);
            SI3 = Math.sin(C);
            for (int I = J; I<N; I+=N1) {
                I1 = I + N2;
                I2 = I1 + N2;
                I3 = I2 + N2;
                R1 = X[I] + X[I2];
                R3 = X[I] - X[I2];
                S1 = Y[I] + Y[I2];
                S3 = Y[I] - Y[I2];
                R2 = X[I1] + X[I3];
                R4 = X[I1] - X[I3];
                S2 = Y[I1] + Y[I3];
                S4 = Y[I1] - Y[I3];
                X[I] = R1 + R2;
                R2 = R1 - R2;
                R1 = R3 - S4;
                R3 = R3 + S4;
                Y[I] = S1 + S2;
                S2 = S1 - S2;
                S1 = S3 + R4;
                S3 = S3 - R4;
                X[I1] = CO1*R3 + SI1*S3;
                Y[I1] = CO1*S3 - SI1*R3;
                X[I2] = CO2*R2 + SI2*S2;
                Y[I2] = CO2*S2 - SI2*R2;
                X[I3] = CO3*R1 + SI3*S1;
                Y[I3] = CO3*S1 - SI3*R1;
            }
        }
    }

    // Radix-4 bit-reverse
    double T;
    int J = 0;
    N2 = N>>2;
    for (int I=0; I < N-1; I++) {
        if (I < J) {
            T = X[I];
            X[I] = X[J];
            X[J] = T;
            T = Y[I];
            Y[I] = Y[J];
            Y[J] = T;
        }
        N1 = N2;
        while ( J >= 3*N1 ) {
            J -= 3*N1;
            N1 >>= 2;
        }
        J += N1;
    }
}

Dưới đây là mã gốc Radix-4 DIF FORTRAN của Sidney Burrus:

Radix-4, DIF, Một con bướm FFT


5

Đầu tiên, 'bướm radix-4' được cho là của bạn là DFT 4 điểm, không phải là FFT. Nó có 16 thao tác phức tạp (tức là: N bình phương). Một FFT 4 điểm thông thường sẽ chỉ có Nlog (cơ sở 2) N (= 8 cho N = 4). Thứ hai, bạn có một số yếu tố được cho là w [] .r và w [] .i 'scale' không thuộc về. Có lẽ bạn đã thu được chúng từ một con bướm radix-4 được hiển thị trong một biểu đồ lớn hơn. Một con bướm như vậy sẽ có một số câu đố xen kẽ được gắn vào nó, nhưng chúng không thực sự là một phần của con bướm. Một FFT 4 điểm chỉ có một con bướm bên trong là -j khi được thiết kế cho một FFT lũy thừa âm.

Thay vì cố gắng sửa mã của bạn, thật dễ dàng để viết mã của riêng tôi, như được hiển thị bên dưới (trình biên dịch DevC ++; đầu ra được nối vào cuối mã):

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
void fft4(double* r, double* i);    // prototype declaration
int main (int nNumberofArgs, char* pszArgs[ ] ) { // arguments needed for Dev C++ I/O

double r[4] = {1.5, -2.3, 4.65, -3.51}, i[4] = {-1.0, 2.6, 3.75, -2.32} ;
long n, k, j;      double  yr[4] = {0.}, yi[4] = {0.};
double ang, C, S, twopi = 6.2831853071795865;

cout<<"\n original real/imag data";
cout<<"\n n         r[n]            i[n]\n";
for (n = 0; n < 4; n++)  {
    printf("%2d\t%9.4f\t%9.4f\n",n,r[n],i[n]);
} //end for loop over n

// 4 point DFT
for (k = 0; k < 4; k++) {
    ang = twopi*k/4;
    for (j = 0; j < 4; j++) {
        C = cos(j*ang);       S = sin(j*ang);
        yr[k] = yr[k] + r[j]*C + i[j]*S;   // ( C - jS )*( r + ji )
        yi[k] = yi[k] + i[j]*C - r[j]*S;   // = ( rC + iS ) + j( iC - rS )
    }
}

cout<<"\n 4 point DFT results";
cout<<"\n n         yr[n]           yi[n]           amplitude       phase(radians)\n";
double amp, phase;
for (n = 0; n < 4; n++)  {
    yr[n] = yr[n]/4 ;      yi[n] = yi[n]/4 ;  // scale outputs
    amp = sqrt( yr[n]*yr[n] + yi[n]*yi[n] ) ;
    phase = atan2( yi[n], yr[n] ) ; 
    printf("%2d\t%9.4f\t%9.4f\t%9.4f\t%9.4f\n",n,yr[n],yi[n],amp,phase);
} //end for loop over n

fft4(r, i) ;

cout<<"\n 4 point FFT results";
cout<<"\n n         r[n]            i[n]            amplitude       phase(radians)\n";

for (n = 0; n < 4; n++)  {
    r[n] = r[n]/4 ;      i[n] = i[n]/4 ;  // scale outputs
    amp = sqrt( r[n]*r[n] + i[n]*i[n] ) ;
    phase = atan2( i[n], r[n] ) ; 
    printf("%2d\t%9.4f\t%9.4f\t%9.4f\t%9.4f\n",n,r[n],i[n],amp,phase);
} //end for loop over n

fft4(i, r); // this is an inverse FFT (complex in/out routine)

cout<<"\n 4 point inverse FFT results";
cout<<"\n n         r[n]            i[n]\n";
for (n = 0; n < 4; n++)  {
    printf("%2d\t%9.4f\t%9.4f\n",n,r[n],i[n]);
} //end for loop over n

system ("PAUSE");
return 0;
} // end main
//************************ fft4 **********
void fft4(double* r, double* i) {
double t;

t = r[0]; r[0] = t + r[2]; r[2] = t - r[2];
t = i[0]; i[0] = t + i[2]; i[2] = t - i[2];
t = r[1]; r[1] = t + r[3]; r[3] = t - r[3];
t = i[1]; i[1] = t + i[3]; i[3] = t - i[3];

t = r[3]; r[3] = i[3]; i[3] = -t; // (r + ji)*(-j)

t = r[0]; r[0] = t + r[1]; r[1] = t - r[1];
t = i[0]; i[0] = t + i[1]; i[1] = t - i[1];
t = r[2]; r[2] = t + r[3]; r[3] = t - r[3];
t = i[2]; i[2] = t + i[3]; i[3] = t - i[3];

t = r[1]; r[1] = r[2]; r[2] = t;  // swap 1
t = i[1]; i[1] = i[2]; i[2] = t;  //  and 2
} // end fft4




 original real/imag data
 n         r[n]            i[n]
 0         1.5000         -1.0000
 1        -2.3000          2.6000
 2         4.6500          3.7500
 3        -3.5100         -2.3200

 4 point DFT results
 n         yr[n]           yi[n]           amplitude       phase(radians)
 0         0.0850          0.7575          0.7623          1.4591
 1         0.4425         -1.4900          1.5543         -1.2821
 2         2.9900          0.6175          3.0531          0.2037
 3        -2.0175         -0.8850          2.2031         -2.7282

 4 point FFT results
 n         r[n]            i[n]            amplitude       phase(radians)
 0         0.0850          0.7575          0.7623          1.4591
 1         0.4425         -1.4900          1.5543         -1.2821
 2         2.9900          0.6175          3.0531          0.2037
 3        -2.0175         -0.8850          2.2031         -2.7282

 4 point inverse FFT results
 n         r[n]            i[n]
 0         1.5000         -1.0000
 1        -2.3000          2.6000
 2         4.6500          3.7500
 3        -3.5100         -2.3200

Đầu tiên, dữ liệu đầu vào (4 thực, 4 ảo) được in ra. Sau đó, một DFT 4 điểm được thực hiện. Kết quả (yr [] và yi [] cộng với amp / pha) được in ra. Vì dữ liệu gốc r [] và i [] không bị ghi đè khi thực hiện DFT, các đầu vào đó được sử dụng lại làm đầu vào cho FFT 4 điểm. Lưu ý rằng cái sau có ít hoạt động +/- hơn DFT.

Mã cho FFT không đặc biệt thanh lịch cũng không hiệu quả - có nhiều cách để làm bướm. Đoạn mã trên tương ứng với bốn con bướm radix-2 được hiển thị trong cuốn sách Rabiner và Gold'sory Lý thuyết và ứng dụng xử lý tín hiệu số (tr. 580, Hình 10.9), với các twiddles được sửa đổi để phản ánh số mũ âm (những cái được sử dụng cho con số trong cuốn sách là tích cực). Lưu ý rằng chỉ có một chuỗi -j trong mã và điều này không yêu cầu nhân (đó là thay đổi hoán đổi / ký hiệu).

Sau FFT, kết quả được in. Chúng giống như DFT

Và cuối cùng, các kết quả được thu nhỏ từ FFT được sử dụng làm đầu vào cho một FFT nghịch đảo. Điều này được thực hiện thông qua phương thức 'trao đổi' hoặc 'đảo ngược danh sách' (nghĩa là: nếu FFT (r, i) là FFT chuyển tiếp, thì FFT (i, r) là một nghịch đảo - tất nhiên, được cung cấp rằng FFT là có khả năng xử lý các đầu vào / đầu ra phức tạp - nói cách khác - không có thói quen 'chỉ thực', mà thường cho rằng đầu vào tưởng tượng bằng không). Phương pháp này đã được mô tả gần 25 năm trước trong:

P. Duhamel, B. Piron, JM Etcheto, Hồi tính toán DFT nghịch đảo, Giao dịch của IEEE về Âm học, Xử lý lời nói và Tín hiệu, tập. 36, tháng 2 năm 1988, trang 285-286.

Kết quả của nghịch đảo sau đó được in ra. Nó giống như dữ liệu đầu vào ban đầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.