トランジスタ技術2014年10月号第6章のソフトウェア

Dependencies:   USBDevice mbed

Information

tg_201410s6_Plethysmographs トランジスタ技術 2014年 10月号 第6章のソフトウェア

Program for Section 6 in October. 2014 issue of the Transistor Gijutsu
(Japanese electronics magazine)

概要

このプログラムは、

  • 脈波データ取得(A-Dサンプリング、ハイパスフィルタ)
  • 脈拍数の算出(パルス検出、LEDおよび同期音出力、移動平均処理)

を行うPulseRateクラスと、それらをUSBシリアル通信でホストへ送信するmain関数で構成されています。

PulseRate.h, PulseRate.cpp

  • A-Dサンプリング - 100 SPS
  • ハイパスフィルタ - 遮断周波数0.1Hz、1次バターワースフィルタ
  • パルス検出 - 脈波を微分処理、5point移動平均して速度脈波を求め、
    それを包絡線検波した波形を、速度脈波が再び超える点をパルス開始とする
  • LED、同期音出力 - パルス同期LED(10ms)、圧電サウンダ出力(1kHz、10ms)
  • 移動平均処理 - 直近5拍分の平均値を脈波として算出

main.cpp

  • PulseRateクラスのインスタンスを生成
  • 処理開始メソッドを実行
  • メインループ - ポーリングにより、脈波データ、脈拍数データの完了フラグが返されたら、
    USBシリアル通信経由で、ホストへ送信する

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

  • 4byte固定長パケット方式
  • 脈波データパケット、脈拍数データパケットの2種類
脈波データパケット脈拍数データパケット
0x00パケットヘッダ(固定値0xAA)パケットヘッダ(固定値0xAA)
0x01波形番号(0 - 99繰り返し)脈拍数ID(固定値0xBB)
0x02, 0x03脈波データ(singed, 2byte)脈拍数データ(20 - 300, 2byte)

Description

This contains PulseRate class and main function.

PulseRate class:

  • Acquiring pulse waveform (A-D sampling, high pass filter)
  • Calculate pulse rate (Detecting pulse, Sync. LED and buzzer, moving averaging)

Main function:

  • Send pulse waveform and rate to host via USB serial class.

PulseRate.h, PulseRate.cpp

  • A-D sampling - 100 SPS
  • High pass filter - Cut off frequency 0.1Hz, first order butterworth
  • Detecting pulse - Calculating velocity pulse waveform by derivation and moving averaging (5point).
    Moreover, calculating threshold waveform like envelope demodulator.
    Detecting point the velocity waveform pass over the threshold waveform as starting pulse.
  • Sync. LED, buzzer - Synchronous pulse LED(10ms), piezo sounder(1kHz, 10ms)
  • Moving averaging - Calculating pulse rate averaging the previous 5 pulse.

main.cpp

  • Generating an instance of PulseRate class
  • Executing start procedure method
  • Main loop - sending pulse waveform data and pulse rate data via USB serial interface when detecting ready in return value.

Packet format for USB serial interface

  • Packet size: 4 bytes(fixed)
  • Two types of packets, pulse waveform packet and pulse rate packet
Pulse waveform packetPulse rate packet
0x00Packet header (0xAA (fixed)))Packet header (0xAA (fixed))
0x01Sampling number (0 - 99)Pulse rate ID (0xBB (fixed))
0x02, 0x03Pulse waveform data (singed, 2byte)Pulse rate data (20 - 300, 2byte)
Committer:
t_tatsuoka
Date:
Thu Sep 11 15:02:45 2014 +0000
Revision:
0:f0c12790aadb
Name?tg_201410s6_Plethysmographs; Description?????????2014?10???6????????; Publish in :CQ Publishing

Who changed what in which revision?

