Реализация дискретного преобразования Фурье - БПФ

Я пытаюсь сделать проект по обработке звука, и мне нужно поместить частоты в другой домен. Я попытался реализовать БПФ, но у меня ничего не вышло. Я попытался понять z-преобразование, но это тоже не помогло. Я прочитал и обнаружил, что DFT намного проще для понимания, особенно алгоритм. Поэтому я закодировал алгоритм, используя примеры, но я не знаю или не думаю, что результат правильный. (У меня здесь нет Matlab, и я не могу найти никаких ресурсов для его тестирования), и мне было интересно, знаете ли вы, ребята, иду ли я в правильном направлении. Вот мой код:

#include <iostream>
#include <complex>
#include <vector>

using namespace std;

const double PI = 3.141592;

vector< complex<double> > DFT(vector< complex<double> >& theData)
{
// Define the Size of the read in vector
const int S = theData.size();

// Initalise new vector with size of S
vector< complex<double> > out(S, 0);
for(unsigned i=0; (i < S); i++)
{
    out[i] = complex<double>(0.0, 0.0);
    for(unsigned j=0; (j < S); j++)
    {
        out[i] += theData[j] * polar<double>(1.0, - 2 * PI * i * j / S);
    }
}

return out;
}

int main(int argc, char *argv[]) {

vector< complex<double> > numbers;

numbers.push_back(102023);
numbers.push_back(102023);
numbers.push_back(102023);
numbers.push_back(102023);

vector< complex<double> > testing = DFT(numbers);

for(unsigned i=0; (i < testing.size()); i++)
{
    cout << testing[i] << endl;

}
}

Входы:

102023               102023

102023               102023

И результат:

(408092,       0)

(-0.0666812,  -0.0666812)

(1.30764e-07, -0.133362)

(0.200044,    -0.200043)

Любая помощь или совет были бы замечательными, я не жду многого, но все было бы здорово. Спасибо :)


person Phorce    schedule 03.09.2012    source источник
comment
Почему бы вам не использовать популярную библиотеку FFTW? fftw.org   -  person Emile Cormier    schedule 03.09.2012
comment
Спасибо за ответ, мне не разрешено использовать библиотеки.   -  person Phorce    schedule 03.09.2012
comment
Единственная проблема, которую я вижу, заключается в том, что вы теряете большую точность, указывая только от PI до 7 цифр. (Конечно, игнорируя производительность; для больших входных данных это будет намного медленнее, чем эквивалентное БПФ).   -  person Mike Seymour    schedule 03.09.2012


Ответы (6)


Ваш код выглядит нормально. out[0] должен представлять "постоянный ток" вашего входного сигнала. В вашем случае он в 4 раза больше, чем входной сигнал, потому что ваш коэффициент нормализации равен 1.

Остальные коэффициенты должны представлять амплитуду и фазу входного сигнала. Коэффициенты зеркалируются, то есть out [i] == out [N-i]. Вы можете проверить это с помощью следующего кода:

double frequency = 1; /* use other values like 2, 3, 4 etc. */
for (int i = 0; i < 16; i++)
    numbers.push_back(sin((double)i / 16 * frequency * 2 * PI));

Для frequency = 1 это дает:

(6.53592e-07,0)
(6.53592e-07,-8)
(6.53592e-07,1.75661e-07)
(6.53591e-07,2.70728e-07)
(6.5359e-07,3.75466e-07)
(6.5359e-07,4.95006e-07)
(6.53588e-07,6.36767e-07)
(6.53587e-07,8.12183e-07)
(6.53584e-07,1.04006e-06)
(6.53581e-07,1.35364e-06)
(6.53576e-07,1.81691e-06)
(6.53568e-07,2.56792e-06)
(6.53553e-07,3.95615e-06)
(6.53519e-07,7.1238e-06)
(6.53402e-07,1.82855e-05)
(-8.30058e-05,7.99999)

