diff --git a/.vscode/settings.json b/.vscode/settings.json
index 82c7cc1..dfdff98 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -184,7 +184,8 @@
"optional": "c",
"so_chen_detect.h": "c",
"adaptive_algorithm.h": "c",
- "pan_tompkins_detect.h": "c"
+ "pan_tompkins_detect.h": "c",
+ "qrs_time_domain_zh.h": "c"
},
"files.encoding": "gbk"
}
\ No newline at end of file
diff --git a/app/app.uvoptx b/app/app.uvoptx
index 9ec7873..49b65de 100644
--- a/app/app.uvoptx
+++ b/app/app.uvoptx
@@ -715,6 +715,18 @@
0
0
+
+ 1
+ 30
+ 1
+ 0
+ 0
+ 0
+ .\src\basic\qrs_time_domain_zh.c
+ qrs_time_domain_zh.c
+ 0
+ 0
+
@@ -725,7 +737,7 @@
0
2
- 30
+ 31
1
0
0
@@ -745,7 +757,7 @@
0
3
- 31
+ 32
1
0
0
@@ -757,7 +769,7 @@
3
- 32
+ 33
1
0
0
@@ -777,7 +789,7 @@
0
4
- 33
+ 34
1
0
0
@@ -797,7 +809,7 @@
0
5
- 34
+ 35
1
0
0
@@ -809,7 +821,7 @@
5
- 35
+ 36
1
0
0
@@ -821,7 +833,7 @@
5
- 36
+ 37
1
0
0
@@ -833,7 +845,7 @@
5
- 37
+ 38
1
0
0
@@ -845,7 +857,7 @@
5
- 38
+ 39
1
0
0
@@ -857,7 +869,7 @@
5
- 39
+ 40
1
0
0
@@ -869,7 +881,7 @@
5
- 40
+ 41
1
0
0
@@ -881,7 +893,7 @@
5
- 41
+ 42
1
0
0
@@ -901,7 +913,7 @@
0
6
- 42
+ 43
1
0
0
@@ -921,7 +933,7 @@
0
7
- 43
+ 44
1
0
0
@@ -933,7 +945,7 @@
7
- 44
+ 45
1
0
0
@@ -945,7 +957,7 @@
7
- 45
+ 46
1
0
0
@@ -957,7 +969,7 @@
7
- 46
+ 47
1
0
0
@@ -969,7 +981,7 @@
7
- 47
+ 48
1
0
0
@@ -981,7 +993,7 @@
7
- 48
+ 49
1
0
0
@@ -993,7 +1005,7 @@
7
- 49
+ 50
1
0
0
@@ -1005,7 +1017,7 @@
7
- 50
+ 51
1
0
0
@@ -1017,7 +1029,7 @@
7
- 51
+ 52
1
0
0
@@ -1029,7 +1041,7 @@
7
- 52
+ 53
1
0
0
@@ -1041,7 +1053,7 @@
7
- 53
+ 54
1
0
0
@@ -1053,7 +1065,7 @@
7
- 54
+ 55
1
0
0
@@ -1065,7 +1077,7 @@
7
- 55
+ 56
1
0
0
@@ -1077,7 +1089,7 @@
7
- 56
+ 57
1
0
0
@@ -1089,7 +1101,7 @@
7
- 57
+ 58
1
0
0
@@ -1101,7 +1113,7 @@
7
- 58
+ 59
1
0
0
@@ -1113,7 +1125,7 @@
7
- 59
+ 60
1
0
0
@@ -1125,7 +1137,7 @@
7
- 60
+ 61
1
0
0
@@ -1137,7 +1149,7 @@
7
- 61
+ 62
1
0
0
@@ -1149,7 +1161,7 @@
7
- 62
+ 63
1
0
0
@@ -1169,7 +1181,7 @@
0
8
- 63
+ 64
1
0
0
@@ -1181,7 +1193,7 @@
8
- 64
+ 65
1
0
0
@@ -1193,7 +1205,7 @@
8
- 65
+ 66
1
0
0
@@ -1205,7 +1217,7 @@
8
- 66
+ 67
1
0
0
@@ -1217,7 +1229,7 @@
8
- 67
+ 68
1
0
0
@@ -1229,7 +1241,7 @@
8
- 68
+ 69
1
0
0
@@ -1241,7 +1253,7 @@
8
- 69
+ 70
1
0
0
@@ -1253,7 +1265,7 @@
8
- 70
+ 71
1
0
0
@@ -1265,7 +1277,7 @@
8
- 71
+ 72
1
0
0
@@ -1277,7 +1289,7 @@
8
- 72
+ 73
1
0
0
@@ -1289,7 +1301,7 @@
8
- 73
+ 74
1
0
0
@@ -1301,7 +1313,7 @@
8
- 74
+ 75
1
0
0
@@ -1313,7 +1325,7 @@
8
- 75
+ 76
1
0
0
@@ -1325,7 +1337,7 @@
8
- 76
+ 77
1
0
0
@@ -1337,7 +1349,7 @@
8
- 77
+ 78
1
0
0
@@ -1349,7 +1361,7 @@
8
- 78
+ 79
1
0
0
@@ -1361,7 +1373,7 @@
8
- 79
+ 80
1
0
0
@@ -1373,7 +1385,7 @@
8
- 80
+ 81
1
0
0
@@ -1385,7 +1397,7 @@
8
- 81
+ 82
1
0
0
@@ -1397,7 +1409,7 @@
8
- 82
+ 83
1
0
0
@@ -1409,7 +1421,7 @@
8
- 83
+ 84
1
0
0
@@ -1421,7 +1433,7 @@
8
- 84
+ 85
1
0
0
@@ -1433,7 +1445,7 @@
8
- 85
+ 86
1
0
0
@@ -1445,7 +1457,7 @@
8
- 86
+ 87
1
0
0
@@ -1457,7 +1469,7 @@
8
- 87
+ 88
1
0
0
@@ -1469,7 +1481,7 @@
8
- 88
+ 89
1
0
0
@@ -1481,7 +1493,7 @@
8
- 89
+ 90
1
0
0
@@ -1501,7 +1513,7 @@
0
9
- 90
+ 91
1
0
0
@@ -1513,7 +1525,7 @@
9
- 91
+ 92
1
0
0
@@ -1525,7 +1537,7 @@
9
- 92
+ 93
1
0
0
@@ -1537,7 +1549,7 @@
9
- 93
+ 94
1
0
0
@@ -1549,7 +1561,7 @@
9
- 94
+ 95
1
0
0
@@ -1561,7 +1573,7 @@
9
- 95
+ 96
1
0
0
@@ -1581,7 +1593,7 @@
0
10
- 96
+ 97
1
0
0
@@ -1593,7 +1605,7 @@
10
- 97
+ 98
1
0
0
@@ -1605,7 +1617,7 @@
10
- 98
+ 99
1
0
0
@@ -1625,7 +1637,7 @@
0
11
- 99
+ 100
1
0
0
@@ -1637,7 +1649,7 @@
11
- 100
+ 101
1
0
0
@@ -1649,7 +1661,7 @@
11
- 101
+ 102
1
0
0
@@ -1669,7 +1681,7 @@
0
12
- 102
+ 103
1
0
0
@@ -1681,7 +1693,7 @@
12
- 103
+ 104
1
0
0
@@ -1693,7 +1705,7 @@
12
- 104
+ 105
1
0
0
@@ -1713,7 +1725,7 @@
0
13
- 105
+ 106
1
0
0
@@ -1725,7 +1737,7 @@
13
- 106
+ 107
1
0
0
@@ -1745,7 +1757,7 @@
0
14
- 107
+ 108
1
0
0
@@ -1757,7 +1769,7 @@
14
- 108
+ 109
1
0
0
@@ -1769,7 +1781,7 @@
14
- 109
+ 110
1
0
0
@@ -1781,7 +1793,7 @@
14
- 110
+ 111
1
0
0
diff --git a/app/app.uvprojx b/app/app.uvprojx
index 472a262..7aada8c 100644
--- a/app/app.uvprojx
+++ b/app/app.uvprojx
@@ -528,6 +528,11 @@
1
.\src\basic\Pan_Tompkins_detect.c
+
+ qrs_time_domain_zh.c
+ 1
+ .\src\basic\qrs_time_domain_zh.c
+
@@ -4437,6 +4442,11 @@
1
.\src\basic\Pan_Tompkins_detect.c
+
+ qrs_time_domain_zh.c
+ 1
+ .\src\basic\qrs_time_domain_zh.c
+
diff --git a/app/src/basic/qrs_time_domain_zh.c b/app/src/basic/qrs_time_domain_zh.c
new file mode 100644
index 0000000..01d45e7
--- /dev/null
+++ b/app/src/basic/qrs_time_domain_zh.c
@@ -0,0 +1,218 @@
+#include "qrs_time_domain_zh.h"
+
+#include
+#include
+#include
+
+#define HEART_RATE_FILTER_SIZE 5
+
+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 >= 5) {
+ pfilter->index = 0;
+ }
+
+ if (pfilter->cnt < 5) {
+ 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 >= 5) {
+ pfilter->index = 0;
+ }
+
+ if (pfilter->cnt < 5) {
+ return data;
+ }
+
+ return pfilter->sum / 5;
+}
+
+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[5];
+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 >= 5) {
+ pQRS_median_filter_cache_index = 0;
+ }
+
+ if (pQRS_median_filter_cache_cnt < 5) {
+ return indata;
+ }
+
+ static uint16_t process_cache[5];
+ memcpy(process_cache, pQRS_median_filter_cache, 5 * sizeof(uint16_t));
+ for (uint8_t i = 0; i < 5; i++) {
+ for (uint8_t j = i + 1; j < 5; 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() { return m_heartrate; }
\ No newline at end of file
diff --git a/app/src/basic/qrs_time_domain_zh.h b/app/src/basic/qrs_time_domain_zh.h
new file mode 100644
index 0000000..37f6f43
--- /dev/null
+++ b/app/src/basic/qrs_time_domain_zh.h
@@ -0,0 +1,17 @@
+/**
+ * @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
+#define TABLE_SIZE 500
+
+void QRS_resetBuf();
+void QRS_processData(uint16_t data);
+uint16_t QRS_getHeartRate();
diff --git a/app/src/heart_wave_sample_service.c b/app/src/heart_wave_sample_service.c
index eaf7518..21a95b0 100644
--- a/app/src/heart_wave_sample_service.c
+++ b/app/src/heart_wave_sample_service.c
@@ -2,11 +2,7 @@
#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 "basic/qrs_time_domain_zh.h"
#include "board/board_ecg_sensor.h"
#include "nrfx_timer.h"
#include "one_conduction_board.h"
@@ -23,117 +19,9 @@ 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) {
m_capture_buffer = m_capture_buffer_a;
@@ -207,19 +95,13 @@ 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
- float val_af100 = (float)val / 4096.0f * 100;
- if (m_prestart_flag) {
- compute_heart_rate(val_af100);
- return;
- } else {
- compute_heart_rate(val_af100);
- }
+ uint16_t val = BoardEcgSensor_plod_get_ecg_val(); // 12bit
/*******************************************************************************
* 显示数据计算并赋值 *
*******************************************************************************/
-
+ QRS_processData(val);
+ float val_af100 = (float)val / 4096.0f * 100;
m_frame_index++;
val_af100 = amp_val(val_af100, 45, 3.5f);
val_af100 = Filter(&m_filter, val_af100);
@@ -281,19 +163,16 @@ void hwss_init(void) {
}
void hwss_uninit(void) { nrfx_timer_disable(&m_timer); }
-void hwss_pre_start_capture(void) {
+void hwss_start_capture(void) {
m_start_capture_tp = znordic_getpower_on_s();
swap_buffer();
m_frame_index = 0;
- QRS_reset();
+ QRS_resetBuf();
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;
@@ -307,7 +186,7 @@ float hwss_read_val(void) {
return val;
}
float hwss_read_heart_rate(void) { //
- return m_qrs.heart_rate;
+ return QRS_getHeartRate();
}
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.c.bak b/app/src/heart_wave_sample_service.c.bak
new file mode 100644
index 0000000..eaf7518
--- /dev/null
+++ b/app/src/heart_wave_sample_service.c.bak
@@ -0,0 +1,313 @@
+#include "heart_wave_sample_service.h"
+
+#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"
+
+static uint16_t m_capture_buffer_a[128];
+static uint16_t m_capture_buffer_b[128];
+
+static uint16_t* m_capture_buffer;
+static uint16_t m_capture_buffer_index = 0;
+
+volatile static float m_sensor_display_data = 0; // 0->100
+static uint32_t m_start_capture_tp;
+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) {
+ m_capture_buffer = m_capture_buffer_a;
+ m_capture_buffer_index = 0;
+ return;
+ }
+
+ if (m_capture_buffer == m_capture_buffer_a) {
+ m_capture_buffer = m_capture_buffer_b;
+ } else {
+ m_capture_buffer = m_capture_buffer_a;
+ }
+ m_capture_buffer_index = 0;
+ return;
+}
+static float amp_val(uint16_t val, uint16_t valcener, float amp) {
+ float valf = (float)val - valcener;
+ valf = valf * amp;
+ valf += valcener;
+
+ if (valf >= 100) {
+ valf = 100;
+ }
+
+ if (valf <= 0) {
+ valf = 0;
+ }
+ return valf;
+}
+
+typedef struct {
+ float value;
+ float efectiveFactor;
+} filter_t;
+
+filter_t m_filter = {0, 0.8};
+
+static float Filter(filter_t* filter, float newInput) {
+ float newv = ((float)filter->value * (1.0f - filter->efectiveFactor)) + ((float)newInput * filter->efectiveFactor);
+ filter->value = newv;
+ return newv;
+}
+
+/*******************************************************************************
+ * 小包数据上报 *
+ *******************************************************************************/
+static inline void prvf_little_block_cache_push_one_frame(uint16_t data) {
+ if (m_little_frame_index >= LITTLE_DATA_BLOCK_FRAME_NUM) {
+ return;
+ }
+ m_sensor_little_frame_cache[m_little_frame_index].data = data;
+ m_little_frame_index++;
+}
+
+static inline bool prvf_light_block_cache_is_full(void) {
+ if (m_little_frame_index >= LITTLE_DATA_BLOCK_FRAME_NUM) {
+ return true;
+ }
+ return false;
+}
+static inline void prvf_light_block_cache_clear(void) { m_little_frame_index = 0; }
+static inline void prvf_light_block_trigger_event() {
+ static app_event_t event;
+ event.eventType = kevent_capture_little_data_block_event;
+ for (uint32_t i = 0; i < LITTLE_DATA_BLOCK_FRAME_NUM; i++) {
+ event.val.little_data_block.data[i].data = m_sensor_little_frame_cache[i].data;
+ }
+ event.val.little_data_block.frameIndex = m_frame_index - LITTLE_DATA_BLOCK_FRAME_NUM;
+ // ZLOGI("%d", event.val.little_data_block.frameIndex);
+ AppEvent_pushEvent(&event);
+}
+
+void nrfx_timer_event_handler(nrf_timer_event_t event_type, void* p_context) { //
+ 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);
+ }
+
+ /*******************************************************************************
+ * 显示数据计算并赋值 *
+ *******************************************************************************/
+
+ 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;
+
+ /*******************************************************************************
+ * 采样数据缓存 *
+ *******************************************************************************/
+ if (m_capture_buffer == NULL) {
+ swap_buffer();
+ }
+
+ if (m_capture_buffer_index < 128) {
+ m_capture_buffer[m_capture_buffer_index++] = val;
+ }
+
+ if (m_capture_buffer_index == 128) {
+ app_event_t evt;
+ evt.eventType = kevent_capture_256data_event;
+ evt.val.capture_data_cache = (uint8_t*)m_capture_buffer;
+ swap_buffer();
+ AppEvent_pushEvent(&evt);
+ }
+
+ /*******************************************************************************
+ * 实时采样数据事件上报 *
+ *******************************************************************************/
+ /**
+ * @brief 缓存数据,并触发小数据块事件
+ */
+ prvf_little_block_cache_push_one_frame(val);
+ if (prvf_light_block_cache_is_full()) {
+ prvf_light_block_trigger_event();
+ prvf_light_block_cache_clear();
+ }
+}
+
+void hwss_init(void) {
+ static bool m_timer_inited = false;
+ if (!m_timer_inited) {
+ /**
+ * @brief 初始化定时器
+ */
+ static nrfx_timer_config_t timer_cfg = {
+ .frequency = NRF_TIMER_FREQ_500kHz,
+ .mode = NRF_TIMER_MODE_TIMER,
+ .bit_width = NRF_TIMER_BIT_WIDTH_24,
+ .p_context = NULL,
+ .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY,
+ };
+
+ // 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, 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_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;
+ prvf_light_block_cache_clear();
+}
+
+float hwss_read_val(void) {
+ __disable_irq();
+ float val = m_sensor_display_data;
+ __enable_irq();
+ return val;
+}
+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 177feed..65e18dc 100644
--- a/app/src/heart_wave_sample_service.h
+++ b/app/src/heart_wave_sample_service.h
@@ -7,7 +7,6 @@ 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 21d3205..b42a0dc 100644
--- a/app/src/one_conduction_main.c
+++ b/app/src/one_conduction_main.c
@@ -98,7 +98,7 @@ void ENTER_DEEP_SLEEP() {
NVIC_SystemReset();
}
-static void check_battery_level() {}
+
/*******************************************************************************
* 状态切换方法 *
*******************************************************************************/
@@ -229,7 +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();
+ // hwss_pre_start_capture();
}
}
@@ -252,7 +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();
+ // hwss_stop_capture();
} else {
/*******************************************************************************
* 页面加载中 *