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

я реализую систему связи с скачкообразной перестройкой частоты BFSK на процессоре DSP. Некоторые участники форума предложили использовать алгоритм Гертцеля для демодуляции скачкообразной перестройки частоты на определенных частотах. Я попытался реализовать алгоритм гертцеля в 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;
}

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

3 ответов


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

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

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;
}

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

некоторые отличные примеры Goertzels находятся в коде DSP АТС Asterisk код DSP звездочки (dsp.c) и в библиотеке spandsp библиотека DSP SPANDSP


рассмотрим два входных образца волновых форм:

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;
}