что мне кажется правильным: незначительный постоянный ток, амплитуда 8 для 1-й гармоники, незначительные амплитуды для других гармоник.

person user1202136    schedule 03.09.2012
comment
Спасибо за ваш ответ. Но ведь это не может быть НАСТОЛЬКО просто, не так ли? Математика выглядит очень сложной ... Это странно! - person Phorce; 03.09.2012
comment
@Phorce: Да, DFT действительно настолько прост. Однако оптимизировать его до БПФ гораздо сложнее. - person Mike Seymour; 04.09.2012
comment
@MikeSeymour Я заметил, что БПФ сложнее. Одна вещь, которая меня сейчас сбивает с толку, это ... Мне дан результат (num, num), но как мне выполнить вычисления с этими числами? Мне нужно еще много чего сделать! - person Phorce; 04.09.2012
comment
Как мне произвести вычисления с этими числами? очень неоднозначно. Попробуйте уточнить свой вопрос. - person user1202136; 05.09.2012

@Phorce здесь. Не думаю, что есть смысл изобретать велосипед. Однако, если вы хотите сделать это, чтобы понять методологию и получить удовольствие от написания кода самостоятельно, я могу предоставить код FORTRAN FFT, который я разработал несколько лет назад. Конечно, это не C ++ и потребуется перевод; это не должно быть слишком сложно и должно позволить вам многому научиться при этом ...

Ниже представлен алгоритм на основе Radix 4; это БПФ с основанием 4 рекурсивно разделяет ДПФ на четыре четверть длины ДПФ групп каждой четвертой временной выборки. Выходные данные этих более коротких БПФ повторно используются для вычисления многих выходных данных, что значительно снижает общие вычислительные затраты. БПФ с децимацией по частоте с основанием 4 счисления группирует каждую четвертую выходную выборку в ДПФ меньшей длины для экономии вычислений. Для БПФ с основанием 4 требуется всего 75% от количества сложных умножений, чем для БПФ с основанием 2. Дополнительную информацию см. здесь.

!+ FILE: RADIX4.FOR
! ===================================================================
! Discription: Radix 4 is a descreet complex Fourier transform algorithim. It 
! is to be supplied with two real arrays, one for real parts of function
! one for imaginary parts: It can also unscramble transformed arrays.
! Usage: calling FASTF(XREAL,XIMAG,ISIZE,ITYPE,IFAULT); we supply the 
! following: 
!
! XREAL - array containing real parts of transform sequence    
! XIMAG - array containing imagianry parts of transformation sequence
! ISIZE - size of transform (ISIZE = 4*2*M)
! ITYPE - +1 forward transform
!         -1 reverse transform
! IFAULT - 1 if error
!        - 0 otherwise
! ===================================================================
!
! Forward transform computes:
!     X(k) = sum_{j=0}^{isize-1} x(j)*exp(-2ijk*pi/isize)
! Backward computes:
!     x(j) = (1/isize) sum_{k=0}^{isize-1} X(k)*exp(ijk*pi/isize)
!
! Forward followed by backwards will result in the origonal sequence!
!
! ===================================================================

      SUBROUTINE FASTF(XREAL,XIMAG,ISIZE,ITYPE,IFAULT)

      REAL*8 XREAL(*),XIMAG(*)
      INTEGER MAX2,II,IPOW

      PARAMETER (MAX2 = 20)

! Check for valid transform size upto 2**(max2):
      IFAULT = 1
      IF(ISIZE.LT.4) THEN
         print*,'FFT: Error: Data array < 4 - Too small!'
         return
      ENDIF
      II = 4
      IPOW = 2 

! Prepare mod 2:
 1    IF((II-ISIZE).NE.0) THEN 
         II = II*2
         IPOW = IPOW + 1       
         IF(IPOW.GT.MAX2) THEN
            print*,'FFT: Error: FFT1!'
            return
         ENDIF
         GOTO 1
      ENDIF

