Interface 2015年4月号 第1部 第7章のプログラム

Dependencies:   USBDevice mbed

Information

FftTest - Interface 2015年4月号 第1部 第7章 のソフトウェア

Program for Section 7 in April 2015 issue of Interface
(Japanese electronics magazine)

概要

このプログラムは、

  • ハイパスフィルタ、ローパスフィルタ、ノッチフィルタ
    を行うFilterTestクラス、
  • FFT (256点)
    を行うFftTestクラス、
    波形をUSBシリアル通信でホストへ送信するmain関数で構成されています。

FilterTest.h, FilterTest.cpp

  • A-Dサンプリング - 1 kSPS
  • ハイパスフィルタ(遮断周波数 0.5 Hz、1次バターワース)
  • ローパスフィルタ(遮断周波数 30 Hz、2次バターワース)
  • ノッチフィルタ(中心周波数 50 Hz、2次)

FftTest.h, FftTest.cpp

  • 256点FFT演算 - クーリー-テューキー アルゴリズム 基数-2 時間間引き
  • ハン窓(ハニング窓)適用
  • パワー値計算
  • 振幅値計算
  • 振幅値正規化(実効値にスケーリング)

main.cpp

  • データ送信レート - 200 SPS
  • メインループ - ポーリングにより、サンプリング、フィルタ処理完了フラグがセットされたら、
    また、FFT完了フラグがセットされたらUSBシリアル通信経由で、ホストへ送信する

シリアル通信フォーマット

 (※)誌面ではパケットサイズ 64 byteとなっていますが、
    64 byteでは、PCのUSBドライバが 4096 byteまで保持し、波形が滑らかに描画できないため、
    Ver.1.0.2で、32 byteに変更しています。

  • 34byte固定長パケット方式
  • 波形データパケット、FFTパケットの2種類
波形データパケットFFTパケット
0x00パケットヘッダ(固定値0xAA)パケットヘッダ(固定値0xAA)
0x01データ種別ID(0x01: 波形データ)(0x02: FFTデータ)
0x02パケット番号(0 - 99繰り返し)レンジ(0: DC - 23 Hz, 1: 23 - 46 Hz, 2: 46 - 70 Hz)
0x03ペイロードサイズ(固定値30)ペイロードサイズ(固定値30)
0x04 - 0x21波形データ(short, big endian)FFTデータ(unsigned short, big endian)

Description

This contains FilterTest class, FftTest class and main function.

FilterTest class:

  • High pass filter, Low pass, Notch filter

FftTest class:

  • FFT (256 points)

Main function:

  • Send waveform and FFT data to host via USB serial class.

FilterTest.h, FilterTest.cpp

  • A-D sampling - 1 kSPS
  • High pass filter - Cut off frequency 0.5 Hz, first order butterworth
  • Low pass filter - Cut off frequency 30 Hz, second order butterworth
  • Notch filter - Center frequency 50 Hz, second order

FftTest.h, FftTest.cpp

  • 256 points FFT - Cooley-Tukey algorithm Radix-2 Decimation-In-Time
  • Apply Hann window
  • Calculate power spectrum
  • Calculate amplitude spectrum
  • Normalize amplitude

main.cpp

  • Data sending rate - 200 SPS
  • Main loop - sending waveform and FFT data via USB serial interface when detecting ready flag.

Packet format for USB serial interface

  • Packet size: 34 bytes(fixed)
  • Two types of packet, waveform packet and FFT packet
