You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
6.6 KiB
231 lines
6.6 KiB
#include "qrs_time_domain_zh.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#define HEART_RATE_FILTER_SIZE 10
|
|
|
|
typedef struct {
|
|
uint16_t data[HEART_RATE_FILTER_SIZE];
|
|
uint16_t data_process_buf[HEART_RATE_FILTER_SIZE];
|
|
uint32_t cnt;
|
|
uint32_t index;
|
|
} HeartRateMedianFilter_t; // 中值滤波器
|
|
|
|
typedef struct {
|
|
uint16_t data[HEART_RATE_FILTER_SIZE];
|
|
uint32_t cnt;
|
|
uint32_t index;
|
|
uint32_t sum;
|
|
} HeartRateMeanFilter_t; // 均值滤波器
|
|
|
|
HeartRateMedianFilter_t m_heart_rate_median_filter;
|
|
HeartRateMeanFilter_t m_heart_rate_mean_filter;
|
|
|
|
static void HeartRateMedianFilter_reset() {
|
|
memset(m_heart_rate_median_filter.data, 0, sizeof(m_heart_rate_median_filter.data));
|
|
m_heart_rate_median_filter.cnt = 0;
|
|
m_heart_rate_median_filter.index = 0;
|
|
}
|
|
static uint16_t HeartRateMedianFilter_process(uint16_t data) {
|
|
HeartRateMedianFilter_t* pfilter = &m_heart_rate_median_filter;
|
|
|
|
pfilter->data[pfilter->index] = data;
|
|
pfilter->index++;
|
|
pfilter->cnt++;
|
|
if (pfilter->index >=HEART_RATE_FILTER_SIZE) {
|
|
pfilter->index = 0;
|
|
}
|
|
|
|
if (pfilter->cnt <HEART_RATE_FILTER_SIZE) {
|
|
return data;
|
|
}
|
|
|
|
memcpy(pfilter->data_process_buf, pfilter->data, HEART_RATE_FILTER_SIZE * sizeof(uint16_t));
|
|
for (uint8_t i = 0; i < HEART_RATE_FILTER_SIZE; i++) {
|
|
for (uint8_t j = i + 1; j < HEART_RATE_FILTER_SIZE; j++) {
|
|
if (pfilter->data_process_buf[i] > pfilter->data_process_buf[j]) {
|
|
uint16_t temp = pfilter->data_process_buf[i];
|
|
pfilter->data_process_buf[i] = pfilter->data_process_buf[j];
|
|
pfilter->data_process_buf[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
return pfilter->data_process_buf[2];
|
|
}
|
|
static void HeartRateMeanFilter_reset() {
|
|
memset(m_heart_rate_mean_filter.data, 0, sizeof(m_heart_rate_mean_filter.data));
|
|
m_heart_rate_mean_filter.cnt = 0;
|
|
m_heart_rate_mean_filter.index = 0;
|
|
m_heart_rate_mean_filter.sum = 0;
|
|
}
|
|
static uint16_t HeartRateMeanFilter_process(uint16_t data) {
|
|
HeartRateMeanFilter_t* pfilter = &m_heart_rate_mean_filter;
|
|
|
|
pfilter->sum -= pfilter->data[pfilter->index];
|
|
pfilter->data[pfilter->index] = data;
|
|
pfilter->sum += data;
|
|
|
|
pfilter->index++;
|
|
pfilter->cnt++;
|
|
|
|
if (pfilter->index >= HEART_RATE_FILTER_SIZE) {
|
|
pfilter->index = 0;
|
|
}
|
|
|
|
if (pfilter->cnt < HEART_RATE_FILTER_SIZE) {
|
|
return data;
|
|
}
|
|
|
|
return pfilter->sum / HEART_RATE_FILTER_SIZE;
|
|
}
|
|
|
|
static uint16_t m_data[TABLE_SIZE];
|
|
static uint32_t m_ndata = 0;
|
|
static uint32_t m_dataindex = 0;
|
|
static uint32_t m_data_cnt = 0;
|
|
static uint16_t m_heartrate = 0;
|
|
|
|
static uint32_t m_datasum = 0;
|
|
static float m_avg = 0;
|
|
static uint32_t m_max_val_in_m_data;
|
|
|
|
static bool m_findpeak = false;
|
|
|
|
static uint16_t pQRS_median_filter_cache[HEART_RATE_FILTER_SIZE];
|
|
static uint16_t pQRS_median_filter_cache_index = 0;
|
|
static uint16_t pQRS_median_filter_cache_cnt = 0;
|
|
|
|
static uint32_t m_last_peak_pos = 0;
|
|
static uint32_t m_peakcnt = 0;
|
|
|
|
static uint16_t pQRS_median_filter(uint16_t indata) {
|
|
// memcpy(pQRS_median_filter_cache + 1, pQRS_median_filter_cache, 4 * sizeof(uint16_t));
|
|
pQRS_median_filter_cache[pQRS_median_filter_cache_index] = indata;
|
|
pQRS_median_filter_cache_index++;
|
|
pQRS_median_filter_cache_cnt++;
|
|
if (pQRS_median_filter_cache_index >= HEART_RATE_FILTER_SIZE) {
|
|
pQRS_median_filter_cache_index = 0;
|
|
}
|
|
|
|
if (pQRS_median_filter_cache_cnt < HEART_RATE_FILTER_SIZE) {
|
|
return indata;
|
|
}
|
|
|
|
static uint16_t process_cache[HEART_RATE_FILTER_SIZE];
|
|
memcpy(process_cache, pQRS_median_filter_cache, HEART_RATE_FILTER_SIZE * sizeof(uint16_t));
|
|
for (uint8_t i = 0; i < HEART_RATE_FILTER_SIZE; i++) {
|
|
for (uint8_t j = i + 1; j < HEART_RATE_FILTER_SIZE; j++) {
|
|
if (process_cache[i] > process_cache[j]) {
|
|
uint16_t temp = process_cache[i];
|
|
process_cache[i] = process_cache[j];
|
|
process_cache[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
return process_cache[2];
|
|
}
|
|
|
|
static uint32_t pQRS_findMaxValue() {
|
|
uint32_t max_val = 0;
|
|
for (uint32_t i = 0; i < TABLE_SIZE; i++) {
|
|
if (m_data[i] > max_val) {
|
|
max_val = m_data[i];
|
|
}
|
|
}
|
|
return max_val;
|
|
}
|
|
|
|
void QRS_resetBuf() { //
|
|
m_ndata = 0;
|
|
m_dataindex = 0;
|
|
m_heartrate = 0;
|
|
m_data_cnt = 0;
|
|
memset(m_data, 0, sizeof(m_data));
|
|
m_datasum = 0;
|
|
m_findpeak = false;
|
|
pQRS_median_filter_cache_index = 0;
|
|
pQRS_median_filter_cache_cnt = 0;
|
|
m_peakcnt = 0;
|
|
|
|
HeartRateMedianFilter_reset();
|
|
HeartRateMeanFilter_reset();
|
|
}
|
|
|
|
void QRS_processData(uint16_t _data) {
|
|
uint16_t data = pQRS_median_filter(_data);
|
|
/*******************************************************************************
|
|
* 填充BUF *
|
|
*******************************************************************************/
|
|
m_datasum -= m_data[m_dataindex];
|
|
m_data[m_dataindex] = data;
|
|
m_datasum += data;
|
|
|
|
m_data_cnt++;
|
|
|
|
if (m_dataindex < TABLE_SIZE) {
|
|
m_dataindex++;
|
|
} else {
|
|
m_dataindex = 0;
|
|
}
|
|
|
|
m_ndata++;
|
|
if (m_ndata > TABLE_SIZE) {
|
|
m_ndata = TABLE_SIZE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 求BUF的平均值和最大值 *
|
|
*******************************************************************************/
|
|
if (m_ndata == TABLE_SIZE) {
|
|
m_avg = (float)m_datasum / m_ndata;
|
|
m_max_val_in_m_data = pQRS_findMaxValue();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 寻找QRS波峰和波谷 *
|
|
*******************************************************************************/
|
|
|
|
if (!m_findpeak) {
|
|
uint16_t thresholdValue = (m_max_val_in_m_data - m_avg) * 0.666 + m_avg;
|
|
if (data > thresholdValue) {
|
|
m_findpeak = true;
|
|
m_peakcnt++;
|
|
|
|
if (m_last_peak_pos != 0) {
|
|
uint16_t diff_peak_pos = m_data_cnt - m_last_peak_pos;
|
|
if (diff_peak_pos > 0) {
|
|
//
|
|
// m_heartrate = 60 * 500 / diff_peak_pos;
|
|
|
|
uint16_t diff_peak_ms = diff_peak_pos * 2; // 500Hz
|
|
uint16_t heart_rate = 60 * 1000 / diff_peak_ms;
|
|
|
|
m_heartrate = HeartRateMeanFilter_process(HeartRateMedianFilter_process(heart_rate));
|
|
}
|
|
}
|
|
|
|
m_last_peak_pos = m_data_cnt;
|
|
}
|
|
} else {
|
|
if (data < m_avg) {
|
|
m_findpeak = false;
|
|
}
|
|
}
|
|
}
|
|
uint16_t QRS_getHeartRate() {
|
|
__disable_fiq();
|
|
uint16_t heartrate = m_heartrate;
|
|
__enable_fiq();
|
|
|
|
if (heartrate > 200) return 0;
|
|
if (heartrate < 55) return 0;
|
|
return heartrate;
|
|
}
|
|
|
|
uint16_t QRS_getMaxValueLastVal() { return m_max_val_in_m_data; }
|
|
|
|
uint16_t QRS_getAvgValueVal() { //
|
|
return m_avg;
|
|
}
|