! Check for correct type:
      IF(IABS(ITYPE).NE.1) THEN
         print*,'FFT: Error: Wrong type of transformation!'
         return
      ENDIF

! No entry errors - continue:
      IFAULT = 0

! call FASTG to preform transformation:
      CALL FASTG(XREAL,XIMAG,ISIZE,ITYPE)

! Due to Radix 4 factorisation results are not in the same order
! after transformation as they were when the data was submitted:
! We now call SCRAM, to unscramble the reults:

      CALL SCRAM(XREAL,XIMAG,ISIZE,IPOW)

      return

      END

!-END: RADIX4.FOR


! ===============================================================
! Discription: This is the radix 4 complex descreet fast Fourier
! transform with out unscrabling. Suitable for convolutions or other
! applications that do not require unscrambling. Designed for use 
! with FASTF.FOR.
!
      SUBROUTINE FASTG(XREAL,XIMAG,N,ITYPE)

      INTEGER N,IFACA,IFCAB,LITLA
      INTEGER I0,I1,I2,I3

      REAL*8 XREAL(*),XIMAG(*),BCOS,BSIN,CW1,CW2,PI
      REAL*8 SW1,SW2,SW3,TEMPR,X1,X2,X3,XS0,XS1,XS2,XS3
      REAL*8 Y1,Y2,Y3,YS0,YS1,YS2,YS3,Z,ZATAN,ZFLOAT,ZSIN

      ZATAN(Z) = ATAN(Z)
      ZFLOAT(K) = FLOAT(K) ! Real equivalent of K.
      ZSIN(Z) = SIN(Z)

      PI = (4.0)*ZATAN(1.0)
      IFACA = N/4

! Forward transform:
      IF(ITYPE.GT.0) THEN
         GOTO 5
      ENDIF

! If this is for an inverse transform - conjugate the data:
      DO 4, K = 1,N
         XIMAG(K) = -XIMAG(K)
 4    CONTINUE

 5    IFCAB = IFACA*4

! Proform appropriate transformations:
      Z = PI/ZFLOAT(IFCAB)
      BCOS = -2.0*ZSIN(Z)**2
      BSIN = ZSIN(2.0*Z)
      CW1 = 1.0
      SW1 = 0.0

! This is the main body of radix 4 calculations:
      DO 10, LITLA = 1,IFACA
         DO 8, I0 = LITLA,N,IFCAB

            I1 = I0 + IFACA
            I2 = I1 + IFACA
            I3 = I2 + IFACA
            XS0 = XREAL(I0) + XREAL(I2)
            XS1 = XREAL(I0) - XREAL(I2)
            YS0 = XIMAG(I0) + XIMAG(I2)
            YS1 = XIMAG(I0) - XIMAG(I2)
            XS2 = XREAL(I1) + XREAL(I3)
            XS3 = XREAL(I1) - XREAL(I3)
            YS2 = XIMAG(I1) + XIMAG(I3)
            YS3 = XIMAG(I1) - XIMAG(I3)

            XREAL(I0) = XS0 + XS2
            XIMAG(I0) = YS0 + YS2

            X1 = XS1 + YS3
            Y1 = YS1 - XS3
            X2 = XS0 - XS2
            Y2 = YS0 - YS2
            X3 = XS1 - YS3
            Y3 = YS1 + XS3

            IF(LITLA.GT.1) THEN
               GOTO 7
            ENDIF

            XREAL(I2) = X1
            XIMAG(I2) = Y1
            XREAL(I1) = X2
            XIMAG(I1) = Y2
            XREAL(I3) = X3
            XIMAG(I3) = Y3
            GOTO 8