Waveform packetFFT packet
0x00Packet header (0xAA (fixed))Packet header (0xAA (fixed))
0x01Data type ID (0x01: Waveform ID)(0x02: FFT ID)
0x02Packet number (0 - 99)Range (0: DC - 23 Hz, 1: 23 - 46 Hz, 2: 46 - 70 Hz)
0x03Payload size (30 (fixed))Payload size (30 (fixed))
0x04 - 0x21Waveform data (short, big endian)FFT data (unsigned short, big endian)
Revision:
0:9779b89a8820
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FftTest.cpp	Mon Feb 23 22:19:58 2015 +0000
@@ -0,0 +1,249 @@
+/**
+ *  @file       FftTest.cpp
+ *  @brief      Calculate 256 point FFT with Hann window
+ *              Cooley-Tukey algorithm Radix-2 Decimation-In-Time
+ *  @date       2015.02.24
+ *  @version    1.0.2
+ */
+#include "FftTest.h"
+
+/** Constructor
+*/
+FftTest::FftTest()
+{
+    init();
+}
+
+/** Destructor
+*/
+FftTest::~FftTest()
+{
+}
+
+/** Apply window fucntion
+ *  @param      in_data[]   Input data
+ *  @param      out_data[]  Output data
+ */
+void FftTest::apply_window(float in_data[], float out_data[])
+{
+    int i;
+    for(i = 0; i < _dat_len; i++) {
+        out_data[i] = in_data[i] * _window_array[i];
+    }
+}
+
+/** Calculate FFT
+ *  @param      in_data     Input data
+ *  @param      out_re_data Output real data
+ *  @param      out_im_data Output imaginary data
+ */
+void FftTest::calc_fft(float in_data[], float out_re_data[], float out_im_data[])
+{
+    int i, stage, phase, block, upper_idx, lower_idx;
+    int32_t block_dist, butterfly_num;
+    float w_re, w_im, u_re, u_im, temp_re, temp_im, temp_w_re, temp_w_im;
+
+    /* Bit reversal */
+    for (i = 0; i < _dat_len; i++) {
+        out_re_data[i] = in_data[_br_idx_array[i]];
+        out_im_data[i] = 0.0f;
+    }
+
+    /* Butterfly */
+    for (stage = 1; stage <= _digit_len; stage++) {
+        block_dist = 1 << stage;
+        butterfly_num = block_dist >> 1;
+
+        w_re = 1.0f;
+        w_im = 0.0f;
+        u_re = cosf(PI / butterfly_num);
+        u_im = -sinf(PI / butterfly_num);
+
+        for (phase = 0; phase < butterfly_num; phase++) {
+            for (block = phase; block < _dat_len; block += block_dist) {
+                upper_idx = block;
+                lower_idx = upper_idx + butterfly_num;
+                temp_re = out_re_data[lower_idx] * w_re - out_im_data[lower_idx] * w_im;
+                temp_im = out_re_data[lower_idx] * w_im + out_im_data[lower_idx] * w_re;
+                out_re_data[lower_idx] = out_re_data[upper_idx] - temp_re;
+                out_im_data[lower_idx] = out_im_data[upper_idx] - temp_im;
+                out_re_data[upper_idx] += temp_re;
+                out_im_data[upper_idx] += temp_im;
+            }
+            temp_w_re = w_re * u_re - w_im * u_im;
+            temp_w_im = w_re * u_im + w_im * u_re;
+            w_re = temp_w_re;
+            w_im = temp_w_im;
+        }
+    }
+}
+
+/** Calculate Power spectrum
+ *  @param      re_data[]   Input real data
+ *  @param      im_data[]   Input imaginary data
+ *  @param      pow_data[]  Power data
+ *  @param      len         Data length
+ */
+void FftTest::calc_power(float re_data[], float im_data[], float pow_data[], int32_t len)
+{
+    int i;
+    int32_t d_len = (len > _dat_len)? _dat_len : len;
+    for(i = 0; i < d_len; i++) {
+        pow_data[i] = re_data[i] * re_data[i] + im_data[i] * im_data[i];
+    }
+}
+
+/** Calculate Amplitude spectrum
+ *  @param      pow_data[]  Input power data
+ *  @param      amp_data[]  Output amplitude data
+ *  @param      len         Data length
+ */
+void FftTest::calc_amplitude(float pow_data[], float amp_data[], int32_t len)
+{
+    int i;
+    int32_t d_len = (len > _dat_len)? _dat_len : len;
+    for(i = 0; i < d_len; i++) {
+        amp_data[i] = sqrtf(pow_data[i]);
+    }
+}
+
+/** Normalize Amplitude spectrum
+ *  @param      pow_data[]  Input power data
+ *  @param      amp_data[]  Output amplitude data
+ *  @param      len         Data length
+ */
+void FftTest::norm_amplitude(float amp_data[], int32_t len)
+{
+    int i;
+    int32_t d_len = (len > _dat_len)? _dat_len : len;
+    amp_data[0] = amp_data[0] / (float)_dat_len;
+    for(i = 1; i < d_len; i++) {
+        amp_data[i] = amp_data[i] * SQRT_2F / (float)_dat_len;
+    }
+}
+
+/** Initialization
+ */
+void FftTest::init()
+{
+    _digit_len = get_digit_len(DATA_LENGTH);
+    _dat_len = (1 << _digit_len);
+
+#ifdef  _LARGE_RAM
+    _window_array = new float[_dat_len];
+    _br_idx_array = new int32_t[_dat_len];
+    set_hann_window();
+    set_bit_reversal();
+#endif  /* LARGE_RAM */
+}
+
+/** Get digit length
+ *  @param      val         Source value
+ *  @return                 Digit length
+ */
+int32_t FftTest::get_digit_len(int32_t val)
+{
+    int32_t ret_val = 0;
+    while( val > 1 ) {
+        ret_val++;
+        val >>= 1;
+    }
+    return ret_val;
+}
+
+/** Calculate Hann window function
+ */
+void FftTest::set_hann_window()
+{
+#ifdef  _LARGE_RAM
+    int i;
+    for(i = 0; i < _dat_len; i++) {
+        _window_array[i] = (1.0f - cosf(2.0f * PI * i / (_dat_len - 1.0f))) / 2.0f;
+    }
+#endif  /* LARGE_RAM */
+}
+
+/** Calculate bit reversal index
+ */
+void FftTest::set_bit_reversal()
+{
+#ifdef  _LARGE_RAM
+    int i, j;
+    int32_t reversed_max_bit = (_dat_len >> 1);
+
+    _br_idx_array[0] = 0;
+    for (i = 1; i < _dat_len; i <<= 1) {
+        for (j = 0; j < i; j++) {
+            _br_idx_array[j + i] = _br_idx_array[j] + reversed_max_bit;
+        }
+        reversed_max_bit >>= 1;
+    }
+#endif  /* LARGE_RAM */
+}
+
+#ifndef _LARGE_RAM
+/* Const */
+const float FftTest::_window_array[] = {
+    0.0f,           0.000151774f,   0.0006070039f,  0.001365413f,   0.002426542f,
+    0.003789745f,   0.005454196f,   0.007418883f,   0.009682614f,   0.01224402f,
+    0.01510153f,    0.01825343f,    0.02169779f,    0.02543253f,    0.02945537f,
+    0.03376389f,    0.03835545f,    0.04322727f,    0.0483764f,     0.05379971f,
+    0.0594939f,     0.06545553f,    0.07168096f,    0.07816643f,    0.08490799f,
+    0.09190154f,    0.09914286f,    0.1066275f,     0.114351f,  0.1223086f,
+    0.1304955f, 0.1389068f, 0.1475372f, 0.1563817f, 0.1654347f, 0.1746908f, 0.1841445f, 0.1937899f, 0.2036212f, 0.2136324f,
+    0.2238175f, 0.2341703f, 0.2446844f, 0.2553535f, 0.2661712f, 0.2771308f, 0.2882257f, 0.2994492f, 0.3107945f, 0.3222546f,
+    0.3338226f, 0.3454915f, 0.3572542f, 0.3691036f, 0.3810324f, 0.3930334f, 0.4050995f, 0.4172231f, 0.4293969f, 0.4416136f,
+    0.4538658f, 0.466146f,  0.4784467f, 0.4907605f, 0.50308f,   0.5153975f, 0.5277057f, 0.5399971f, 0.5522642f, 0.5644996f,
+    0.5766958f, 0.5888455f, 0.6009412f, 0.6129757f, 0.6249415f, 0.6368315f, 0.6486384f, 0.6603551f, 0.6719745f, 0.6834894f,
+    0.6948929f, 0.7061782f, 0.7173382f, 0.7283663f, 0.7392558f, 0.75f,      0.7605925f, 0.7710267f, 0.7812964f, 0.7913953f,
+    0.8013173f, 0.8110564f, 0.8206066f, 0.8299623f, 0.8391176f, 0.848067f,  0.8568051f, 0.8653266f, 0.8736263f, 0.8816991f,
+    0.8895403f, 0.897145f,  0.9045085f, 0.9116265f, 0.9184946f, 0.9251086f, 0.9314645f, 0.9375585f, 0.9433869f, 0.948946f,
+    0.9542326f, 0.9592435f, 0.9639755f, 0.9684259f, 0.9725919f, 0.976471f,  0.9800608f, 0.9833592f, 0.9863641f, 0.9890738f,
+    0.9914865f, 0.9936009f, 0.9954156f, 0.9969296f, 0.9981418f, 0.9990517f, 0.9996585f, 0.999962f,  0.999962f,  0.9996585f,
+    0.9990517f, 0.9981418f, 0.9969296f, 0.9954156f, 0.9936009f, 0.9914865f, 0.9890738f, 0.9863641f, 0.9833592f, 0.9800608f,
+    0.976471f,  0.9725919f, 0.9684259f, 0.9639755f, 0.9592435f, 0.9542326f, 0.948946f,  0.9433869f, 0.9375585f, 0.9314645f,
+    0.9251086f, 0.9184946f, 0.9116265f, 0.9045085f, 0.897145f,  0.8895403f, 0.8816991f, 0.8736263f, 0.8653266f, 0.8568051f,
+    0.848067f,  0.8391176f, 0.8299623f, 0.8206066f, 0.8110564f, 0.8013173f, 0.7913953f, 0.7812964f, 0.7710267f, 0.7605925f,
+    0.75f,      0.7392558f, 0.7283663f, 0.7173382f, 0.7061782f, 0.6948929f, 0.6834894f, 0.6719745f, 0.6603551f, 0.6486384f,
+    0.6368315f, 0.6249415f, 0.6129757f, 0.6009412f, 0.5888455f, 0.5766958f, 0.5644996f, 0.5522642f, 0.5399971f, 0.5277057f,
+    0.5153975f, 0.50308f,   0.4907605f, 0.4784467f, 0.466146f,  0.4538658f, 0.4416136f, 0.4293969f, 0.4172231f, 0.4050995f,
+    0.3930334f, 0.3810324f, 0.3691036f, 0.3572542f, 0.3454915f, 0.3338226f, 0.3222546f, 0.3107945f, 0.2994492f, 0.2882257f,
+    0.2771308f, 0.2661712f, 0.2553535f, 0.2446844f, 0.2341703f, 0.2238175f, 0.2136324f, 0.2036212f, 0.1937899f, 0.1841445f,
+    0.1746908f, 0.1654347f, 0.1563817f, 0.1475372f, 0.1389068f, 0.1304955f, 0.1223086f, 0.114351f,  0.1066275f, 0.09914286f,
+    0.09190154f,    0.08490799f,    0.07816643f,    0.07168096f,    0.06545553f,
+    0.0594939f,     0.05379971f,    0.0483764f,     0.04322727f,    0.03835545f,
+    0.03376389f,    0.02945537f,    0.02543253f,    0.02169779f,    0.01825343f,
+    0.01510153f,    0.01224402f,    0.009682614f,   0.007418883f,   0.005454196f,
+    0.003789745f,   0.002426542f,   0.001365413f,   0.0006070039f,  0.000151774f,
+    0.0f
+};
+
+const int FftTest::_br_idx_array[] = {
+    0,      128,    64,     192,    32,     160,    96,     224,    16,     144,
+    80,     208,    48,     176,    112,    240,    8,      136,    72,     200,
+    40,     168,    104,    232,    24,     152,    88,     216,    56,     184,
+    120,    248,    4,      132,    68,     196,    36,     164,    100,    228,
+    20,     148,    84,     212,    52,     180,    116,    244,    12,     140,
+    76,     204,    44,     172,    108,    236,    28,     156,    92,     220,
+    60,     188,    124,    252,    2,      130,    66,     194,    34,     162,
+    98,     226,    18,     146,    82,     210,    50,     178,    114,    242,
+    10,     138,    74,     202,    42,     170,    106,    234,    26,     154,
+    90,     218,    58,     186,    122,    250,    6,      134,    70,     198,
+    38,     166,    102,    230,    22,     150,    86,     214,    54,     182,
+    118,    246,    14,     142,    78,     206,    46,     174,    110,    238,
+    30,     158,    94,     222,    62,     190,    126,    254,    1,      129,
+    65,     193,    33,     161,    97,     225,    17,     145,    81,     209,
+    49,     177,    113,    241,    9,      137,    73,     201,    41,     169,
+    105,    233,    25,     153,    89,     217,    57,     185,    121,    249,
+    5,      133,    69,     197,    37,     165,    101,    229,    21,     149,
+    85,     213,    53,     181,    117,    245,    13,     141,    77,     205,
+    45,     173,    109,    237,    29,     157,    93,     221,    61,     189,
+    125,    253,    3,      131,    67,     195,    35,     163,    99,     227,
+    19,     147,    83,     211,    51,     179,    115,    243,    11,     139,
+    75,     203,    43,     171,    107,    235,    27,     155,    91,     219,
+    59,     187,    123,    251,    7,      135,    71,     199,    39,     167,
+    103,    231,    23,     151,    87,     215,    55,     183,    119,    247,
+    15,     143,    79,     207,    47,     175,    111,    239,    31,     159,
+    95,     223,    63,     191,    127,    255
+};
+#endif  /* LARGE_RAM */