diff --git a/.vscode/settings.json b/.vscode/settings.json index 87ff0fe..82c7cc1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -177,7 +177,14 @@ "xstring": "cpp", "xtr1common": "cpp", "xtree": "cpp", - "xutility": "cpp" + "xutility": "cpp", + "hc_chen_detect.h": "c", + "fir.h": "c", + "algorithm": "c", + "optional": "c", + "so_chen_detect.h": "c", + "adaptive_algorithm.h": "c", + "pan_tompkins_detect.h": "c" }, "files.encoding": "gbk" } \ No newline at end of file diff --git a/app/app.uvoptx b/app/app.uvoptx index 1dc2f2d..9ec7873 100644 --- a/app/app.uvoptx +++ b/app/app.uvoptx @@ -655,6 +655,66 @@ 0 0 + + 1 + 25 + 1 + 0 + 0 + 0 + .\src\basic\FIR.c + FIR.c + 0 + 0 + + + 1 + 26 + 1 + 0 + 0 + 0 + .\src\basic\HC_Chen_detect.c + HC_Chen_detect.c + 0 + 0 + + + 1 + 27 + 1 + 0 + 0 + 0 + .\src\basic\So_Chen_detect.c + So_Chen_detect.c + 0 + 0 + + + 1 + 28 + 1 + 0 + 0 + 0 + .\src\basic\adaptive_algorithm.c + adaptive_algorithm.c + 0 + 0 + + + 1 + 29 + 1 + 0 + 0 + 0 + .\src\basic\Pan_Tompkins_detect.c + Pan_Tompkins_detect.c + 0 + 0 + @@ -665,7 +725,7 @@ 0 2 - 25 + 30 1 0 0 @@ -685,7 +745,7 @@ 0 3 - 26 + 31 1 0 0 @@ -697,7 +757,7 @@ 3 - 27 + 32 1 0 0 @@ -717,7 +777,7 @@ 0 4 - 28 + 33 1 0 0 @@ -737,7 +797,7 @@ 0 5 - 29 + 34 1 0 0 @@ -749,7 +809,7 @@ 5 - 30 + 35 1 0 0 @@ -761,7 +821,7 @@ 5 - 31 + 36 1 0 0 @@ -773,7 +833,7 @@ 5 - 32 + 37 1 0 0 @@ -785,7 +845,7 @@ 5 - 33 + 38 1 0 0 @@ -797,7 +857,7 @@ 5 - 34 + 39 1 0 0 @@ -809,7 +869,7 @@ 5 - 35 + 40 1 0 0 @@ -821,7 +881,7 @@ 5 - 36 + 41 1 0 0 @@ -841,7 +901,7 @@ 0 6 - 37 + 42 1 0 0 @@ -861,7 +921,7 @@ 0 7 - 38 + 43 1 0 0 @@ -873,7 +933,7 @@ 7 - 39 + 44 1 0 0 @@ -885,7 +945,7 @@ 7 - 40 + 45 1 0 0 @@ -897,7 +957,7 @@ 7 - 41 + 46 1 0 0 @@ -909,7 +969,7 @@ 7 - 42 + 47 1 0 0 @@ -921,7 +981,7 @@ 7 - 43 + 48 1 0 0 @@ -933,7 +993,7 @@ 7 - 44 + 49 1 0 0 @@ -945,7 +1005,7 @@ 7 - 45 + 50 1 0 0 @@ -957,7 +1017,7 @@ 7 - 46 + 51 1 0 0 @@ -969,7 +1029,7 @@ 7 - 47 + 52 1 0 0 @@ -981,7 +1041,7 @@ 7 - 48 + 53 1 0 0 @@ -993,7 +1053,7 @@ 7 - 49 + 54 1 0 0 @@ -1005,7 +1065,7 @@ 7 - 50 + 55 1 0 0 @@ -1017,7 +1077,7 @@ 7 - 51 + 56 1 0 0 @@ -1029,7 +1089,7 @@ 7 - 52 + 57 1 0 0 @@ -1041,7 +1101,7 @@ 7 - 53 + 58 1 0 0 @@ -1053,7 +1113,7 @@ 7 - 54 + 59 1 0 0 @@ -1065,7 +1125,7 @@ 7 - 55 + 60 1 0 0 @@ -1077,7 +1137,7 @@ 7 - 56 + 61 1 0 0 @@ -1089,7 +1149,7 @@ 7 - 57 + 62 1 0 0 @@ -1109,7 +1169,7 @@ 0 8 - 58 + 63 1 0 0 @@ -1121,7 +1181,7 @@ 8 - 59 + 64 1 0 0 @@ -1133,7 +1193,7 @@ 8 - 60 + 65 1 0 0 @@ -1145,7 +1205,7 @@ 8 - 61 + 66 1 0 0 @@ -1157,7 +1217,7 @@ 8 - 62 + 67 1 0 0 @@ -1169,7 +1229,7 @@ 8 - 63 + 68 1 0 0 @@ -1181,7 +1241,7 @@ 8 - 64 + 69 1 0 0 @@ -1193,7 +1253,7 @@ 8 - 65 + 70 1 0 0 @@ -1205,7 +1265,7 @@ 8 - 66 + 71 1 0 0 @@ -1217,7 +1277,7 @@ 8 - 67 + 72 1 0 0 @@ -1229,7 +1289,7 @@ 8 - 68 + 73 1 0 0 @@ -1241,7 +1301,7 @@ 8 - 69 + 74 1 0 0 @@ -1253,7 +1313,7 @@ 8 - 70 + 75 1 0 0 @@ -1265,7 +1325,7 @@ 8 - 71 + 76 1 0 0 @@ -1277,7 +1337,7 @@ 8 - 72 + 77 1 0 0 @@ -1289,7 +1349,7 @@ 8 - 73 + 78 1 0 0 @@ -1301,7 +1361,7 @@ 8 - 74 + 79 1 0 0 @@ -1313,7 +1373,7 @@ 8 - 75 + 80 1 0 0 @@ -1325,7 +1385,7 @@ 8 - 76 + 81 1 0 0 @@ -1337,7 +1397,7 @@ 8 - 77 + 82 1 0 0 @@ -1349,7 +1409,7 @@ 8 - 78 + 83 1 0 0 @@ -1361,7 +1421,7 @@ 8 - 79 + 84 1 0 0 @@ -1373,7 +1433,7 @@ 8 - 80 + 85 1 0 0 @@ -1385,7 +1445,7 @@ 8 - 81 + 86 1 0 0 @@ -1397,7 +1457,7 @@ 8 - 82 + 87 1 0 0 @@ -1409,7 +1469,7 @@ 8 - 83 + 88 1 0 0 @@ -1421,7 +1481,7 @@ 8 - 84 + 89 1 0 0 @@ -1441,7 +1501,7 @@ 0 9 - 85 + 90 1 0 0 @@ -1453,7 +1513,7 @@ 9 - 86 + 91 1 0 0 @@ -1465,7 +1525,7 @@ 9 - 87 + 92 1 0 0 @@ -1477,7 +1537,7 @@ 9 - 88 + 93 1 0 0 @@ -1489,7 +1549,7 @@ 9 - 89 + 94 1 0 0 @@ -1501,7 +1561,7 @@ 9 - 90 + 95 1 0 0 @@ -1521,7 +1581,7 @@ 0 10 - 91 + 96 1 0 0 @@ -1533,7 +1593,7 @@ 10 - 92 + 97 1 0 0 @@ -1545,7 +1605,7 @@ 10 - 93 + 98 1 0 0 @@ -1565,7 +1625,7 @@ 0 11 - 94 + 99 1 0 0 @@ -1577,7 +1637,7 @@ 11 - 95 + 100 1 0 0 @@ -1589,7 +1649,7 @@ 11 - 96 + 101 1 0 0 @@ -1609,7 +1669,7 @@ 0 12 - 97 + 102 1 0 0 @@ -1621,7 +1681,7 @@ 12 - 98 + 103 1 0 0 @@ -1633,7 +1693,7 @@ 12 - 99 + 104 1 0 0 @@ -1653,7 +1713,7 @@ 0 13 - 100 + 105 1 0 0 @@ -1665,7 +1725,7 @@ 13 - 101 + 106 1 0 0 @@ -1685,7 +1745,7 @@ 0 14 - 102 + 107 1 0 0 @@ -1697,7 +1757,7 @@ 14 - 103 + 108 1 0 0 @@ -1709,7 +1769,7 @@ 14 - 104 + 109 1 0 0 @@ -1721,7 +1781,7 @@ 14 - 105 + 110 1 0 0 diff --git a/app/app.uvprojx b/app/app.uvprojx index edfd69e..472a262 100644 --- a/app/app.uvprojx +++ b/app/app.uvprojx @@ -503,6 +503,31 @@ 1 .\src\board\board_light_ctrl.c + + FIR.c + 1 + .\src\basic\FIR.c + + + HC_Chen_detect.c + 1 + .\src\basic\HC_Chen_detect.c + + + So_Chen_detect.c + 1 + .\src\basic\So_Chen_detect.c + + + adaptive_algorithm.c + 1 + .\src\basic\adaptive_algorithm.c + + + Pan_Tompkins_detect.c + 1 + .\src\basic\Pan_Tompkins_detect.c + @@ -4387,6 +4412,31 @@ 1 .\src\board\board_light_ctrl.c + + FIR.c + 1 + .\src\basic\FIR.c + + + HC_Chen_detect.c + 1 + .\src\basic\HC_Chen_detect.c + + + So_Chen_detect.c + 1 + .\src\basic\So_Chen_detect.c + + + adaptive_algorithm.c + 1 + .\src\basic\adaptive_algorithm.c + + + Pan_Tompkins_detect.c + 1 + .\src\basic\Pan_Tompkins_detect.c + diff --git a/app/config/sdk_config.h b/app/config/sdk_config.h index 03e3122..54116c9 100644 --- a/app/config/sdk_config.h +++ b/app/config/sdk_config.h @@ -7812,7 +7812,7 @@ // NRF_LOG_ENABLED - nrf_log - Logger //========================================================== #ifndef NRF_LOG_ENABLED -#define NRF_LOG_ENABLED 0 +#define NRF_LOG_ENABLED 1 #endif // Log message pool - Configuration of log message pool diff --git a/app/src/basic/FIR.c b/app/src/basic/FIR.c new file mode 100644 index 0000000..dfcad40 --- /dev/null +++ b/app/src/basic/FIR.c @@ -0,0 +1,38 @@ +#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; +} diff --git a/app/src/basic/FIR.h b/app/src/basic/FIR.h new file mode 100644 index 0000000..f9545c5 --- /dev/null +++ b/app/src/basic/FIR.h @@ -0,0 +1,14 @@ +#ifndef __FIR_H__ +#define __FIR_H__ + +#include +#include +#include +#include +#include + + +extern float FIR_filter(float); +extern void FIR_reset_buffer(); + +#endif diff --git a/app/src/basic/HC_Chen_detect.c b/app/src/basic/HC_Chen_detect.c new file mode 100644 index 0000000..5bfcd67 --- /dev/null +++ b/app/src/basic/HC_Chen_detect.c @@ -0,0 +1,128 @@ +#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; +} diff --git a/app/src/basic/HC_Chen_detect.h b/app/src/basic/HC_Chen_detect.h new file mode 100644 index 0000000..cb9fa51 --- /dev/null +++ b/app/src/basic/HC_Chen_detect.h @@ -0,0 +1,53 @@ +#ifndef __HC_CHEN__ +#define __HC_CHEN__ + +#include +#include +#include +#include +#include + + +#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 diff --git a/app/src/basic/Pan_Tompkins_detect.c b/app/src/basic/Pan_Tompkins_detect.c new file mode 100644 index 0000000..6caea37 --- /dev/null +++ b/app/src/basic/Pan_Tompkins_detect.c @@ -0,0 +1,373 @@ +#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; + } diff --git a/app/src/basic/Pan_Tompkins_detect.h b/app/src/basic/Pan_Tompkins_detect.h new file mode 100644 index 0000000..cbd65d0 --- /dev/null +++ b/app/src/basic/Pan_Tompkins_detect.h @@ -0,0 +1,21 @@ +#ifndef __PAN_TOMPKINS__ +#define __PAN_TOMPKINS__ + +#include +#include +#include +#include +#include + +#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 diff --git a/app/src/basic/QRS.h b/app/src/basic/QRS.h new file mode 100644 index 0000000..33aba1b --- /dev/null +++ b/app/src/basic/QRS.h @@ -0,0 +1,30 @@ +#pragma once +#include +//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 */ +}; diff --git a/app/src/basic/So_Chen_detect.c b/app/src/basic/So_Chen_detect.c new file mode 100644 index 0000000..e329825 --- /dev/null +++ b/app/src/basic/So_Chen_detect.c @@ -0,0 +1,93 @@ +#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; +} diff --git a/app/src/basic/So_Chen_detect.h b/app/src/basic/So_Chen_detect.h new file mode 100644 index 0000000..a505775 --- /dev/null +++ b/app/src/basic/So_Chen_detect.h @@ -0,0 +1,42 @@ +#ifndef __SO_AND_CHEN__ +#define __SO_AND_CHEN__ + +#include +#include +#include +#include +#include + +#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 diff --git a/app/src/basic/adaptive_algorithm.c b/app/src/basic/adaptive_algorithm.c new file mode 100644 index 0000000..11a4e60 --- /dev/null +++ b/app/src/basic/adaptive_algorithm.c @@ -0,0 +1,100 @@ +#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; +} + diff --git a/app/src/basic/adaptive_algorithm.h b/app/src/basic/adaptive_algorithm.h new file mode 100644 index 0000000..d1bbbc6 --- /dev/null +++ b/app/src/basic/adaptive_algorithm.h @@ -0,0 +1,41 @@ +#ifndef __ALGORITHM__ +#define __ALGORITHM__ + +#include +#include +#include +#include +#include +#include + +#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 diff --git a/app/src/board/board.h b/app/src/board/board.h index 95ef75e..5f27533 100644 --- a/app/src/board/board.h +++ b/app/src/board/board.h @@ -32,7 +32,7 @@ #define SAMPLE_MIN_TIME_S (30.0) // ²ÉÑù×îСʱ¼ä #define LITTLE_DATA_BLOCK_FRAME_NUM 5 // ÿ´Î¶àÉÙÖ¡Éϱ¨Ò»´Î -#define KEEP_STILL_OVERTIME_MS_1P5 2000 // ±£³Ö¾²Ö¹³¬Ê±Ê±¼äµÄ1/6 +#define KEEP_STILL_OVERTIME_MS_1P5 1500 // ±£³Ö¾²Ö¹³¬Ê±Ê±¼äµÄ1/6 #define LED_GREEN_PIN 9 #define LED_BLUE_PIN 10 diff --git a/app/src/display_manager.c b/app/src/display_manager.c index f9dd181..84acf75 100644 --- a/app/src/display_manager.c +++ b/app/src/display_manager.c @@ -7,11 +7,11 @@ #include "basic/ssd1306/driver_ssd1306.h" #include "basic/ssd1306/driver_ssd1306_basic.h" #include "basic/ssd1306/wave_drawer.h" +#include "board/board_battery_state.h" #include "config.h" #include "font.h" #include "heart_wave_sample_service.h" #include "one_conduction_board.h" -#include "board/board_battery_state.h" PageState_t g_pageState; void dsp_mgr_change_to_page(page_t page) { // @@ -360,7 +360,7 @@ void samplePage_schedule() { int wave_y = (int)hwss_read_val(); int heartrate = (int)hwss_read_heart_rate(); // ZLOGI("samplePage_schedule %d %d %d", capturetime, wave_y, heartrate); - samplePage_update_state(capturetime / 1000, wave_y, 123, count % 10 == 0, count % 5 == 0); + samplePage_update_state(capturetime / 1000, wave_y, hwss_read_heart_rate(), count % 30 == 0, count % 5 == 0); } /******************************************************************************* diff --git a/app/src/heart_wave_sample_service.c b/app/src/heart_wave_sample_service.c index c07b8c0..ce9d0e6 100644 --- a/app/src/heart_wave_sample_service.c +++ b/app/src/heart_wave_sample_service.c @@ -2,6 +2,11 @@ #include "app_event.h" #include "app_event_distribute.h" +#include "basic/FIR.h" +#include "basic/HC_Chen_detect.h" +#include "basic/So_Chen_detect.h" +#include "basic/adaptive_algorithm.h" +#include "basic/Pan_Tompkins_detect.h" #include "board/board_ecg_sensor.h" #include "nrfx_timer.h" #include "one_conduction_board.h" @@ -18,6 +23,116 @@ static uint32_t m_frame_index = 0; static one_frame_data_t m_sensor_little_frame_cache[LITTLE_DATA_BLOCK_FRAME_NUM]; static uint32_t m_little_frame_index; +static bool m_prestart_flag; + +static nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(HEART_WAVE_SAMPLE_TMR_INSTANCE); + +typedef struct { + int heartSignalCnt; + int heart_rate; + uint32_t last_qrs_point; + uint32_t time_cnt; + uint32_t origin_time_cnt; + + uint32_t index; + float index_f; +} QRS_t; + +QRS_t m_qrs; + +void QRS_reset() { + m_qrs.heartSignalCnt = 0; + m_qrs.heart_rate = 0; + m_qrs.time_cnt = 0; + m_qrs.index = 0; + m_qrs.index_f = 0; + FIR_reset_buffer(); +} + +void QRS_process(float value) { + bool isPeak = false; +#if 1 + float result = value; + SignalPoint sp; + sp.value = result; + sp.index = m_qrs.time_cnt; + SignalPoint peak = So_Chen_detect(sp, SAMPLING_RATE * 0.25f, 4, 16); + if (peak.index != -1) { + isPeak = true; + } +#endif + +#if 0 + isPeak = HC_Chen_detect(value); +#endif + +#if 0 + static const float CV_LIMIT = 50.0f; + static const float THRESHOLD_FACTOR = 3.0f; + double mean = CalculateMean(value); + double rms = CalculateRootMeanSquare(value); + double cv = CalculateCoefficientOfVariation(value); + double threshold; + if (cv > CV_LIMIT) { + threshold = rms; + } else { + threshold = rms * (cv / 100.0f) * THRESHOLD_FACTOR; + } + bool is_peak; + SignalPoint result; + result = PeakDetect(value, m_qrs.time_cnt, threshold, &is_peak); + if (result.index != -1) { + if (is_peak) { + isPeak = true; + } + } +#endif + +#if 0 + + + double result = value; + double bandpass; + double integral; + double square; + + bandpass = result; + result = Derivative(result); + result = Squar(result); + square = result; + result = MovingWindowIntegral(result); + integral = result; + + SignalPoint peak = ThresholdCalculate(m_qrs.time_cnt,value,bandpass,square,integral); + + if(peak.index != -1){ + isPeak = true; + } + +#endif + + if (isPeak) { + uint32_t time_diff = m_qrs.time_cnt - m_qrs.last_qrs_point; + m_qrs.last_qrs_point = m_qrs.time_cnt; + m_qrs.heartSignalCnt++; + // m_qrs.heart_rate = m_qrs.heartSignalCnt; + if (m_qrs.last_qrs_point != 0) { + m_qrs.heart_rate = 60 * (1 / (time_diff * 1.0 / SAMPLING_RATE)); + } + } + + m_qrs.time_cnt++; +} + +static const void compute_heart_rate(float sample_data) { + ZASSERT(SAMPLE_RATE == 500); + m_qrs.index_f = m_frame_index / 500.0 * 360; + if ((m_qrs.index_f - m_qrs.index) > 1) { + m_qrs.index = m_qrs.index_f; + float val = sample_data; + QRS_process(FIR_filter(val)); + } +} static void swap_buffer() { if (m_capture_buffer == NULL) { @@ -92,14 +207,21 @@ static inline void prvf_light_block_trigger_event() { } void nrfx_timer_event_handler(nrf_timer_event_t event_type, void* p_context) { // - uint16_t val = BoardEcgSensor_plod_get_ecg_val(); // 12bit - m_frame_index++; + uint16_t val = BoardEcgSensor_plod_get_ecg_val(); // 12bit + float val_af100 = (float)val / 4096.0f * 100; + if (m_prestart_flag) { + compute_heart_rate(val_af100); + return; + } else { + compute_heart_rate(val_af100); + } /******************************************************************************* * ÏÔʾÊý¾Ý¼ÆËã²¢¸³Öµ * *******************************************************************************/ - float val_af100 = (float)val / 4096.0f * 100; - val_af100 = amp_val(val_af100, 50, 1.8f); + + m_frame_index++; + val_af100 = amp_val(val_af100, 45, 3.5f); val_af100 = Filter(&m_filter, val_af100); m_sensor_display_data = val_af100; @@ -134,7 +256,6 @@ void nrfx_timer_event_handler(nrf_timer_event_t event_type, void* p_context) { prvf_light_block_cache_clear(); } } -static const nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(HEART_WAVE_SAMPLE_TMR_INSTANCE); void hwss_init(void) { static bool m_timer_inited = false; @@ -152,20 +273,27 @@ void hwss_init(void) { // nrfx_timer_init(&m_timer, &timer_cfg, nrfx_timer_event_handler); ZERROR_CHECK(nrfx_timer_init(&m_timer, &timer_cfg, nrfx_timer_event_handler)); - uint32_t timer_ticks = nrfx_timer_ms_to_ticks(&m_timer, 5); // 200HZ + uint32_t timer_ticks = nrfx_timer_ms_to_ticks(&m_timer, 2); // + ZASSERT(SAMPLE_RATE == 500); nrfx_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, timer_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); m_timer_inited = true; } } void hwss_uninit(void) { nrfx_timer_disable(&m_timer); } -void hwss_start_capture(void) { +void hwss_pre_start_capture(void) { m_start_capture_tp = znordic_getpower_on_s(); swap_buffer(); m_frame_index = 0; + + QRS_reset(); + prvf_light_block_cache_clear(); nrfx_timer_enable(&m_timer); + m_prestart_flag = true; } + +void hwss_start_capture(void) { m_prestart_flag = false; } void hwss_stop_capture(void) { nrfx_timer_disable(&m_timer); m_frame_index = 0; @@ -178,6 +306,8 @@ float hwss_read_val(void) { __enable_irq(); return val; } -float hwss_read_heart_rate(void) { return 0; } +float hwss_read_heart_rate(void) { // + return m_qrs.heart_rate; +} int hwss_has_captured_time_ms() { return (znordic_getpower_on_s() - m_start_capture_tp) * 1000; } diff --git a/app/src/heart_wave_sample_service.h b/app/src/heart_wave_sample_service.h index 65e18dc..177feed 100644 --- a/app/src/heart_wave_sample_service.h +++ b/app/src/heart_wave_sample_service.h @@ -7,6 +7,7 @@ typedef void (*heart_wave_sample_service_callback_t)(uint16_t *p_data, uint16_t void hwss_init(void); void hwss_uninit(void); +void hwss_pre_start_capture(void); void hwss_start_capture(void); void hwss_stop_capture(void); diff --git a/app/src/one_conduction_main.c b/app/src/one_conduction_main.c index 8b14942..21d3205 100644 --- a/app/src/one_conduction_main.c +++ b/app/src/one_conduction_main.c @@ -229,6 +229,7 @@ static void app_event_listener(void* p_event_data, uint16_t event_size) { if (!BoardBattery_get_charging_state()) { ds_change_to_state(kdevice_state_keep_still); dsp_mgr_change_to_preparePage(); + hwss_pre_start_capture(); } } @@ -251,6 +252,7 @@ static void app_event_listener(void* p_event_data, uint16_t event_size) { if (!BoardEcgSensor_plod_get_connected_state_after_filter()) { // Èç¹ûÓû§Î´±£³Ö¾²Ö¹£¬Çл»µ½Ê×Ò³ state_machine__change_to_home_state(); + hwss_stop_capture(); } else { /******************************************************************************* * Ò³Ãæ¼ÓÔØÖÐ * diff --git a/ify_hrs_protocol b/ify_hrs_protocol index da8e077..f2eae40 160000 --- a/ify_hrs_protocol +++ b/ify_hrs_protocol @@ -1 +1 @@ -Subproject commit da8e07706a41c157a3e42da3f69136f665f5a20f +Subproject commit f2eae40f67048820f37dc5dd3585ca9a75156dfc diff --git a/tools/build.bat b/tools/build.bat index efaced1..5c14c42 100644 --- a/tools/build.bat +++ b/tools/build.bat @@ -1,3 +1,4 @@ g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ .\sample_bin_parse.cpp -o sample_bin_parse.exe g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ text2bin.cpp -o text2bin.exe g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ one_lead_upload_packet_parser.cpp -o one_lead_upload_packet_parser.exe +g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ one_lead_realtime_report_parser.cpp -o one_lead_realtime_report_parser.exe \ No newline at end of file diff --git a/tools/one_lead_realtime_report_parser.cpp b/tools/one_lead_realtime_report_parser.cpp new file mode 100644 index 0000000..5b7910f --- /dev/null +++ b/tools/one_lead_realtime_report_parser.cpp @@ -0,0 +1,121 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// +#include +// +#include +// +#include + +#pragma comment(lib, "ws2_32.lib") +#define PORT 8988 + +// #define EACH_FRAME_SIZE 180 +// #define DATA_NUM (EACH_FRAME_SIZE / 3) + +// 5A A5 +// 03 00 65 +// 05 00 00 00 +// 77 07 +// 80 07 +// 7F 07 +// 88 07 +// 8D 07 +// 5B B5 +#pragma pack(1) +typedef struct parse_packet_to_csv { + uint8_t h0; + uint8_t h1; + uint8_t header[3]; + uint8_t index[4]; + uint16_t data[5]; + uint8_t e0; + uint8_t e1; +}; +#pragma pack() + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +uint16_t chdatacache[5]; + +void parse_chdatacache(parse_packet_to_csv *packet) { + chdatacache[0] = packet->data[0]; + chdatacache[1] = packet->data[1]; + chdatacache[2] = packet->data[2]; + chdatacache[3] = packet->data[3]; + chdatacache[4] = packet->data[4]; +} + +int main(int argc, char *argv[]) { + /** + * @brief + * ´ò¿ªraw.txt + * ¶ÁÈ¡raw.txtÈ«²¿ÄÚÈÝ + * È¥µô»»ÐУ¬¿Õ¸ñ£¬»Ø³µ + * ½«16½øÖÆ×Ö·û´®×ª»»³É¶þ½øÖÆ + * дÈëraw.bin + */ + + std::ifstream ifs("Data.txt"); + std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + ifs.close(); + + std::string str2; + for (auto &c : str) { + if (c == ' ' || c == '\n' || c == '\r') { + continue; + } + str2.push_back(c); + } + + std::ofstream ofs("Data.bin", std::ios::binary); + for (size_t i = 0; i < str2.size(); i += 2) { + std::string str3 = str2.substr(i, 2); + char c = (char)std::stoi(str3, nullptr, 16); + ofs.write(&c, 1); + } + ofs.close(); + + /** + * @brief + * ½âÎöÉϱ¨Îļþ + */ + + { + std::ifstream ifs("Data.bin", std::ios::binary); + std::vector buffer((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + ifs.close(); + + printf("buffer.size() = %d\n", buffer.size()); + std::ofstream ofs("Data.csv"); + for (size_t i = 0; i < buffer.size();) { + parse_packet_to_csv *packet = (parse_packet_to_csv *)&buffer[i]; + + if (packet->h0 == 0x5A && packet->h1 == 0xA5 && packet->e0 == 0x5B && packet->e1 == 0xB5) { + parse_chdatacache(packet); + for (size_t i = 0; i < ARRAY_SIZE(chdatacache); i += 1) { + ofs << chdatacache[i] << "\n"; + } + i += sizeof(parse_packet_to_csv); + } else { + i++; + } + } + } + + printf("parse over\n"); + + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } +} \ No newline at end of file diff --git a/tools/one_lead_realtime_report_parser.exe b/tools/one_lead_realtime_report_parser.exe new file mode 100644 index 0000000..f42f8bb Binary files /dev/null and b/tools/one_lead_realtime_report_parser.exe differ diff --git a/tools/one_lead_upload_packet_parser.exe b/tools/one_lead_upload_packet_parser.exe index 1c444f4..ebc8bcb 100644 Binary files a/tools/one_lead_upload_packet_parser.exe and b/tools/one_lead_upload_packet_parser.exe differ diff --git a/tools/sample_bin_parse.exe b/tools/sample_bin_parse.exe index 9653ef9..514e45f 100644 Binary files a/tools/sample_bin_parse.exe and b/tools/sample_bin_parse.exe differ diff --git a/tools/text2bin.exe b/tools/text2bin.exe index dd29e08..1c2ad77 100644 Binary files a/tools/text2bin.exe and b/tools/text2bin.exe differ