19 changed files with 27 additions and 1296 deletions
-
43app/src/board/ads129x/ads129x.c
-
36app/src/service/ble_cmd_processer/ble_cmd_process_service.c
-
5app/src/service/heart_wave_sample_service/heart_wave_sample_service.h
-
38bak/FIR.c
-
14bak/FIR.h
-
128bak/HC_Chen_detect.c
-
53bak/HC_Chen_detect.h
-
373bak/Pan_Tompkins_detect.c
-
21bak/Pan_Tompkins_detect.h
-
30bak/QRS.h
-
93bak/So_Chen_detect.c
-
42bak/So_Chen_detect.h
-
100bak/adaptive_algorithm.c
-
41bak/adaptive_algorithm.h
-
231bak/qrs_time_domain_zh.c
-
19bak/qrs_time_domain_zh.h
-
29bak/zapp_timer.c
-
17bak/zapp_timer.h
-
2ify_hrs_protocol
@ -1,38 +0,0 @@ |
|||||
#include "FIR.h" |
|
||||
/*360hz 0.51Hz~8.9Hz 20190925*/ |
|
||||
#define taps 32 |
|
||||
static const float coefficients[taps] = {0.012177,0.01599,0.019905,0.02387,0.027827,0.031719,0.035487,0.039075,0.042426,0.045488,0.048212,0.050553,0.052475,0.053944,0.054937,0.055438,0.055438,0.054937,0.053944,0.052475,0.050553,0.048212,0.045488,0.042426,0.039075,0.035487,0.031719,0.027827,0.02387,0.019905,0.01599,0.012177}; |
|
||||
|
|
||||
static float buffer[taps]; |
|
||||
unsigned offset; |
|
||||
|
|
||||
float FIR_filter(float input) { |
|
||||
const float *coeff = coefficients; |
|
||||
const float *coeff_end = coefficients + taps; |
|
||||
|
|
||||
float *buf_val = buffer + offset; |
|
||||
|
|
||||
*buf_val = input; |
|
||||
float output_ = 0; |
|
||||
|
|
||||
while (buf_val >= buffer) { |
|
||||
output_ += *buf_val-- * *coeff++; |
|
||||
} |
|
||||
|
|
||||
buf_val = buffer + taps - 1; |
|
||||
|
|
||||
while (coeff < coeff_end) { |
|
||||
output_ += *buf_val-- * *coeff++; |
|
||||
} |
|
||||
|
|
||||
if (++offset >= taps) { |
|
||||
offset = 0; |
|
||||
} |
|
||||
|
|
||||
return output_; |
|
||||
} |
|
||||
|
|
||||
void FIR_reset_buffer() { |
|
||||
memset(buffer, 0, sizeof(float) * taps); |
|
||||
offset = 0; |
|
||||
} |
|
@ -1,14 +0,0 @@ |
|||||
#ifndef __FIR_H__ |
|
||||
#define __FIR_H__ |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <string.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <stdint.h> |
|
||||
#include <math.h> |
|
||||
|
|
||||
|
|
||||
extern float FIR_filter(float); |
|
||||
extern void FIR_reset_buffer(); |
|
||||
|
|
||||
#endif |
|
@ -1,128 +0,0 @@ |
|||||
#include "HC_Chen_detect.h" |
|
||||
|
|
||||
bool HC_Chen_detect(float signal) |
|
||||
{ |
|
||||
ecg_buff[ecg_buff_WR_idx++] = signal; |
|
||||
sample = ecg_buff_WR_idx; |
|
||||
ecg_buff_WR_idx %= (M+1); |
|
||||
|
|
||||
/* High pass filtering */ |
|
||||
if(number_iter < M){ |
|
||||
// first fill buffer with enough points for HP filter |
|
||||
hp_sum += ecg_buff[ecg_buff_RD_idx]; |
|
||||
hp_buff[hp_buff_WR_idx] = 0; |
|
||||
} |
|
||||
else{ |
|
||||
hp_sum += ecg_buff[ecg_buff_RD_idx]; |
|
||||
|
|
||||
int tmp = ecg_buff_RD_idx - M; |
|
||||
if(tmp < 0){ |
|
||||
tmp += M + 1; |
|
||||
} |
|
||||
|
|
||||
hp_sum -= ecg_buff[tmp]; |
|
||||
|
|
||||
float y1 = 0; |
|
||||
float y2 = 0; |
|
||||
|
|
||||
tmp = (ecg_buff_RD_idx - ((M+1)/2)); |
|
||||
if(tmp < 0){ |
|
||||
tmp += M + 1; |
|
||||
} |
|
||||
|
|
||||
y2 = ecg_buff[tmp]; |
|
||||
|
|
||||
y1 = HP_CONSTANT * hp_sum; |
|
||||
|
|
||||
hp_buff[hp_buff_WR_idx] = y2 - y1; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
// done reading ECG buffer, increment position |
|
||||
ecg_buff_RD_idx++; |
|
||||
ecg_buff_RD_idx %= (M+1); |
|
||||
|
|
||||
// done writing to HP buffer, increment position |
|
||||
hp_buff_WR_idx++; |
|
||||
hp_buff_WR_idx %= (N+1); |
|
||||
|
|
||||
/* Low pass filtering */ |
|
||||
|
|
||||
// shift in new sample from high pass filter |
|
||||
lp_sum += hp_buff[hp_buff_RD_idx] * hp_buff[hp_buff_RD_idx]; |
|
||||
|
|
||||
if(number_iter < N){ |
|
||||
// first fill buffer with enough points for LP filter |
|
||||
next_eval_pt = 0; |
|
||||
} |
|
||||
else{ |
|
||||
// shift out oldest data point |
|
||||
int tmp = hp_buff_RD_idx - N; |
|
||||
if(tmp < 0){ |
|
||||
tmp += N+1; |
|
||||
} |
|
||||
|
|
||||
lp_sum -= hp_buff[tmp] * hp_buff[tmp]; |
|
||||
|
|
||||
next_eval_pt = lp_sum; |
|
||||
} |
|
||||
|
|
||||
// done reading HP buffer, increment position |
|
||||
hp_buff_RD_idx++; |
|
||||
hp_buff_RD_idx %= (N+1); |
|
||||
|
|
||||
/* Adapative thresholding beat detection */ |
|
||||
// set initial threshold |
|
||||
if(number_iter < window_size) { |
|
||||
if(next_eval_pt > treshold) { |
|
||||
treshold = next_eval_pt; |
|
||||
} |
|
||||
++number_iter; |
|
||||
} |
|
||||
|
|
||||
// check if detection hold off period has passed |
|
||||
if(triggered){ |
|
||||
trig_time++; |
|
||||
|
|
||||
if(trig_time >= DELAY_TIME){ |
|
||||
triggered = false; |
|
||||
trig_time = 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// find if we have a new max |
|
||||
if(next_eval_pt > win_max) win_max = next_eval_pt; |
|
||||
|
|
||||
// find if we are above adaptive threshold |
|
||||
if(next_eval_pt > treshold && !triggered) { |
|
||||
//result.push_back(true); |
|
||||
|
|
||||
last_qrs_point = sample; |
|
||||
|
|
||||
triggered = true; |
|
||||
return true; |
|
||||
} |
|
||||
else { |
|
||||
//result.push_back(false); |
|
||||
} |
|
||||
|
|
||||
// adjust adaptive threshold using max of signal found |
|
||||
// in previous window |
|
||||
if(win_idx++ >= window_size){ |
|
||||
// weighting factor for determining the contribution of |
|
||||
// the current peak value to the threshold adjustment |
|
||||
float gamma = (0.2f+0.15f)/2.0f; // 0.15~0.2 |
|
||||
|
|
||||
// forgetting factor - |
|
||||
// rate at which we forget old observations |
|
||||
float alpha = 0.01f + ( ((float) rand() / (float) RAND_MAX) * ((0.1f - 0.01f))); // 0~1 |
|
||||
//float alpha = 1.0f*exp(-0.00005f*(sample - last_qrs_point)); |
|
||||
|
|
||||
treshold = alpha * gamma * win_max + (1.0f - alpha) * treshold; |
|
||||
|
|
||||
// reset current window ind |
|
||||
win_idx = 0; |
|
||||
win_max = -10000000; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
@ -1,53 +0,0 @@ |
|||||
#ifndef __HC_CHEN__ |
|
||||
#define __HC_CHEN__ |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <math.h> |
|
||||
#include <stdint.h> |
|
||||
|
|
||||
|
|
||||
#include "QRS.h" |
|
||||
|
|
||||
|
|
||||
#define M 9 |
|
||||
#define N 54//SAMPLING_RATE * 0.15f |
|
||||
static const uint32_t window_size = SAMPLING_RATE; |
|
||||
static const float HP_CONSTANT = ((float) 1.0f / (float) M); |
|
||||
// circular buffer for input ecg signal |
|
||||
// we need to keep a history of M + 1 samples for HP filter |
|
||||
static float ecg_buff[M + 1] = {0}; |
|
||||
static int ecg_buff_WR_idx = 0; |
|
||||
static int ecg_buff_RD_idx = 0; |
|
||||
|
|
||||
// circular buffer for input ecg signal |
|
||||
// we need to keep a history of N+1 samples for LP filter |
|
||||
static float hp_buff[N + 1] = {0}; |
|
||||
static int hp_buff_WR_idx = 0; |
|
||||
static int hp_buff_RD_idx = 0; |
|
||||
|
|
||||
// LP filter outputs a single point for every input point |
|
||||
// This goes straight to adaptive filtering for eval |
|
||||
static float next_eval_pt = 0; |
|
||||
|
|
||||
// running sums for HP and LP filters, values shifted in FILO |
|
||||
static float hp_sum = 0; |
|
||||
static float lp_sum = 0; |
|
||||
|
|
||||
// parameters for adaptive thresholding |
|
||||
static float treshold = 0; |
|
||||
static bool triggered = false; |
|
||||
static int trig_time = 0; |
|
||||
static float win_max = 0; |
|
||||
static int win_idx = 0; |
|
||||
static int number_iter = 0; |
|
||||
|
|
||||
static int sample = 0; |
|
||||
static int last_qrs_point = 0; |
|
||||
|
|
||||
static const int DELAY_TIME = 180;//window_size * 0.5f; |
|
||||
|
|
||||
extern bool HC_Chen_detect(float); |
|
||||
|
|
||||
#endif |
|
@ -1,373 +0,0 @@ |
|||||
#include "Pan_Tompkins_detect.h" |
|
||||
|
|
||||
/* y(nT) = 1.875y(nT – T) – 0.9219y(nT – 2T) + x (nT) – x(nT – 2T) */ |
|
||||
int TwoPoleRecursive(int data) |
|
||||
{ |
|
||||
static int xnt, xm1, xm2, ynt, ym1, ym2 = 0; |
|
||||
|
|
||||
xnt = data; |
|
||||
ynt = (ym1 + (ym1 >> 1) + (ym1 >> 2) + (ym1 >> 3)) + // 1.875 = 1 + 1/2 + 1/4 + 1/8 |
|
||||
(((ym2 >> 1) + (ym2 >> 2) + (ym2 >> 3) + (ym2 >> 5) + (ym2 >> 6)) + xnt - xm2); // 0.916 = 1 + 1/2 + 1/4 + 1/8 + 1/32 + 1/64 |
|
||||
xm2 = xm1; |
|
||||
xm1 = xnt; |
|
||||
xm2 = ym1; |
|
||||
ym2 = ym1; |
|
||||
ym1 = ynt; |
|
||||
return ynt; |
|
||||
} |
|
||||
|
|
||||
/* y(nT) = 2y(nT – T) – y(nT – 2T) + x(nT) – 2x(nT – 6T) + x(nT – 12T) */ |
|
||||
int LowPassFilter(int data) |
|
||||
{ |
|
||||
static int y1 = 0, y2 = 0, x[26], n = 12; |
|
||||
int y0; |
|
||||
|
|
||||
x[n] = x[n + 13] = data; |
|
||||
y0 = (y1 << 1) - y2 + x[n] - (x[n + 6] << 1) + x[n + 12]; |
|
||||
y2 = y1; |
|
||||
y1 = y0; |
|
||||
y0 >>= 5; |
|
||||
if(--n < 0){ |
|
||||
n = 12; |
|
||||
} |
|
||||
return y0; |
|
||||
} |
|
||||
|
|
||||
/* p(nT) = x(nT – 16T) – 32 [y(nT – T) + x(nT) – x(nT – 32T)] */ |
|
||||
int HighPassFilter(int data) |
|
||||
{ |
|
||||
static int y1 = 0, x[66], n = 32; |
|
||||
int y0; |
|
||||
|
|
||||
x[n] = x[n + 33] = data; |
|
||||
y0 = y1 + x[n] - x[n + 32]; |
|
||||
y1 = y0; |
|
||||
if(--n < 0){ |
|
||||
n = 32; |
|
||||
} |
|
||||
return (x[n + 16] - (y0 >> 5)); |
|
||||
} |
|
||||
|
|
||||
/* y = 1/8 (2x( nT) + x( nT - T) - x( nT - 3T) - 2x( nT - 4T)) */ |
|
||||
int Derivative(int data) |
|
||||
{ |
|
||||
int y; |
|
||||
static int x_derv[4]; |
|
||||
|
|
||||
y = (data << 1) + x_derv[3] - x_derv[1] - ( x_derv[0] << 1); |
|
||||
y >>= 3; |
|
||||
for(int i = 0; i < 3; ++i){ |
|
||||
x_derv[i] = x_derv[i + 1]; |
|
||||
} |
|
||||
x_derv[3] = data; |
|
||||
return y; |
|
||||
} |
|
||||
|
|
||||
int Squar(int data) |
|
||||
{ |
|
||||
return (data * data); |
|
||||
} |
|
||||
|
|
||||
/* y(nT) = 1/N [x(nT – (N – 1)T) + x(nT – (N – 2)T) +...+ x(nT)] */ |
|
||||
int MovingWindowIntegral(int data) |
|
||||
{ |
|
||||
//static const int WINDOW_SIZE = SAMPLING_RATE * 0.2; |
|
||||
#define WINDOW_SIZE 72 |
|
||||
|
|
||||
static int x[WINDOW_SIZE], ptr = 0; |
|
||||
static long sum = 0; |
|
||||
long ly; |
|
||||
int y; |
|
||||
|
|
||||
if(++ptr == WINDOW_SIZE){ |
|
||||
ptr = 0; |
|
||||
} |
|
||||
sum -= x[ptr]; |
|
||||
sum += data; |
|
||||
x[ptr] = data; |
|
||||
ly = sum >> 5; |
|
||||
uint32_t MAX_INTEGRAL = 4096;//32400; |
|
||||
if(ly > MAX_INTEGRAL){ |
|
||||
y = MAX_INTEGRAL; |
|
||||
} |
|
||||
else{ |
|
||||
y = (int)ly; |
|
||||
} |
|
||||
return (y); |
|
||||
} |
|
||||
|
|
||||
SignalPoint ThresholdCalculate(int sample,float value,int bandpass,int square,int integral) |
|
||||
{ |
|
||||
//static const int QRS_TIME = SAMPLING_RATE * 0.1; |
|
||||
//static const int SEARCH_BACK_TIME = SAMPLING_RATE * 1.66f; |
|
||||
#define QRS_TIME 36 |
|
||||
#define SEARCH_BACK_TIME 598 |
|
||||
|
|
||||
static int bandpass_buffer[SEARCH_BACK_TIME],integral_buffer[SEARCH_BACK_TIME]; |
|
||||
static SignalPoint peak_buffer[SEARCH_BACK_TIME]; |
|
||||
static int square_buffer[QRS_TIME]; |
|
||||
static long unsigned last_qrs = 0, last_slope = 0, current_slope = 0; |
|
||||
static int peak_i = 0, peak_f = 0, threshold_i1 = 0, threshold_i2 = 0, threshold_f1 = 0, threshold_f2 = 0, spk_i = 0, spk_f = 0, npk_i = 0, npk_f = 0; |
|
||||
static bool qrs, regular = true, prev_regular; |
|
||||
static int rr1[8]={0}, rr2[8]={0}, rravg1, rravg2, rrlow = 0, rrhigh = 0, rrmiss = 0; |
|
||||
|
|
||||
SignalPoint result; |
|
||||
result.index = -1; |
|
||||
|
|
||||
peak_buffer[sample%SEARCH_BACK_TIME].index = sample; |
|
||||
peak_buffer[sample%SEARCH_BACK_TIME].value = value; |
|
||||
bandpass_buffer[sample%SEARCH_BACK_TIME] = bandpass; |
|
||||
integral_buffer[sample%SEARCH_BACK_TIME] = integral; |
|
||||
square_buffer[sample%QRS_TIME] = square; |
|
||||
|
|
||||
// If the current signal is above one of the thresholds (integral or filtered signal), it's a peak candidate. |
|
||||
if(integral >= threshold_i1 || bandpass >= threshold_f1){ |
|
||||
peak_i = integral; |
|
||||
peak_f = bandpass; |
|
||||
} |
|
||||
|
|
||||
// If both the integral and the signal are above their thresholds, they're probably signal peaks. |
|
||||
if((integral >= threshold_i1) && (bandpass >= threshold_f1)){ |
|
||||
// There's a 200ms latency. If the new peak respects this condition, we can keep testing. |
|
||||
if(sample > last_qrs + SAMPLING_RATE*0.2f){ |
|
||||
//if(sample > last_qrs + (SAMPLING_RATE*0.2f)){ |
|
||||
// If it respects the 200ms latency, but it doesn't respect the 360ms latency, we check the slope. |
|
||||
if(sample <= last_qrs + (long unsigned int)(0.36*SAMPLING_RATE)){ |
|
||||
// The squared slope is "M" shaped. So we have to check nearby samples to make sure we're really looking |
|
||||
// at its peak value, rather than a low one. |
|
||||
int current = sample; |
|
||||
current_slope = 0; |
|
||||
for(int j = current - QRS_TIME; j <= current; ++j){ |
|
||||
if(square_buffer[j%QRS_TIME] > current_slope){ |
|
||||
current_slope = square_buffer[j%QRS_TIME]; |
|
||||
} |
|
||||
} |
|
||||
//current_slope = square; |
|
||||
|
|
||||
if(current_slope <= (int)(last_slope/2)){ |
|
||||
qrs = false; |
|
||||
//return qrs; |
|
||||
} |
|
||||
|
|
||||
else{ |
|
||||
spk_i = 0.125*peak_i + 0.875*spk_i; |
|
||||
threshold_i1 = npk_i + 0.25*(spk_i - npk_i); |
|
||||
threshold_i2 = 0.5*threshold_i1; |
|
||||
|
|
||||
spk_f = 0.125*peak_f + 0.875*spk_f; |
|
||||
threshold_f1 = npk_f + 0.25*(spk_f - npk_f); |
|
||||
threshold_f2 = 0.5*threshold_f1; |
|
||||
|
|
||||
last_slope = current_slope; |
|
||||
qrs = true; |
|
||||
|
|
||||
result.value = value; |
|
||||
result.index = sample; |
|
||||
} |
|
||||
} |
|
||||
// If it was above both thresholds and respects both latency periods, it certainly is a R peak. |
|
||||
else{ |
|
||||
int current = sample; |
|
||||
current_slope = 0; |
|
||||
for(int j = current - QRS_TIME; j <= current; ++j){ |
|
||||
if(square_buffer[j%QRS_TIME] > current_slope){ |
|
||||
current_slope = square_buffer[j%QRS_TIME]; |
|
||||
} |
|
||||
} |
|
||||
//current_slope = square; |
|
||||
|
|
||||
spk_i = 0.125*peak_i + 0.875*spk_i; |
|
||||
threshold_i1 = npk_i + 0.25*(spk_i - npk_i); |
|
||||
threshold_i2 = 0.5*threshold_i1; |
|
||||
|
|
||||
spk_f = 0.125*peak_f + 0.875*spk_f; |
|
||||
threshold_f1 = npk_f + 0.25*(spk_f - npk_f); |
|
||||
threshold_f2 = 0.5*threshold_f1; |
|
||||
|
|
||||
last_slope = current_slope; |
|
||||
qrs = true; |
|
||||
|
|
||||
result.value = value; |
|
||||
result.index = sample; |
|
||||
} |
|
||||
} |
|
||||
// If the new peak doesn't respect the 200ms latency, it's noise. Update thresholds and move on to the next sample. |
|
||||
else{ |
|
||||
peak_i = integral; |
|
||||
npk_i = 0.125*peak_i + 0.875*npk_i; |
|
||||
threshold_i1 = npk_i + 0.25*(spk_i - npk_i); |
|
||||
threshold_i2 = 0.5*threshold_i1; |
|
||||
peak_f = bandpass; |
|
||||
npk_f = 0.125*peak_f + 0.875*npk_f; |
|
||||
threshold_f1 = npk_f + 0.25*(spk_f - npk_f); |
|
||||
threshold_f2 = 0.5*threshold_f1; |
|
||||
qrs = false; |
|
||||
/*outputSignal[current] = qrs; |
|
||||
if (sample > DELAY + BUFFSIZE) |
|
||||
output(outputSignal[0]); |
|
||||
continue;*/ |
|
||||
|
|
||||
//return qrs; |
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// If a QRS complex was detected, the RR-averages must be updated. |
|
||||
if(qrs){ |
|
||||
// Add the newest RR-interval to the buffer and get the new average. |
|
||||
rravg1 = 0; |
|
||||
for (int i = 0; i < 7; ++i){ |
|
||||
rr1[i] = rr1[i+1]; |
|
||||
rravg1 += rr1[i]; |
|
||||
} |
|
||||
rr1[7] = sample - last_qrs; |
|
||||
last_qrs = sample; |
|
||||
rravg1 += rr1[7]; |
|
||||
rravg1 *= 0.125; |
|
||||
|
|
||||
// If the newly-discovered RR-average is normal, add it to the "normal" buffer and get the new "normal" average. |
|
||||
// Update the "normal" beat parameters. |
|
||||
if ( (rr1[7] >= rrlow) && (rr1[7] <= rrhigh) ){ |
|
||||
rravg2 = 0; |
|
||||
for (int i = 0; i < 7; ++i){ |
|
||||
rr2[i] = rr2[i+1]; |
|
||||
rravg2 += rr2[i]; |
|
||||
} |
|
||||
rr2[7] = rr1[7]; |
|
||||
rravg2 += rr2[7]; |
|
||||
rravg2 *= 0.125; |
|
||||
rrlow = 0.92*rravg2; |
|
||||
rrhigh = 1.16*rravg2; |
|
||||
rrmiss = 1.66*rravg2; |
|
||||
} |
|
||||
|
|
||||
prev_regular = regular; |
|
||||
if(rravg1 == rravg2){ |
|
||||
regular = true; |
|
||||
} |
|
||||
// If the beat had been normal but turned odd, change the thresholds. |
|
||||
else{ |
|
||||
regular = false; |
|
||||
if (prev_regular){ |
|
||||
threshold_i1 /= 2; |
|
||||
threshold_f1 /= 2; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
// If no R-peak was detected, it's important to check how long it's been since the last detection. |
|
||||
else{ |
|
||||
int current = sample; |
|
||||
// If no R-peak was detected for too long, use the lighter thresholds and do a back search. |
|
||||
// However, the back search must respect the 200ms limit and the 360ms one (check the slope). |
|
||||
if((sample - last_qrs > (long unsigned int)rrmiss) && (sample > last_qrs + SAMPLING_RATE*0.2f)){ |
|
||||
//if((sample - last_qrs > (long unsigned int)rrmiss) && (sample > last_qrs + (SAMPLING_RATE*0.2f))){ |
|
||||
|
|
||||
// If over SEARCH_BACK_TIME of QRS complex |
|
||||
if((sample - last_qrs) > SEARCH_BACK_TIME){ |
|
||||
last_qrs = sample; |
|
||||
//return result; |
|
||||
} |
|
||||
|
|
||||
int qrs_last_index = 0; // Last point of QRS complex |
|
||||
|
|
||||
for(int i = current - (sample - last_qrs) + SAMPLING_RATE*0.2f; i < (long unsigned int)current; ++i){ |
|
||||
//for(int i = current - (sample - last_qrs) + (SAMPLING_RATE*0.2f); i < (long unsigned int)current; ++i){ |
|
||||
if((integral_buffer[i%SEARCH_BACK_TIME] > threshold_i2) && (bandpass_buffer[i%SEARCH_BACK_TIME] > threshold_f2)){ |
|
||||
current_slope = 0; |
|
||||
for(int j = current - QRS_TIME; j <= current; ++j){ |
|
||||
if(square_buffer[j%QRS_TIME] > current_slope){ |
|
||||
current_slope = square_buffer[j%QRS_TIME]; |
|
||||
} |
|
||||
} |
|
||||
//current_slope = square; |
|
||||
|
|
||||
if((current_slope < (int)(last_slope/2)) && (i + sample) < last_qrs + 0.36*last_qrs){ |
|
||||
qrs = false; |
|
||||
} |
|
||||
else if(i - last_qrs > 550){ |
|
||||
peak_i = integral_buffer[i%SEARCH_BACK_TIME]; |
|
||||
peak_f = bandpass_buffer[i%SEARCH_BACK_TIME]; |
|
||||
spk_i = 0.25*peak_i+ 0.75*spk_i; |
|
||||
spk_f = 0.25*peak_f + 0.75*spk_f; |
|
||||
threshold_i1 = npk_i + 0.25*(spk_i - npk_i); |
|
||||
threshold_i2 = 0.5*threshold_i1; |
|
||||
last_slope = current_slope; |
|
||||
threshold_f1 = npk_f + 0.25*(spk_f - npk_f); |
|
||||
threshold_f2 = 0.5*threshold_f1; |
|
||||
// If a signal peak was detected on the back search, the RR attributes must be updated. |
|
||||
// This is the same thing done when a peak is detected on the first try. |
|
||||
//RR Average 1 |
|
||||
rravg1 = 0; |
|
||||
for(int j = 0; j < 7; ++j){ |
|
||||
rr1[j] = rr1[j+1]; |
|
||||
rravg1 += rr1[j]; |
|
||||
} |
|
||||
rr1[7] = sample - (current - i) - last_qrs; |
|
||||
qrs = true; |
|
||||
qrs_last_index = i; |
|
||||
last_qrs = sample - (current - i); |
|
||||
rravg1 += rr1[7]; |
|
||||
rravg1 *= 0.125; |
|
||||
|
|
||||
//RR Average 2 |
|
||||
if((rr1[7] >= rrlow) && (rr1[7] <= rrhigh)){ |
|
||||
rravg2 = 0; |
|
||||
for (int i = 0; i < 7; ++i){ |
|
||||
rr2[i] = rr2[i+1]; |
|
||||
rravg2 += rr2[i]; |
|
||||
} |
|
||||
rr2[7] = rr1[7]; |
|
||||
rravg2 += rr2[7]; |
|
||||
rravg2 *= 0.125; |
|
||||
rrlow = 0.92*rravg2; |
|
||||
rrhigh = 1.16*rravg2; |
|
||||
rrmiss = 1.66*rravg2; |
|
||||
} |
|
||||
|
|
||||
prev_regular = regular; |
|
||||
if(rravg1 == rravg2){ |
|
||||
regular = true; |
|
||||
} |
|
||||
else{ |
|
||||
regular = false; |
|
||||
if(prev_regular){ |
|
||||
threshold_i1 /= 2; |
|
||||
threshold_f1 /= 2; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if(qrs){ |
|
||||
//outputSignal[current] = false; |
|
||||
//outputSignal[i] = true; |
|
||||
//if (sample > DELAY + BUFFSIZE) |
|
||||
//output(outputSignal[0]); |
|
||||
//continue; |
|
||||
|
|
||||
//return qrs; |
|
||||
return peak_buffer[qrs_last_index%SEARCH_BACK_TIME]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Definitely no signal peak was detected. |
|
||||
if(!qrs){ |
|
||||
// If some kind of peak had been detected, then it's certainly a noise peak. Thresholds must be updated accordinly. |
|
||||
if((integral >= threshold_i1) || (bandpass >= threshold_f1)){ |
|
||||
peak_i = integral; |
|
||||
npk_i = 0.125*peak_i + 0.875*npk_i; |
|
||||
threshold_i1 = npk_i + 0.25*(spk_i - npk_i); |
|
||||
threshold_i2 = 0.5*threshold_i1; |
|
||||
peak_f = bandpass; |
|
||||
npk_f = 0.125*peak_f + 0.875*npk_f; |
|
||||
threshold_f1 = npk_f + 0.25*(spk_f - npk_f); |
|
||||
threshold_f2 = 0.5*threshold_f1; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
@ -1,21 +0,0 @@ |
|||||
#ifndef __PAN_TOMPKINS__ |
|
||||
#define __PAN_TOMPKINS__ |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <string.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <math.h> |
|
||||
|
|
||||
#include "QRS.h" |
|
||||
extern int TwoPoleRecursive(int); |
|
||||
|
|
||||
extern int LowPassFilter(int); |
|
||||
extern int HighPassFilter(int); |
|
||||
|
|
||||
extern int Derivative(int); |
|
||||
extern int Squar(int); |
|
||||
extern int MovingWindowIntegral(int); |
|
||||
|
|
||||
extern SignalPoint ThresholdCalculate(int,float,int,int,int); |
|
||||
#endif |
|
@ -1,30 +0,0 @@ |
|||||
#pragma once |
|
||||
#include <stdint.h> |
|
||||
//const uint32_t SAMPLING_RATE = 1000; |
|
||||
#define SAMPLING_RATE 360 |
|
||||
|
|
||||
typedef struct |
|
||||
{ |
|
||||
float value; |
|
||||
int32_t index; |
|
||||
}SignalPoint; |
|
||||
|
|
||||
enum |
|
||||
{ |
|
||||
NOTQRS, /* not-QRS (not a getann/putann code) */ |
|
||||
NORMAL, /* normal beat */ |
|
||||
LBBB, /* left bundle branch block beat */ |
|
||||
RBBB, /* right bundle branch block beat */ |
|
||||
ABERR, /* aberrated atrial premature beat */ |
|
||||
PVC, /* premature ventricular contraction */ |
|
||||
FUSION, /* fusion of ventricular and normal beat */ |
|
||||
NPC, /* nodal (junctional) premature beat */ |
|
||||
APC, /* atrial premature contraction */ |
|
||||
SVPB, /* premature or ectopic supraventricular beat */ |
|
||||
VESC, /* ventricular escape beat */ |
|
||||
NESC, /* nodal (junctional) escape beat */ |
|
||||
PACE, /* paced beat */ |
|
||||
UNKNOWN, /* unclassifiable beat */ |
|
||||
NOISE, /* signal quality change */ |
|
||||
ARFCT /* isolated QRS-like artifact */ |
|
||||
}; |
|
@ -1,93 +0,0 @@ |
|||||
#include "So_Chen_detect.h" |
|
||||
|
|
||||
SignalPoint So_Chen_detect(SignalPoint signal,int initial_point,float threshold_parameter,float filter_parameter) |
|
||||
{ |
|
||||
/* init slop window pool, size = 5 */ |
|
||||
if(signal_window_count < signal_window_size){ |
|
||||
signal_window[signal_window_count%signal_window_size] = signal; |
|
||||
++signal_window_count; |
|
||||
SignalPoint value; |
|
||||
value.index = -1; |
|
||||
return value; |
|
||||
} |
|
||||
else{ |
|
||||
signal_window[signal_window_count%signal_window_size] = signal; |
|
||||
++signal_window_count; |
|
||||
SignalPoint value; |
|
||||
} |
|
||||
|
|
||||
/* calculate slop */ |
|
||||
uint32_t idx_for_slop = signal_window_count-2; |
|
||||
slop.value = ( (-2.0f * signal_window[(idx_for_slop-2)%signal_window_size].value) - signal_window[(idx_for_slop-1)%signal_window_size].value + signal_window[(idx_for_slop+1)%signal_window_size].value + (2.0f * signal_window[(idx_for_slop+2)%signal_window_size].value) ); |
|
||||
slop.index = signal_window[idx_for_slop%signal_window_size].index; |
|
||||
|
|
||||
/* init maxi */ |
|
||||
if(!so_chen_init_flag){ |
|
||||
if(!maxi_init){ |
|
||||
max.value = 0; |
|
||||
max.index = -1; |
|
||||
maxi = slop.value; |
|
||||
maxi_init = true; |
|
||||
} |
|
||||
++init_count; |
|
||||
if(init_count > initial_point){ |
|
||||
so_chen_init_flag = true; |
|
||||
/* calculate slop threshold */ |
|
||||
slop_threshold = threshold_parameter / 16.0f * maxi; |
|
||||
} |
|
||||
if(slop.value > maxi){ |
|
||||
maxi = slop.value; |
|
||||
} |
|
||||
SignalPoint value; |
|
||||
value.index = -1; |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
/* detect QRS complex on set */ |
|
||||
if(qrs_on_set_flag && (signal_window_count - last_point > enhanced_point)){ |
|
||||
if(!max_init){ |
|
||||
max = signal_window[(idx_for_slop)%signal_window_size]; |
|
||||
max_init = true; |
|
||||
} |
|
||||
if(signal_window[(idx_for_slop)%signal_window_size].value > max.value){ |
|
||||
max = signal_window[(idx_for_slop)%signal_window_size]; |
|
||||
max_slop = slop; |
|
||||
} |
|
||||
else if(signal_window[(idx_for_slop)%signal_window_size].value < max.value){ |
|
||||
last_point = signal_window_count; |
|
||||
qrs_on_set_flag = false; |
|
||||
max_init = false; |
|
||||
maxi = ((abs(max.value - qrs_onset_point.value) - maxi) / filter_parameter) + maxi; |
|
||||
slop_threshold = threshold_parameter / 16.0f * maxi; |
|
||||
last_maxi = maxi; |
|
||||
return max; |
|
||||
} |
|
||||
} |
|
||||
else{ |
|
||||
if(slop.value > slop_threshold){ |
|
||||
++qrs_on_set_count; |
|
||||
} |
|
||||
else if(qrs_on_set_count){ |
|
||||
qrs_on_set_count = 0; |
|
||||
} |
|
||||
|
|
||||
if(qrs_on_set_count >= 2){ // is QRS complex on set |
|
||||
qrs_on_set_flag = true; |
|
||||
qrs_on_set_count = 0; |
|
||||
qrs_onset_idx = idx_for_slop; |
|
||||
qrs_onset_point = signal; |
|
||||
} |
|
||||
else if((signal_window_count - last_point > enhanced_point * 2) && (slop_threshold > 0)){ //decay threshold |
|
||||
|
|
||||
slop_threshold -= slop.value; |
|
||||
|
|
||||
if((signal_window_count - last_point > SAMPLING_RATE * 3)){ //threshold oscillating |
|
||||
slop_threshold -= ((signal_window_count - last_point) >> (int)threshold_parameter); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
SignalPoint value; |
|
||||
value.index = -1; |
|
||||
return value; |
|
||||
} |
|
@ -1,42 +0,0 @@ |
|||||
#ifndef __SO_AND_CHEN__ |
|
||||
#define __SO_AND_CHEN__ |
|
||||
|
|
||||
#include <stdlib.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <stdio.h> |
|
||||
#include <stdint.h> |
|
||||
#include <math.h> |
|
||||
|
|
||||
#include "QRS.h" |
|
||||
static const uint32_t enhanced_point = SAMPLING_RATE * 0.35f; |
|
||||
|
|
||||
#define signal_window_size 5 |
|
||||
static SignalPoint signal_window[signal_window_size]; |
|
||||
static uint32_t signal_window_count = 0; |
|
||||
|
|
||||
static SignalPoint slop; |
|
||||
|
|
||||
static bool so_chen_init_flag = false; |
|
||||
static uint32_t init_count = 0; |
|
||||
|
|
||||
static bool maxi_init = false; |
|
||||
static float maxi; |
|
||||
|
|
||||
static float slop_threshold = 0; |
|
||||
|
|
||||
static SignalPoint qrs_onset_point; |
|
||||
|
|
||||
static int qrs_on_set_count = 0; |
|
||||
static int qrs_onset_idx = 0; |
|
||||
static bool qrs_on_set_flag = false; |
|
||||
|
|
||||
static SignalPoint max; |
|
||||
static SignalPoint max_slop; |
|
||||
static bool max_init = false; |
|
||||
|
|
||||
static float last_maxi = 0; |
|
||||
static uint32_t last_point = 0; |
|
||||
|
|
||||
SignalPoint So_Chen_detect(SignalPoint,int,float,float); |
|
||||
|
|
||||
#endif |
|
@ -1,100 +0,0 @@ |
|||||
#include "adaptive_algorithm.h" |
|
||||
|
|
||||
float CalculateMean(float value) |
|
||||
{ |
|
||||
value /= 1000.0f; |
|
||||
if(mean_count < MEAN_SIZE){ |
|
||||
mean_sum += value; |
|
||||
++mean_count; |
|
||||
} |
|
||||
else{ |
|
||||
mean = mean_sum/MEAN_SIZE; |
|
||||
mean_count = 0; |
|
||||
mean_sum = 0; |
|
||||
|
|
||||
} |
|
||||
return (mean * 1000.0f); |
|
||||
} |
|
||||
|
|
||||
float CalculateRootMeanSquare(float value) |
|
||||
{ |
|
||||
value /= 1000.0f; |
|
||||
if(rms_count < RMS_SIZE){ |
|
||||
rms_sum += value * value; |
|
||||
++rms_count; |
|
||||
} |
|
||||
else{ |
|
||||
rms = sqrt(rms_sum/RMS_SIZE); |
|
||||
rms_count = 0; |
|
||||
rms_sum = 0; |
|
||||
|
|
||||
} |
|
||||
return (rms * 1000.0f); |
|
||||
} |
|
||||
|
|
||||
float CalculateCoefficientOfVariation(float value) |
|
||||
{ |
|
||||
value /= 1000.0f; |
|
||||
if(cv_count < CV_SIZE){ |
|
||||
sd += (value - mean) * (value - mean); |
|
||||
++cv_count; |
|
||||
} |
|
||||
else{ |
|
||||
sd = sqrt(sd / (CV_SIZE-1)); |
|
||||
cv = (sd / mean) * 100; |
|
||||
cv_count = 0; |
|
||||
sd = 0; |
|
||||
|
|
||||
} |
|
||||
return cv; |
|
||||
} |
|
||||
|
|
||||
void InitPeakDetect(float value,bool emi_first) |
|
||||
{ |
|
||||
if(!init_flag){ |
|
||||
current_max = value; |
|
||||
current_min = value; |
|
||||
is_detecting_emi = emi_first; |
|
||||
init_flag = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
SignalPoint PeakDetect(float value,int index,float gradient,bool *is_peak) |
|
||||
{ |
|
||||
if(value > current_max){ |
|
||||
max_point = index; |
|
||||
current_max = value; |
|
||||
} |
|
||||
if(value < current_min){ |
|
||||
min_point = index; |
|
||||
current_min = value; |
|
||||
} |
|
||||
|
|
||||
if(is_detecting_emi && value < (current_max - gradient) ){ |
|
||||
|
|
||||
is_detecting_emi = false; |
|
||||
|
|
||||
current_min = current_max; |
|
||||
min_point = max_point; |
|
||||
*is_peak = true; |
|
||||
peak.value = current_max; |
|
||||
peak.index = max_point; |
|
||||
return peak; |
|
||||
} |
|
||||
else if((!is_detecting_emi) && value > (current_min + gradient)) |
|
||||
{ |
|
||||
|
|
||||
is_detecting_emi = true; |
|
||||
|
|
||||
current_max = current_min; |
|
||||
max_point = min_point; |
|
||||
*is_peak = false; |
|
||||
peak.value = current_min; |
|
||||
peak.index = min_point; |
|
||||
return peak; |
|
||||
} |
|
||||
|
|
||||
peak.index = -1; |
|
||||
return peak; |
|
||||
} |
|
||||
|
|
@ -1,41 +0,0 @@ |
|||||
#ifndef __ALGORITHM__ |
|
||||
#define __ALGORITHM__ |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <string.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <stdint.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <math.h> |
|
||||
|
|
||||
#include "QRS.h" |
|
||||
static const uint32_t MEAN_SIZE = SAMPLING_RATE; |
|
||||
static uint32_t mean_count; |
|
||||
static float mean_sum; |
|
||||
static float mean; |
|
||||
|
|
||||
static const uint32_t RMS_SIZE = SAMPLING_RATE; |
|
||||
static uint32_t rms_count; |
|
||||
static float rms_sum; |
|
||||
static float rms; |
|
||||
|
|
||||
static const uint32_t CV_SIZE = SAMPLING_RATE; |
|
||||
static uint32_t cv_count; |
|
||||
static float sd; |
|
||||
static float cv; |
|
||||
|
|
||||
static float current_max; |
|
||||
static float current_min; |
|
||||
static int max_point; |
|
||||
static int min_point; |
|
||||
static SignalPoint peak; |
|
||||
static bool is_detecting_emi; |
|
||||
static bool init_flag = false; |
|
||||
|
|
||||
extern float CalculateMean(float); |
|
||||
extern float CalculateRootMeanSquare(float); |
|
||||
extern float CalculateCoefficientOfVariation(float); |
|
||||
extern void InitPeakDetect(float,bool); |
|
||||
extern SignalPoint PeakDetect(float,int,float,bool*); |
|
||||
|
|
||||
#endif |
|
@ -1,231 +0,0 @@ |
|||||
#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; |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
/** |
|
||||
* @file qrs_time_domain_zh.h |
|
||||
* @author zhaohe (zhaohe@domain.com) |
|
||||
* @brief |
|
||||
* @version 0.1 |
|
||||
* @date 2024-02-10 |
|
||||
* |
|
||||
* @copyright Copyright (c) 2024 |
|
||||
* |
|
||||
*/ |
|
||||
#pragma once |
|
||||
#include <stdint.h> |
|
||||
#define TABLE_SIZE 1000 |
|
||||
|
|
||||
void QRS_resetBuf(); |
|
||||
void QRS_processData(uint16_t data); |
|
||||
uint16_t QRS_getHeartRate(); |
|
||||
uint16_t QRS_getMaxValueLastVal(); |
|
||||
uint16_t QRS_getAvgValueVal(); |
|
@ -1,29 +0,0 @@ |
|||||
#include "zapp_timer.h" |
|
||||
|
|
||||
static void app_timer_timeout_handler(void* p_context) { // |
|
||||
zapp_timer_context* zcontext = (zapp_timer_context*)p_context; |
|
||||
ZASSERT(zcontext != NULL); |
|
||||
ZASSERT(zcontext->mark = 0xAABBCCDD); |
|
||||
|
|
||||
if (zcontext->timeout_handler) zcontext->timeout_handler(zcontext->usrcontext); |
|
||||
} |
|
||||
|
|
||||
ret_code_t zapp_timer_create(zapp_timer_context* context, // |
|
||||
app_timer_id_t* p_timer_id, app_timer_mode_t mode, app_timer_timeout_handler_t timeout_handler) { |
|
||||
context->timeout_handler = timeout_handler; |
|
||||
context->mark = 0xAABBCCDD; |
|
||||
ret_code_t ret = app_timer_create(p_timer_id, mode, app_timer_timeout_handler); |
|
||||
if (ret != NRF_SUCCESS) { |
|
||||
return ret; |
|
||||
} |
|
||||
(*p_timer_id)->p_context = context; |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
ret_code_t zapp_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void* p_context) { // |
|
||||
zapp_timer_context* zcontext = (zapp_timer_context*)timer_id->p_context; |
|
||||
ZASSERT(zcontext != NULL); |
|
||||
ZASSERT(zcontext->mark = 0xAABBCCDD); |
|
||||
zcontext->usrcontext = p_context; |
|
||||
return app_timer_start(timer_id, timeout_ticks, zcontext); |
|
||||
} |
|
@ -1,17 +0,0 @@ |
|||||
#pragma once |
|
||||
#include <stdbool.h> |
|
||||
#include <stdint.h> |
|
||||
|
|
||||
#include "znordic.h" |
|
||||
|
|
||||
typedef void (*app_event_listener_t)(void* p_event_data, uint16_t event_size); |
|
||||
|
|
||||
typedef struct { |
|
||||
uint32_t mark; |
|
||||
app_timer_timeout_handler_t timeout_handler; |
|
||||
void* usrcontext; |
|
||||
} zapp_timer_context; |
|
||||
|
|
||||
ret_code_t zapp_timer_create(zapp_timer_context* context, // |
|
||||
app_timer_id_t const* p_timer_id, app_timer_mode_t mode, app_timer_timeout_handler_t timeout_handler); |
|
||||
ret_code_t zapp_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void* p_context); |
|
@ -1 +1 @@ |
|||||
Subproject commit 81bdc5323d5618123e1cbe6ee590d3514ca216b0 |
|
||||
|
Subproject commit 6bceb372ed9409f8a4ac2a365b7af800f0a2353b |
Write
Preview
Loading…
Cancel
Save
Reference in new issue