Выход KISS FFT с оконным режимом или без него

В настоящее время я пытаюсь внедрить FFT в микроконтроллеры avr32 для целей обработки сигналов, используя Kiss FFT. И у меня странная проблема с выводом. По сути, я передаю образцы АЦП (тестирование с помощью генератора функций) в fft (реальный ввод, размер 256 n), и извлеченный вывод имеет для меня смысл. Однако, если я применяю окно Хэмминга к выборкам АЦП, а затем передаю их в БПФ, частотный бин пиковой величины неверен (и отличается от предыдущего результата без окна). Образцы АЦП имеют смещение постоянного тока, поэтому я устранил смещение, но оно все равно не работает с оконными образцами.

Ниже приведены первые несколько выходных значений через rs485. Первый столбец - это вывод fft без окна, тогда как второй столбец - это вывод с окном. Из столбца 1 пик находится в строке 6 (6 x fs (10,5 кГц) / 0,5 Н) дал мне правильный результат входной частоты, где столбец 2 имеет пиковую величину в строке 2 (кроме бина постоянного тока), что для меня не имеет смысла . Любое предложение будет полезно. Заранее спасибо.

    488         260   //dc bin
    5            97
    5            41
    5            29  
    4            26
    10           35
    133          76
    33           28
    21           6
    17           3
kiss_fft_scalar zero;
memset(&zero,0,sizeof(zero));
kiss_fft_cpx fft_input[n];
kiss_fft_cpx fft_output[n];

for(ctr=0; ctr<n; ctr++)
{
    fft_input[ctr].r = zero;
    fft_input[ctr].i = zero;
    fft_output[ctr].r =zero;
    fft_output[ctr].i = zero;
}

// IIR filter calculation

for (ctr=0; ctr<n; ctr++)
{       
    // filter calculation
    y[ctr] = num_coef[0]*x[ctr];

    y[ctr] += (num_coef[1]*x[ctr-1]) - (den_coef[1]*y[ctr-1]);
    y[ctr] += (num_coef[2]*x[ctr-2]) - (den_coef[2]*y[ctr-2]);
    //y1[ctr] += y[ctr] - 500;
    // hamming window
    hamming[ctr] = (0.54-((0.46) * cos(2*PI*ctr/256)));
    window[ctr] = hamming[ctr]*y[ctr];

    fft_input[ctr].r = window[ctr];
    fft_input[ctr].i = 0;
    fft_output[ctr].r = 0;
    fft_output[ctr].i = 0;

}

kiss_fftr_cfg fftConfig = kiss_fftr_alloc(n,0,NULL,NULL);
kiss_fftr(fftConfig, (kiss_fft_scalar * )fft_input, fft_output);


for (ctr=0; ctr<n; ctr++)
{   
    fft_mag[ctr] = (sqrt((fft_output[ctr].r * fft_output[ctr].r) + (fft_output[ctr].i * fft_output[ctr].i)))/(0.5*n);

    //Usart write
    char filtResult[10];
    sprintf(filtResult, "%04d %04d\n", (int)x[ctr], (int)fft_mag[ctr]);
    //sprintf(filtResult, "%04d %04d\n", (int)x[ctr], (int)window[ctr]);
    char c;
    char *ptr = &filtResult[0];
    do
    {   
        c = *ptr;
        ptr++;
        usart_bw_write_char(&AVR32_USART2, (int)c);
        // sendByte(c);

    } while (c != '\n');
}

kiss_fft_cleanup();
free(fftConfig);        

person Jin    schedule 29.05.2015    source источник


Ответы (1)


Пояснения к выходным данным в частотной области

В частотной области прямоугольное окно и окно Хэмминга выглядят так:

Прямоугольные окна и окна Хэмминга в частотной области

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

В результате всплеск энергии постоянного тока в конечном итоге распространяется за пределы ячейки 0 и попадает в ячейку 1 при использовании окна Хэмминга. Дело не в том, что у вас есть сильный пик в ячейке 1. На самом деле, если вы нанесете на график предоставленные вами данные, вы должны увидеть, что всплеск, который вы видели в индексе 6, на самом деле все еще присутствует с той же частотой, что и без использования окна Хэмминга:

Данные

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

Проблема с фильтрацией

Наконец, обратите внимание, что существует также проблема со способом реализации IIR-фильтра, из-за которого массивы x и y будут индексироваться за пределами границ, когда ctr==0 и ctr==1 (если только вы не предусмотрели какие-то специальные условия, а x и y на самом деле не являются указателями). со смещением от начала выделенных буферов). Это может повлиять на результаты как с окном, так и без него. Если вы фильтруете только один блок данных, обычно предполагается, что предыдущие выборки были нулевыми. В этом случае вы можете избежать внешней индексации с помощью:

// filter calculation
y[ctr] = num_coef[0]*x[ctr];

if (ctr>=1)
{
  y[ctr] += (num_coef[1]*x[ctr-1]) - (den_coef[1]*y[ctr-1]);
}
if (ctr>=2)
{
  y[ctr] += (num_coef[2]*x[ctr-2]) - (den_coef[2]*y[ctr-2]);
}

С другой стороны, если вы хотите отфильтровать несколько блоков из n семплов, вам придется запомнить несколько последних семплов из предыдущего блока. Это можно сделать, выделив буферы, немного превышающие размер блока:

x = malloc((n+2)*sizeof(kiss_fft_scalar));
y = malloc((n+2)*sizeof(kiss_fft_scalar));
// initialize "past samples" for the first block, assuming data was zero
x[0] = x[1] = 0;
y[0] = y[1] = 0;

Затем вы можете использовать смещение в этом буфере. Индексы 0 и 1 представляют прошлые выборки, тогда как остальная часть буфера, начиная с индекса 2, заполняется текущим блоком входных данных. Это приводит к следующему слегка измененному коду фильтрации:

// filter calculation
y[ctr+2] = num_coef[0]*x[ctr+2];

y[ctr+2] += (num_coef[1]*x[ctr+1]) - (den_coef[1]*y[ctr+1]);
y[ctr+2] += (num_coef[2]*x[ctr]) - (den_coef[2]*y[ctr]);

// hamming window
hamming[ctr] = (0.54-((0.46) * cos(2*PI*ctr/256)));
window[ctr] = hamming[ctr]*y[ctr+2];

Наконец, в конце каждого блока вам необходимо обновить «прошлые образцы» с индексами 0 и 1, чтобы последние образцы текущего блока были готовы к обработке следующего входного блока:

// remember last 2 samples of block
x[0] = x[n-2];
x[1] = x[n-1];
y[0] = y[n-2];
y[1] = y[n-1];
person SleuthEye    schedule 29.05.2015
comment
Вау, спасибо за действительно хорошее разъяснение. Поскольку я уже сегментирую ввод в реальном времени от АЦП, означает ли это, что прямоугольное окно было выполнено для компенсации предположения о преобразовании Фурье (периодический сигнал)? И даже не нужно беспокоиться о других окнах? А что касается вашего последнего комментария о фильтре, не могли бы вы рассказать немного подробнее? Я новичок в мире программирования. Спасибо! - person Jin; 29.05.2015
comment
Прямоугольное окно неявно при выполнении БПФ над блоком данных (возможно, из гораздо более длинной последовательности данных). Однако вы можете по-прежнему использовать другое окно для настройки компромисса между частотным разрешением (шириной главного лепестка окна в частотной области) и затуханием боковых лепестков. - person SleuthEye; 29.05.2015
comment
Спасибо! Вы очень помогли! - person Jin; 02.06.2015