iPhone ускоряет каркас FFT против Matlab FFT
У меня нет большого математического фона, но часть проекта, над которым я работаю, требует БПФ одного вектора. Функция matlab fft(x) работает точно для того, что мне нужно, но после попытки настроить функции fft в Accelerate Framework я получаю совершенно неточные результаты. Если у кого-то есть больше опыта / опыта в Accelerate Framework fft, я мог бы действительно использовать некоторую помощь, чтобы выяснить, что я делаю неправильно. Я основал свою настройку FFT на примере, который я нашел в Google, но не было никаких учебных пособий или чего-либо, что могло бы привести к другим результатам.
РЕДАКТИРОВАТЬ 1: Изменено вокруг некоторых вещей на основе ответов до сих пор. Кажется, что он делает вычисления, но не выводит их в какой-либо степени близко к Matlab.
Это документация для fft для matlab: http://www.mathworks.com/help/techdoc/ref/fft.html
** ПРИМЕЧАНИЕ: для примера, массив x будет {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} в обоих примерах
Код Matlab:
x = fft(x)
Выход Matlab:
x =
1.0e+02 *
Columns 1 through 4
1.3600 -0.0800 + 0.4022i -0.0800 + 0.1931i -0.0800 + 0.1197i
Columns 5 through 8
-0.0800 + 0.0800i -0.0800 + 0.0535i -0.0800 + 0.0331i -0.0800 + 0.0159i
Columns 9 through 12
-0.0800 -0.0800 - 0.0159i -0.0800 - 0.0331i -0.0800 - 0.0535i
Columns 13 through 16
-0.0800 - 0.0800i -0.0800 - 0.1197i -0.0800 - 0.1931i -0.0800 - 0.4022i
Apple Accelerate Framework: http://developer.apple.com/library/mac/#documentation/Accelerate/Reference/vDSPRef/Reference/reference.html#//apple_ref/doc/uid/TP40009464
Код цели C:
int log2n = log2f(16);
FFTSetupD fftSetup = vDSP_create_fftsetupD (log2n, kFFTRadix2);
DSPDoubleSplitComplex fft_data;
fft_data.realp = (double *)malloc(8 * sizeof(double));
fft_data.imagp = (double *)malloc(8 * sizeof(double));
vDSP_ctoz((COMPLEX *) ffx, 2, &fft_data, 1, nOver2); //split data (1- 16) into odds and evens
vDSP_fft_zrip (fftSetup, &fft_data, 1, log2n, kFFTDirection_Forward); //fft forward
vDSP_fft_zrip (fftSetup, &fft_data, 1, log2n, kFFTDirection_Inverse); //fft inverse
vDSP_ztoc(&fft_data, 2, (COMPLEX *) ffx, 1, nOver2); //combine complex back into real numbers
Цель C выходной:
ffx теперь содержит:
272.000000
-16.000000
-16.000000
-16.000000
0.000000
0.000000
0.000000
0.000000
0.000000
10.000000
11.000000
12.000000
13.000000
14.000000
15.000000
16.000000
3 ответа
Одна большая проблема: массивы C индексируются от 0, в отличие от массивов MATLAB, которые основаны на 1. Так что вам нужно изменить свой цикл с
for(int i = 1; i <= 16; i++)
в
for(int i = 0; i < 16; i++)
Вторая большая проблема - вы смешиваете одинарную точность (float
) и двойной точности (double
) процедуры. Ваши данные double
так что вы должны использовать vDSP_ctozD
не vDSP_ctoz
, а также vDSP_fft_zripD
скорее, чем vDSP_fft_zrip
, так далее.
Еще одна вещь, на которую следует обратить внимание: разные реализации FFT используют разные определения формулы DFT, особенно в отношении коэффициента масштабирования. Похоже, что БПФ MATLAB включает коррекцию масштабирования 1/N, чего нет в большинстве других БПФ.
Вот полный рабочий пример, выход которого соответствует Octave (клон MATLAB):
#include <stdio.h>
#include <stdlib.h>
#include <Accelerate/Accelerate.h>
int main(void)
{
const int log2n = 4;
const int n = 1 << log2n;
const int nOver2 = n / 2;
FFTSetupD fftSetup = vDSP_create_fftsetupD (log2n, kFFTRadix2);
double *input;
DSPDoubleSplitComplex fft_data;
int i;
input = malloc(n * sizeof(double));
fft_data.realp = malloc(nOver2 * sizeof(double));
fft_data.imagp = malloc(nOver2 * sizeof(double));
for (i = 0; i < n; ++i)
{
input[i] = (double)(i + 1);
}
printf("Input\n");
for (i = 0; i < n; ++i)
{
printf("%d: %8g\n", i, input[i]);
}
vDSP_ctozD((DSPDoubleComplex *)input, 2, &fft_data, 1, nOver2);
printf("FFT Input\n");
for (i = 0; i < nOver2; ++i)
{
printf("%d: %8g%8g\n", i, fft_data.realp[i], fft_data.imagp[i]);
}
vDSP_fft_zripD (fftSetup, &fft_data, 1, log2n, kFFTDirection_Forward);
printf("FFT output\n");
for (i = 0; i < nOver2; ++i)
{
printf("%d: %8g%8g\n", i, fft_data.realp[i], fft_data.imagp[i]);
}
for (i = 0; i < nOver2; ++i)
{
fft_data.realp[i] *= 0.5;
fft_data.imagp[i] *= 0.5;
}
printf("Scaled FFT output\n");
for (i = 0; i < nOver2; ++i)
{
printf("%d: %8g%8g\n", i, fft_data.realp[i], fft_data.imagp[i]);
}
printf("Unpacked output\n");
printf("%d: %8g%8g\n", 0, fft_data.realp[0], 0.0); // DC
for (i = 1; i < nOver2; ++i)
{
printf("%d: %8g%8g\n", i, fft_data.realp[i], fft_data.imagp[i]);
}
printf("%d: %8g%8g\n", nOver2, fft_data.imagp[0], 0.0); // Nyquist
return 0;
}
Выход:
Input
0: 1
1: 2
2: 3
3: 4
4: 5
5: 6
6: 7
7: 8
8: 9
9: 10
10: 11
11: 12
12: 13
13: 14
14: 15
15: 16
FFT Input
0: 1 2
1: 3 4
2: 5 6
3: 7 8
4: 9 10
5: 11 12
6: 13 14
7: 15 16
FFT output
0: 272 -16
1: -16 80.4374
2: -16 38.6274
3: -16 23.9457
4: -16 16
5: -16 10.6909
6: -16 6.62742
7: -16 3.1826
Scaled FFT output
0: 136 -8
1: -8 40.2187
2: -8 19.3137
3: -8 11.9728
4: -8 8
5: -8 5.34543
6: -8 3.31371
7: -8 1.5913
Unpacked output
0: 136 0
1: -8 40.2187
2: -8 19.3137
3: -8 11.9728
4: -8 8
5: -8 5.34543
6: -8 3.31371
7: -8 1.5913
8: -8 0
По сравнению с Octave получаем:
octave-3.4.0:15> x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
x =
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
octave-3.4.0:16> fft(x)
ans =
Columns 1 through 7:
136.0000 + 0.0000i -8.0000 + 40.2187i -8.0000 + 19.3137i -8.0000 + 11.9728i -8.0000 + 8.0000i -8.0000 + 5.3454i -8.0000 + 3.3137i
Columns 8 through 14:
-8.0000 + 1.5913i -8.0000 + 0.0000i -8.0000 - 1.5913i -8.0000 - 3.3137i -8.0000 - 5.3454i -8.0000 - 8.0000i -8.0000 - 11.9728i
Columns 15 and 16:
-8.0000 - 19.3137i -8.0000 - 40.2187i
octave-3.4.0:17>
Обратите внимание, что выходные данные с 9 по 16 являются просто комплексным сопряженным зеркальным отображением или нижними 8 членами, как это ожидается в случае БПФ с реальным входом.
Также обратите внимание, что нам нужно было масштабировать БПФ vDSP с коэффициентом 2 - это связано с тем фактом, что это БПФ от реального к сложному, которое основано на N/2-точечном БПФ от сложного к сложному, поэтому выходы масштабируются на N/2, тогда как нормальное FFT будет масштабироваться на N.
Я думаю, что это также может быть проблемой упаковки массивов. Я только что посмотрел на их пример кода, и я вижу, что они продолжают вызывать процедуры преобразования, такие как
vDSP_ctoz
Вот пример ссылки на код от Apple: http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/vDSP_Programming_Guide/SampleCode/SampleCode.html#//apple_ref/doc/uid/TP40005147-CH205- CIAEJIGF
Я не думаю, что это полный ответ, но я также согласен с Полом Р.
Кстати, просто любопытно, если вы перейдете на Wolfram Alpha, они дадут совершенно другой ответ для FFT{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
В MATLAB похоже, что вы делаете fft из 16 реальных значений {1+0i, 2+0i, 3+0i и т. Д.}}, Тогда как в Accelerate вы делаете fft из 8 комплексных значений {1+2i, 3+4i, 5+6i и т.д...}