! Now IF required - we multiply by twiddle factors:
 7          XREAL(I2) = X1*CW1 + Y1*SW1
            XIMAG(I2) = Y1*CW1 - X1*SW1
            XREAL(I1) = X2*CW2 + Y2*SW2
            XIMAG(I1) = Y2*CW2 - X2*SW2
            XREAL(I3) = X3*CW3 + Y3*SW3
            XIMAG(I3) = Y3*CW3 - X3*SW3
 8       CONTINUE
         IF(LITLA.EQ.IFACA) THEN
            GOTO 10
         ENDIF

! Calculate a new set of twiddle factors:
         Z = CW1*BCOS - SW1*BSIN + CW1
         SW1 = BCOS*SW1 + BSIN*CW1 + SW1
         TEMPR = 1.5 - 0.5*(Z*Z + SW1*SW1)
         CW1 = Z*TEMPR
         SW1 = SW1*TEMPR         
         CW2 = CW1*CW1 - SW1*SW1
         SW2 = 2.0*CW1*SW1
         CW3 = CW1*CW2 - SW1*SW2
         SW3 = CW1*SW2 + CW2*SW1
 10   CONTINUE
      IF(IFACA.LE.1) THEN 
         GOTO 14
      ENDIF

! Set up tranform split for next stage:
      IFACA = IFACA/4
      IF(IFACA.GT.0) THEN 
         GOTO 5
      ENDIF

! This is the calculation of a radix two-stage:
      DO 13, K = 1,N,2
         TEMPR = XREAL(K) + XREAL(K + 1)
         XREAL(K + 1) = XREAL(K) - XREAL(K + 1)
         XREAL(K) = TEMPR
         TEMPR = XIMAG(K) + XIMAG(K + 1)
         XIMAG(K + 1) = XIMAG(K) - XIMAG(K + 1)
         XIMAG(K) = TEMPR
 13   CONTINUE
 14   IF(ITYPE.GT.0) THEN
         GOTO 17
      ENDIF

! For the inverse case, cojugate and scale the transform:
      Z = 1.0/ZFLOAT(N)
      DO 16, K = 1,N
         XIMAG(K) = -XIMAG(K)*Z
         XREAL(K) = XREAL(K)*Z
 16   CONTINUE

 17   return

      END
! ----------------------------------------------------------
!-END of subroutine FASTG.FOR.
! ----------------------------------------------------------


!+ FILE: SCRAM.FOR
! ==========================================================
! Discription: Subroutine for unscrambiling FFT data:
! ==========================================================
      SUBROUTINE SCRAM(XREAL,XIMAG,N,IPOW)

      INTEGER L(19),II,J1,J2,J3,J4,J5,J6,J7,J8,J9,J10,J11,J12
      INTEGER J13,J14,J15,J16,J17,J18,J19,J20,ITOP,I
      REAL*8 XREAL(*),XIMAG(*),TEMPR

      EQUIVALENCE (L1,L(1)),(L2,L(2)),(L3,L(3)),(L4,L(4))
      EQUIVALENCE (L5,L(5)),(L6,L(6)),(L7,L(7)),(L8,L(8))
      EQUIVALENCE (L9,L(9)),(L10,L(10)),(L11,L(11)),(L12,L(12))
      EQUIVALENCE (L13,L(13)),(L14,L(14)),(L15,L(15)),(L16,L(16))
      EQUIVALENCE (L17,L(17)),(L18,L(18)),(L19,L(19))

      II = 1
      ITOP = 2**(IPOW - 1)
      I = 20 - IPOW
      DO 5, K = 1,I
         L(K) = II
 5    CONTINUE
      L0 = II 
      I = I + 1
      DO 6, K = I,19
         II = II*2
         L(K) = II
 6    CONTINUE
      II = 0
      DO 9, J1 = 1,L1,L0
        DO 9, J2 = J1,L2,L1
          DO 9, J3 = J2,L3,L2
            DO 9, J4 = J3,L4,L3
              DO 9, J5 = J4,L5,L4
                DO 9, J6 = J5,L6,L5
                  DO 9, J7 = J6,L7,L6
                    DO 9, J8 = J7,L8,L7
                      DO 9, J9 = J8,L9,L8
                        DO 9, J10 = J9,L10,L9
                          DO 9, J11 = J10,L11,L10
                            DO 9, J12 = J11,L12,L11
                              DO 9, J13 = J12,L13,L12
                                DO 9, J14 = J13,L14,L13
                                  DO 9, J15 = J14,L15,L14
                                    DO 9, J16 = J15,L16,L15
                                      DO 9, J17 = J16,L17,L16
                                        DO 9, J18 = J17,L18,L17
                                          DO 9, J19 = J18,L19,L18
                                             J20 = J19
                                             DO 9, I = 1,2
                                                II = II +1
                                                IF(II.GE.J20) THEN
                                                   GOTO 8
                                                ENDIF
