Measuring plethysmogram with BH1792GLC (Rohm Semiconductor) and calculating pulse rate

Dependencies:   USBDevice mbed

PulseRate.cpp

Committer:
t_tatsuoka
Date:
2018-02-05
Revision:
0:18d735a66926
Child:
1:90f70c146a26

File content as of revision 0:18d735a66926:

/**
 *  @file       PulseRate.cpp
 *  @brief      Measuring plethysmogram and calc pulse rate
 *  @date       2018.02.03
 *  @version    1.1.0
 */
#include "PulseRate.h"

#ifdef _OP_MODE_INT_AD
/** 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)
{
    init();
}
#elif defined _OP_MODE_BH1792GLC
/** Constructor
 *  @param      bh_sda      Pin for SDA on BH1792GLC
 *  @param      bh_scl      Pin for SCL on BH1792GLC
 *  @param      bh_int      Pin for INT on BH1792GLC
 *  @param      sync_led    Pin for synchronous LED
 *  @param      beep        Pin for piezo sounder
 */
PulseRate::PulseRate(PinName bh_sda, PinName bh_scl, PinName bh_int, PinName sync_led, PinName beep) :
    _bh(bh_sda, bh_scl, bh_int), _sync_led(sync_led), _beep(beep)
{
    init();
}
#endif

/** Initialize
 */
void PulseRate::init()
{
    _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(callback(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 */
#ifdef _OP_MODE_INT_AD
    _val = ((int32_t)(_sensor.read_u16()) - AD_OFFSET); /* Get AD value */
#elif defined _OP_MODE_BH1792GLC
    if(_bh.get_val(_val)) {
        //printf("Value is set 0.\r\n");
        _val = 0;
    }
    _val = -_val;
#endif

    _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;
}