トランジスタ技術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)

Files at this revision

API Documentation at this revision

Comitter:
t_tatsuoka
Date:
Thu Sep 11 15:02:45 2014 +0000
Commit message:
Name?tg_201410s6_Plethysmographs; Description?????????2014?10???6????????; Publish in :CQ Publishing

Changed in this revision

PulseRate.cpp Show annotated file Show diff for this revision Revisions of this file
PulseRate.h Show annotated file Show diff for this revision Revisions of this file
USBDevice.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r f0c12790aadb PulseRate.cpp
--- /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;
+}
diff -r 000000000000 -r f0c12790aadb PulseRate.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PulseRate.h	Thu Sep 11 15:02:45 2014 +0000
@@ -0,0 +1,87 @@
+/**
+ *  @file       PulseRate.h
+ *  @brief      Header file for PulseRate.cpp
+ *  @date       2014.08.08
+ *  @version    1.0.0
+ */
+#ifndef _INC_PulseRate
+#define _INC_PulseRate
+
+#include "mbed.h"
+
+#define LED_ON  (0)
+#define LED_OFF (1)
+
+#define BEEP_FREQ (1000.0)
+#define BEEP_LOUD (0.5)
+
+#define SAMPLING_RATE (0.01)    /* A/D sampling rate (10ms) */
+#define SPL_NUM (100)           /* Range of sampling number (0 - 99) */
+
+#define AD_OFFSET (21845)       /* Offset value for VREF (about +1.1V) */
+
+#define COEF_BH (0x3FCCB062)    /* High pass filter b0, -b1 coefficient 0.1Hz/100Hz Q30 format */
+                                /* Use (0x3F02946A) when 0.5Hz/100Hz Q30 format */
+#define COEF_AH (0x3F9960C5)    /* High pass filter -a1     coefficient 0.1Hz/200Hz Q30 format */
+                                /* Use (0x3E0528D5) when 0.5Hz/100Hz Q30 format */
+
+#define MV_LENGTH (5)           /* Number of moving averaging for pulse detection */
+#define TH_COEF (0.993)         /* Coefficient for pulse threshold (exponential decline) */
+#define PEAK_MIN (127)          /* Ignore waveform as pulse under this value */
+
+#define PR_LENGTH (5)           /* Number of average for pulse rate */
+#define PR_1MIN_SPL (6000)      /* Number of sampling for 1 minute (60*100) */
+#define PR_INT_MAX (300)        /* 20 bpm (60*100/20) */
+#define PR_INT_MIN (20)         /* 300 bpm (60*100/300) */
+
+/** Calculate pulse waveform and pulse rate
+ */
+class PulseRate {
+
+public:
+    PulseRate(PinName sensor, PinName sync_led, PinName beep);
+    void start_sampling();
+    bool get_wave(uint32_t &num, int32_t &wave_val);
+    bool get_pr_val(uint32_t &pr);
+
+private:
+    Ticker      _sampling;          /* Interval timer for data sampling */
+
+    AnalogIn    _sensor;            /* A/D converter */
+    DigitalOut  _sync_led;          /* Synchronous LED */
+    PwmOut      _beep;              /* Piezo sounder */
+
+    /* Pulse waveform */
+    int32_t     _val;               /* Pulse waveform value */
+    int32_t     _prev_val;          /* Previous value */
+    int32_t     _reg_hpf;           /* High pass filter memory value */
+    bool        _wave_flag;         /* Pulse waveform set flag */
+    
+    uint32_t    _sampling_num;      /* Sampling number */
+    
+    /* Moving averaging */
+    int32_t     _mv_buf[MV_LENGTH]; /* Circular buffer */
+    int32_t     _mv_idx;            /* Buffer index */
+    
+    /* Threshold for detecting pulse */
+    int32_t     _detect_val;        /* Detection value */
+    int32_t     _prev_dt_val;       /* Previous data */
+    
+    int32_t     _threshold_val;     /* Threshold value */
+    int32_t     _prev_th_val;       /* Previous data */
+    
+    /* Pulse rate */
+    int32_t     _pr_counter;        /* Counter for pulse rate */
+    int32_t     _pr_buf[PR_LENGTH]; /* Circular buffer */
+    int32_t     _pr_idx;            /* Buffer index */
+    
+    int32_t     _pr;                /* Pulse rate value */
+    bool        _pr_flag;           /* Pulse rate set flag */
+
+    /* Member functions */
+    void interval_timer();
+    int32_t hpf(int32_t val);
+    bool detect_peak(int32_t val);
+    void calc_pr();
+};
+#endif    /* INC_PulseRate */
diff -r 000000000000 -r f0c12790aadb USBDevice.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBDevice.lib	Thu Sep 11 15:02:45 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/USBDevice/#c09a0c9bf425
diff -r 000000000000 -r f0c12790aadb main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Sep 11 15:02:45 2014 +0000
@@ -0,0 +1,56 @@
+/**
+ *  @file       Main.cpp
+ *  @brief      Send pulse waveform and pulse rate 
+ *  @date       2014.08.08
+ *  @version    1.0.0
+ */
+#include "mbed.h"
+#include "USBSerial.h"
+#include "PulseRate.h"
+
+#define PACKET_HEADER (0xAA)
+#define PULSE_RATE_ID (0xBB)
+#define BYTE_MASK (0xFF)
+
+USBSerial serial;
+PulseRate pr(p20, LED1, p26); /* AD, LED, Beep */
+
+/** Send data packet
+ *  @param      second_val  Byte data at packet address 0x01
+ *  @param      data_val    Short data at packet address 0x02, 0x03
+ */
+bool send_packet(int second_val, int data_val)
+{
+    if(serial.writeable()) {
+        serial.putc(PACKET_HEADER);
+        serial.putc(second_val & BYTE_MASK);
+        serial.putc((data_val >> 8 ) & BYTE_MASK);
+        serial.putc(data_val & BYTE_MASK);
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+/** Main function
+ */
+int main() {
+    uint32_t num;
+    int32_t wave;
+    uint32_t rate;
+    
+    pr.start_sampling();    /* start procedure */
+
+    while(1) {
+        /* Pulse waveform */
+        if(pr.get_wave(num, wave)) {
+            send_packet(num, wave);
+        }
+        /* Pulse rate */
+        if(pr.get_pr_val(rate)) {
+            send_packet(PULSE_RATE_ID, rate);
+        }
+    }
+}
diff -r 000000000000 -r f0c12790aadb mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Thu Sep 11 15:02:45 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/6213f644d804
\ No newline at end of file