
スペクトログラム このプログラムの説明は,CQ出版社「トランジスタ技術」の2021年10月号から開始された連載記事「STM32マイコンではじめるPC計測」の中にあります.このプログラムといっしょに使うPC側のプログラムについても同誌を参照してください.
Dependencies: Array_Matrix mbed SerialTxRxIntr DSP_ADDA UIT_FFT_Real Window
Revision 1:d4e3f39ce206, committed 2021-12-08
- Comitter:
- MikamiUitOpen
- Date:
- Wed Dec 08 03:15:17 2021 +0000
- Parent:
- 0:3bf11d2ab6ad
- Child:
- 2:2ca9f8a0f6ef
- Commit message:
- 2
Changed in this revision
--- a/DoubleBuffer.hpp Thu Sep 09 08:55:42 2021 +0000 +++ b/DoubleBuffer.hpp Wed Dec 08 03:15:17 2021 +0000 @@ -2,7 +2,7 @@ // ダブル・バッファの template クラス // バッファに2次元配列(Matrix クラス)を使用 // -// 2021/05/22, Copyright (c) 2021 MIKAMI, Naoki +// 2021/10/22, Copyright (c) 2021 MIKAMI, Naoki //-------------------------------------------------------- #ifndef DOUBLE_BUFFER_HPP @@ -11,19 +11,19 @@ #include "Matrix.hpp" using namespace Mikami; -template<class T> class DoubleBuffer +class DoubleBuffer { public: // コンストラクタ - explicit DoubleBuffer(int size, T initialValue = 0) + explicit DoubleBuffer(int size, float initialValue = 0) : N_(size), buf_(2, size, initialValue), ping_(0), pong_(1), index_(0), full_(false) {} // データを格納 - void Store(T data) { buf_[ping_][index_++] = data; } + void Store(float data) { buf_[ping_][index_++] = data; } // 出力バッファからデータの取り出し - T Get(int n) const { return buf_[pong_][n]; } + float Get(int n) const { return buf_[pong_][n]; } // バッファが満杯でバッファを切り替える void IsFullSwitch() @@ -46,7 +46,7 @@ private: const int N_; // バッファのサイズ - Matrix<T> buf_; // バッファ + Matrix<float> buf_; // バッファ int ping_, pong_; // バッファ切替用 int index_; // 入力データのカウンタ bool full_; // 満杯の場合 true
--- a/MySpectrogram/FFT_Spectrogram.cpp Thu Sep 09 08:55:42 2021 +0000 +++ b/MySpectrogram/FFT_Spectrogram.cpp Wed Dec 08 03:15:17 2021 +0000 @@ -1,23 +1,23 @@ //------------------------------------------------------- // スペクトログラムで使う FFT 解析用クラス // -// 2021/05/24, Copyright (c) 2021 MIKAMI, Naoki +// 2021/11/17, Copyright (c) 2021 MIKAMI, Naoki //------------------------------------------------------- #include "FFT_Spectrogram.hpp" namespace Mikami { - FftSpectropgram::FftSpectropgram(int nData, int nFft) - : N_DATA_(nData), N_FFT_(nFft), - fft_(nFft), wHm_(nFft, nData-1), b1_(1.0f), - xData_(nFft), wData_(nFft), yFft_(nFft/2+1) {} + FftSpectropgram::FftSpectropgram(int nFft) + : N_FFT_(nFft), fft_(nFft), wHm_(nFft), + xData_(nFft), wData_(nFft), yFft_(nFft/2+1) + { SwEmphasis(false); } // 最初は高域強調なし - void FftSpectropgram::Execute(const Array<float> &xn, Array<float> &absFt) + void FftSpectropgram::Execute(const Array<float> &xn, + Array<float> &absFt) { - // 高域強調 - for (int n=0; n<N_DATA_-1; n++) - xData_[n] = xn[n+1] - b1_*xn[n]; + // データのコピー,高域強調の有無は SwEmphasis() で切り替え + (this->*fp)(xn); // 直流分を除去 float sum = 0; @@ -25,9 +25,18 @@ float ave = sum/N_FFT_; for (int n=0; n<N_FFT_; n++) xData_[n] = xData_[n] - ave; - wData_ = wHm_.Execute(xData_); // 窓掛け - fft_.Execute(wData_, yFft_); // FFT の実行 + wData_ = wHm_.Execute(xData_); // 窓掛け + fft_.Execute(wData_, yFft_); // FFT の実行 for (int n=0; n<=N_FFT_/2; n++) // 絶対値に変換 absFt[n] = 100.0f*abs(yFft_[n]); } + + // データを作業領域にコピーする際に高域強調処理を行う + void FftSpectropgram::CopyH(const Array<float> &xn) + { + // 差分の処理 + for (int n=1; n<N_FFT_; n++) // n=1 から開始 + xData_[n] = xn[n] - xn[n-1]; + xData_[0] = xData_[1]; // n=0 に対応 + } } \ No newline at end of file
--- a/MySpectrogram/FFT_Spectrogram.hpp Thu Sep 09 08:55:42 2021 +0000 +++ b/MySpectrogram/FFT_Spectrogram.hpp Wed Dec 08 03:15:17 2021 +0000 @@ -1,11 +1,11 @@ //------------------------------------------------------- // スペクトログラムで使う FFT 解析用クラス(ヘッダ) // -// 2021/05/24, Copyright (c) 2021 MIKAMI, Naoki +// 2021/11/17, Copyright (c) 2021 MIKAMI, Naoki //------------------------------------------------------- -#ifndef FFT_SPECTROGRAM_HPP -#define FFT_SPECTROGRAM_HPP +#ifndef FFT_ANALYZER_HPP +#define FFT_ANALYZER_HPP #include "Array.hpp" #include "fftReal.hpp" @@ -16,32 +16,35 @@ class FftSpectropgram { public: - // nData: 解析で使うデータ数 - // nFft: 解析で使う FFT の点数 - FftSpectropgram(int nData, int nFft); + // nFft: FFT のデータ点の数 + explicit FftSpectropgram(int nFft); virtual ~FftSpectropgram() {} - void Execute(const Array<float> &xn, Array<float> &db); - // 高域強調の程度を決める定数の設定(b1 = 1 で差分,b1 = 0 で高域強調なし) - void SetHighEmphasizer(float b1) { b1_ = b1; } + void Execute(const Array<float> &xn, Array<float> &absFt); + + // データのコピーを行う際の高域強調の有無切り替え + void SwEmphasis(bool on) + { fp = on ? &FftSpectropgram::CopyH : &FftSpectropgram::Copy; } private: - const int N_DATA_; const int N_FFT_; FftReal fft_; - HammingWindow wHm_; - float b1_; + HammingWindow wHm_; // ハミング窓 - Array<float> xData_; // 解析で使うデータ + Array<float> xData_; // 解析対象の時系列データ Array<float> wData_; // 窓掛けされたデータ - Array<Complex> yFft_; // FFT の出力 + Array<Complex> yFft_; // FFT の結果 - float Norm(Complex x) - { return x.real()*x.real() + x.imag()*x.imag(); } + // コピーの際に使う関数に対する関数ポインタ + void (FftSpectropgram::*fp)(const Array<float> &xn); + // データを作業領域にコピー:高域強調は行わない + void Copy(const Array<float> &xn) { xData_ = xn; } + // データを作業領域にコピー:高域強調は行う + void CopyH(const Array<float> &xn); // コピー・コンストラクタおよび代入演算子の禁止のため FftSpectropgram(const FftSpectropgram& ); FftSpectropgram& operator=(const FftSpectropgram& ); }; } -#endif // FFT_SPECTROGRAM_HPP \ No newline at end of file +#endif // FFT_ANALYZER_HPP \ No newline at end of file
--- a/MySpectrogram/Window.lib Thu Sep 09 08:55:42 2021 +0000 +++ b/MySpectrogram/Window.lib Wed Dec 08 03:15:17 2021 +0000 @@ -1,1 +1,1 @@ -https://os.mbed.com/users/MikamiUitOpen/code/Window/#823e9a4ab223 +https://os.mbed.com/users/MikamiUitOpen/code/Window/#d8673bf6f89c
--- a/main.cpp Thu Sep 09 08:55:42 2021 +0000 +++ b/main.cpp Wed Dec 08 03:15:17 2021 +0000 @@ -4,22 +4,22 @@ // 標本化周波数を 10 倍に設定し,アンチエイリアシングフィルタを使う // // ● PC 側のプログラム: "CQ_Spectrogram" -// ● ボーレート: 最初: 9600 baud +// ● ボーレート: 最初: 9600 baud // 通信確立後: 460800 baud // ● 受信データの文字列の終了マーク: "\r" // // ● 入力: A1 // -// 2021/07/11, Copyright (c) 2021 MIKAMI, Naoki +// 2021/11/17, Copyright (c) 2021 MIKAMI, Naoki //--------------------------------------------------------------------- #include <string> #include "Array.hpp" #include "DSP_AdcIntr.hpp" +#include "FFT_Spectrogram.hpp" +#include "DoubleBuffer.hpp" #include "Coefs_IIR_LP.hpp" // 縦続形 IIR フィルタの係数 #include "IirCascade.hpp" // 縦続形 IIR フィルタ -#include "FFT_Spectrogram.hpp" -#include "DoubleBuffer.hpp" #include "XferSpectrum.hpp" using namespace Mikami; @@ -28,15 +28,14 @@ #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_SMPL_ = N_FFT_/2; // 1度に標本化するデータ数 +const int N_SPC_ = N_FFT_/2 + 1; // 有効なスペクトルの点数 +const int RATIO_ = 10; // ダウンサンプリングの倍率:1/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 の結果を保存するダブル・バッファ +DoubleBuffer buf_(N_SMPL_); // ダウンサンプリングの結果を保存するダブル・バッファ // ADC 変換終了割り込みに対する割り込みサービス・ルーチン void AdcIsr() @@ -56,15 +55,12 @@ int main() { - // FFT によるスペクトル解析オブジェクトの生成 - FftSpectropgram analyzer(N_DATA_, N_FFT_); - float empha; // 高域強調器の係数 + SerialRxTxIntr rxTx; // PC との通信用 + XferSpectrum tx(rxTx, N_TX_); // PC に転送するためのオブジェクトの生成 + FftSpectropgram analyzer(N_FFT_); // スペクトログラムで使うオブジェクトの生成 - SerialRxTxIntr rxTx; // PC との通信用 - XferSpectrum tx(rxTx, N_TX_); // PC に転送するためのオブジェクトの生成 - - Array<float> sn(N_FFT_+1, 0.0f); // スペクトル解析の対象となるデータ - Array<float> absFt(N_FRAME_); // 解析結果:スペクトルの絶対値 + Array<float> sn(N_FFT_, 0.0f); // スペクトル解析の対象となるデータ + Array<float> absFt(N_SPC_); // 解析結果:スペクトルの絶対値 NVIC_SetPriority(ADC_IRQn, 0); // AD変換終了割り込みの優先度が最高 NVIC_SetPriority(USART2_IRQn, 1); @@ -85,16 +81,9 @@ wait_ms(10); rxTx.Baud(460800); // 以降は 460,800 baud } - else if (str.substr(0, 2) == "GO") + if (str.substr(0, 2) == "GO") { - // str の内容 - // [0] 'G' - // [1] 'O' - // [2] 高域強調の有無:'Y', 'N' - - if (str[2] == 'Y') empha = 1.0f; // 高域強調は有効 - else empha = 0; // 高域強調は無効 - + analyzer.SwEmphasis(str[2] == 'Y'); okGo = true; // データの転送要求あり } } @@ -102,15 +91,14 @@ if (buf_.IsFull()) // 入力データが満杯の場合,以下の処理を行う { // フレームの後半のデータを前半に移動する - for (int n=0; n<N_FFT_2_; n++) - sn[n] = sn[n+N_FRAME_]; + for (int n=0; n<N_SMPL_; n++) + sn[n] = sn[n+N_SMPL_]; // フレームの後半には新しいデータを格納する - for (int n=0; n<N_FRAME_; n++) - sn[n+N_FFT_2_] = buf_.Get(n); + for (int n=0; n<N_SMPL_; n++) + sn[n+N_SMPL_] = buf_.Get(n); - analyzer.SetHighEmphasizer(empha); // 高域強調の有無の指令 - analyzer.Execute(sn, absFt); // スペクトル解析の実行 - ready = true; // スペクトル解析終了 + analyzer.Execute(sn, absFt); // スペクトル解析の実行 + ready = true; // スペクトル解析終了 } // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する