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) |
Diff: main.cpp
- Revision:
- 0:9779b89a8820
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Feb 23 22:19:58 2015 +0000 @@ -0,0 +1,278 @@ +/** + * @file Main.cpp + * @brief Send 1ch waveform and FFT data to PC via USB serial + * @date 2015.02.24 + * @version 1.0.2 + */ +#include "mbed.h" +#include "USBSerial.h" +#include "FilterTest.h" +#include "FftTest.h" + +#define ON (1) +#define OFF (0) + +#define LED_ON (0) +#define LED_OFF (1) + +#define BYTE_MASK (0xFF) +#define INT16_MAX (32767) +#define INT16_MIN (-32768) +#define UINT16_MAX (65535) +#define UINT16_MIN (0) + +#define SAMPLING_RATE (0.001) /* A/D sampling rate (1kHz) */ +#define DEC_COUNT (5) /* Decimation count (sampling 200Hz)*/ + +#define PACKET_HEADER (0xAA) +#define WAVEFORM_ID (0x01) +#define FFT_ID (0x02) +#define PACKET_SIZE (34) /* 64 -> 34 for PC to receive waveform data smoothly */ +#define PAYLOAD_SIZE (30) /* PACKET_SIZE - 4(Header size) */ +#define WAV_BUF_NUM (2) /* Double buffer */ +#define WV_PKT_CNTR_NUM (100) +/* [ Packet format ] */ +/* ---------------- */ +/* 0x00 | Header byte | Fixed value (PACKET_HEADER:0xAA) */ +/* 0x01 | Data type | ID value (0x01:Waveform, 0x02:FFT) */ +/* 0x02 | Packet counter | Count value (0to99 < CNTR_SIZE:100) */ +/* 0x03 | Payload size | Fixed value (DATA_NUM:30) */ +/* 0x04 | Data0 (MSBside)| Short type (signed, big endian) */ +/* ... | ... | */ +/* 0x21 | Data29(LSBside)| */ +/* ---------------- */ + +#define FFT_LENGTH (256) /* FFT data length */ +#define FFT_INTERVAL (200) /* FFT calc interval */ +#define FFT_OVERLAP_LEN (56) /* FFT overlap length */ +#define FFT_BUF_NUM (2) /* Double buffer */ +#define FFT_SEND_LEN (90) /* FFT send data length */ + + +Ticker sampling; /* Interval timer for A/D sampling */ +AnalogIn wave_in(p20); /* Waveform input */ +USBSerial serial; +FilterTest filter; +FftTest fft; + +/* DIP switch for filter ON/OFF */ +DigitalIn hpf_on(p28); +DigitalIn lpf_on(p27); +DigitalIn brf_on(p26); +/* [ DIP switch ] */ +/* TG-LPC11U35-501 +3.3V */ +/* | CN1 CN2 | --- */ +/* | mbed BD | | */ +/* | 13 | p28 -/ -+ Hiph pass filter */ +/* | 14 | p27 -/ -+ Low pass filter */ +/* | 15 | p26 -/ -+ Notch filter */ +/* | | */ +/* pull-down is default as below. */ +/* http://developer.mbed.org/handbook/DigitalIn */ +/* It says "By default, the DigitalIn is setup */ +/* with an internal pull-down resistor." */ +/* Better to set the pins to pull-up mode and the */ +/* switch connected to GND. */ + +/* For debug pins */ +DigitalOut dbg_p21(p21); +DigitalOut dbg_p22(p22); +DigitalOut dbg_led1(LED1); +DigitalOut dbg_led2(LED2); + +/* Variables */ +int32_t wav_dec_cntr; /* Decimation counter */ + +uint8_t wav_pkt_buf[WAV_BUF_NUM][PACKET_SIZE]; /* Waveform packet buffer */ +int32_t wav_pkt_idx; /* Packet buffer index */ +int32_t wav_tgl; /* Packet buffer toggle */ +int32_t wav_send_flag; /* Waveform sending flag */ +uint8_t wav_pkt_cntr; /* Waveform packet counter */ + +float fft_buf[FFT_BUF_NUM][FFT_LENGTH]; /* FFT data buffer */ +uint8_t pkt_buf_fft[PACKET_SIZE] = {0}; /* FFT packet buffer */ +int32_t fft_dat_idx; /* FFT buffer index */ +int32_t fft_tgl; /* FFT buffer toggle */ +int32_t fft_send_flag; /* FFT sendign flag */ + +float fft_out_re[FFT_LENGTH] = {0.0f}; /* FFT result real */ +float fft_out_im[FFT_LENGTH] = {0.0f}; /* FFT result imaginary */ +uint16_t fft_res[FFT_SEND_LEN] = {0}; /* FFT result */ + +/** Initialize waveform packet buffer + * @param tgl buffer wav_tgl index + * @param ctr packet counter + */ +void init_wav_pkt_buf(int32_t tgl, uint8_t ctr) +{ + int i; + if(tgl>WAV_BUF_NUM) { + return; + } + wav_pkt_buf[tgl][0] = PACKET_HEADER; + wav_pkt_buf[tgl][1] = WAVEFORM_ID; + wav_pkt_buf[tgl][2] = ctr; /* Packet counter */ + wav_pkt_buf[tgl][3] = PAYLOAD_SIZE; /* Payload size */ + wav_pkt_idx = 4; /* Start index of waveform */ + for(i=4; i<PACKET_SIZE; i++) { + wav_pkt_buf[tgl][i] = 0; + } +} + +/** Set FFT packet buffer + * @param buf Source data buffer + * @param ctr packet counter + */ +void set_fft_pkt_buf(uint16_t* buf, uint8_t ctr) +{ + int i; + pkt_buf_fft[0] = PACKET_HEADER; + pkt_buf_fft[1] = FFT_ID; + pkt_buf_fft[2] = ctr; /* Packet counter */ + pkt_buf_fft[3] = PAYLOAD_SIZE; /* Payload size */ + i=0; + while(i<PAYLOAD_SIZE) { + pkt_buf_fft[i + 4] = (uint8_t)((buf[i / 2] >> 8 ) & BYTE_MASK); + i++; + pkt_buf_fft[i + 4] = (uint8_t)(buf[i / 2] & BYTE_MASK); + i++; + } +} + +/** Interval timer for read A/D value + */ +void ad_sampling() +{ + int32_t wav_temp; + dbg_led2 = lpf_on; /* for debug */ + + /* Read and filter data */ + wav_temp = (int32_t)filter.calc( (double)(wave_in.read_u16() - INT16_MAX), hpf_on, lpf_on, brf_on ); + + /* Store data */ + wav_dec_cntr = (wav_dec_cntr + 1) % DEC_COUNT; + if(wav_dec_cntr == 0) { /* Decimation */ + + dbg_p21 = ON; /* for debug */ + /* Waveform */ + wav_temp = (wav_temp > INT16_MAX) ? INT16_MAX : wav_temp; + wav_temp = (wav_temp < INT16_MIN) ? INT16_MIN : wav_temp; + wav_pkt_buf[wav_tgl][wav_pkt_idx] = (uint8_t)((wav_temp >> 8 ) & BYTE_MASK); + wav_pkt_idx++; + wav_pkt_buf[wav_tgl][wav_pkt_idx] = (uint8_t)(wav_temp & BYTE_MASK); + wav_pkt_idx++; + + if(wav_pkt_idx >= PACKET_SIZE) { /* Counter reached */ + wav_tgl = !wav_tgl; + wav_pkt_cntr = (wav_pkt_cntr + 1 ) % WV_PKT_CNTR_NUM; + init_wav_pkt_buf(wav_tgl, wav_pkt_cntr); + wav_send_flag = ON; /* Set flag */ + } + + /* FFT */ + fft_buf[fft_tgl][fft_dat_idx + FFT_OVERLAP_LEN] = (float)wav_temp; + if(fft_dat_idx >= (FFT_INTERVAL - FFT_OVERLAP_LEN)) { + fft_buf[!fft_tgl][fft_dat_idx + FFT_OVERLAP_LEN - FFT_INTERVAL] = (float)wav_temp; + } + + fft_dat_idx = (fft_dat_idx + 1) % FFT_INTERVAL; + if(fft_dat_idx == 0) { + fft_tgl = !fft_tgl; + fft_send_flag = ON; /* Set flag */ + } + + dbg_p21 = OFF; /* for debug */ + } +} + +/** Send data packet + * @param p_buf Data array + * @param size Data length + */ +bool send_packet(uint8_t *p_buf, uint16_t size) +{ + if(serial.writeable()) { + dbg_p22 = ON; /* for debug */ + serial.writeBlock (p_buf, size); /* Send data via USB */ + dbg_p22 = OFF; /* for debug */ + return true; + } else { + return false; + } +} + +/** Main function + */ +int main() +{ + int i; + uint16_t fft_temp; + + /* Initialization */ + wav_dec_cntr = 0; + + init_wav_pkt_buf(wav_tgl, wav_pkt_cntr); + init_wav_pkt_buf(!wav_tgl, wav_pkt_cntr); + wav_pkt_idx = 0; + wav_tgl = 0; + wav_send_flag = OFF; + wav_pkt_cntr = 0; + + fft_dat_idx = 0; + fft_tgl = 0; + fft_send_flag = OFF; + + dbg_p21 = OFF; + dbg_p22 = OFF; + dbg_led1 = LED_OFF; + dbg_led2 = LED_OFF; + + /* Start interval timer */ + sampling.attach(&ad_sampling, SAMPLING_RATE); + + /* Main loop */ + while(1) { + /* Waveform */ + if(wav_send_flag != OFF) { + /* Send data */ + send_packet(wav_pkt_buf[!wav_tgl], (uint16_t)PACKET_SIZE); + + /* Disable interrupt */ + __disable_irq(); + wav_send_flag = OFF; /* Clear flag */ + __enable_irq(); + } + + /* FFT */ + if(fft_send_flag != OFF) { + dbg_led1 = LED_ON; /* for debug */ + + /* Calculate FFT and normalized amplitude spectrum */ + fft.apply_window(fft_buf[!fft_tgl], fft_buf[!fft_tgl]); + fft.calc_fft(fft_buf[!fft_tgl], fft_out_re, fft_out_im); + fft.calc_power(fft_out_re, fft_out_im, fft_out_re, FFT_SEND_LEN); + fft.calc_amplitude(fft_out_re, fft_out_re, FFT_SEND_LEN); + fft.norm_amplitude(fft_out_re, FFT_SEND_LEN); + for(i=0; i<FFT_SEND_LEN; i++) { + fft_temp = (fft_out_re[i] > UINT16_MAX) ? UINT16_MAX : (uint16_t)fft_out_re[i]; + fft_temp = (fft_out_re[i] < UINT16_MIN) ? UINT16_MIN : fft_temp; + fft_res[i] = fft_temp; + } + + /* Send data */ + i=0; + while((PAYLOAD_SIZE * i) < FFT_SEND_LEN * sizeof(fft_res[0])) { + set_fft_pkt_buf((fft_res + (PAYLOAD_SIZE/sizeof(fft_res[0])) * i), i); + send_packet(pkt_buf_fft, (uint16_t)PACKET_SIZE); + i++; + } + + /* Disable interrupt */ + __disable_irq(); + fft_send_flag = OFF; /* Clear flag */ + __enable_irq(); + + dbg_led1 = LED_OFF; /* for debug */ + } + } +}