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