! J20 is the bit reverse of II!
! Pairwise exchange:
                                                TEMPR = XREAL(II)
                                                XREAL(II) = XREAL(J20)
                                                XREAL(J20) = TEMPR
                                                TEMPR = XIMAG(II)
                                                XIMAG(II) = XIMAG(J20)
                                                XIMAG(J20) = TEMPR
 8                                              J20 = J20 + ITOP
 9    CONTINUE

      return

      END
! -------------------------------------------------------------------
!-END:
! -------------------------------------------------------------------

Чтобы пройти через это и понять это, потребуется время! Я написал это, используя документ CalTech, который я нашел много лет назад, я не могу вспомнить ссылку, которую я боюсь. Удачи.

Надеюсь, это поможет.

person MoonKnight    schedule 03.09.2012
comment
Я думаю, что OP хочет понять БПФ, сначала понимая ДПФ. Я не уверен, что сброс старого кода FORTRAN поможет ему. - person user1202136; 03.09.2012
comment
Чтобы понять эти предметные области, требуется книга по математике, время, ручка и бумага. Сначала преобразование Фурье в целом, затем ДПФ, затем алгоритм БПФ и т. Д. Реализация БПФ сложна, и я просто подумал, что полный листинг кода, который четко прокомментирован, помог бы мне вернуться назад, когда Меня попросили создать вейвлет-код. Я слышу, что вы говорите, но поступая так, у меня самые лучшие намерения, и я всего лишь пытаюсь помочь ОП. Я полагаю, он может принять это или оставить это ...:] - person MoonKnight; 03.09.2012

Ваш код работает. Я бы дал больше цифр для PI (3,1415926535898). Кроме того, вы должны разделить результат суммирования ДПФ на S, размер ДПФ.

Поскольку входная серия в вашем тесте постоянна, выход DFT должен иметь только один ненулевой коэффициент. И действительно, все выходные коэффициенты очень малы по сравнению с первым.

Но для большой длины ввода это неэффективный способ реализации ДПФ. Если вас беспокоит время, посмотрите быстрое преобразование Фурье, чтобы узнать о более быстрых методах вычисления ДПФ.

person Jacob G    schedule 03.09.2012

Ваш код мне кажется правильным. Я не уверен, что вы ожидали от вывода, но, учитывая, что ваш ввод является постоянным значением, ДПФ константы - это член постоянного тока в ячейке 0 и нули в оставшихся ячейках (или близкий эквивалент, который у вас есть) .

Вы можете попробовать протестировать свой код с более длинной последовательностью, содержащей какой-либо тип сигнала, например синусоидальную или прямоугольную волну. В целом, однако, вам следует подумать об использовании чего-то вроде fftw в производственном коде. Он долгое время отжат и оптимизирован многими людьми. БПФ - это оптимизированные ДПФ для особых случаев (например, длины, являющиеся степенями двойки).

person sizzzzlerz    schedule 03.09.2012

MoonKnight уже предоставил схему Кули-Тьюки Radix-4 Decimation In Frequency в Fortran. Ниже я предоставляю схему Кули-Тьюки Radix-2 по частоте в Matlab.

