Дискретное преобразование Фурье дает неверные результаты

Я пытаюсь сделать дискретные преобразования Фурье в C.

Сначала просто методом грубой силы. Сначала я попросил программу открыть файл данных (амплитуд) и поместить данные в массив (только один, так как я ограничиваюсь вводом вещественных значений).

Но преобразование выглядело неправильно, поэтому вместо этого я попытался сгенерировать простую волновую функцию и проверить, правильно ли она преобразуется.

Вот мой код, лишенный наворотов:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define M_PI 3.14159265358979323846

//the test wavefunction
double theoretical(double t)
{
    double a = sin(M_PI * t) + 2 * sin(2 * M_PI * t) + 4 * sin(4 * M_PI * t); 
    return a;
}
//-------------------------------------------------------------------------
void dftreal(double inreal[], double outreal[], double outimag[], int linecount) 
{
    int n, k;
    for (k = 0; k < linecount; k++)
    {
        double sumreal = 0;
        double sumimag = 0;

        for (n = 0; n < linecount; n++) 
        {
            double angle = 2 * M_PI * n * ( k / (double) linecount);
            sumreal +=  inreal[n] * cos(angle);
            sumimag +=  inreal[n] * sin(angle);
        }
        outreal[k] = sumreal;
        outimag[k] = sumimag;
    }
}
//=========================================================================
int main(void)
{
     int linecount = 44100;
     //creates all necessary arrays
     double inreal[linecount], outreal[linecount], outimag[linecount], p[linecount]; 

     FILE *fout = fopen("Output.txt", "w");

     for (int i = 0 ; i < linecount ; ++i)
     {
         inreal[i] = theoretical( i / (double) linecount);
     }

     //actually computes the transform
     dftreal(inreal, outreal, outimag, linecount); 

     for (int i = 0 ; i < linecount ; ++i)
     {
          p[i] = 2*(outreal[i] * outreal[i] + outimag[i] * outimag[i]);
          fprintf(fout, "%f %f \n", (i / (double) linecount), p[i]);
     }
     fclose(fout);
     printf("\nEnd of program");
     getchar();
     return 0;
}

Программа компилируется, завершается, но вместо нескольких острых пиков на графике мощности (частоты) я получаю следующее: Я понял.  .

Одна частота или разные частоты дают точно такую ​​же кривую перевернутой ванны.

Я проверил несколько источников о ДПФ, и я до сих пор не знаю, что происходит не так, вроде бы нет никаких вопиющих ошибок с функцией:

dftreal

сам. Я хотел бы попросить помощи в том, что может быть причиной проблемы. Я использую компилятор MinGW в Windows 7. Спасибо!


c dft
person Ravenchant    schedule 26.03.2016    source источник
comment
ДПФ требует высокой десятичной точности. Вы пытались изменить свои операторы fprintf, чтобы они могли включать больше десятичных знаков? Как %.10f или больше...   -  person George Bou    schedule 26.03.2016


Ответы (3)


Хорошая новость заключается в том, что с вашей dftreal реализацией все в порядке.

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

Если вы увеличиваете частоты тестового сигнала, делая частоту относительно частоты дискретизации, например:

//the test wavefunction
double theoretical(double t)
{
  double f = 0.1*44100;
  double a = sin(2 * M_PI * f * t) + 2 * sin(4 * M_PI * f * t) + 4 * sin(8 * M_PI * f * t); 
  return a;
}

Вы должны получить такой сюжет, как: dftreal output с более высокой частотой тестового сигнала

person SleuthEye    schedule 27.03.2016
comment
Вы, конечно, правы. Я не знаю, как мне удалось так сильно исказить математику, программа теперь работает, как задумано. Большое спасибо! - person Ravenchant; 27.03.2016

Предупреждение: я немного заржавел в этом

Насколько я помню, часть cos/real дает частотный спектр, а часть sin/imag дает спектр мощности. Итак, я думаю, что вы хотите напечатать частотный спектр (который просто outreal[i]), а не то, что вы сделали (т.е. добавление outreal и outimag неправильно). Вы можете изобразить их разными цветами, но я бы начал с простого.

Кроме того, я бы начал с более простого ввода данных.

Я изменил theoretical, чтобы сделать одну волну косинуса [так же, как и ваш оригинал]. Это предсказуемо, и вы должны получить один всплеск, что вы и делаете, поэтому я думаю, что dftreal правильно.

Я также добавил несколько параметров командной строки, чтобы вы могли поэкспериментировать.

Я также обнаружил, что деление i / linecount иногда усекается, поэтому я создал макрос FRAG, чтобы проиллюстрировать/исправить это. Без этого всплеск для ввода cos(2 * pi * t) оказался в outreal[0] (часть постоянного тока?) вместо outreal[1]

Кроме того, в целях тестирования вы можете обойтись гораздо меньшим количеством образцов (например, 1000). Это также может помочь расширить ваш участок по горизонтали, чтобы лучше видеть вещи.

