スペクトログラム このプログラムの説明は,CQ出版社「トランジスタ技術」の2021年10月号から開始された連載記事「STM32マイコンではじめるPC計測」の中にあります.このプログラムといっしょに使うPC側のプログラムについても同誌を参照してください.

Dependencies:   Array_Matrix mbed SerialTxRxIntr DSP_ADDA UIT_FFT_Real Window

Files at this revision

API Documentation at this revision

Comitter:
MikamiUitOpen
Date:
Wed Dec 08 03:15:17 2021 +0000
Parent:
0:3bf11d2ab6ad
Child:
2:2ca9f8a0f6ef
Commit message:
2

Changed in this revision

DoubleBuffer.hpp Show annotated file Show diff for this revision Revisions of this file
MySpectrogram/FFT_Spectrogram.cpp Show annotated file Show diff for this revision Revisions of this file
MySpectrogram/FFT_Spectrogram.hpp Show annotated file Show diff for this revision Revisions of this file
MySpectrogram/Window.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- 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 へ転送する