J'ai implémenté une FFT radix-4 à 4 points et j'ai constaté que je devais manipuler les termes de sortie pour l'adapter à un dft.
Mon code est une implémentation assez directe de la formulation matricielle, donc je ne sais pas quel est le problème
// |
// 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)
// |
Quelqu'un peut-il repérer où je me suis trompé?
Merci,
-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()
Réponses:
Je viens de porter un fft radix-4 DIF du code S. Burrus Fortran vers Java. En fait, il manque plusieurs optimisations, tout d'abord le facteur twiddle piloté par table (les facteurs sin et cos doivent être pré-calculés). Cela devrait accélérer le fft un peu plus (peut-être 50%). Je dois pirater un peu pour cela, mais si quelqu'un a la bonne réponse, je serai très heureux et reconnaissant. Je posterai le code optimisé dès que possible, j'espère peut-être avec des tests de vitesse vs l'algorithme radix-2.
De plus, les multiplications par 1 et sqrt (-1) ne sont pas supprimées. Les supprimer accélérera un peu plus. Mais globalement IMHO radix-4 ne semble pas être plus de 25% plus rapide qu'un radix-2, donc je ne sais pas si le rapport vitesse / complexité vaut vraiment la peine. Gardez à l'esprit que des bibliothèques très optimisées comme FFTW sont largement disponibles et utilisées, donc cet effort pourrait être juste un «détournement» personnel!
Voici le code java. Le porter en C, C ++ ou C # devrait être très facile.
Voici le code original Radix-4 DIF FORTRAN de Sidney Burrus:
Radix-4, DIF, un papillon FFT
la source
Tout d'abord, votre supposé «papillon radix-4» est un DFT à 4 points, pas un FFT. Il a 16 opérations complexes (ie: N au carré). Une FFT typique à 4 points aurait seulement Nlog (base 2) N (= 8 pour N = 4). Deuxièmement, vous avez des facteurs d'échelle supposés w [] .r et w [] .i qui n'appartiennent pas. Peut-être les avez-vous obtenus à partir d'un papillon Radix-4 montré dans un graphique plus grand. Un tel papillon aurait des twiddles inter-étages, mais ils ne font pas partie du papillon. Une FFT à 4 points n'a qu'un papillon interne de -j lorsqu'elle est conçue pour une FFT à exposant négatif.
Plutôt que d'essayer de corriger votre code, il est tout aussi facile d'écrire le mien, comme indiqué ci-dessous (compilateur DevC ++; sorties ajoutées à la fin du code):
Tout d'abord, les données d'entrée (4 réelles, 4 imaginaires) sont imprimées. Ensuite, une DFT à 4 points est prise. Les résultats (yr [] et yi [] plus ampli / phase) sont imprimés. Étant donné que les données d'origine r [] et i [] n'ont pas été écrasées lors de l'exécution de la DFT, ces entrées sont réutilisées en tant qu'entrées dans la FFT à 4 points. Notez que ce dernier a moins d'opérations +/- que le DFT.
Le code de la FFT n'est pas particulièrement élégant ni efficace - il existe de nombreuses façons de faire des papillons. Le code ci-dessus correspond aux quatre papillons radix-2 présentés dans le livre de Rabiner et Gold «Théorie et application du traitement numérique du signal» (p. 580, Fig. 10.9), avec des twiddles modifiés pour refléter un exposant négatif (ceux utilisés pour le chiffre dans le livre étaient positifs). Notez qu'il n'y a qu'un seul twiddle de -j dans le code, et cela ne nécessite pas de multiplication (c'est un changement de swap / signe).
Après la FFT, les résultats sont imprimés. Ils sont les mêmes que le DFT
Et enfin, les résultats mis à l'échelle de la FFT sont utilisés comme entrées d'une FFT inverse. Ceci est accompli via la méthode «échange» ou «inverser la liste» (c'est-à-dire: si la FFT (r, i) est une FFT vers l'avant, alors la FFT (i, r) est un inverse - à condition, bien sûr, que la FFT soit capable de gérer des entrées / sorties complexes - en d'autres termes - pas de routines «réelles uniquement», qui supposent généralement que les entrées imaginaires sont nulles). Cette méthode a été décrite il y a près de 25 ans dans:
P. Duhamel, B. Piron, JM Etcheto, «On Computing the Inverse DFT», IEEE Transactions on Acoustics, Speech and Signal Processing, vol. 36, février 1988, p. 285-286.
Le résultat de l'inverse est ensuite imprimé. C'est la même chose que les données d'entrée d'origine.
la source