19 changed files with 595 additions and 62 deletions
-
24app/app.uvoptx
-
20app/app.uvprojx
-
2app/src/app_basic_service/basic/event.h
-
88app/src/app_service/ecg_service/algo/MedianFilter.c
-
38app/src/app_service/ecg_service/algo/MedianFilter.h
-
45app/src/app_service/ecg_service/algo/iflytop_simple_filter.c
-
13app/src/app_service/ecg_service/algo/iflytop_simple_filter.h
-
5app/src/app_service/ecg_service/algo/iflytop_simple_filter_ext.h
-
114app/src/app_service/ecg_service/ecg_algo.c
-
3app/src/app_service/ecg_service/ecg_algo.h
-
5app/src/app_service/ecg_service/ecg_service.c
-
5app/src/aproject_config/config.h
-
10app/src/ble_data_processer_utils.c
-
2ify_hrs_protocol
-
88test/MedianFilter.c
-
38test/MedianFilter.h
-
1test/README.md
-
27test/test.c
-
129test/test.py
@ -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; |
||||
|
} |
@ -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 +1 @@ |
|||||
Subproject commit c5a86174de8f27f4c8ab6c18dd131effa7b43ea5 |
|
||||
|
Subproject commit 286f8bbe136357e72b72f7d3e2a5b0ad64e99710 |
@ -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; |
||||
|
} |
@ -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 |
@ -0,0 +1 @@ |
|||||
|
pip install wfdb |
@ -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; |
||||
|
} |
@ -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) |
||||
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue