スペクトログラム このプログラムの説明は,CQ出版社「トランジスタ技術」の2021年10月号から開始された連載記事「STM32マイコンではじめるPC計測」の中にあります.このプログラムといっしょに使うPC側のプログラムについても同誌を参照してください.
Dependencies: Array_Matrix mbed SerialTxRxIntr DSP_ADDA UIT_FFT_Real Window
Diff: main.cpp
- Revision:
- 0:3bf11d2ab6ad
- Child:
- 1:d4e3f39ce206
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Sep 09 08:55:42 2021 +0000 @@ -0,0 +1,124 @@ +//--------------------------------------------------------------------- +// スペクトログラム (Nucleo-F446RE 用) +// +// 標本化周波数を 10 倍に設定し,アンチエイリアシングフィルタを使う +// +// ● PC 側のプログラム: "CQ_Spectrogram" +// ● ボーレート: 最初: 9600 baud +// 通信確立後: 460800 baud +// ● 受信データの文字列の終了マーク: "\r" +// +// ● 入力: A1 +// +// 2021/07/11, Copyright (c) 2021 MIKAMI, Naoki +//--------------------------------------------------------------------- + +#include <string> +#include "Array.hpp" +#include "DSP_AdcIntr.hpp" +#include "Coefs_IIR_LP.hpp" // 縦続形 IIR フィルタの係数 +#include "IirCascade.hpp" // 縦続形 IIR フィルタ +#include "FFT_Spectrogram.hpp" +#include "DoubleBuffer.hpp" +#include "XferSpectrum.hpp" +using namespace Mikami; + +#ifndef __STM32F446xx_H +#error "Use Nucleo-F446RE" +#endif + +const int N_FFT_ = 512; // FFT の点数 +const int N_DATA_ = N_FFT_ + 1; // スペクトル解析に使うデータ数(差分処理を考慮) +const int N_FRAME_ = N_FFT_/2 + 1; // 1フレーム当たり標本化するデータ数 +const int N_FFT_2_ = N_FFT_/2; // FFT の点数の半分 +const int RATIO_ = 10; // オーバーサンプリングの倍率 +const int N_TX_ = 251; // PC に転送するデータ数 + +DspAdcIntr myAdc_(10.24f*RATIO_, A1); // 標本化周波数: 100 kHz +IirCascade aaf_(ORDER1_, CK1_, G01_); // ダウンサンプリング用 Anti-alias フィルタ +DoubleBuffer<float> buf_(N_FRAME_); // AD の結果を保存するダブル・バッファ + +// ADC 変換終了割り込みに対する割り込みサービス・ルーチン +void AdcIsr() +{ + static int count = 0; + + float xn = myAdc_.Read(); + float yn = aaf_.Execute(xn); // ダウンサンプリング用 Anti-alias フィルタの実行 + + if (++count >= RATIO_) + { + buf_.Store(yn); // ダウンサンプリングされたデータをバッファへ格納 + count = 0; + buf_.IsFullSwitch(); // バッファが満杯であればバッファを切り替える + } +} + +int main() +{ + // FFT によるスペクトル解析オブジェクトの生成 + FftSpectropgram analyzer(N_DATA_, N_FFT_); + float empha; // 高域強調器の係数 + + SerialRxTxIntr rxTx; // PC との通信用 + XferSpectrum tx(rxTx, N_TX_); // PC に転送するためのオブジェクトの生成 + + Array<float> sn(N_FFT_+1, 0.0f); // スペクトル解析の対象となるデータ + Array<float> absFt(N_FRAME_); // 解析結果:スペクトルの絶対値 + + NVIC_SetPriority(ADC_IRQn, 0); // AD変換終了割り込みの優先度が最高 + NVIC_SetPriority(USART2_IRQn, 1); + + bool ready = false; // スペクトルの計算終了で true + bool okGo = false; // "GO" を受信したら true + + myAdc_.SetIntrVec(&AdcIsr); // AD変換終了割り込みの割り当て + while (true) + { + // PC からのコマンドの解析 + if (rxTx.IsEol()) // 受信バッファのデータが有効になった場合の処理 + { + string str = rxTx.GetBuffer(); + if (str == "Spectrogram") + { + rxTx.TxString("ACK\n"); // PC からの "Spectrogram" に対して "ACK" を送信する + wait_ms(10); + rxTx.Baud(460800); // 以降は 460,800 baud + } + else if (str.substr(0, 2) == "GO") + { + // str の内容 + // [0] 'G' + // [1] 'O' + // [2] 高域強調の有無:'Y', 'N' + + if (str[2] == 'Y') empha = 1.0f; // 高域強調は有効 + else empha = 0; // 高域強調は無効 + + okGo = true; // データの転送要求あり + } + } + + if (buf_.IsFull()) // 入力データが満杯の場合,以下の処理を行う + { + // フレームの後半のデータを前半に移動する + for (int n=0; n<N_FFT_2_; n++) + sn[n] = sn[n+N_FRAME_]; + // フレームの後半には新しいデータを格納する + for (int n=0; n<N_FRAME_; n++) + sn[n+N_FFT_2_] = buf_.Get(n); + + analyzer.SetHighEmphasizer(empha); // 高域強調の有無の指令 + analyzer.Execute(sn, absFt); // スペクトル解析の実行 + ready = true; // スペクトル解析終了 + } + + // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する + if (okGo && ready) + { + tx.ToPC(absFt); // データを PC へ転送 + ready = false; + okGo = false; + } + } +} \ No newline at end of file