Ich habe eine 4-Punkt-Radix-4-FFT implementiert und festgestellt, dass ich einige Manipulationen an den Ausgangstermen vornehmen muss, damit sie mit einer dft übereinstimmen.
Mein Code ist eine ziemlich direkte Implementierung der Matrixformulierung, daher ist mir nicht klar, wo das Problem liegt
// |
// 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)
// |
Kann jemand erkennen, wo ich falsch gelaufen bin?
Vielen Dank,
-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()
Antworten:
Ich habe gerade einen Radix-4-DIF-FFT von S. Burrus Fortran-Code nach Java portiert. Tatsächlich fehlen mehrere Optimierungen, vor allem der tabellengesteuerte Twiddle-Faktor (Sin- und Cos-Faktoren sollten vorberechnet werden). Dies sollte das fft etwas beschleunigen (vielleicht 50%). Ich muss ein bisschen dafür hacken, aber wenn jemand die richtige Antwort hat, bin ich sehr glücklich und dankbar. Ich werde den optimierten Code so schnell wie möglich veröffentlichen. Ich hoffe, dass ich einige Geschwindigkeitstests gegen den Radix-2-Algorithmus durchführen kann.
Außerdem werden die Multiplikationen mit 1 und sqrt (-1) nicht entfernt. Wenn Sie sie entfernen, wird die Geschwindigkeit etwas erhöht. Aber insgesamt scheint IMHO Radix-4 nicht mehr als 25% schneller zu sein als ein Radix-2, daher weiß ich nicht, ob sich das Verhältnis Geschwindigkeit / Komplexität wirklich lohnt. Denken Sie daran, dass sehr optimierte Bibliotheken wie FFTW weitgehend verfügbar sind und verwendet werden. Diese Bemühungen können also nur eine persönliche "Umleitung" sein!
Hier ist der Java-Code. Das Portieren nach C, C ++ oder C # sollte sehr einfach sein.
Hier ist der Original Radix-4 DIF FORTRAN Code von Sidney Burrus:
Radix-4, DIF, One Butterfly FFT
quelle
Erstens ist Ihr angeblicher "Radix-4-Schmetterling" eine 4-Punkt-DFT, keine FFT. Es hat 16 komplexe Operationen (dh: N im Quadrat). Eine typische 4-Punkt-FFT hätte nur Nlog (Basis 2) N (= 8 für N = 4). Zweitens haben Sie einige vermeintliche Skalierungsfaktoren w [] .r und w [] .i, die nicht dazu gehören. Vielleicht haben Sie sie von einem Radix-4-Schmetterling erhalten, der in einer größeren Grafik dargestellt ist. An einen solchen Schmetterling wären einige Zwischenstufen-Twiddles angehängt, aber sie sind eigentlich nicht Teil des Schmetterlings. Eine 4-Punkt-FFT hat nur einen internen Schmetterling von -j, wenn sie für eine negative Exponenten-FFT ausgelegt ist.
Anstatt zu versuchen, Ihren Code zu reparieren, ist es genauso einfach, meinen eigenen zu schreiben, wie unten gezeigt (DevC ++ - Compiler; Ausgaben am Ende des Codes angehängt):
Zunächst werden die Eingabedaten (4 reelle, 4 imaginäre) ausgedruckt. Dann wird eine 4-Punkt-DFT genommen. Die Ergebnisse (yr [] und yi [] plus Ampere / Phase) werden ausgedruckt. Da die Originaldaten r [] und i [] bei der DFT nicht überschrieben wurden, werden diese Eingaben als Eingaben für die 4-Punkt-FFT wiederverwendet. Beachten Sie, dass letztere weniger +/- Operationen als die DFT hat.
Der Code für die FFT ist weder besonders elegant noch effizient - es gibt viele Möglichkeiten, Schmetterlinge zu machen. Der obige Code entspricht den vier Radix-2-Schmetterlingen, die in Rabiner und Golds Buch „Theorie und Anwendung der digitalen Signalverarbeitung“ (S. 580, Abb. 10.9) gezeigt sind Zahlen im Buch waren positiv). Beachten Sie, dass der Code nur ein Twiddle von -j enthält und dies keine Multiplikation erfordert (es handelt sich um einen Swap- / Vorzeichenwechsel).
Nach der FFT werden die Ergebnisse gedruckt. Sie sind die gleichen wie die DFT
Und schließlich werden die skalierten Ergebnisse der FFT als Eingaben für eine inverse FFT verwendet. Dies wird über die Methode 'Austausch' oder 'Liste umkehren' erreicht (dh wenn FFT (r, i) eine Vorwärts-FFT ist, dann ist FFT (i, r) eine Umkehrung - vorausgesetzt natürlich, dass die FFT ist fähig, komplexe Ein- / Ausgänge zu handhaben - mit anderen Worten - keine "nur realen" Routinen, die normalerweise davon ausgehen, dass imaginäre Eingänge Null sind). Diese Methode wurde vor fast 25 Jahren beschrieben in:
P. Duhamel, B. Piron, JM Etcheto, "Über die Berechnung der inversen DFT", IEEE Transactions on Acoustics, Speech and Signal Processing, vol. 36, Februar 1988, S. 285-286.
Das Ergebnis der Umkehrung wird dann ausgedruckt. Dies entspricht den ursprünglichen Eingabedaten.
quelle