Browse Source

update

master
zhaohe 1 year ago
parent
commit
f58bb50384
  1. 24
      app/app.uvoptx
  2. 20
      app/app.uvprojx
  3. 2
      app/src/app_basic_service/basic/event.h
  4. 88
      app/src/app_service/ecg_service/algo/MedianFilter.c
  5. 38
      app/src/app_service/ecg_service/algo/MedianFilter.h
  6. 45
      app/src/app_service/ecg_service/algo/iflytop_simple_filter.c
  7. 13
      app/src/app_service/ecg_service/algo/iflytop_simple_filter.h
  8. 5
      app/src/app_service/ecg_service/algo/iflytop_simple_filter_ext.h
  9. 114
      app/src/app_service/ecg_service/ecg_algo.c
  10. 3
      app/src/app_service/ecg_service/ecg_algo.h
  11. 5
      app/src/app_service/ecg_service/ecg_service.c
  12. 5
      app/src/aproject_config/config.h
  13. 10
      app/src/ble_data_processer_utils.c
  14. 2
      ify_hrs_protocol
  15. 88
      test/MedianFilter.c
  16. 38
      test/MedianFilter.h
  17. 1
      test/README.md
  18. 27
      test/test.c
  19. 129
      test/test.py

24
app/app.uvoptx

@ -1787,6 +1787,30 @@
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>15</GroupNumber>
<FileNumber>110</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\app_service\ecg_service\algo\MedianFilter.c</PathWithFileName>
<FilenameWithoutPath>MedianFilter.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>15</GroupNumber>
<FileNumber>111</FileNumber>
<FileType>5</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\app_service\ecg_service\algo\MedianFilter.h</PathWithFileName>
<FilenameWithoutPath>MedianFilter.h</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>

20
app/app.uvprojx

@ -3905,6 +3905,16 @@
<FileType>1</FileType>
<FilePath>.\src\app_service\ecg_service\algo\zdata_statistics.c</FilePath>
</File>
<File>
<FileName>MedianFilter.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\app_service\ecg_service\algo\MedianFilter.c</FilePath>
</File>
<File>
<FileName>MedianFilter.h</FileName>
<FileType>5</FileType>
<FilePath>.\src\app_service\ecg_service\algo\MedianFilter.h</FilePath>
</File>
</Files>
</Group>
<Group>
@ -7814,6 +7824,16 @@
<FileType>1</FileType>
<FilePath>.\src\app_service\ecg_service\algo\zdata_statistics.c</FilePath>
</File>
<File>
<FileName>MedianFilter.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\app_service\ecg_service\algo\MedianFilter.c</FilePath>
</File>
<File>
<FileName>MedianFilter.h</FileName>
<FileType>5</FileType>
<FilePath>.\src\app_service\ecg_service\algo\MedianFilter.h</FilePath>
</File>
</Files>
</Group>
<Group>

2
app/src/app_basic_service/basic/event.h