Во всяком случае, вот код [пожалуйста, простите за беспричинную очистку стиля]:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

//#define M_PI 3.14159265358979323846
#define M_2PI (M_PI * 2.0)

int opt_A;                              // algorithm to use
int linecount;                          // number of samples
int opt_a;                              // use absolute value on output
int opt_s;                              // use squared value on output
int opt_i;                              // use integer output index
int opt_j;                              // output imaginary part
int opt_c;                              // .csv compatibility

time_t todlast;

// the first was your original and would truncate
#if 0
#define FRAG(_i)    ((_i) / linecount)
#else
#define FRAG(_i)    ((double) (_i) / linecount)
#endif

void
pgr(int k,int n,int count)
{
    time_t todnow = time(NULL);

    if ((todnow - todlast) >= 1) {
        todlast = todnow;
        printf("\r%d %d ",count,k);
        fflush(stdout);
    }
}

// the test wavefunction
double
theoretical(double t)
{
    double a;

    switch (opt_A) {
    case 0:
        a = 0.0;
        a += sin(M_PI * t);
        a += 2.0 * sin(2.0 * M_PI * t);
        a += 4.0 * sin(4.0 * M_PI * t);
        break;
    default:
        a = cos(M_2PI * t * opt_A);
        break;
    }

    return a;
}

//-------------------------------------------------------------------------
void
dftreal(double inreal[], double outreal[], double outimag[], int linecount)
{
    int n;
    int k;

    for (k = 0; k < linecount; k++) {
        double sumreal = 0;
        double sumimag = 0;
        double omega_k = (M_2PI * k) / (double) linecount;

        for (n = 0; n < linecount; n++) {
            // omega k:
#if 0
            double angle = M_2PI * n * FRAG(k);
#else
            double angle = omega_k * n;
#endif

            sumreal += inreal[n] * cos(angle);
            sumimag += inreal[n] * sin(angle);

            pgr(k,n,linecount);
        }

        outreal[k] = sumreal;
        outimag[k] = sumimag;
    }
}

//=========================================================================
int
main(int argc,char **argv)
{
    char *cp;

    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'a':  // absolute value
            opt_a = ! opt_a;
            break;
        case 's':  // square the output value
            opt_s = ! opt_s;
            break;
        case 'c':  // .csv output
            opt_c = ! opt_c;
            break;
        case 'i':  // integer output
            opt_i = ! opt_i;
            break;
        case 'j':  // imaginary output
            opt_j = ! opt_j;
            break;
        case 'A':
            opt_A = atoi(cp + 2);
            break;
        case 'N':
            linecount = atoi(cp + 2);
            break;
        }
    }

    if (linecount <= 0)
        linecount = 44100 / 2;

    // creates all necessary arrays
    double inreal[linecount];
    double outreal[linecount];
    double outimag[linecount];
#if 0
    double p[linecount];
#endif

    FILE *fout = fopen("Output.txt", "w");

    for (int i = 0; i < linecount; ++i)
        inreal[i] = theoretical(FRAG(i));

    todlast = time(NULL);

    // actually computes the transform
    dftreal(inreal, outreal, outimag, linecount);

    for (int i = 0; i < linecount; ++i) {
#if 0
        p[i] = 2 * (outreal[i] * outreal[i] + outimag[i] * outimag[i]);
        fprintf(fout, "%f %f\n", (i / (double) linecount), p[i]);
#endif

#if 0
        fprintf(fout, "%f %f %f\n", (i / (double) linecount),
            outreal[i] * outreal[i],
            outimag[i] * outimag[i]);
#endif

#if 0
        fprintf(fout, "%f %f\n",
            (i / (double) linecount),outreal[i] * outreal[i]);
#endif

        if (opt_a) {
            outreal[i] = fabs(outreal[i]);
            outimag[i] = fabs(outimag[i]);
        }

        if (opt_s) {
            outreal[i] *= outreal[i];
            outimag[i] *= outimag[i];
        }

        if (! opt_c) {
            if (opt_i)
                fprintf(fout, "%d ",i);
            else
                fprintf(fout, "%f ",FRAG(i));
        }

        fprintf(fout, "%f",outreal[i]);
        if (opt_j)
            fprintf(fout, "%f",outimag[i]);
        fprintf(fout, "\n");
    }
    fclose(fout);

    printf("\nEnd of program\n");

    //getchar();
    return 0;
}
person Craig Estey    schedule 26.03.2016

Вы упомянули, что "... преобразование выглядело неправильно,..."

Вы сравнивали результаты с другой программой или программным пакетом, чтобы убедиться, что результаты действительно неверны? Недавно я написал DFT на C++ и JavaScript и сравнил результаты с результатами MATLAB, Maple и MathCAD. Иногда разница заключается только в масштабировании или форматировании.

Насколько большим был исходный массив амплитуд, который вы хотели ввести? Если вы разместите здесь примеры данных, я готов запустить их в своей собственной программе и посмотреть, выводит ли моя программа те же результаты, что и ваши.

person DavidB2013    schedule 27.03.2016