| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef HR_PPG_HPP |
| #define HR_PPG_HPP |
|
|
| #include "edge-impulse-sdk/dsp/numpy.hpp" |
| #include "edge-impulse-sdk/dsp/ei_dsp_handle.h" |
| #include "edge-impulse-enterprise/hr/hr_ppg.hpp" |
| #include "edge-impulse-enterprise/hr/hrv.hpp" |
| #include "edge-impulse-sdk/dsp/memory.hpp" |
|
|
| |
| |
| class hrv_wrap : public ei::hrv::ibi_to_hrv { |
| public: |
| |
| void* operator new(size_t size) { |
| |
| return ei_malloc(size); |
| } |
|
|
| void operator delete(void* ptr) { |
| |
| ei_free(ptr); |
| } |
| |
|
|
| |
| using ei::hrv::ibi_to_hrv::ibi_to_hrv; |
| }; |
|
|
| class hr_class : public DspHandle { |
| public: |
| int print() override { |
| ei_printf("Last HR: %f\n", ppg._res.hr); |
| return ei::EIDSP_OK; |
| } |
|
|
| int extract( |
| ei::signal_t *signal, |
| ei::matrix_t *output_matrix, |
| void *config_ptr, |
| const float frequency, |
| ei_impulse_result_t *result) override |
| { |
| using namespace ei; |
|
|
| |
| ei_dsp_config_hr_t& config = *((ei_dsp_config_hr_t*)config_ptr); |
| size_t floats_per_inc = ppg.win_inc_samples * ppg.axes; |
| size_t hrv_inc_samples = config.hrv_update_interval_s * frequency * ppg.axes; |
| |
| assert(signal->total_length >= floats_per_inc); |
|
|
| int out_idx = 0; |
| size_t hrv_count = 0; |
| for (size_t i = 0; i <= signal->total_length - floats_per_inc; i += floats_per_inc) { |
| |
| |
| matrix_t temp(ppg.win_inc_samples, ppg.axes); |
| signal->get_data(i, floats_per_inc, temp.buffer); |
| auto hr = ppg.stream(&temp); |
| if (result) { |
| result->hr_calcs.heart_rate = hr; |
| } |
| if(!hrv || (hrv && config.include_hr)) { |
| output_matrix->buffer[out_idx++] = hr; |
| } |
| if(hrv) { |
| auto ibis = ppg.get_ibis(); |
| hrv->add_ibis(ibis); |
| hrv_count += floats_per_inc; |
| if(hrv_count >= hrv_inc_samples) { |
| fvec features = hrv->get_hrv_features(0); |
| for(size_t j = 0; j < features.size(); j++) { |
| output_matrix->buffer[out_idx++] = features[j]; |
| } |
| hrv_count = 0; |
| } |
| } |
| } |
| return EIDSP_OK; |
| } |
|
|
| hr_class(ei_dsp_config_hr_t* config, float frequency) |
| : ppg(frequency, |
| config->axes, |
| int(frequency * config->hr_win_size_s), |
| int(frequency * 2), |
| config->filter_preset, |
| config->acc_resting_std, |
| config->sensitivity, |
| true), hrv(nullptr) |
| { |
| auto table = config->named_axes; |
| for( size_t i = 0; i < config->named_axes_size; i++ ) { |
| ppg.set_offset_table(i, table[i].axis); |
| } |
| |
| if(strcmp(config->hrv_features,"none") != 0) { |
| |
| hrv = new hrv_wrap( |
| frequency, |
| config->hrv_features, |
| config->hrv_update_interval_s, |
| config->hrv_win_size_s, |
| 2); |
| } |
| } |
|
|
| ~hr_class() { |
| if(hrv) { |
| |
| delete hrv; |
| } |
| } |
|
|
| float get_last_hr() { |
| return ppg._res.hr; |
| } |
|
|
| |
| static DspHandle* create(void* config, float frequency); |
|
|
| void* operator new(size_t size) { |
| |
| return ei_malloc(size); |
| } |
|
|
| void operator delete(void* ptr) { |
| |
| ei_free(ptr); |
| } |
| |
| private: |
| ei::hr_ppg ppg; |
| hrv_wrap* hrv; |
| }; |
|
|
| DspHandle* hr_class::create(void* config_in, float frequency) { |
| auto config = reinterpret_cast<ei_dsp_config_hr_t*>(config_in); |
| return new hr_class(config, frequency); |
| }; |
|
|
| |
| |
| |
|
|
| #endif |