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

PulseRate.cpp

Committer:
t_tatsuoka
Date:
2014-09-11
Revision:
0:f0c12790aadb

File content as of revision 0:f0c12790aadb:

/**
 *  @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;
}