UserRevisionLine numberNew contents of line
t_tatsuoka 0:f0c12790aadb 1 /**
t_tatsuoka 0:f0c12790aadb 2 * @file PulseRate.cpp
t_tatsuoka 0:f0c12790aadb 3 * @brief Calculate pulse waveform and pulse rate
t_tatsuoka 0:f0c12790aadb 4 * @date 2014.08.08
t_tatsuoka 0:f0c12790aadb 5 * @version 1.0.0
t_tatsuoka 0:f0c12790aadb 6 */
t_tatsuoka 0:f0c12790aadb 7 #include "PulseRate.h"
t_tatsuoka 0:f0c12790aadb 8
t_tatsuoka 0:f0c12790aadb 9 /** Constructor
t_tatsuoka 0:f0c12790aadb 10 * @param sensor Pin for A/D converter
t_tatsuoka 0:f0c12790aadb 11 * @param sync_led Pin for synchronous LED
t_tatsuoka 0:f0c12790aadb 12 * @param beep Pin for piezo sounder
t_tatsuoka 0:f0c12790aadb 13 */
t_tatsuoka 0:f0c12790aadb 14 PulseRate::PulseRate(PinName sensor, PinName sync_led, PinName beep) :
t_tatsuoka 0:f0c12790aadb 15 _sensor(sensor), _sync_led(sync_led), _beep(beep) {
t_tatsuoka 0:f0c12790aadb 16
t_tatsuoka 0:f0c12790aadb 17 _sync_led = LED_OFF;
t_tatsuoka 0:f0c12790aadb 18 _val = 0;
t_tatsuoka 0:f0c12790aadb 19 _wave_flag = false;
t_tatsuoka 0:f0c12790aadb 20 _pr_flag = false;
t_tatsuoka 0:f0c12790aadb 21 _sampling_num = 0;
t_tatsuoka 0:f0c12790aadb 22 _mv_idx = 0;
t_tatsuoka 0:f0c12790aadb 23 _beep.period(1.0/BEEP_FREQ);
t_tatsuoka 0:f0c12790aadb 24 }
t_tatsuoka 0:f0c12790aadb 25
t_tatsuoka 0:f0c12790aadb 26 /** Start interval timer
t_tatsuoka 0:f0c12790aadb 27 */
t_tatsuoka 0:f0c12790aadb 28 void PulseRate::start_sampling() {
t_tatsuoka 0:f0c12790aadb 29 _sampling.attach(this, &PulseRate::interval_timer, SAMPLING_RATE);
t_tatsuoka 0:f0c12790aadb 30 }
t_tatsuoka 0:f0c12790aadb 31
t_tatsuoka 0:f0c12790aadb 32 /** Get waveform data
t_tatsuoka 0:f0c12790aadb 33 * @param &num Sampling number
t_tatsuoka 0:f0c12790aadb 34 * @param &wave_val Waveform value
t_tatsuoka 0:f0c12790aadb 35 * @retval true Ready for data
t_tatsuoka 0:f0c12790aadb 36 * @retval false Not ready
t_tatsuoka 0:f0c12790aadb 37 */
t_tatsuoka 0:f0c12790aadb 38 bool PulseRate::get_wave(uint32_t &num, int32_t &wave_val) {
t_tatsuoka 0:f0c12790aadb 39 if(_wave_flag) {
t_tatsuoka 0:f0c12790aadb 40 _wave_flag = false;
t_tatsuoka 0:f0c12790aadb 41 num = _sampling_num;
t_tatsuoka 0:f0c12790aadb 42 wave_val = _val;
t_tatsuoka 0:f0c12790aadb 43 return true;
t_tatsuoka 0:f0c12790aadb 44 } else {
t_tatsuoka 0:f0c12790aadb 45 return false;
t_tatsuoka 0:f0c12790aadb 46 }
t_tatsuoka 0:f0c12790aadb 47 }
t_tatsuoka 0:f0c12790aadb 48
t_tatsuoka 0:f0c12790aadb 49 /** Gat pulse rate
t_tatsuoka 0:f0c12790aadb 50 * @param &pr Pulse rate
t_tatsuoka 0:f0c12790aadb 51 * @retval true Ready for data
t_tatsuoka 0:f0c12790aadb 52 * @retval false Not ready
t_tatsuoka 0:f0c12790aadb 53 */
t_tatsuoka 0:f0c12790aadb 54 bool PulseRate::get_pr_val(uint32_t &pr) {
t_tatsuoka 0:f0c12790aadb 55 if(_pr_flag) {
t_tatsuoka 0:f0c12790aadb 56 _pr_flag = false;
t_tatsuoka 0:f0c12790aadb 57 pr = _pr;
t_tatsuoka 0:f0c12790aadb 58 return true;
t_tatsuoka 0:f0c12790aadb 59 } else {
t_tatsuoka 0:f0c12790aadb 60 return false;
t_tatsuoka 0:f0c12790aadb 61 }
t_tatsuoka 0:f0c12790aadb 62 }
t_tatsuoka 0:f0c12790aadb 63
t_tatsuoka 0:f0c12790aadb 64 /** Interval timer
t_tatsuoka 0:f0c12790aadb 65 */
t_tatsuoka 0:f0c12790aadb 66 void PulseRate::interval_timer() {
t_tatsuoka 0:f0c12790aadb 67
t_tatsuoka 0:f0c12790aadb 68 /* Pulse waveform */
t_tatsuoka 0:f0c12790aadb 69 _val = ((int32_t)(_sensor.read_u16()) - AD_OFFSET); /* Get AD value */
t_tatsuoka 0:f0c12790aadb 70 _val = hpf(_val); /* High pass filter (Comment out if not necessary) */
t_tatsuoka 0:f0c12790aadb 71 _sampling_num = (_sampling_num + 1) % SPL_NUM; /* Update sampling number */
t_tatsuoka 0:f0c12790aadb 72 _wave_flag = true; /* Set ready flag for pulse waveform */
t_tatsuoka 0:f0c12790aadb 73
t_tatsuoka 0:f0c12790aadb 74 /* Pulse rate */
t_tatsuoka 0:f0c12790aadb 75 if(detect_peak(_val)) { /* If detecting pulse */
t_tatsuoka 0:f0c12790aadb 76 calc_pr(); /* Calculate pulse rate including flag set */
t_tatsuoka 0:f0c12790aadb 77 }
t_tatsuoka 0:f0c12790aadb 78
t_tatsuoka 0:f0c12790aadb 79 /* Control LED and Beep */
t_tatsuoka 0:f0c12790aadb 80 if(_pr_flag) {
t_tatsuoka 0:f0c12790aadb 81 _sync_led = LED_ON;
t_tatsuoka 0:f0c12790aadb 82 _beep.write(BEEP_LOUD);
t_tatsuoka 0:f0c12790aadb 83 } else {
t_tatsuoka 0:f0c12790aadb 84 _sync_led = LED_OFF;
t_tatsuoka 0:f0c12790aadb 85 _beep.write(0);
t_tatsuoka 0:f0c12790aadb 86 }
t_tatsuoka 0:f0c12790aadb 87 }
t_tatsuoka 0:f0c12790aadb 88
t_tatsuoka 0:f0c12790aadb 89 /** Fixed point high pass filter
t_tatsuoka 0:f0c12790aadb 90 * @param val Input value
t_tatsuoka 0:f0c12790aadb 91 * @return Output value
t_tatsuoka 0:f0c12790aadb 92 *
t_tatsuoka 0:f0c12790aadb 93 * A/D value of mbed is Q6 format.
t_tatsuoka 0:f0c12790aadb 94 * Please shift in advance if necessary.
t_tatsuoka 0:f0c12790aadb 95 */
t_tatsuoka 0:f0c12790aadb 96 int32_t PulseRate::hpf(int32_t val) {
t_tatsuoka 0:f0c12790aadb 97 int32_t reg, ret_val;
t_tatsuoka 0:f0c12790aadb 98 int64_t temp_val;
t_tatsuoka 0:f0c12790aadb 99
t_tatsuoka 0:f0c12790aadb 100 temp_val = (int64_t)COEF_AH * (int64_t)_reg_hpf;
t_tatsuoka 0:f0c12790aadb 101 reg = val + (int32_t)(temp_val >> 30);
t_tatsuoka 0:f0c12790aadb 102 ret_val = reg - _reg_hpf;
t_tatsuoka 0:f0c12790aadb 103 temp_val = (int64_t)COEF_BH * (int64_t)ret_val;
t_tatsuoka 0:f0c12790aadb 104 ret_val = (int32_t)(temp_val >> 30);
t_tatsuoka 0:f0c12790aadb 105 _reg_hpf = reg;
t_tatsuoka 0:f0c12790aadb 106 return ret_val;
t_tatsuoka 0:f0c12790aadb 107 }
t_tatsuoka 0:f0c12790aadb 108
t_tatsuoka 0:f0c12790aadb 109 /** Detect pulse peak
t_tatsuoka 0:f0c12790aadb 110 * @param &val Waveform data value
t_tatsuoka 0:f0c12790aadb 111 * @retval true Detected pulse peak
t_tatsuoka 0:f0c12790aadb 112 * @retval false No detection
t_tatsuoka 0:f0c12790aadb 113 */
t_tatsuoka 0:f0c12790aadb 114 bool PulseRate::detect_peak(int32_t val) {
t_tatsuoka 0:f0c12790aadb 115 int i;
t_tatsuoka 0:f0c12790aadb 116 bool retVal = false;
t_tatsuoka 0:f0c12790aadb 117
t_tatsuoka 0:f0c12790aadb 118 /* Calculate differential of input value */
t_tatsuoka 0:f0c12790aadb 119 _mv_buf[_mv_idx] = val - _prev_val;
t_tatsuoka 0:f0c12790aadb 120 _prev_val = val;
t_tatsuoka 0:f0c12790aadb 121 _mv_idx = (_mv_idx + 1) % MV_LENGTH;
t_tatsuoka 0:f0c12790aadb 122
t_tatsuoka 0:f0c12790aadb 123 /* Calculate moving averaging */
t_tatsuoka 0:f0c12790aadb 124 _detect_val = 0;
t_tatsuoka 0:f0c12790aadb 125 for(i=0; i<MV_LENGTH; i++)
t_tatsuoka 0:f0c12790aadb 126 {
t_tatsuoka 0:f0c12790aadb 127 _detect_val += _mv_buf[i];
t_tatsuoka 0:f0c12790aadb 128 }
t_tatsuoka 0:f0c12790aadb 129 _detect_val = _detect_val / MV_LENGTH;
t_tatsuoka 0:f0c12790aadb 130
t_tatsuoka 0:f0c12790aadb 131 /* Calculate exponential decline for threshold line */
t_tatsuoka 0:f0c12790aadb 132 _threshold_val = (int32_t)((double)_prev_th_val * TH_COEF);
t_tatsuoka 0:f0c12790aadb 133
t_tatsuoka 0:f0c12790aadb 134 if(_detect_val >= _threshold_val) {
t_tatsuoka 0:f0c12790aadb 135 /* If exceeding threshold */
t_tatsuoka 0:f0c12790aadb 136 if(_prev_dt_val < _prev_th_val){
t_tatsuoka 0:f0c12790aadb 137 /* If previous value is under threshold and over ignore value */
t_tatsuoka 0:f0c12790aadb 138 if((_detect_val > PEAK_MIN) && (_pr_counter >= PR_INT_MIN)) {
t_tatsuoka 0:f0c12790aadb 139 /* Detecting peak!!! */
t_tatsuoka 0:f0c12790aadb 140 retVal = true;
t_tatsuoka 0:f0c12790aadb 141 }
t_tatsuoka 0:f0c12790aadb 142 }
t_tatsuoka 0:f0c12790aadb 143 /* Previous threshold value is set to input value */
t_tatsuoka 0:f0c12790aadb 144 _prev_th_val = _detect_val;
t_tatsuoka 0:f0c12790aadb 145 } else {
t_tatsuoka 0:f0c12790aadb 146 /* Previous threshold value is set to decline value */
t_tatsuoka 0:f0c12790aadb 147 _prev_th_val = _threshold_val;
t_tatsuoka 0:f0c12790aadb 148 }
t_tatsuoka 0:f0c12790aadb 149 /* Update previous input value */
t_tatsuoka 0:f0c12790aadb 150 _prev_dt_val = _detect_val;
t_tatsuoka 0:f0c12790aadb 151
t_tatsuoka 0:f0c12790aadb 152 /* Increment pulse rate counter */
t_tatsuoka 0:f0c12790aadb 153 _pr_counter++;
t_tatsuoka 0:f0c12790aadb 154
t_tatsuoka 0:f0c12790aadb 155 return retVal;
t_tatsuoka 0:f0c12790aadb 156 }
t_tatsuoka 0:f0c12790aadb 157
t_tatsuoka 0:f0c12790aadb 158 /** Calculate pulse rate
t_tatsuoka 0:f0c12790aadb 159 */
t_tatsuoka 0:f0c12790aadb 160 void PulseRate::calc_pr() {
t_tatsuoka 0:f0c12790aadb 161 int i;
t_tatsuoka 0:f0c12790aadb 162
t_tatsuoka 0:f0c12790aadb 163 /* If pulse rate counter is within maximum value */
t_tatsuoka 0:f0c12790aadb 164 if(_pr_counter <= PR_INT_MAX) {
t_tatsuoka 0:f0c12790aadb 165 /* Calculate moving averaging */
t_tatsuoka 0:f0c12790aadb 166 _pr_buf[_pr_idx] = _pr_counter;
t_tatsuoka 0:f0c12790aadb 167 _pr_idx = (_pr_idx + 1) % PR_LENGTH;
t_tatsuoka 0:f0c12790aadb 168 _pr = 0;
t_tatsuoka 0:f0c12790aadb 169 for(i=0; i<PR_LENGTH; i++)
t_tatsuoka 0:f0c12790aadb 170 {
t_tatsuoka 0:f0c12790aadb 171 _pr += _pr_buf[i];
t_tatsuoka 0:f0c12790aadb 172 }
t_tatsuoka 0:f0c12790aadb 173 /* Set pulse rate value */
t_tatsuoka 0:f0c12790aadb 174 _pr = PR_1MIN_SPL * PR_LENGTH / _pr;
t_tatsuoka 0:f0c12790aadb 175 } else {
t_tatsuoka 0:f0c12790aadb 176 /* Pulse rate is set to invalid value */
t_tatsuoka 0:f0c12790aadb 177 _pr = 0;
t_tatsuoka 0:f0c12790aadb 178 }
t_tatsuoka 0:f0c12790aadb 179 _pr_counter = 0;
t_tatsuoka 0:f0c12790aadb 180
t_tatsuoka 0:f0c12790aadb 181 /* Set pulse rate flag */
t_tatsuoka 0:f0c12790aadb 182 _pr_flag = true;
t_tatsuoka 0:f0c12790aadb 183 }