DSP 库 - RFFT - 奇怪的结果
DSP libraries - RFFT - strange results
最近我一直在尝试在我的 STM32F4-Discovery 评估板上进行 FFT 计算,然后将其发送到 PC。我已经调查了我的问题 - 我认为我在使用制造商提供的 FFT 函数时做错了。
我正在使用 CMSIS-DSP 库。
现在我一直在用代码生成样本(如果工作正常我会通过麦克风进行采样)。
我正在使用 arm_rfft_fast_f32
因为我的数据将来会变成浮点数,但是我在输出数组中得到的结果很疯狂(我认为) - 我的频率低于 0。
number_of_samples = 512; (l_probek in code)
dt = 1/freq/number_of_samples
这是我的代码
float32_t buffer_input[l_probek];
uint16_t i;
uint8_t mode;
float32_t dt;
float32_t freq;
bool DoFlag = false;
bool UBFlag = false;
uint32_t rozmiar = 4*l_probek;
union
{
float32_t f[l_probek];
uint8_t b[4*l_probek];
}data_out;
union
{
float32_t f[l_probek];
uint8_t b[4*l_probek];
}data_mag;
union
{
float32_t f;
uint8_t b[4];
}czest_rozdz;
/* Pointers ------------------------------------------------------------------*/
arm_rfft_fast_instance_f32 S;
arm_cfft_radix4_instance_f32 S_CFFT;
uint16_t output;
/* ---------------------------------------------------------------------------*/
int main(void)
{
freq = 5000;
dt = 0.000000390625;
_GPIO();
_LED();
_NVIC();
_EXTI(0);
arm_rfft_fast_init_f32(&S, l_probek);
GPIO_SetBits(GPIOD, LED_Green);
mode = 2;
//----------------- Infinite loop
while (1)
{
if(true)//(UBFlag == true)
for(i=0; i<l_probek; ++i)
{
buffer_input[i] = (float32_t) 15*sin(2*PI*freq*i*dt);
}
//Obliczanie FFT
arm_rfft_fast_f32(&S, buffer_input, data_out.f, 0);
//Obliczanie modulow
arm_cmplx_mag_f32(data_out.f, data_mag.f, l_probek);
USART_putdata(USART1, data_out.b, data_mag.b, rozmiar);
//USART_putdata(USART1, czest_rozdz.b, data_mag.b, rozmiar);
GPIO_ToggleBits(GPIOD, LED_Orange);
//mode++;
//UBFlag = false;
}
}
}
I'm using arm_rfft_fast_f32
as my data are going to be floats in the future, but results I get in my output array are insane (I think) - I'm getting frequencies below 0.
arm_rfft_fast_f32
function does not return frequencies, but rather complex-valued coefficients computed using the Fast Fourier Transform (FFT)。因此,这些系数为负是完全合理的。更具体地说,您的 single-cycle sin
幅度为 15 的测试音输入的预期系数为:
0.0, 0.0; // special case packing real-valued X[0] and X[N/2]
0.0, -3840.0; // X[1]
0.0, 0.0; // X[2]
0.0, 0.0; // X[3]
...
0.0, 0.0; // X[255]
请注意,如 documentation 中所示,前两个输出对应于纯实数系数 X[0]
和 X[N/2]
(您在后续操作中应特别注意这种特殊情况调用 arm_cmplx_mag_f32
;见下文最后一点)。
每个频率分量的频率由 k*fs/N
给出,其中 N
是样本数(在您的例子中是 l_probek
),fs = 1/dt
是采样率(在你的例子中 freq*l_probek
):
X[0] -> 0*freq*l_probek/l_probek = 0
X[1] -> 1*freq*l_probek/l_probek = freq = 5000
X[2] -> 2*freq*l_probek/l_probek = 2*freq = 10000
X[3] -> 3*freq*l_probek/l_probek = 2*freq = 15000
...
最后,由于前两个值的特殊包装,计算N/2+1
个量级时需要小心:
// General case for the magnitudes
arm_cmplx_mag_f32(data_out.f+2, data_mag.f+1, l_probek/2 - 1);
// Handle special cases
data_mag.f[0] = data_out.f[0];
data_mag.f[l_probek/2] = data_out.f[1];
作为上述答案的后续,这太棒了,我花了很长时间才弄清楚一些进一步的说明。
频率仓以目标频率为中心,因此例如在上面的示例中,X[0] 代表 -2500Hz 到 2500Hz,以零为中心,X[1] 以 2500Hz 到 7500Hz 为中心5000Hz 等等
通过查看相邻 bin 的能量(参见 https://dspguru.com/dsp/howtos/how-to-interpolate-fft-peak/)在 bin 内插入频率是很常见的,如果您这样做,您将需要确保您的幅度数组是对于 bins + Nyquist 足够大并且 Nyquist 上方的 bin 为 0,但请注意许多插值技术需要 复数值 (e.q。Quinn,Jacobson)因此请确保您在找到幅度之前进行插值。
上面的特殊情况代码有效,因为没有 DC 和 Nyquist 值的复分量,因此幅度只是实部
但是上面的代码有一个错误——虽然DC和奈奎斯特分量的虚部总是零,但实部仍然可能是负数,所以你需要取绝对值获取幅度:
// Handle special cases
data_mag.f[0] = fabs(data_out.f[0]);
data_mag.f[l_probek/2] = fabs(data_out.f[1]);
最近我一直在尝试在我的 STM32F4-Discovery 评估板上进行 FFT 计算,然后将其发送到 PC。我已经调查了我的问题 - 我认为我在使用制造商提供的 FFT 函数时做错了。
我正在使用 CMSIS-DSP 库。 现在我一直在用代码生成样本(如果工作正常我会通过麦克风进行采样)。
我正在使用 arm_rfft_fast_f32
因为我的数据将来会变成浮点数,但是我在输出数组中得到的结果很疯狂(我认为) - 我的频率低于 0。
number_of_samples = 512; (l_probek in code)
dt = 1/freq/number_of_samples
这是我的代码
float32_t buffer_input[l_probek];
uint16_t i;
uint8_t mode;
float32_t dt;
float32_t freq;
bool DoFlag = false;
bool UBFlag = false;
uint32_t rozmiar = 4*l_probek;
union
{
float32_t f[l_probek];
uint8_t b[4*l_probek];
}data_out;
union
{
float32_t f[l_probek];
uint8_t b[4*l_probek];
}data_mag;
union
{
float32_t f;
uint8_t b[4];
}czest_rozdz;
/* Pointers ------------------------------------------------------------------*/
arm_rfft_fast_instance_f32 S;
arm_cfft_radix4_instance_f32 S_CFFT;
uint16_t output;
/* ---------------------------------------------------------------------------*/
int main(void)
{
freq = 5000;
dt = 0.000000390625;
_GPIO();
_LED();
_NVIC();
_EXTI(0);
arm_rfft_fast_init_f32(&S, l_probek);
GPIO_SetBits(GPIOD, LED_Green);
mode = 2;
//----------------- Infinite loop
while (1)
{
if(true)//(UBFlag == true)
for(i=0; i<l_probek; ++i)
{
buffer_input[i] = (float32_t) 15*sin(2*PI*freq*i*dt);
}
//Obliczanie FFT
arm_rfft_fast_f32(&S, buffer_input, data_out.f, 0);
//Obliczanie modulow
arm_cmplx_mag_f32(data_out.f, data_mag.f, l_probek);
USART_putdata(USART1, data_out.b, data_mag.b, rozmiar);
//USART_putdata(USART1, czest_rozdz.b, data_mag.b, rozmiar);
GPIO_ToggleBits(GPIOD, LED_Orange);
//mode++;
//UBFlag = false;
}
}
}
I'm using
arm_rfft_fast_f32
as my data are going to be floats in the future, but results I get in my output array are insane (I think) - I'm getting frequencies below 0.
arm_rfft_fast_f32
function does not return frequencies, but rather complex-valued coefficients computed using the Fast Fourier Transform (FFT)。因此,这些系数为负是完全合理的。更具体地说,您的 single-cycle sin
幅度为 15 的测试音输入的预期系数为:
0.0, 0.0; // special case packing real-valued X[0] and X[N/2]
0.0, -3840.0; // X[1]
0.0, 0.0; // X[2]
0.0, 0.0; // X[3]
...
0.0, 0.0; // X[255]
请注意,如 documentation 中所示,前两个输出对应于纯实数系数 X[0]
和 X[N/2]
(您在后续操作中应特别注意这种特殊情况调用 arm_cmplx_mag_f32
;见下文最后一点)。
每个频率分量的频率由 k*fs/N
给出,其中 N
是样本数(在您的例子中是 l_probek
),fs = 1/dt
是采样率(在你的例子中 freq*l_probek
):
X[0] -> 0*freq*l_probek/l_probek = 0
X[1] -> 1*freq*l_probek/l_probek = freq = 5000
X[2] -> 2*freq*l_probek/l_probek = 2*freq = 10000
X[3] -> 3*freq*l_probek/l_probek = 2*freq = 15000
...
最后,由于前两个值的特殊包装,计算N/2+1
个量级时需要小心:
// General case for the magnitudes
arm_cmplx_mag_f32(data_out.f+2, data_mag.f+1, l_probek/2 - 1);
// Handle special cases
data_mag.f[0] = data_out.f[0];
data_mag.f[l_probek/2] = data_out.f[1];
作为上述答案的后续,这太棒了,我花了很长时间才弄清楚一些进一步的说明。
频率仓以目标频率为中心,因此例如在上面的示例中,X[0] 代表 -2500Hz 到 2500Hz,以零为中心,X[1] 以 2500Hz 到 7500Hz 为中心5000Hz 等等
通过查看相邻 bin 的能量(参见 https://dspguru.com/dsp/howtos/how-to-interpolate-fft-peak/)在 bin 内插入频率是很常见的,如果您这样做,您将需要确保您的幅度数组是对于 bins + Nyquist 足够大并且 Nyquist 上方的 bin 为 0,但请注意许多插值技术需要 复数值 (e.q。Quinn,Jacobson)因此请确保您在找到幅度之前进行插值。
上面的特殊情况代码有效,因为没有 DC 和 Nyquist 值的复分量,因此幅度只是实部
但是上面的代码有一个错误——虽然DC和奈奎斯特分量的虚部总是零,但实部仍然可能是负数,所以你需要取绝对值获取幅度:
// Handle special cases
data_mag.f[0] = fabs(data_out.f[0]);
data_mag.f[l_probek/2] = fabs(data_out.f[1]);