テストコード
bool flag(true);
uint8_t is_complex(0);
size_t length(0);
float SPS(0);
float freq(0);
const uint8_t ifft_flag(0);
const uint8_t do_bit_reverse(1);
flag = flag && (4 == sscanf(command,
"fft %hhu %u %f %f",
&is_complex,
&length,
&SPS,
&freq));
if (flag)
{
printf("FFT %s %upoint %.1fSPS %.1fHz\n",
is_complex ? "complex" : "real",
length,
SPS,
freq);
}
if (is_complex)
{
const arm_cfft_instance_f32 *S(0);
if (flag)
{ // check length
switch (length)
{
case 16:
S = &arm_cfft_sR_f32_len16;
break;
case 32:
S = &arm_cfft_sR_f32_len32;
break;
case 64:
S = &arm_cfft_sR_f32_len64;
break;
case 128:
S = &arm_cfft_sR_f32_len128;
break;
case 256:
S = &arm_cfft_sR_f32_len256;
break;
case 512:
S = &arm_cfft_sR_f32_len512;
break;
case 1024:
S = &arm_cfft_sR_f32_len1024;
break;
case 2048:
S = &arm_cfft_sR_f32_len2048;
break;
case 4096:
S = &arm_cfft_sR_f32_len4096;
break;
default:
flag = false;
printf("length error\n");
break;
}
}
float *FFT_input(0);
if (flag)
{ // alloc
try
{
FFT_input = new float32_t[length * 2]();
}
catch (std::bad_alloc)
{
flag = false;
printf("bad alloc\n");
}
}
if (flag)
{ //generate wave
for (size_t i(0); i < length; i++)
{
const float phase(i * pi * 2 / SPS * freq);
FFT_input[i * 2 + 0] = cosf(phase);
FFT_input[i * 2 + 1] = sinf(phase);
}
}
float FFT_process_seconds(0);
if (flag)
{ // FFT
const uint32_t start(HAL_GetTick());
arm_cfft_f32(S, FFT_input, ifft_flag, do_bit_reverse);
const uint32_t stop(HAL_GetTick());
FFT_process_seconds = (stop - start) * 0.001f;
}
if (flag)
{ // dump result
printf(" %.3fsec\n\n re im\n", FFT_process_seconds);
for (size_t i(0); i < length; i++)
{
const int j(i - length / 2);
const int k(i < length / 2 ? (i + length / 2) : (i - length / 2));
printf("%f %f %f\n",
j * SPS / length,
FFT_input[k * 2 + 0],
FFT_input[k * 2 + 1]);
}
}
delete[] FFT_input;
FFT_input = 0;
}
else
{
arm_rfft_fast_instance_f32 S;
if (flag)
{ // check length and initialize
flag = (ARM_MATH_SUCCESS == arm_rfft_fast_init_f32(&S, length));
if (!flag)
{
printf("init error\n");
}
}
float *FFT_input(0);
float *FFT_output(0);
if (flag)
{ // alloc
try
{
FFT_input = new float32_t[length]();
FFT_output = new float32_t[length]();
}
catch (std::bad_alloc)
{
flag = false;
printf("bad alloc\n");
}
}
if (flag)
{ //generate wave
for (size_t i(0); i < length; i++)
{
const float phase(i * pi * 2 / SPS * freq);
FFT_input[i] = cosf(phase);
}
}
float FFT_process_seconds(0);
if (flag)
{ // FFT
arm_rfft_fast_f32(&S, FFT_input, FFT_output, ifft_flag);
}
if (flag)
{ // dump result
printf("%.3fsec\n\n re\n", FFT_process_seconds);
for (size_t i(0); i < length; i++)
{
printf("%f %f\n",
i * SPS / length / 2,
FFT_output[i]);
}
}
delete[] FFT_input;
FFT_input = 0;
delete[] FFT_output;
FFT_output = 0;
}
リンカでDrivers/CMSIS/Lib/GCC/libarm_cortexM4lf_math.aを読み込む必要がある。
ARM_MATH_CM4(コアに合わせて選択)をdefine定義した後、arm_math.hとarm_const_structs.hをインクルードする。あとnewのstd::bad_allocをcatchするのに<new>もインクルードする必要がある。
CFFTは複素数、RFFTは実数の処理で、それぞれ呼び方が違う。また引数のとり方も違い、CFFTはinputとoutputを同じ配列で行う。一方、RFFTはinputとoutputは別の配列を使う。
CFFTは負の周波数も解析できる。一方、RFFTは負の周波数は扱えないが、CFFTの分解能の2倍くらいあると思う。1024ポイントでSPS1kHzとした場合、CFFTは1024ポイントで-0.5kHzから+0.5kHzの範囲を取る。RFFTは1024ポイントで0Hzから0.5kHzの範囲を取る。
ちゃんと試してないけど、CFFTは16ポイントから4kポイントまで、RFFTは32ポイントから4kポイントまで解析できる。
CFFT/RFFTを行う上記コードを含めるとバイナリサイズは256kくらいになった。この部分をコメントアウトすると128k位になる。FFT解析を行うには128kほどのFlashが必要になる。もちろん、FFTを行う上でメモリ領域も必要になる。
const char *const commandに文字列を入れておけば、それを解析する。主にUART等からコマンドを受け取って実行する事を想定。もちろんsprintfで作ってもいいけど、そんな事するくらいなら直接呼べという話で。
"fft 1 4096 100 -10" というコマンドを実行すると、複素数で100SPS/-10Hzの波形を生成しFFT解析する。
"fft 0 4096 100 30" というコマンドを実行すると、実部のみで100SPS/30Hzの波形を生成しFFT解析する。
それぞれの結果は以下の通り。
CFFTは左端が-50Hz、右端が+50Hz、RFFTは左端が0Hz、右端が+50Hzになっている。どちらも結果は4096ポイント得られるので、RFFTのほうが倍の分解能になる。
4096ポイントの場合、168MHzのCortex-M4FでCFFTが4msec未満、RFFTが1msec未満だった。CFFTは若干の時間がかかるが、RFFTは恐ろしく早い。
ただ、どちらも4kポイントだと32KiBのRAMが必要になる。自由に使えるヒープ領域は100KiB程度なので、あまり大規模なものを作ろうとするとちょっと心もとない。***
とりあえずFFTが動くようになったので、パルス圧縮の相関処理とかもできるわけだが、時間的・空間的な余裕がかなり少なそう。
超音波距離計もちょっと飽きてきたし、気分転換に別のヤツで遊ぼうかな。
FFT/IFFTが動くならOFDM変調とかできる。


0 件のコメント:
コメントを投稿