@ -5,7 +5,7 @@
#include "aproject_config/config.h"
#include "ify_hrs_protocol/heart_rate_sensor_protocol.h"
#define ECG_DATA_REPORT_FRAME_NUM 64 // ecg每次上报的帧数
#define ECG_DATA_REPORT_FRAME_NUM 50 // ecg每次上报的帧数
typedef enum {
kappevent_tmr_1s_scheduler_event,

88
app/src/app_service/ecg_service/algo/MedianFilter.c

@ -0,0 +1,88 @@
/*
* MedianFilter.c
*
* Created on: May 19, 2018
* Author: alexandru.bogdan
*/
#include "MedianFilter.h"
int MEDIANFILTER_Init(sMedianFilter_t *medianFilter)
{
if(medianFilter && medianFilter->medianBuffer &&
(medianFilter->numNodes % 2) && (medianFilter->numNodes > 1))
{
//initialize buffer nodes
for(unsigned int i = 0; i < medianFilter->numNodes; i++)
{
medianFilter->medianBuffer[i].value = 0;
medianFilter->medianBuffer[i].nextAge = &medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes];
medianFilter->medianBuffer[i].nextValue = &medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes];
medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes].prevValue = &medianFilter->medianBuffer[i];
}
//initialize heads
medianFilter->ageHead = medianFilter->medianBuffer;
medianFilter->valueHead = medianFilter->medianBuffer;
medianFilter->medianHead = &medianFilter->medianBuffer[medianFilter->numNodes / 2];
return 0;
}
return -1;
}
int MEDIANFILTER_Insert(sMedianFilter_t *medianFilter, int sample)
{
unsigned int i;
sMedianNode_t *newNode, *it;
if(medianFilter->ageHead == medianFilter->valueHead)
{ //if oldest node is also the smallest node, increment value head
medianFilter->valueHead = medianFilter->valueHead->nextValue;
}
if((medianFilter->ageHead == medianFilter->medianHead) ||
(medianFilter->ageHead->value > medianFilter->medianHead->value))
{ //prepare for median correction
medianFilter->medianHead = medianFilter->medianHead->prevValue;
}
//replace age head with new sample
newNode = medianFilter->ageHead;
newNode->value = sample;
//remove age head from list
medianFilter->ageHead->nextValue->prevValue = medianFilter->ageHead->prevValue;
medianFilter->ageHead->prevValue->nextValue = medianFilter->ageHead->nextValue;
//increment age head
medianFilter->ageHead = medianFilter->ageHead->nextAge;
//find new node position
it = medianFilter->valueHead; //set iterator as value head
for(i = 0; i < medianFilter->numNodes - 1; i++)
{
if(sample < it->value)
{
if(i == 0)
{ //replace value head if new node is the smallest
medianFilter->valueHead = newNode;
}
break;
}
it = it->nextValue;
}
//insert new node in list
it->prevValue->nextValue = newNode;
newNode->prevValue = it->prevValue;
it->prevValue = newNode;
newNode->nextValue = it;
//adjust median node
if(i >= (medianFilter->numNodes / 2))
{
medianFilter->medianHead = medianFilter->medianHead->nextValue;
}
return medianFilter->medianHead->value;
}

38
app/src/app_service/ecg_service/algo/MedianFilter.h

@ -0,0 +1,38 @@
/*
* MedianFilter.h
*
* Created on: May 19, 2018
* Author: alexandru.bogdan
*/
#ifndef MEDIANFILTER_H_
#define MEDIANFILTER_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct sMedianNode
{
int value; //sample value
struct sMedianNode *nextAge; //pointer to next oldest value
struct sMedianNode *nextValue; //pointer to next smallest value
struct sMedianNode *prevValue; //pointer to previous smallest value
}sMedianNode_t;
typedef struct
{
unsigned int numNodes; //median node buffer length
sMedianNode_t *medianBuffer; //median node buffer
sMedianNode_t *ageHead; //pointer to oldest value
sMedianNode_t *valueHead; //pointer to smallest value
sMedianNode_t *medianHead; //pointer to median value
}sMedianFilter_t;
int MEDIANFILTER_Init(sMedianFilter_t *medianFilter);
int MEDIANFILTER_Insert(sMedianFilter_t *medianFilter, int sample);
#ifdef __cplusplus
}
#endif
#endif

45
app/src/app_service/ecg_service/algo/iflytop_simple_filter.c

@ -206,4 +206,47 @@ float SmoothingFilter_Update(SmoothingFilter_t *filter, float vin) {
}
return filter->sum / filter->datanum;
}
}
void BaselineDriftRemoval_Init(BaselineDriftRemoval_t *filter, int windows_size, bool enable) {
if (windows_size > MEDIAN_FILTER_SIZE) {
windows_size = MEDIAN_FILTER_SIZE;
}
filter->windows_size = windows_size;
filter->datanum = 0;
filter->enable = enable;
for (int i = 0; i < windows_size; i++) {
filter->fifo[i] = 0;
}
}
float BaselineDriftRemoval_Update(BaselineDriftRemoval_t *filter, float vin) {
if (!filter->enable) return vin;
// memmove(filter->fifo, filter->fifo + 1, (filter->windows_size - 1) * sizeof(float));
filter->fifo[filter->windows_size - 1] = vin;
filter->datanum++;
if (filter->datanum > filter->windows_size) {
filter->datanum = filter->windows_size;
}
if (filter->datanum < filter->windows_size) {
return 0;
}
// ÇóÖÐÖµ
// memcpy(filter->processcache, filter->fifo, filter->windows_size * sizeof(float));
for (int i = 0; i < filter->windows_size; i++) {
for (int j = i + 1; j < filter->windows_size; j++) {
if (filter->processcache[i] > filter->processcache[j]) {
float temp = filter->processcache[i];
filter->processcache[i] = filter->processcache[j];
filter->processcache[j] = temp;
}
}
}
float mid = filter->processcache[filter->windows_size / 2];
float min_data_in_fifo = filter->fifo[filter->windows_size / 2];
return min_data_in_fifo - mid;
}

