You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
11 KiB
373 lines
11 KiB
#include "Pan_Tompkins_detect.h"
|
|
|
|
/* y(nT) = 1.875y(nT – T) – 0.9219y(nT – 2T) + x (nT) – x(nT – 2T) */
|
|
int TwoPoleRecursive(int data)
|
|
{
|
|
static int xnt, xm1, xm2, ynt, ym1, ym2 = 0;
|
|
|
|
xnt = data;
|
|
ynt = (ym1 + (ym1 >> 1) + (ym1 >> 2) + (ym1 >> 3)) + // 1.875 = 1 + 1/2 + 1/4 + 1/8
|
|
(((ym2 >> 1) + (ym2 >> 2) + (ym2 >> 3) + (ym2 >> 5) + (ym2 >> 6)) + xnt - xm2); // 0.916 = 1 + 1/2 + 1/4 + 1/8 + 1/32 + 1/64
|
|
xm2 = xm1;
|
|
xm1 = xnt;
|
|
xm2 = ym1;
|
|
ym2 = ym1;
|
|
ym1 = ynt;
|
|
return ynt;
|
|
}
|
|
|
|
/* y(nT) = 2y(nT – T) – y(nT – 2T) + x(nT) – 2x(nT – 6T) + x(nT – 12T) */
|
|
int LowPassFilter(int data)
|
|
{
|
|
static int y1 = 0, y2 = 0, x[26], n = 12;
|
|
int y0;
|
|
|
|
x[n] = x[n + 13] = data;
|
|
y0 = (y1 << 1) - y2 + x[n] - (x[n + 6] << 1) + x[n + 12];
|
|
y2 = y1;
|
|
y1 = y0;
|
|
y0 >>= 5;
|
|
if(--n < 0){
|
|
n = 12;
|
|
}
|
|
return y0;
|
|
}
|
|
|
|
/* p(nT) = x(nT – 16T) – 32 [y(nT – T) + x(nT) – x(nT – 32T)] */
|
|
int HighPassFilter(int data)
|
|
{
|
|
static int y1 = 0, x[66], n = 32;
|
|
int y0;
|
|
|
|
x[n] = x[n + 33] = data;
|
|
y0 = y1 + x[n] - x[n + 32];
|
|
y1 = y0;
|
|
if(--n < 0){
|
|
n = 32;
|
|
}
|
|
return (x[n + 16] - (y0 >> 5));
|
|
}
|
|
|
|
/* y = 1/8 (2x( nT) + x( nT - T) - x( nT - 3T) - 2x( nT - 4T)) */
|
|
int Derivative(int data)
|
|
{
|
|
int y;
|
|
static int x_derv[4];
|
|
|
|
y = (data << 1) + x_derv[3] - x_derv[1] - ( x_derv[0] << 1);
|
|
y >>= 3;
|
|
for(int i = 0; i < 3; ++i){
|
|
x_derv[i] = x_derv[i + 1];
|
|
}
|
|
x_derv[3] = data;
|
|
return y;
|
|
}
|
|
|
|
int Squar(int data)
|
|
{
|
|
return (data * data);
|
|
}
|
|
|
|
/* y(nT) = 1/N [x(nT – (N – 1)T) + x(nT – (N – 2)T) +...+ x(nT)] */
|
|
int MovingWindowIntegral(int data)
|
|
{
|
|
//static const int WINDOW_SIZE = SAMPLING_RATE * 0.2;
|
|
#define WINDOW_SIZE 72
|
|
|
|
static int x[WINDOW_SIZE], ptr = 0;
|
|
static long sum = 0;
|
|
long ly;
|
|
int y;
|
|
|
|
if(++ptr == WINDOW_SIZE){
|
|
ptr = 0;
|
|
}
|
|
sum -= x[ptr];
|
|
sum += data;
|
|
x[ptr] = data;
|
|
ly = sum >> 5;
|
|
uint32_t MAX_INTEGRAL = 4096;//32400;
|
|
if(ly > MAX_INTEGRAL){
|
|
y = MAX_INTEGRAL;
|
|
}
|
|
else{
|
|
y = (int)ly;
|
|
}
|
|
return (y);
|
|
}
|
|
|
|
SignalPoint ThresholdCalculate(int sample,float value,int bandpass,int square,int integral)
|
|
{
|
|
//static const int QRS_TIME = SAMPLING_RATE * 0.1;
|
|
//static const int SEARCH_BACK_TIME = SAMPLING_RATE * 1.66f;
|
|
#define QRS_TIME 36
|
|
#define SEARCH_BACK_TIME 598
|
|
|
|
static int bandpass_buffer[SEARCH_BACK_TIME],integral_buffer[SEARCH_BACK_TIME];
|
|
static SignalPoint peak_buffer[SEARCH_BACK_TIME];
|
|
static int square_buffer[QRS_TIME];
|
|
static long unsigned last_qrs = 0, last_slope = 0, current_slope = 0;
|
|
static int peak_i = 0, peak_f = 0, threshold_i1 = 0, threshold_i2 = 0, threshold_f1 = 0, threshold_f2 = 0, spk_i = 0, spk_f = 0, npk_i = 0, npk_f = 0;
|
|
static bool qrs, regular = true, prev_regular;
|
|
static int rr1[8]={0}, rr2[8]={0}, rravg1, rravg2, rrlow = 0, rrhigh = 0, rrmiss = 0;
|
|
|
|
SignalPoint result;
|
|
result.index = -1;
|
|
|
|
peak_buffer[sample%SEARCH_BACK_TIME].index = sample;
|
|
peak_buffer[sample%SEARCH_BACK_TIME].value = value;
|
|
bandpass_buffer[sample%SEARCH_BACK_TIME] = bandpass;
|
|
integral_buffer[sample%SEARCH_BACK_TIME] = integral;
|
|
square_buffer[sample%QRS_TIME] = square;
|
|
|
|
// If the current signal is above one of the thresholds (integral or filtered signal), it's a peak candidate.
|
|
if(integral >= threshold_i1 || bandpass >= threshold_f1){
|
|
peak_i = integral;
|
|
peak_f = bandpass;
|
|
}
|
|
|
|
// If both the integral and the signal are above their thresholds, they're probably signal peaks.
|
|
if((integral >= threshold_i1) && (bandpass >= threshold_f1)){
|
|
// There's a 200ms latency. If the new peak respects this condition, we can keep testing.
|
|
if(sample > last_qrs + SAMPLING_RATE*0.2f){
|
|
//if(sample > last_qrs + (SAMPLING_RATE*0.2f)){
|
|
// If it respects the 200ms latency, but it doesn't respect the 360ms latency, we check the slope.
|
|
if(sample <= last_qrs + (long unsigned int)(0.36*SAMPLING_RATE)){
|
|
// The squared slope is "M" shaped. So we have to check nearby samples to make sure we're really looking
|
|
// at its peak value, rather than a low one.
|
|
int current = sample;
|
|
current_slope = 0;
|
|
for(int j = current - QRS_TIME; j <= current; ++j){
|
|
if(square_buffer[j%QRS_TIME] > current_slope){
|
|
current_slope = square_buffer[j%QRS_TIME];
|
|
}
|
|
}
|
|
//current_slope = square;
|
|
|
|
if(current_slope <= (int)(last_slope/2)){
|
|
qrs = false;
|
|
//return qrs;
|
|
}
|
|
|
|
else{
|
|
spk_i = 0.125*peak_i + 0.875*spk_i;
|
|
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
|
|
threshold_i2 = 0.5*threshold_i1;
|
|
|
|
spk_f = 0.125*peak_f + 0.875*spk_f;
|
|
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
|
|
threshold_f2 = 0.5*threshold_f1;
|
|
|
|
last_slope = current_slope;
|
|
qrs = true;
|
|
|
|
result.value = value;
|
|
result.index = sample;
|
|
}
|
|
}
|
|
// If it was above both thresholds and respects both latency periods, it certainly is a R peak.
|
|
else{
|
|
int current = sample;
|
|
current_slope = 0;
|
|
for(int j = current - QRS_TIME; j <= current; ++j){
|
|
if(square_buffer[j%QRS_TIME] > current_slope){
|
|
current_slope = square_buffer[j%QRS_TIME];
|
|
}
|
|
}
|
|
//current_slope = square;
|
|
|
|
spk_i = 0.125*peak_i + 0.875*spk_i;
|
|
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
|
|
threshold_i2 = 0.5*threshold_i1;
|
|
|
|
spk_f = 0.125*peak_f + 0.875*spk_f;
|
|
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
|
|
threshold_f2 = 0.5*threshold_f1;
|
|
|
|
last_slope = current_slope;
|
|
qrs = true;
|
|
|
|
result.value = value;
|
|
result.index = sample;
|
|
}
|
|
}
|
|
// If the new peak doesn't respect the 200ms latency, it's noise. Update thresholds and move on to the next sample.
|
|
else{
|
|
peak_i = integral;
|
|
npk_i = 0.125*peak_i + 0.875*npk_i;
|
|
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
|
|
threshold_i2 = 0.5*threshold_i1;
|
|
peak_f = bandpass;
|
|
npk_f = 0.125*peak_f + 0.875*npk_f;
|
|
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
|
|
threshold_f2 = 0.5*threshold_f1;
|
|
qrs = false;
|
|
/*outputSignal[current] = qrs;
|
|
if (sample > DELAY + BUFFSIZE)
|
|
output(outputSignal[0]);
|
|
continue;*/
|
|
|
|
//return qrs;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// If a QRS complex was detected, the RR-averages must be updated.
|
|
if(qrs){
|
|
// Add the newest RR-interval to the buffer and get the new average.
|
|
rravg1 = 0;
|
|
for (int i = 0; i < 7; ++i){
|
|
rr1[i] = rr1[i+1];
|
|
rravg1 += rr1[i];
|
|
}
|
|
rr1[7] = sample - last_qrs;
|
|
last_qrs = sample;
|
|
rravg1 += rr1[7];
|
|
rravg1 *= 0.125;
|
|
|
|
// If the newly-discovered RR-average is normal, add it to the "normal" buffer and get the new "normal" average.
|
|
// Update the "normal" beat parameters.
|
|
if ( (rr1[7] >= rrlow) && (rr1[7] <= rrhigh) ){
|
|
rravg2 = 0;
|
|
for (int i = 0; i < 7; ++i){
|
|
rr2[i] = rr2[i+1];
|
|
rravg2 += rr2[i];
|
|
}
|
|
rr2[7] = rr1[7];
|
|
rravg2 += rr2[7];
|
|
rravg2 *= 0.125;
|
|
rrlow = 0.92*rravg2;
|
|
rrhigh = 1.16*rravg2;
|
|
rrmiss = 1.66*rravg2;
|
|
}
|
|
|
|
prev_regular = regular;
|
|
if(rravg1 == rravg2){
|
|
regular = true;
|
|
}
|
|
// If the beat had been normal but turned odd, change the thresholds.
|
|
else{
|
|
regular = false;
|
|
if (prev_regular){
|
|
threshold_i1 /= 2;
|
|
threshold_f1 /= 2;
|
|
}
|
|
}
|
|
}
|
|
// If no R-peak was detected, it's important to check how long it's been since the last detection.
|
|
else{
|
|
int current = sample;
|
|
// If no R-peak was detected for too long, use the lighter thresholds and do a back search.
|
|
// However, the back search must respect the 200ms limit and the 360ms one (check the slope).
|
|
if((sample - last_qrs > (long unsigned int)rrmiss) && (sample > last_qrs + SAMPLING_RATE*0.2f)){
|
|
//if((sample - last_qrs > (long unsigned int)rrmiss) && (sample > last_qrs + (SAMPLING_RATE*0.2f))){
|
|
|
|
// If over SEARCH_BACK_TIME of QRS complex
|
|
if((sample - last_qrs) > SEARCH_BACK_TIME){
|
|
last_qrs = sample;
|
|
//return result;
|
|
}
|
|
|
|
int qrs_last_index = 0; // Last point of QRS complex
|
|
|
|
for(int i = current - (sample - last_qrs) + SAMPLING_RATE*0.2f; i < (long unsigned int)current; ++i){
|
|
//for(int i = current - (sample - last_qrs) + (SAMPLING_RATE*0.2f); i < (long unsigned int)current; ++i){
|
|
if((integral_buffer[i%SEARCH_BACK_TIME] > threshold_i2) && (bandpass_buffer[i%SEARCH_BACK_TIME] > threshold_f2)){
|
|
current_slope = 0;
|
|
for(int j = current - QRS_TIME; j <= current; ++j){
|
|
if(square_buffer[j%QRS_TIME] > current_slope){
|
|
current_slope = square_buffer[j%QRS_TIME];
|
|
}
|
|
}
|
|
//current_slope = square;
|
|
|
|
if((current_slope < (int)(last_slope/2)) && (i + sample) < last_qrs + 0.36*last_qrs){
|
|
qrs = false;
|
|
}
|
|
else if(i - last_qrs > 550){
|
|
peak_i = integral_buffer[i%SEARCH_BACK_TIME];
|
|
peak_f = bandpass_buffer[i%SEARCH_BACK_TIME];
|
|
spk_i = 0.25*peak_i+ 0.75*spk_i;
|
|
spk_f = 0.25*peak_f + 0.75*spk_f;
|
|
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
|
|
threshold_i2 = 0.5*threshold_i1;
|
|
last_slope = current_slope;
|
|
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
|
|
threshold_f2 = 0.5*threshold_f1;
|
|
// If a signal peak was detected on the back search, the RR attributes must be updated.
|
|
// This is the same thing done when a peak is detected on the first try.
|
|
//RR Average 1
|
|
rravg1 = 0;
|
|
for(int j = 0; j < 7; ++j){
|
|
rr1[j] = rr1[j+1];
|
|
rravg1 += rr1[j];
|
|
}
|
|
rr1[7] = sample - (current - i) - last_qrs;
|
|
qrs = true;
|
|
qrs_last_index = i;
|
|
last_qrs = sample - (current - i);
|
|
rravg1 += rr1[7];
|
|
rravg1 *= 0.125;
|
|
|
|
//RR Average 2
|
|
if((rr1[7] >= rrlow) && (rr1[7] <= rrhigh)){
|
|
rravg2 = 0;
|
|
for (int i = 0; i < 7; ++i){
|
|
rr2[i] = rr2[i+1];
|
|
rravg2 += rr2[i];
|
|
}
|
|
rr2[7] = rr1[7];
|
|
rravg2 += rr2[7];
|
|
rravg2 *= 0.125;
|
|
rrlow = 0.92*rravg2;
|
|
rrhigh = 1.16*rravg2;
|
|
rrmiss = 1.66*rravg2;
|
|
}
|
|
|
|
prev_regular = regular;
|
|
if(rravg1 == rravg2){
|
|
regular = true;
|
|
}
|
|
else{
|
|
regular = false;
|
|
if(prev_regular){
|
|
threshold_i1 /= 2;
|
|
threshold_f1 /= 2;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(qrs){
|
|
//outputSignal[current] = false;
|
|
//outputSignal[i] = true;
|
|
//if (sample > DELAY + BUFFSIZE)
|
|
//output(outputSignal[0]);
|
|
//continue;
|
|
|
|
//return qrs;
|
|
return peak_buffer[qrs_last_index%SEARCH_BACK_TIME];
|
|
}
|
|
}
|
|
|
|
// Definitely no signal peak was detected.
|
|
if(!qrs){
|
|
// If some kind of peak had been detected, then it's certainly a noise peak. Thresholds must be updated accordinly.
|
|
if((integral >= threshold_i1) || (bandpass >= threshold_f1)){
|
|
peak_i = integral;
|
|
npk_i = 0.125*peak_i + 0.875*npk_i;
|
|
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
|
|
threshold_i2 = 0.5*threshold_i1;
|
|
peak_f = bandpass;
|
|
npk_f = 0.125*peak_f + 0.875*npk_f;
|
|
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
|
|
threshold_f2 = 0.5*threshold_f1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|