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