18 changed files with 16117 additions and 262 deletions
-
3.vscode/settings.json
-
5README.md
-
186app/app.uvoptx
-
20app/app.uvprojx
-
17app/src/algo/baseline_filtering.c
-
1app/src/algo/baseline_filtering.h
-
5app/src/algo/fix_delay_fifo.h
-
224app/src/algo/iflytop_simple_filter.c
-
96app/src/algo/iflytop_simple_filter.h
-
57app/src/algo/iflytop_simple_filter_ext.c
-
31app/src/algo/iflytop_simple_filter_ext.h
-
109app/src/basic/filters.c
-
53app/src/basic/filters.h
-
2app/src/basic/version.h
-
2app/src/board/board.h
-
10app/src/heart_wave_sample_service.c
-
15558release/V30/three_lead_ecg.hex
-
BINrelease/V30/three_lead_ecg.zip
@ -0,0 +1,224 @@ |
|||||
|
|
||||
|
#include "iflytop_simple_filter.h" |
||||
|
|
||||
|
#include <math.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define PI 3.141592653 |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* LOW PASS FILTER |
||||
|
********************************************************************************************************/ |
||||
|
|
||||
|
void LPFilter_Init(LPFilter_t *filter, float cutoffFreqHz, float sampleTimeS, bool enable) { |
||||
|
float RC = 0.0; |
||||
|
RC = 1.0 / (2 * PI * cutoffFreqHz); |
||||
|
filter->coef[0] = sampleTimeS / (sampleTimeS + RC); |
||||
|
filter->coef[1] = RC / (sampleTimeS + RC); |
||||
|
|
||||
|
filter->v_out[0] = 0.0; |
||||
|
filter->v_out[1] = 0.0; |
||||
|
|
||||
|
filter->enable = enable; |
||||
|
} |
||||
|
|
||||
|
float LPFilter_Update(LPFilter_t *filter, float v_in) { |
||||
|
if (filter->enable == false) return v_in; |
||||
|
|
||||
|
filter->v_out[1] = filter->v_out[0]; |
||||
|
filter->v_out[0] = (filter->coef[0] * v_in) + (filter->coef[1] * filter->v_out[1]); |
||||
|
|
||||
|
return (filter->v_out[0]); |
||||
|
} |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* HIGH PASS FILTER |
||||
|
********************************************************************************************************/ |
||||
|
void HPFilter_Init(HPFilter_t *filter, float cutoffFreqHz, float sampleTimeS, bool enable) { |
||||
|
float RC = 0.0; |
||||
|
RC = 1.0 / (2 * PI * cutoffFreqHz); |
||||
|
|
||||
|
filter->coef = RC / (sampleTimeS + RC); |
||||
|
|
||||
|
filter->v_in[0] = 0.0; |
||||
|
filter->v_in[1] = 0.0; |
||||
|
|
||||
|
filter->v_out[0] = 0.0; |
||||
|
filter->v_out[1] = 0.0; |
||||
|
|
||||
|
filter->enable = enable; |
||||
|
} |
||||
|
|
||||
|
float HPFilter_Update(HPFilter_t *filter, float v_in) { |
||||
|
if (filter->enable == false) return v_in; |
||||
|
|
||||
|
filter->v_in[1] = filter->v_in[0]; |
||||
|
filter->v_in[0] = v_in; |
||||
|
|
||||
|
filter->v_out[1] = filter->v_out[0]; |
||||
|
|
||||
|
filter->v_out[0] = filter->coef * (filter->v_in[0] - filter->v_in[1] + filter->v_out[1]); |
||||
|
|
||||
|
return (filter->v_out[0]); |
||||
|
} |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* BAND PASS FILTER |
||||
|
********************************************************************************************************/ |
||||
|
|
||||
|
void PBFilter_Init(PBFilter_t *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS, bool enable) { |
||||
|
LPFilter_Init(&filter->lpf, LPF_cutoffFreqHz, sampleTimeS, enable); |
||||
|
HPFilter_Init(&filter->hpf, HPF_cutoffFreqHz, sampleTimeS, enable); |
||||
|
filter->out_in = 0.0; |
||||
|
filter->enable = enable; |
||||
|
} |
||||
|
|
||||
|
float PBFilter_Update(PBFilter_t *filter, float v_in) { |
||||
|
if (filter->enable == false) return v_in; |
||||
|
|
||||
|
filter->out_in = HPFilter_Update(&filter->hpf, v_in); |
||||
|
|
||||
|
filter->out_in = LPFilter_Update(&filter->lpf, filter->out_in); |
||||
|
|
||||
|
return (filter->out_in); |
||||
|
} |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* NOTCH FILTER |
||||
|
********************************************************************************************************/ |
||||
|
|
||||
|
void NOTCHFilter_Init(NOTCHFilter_t *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS, bool enable) { |
||||
|
// filter frequency to angular (rad/s) |
||||
|
float w0_rps = 2.0 * PI * centerFreqHz; |
||||
|
float ww_rps = 2.0 * PI * notchWidthHz; |
||||
|
|
||||
|
// pre warp center frequency |
||||
|
float w0_pw_rps = (2.0f / sampleTimeS) * tanf(0.5f * w0_rps * sampleTimeS); |
||||
|
|
||||
|
// computing filter coefficients |
||||
|
|
||||
|
filter->alpha = 4.0f + w0_rps * w0_pw_rps * sampleTimeS * sampleTimeS; |
||||
|
filter->beta = 2.0f * ww_rps * sampleTimeS; |
||||
|
|
||||
|
// clearing input and output buffers |
||||
|
|
||||
|
for (uint8_t n = 0; n < 3; n++) { |
||||
|
filter->vin[n] = 0; |
||||
|
filter->vout[n] = 0; |
||||
|
} |
||||
|
filter->enable = enable; |
||||
|
} |
||||
|
|
||||
|
float NOTCHFilter_Update(NOTCHFilter_t *filter, float vin) { |
||||
|
if (filter->enable == false) return vin; |
||||
|
|
||||
|
// shifting samples |
||||
|
filter->vin[2] = filter->vin[1]; |
||||
|
filter->vin[1] = filter->vin[0]; |
||||
|
|
||||
|
filter->vout[2] = filter->vout[1]; |
||||
|
filter->vout[1] = filter->vout[0]; |
||||
|
|
||||
|
filter->vin[0] = vin; |
||||
|
|
||||
|
// compute new output |
||||
|
filter->vout[0] = (filter->alpha * filter->vin[0] + 2.0f * (filter->alpha - 8.0f) * filter->vin[1] + filter->alpha * filter->vin[2] - |
||||
|
(2.0f * (filter->alpha - 8.0f) * filter->vout[1] + (filter->alpha - filter->beta) * filter->vout[2])) / |
||||
|
(filter->alpha + filter->beta); |
||||
|
|
||||
|
return (filter->vout[0]); |
||||
|
} |
||||
|
#if 0 |
||||
|
typedef struct { |
||||
|
float val[MEDIAN_FILTER_MAX_SIZE]; |
||||
|
int valStartPos; |
||||
|
int valEndPos; |
||||
|
int medianSize; |
||||
|
} median_filter_t; |
||||
|
#endif |
||||
|
|
||||
|
void median_filter_init(median_filter_t *filter, int medianSize) { |
||||
|
filter->medianSize = medianSize; |
||||
|
filter->numVal = 0; |
||||
|
for (int i = 0; i < MEDIAN_FILTER_MAX_SIZE; i++) { |
||||
|
filter->val[i] = 0; |
||||
|
} |
||||
|
} |
||||
|
float median_filter_update(median_filter_t *filter, float val) { |
||||
|
if (filter->numVal >= filter->medianSize) { |
||||
|
memmove(filter->val, filter->val + 1, (filter->medianSize - 1) * sizeof(float)); |
||||
|
filter->numVal--; |
||||
|
} |
||||
|
|
||||
|
filter->val[filter->numVal] = val; |
||||
|
filter->numVal++; |
||||
|
|
||||
|
for (int i = 0; i < filter->numVal; i++) { |
||||
|
filter->temp[i] = filter->val[i]; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < filter->numVal; i++) { |
||||
|
for (int j = i + 1; j < filter->numVal; j++) { |
||||
|
if (filter->temp[i] > filter->temp[j]) { |
||||
|
float temp = filter->temp[i]; |
||||
|
filter->temp[i] = filter->temp[j]; |
||||
|
filter->temp[j] = temp; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (filter->numVal % 2 == 0) { |
||||
|
return (filter->temp[filter->numVal / 2 - 1] + filter->temp[filter->numVal / 2]) / 2; |
||||
|
} else { |
||||
|
return filter->temp[filter->numVal / 2]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
|
@ -0,0 +1,96 @@ |
|||||
|
#pragma once |
||||
|
#include <stdbool.h> |
||||
|
|
||||
|
// #ifdef __cplusplus |
||||
|
// extern "C" { |
||||
|
// #endif |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* LOW PASS FILTER |
||||
|
********************************************************************************************************/ |
||||
|
typedef struct { |
||||
|
float coef[2]; |
||||
|
float v_out[2]; |
||||
|
bool enable; |
||||
|
} LPFilter_t; |
||||
|
|
||||
|
void LPFilter_Init(LPFilter_t *filter, float cutoffFreqHz, float sampleTimeS, bool enable); |
||||
|
float LPFilter_Update(LPFilter_t *filter, float v_in); |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* HIGH PASS FILTER |
||||
|
********************************************************************************************************/ |
||||
|
typedef struct { |
||||
|
float coef; |
||||
|
float v_out[2]; |
||||
|
float v_in[2]; |
||||
|
bool enable; |
||||
|
|
||||
|
} HPFilter_t; |
||||
|
|
||||
|
void HPFilter_Init(HPFilter_t *filter, float cutoffFreqHz, float sampleTimeS, bool enable); |
||||
|
float HPFilter_Update(HPFilter_t *filter, float v_in); |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* BAND PASS FILTER |
||||
|
********************************************************************************************************/ |
||||
|
|
||||
|
typedef struct { |
||||
|
LPFilter_t lpf; |
||||
|
HPFilter_t hpf; |
||||
|
float out_in; |
||||
|
bool enable; |
||||
|
|
||||
|
} PBFilter_t; |
||||
|
|
||||
|
void PBFilter_Init(PBFilter_t *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS, bool enable); |
||||
|
float PBFilter_Update(PBFilter_t *filter, float v_in); |
||||
|
|
||||
|
/******************************************************************************************************** |
||||
|
* NOTCH FILTER |
||||
|
********************************************************************************************************/ |
||||
|
|
||||
|
typedef struct { |
||||
|
float alpha; |
||||
|
float beta; |
||||
|
|
||||
|
float vin[3]; |
||||
|
float vout[3]; |
||||
|
bool enable; |
||||
|
|
||||
|
} NOTCHFilter_t; |
||||
|
|
||||
|
void NOTCHFilter_Init(NOTCHFilter_t *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS, bool enable); |
||||
|
float NOTCHFilter_Update(NOTCHFilter_t *filter, float vin); |
||||
|
|
||||
|
// #ifdef __cplusplus |
||||
|
// } |
||||
|
// #endif |
||||
|
#define MEDIAN_FILTER_MAX_SIZE 200 |
||||
|
|
||||
|
typedef struct { |
||||
|
float val[MEDIAN_FILTER_MAX_SIZE]; |
||||
|
float temp[MEDIAN_FILTER_MAX_SIZE]; |
||||
|
|
||||
|
int medianSize; |
||||
|
int numVal; |
||||
|
} median_filter_t; |
||||
|
|
||||
|
void median_filter_init(median_filter_t *filter, int medianSize); |
||||
|
float median_filter_update(median_filter_t *filter, float val); |
||||
|
|
||||
|
|
||||
|
#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); |
||||
|
|
||||
|
|
@ -0,0 +1,57 @@ |
|||||
|
#include "iflytop_simple_filter_ext.h" |
||||
|
|
||||
|
void LPFilterExt_init(LPFilterExt_t *filter, float cutoffFreqHz, float sampleTimeS, int order, bool enable) { |
||||
|
if (order > FILTER_MAX_ORDER) { |
||||
|
order = FILTER_MAX_ORDER; |
||||
|
} |
||||
|
|
||||
|
filter->order = order; |
||||
|
for (int i = 0; i < order; i++) { |
||||
|
LPFilter_Init(&filter->lp_filter[i], cutoffFreqHz, sampleTimeS, enable); |
||||
|
} |
||||
|
} |
||||
|
float LPFilterExt_update(LPFilterExt_t *filter, float v_in) { |
||||
|
float v_out = v_in; |
||||
|
for (int i = 0; i < filter->order; i++) { |
||||
|
v_out = LPFilter_Update(&filter->lp_filter[i], v_out); |
||||
|
} |
||||
|
return v_out; |
||||
|
} |
||||
|
|
||||
|
void HPFilterExt_init(HPFilterExt_t *filter, float cutoffFreqHz, float sampleTimeS, int order, bool enable) { |
||||
|
if (order > FILTER_MAX_ORDER) { |
||||
|
order = FILTER_MAX_ORDER; |
||||
|
} |
||||
|
|
||||
|
filter->order = order; |
||||
|
|
||||
|
for (int i = 0; i < order; i++) { |
||||
|
HPFilter_Init(&filter->hp_filter[i], cutoffFreqHz, sampleTimeS, enable); |
||||
|
} |
||||
|
} |
||||
|
float HPFilterExt_update(HPFilterExt_t *filter, float v_in) { |
||||
|
float v_out = v_in; |
||||
|
for (int i = 0; i < filter->order; i++) { |
||||
|
v_out = HPFilter_Update(&filter->hp_filter[i], v_out); |
||||
|
} |
||||
|
return v_out; |
||||
|
} |
||||
|
|
||||
|
void NOTCHFilterExt_init(NOTCHFilterExt_t *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS, int order, bool enable) { |
||||
|
if (order > FILTER_MAX_ORDER) { |
||||
|
order = FILTER_MAX_ORDER; |
||||
|
} |
||||
|
|
||||
|
filter->order = order; |
||||
|
|
||||
|
for (int i = 0; i < order; i++) { |
||||
|
NOTCHFilter_Init(&filter->notch_filter[i], centerFreqHz, notchWidthHz, sampleTimeS, enable); |
||||
|
} |
||||
|
} |
||||
|
float NOTCHFilterExt_update(NOTCHFilterExt_t *filter, float v_in) { |
||||
|
float v_out = v_in; |
||||
|
for (int i = 0; i < filter->order; i++) { |
||||
|
v_out = NOTCHFilter_Update(&filter->notch_filter[i], v_out); |
||||
|
} |
||||
|
return v_out; |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
#pragma once |
||||
|
#include "iflytop_simple_filter.h" |
||||
|
|
||||
|
#define FILTER_MAX_ORDER 20 |
||||
|
typedef struct { |
||||
|
LPFilter_t lp_filter[FILTER_MAX_ORDER]; |
||||
|
int order; |
||||
|
} LPFilterExt_t; |
||||
|
|
||||
|
void LPFilterExt_init(LPFilterExt_t *filter, float cutoffFreqHz, float sampleTimeS, int order, bool enable); |
||||
|
float LPFilterExt_update(LPFilterExt_t *filter, float v_in); |
||||
|
|
||||
|
typedef struct { |
||||
|
HPFilter_t hp_filter[FILTER_MAX_ORDER]; |
||||
|
int order; |
||||
|
} HPFilterExt_t; |
||||
|
|
||||
|
void HPFilterExt_init(HPFilterExt_t *filter, float cutoffFreqHz, float sampleTimeS, int order, bool enable); |
||||
|
float HPFilterExt_update(HPFilterExt_t *filter, float v_in); |
||||
|
|
||||
|
|
||||
|
typedef struct { |
||||
|
NOTCHFilter_t notch_filter[FILTER_MAX_ORDER]; |
||||
|
int order; |
||||
|
} 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); |
||||
|
|
||||
|
|
||||
|
|
@ -1,56 +1,3 @@ |
|||||
#pragma once |
#pragma once |
||||
#include <stdint.h> |
#include <stdint.h> |
||||
|
|
||||
/******************************************************************************************************** |
|
||||
* LOW PASS FILTER |
|
||||
********************************************************************************************************/ |
|
||||
typedef struct { |
|
||||
float coef[2]; |
|
||||
float v_out[2]; |
|
||||
} LPFilter; |
|
||||
|
|
||||
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS); |
|
||||
float LPFilter_Update(LPFilter *filter, float v_in); |
|
||||
|
|
||||
/******************************************************************************************************** |
|
||||
* HIGH PASS FILTER |
|
||||
********************************************************************************************************/ |
|
||||
typedef struct { |
|
||||
float coef; |
|
||||
float v_out[2]; |
|
||||
float v_in[2]; |
|
||||
|
|
||||
} HPFilter; |
|
||||
|
|
||||
void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS); |
|
||||
float HPFilter_Update(HPFilter *filter, float v_in); |
|
||||
|
|
||||
/******************************************************************************************************** |
|
||||
* BAND PASS FILTER |
|
||||
********************************************************************************************************/ |
|
||||
|
|
||||
typedef struct { |
|
||||
LPFilter lpf; |
|
||||
HPFilter hpf; |
|
||||
float out_in; |
|
||||
} PBFilter; |
|
||||
|
|
||||
void PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS); |
|
||||
float PBFilter_Update(PBFilter *filter, float v_in); |
|
||||
|
|
||||
/******************************************************************************************************** |
|
||||
* NOTCH FILTER |
|
||||
********************************************************************************************************/ |
|
||||
|
|
||||
typedef struct { |
|
||||
float alpha; |
|
||||
float beta; |
|
||||
|
|
||||
float vin[3]; |
|
||||
float vout[3]; |
|
||||
|
|
||||
} NOTCHFilter; |
|
||||
|
|
||||
void NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS); |
|
||||
float NOTCHFilter_Update(NOTCHFilter *filter, float vin); |
|
||||
|
|
15558
release/V30/three_lead_ecg.hex
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue