Interface 2015年4月号 第1部 第7章のプログラム
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 packet | FFT packet | |
0x00 | Packet header (0xAA (fixed)) | Packet header (0xAA (fixed)) |
0x01 | Data type ID (0x01: Waveform ID) | (0x02: FFT ID) |
0x02 | Packet number (0 - 99) | Range (0: DC - 23 Hz, 1: 23 - 46 Hz, 2: 46 - 70 Hz) |
0x03 | Payload size (30 (fixed)) | Payload size (30 (fixed)) |
0x04 - 0x21 | Waveform data (short, big endian) | FFT data (unsigned short, big endian) |
FftTest.cpp
- Committer:
- t_tatsuoka
- Date:
- 2015-02-23
- Revision:
- 0:9779b89a8820
File content as of revision 0:9779b89a8820:
/** * @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 */