トランジスタ技術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)
Revision:
0:f0c12790aadb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PulseRate.cpp	Thu Sep 11 15:02:45 2014 +0000
@@ -0,0 +1,183 @@
+/**
+ *  @file       PulseRate.cpp
+ *  @brief      Calculate pulse waveform and pulse rate 
+ *  @date       2014.08.08
+ *  @version    1.0.0
+ */
+ #include "PulseRate.h"
+
+/** Constructor
+ *  @param      sensor      Pin for A/D converter
+ *  @param      sync_led    Pin for synchronous LED
+ *  @param      beep        Pin for piezo sounder
+ */
+ PulseRate::PulseRate(PinName sensor, PinName sync_led, PinName beep) :
+ _sensor(sensor), _sync_led(sync_led), _beep(beep) {
+      
+    _sync_led = LED_OFF;
+    _val = 0;
+    _wave_flag = false;
+    _pr_flag = false;
+    _sampling_num = 0;
+    _mv_idx = 0;
+    _beep.period(1.0/BEEP_FREQ);
+}
+
+/** Start interval timer
+ */
+void PulseRate::start_sampling() {
+    _sampling.attach(this, &PulseRate::interval_timer, SAMPLING_RATE);
+}
+
+/** Get waveform data
+ *  @param      &num        Sampling number
+ *  @param      &wave_val   Waveform value
+ *  @retval     true        Ready for data
+ *  @retval     false       Not ready
+ */ 
+bool PulseRate::get_wave(uint32_t &num, int32_t &wave_val) {    
+    if(_wave_flag) {
+        _wave_flag = false;
+        num = _sampling_num;
+        wave_val = _val;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/** Gat pulse rate
+ *  @param      &pr         Pulse rate
+ *  @retval     true        Ready for data
+ *  @retval     false       Not ready
+ */
+ bool PulseRate::get_pr_val(uint32_t &pr) {
+    if(_pr_flag) {
+        _pr_flag = false;
+        pr = _pr;
+        return true; 
+    } else {
+        return false;
+    }
+}
+
+/** Interval timer  
+ */
+void PulseRate::interval_timer() {
+    
+    /* Pulse waveform */
+    _val = ((int32_t)(_sensor.read_u16()) - AD_OFFSET); /* Get AD value */
+    _val = hpf(_val);                                   /* High pass filter (Comment out if not necessary) */
+    _sampling_num = (_sampling_num + 1) % SPL_NUM;      /* Update sampling number */
+    _wave_flag = true;                                  /* Set ready flag for pulse waveform */
+    
+    /* Pulse rate */
+    if(detect_peak(_val)) {     /* If detecting pulse */
+        calc_pr();              /* Calculate pulse rate including flag set */                              
+    }
+    
+    /* Control LED and Beep */
+    if(_pr_flag) {
+        _sync_led = LED_ON;
+        _beep.write(BEEP_LOUD);        
+    } else {
+        _sync_led = LED_OFF;
+        _beep.write(0); 
+    }
+}
+
+/** Fixed point high pass filter
+ *  @param      val         Input value
+ *  @return                 Output value
+ *
+ *  A/D value of mbed is Q6 format.
+ *  Please shift in advance if necessary.
+ */
+int32_t PulseRate::hpf(int32_t val) {
+    int32_t reg, ret_val;
+    int64_t temp_val;
+    
+    temp_val = (int64_t)COEF_AH * (int64_t)_reg_hpf;
+    reg = val + (int32_t)(temp_val >> 30);
+    ret_val = reg - _reg_hpf;
+    temp_val = (int64_t)COEF_BH * (int64_t)ret_val;
+    ret_val = (int32_t)(temp_val >> 30);
+    _reg_hpf = reg;
+    return ret_val;
+}
+
+/** Detect pulse peak
+ *  @param      &val        Waveform data value
+ *  @retval     true        Detected pulse peak
+ *  @retval     false       No detection
+ */
+bool PulseRate::detect_peak(int32_t val) {
+    int i;
+    bool retVal = false;
+
+    /* Calculate differential of input value */ 
+    _mv_buf[_mv_idx] = val - _prev_val;
+    _prev_val = val;
+    _mv_idx = (_mv_idx + 1) % MV_LENGTH;
+
+    /* Calculate moving averaging */ 
+    _detect_val = 0;
+    for(i=0; i<MV_LENGTH; i++)
+    {
+        _detect_val += _mv_buf[i];
+    }
+    _detect_val = _detect_val / MV_LENGTH;
+    
+    /* Calculate exponential decline for threshold line */
+    _threshold_val = (int32_t)((double)_prev_th_val * TH_COEF);
+    
+    if(_detect_val >= _threshold_val) {
+        /* If exceeding threshold */
+        if(_prev_dt_val < _prev_th_val){
+            /* If previous value is under threshold and over ignore value */
+            if((_detect_val > PEAK_MIN) && (_pr_counter >= PR_INT_MIN)) {
+                /* Detecting peak!!! */
+                retVal = true;
+            }
+        }
+        /* Previous threshold value is set to input value */
+        _prev_th_val = _detect_val;
+    } else {
+        /* Previous threshold value is set to decline value */
+        _prev_th_val = _threshold_val;
+    }
+    /* Update previous input value */
+    _prev_dt_val = _detect_val;
+    
+    /* Increment pulse rate counter */
+    _pr_counter++;
+    
+    return retVal;
+}
+
+/** Calculate pulse rate
+ */
+void PulseRate::calc_pr() {
+    int i;
+
+    /* If pulse rate counter is within maximum value */
+    if(_pr_counter <= PR_INT_MAX) {
+        /* Calculate moving averaging */ 
+        _pr_buf[_pr_idx] = _pr_counter;
+        _pr_idx = (_pr_idx + 1) % PR_LENGTH;
+        _pr = 0;
+        for(i=0; i<PR_LENGTH; i++)
+        {
+            _pr += _pr_buf[i];
+        }
+        /* Set pulse rate value */
+        _pr =  PR_1MIN_SPL * PR_LENGTH / _pr;
+    } else {
+        /* Pulse rate is set to invalid value */
+        _pr = 0;
+    }
+    _pr_counter = 0;
+
+    /* Set pulse rate flag */
+    _pr_flag = true;
+}