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)
Committer:
t_tatsuoka
Date:
Thu Jul 30 10:26:38 2015 +0000
Revision:
1:537eb14c5332
Parent:
0:9779b89a8820
Notch filter 60 Hz version

Who changed what in which revision?

UserRevisionLine numberNew contents of line
t_tatsuoka 0:9779b89a8820 1 /**
t_tatsuoka 0:9779b89a8820 2 * @file FilterTest.cpp
t_tatsuoka 0:9779b89a8820 3 * @brief Calculate filters
t_tatsuoka 0:9779b89a8820 4 * HPF: 1st order / LPF: 2nd order / Notch: 2nd order
t_tatsuoka 0:9779b89a8820 5 * @date 2015.02.22
t_tatsuoka 0:9779b89a8820 6 * @version 1.0.1
t_tatsuoka 0:9779b89a8820 7 */
t_tatsuoka 0:9779b89a8820 8 #include "FilterTest.h"
t_tatsuoka 0:9779b89a8820 9
t_tatsuoka 0:9779b89a8820 10 /** Constructor
t_tatsuoka 0:9779b89a8820 11 */
t_tatsuoka 0:9779b89a8820 12 FilterTest::FilterTest ()
t_tatsuoka 0:9779b89a8820 13 {
t_tatsuoka 0:9779b89a8820 14 set_hpf_coef(INIT_HB, INIT_HA);
t_tatsuoka 0:9779b89a8820 15 set_lpf_coef(INIT_LB, INIT_LA1, INIT_LA2);
t_tatsuoka 0:9779b89a8820 16 set_brf_coef(INIT_NB, INIT_NA1, INIT_NA2);
t_tatsuoka 0:9779b89a8820 17 reset_hpf_buf();
t_tatsuoka 0:9779b89a8820 18 reset_lpf_buf();
t_tatsuoka 0:9779b89a8820 19 reset_brf_buf();
t_tatsuoka 0:9779b89a8820 20 }
t_tatsuoka 0:9779b89a8820 21
t_tatsuoka 0:9779b89a8820 22 /** Calculate filter
t_tatsuoka 0:9779b89a8820 23 * @param val Input value
t_tatsuoka 0:9779b89a8820 24 * @param hpf_on High pass filter enable
t_tatsuoka 0:9779b89a8820 25 * @param lpf_on Low pass filter enable
t_tatsuoka 0:9779b89a8820 26 * @param brf_on Notch filter enable
t_tatsuoka 0:9779b89a8820 27 * @return Output value
t_tatsuoka 0:9779b89a8820 28 */
t_tatsuoka 0:9779b89a8820 29 double FilterTest::calc(double val, int hpf_on, int lpf_on, int brf_on)
t_tatsuoka 0:9779b89a8820 30 {
t_tatsuoka 0:9779b89a8820 31 double retVal = val;
t_tatsuoka 0:9779b89a8820 32 /* High pass filter */
t_tatsuoka 0:9779b89a8820 33 if(hpf_on) {
t_tatsuoka 0:9779b89a8820 34 retVal = hpf(retVal);
t_tatsuoka 0:9779b89a8820 35 } else {
t_tatsuoka 0:9779b89a8820 36 reset_hpf_buf();
t_tatsuoka 0:9779b89a8820 37 }
t_tatsuoka 0:9779b89a8820 38 /* Low pass filter */
t_tatsuoka 0:9779b89a8820 39 if(lpf_on) {
t_tatsuoka 0:9779b89a8820 40 retVal = lpf(retVal);
t_tatsuoka 0:9779b89a8820 41 } else {
t_tatsuoka 0:9779b89a8820 42 reset_lpf_buf();
t_tatsuoka 0:9779b89a8820 43 }
t_tatsuoka 0:9779b89a8820 44 /* Notch (Band reject) filter */
t_tatsuoka 0:9779b89a8820 45 if(brf_on) {
t_tatsuoka 0:9779b89a8820 46 retVal = brf(retVal);
t_tatsuoka 0:9779b89a8820 47 } else {
t_tatsuoka 0:9779b89a8820 48 reset_brf_buf();
t_tatsuoka 0:9779b89a8820 49 }
t_tatsuoka 0:9779b89a8820 50 return retVal;
t_tatsuoka 0:9779b89a8820 51 }
t_tatsuoka 0:9779b89a8820 52
t_tatsuoka 0:9779b89a8820 53 /** Reset delay buffers for high pass filter
t_tatsuoka 0:9779b89a8820 54 */
t_tatsuoka 0:9779b89a8820 55 void FilterTest::reset_hpf_buf()
t_tatsuoka 0:9779b89a8820 56 {
t_tatsuoka 0:9779b89a8820 57 _hw = 0.0;
t_tatsuoka 0:9779b89a8820 58 }
t_tatsuoka 0:9779b89a8820 59
t_tatsuoka 0:9779b89a8820 60 /** Reset delay buffers for low pass filter
t_tatsuoka 0:9779b89a8820 61 */
t_tatsuoka 0:9779b89a8820 62 void FilterTest::reset_lpf_buf()
t_tatsuoka 0:9779b89a8820 63 {
t_tatsuoka 0:9779b89a8820 64 _lw1 = 0.0;
t_tatsuoka 0:9779b89a8820 65 _lw2 = 0.0;
t_tatsuoka 0:9779b89a8820 66 }
t_tatsuoka 0:9779b89a8820 67
t_tatsuoka 0:9779b89a8820 68 /** Reset delay buffers for notch filter
t_tatsuoka 0:9779b89a8820 69 */
t_tatsuoka 0:9779b89a8820 70 void FilterTest::reset_brf_buf()
t_tatsuoka 0:9779b89a8820 71 {
t_tatsuoka 0:9779b89a8820 72 _nw1 = 0.0;
t_tatsuoka 0:9779b89a8820 73 _nw2 = 0.0;
t_tatsuoka 0:9779b89a8820 74 }
t_tatsuoka 0:9779b89a8820 75
t_tatsuoka 0:9779b89a8820 76 /** Set coefficient for HPF
t_tatsuoka 0:9779b89a8820 77 * @param hb Numerator cofficient
t_tatsuoka 0:9779b89a8820 78 * @param ha Denominator cofficient
t_tatsuoka 0:9779b89a8820 79 * @retval true OK
t_tatsuoka 0:9779b89a8820 80 * @retval false NG
t_tatsuoka 0:9779b89a8820 81 */
t_tatsuoka 0:9779b89a8820 82 bool FilterTest:: set_hpf_coef(double hb, double ha)
t_tatsuoka 0:9779b89a8820 83 {
t_tatsuoka 0:9779b89a8820 84 if(hb > 1.0) {
t_tatsuoka 0:9779b89a8820 85 return false;
t_tatsuoka 0:9779b89a8820 86 } else if((ha > 1.0)||(ha < -1.0)) {
t_tatsuoka 0:9779b89a8820 87 return false;
t_tatsuoka 0:9779b89a8820 88 } else {
t_tatsuoka 0:9779b89a8820 89 _hb = hb;
t_tatsuoka 0:9779b89a8820 90 _ha = ha;
t_tatsuoka 0:9779b89a8820 91 reset_hpf_buf();
t_tatsuoka 0:9779b89a8820 92 return true;
t_tatsuoka 0:9779b89a8820 93 }
t_tatsuoka 0:9779b89a8820 94 }
t_tatsuoka 0:9779b89a8820 95
t_tatsuoka 0:9779b89a8820 96 /** Set coefficient for LPF
t_tatsuoka 0:9779b89a8820 97 * @param lb Numerator cofficient
t_tatsuoka 0:9779b89a8820 98 * @param la1 Denominator cofficient 1
t_tatsuoka 0:9779b89a8820 99 * @param la2 Denominator cofficient 2
t_tatsuoka 0:9779b89a8820 100 * @retval true OK
t_tatsuoka 0:9779b89a8820 101 * @retval false NG
t_tatsuoka 0:9779b89a8820 102 */
t_tatsuoka 0:9779b89a8820 103 bool FilterTest:: set_lpf_coef(double lb, double la1, double la2)
t_tatsuoka 0:9779b89a8820 104 {
t_tatsuoka 0:9779b89a8820 105 if(lb > 1.0) {
t_tatsuoka 0:9779b89a8820 106 return false;
t_tatsuoka 0:9779b89a8820 107 } else if((la1 > 2.0)||(la1 < -2.0)) {
t_tatsuoka 0:9779b89a8820 108 return false;
t_tatsuoka 0:9779b89a8820 109 } else if(la2 > 1.0) {
t_tatsuoka 0:9779b89a8820 110 return false;
t_tatsuoka 0:9779b89a8820 111 } else {
t_tatsuoka 0:9779b89a8820 112 _lb = lb;
t_tatsuoka 0:9779b89a8820 113 _la1 = la1;
t_tatsuoka 0:9779b89a8820 114 _la2 = la2;
t_tatsuoka 0:9779b89a8820 115 reset_lpf_buf();
t_tatsuoka 0:9779b89a8820 116 return true;
t_tatsuoka 0:9779b89a8820 117 }
t_tatsuoka 0:9779b89a8820 118 }
t_tatsuoka 0:9779b89a8820 119
t_tatsuoka 0:9779b89a8820 120 /** Set coefficient for BRF
t_tatsuoka 0:9779b89a8820 121 * @param nb Numerator cofficient
t_tatsuoka 0:9779b89a8820 122 * @param na1 Denominator cofficient 1
t_tatsuoka 0:9779b89a8820 123 * @param na2 Denominator cofficient 2
t_tatsuoka 0:9779b89a8820 124 * @retval true OK
t_tatsuoka 0:9779b89a8820 125 * @retval false NG
t_tatsuoka 0:9779b89a8820 126 */
t_tatsuoka 0:9779b89a8820 127 bool FilterTest:: set_brf_coef(double nb, double na1, double na2)
t_tatsuoka 0:9779b89a8820 128 {
t_tatsuoka 0:9779b89a8820 129 if(nb > 1.0) {
t_tatsuoka 0:9779b89a8820 130 return false;
t_tatsuoka 0:9779b89a8820 131 } else if((na1 > 2.0)||(na1 < -2.0)) {
t_tatsuoka 0:9779b89a8820 132 return false;
t_tatsuoka 0:9779b89a8820 133 } else if(na2 > 1.0) {
t_tatsuoka 0:9779b89a8820 134 return false;
t_tatsuoka 0:9779b89a8820 135 } else {
t_tatsuoka 0:9779b89a8820 136 _nb = nb;
t_tatsuoka 0:9779b89a8820 137 _na1 = na1;
t_tatsuoka 0:9779b89a8820 138 _na2 = na2;
t_tatsuoka 0:9779b89a8820 139 reset_brf_buf();
t_tatsuoka 0:9779b89a8820 140 return true;
t_tatsuoka 0:9779b89a8820 141 }
t_tatsuoka 0:9779b89a8820 142 }
t_tatsuoka 0:9779b89a8820 143
t_tatsuoka 0:9779b89a8820 144 /** High pass filter (1st order)
t_tatsuoka 0:9779b89a8820 145 * @param x Input value
t_tatsuoka 0:9779b89a8820 146 * @return Output value
t_tatsuoka 0:9779b89a8820 147 *
t_tatsuoka 0:9779b89a8820 148 * hb v +
t_tatsuoka 0:9779b89a8820 149 * x ---I>---+---O-------+--- y
t_tatsuoka 0:9779b89a8820 150 * | hw|- |
t_tatsuoka 0:9779b89a8820 151 * | [z] |
t_tatsuoka 0:9779b89a8820 152 * | | ha |
t_tatsuoka 0:9779b89a8820 153 * +---O---<I--+
t_tatsuoka 0:9779b89a8820 154 */
t_tatsuoka 0:9779b89a8820 155 double FilterTest::hpf(double x)
t_tatsuoka 0:9779b89a8820 156 {
t_tatsuoka 0:9779b89a8820 157 double v, y;
t_tatsuoka 0:9779b89a8820 158
t_tatsuoka 0:9779b89a8820 159 v = _hb * x;
t_tatsuoka 0:9779b89a8820 160 y = v - _hw;
t_tatsuoka 0:9779b89a8820 161 _hw = v + _ha * y;
t_tatsuoka 0:9779b89a8820 162 return y;
t_tatsuoka 0:9779b89a8820 163 }
t_tatsuoka 0:9779b89a8820 164
t_tatsuoka 0:9779b89a8820 165 /** Low pass filter (2nd order)
t_tatsuoka 0:9779b89a8820 166 * @param x Input value
t_tatsuoka 0:9779b89a8820 167 * @return Output value
t_tatsuoka 0:9779b89a8820 168 *
t_tatsuoka 0:9779b89a8820 169 * lb v
t_tatsuoka 0:9779b89a8820 170 * x --I>--+-----O-------+--- y
t_tatsuoka 0:9779b89a8820 171 * | lw1| |
t_tatsuoka 0:9779b89a8820 172 * | [z] |
t_tatsuoka 0:9779b89a8820 173 * | 2 | -la1 |
t_tatsuoka 0:9779b89a8820 174 * +-I>--O---<I--+
t_tatsuoka 0:9779b89a8820 175 * | lw2| |
t_tatsuoka 0:9779b89a8820 176 * | [z] |
t_tatsuoka 0:9779b89a8820 177 * | | -la2 |
t_tatsuoka 0:9779b89a8820 178 * +-----O---<I--+
t_tatsuoka 0:9779b89a8820 179 */
t_tatsuoka 0:9779b89a8820 180 double FilterTest::lpf(double x)
t_tatsuoka 0:9779b89a8820 181 {
t_tatsuoka 0:9779b89a8820 182 double v, y;
t_tatsuoka 0:9779b89a8820 183
t_tatsuoka 0:9779b89a8820 184 v = _lb * x;
t_tatsuoka 0:9779b89a8820 185 y = v + _lw1;
t_tatsuoka 0:9779b89a8820 186 _lw1 = 2 * v - _la1 * y + _lw2;
t_tatsuoka 0:9779b89a8820 187 _lw2 = v - _la2 * y;
t_tatsuoka 0:9779b89a8820 188 return y;
t_tatsuoka 0:9779b89a8820 189 }
t_tatsuoka 0:9779b89a8820 190
t_tatsuoka 0:9779b89a8820 191 /** Notch filter (Band reject filter) (2nd order)
t_tatsuoka 0:9779b89a8820 192 * @param x Input value
t_tatsuoka 0:9779b89a8820 193 * @return Output value
t_tatsuoka 0:9779b89a8820 194 *
t_tatsuoka 0:9779b89a8820 195 * nb v
t_tatsuoka 0:9779b89a8820 196 * x -+-I>--+----O-------+--- y
t_tatsuoka 0:9779b89a8820 197 * | | nw1| |
t_tatsuoka 0:9779b89a8820 198 * | | [z] |
t_tatsuoka 0:9779b89a8820 199 * +| na1 | | |
t_tatsuoka 0:9779b89a8820 200 * O-I>-------O |
t_tatsuoka 0:9779b89a8820 201 * -| | nw2| |
t_tatsuoka 0:9779b89a8820 202 * | | [z] |
t_tatsuoka 0:9779b89a8820 203 * | | | -na2 |
t_tatsuoka 0:9779b89a8820 204 * | +----O---<I--+
t_tatsuoka 0:9779b89a8820 205 * | |
t_tatsuoka 0:9779b89a8820 206 * +------------------+
t_tatsuoka 0:9779b89a8820 207 */
t_tatsuoka 0:9779b89a8820 208 double FilterTest::brf(double x)
t_tatsuoka 0:9779b89a8820 209 {
t_tatsuoka 0:9779b89a8820 210 double v, y;
t_tatsuoka 0:9779b89a8820 211
t_tatsuoka 0:9779b89a8820 212 v = _nb * x;
t_tatsuoka 0:9779b89a8820 213 y = v + _nw1;
t_tatsuoka 0:9779b89a8820 214 _nw1 = _na1 * ( x - y ) + _nw2;
t_tatsuoka 0:9779b89a8820 215 _nw2 = v - _na2 * y;
t_tatsuoka 0:9779b89a8820 216 return y;
t_tatsuoka 0:9779b89a8820 217 }