4

我目前正在尝试使用吻 fft 将 fft 实现到 avr32 微控制器中以进行信号处理。我的输出有一个奇怪的问题。基本上,我将 ADC 样本(使用函数发生器测试)传递给 fft(实际输入,256 n 大小)并且检索到的输出对我来说很有意义。但是,如果我将汉明窗应用于 ADC 样本,然后将它们传递给 FFT,则峰值幅度的频率箱是错误的(并且与之前没有开窗的结果不同)。ADC 样本有 DC 偏移,因此我消除了偏移,但它仍然不适用于加窗样本。

下面是通过 rs485 的前几个输出值。第一列是没有窗口的 fft 输出,而第二列是有窗口的输出。从第 1 列开始,峰值位于第 6 行(6 x fs (10.5kHz) / 0.5N)给了我正确的输入频率结果,其中第 2 列在第 2 行(直流 bin 除外)有一个峰值幅度,这对我来说没有意义. 任何建议都会有所帮助。提前致谢。

    488 260 //直流仓
    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);        
4

1 回答 1

6

频域输出说明

在频域中,矩形窗和汉明窗如下所示:

频域中的矩形和汉明窗

您可能知道,时域乘以一个窗口对应于频域中的卷积,它本质上将信号的能量传播到多个频率区间上,这通常称为频谱泄漏。对于您选择的特定窗口(如上文在频域中描述的),您可能会注意到汉明窗口在主瓣外传播的能量要少得多,但主瓣比矩形窗口宽一点。

因此,当使用汉明窗时,直流能量的尖峰最终会扩散到 bin 0 并进入 bin 1。您在 bin 1 中的峰值并没有那么多。事实上,如果您绘制您提供的数据,您应该看到您在索引 6 处看到的峰值实际上仍然以相同的频率存在,并且不使用汉明窗:

数据

如果您想消除周围箱中的直流尖峰和泄漏,请消除数据中的偏差(主要是应用陷波滤波器),或者在查找“第一次强秒杀”。

过滤问题

最后,请注意,IIR 过滤器的实现方式也存在一个问题,即数组x和将在和y时被索引超出范围(除非您做出了一些特殊规定并且&实际上是与开始有偏移的指针分配的缓冲区)。这可能会影响有窗口和无窗口的结果。如果您只过滤单个数据块,一个常见的假设是早期样本为零。在这种情况下,您可以通过以下方式避免越界索引:ctr==0ctr==1xy

// 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];
于 2015-05-29T02:17:07.270 回答