Код является итеративным и рассматривает схему на следующем рисунке:

введите описание изображения здесь

Возможен также рекурсивный подход.

Как вы увидите, реализация также вычисляет количество выполненных умножений и сложений и сравнивает их с теоретическими расчетами, указанными в Сколько FLOPS для БПФ?.

Код, очевидно, намного медленнее, чем сильно оптимизированный FFTW, используемый Matlab.

Также обратите внимание, что коэффициенты вращения omegaa^((2^(p - 1) * n)) можно вычислить в автономном режиме, а затем восстановить из справочной таблицы, но этот момент пропущен в приведенном ниже коде.

Для реализации в Matlab итеративной схемы Radix-2 Decimation In Time Кули-Тьюки см. Реализация быстрого преобразования Фурье для определения цены на опционы.

% --- Radix-2 Decimation In Frequency - Iterative approach

clear all
close all
clc

N = 32;

x = randn(1, N);
xoriginal = x;
xhat = zeros(1, N);

numStages = log2(N);

omegaa = exp(-1i * 2 * pi / N);

mulCount = 0;
sumCount = 0;
tic
M = N / 2;
for p = 1 : numStages;
    for index = 0 : (N / (2^(p - 1))) : (N - 1);
        for n = 0 : M - 1;        
            a = x(n + index + 1) + x(n + index + M + 1);
            b = (x(n + index + 1) - x(n + index + M + 1)) .* omegaa^((2^(p - 1) * n));
            x(n + 1 + index) = a;
            x(n + M + 1 + index) = b;
            mulCount = mulCount + 4;
            sumCount = sumCount + 6;
        end;
    end;
    M = M / 2;
end
xhat = bitrevorder(x);
timeCooleyTukey = toc;

tic
xhatcheck = fft(xoriginal);
timeFFTW = toc;

rms = 100 * sqrt(sum(sum(abs(xhat - xhatcheck).^2)) / sum(sum(abs(xhat).^2)));

fprintf('Time Cooley-Tukey = %f; \t Time FFTW = %f\n\n', timeCooleyTukey, timeFFTW);
fprintf('Theoretical multiplications count \t = %i; \t Actual multiplications count \t = %i\n', ...
         2 * N * log2(N), mulCount);
fprintf('Theoretical additions count \t\t = %i; \t Actual additions count \t\t = %i\n\n', ...
         3 * N * log2(N), sumCount);
fprintf('Root mean square with FFTW implementation = %.10e\n', rms);
person Vitality    schedule 18.02.2017

Ваш код верен для получения ДПФ.

Функция, которую вы тестируете, (sin ((double) i / points * frequency * 2), что соответствует синноиду с амплитудой 1, частотой 1 и частотой дискретизации Fs = количество взятых точек.

Оперируя полученными данными, имеем:

введите описание изображения здесь

Как видите, коэффициенты ДПФ симметричны относительно коэффициента положения N / 2, поэтому информацию предоставляют только первые N / 2. Амплитуду, полученную с помощью модуля действительной и мнимой части, необходимо разделить на N и умножить на 2, чтобы восстановить ее. Частоты коэффициентов будут кратны Fs / N на номер коэффициента.

Если мы введем две синусоиды, одну с частотой 2 и амплитудой 1,3, а другую с частотой 3 и амплитудой 1,7.

for (int i = 0; i < 16; i++)
{
    numbers.push_back(1.3 *sin((double)i / 16 * frequency1 * 2 * PI)+ 1.7 *
        sin((double)i / 16 * frequency2 * 2 * PI));
} 

Полученные данные:

введите описание изображения здесь

Удачи.

person Jaime    schedule 13.05.2018
comment
Вставьте изображения, чтобы их можно было просмотреть, не оставляя ответа. - person milbrandt; 13.05.2018