13
app/src/app_service/ecg_service/algo/iflytop_simple_filter.h

@ -94,4 +94,17 @@ void SmoothingFilter_Init(SmoothingFilter_t *filter, int windows_size, bool ena
float SmoothingFilter_Update(SmoothingFilter_t *filter, float vin);
#define MEDIAN_FILTER_SIZE 300
typedef struct {
float fifo[MEDIAN_FILTER_SIZE];
float processcache[MEDIAN_FILTER_SIZE];
bool enable;
int windows_size;
int datanum;
} BaselineDriftRemoval_t;
void BaselineDriftRemoval_Init(BaselineDriftRemoval_t *filter, int windows_size, bool enable);
float BaselineDriftRemoval_Update(BaselineDriftRemoval_t *filter, float vin);
#endif

5
app/src/app_service/ecg_service/algo/iflytop_simple_filter_ext.h

@ -25,4 +25,7 @@ typedef struct {
} NOTCHFilterExt_t;
void NOTCHFilterExt_init(NOTCHFilterExt_t *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS, int order, bool enable);
float NOTCHFilterExt_update(NOTCHFilterExt_t *filter, float v_in);
float NOTCHFilterExt_update(NOTCHFilterExt_t *filter, float v_in);

114
app/src/app_service/ecg_service/ecg_algo.c

@ -1,20 +1,30 @@
#include "ecg_algo.h"
#include "algo/MedianFilter.h"
#include "algo/iflytop_simple_filter_ext.h"
#include "algo/zdata_statistics.h"
#include "algo/zsimple_qrs.h"
#include "znordic.h"
LPFilterExt_t m_lp_filter;
HPFilterExt_t m_hp_filter;
NOTCHFilterExt_t m_notch_filter;
SmoothingFilter_t m_smoothingFilter;
#define REPORT_MEDIAN_WINDOWS_SIZE 101 // 必须是奇数
LPFilterExt_t m_lp_filter;
HPFilterExt_t m_hp_filter;
NOTCHFilterExt_t m_notch_filter;
SmoothingFilter_t m_report_smoothing_filter;
median_filter_t m_display_median_filter;
static sMedianFilter_t report_medianFilter;
static sMedianNode_t report_medianBuffer[REPORT_MEDIAN_WINDOWS_SIZE];
void BaselineDriftRemoval_Init(BaselineDriftRemoval_t *filter, int windows_size, bool enable);
float BaselineDriftRemoval_Update(BaselineDriftRemoval_t *filter, float vin);
int32_t m_data_statistics_buf[STATISTICS_BUF_SIZE]; //
zdata_statistics_t m_data_statistics;
zsimple_qrs_t m_qrs;
int32_t data_af_high_pass_filter;
int32_t reportdata;
int32_t displaydata;
@ -27,42 +37,66 @@ void ecg_algo_init() {
int32_t maxval;
int32_t minval;
int32_t nowval100;
void ecg_algo_process_data(int32_t indata) {
float data = indata;
data = HPFilterExt_update(&m_hp_filter, data);
data_af_high_pass_filter = data;
data = LPFilterExt_update(&m_lp_filter, data);
data = NOTCHFilterExt_update(&m_notch_filter, data);
data = SmoothingFilter_Update(&m_smoothingFilter, data);
static void display_data_processer(int32_t indata) {
// float data = indata;
// data = BaselineDriftRemoval_Update(&m_display_drift_filter, data); // 线
// data = median_filter_update(&m_display_median_filter, data);
// zdata_statistics_push(&m_data_statistics, data);
// if (zdata_statistics_is_full(&m_data_statistics)) {
// zsimple_qrs_process_data(&m_qrs, data, zdata_statistics_get_min(&m_data_statistics), zdata_statistics_get_max(&m_data_statistics),
// zdata_statistics_get_avg(&m_data_statistics));
// maxval = zdata_statistics_get_max(&m_data_statistics);
// minval = zdata_statistics_get_min(&m_data_statistics);
// int32_t nowvaloff = data - minval;
// nowval100 = 10 + nowvaloff * 80 / (maxval - minval);
// displaydata = nowval100;
// } else {
// maxval = 0;
// minval = 0;
// displaydata = 50;
// }
}
static void report_data_processer(int32_t indata) {
float data = indata;
int medianValue = MEDIANFILTER_Insert(&report_medianFilter, data);
data = indata - medianValue;
// data = BaselineDriftRemoval_Update(&m_report_drift_filter, data); // 线
data = SmoothingFilter_Update(&m_report_smoothing_filter, data);
reportdata = data;
}
zdata_statistics_push(&m_data_statistics, data);
if (zdata_statistics_is_full(&m_data_statistics)) {
zsimple_qrs_process_data(&m_qrs, data, zdata_statistics_get_min(&m_data_statistics), zdata_statistics_get_max(&m_data_statistics),
zdata_statistics_get_avg(&m_data_statistics));
/**
* @brief
*/
maxval = zdata_statistics_get_max(&m_data_statistics);
minval = zdata_statistics_get_min(&m_data_statistics);
int32_t nowvaloff = data - minval;
nowval100 = 10 + nowvaloff * 80 / (maxval - minval);
displaydata = nowval100;
} else {
maxval = 0;
minval = 0;
displaydata = 50;
}
void ecg_algo_process_data(int32_t indata) {
float data = indata;
data = HPFilterExt_update(&m_hp_filter, data); //
data = LPFilterExt_update(&m_lp_filter, data); //
data = NOTCHFilterExt_update(&m_notch_filter, data); //
// display_data_processer(data);
report_data_processer(data);
}
void ecg_algo_reset() {
LPFilterExt_init(&m_lp_filter, 40, SAMPLE_PERIOD_S, 5, true);
HPFilterExt_init(&m_hp_filter, 1, SAMPLE_PERIOD_S, 1, true);
NOTCHFilterExt_init(&m_notch_filter, 125, 2, SAMPLE_PERIOD_S, 2, true);
SmoothingFilter_Init(&m_smoothingFilter, 8, true);
LPFilterExt_init(&m_lp_filter, 40, SAMPLE_PERIOD_S, 4, true);
HPFilterExt_init(&m_hp_filter, 1, SAMPLE_PERIOD_S, 1, false);
NOTCHFilterExt_init(&m_notch_filter, 125, 2, SAMPLE_PERIOD_S, 2, false);
SmoothingFilter_Init(&m_report_smoothing_filter, 8, true);
{
memset(&report_medianFilter, 0, sizeof(report_medianFilter));
memset(report_medianBuffer, 0, sizeof(report_medianBuffer));
report_medianFilter.numNodes = REPORT_MEDIAN_WINDOWS_SIZE;
report_medianFilter.medianBuffer = report_medianBuffer;
MEDIANFILTER_Init(&report_medianFilter);
}
median_filter_init(&m_display_median_filter, 5);
zdata_statistics_clear(&m_data_statistics);
zsimple_qrs_clear();
@ -71,12 +105,6 @@ void ecg_algo_reset() {
int32_t ecg_algo_get_report_data() { return reportdata; }
int32_t ecg_algo_get_display_data() { return displaydata; }
int32_t ecg_algo_get_heart_rate() { return zsimple_qrs_get_heartrate(&m_qrs); }
int32_t ecg_algo_get_raw_data() { return data_af_high_pass_filter; }
int32_t ecg_algo_get_max_data() { return maxval; }
int32_t ecg_algo_get_min_data() { return minval; }
int32_t ecg_algo_get_peak2peak() { return maxval - minval; }
int32_t ecg_algo_get_data_after_high_pass_filter_data() { return data_af_high_pass_filter; }
// maxval
// minval

3
app/src/app_service/ecg_service/ecg_algo.h

@ -5,7 +5,7 @@
#include "app_basic_service/zapp.h"
#define SAMPLE_PERIOD_S ((float)(1.0 / SAMPLE_RATE))
#define STATISTICS_BUF_SIZE ((int32_t)(1.5 / SAMPLE_PERIOD_S))
#define STATISTICS_BUF_SIZE ((int32_t)(1 / SAMPLE_PERIOD_S))
void ecg_algo_init();
void ecg_algo_process_data(int32_t indata);
@ -15,7 +15,6 @@ int32_t ecg_algo_get_report_data();
int32_t ecg_algo_get_display_data();
int32_t ecg_algo_get_heart_rate();
int32_t ecg_algo_get_data_after_high_pass_filter_data();
int32_t ecg_algo_get_max_data();
int32_t ecg_algo_get_min_data();
int32_t ecg_algo_get_peak2peak();

5
app/src/app_service/ecg_service/ecg_service.c

@ -244,7 +244,7 @@ static void leadoff_state_process(ads129x_capture_data_t* capture_data) {
cnt = 0;
}
if (cnt > 10) {
if (cnt > 3) {
cnt = 0;
m_leadoffstate = false;
}
@ -268,7 +268,7 @@ static void ads1291_ready_pin_irq(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t a
// Éϱ¨
one_frame_t frame;
if (m_report_data_in_raw_mode_flag) {
frame.data = capture_data.ch1data * 0.5;
frame.data = capture_data.ch1data;
} else {
frame.data = ecg_algo_get_report_data();
}
@ -434,6 +434,7 @@ void ecg_service_set_in_test_mode(bool testmode) {
void ecg_service_set_report_data_in_raw_mode(bool rawmode) { m_report_data_in_raw_mode_flag = rawmode; }
bool ecg_leadoff_detect() {
return false;
if (m_testmode_flag) {
return false;
}

5
app/src/aproject_config/config.h

@ -7,7 +7,7 @@
#define CATEGORY "M1003" // 单导联
#define MANUFACTURER_NAME "iflytop"
#define FIRMWARE_VERSION (506)
#define FIRMWARE_VERSION (507)
#define BLESTACK_VERSION 1
#define BOOTLOADER_VERSION 1
#define HARDWARE_VERSION (2)
@ -32,7 +32,7 @@
#define MAX_STORAGE_SIZE (MAX_STORAGE_TIMEOUT_S * 400) // 存储最大限制为 (256-8)kbyte
#define MAX_FILE_NUM 10 // 最多存储条目数
#define SAMPLE_RATE 500 // 采样率
#define SAMPLE_PRECISION 16 // 采样精度
#define SAMPLE_PRECISION 32 // 采样精度
#define AUTOMATIC_SLEEP_TIME 30000 // 开机后自动休眠时间
#define SAMPLE_MIN_TIME_S (30.0) // 采样最小时间
#define LITTLE_DATA_BLOCK_FRAME_NUM 50 // 每次多少帧上报一次
@ -60,4 +60,3 @@
***********************************************************************************************************************/
#define ADS1291_SPI_INSTANCE 2

10
app/src/ble_data_processer_utils.c

@ -45,14 +45,8 @@ void report_ecg_data(app_event_t* data) {
reportpacket->sample_data_num = ECG_DATA_REPORT_FRAME_NUM;
for (int i = 0; i < ECG_DATA_REPORT_FRAME_NUM; i++) {
int32_t frame = data->val.ecg_data_report_event.ecgData->frame[i].data;
if (frame >= INT16_MAX) {
reportpacket->frame[i] = INT16_MAX;
} else if (frame <= INT16_MIN) {
reportpacket->frame[i] = INT16_MIN;
} else {
reportpacket->frame[i] = frame;
}
int32_t frame = data->val.ecg_data_report_event.ecgData->frame[i].data;
reportpacket->frame[i] = frame;
}
uint8_t leadoffstate = 0;
for (int i = 0; i < ECG_DATA_REPORT_FRAME_NUM; i++) {

2
ify_hrs_protocol

@ -1 +1 @@
Subproject commit c5a86174de8f27f4c8ab6c18dd131effa7b43ea5
Subproject commit 286f8bbe136357e72b72f7d3e2a5b0ad64e99710

88
test/MedianFilter.c

@ -0,0 +1,88 @@
/*
* MedianFilter.c
*
* Created on: May 19, 2018
* Author: alexandru.bogdan
*/
#include "MedianFilter.h"
int MEDIANFILTER_Init(sMedianFilter_t *medianFilter)
{
if(medianFilter && medianFilter->medianBuffer &&
(medianFilter->numNodes % 2) && (medianFilter->numNodes > 1))
{
//initialize buffer nodes
for(unsigned int i = 0; i < medianFilter->numNodes; i++)
{
medianFilter->medianBuffer[i].value = 0;
medianFilter->medianBuffer[i].nextAge = &medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes];
medianFilter->medianBuffer[i].nextValue = &medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes];
medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes].prevValue = &medianFilter->medianBuffer[i];
}
//initialize heads
medianFilter->ageHead = medianFilter->medianBuffer;
medianFilter->valueHead = medianFilter->medianBuffer;
medianFilter->medianHead = &medianFilter->medianBuffer[medianFilter->numNodes / 2];
return 0;
}
return -1;
}
int MEDIANFILTER_Insert(sMedianFilter_t *medianFilter, int sample)
{
unsigned int i;
sMedianNode_t *newNode, *it;
if(medianFilter->ageHead == medianFilter->valueHead)
{ //if oldest node is also the smallest node, increment value head
medianFilter->valueHead = medianFilter->valueHead->nextValue;
}
if((medianFilter->ageHead == medianFilter->medianHead) ||
(medianFilter->ageHead->value > medianFilter->medianHead->value))
{ //prepare for median correction
medianFilter->medianHead = medianFilter->medianHead->prevValue;
}
//replace age head with new sample
newNode = medianFilter->ageHead;
newNode->value = sample;
//remove age head from list
medianFilter->ageHead->nextValue->prevValue = medianFilter->ageHead->prevValue;
medianFilter->ageHead->prevValue->nextValue = medianFilter->ageHead->nextValue;
//increment age head
medianFilter->ageHead = medianFilter->ageHead->nextAge;
//find new node position
it = medianFilter->valueHead; //set iterator as value head
for(i = 0; i < medianFilter->numNodes - 1; i++)
{
if(sample < it->value)
{
if(i == 0)
{ //replace value head if new node is the smallest
medianFilter->valueHead = newNode;
}
break;
}
it = it->nextValue;
}
//insert new node in list
it->prevValue->nextValue = newNode;
newNode->prevValue = it->prevValue;
it->prevValue = newNode;
newNode->nextValue = it;
//adjust median node
if(i >= (medianFilter->numNodes / 2))
{
medianFilter->medianHead = medianFilter->medianHead->nextValue;
}
return medianFilter->medianHead->value;
}

38
test/MedianFilter.h

@ -0,0 +1,38 @@
/*
* MedianFilter.h
*
* Created on: May 19, 2018
* Author: alexandru.bogdan
*/
#ifndef MEDIANFILTER_H_
#define MEDIANFILTER_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct sMedianNode
{
int value; //sample value
struct sMedianNode *nextAge; //pointer to next oldest value
struct sMedianNode *nextValue; //pointer to next smallest value
struct sMedianNode *prevValue; //pointer to previous smallest value
}sMedianNode_t;
typedef struct
{
unsigned int numNodes; //median node buffer length
sMedianNode_t *medianBuffer; //median node buffer
sMedianNode_t *ageHead; //pointer to oldest value
sMedianNode_t *valueHead; //pointer to smallest value
sMedianNode_t *medianHead; //pointer to median value
}sMedianFilter_t;
int MEDIANFILTER_Init(sMedianFilter_t *medianFilter);
int MEDIANFILTER_Insert(sMedianFilter_t *medianFilter, int sample);
#ifdef __cplusplus
}
#endif
#endif

1
test/README.md

@ -0,0 +1 @@
pip install wfdb

27
test/test.c

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include "MedianFilter.h"
#define NUM_ELEMENTS 201
static sMedianFilter_t medianFilter;
static sMedianNode_t medianBuffer[NUM_ELEMENTS];
int main() {
medianFilter.numNodes = NUM_ELEMENTS;
medianFilter.medianBuffer = medianBuffer;
MEDIANFILTER_Init(&medianFilter);
while (1) {
int newValue = rand() % 100;
int medianValue = MEDIANFILTER_Insert(&medianFilter, newValue);
printf("New value: %d \tMedian value: %d\r\n", newValue, medianValue);
Sleep(1);
}
return 0;
}

129
test/test.py

@ -0,0 +1,129 @@
import csv
from datetime import datetime
import wfdb
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import medfilt
import pywt
fliter = int(0.8*500)
Initial_intercept_point = 0
Final_intercept_point = 2000
Give_up_size = int(fliter/2)
# Get dates, high, and low temperatures from file.
filename = 'sunyan.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
leaddatas= []
for row in reader:
try:
leaddata = int(row[0])
except ValueError:
print(leaddata, 'missing data')
else:
leaddatas.append(leaddata)
#读取文件结束
#bandpass filter
from scipy import signal
fs = 500 # 采样率为500 Hz
# 设计低通滤波器
cutoff_freq = 40 # 截止频率
order = 1 # 滤波器阶数
nyquist = 0.5 * fs
cutoff = cutoff_freq / nyquist
b, a = signal.butter(order, cutoff, btype='low')
# 应用低通滤波器
lowpass_ecg_leaddatas = signal.filtfilt(b, a, leaddatas)
# 设计高通滤波器
cutoff_freq_high = 0.5 # 截止频率
order_high = 1 # 滤波器阶数
#nyquist = 0.5 * fs
cutoff_high = cutoff_freq_high / nyquist
b, a = signal.butter(order_high, cutoff_high, btype='high')
# 应用高通滤波器
filtered_ecg_signal_leaddatas = signal.filtfilt(b, a, lowpass_ecg_leaddatas)
'''
# 使用小波变换,效果不好
wavelet = 'sym5' # 选择小波类型,sym5是一种对称小波,常用于ECG信号
levels = 4 # 分解级数
coeffs = pywt.wavedec(filtered_ecg_signal_leaddatas, wavelet, level=levels)
# 对小波系数进行阈值处理,这有助于去噪
threshold = 0.2 * np.max(coeffs[-1])
coeffs = [pywt.threshold(c, threshold, mode='soft') for c in coeffs]
reconstructed_signal = pywt.waverec(coeffs, wavelet)
'''
'''
# 小波分解
wavelet = 'db4'
coeffs = pywt.wavedec(filtered_ecg_signal_leaddatas, wavelet, level=4)
# 对高频系数进行阈值处理(软阈值)
sigma = np.median(np.abs(coeffs[-1])) / 0.6745 # 估计噪声标准差
threshold = sigma * np.sqrt(2 * np.log(len(filtered_ecg_signal_leaddatas)))
# 对各层系数进行阈值处理
denoised_coeffs = [pywt.threshold(c, threshold, mode='soft') for c in coeffs]
# 重建信号
denoised_signal = pywt.waverec(denoised_coeffs, wavelet)
'''
# Define the window size for moving average (adjust as needed)
window_size = 2
# Perform moving average filtering
moving_avg_ecg1 = np.convolve(lowpass_ecg_leaddatas, np.ones(window_size)/window_size, mode='valid')
#record = wfdb.rdrecord('mit-bih-arrhythmia-database-1.0.0/100', sampfrom=0, sampto=25000, physical=True, channels=[0, ])
#Original_ECG = record.p_signal[Initial_intercept_point:Final_intercept_point].flatten()
Original_ECG = moving_avg_ecg1
ECG_baseline = medfilt(Original_ECG, fliter+1)
Totality_Bias = np.sum(ECG_baseline[Give_up_size:-Give_up_size])/(Final_intercept_point-Initial_intercept_point-fliter)
Filtered_ECG = Original_ECG - ECG_baseline
Final_Filtered_ECG = Filtered_ECG[Give_up_size:-Give_up_size]-Totality_Bias
plt.figure(figsize=(100, 8))
plt.subplot(2, 1, 1)
plt.ylabel("Original ECG signal")
plt.plot(leaddatas)#输出原图像leaddatas
plt.subplot(2, 1, 2)
plt.ylabel("Filtered ECG signal")
plt.plot(Original_ECG)#基线滤波结果
plt.show()
#心率计算
import biosppy.signals.ecg as ecg
rpeaks = ecg.hamilton_segmenter(signal=Final_Filtered_ECG, sampling_rate=500)
def calculate_heart_rate(r_peaks, sampling_rate):
# 计算相邻R波之间的时间间隔(单位:秒)
rr_intervals = np.diff(r_peaks) / sampling_rate
# 将时间间隔转换为每分钟的心跳数(bpm)
heart_rates = 60 / rr_intervals
return heart_rates
# 计算心率
heart_rates = calculate_heart_rate(rpeaks, sampling_rate=500)
print("Heart rates (bpm):", heart_rates)
Loading…
Cancel
Save