Реализация алгоритма Герцеля в C

Я внедряю систему связи скачкообразной перестройки частоты BFSK на процессоре DSP. Некоторые участники форума предложили использовать алгоритм Герцеля для демодуляции скачкообразного изменения частоты на определенных частотах. Я попытался реализовать алгоритм goertzel в C. код:

float goertzel(int numSamples,int TARGET_FREQUENCY,int SAMPLING_RATE, float* data)
{
    int     k,i;
    float   floatnumSamples;
    float   omega,sine,cosine,coeff,q0,q1,q2,result,real,imag;

    floatnumSamples = (float) numSamples;
    k = (int) (0.5 + ((floatnumSamples * TARGET_FREQUENCY) / SAMPLING_RATE));
    omega = (2.0 * M_PI * k) / floatnumSamples;
    sine = sin(omega);
    cosine = cos(omega);
    coeff = 2.0 * cosine;
    q0=0;
    q1=0;
    q2=0;

    for(i=0; i<numSamples; i++)
    {
        q0 = coeff * q1 - q2 + data[i];
        q2 = q1;
        q1 = q0;
    }
    real = (q1 - q2 * cosine);
    imag = (q2 * sine);
    result = sqrtf(real*real + imag*imag);
    return result;
}

Когда я использую функцию для вычисления результата на определенных частотах для данного набора данных, я не получаю правильных результатов. Однако, если я использую один и тот же набор данных и вычисляю результат goertzel с помощью функции MATLAB goertzel(), я получаю результаты отлично. Я реализовал алгоритм с помощью C, с помощью некоторых онлайн-руководств, которые я нашел через Интернет. Я просто хочу получить представление о вас, если функция правильно реализует алгоритм goertzel.

Ответ 1

Если вы говорите, что реализация Matlab хороша, потому что ее результаты соответствуют результату для этой частоты DFT или FFT ваших данных, то это, вероятно, потому, что реализация Matlab нормализует результаты с помощью коэффициента масштабирования, как это делается с помощью БПФ.

Измените свой код, чтобы принять это во внимание, и посмотрите, улучшает ли он ваши результаты. Обратите внимание, что я также изменил имена функций и результатов, чтобы отразить, что ваш ядро ​​вычисляет величину, а не полный комплексный результат, для ясности:

float goertzel_mag(int numSamples,int TARGET_FREQUENCY,int SAMPLING_RATE, float* data)
{
    int     k,i;
    float   floatnumSamples;
    float   omega,sine,cosine,coeff,q0,q1,q2,magnitude,real,imag;

    float   scalingFactor = numSamples / 2.0;

    floatnumSamples = (float) numSamples;
    k = (int) (0.5 + ((floatnumSamples * TARGET_FREQUENCY) / SAMPLING_RATE));
    omega = (2.0 * M_PI * k) / floatnumSamples;
    sine = sin(omega);
    cosine = cos(omega);
    coeff = 2.0 * cosine;
    q0=0;
    q1=0;
    q2=0;

    for(i=0; i<numSamples; i++)
    {
        q0 = coeff * q1 - q2 + data[i];
        q2 = q1;
        q1 = q0;
    }

    // calculate the real and imaginary results
    // scaling appropriately
    real = (q1 - q2 * cosine) / scalingFactor;
    imag = (q2 * sine) / scalingFactor;

    magnitude = sqrtf(real*real + imag*imag);
    return magnitude;
}

Ответ 2

Часто вы можете просто использовать квадрат величины в своих вычислениях, например, для обнаружения тона.

Некоторые превосходные примеры Goertzels заключаются в коде DSP Asterisk PBX Код DSP Asterisk (dsp.c) и в библиотеке spandsp SPANDSP DSP Library

Ответ 3

Рассмотрим две входные выборочные волновые формы:

1) синусоидальная волна с амплитудой A и частотой W

2) косинусоидальная волна с одинаковой амплитудой и частотой A и W

Алгоритм Герцеля должен давать те же результаты для двух упомянутых входных волновых форм, но предоставленный код приводит к различным возвращаемым значениям. Я думаю, что код должен быть пересмотрен следующим образом:

float goertzel_mag(int numSamples,int TARGET_FREQUENCY,int SAMPLING_RATE, float* data)
{
    int     k,i;
    float   floatnumSamples;
    float   omega,sine,cosine,coeff,q0,q1,q2,magnitude,real,imag;

    float   scalingFactor = numSamples / 2.0;

    floatnumSamples = (float) numSamples;
    k = (int) (0.5 + ((floatnumSamples * TARGET_FREQUENCY) / SAMPLING_RATE));
    omega = (2.0 * M_PI * k) / floatnumSamples;
    sine = sin(omega);
    cosine = cos(omega);
    coeff = 2.0 * cosine;
    q0=0;
    q1=0;
    q2=0;

    for(i=0; i<numSamples; i++)
    {
        q2 = q1;
        q1 = q0;
        q0 = coeff * q1 - q2 + data[i];
    }

    // calculate the real and imaginary results
    // scaling appropriately
    real = (q0 - q1 * cosine) / scalingFactor;
    imag = (-q1 * sine) / scalingFactor;

    magnitude = sqrtf(real*real + imag*imag);
    return magnitude;
}