I've got some basic filter code setup (but not yet tested).

Dependencies:   BLE_API Queue mbed nRF51822

Fork of BLE_HeartRate by Bluetooth Low Energy

Revision:
62:8e2fbe131b53
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qrsdet.cpp	Sun Jun 28 03:06:00 2015 +0000
@@ -0,0 +1,452 @@
+/*****************************************************************************
+FILE:  qrsdet.cpp
+AUTHOR: Patrick S. Hamilton
+REVISED:    12/04/2000
+  ___________________________________________________________________________
+
+qrsdet.cpp: A QRS detector.
+Copywrite (C) 2000 Patrick S. Hamilton
+
+This file is free software; you can redistribute it and/or modify it under
+the terms of the GNU Library General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option) any
+later version.
+
+This software is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU Library General Public License for more
+details.
+
+You should have received a copy of the GNU Library General Public License along
+with this library; if not, write to the Free Software Foundation, Inc., 59
+Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+You may contact the author by e-mail (pat@eplimited.edu) or postal mail
+(Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
+MA 02143 USA).  For updates to this software, please visit our website
+(http://www.eplimited.com).
+  __________________________________________________________________________
+
+This file contains functions for detecting QRS complexes in an ECG.  The
+QRS detector requires filter functions in qrsfilt.cpp and parameter
+definitions in qrsdet.h.  QRSDet is the only function that needs to be
+visable outside of these files.
+
+Syntax:
+    int QRSDet(int ecgSample, int init) ;
+
+Description:
+    QRSDet() implements a modified version of the QRS detection
+    algorithm described in:
+
+    Hamilton, Tompkins, W. J., "Quantitative investigation of QRS
+    detection rules using the MIT/BIH arrhythmia database",
+    IEEE Trans. Biomed. Eng., BME-33, pp. 1158-1165, 1987.
+
+    Consecutive ECG samples are passed to QRSDet.  QRSDet was
+    designed for a 200 Hz sample rate.  QRSDet contains a number
+    of static variables that it uses to adapt to different ECG
+    signals.  These variables can be reset by passing any value
+    not equal to 0 in init.
+
+    Note: QRSDet() requires filters in QRSFilt.cpp
+
+Returns:
+    When a QRS complex is detected QRSDet returns the detection delay.
+
+****************************************************************/
+
+#include <mbed.h>
+//#include <mem.h>        /* For memmov. */
+//#include <math.h>
+#include "qrsdet.h"
+#define PRE_BLANK   MS200
+
+// External Prototypes.
+
+int QRSFilter(int datum, int init) ;
+int deriv1( int x0, int init ) ;
+
+// Local Prototypes.
+
+int Peak( int datum, int init ) ;
+int median(int *array, int datnum) ;
+int thresh(int qmedian, int nmedian) ;
+int BLSCheck(int *dBuf,int dbPtr,int *maxder) ;
+
+int earlyThresh(int qmedian, int nmedian) ;
+
+
+double TH = 0.475  ;
+
+int DDBuffer[DER_DELAY], DDPtr ;    /* Buffer holding derivative data. */
+int Dly  = 0 ;
+
+const int MEMMOVELEN = 7*sizeof(int);
+
+int QRSDet( int datum, int init )
+    {
+    static int det_thresh, qpkcnt = 0 ;
+    static int qrsbuf[8], noise[8], rrbuf[8] ;
+    static int rsetBuff[8], rsetCount = 0 ;
+    static int nmedian, qmedian, rrmedian ;
+    static int count, sbpeak = 0, sbloc, sbcount = MS1500 ;
+    static int maxder, lastmax ;
+    static int initBlank, initMax ;
+    static int preBlankCnt, tempPeak ;
+    
+    int fdatum, QrsDelay = 0 ;
+    int i, newPeak, aPeak ;
+
+/*  Initialize all buffers to 0 on the first call.  */
+
+    if( init )
+        {
+        for(i = 0; i < 8; ++i)
+            {
+            noise[i] = 0 ;  /* Initialize noise buffer */
+            rrbuf[i] = MS1000 ;/* and R-to-R interval buffer. */
+            }
+
+        qpkcnt = maxder = lastmax = count = sbpeak = 0 ;
+        initBlank = initMax = preBlankCnt = DDPtr = 0 ;
+        sbcount = MS1500 ;
+        QRSFilter(0,1) ;    /* initialize filters. */
+        Peak(0,1) ;
+        }
+
+    fdatum = QRSFilter(datum,0) ;   /* Filter data. */
+
+
+    /* Wait until normal detector is ready before calling early detections. */
+
+    aPeak = Peak(fdatum,0) ;
+
+    // Hold any peak that is detected for 200 ms
+    // in case a bigger one comes along.  There
+    // can only be one QRS complex in any 200 ms window.
+
+    newPeak = 0 ;
+    if(aPeak && !preBlankCnt)           // If there has been no peak for 200 ms
+        {                                       // save this one and start counting.
+        tempPeak = aPeak ;
+        preBlankCnt = PRE_BLANK ;           // MS200
+        }
+
+    else if(!aPeak && preBlankCnt)  // If we have held onto a peak for
+        {                                       // 200 ms pass it on for evaluation.
+        if(--preBlankCnt == 0)
+            newPeak = tempPeak ;
+        }
+
+    else if(aPeak)                          // If we were holding a peak, but
+        {                                       // this ones bigger, save it and
+        if(aPeak > tempPeak)                // start counting to 200 ms again.
+            {
+            tempPeak = aPeak ;
+            preBlankCnt = PRE_BLANK ; // MS200
+            }
+        else if(--preBlankCnt == 0)
+            newPeak = tempPeak ;
+        }
+
+/*  newPeak = 0 ;
+    if((aPeak != 0) && (preBlankCnt == 0))
+        newPeak = aPeak ;
+    else if(preBlankCnt != 0) --preBlankCnt ; */
+
+
+
+    /* Save derivative of raw signal for T-wave and baseline
+       shift discrimination. */
+    
+    DDBuffer[DDPtr] = deriv1( datum, 0 ) ;
+    if(++DDPtr == DER_DELAY)
+        DDPtr = 0 ;
+
+    /* Initialize the qrs peak buffer with the first eight  */
+    /* local maximum peaks detected.                        */
+
+    if( qpkcnt < 8 )
+        {
+        ++count ;
+        if(newPeak > 0) count = WINDOW_WIDTH ;
+        if(++initBlank == MS1000)
+            {
+            initBlank = 0 ;
+            qrsbuf[qpkcnt] = initMax ;
+            initMax = 0 ;
+            ++qpkcnt ;
+            if(qpkcnt == 8)
+                {
+                qmedian = median( qrsbuf, 8 ) ;
+                nmedian = 0 ;
+                rrmedian = MS1000 ;
+                sbcount = MS1500+MS150 ;
+                det_thresh = thresh(qmedian,nmedian) ;
+                }
+            }
+        if( newPeak > initMax )
+            initMax = newPeak ;
+        }
+
+    else    /* Else test for a qrs. */
+        {
+        ++count ;
+        if(newPeak > 0)
+            {
+            
+            
+            /* Check for maximum derivative and matching minima and maxima
+               for T-wave and baseline shift rejection.  Only consider this
+               peak if it doesn't seem to be a base line shift. */
+               
+            if(!BLSCheck(DDBuffer, DDPtr, &maxder))
+                {
+
+
+                // Classify the beat as a QRS complex
+                // if the peak is larger than the detection threshold.
+
+                if(newPeak > det_thresh)
+                    {
+                    memmove(&qrsbuf[1], qrsbuf, MEMMOVELEN) ;
+                    qrsbuf[0] = newPeak ;
+                    qmedian = median(qrsbuf,8) ;
+                    det_thresh = thresh(qmedian,nmedian) ;
+                    memmove(&rrbuf[1], rrbuf, MEMMOVELEN) ;
+                    rrbuf[0] = count - WINDOW_WIDTH ;
+                    rrmedian = median(rrbuf,8) ;
+                    sbcount = rrmedian + (rrmedian >> 1) + WINDOW_WIDTH ;
+                    count = WINDOW_WIDTH ;
+
+                    sbpeak = 0 ;
+
+                    lastmax = maxder ;
+                    maxder = 0 ;
+                    QrsDelay =  WINDOW_WIDTH + FILTER_DELAY ;
+                    initBlank = initMax = rsetCount = 0 ;
+
+            //      preBlankCnt = PRE_BLANK ;
+                    }
+
+                // If a peak isn't a QRS update noise buffer and estimate.
+                // Store the peak for possible search back.
+
+
+                else
+                    {
+                    memmove(&noise[1],noise,MEMMOVELEN) ;
+                    noise[0] = newPeak ;
+                    nmedian = median(noise,8) ;
+                    det_thresh = thresh(qmedian,nmedian) ;
+
+                    // Don't include early peaks (which might be T-waves)
+                    // in the search back process.  A T-wave can mask
+                    // a small following QRS.
+
+                    if((newPeak > sbpeak) && ((count-WINDOW_WIDTH) >= MS360))
+                        {
+                        sbpeak = newPeak ;
+                        sbloc = count  - WINDOW_WIDTH ;
+                        }
+                    }
+                }
+            }
+        
+        /* Test for search back condition.  If a QRS is found in  */
+        /* search back update the QRS buffer and det_thresh.      */
+
+        if((count > sbcount) && (sbpeak > (det_thresh >> 1)))
+            {
+            memmove(&qrsbuf[1],qrsbuf,MEMMOVELEN) ;
+            qrsbuf[0] = sbpeak ;
+            qmedian = median(qrsbuf,8) ;
+            det_thresh = thresh(qmedian,nmedian) ;
+            memmove(&rrbuf[1],rrbuf,MEMMOVELEN) ;
+            rrbuf[0] = sbloc ;
+            rrmedian = median(rrbuf,8) ;
+            sbcount = rrmedian + (rrmedian >> 1) + WINDOW_WIDTH ;
+            QrsDelay = count = count - sbloc ;
+            QrsDelay += FILTER_DELAY ;
+            sbpeak = 0 ;
+            lastmax = maxder ;
+            maxder = 0 ;
+            initBlank = initMax = rsetCount = 0 ;
+            }
+        }
+
+    // In the background estimate threshold to replace adaptive threshold
+    // if eight seconds elapses without a QRS detection.
+
+    if( qpkcnt == 8 )
+        {
+        if(++initBlank == MS1000)
+            {
+            initBlank = 0 ;
+            rsetBuff[rsetCount] = initMax ;
+            initMax = 0 ;
+            ++rsetCount ;
+
+            // Reset threshold if it has been 8 seconds without
+            // a detection.
+
+            if(rsetCount == 8)
+                {
+                for(i = 0; i < 8; ++i)
+                    {
+                    qrsbuf[i] = rsetBuff[i] ;
+                    noise[i] = 0 ;
+                    }
+                qmedian = median( rsetBuff, 8 ) ;
+                nmedian = 0 ;
+                rrmedian = MS1000 ;
+                sbcount = MS1500+MS150 ;
+                det_thresh = thresh(qmedian,nmedian) ;
+                initBlank = initMax = rsetCount = 0 ;
+            sbpeak = 0 ;
+                }
+            }
+        if( newPeak > initMax )
+            initMax = newPeak ;
+        }
+
+    return(QrsDelay) ;
+    }
+
+/**************************************************************
+* peak() takes a datum as input and returns a peak height
+* when the signal returns to half its peak height, or 
+**************************************************************/
+
+int Peak( int datum, int init )
+    {
+    static int max = 0, timeSinceMax = 0, lastDatum ;
+    int pk = 0 ;
+
+    if(init)
+        max = timeSinceMax = 0 ;
+        
+    if(timeSinceMax > 0)
+        ++timeSinceMax ;
+
+    if((datum > lastDatum) && (datum > max))
+        {
+        max = datum ;
+        if(max > 2)
+            timeSinceMax = 1 ;
+        }
+
+    else if(datum < (max >> 1))
+        {
+        pk = max ;
+        max = 0 ;
+        timeSinceMax = 0 ;
+        Dly = 0 ;
+        }
+
+    else if(timeSinceMax > MS95)
+        {
+        pk = max ;
+        max = 0 ;
+        timeSinceMax = 0 ;
+        Dly = 3 ;
+        }
+    lastDatum = datum ;
+    return(pk) ;
+    }
+
+/********************************************************************
+median returns the median of an array of integers.  It uses a slow
+sort algorithm, but these arrays are small, so it hardly matters.
+********************************************************************/
+
+int median(int *array, int datnum)
+    {
+    int i, j, k, temp, sort[20] ;
+    for(i = 0; i < datnum; ++i)
+        sort[i] = array[i] ;
+    for(i = 0; i < datnum; ++i)
+        {
+        temp = sort[i] ;
+        for(j = 0; (temp < sort[j]) && (j < i) ; ++j) ;
+        for(k = i - 1 ; k >= j ; --k)
+            sort[k+1] = sort[k] ;
+        sort[j] = temp ;
+        }
+    return(sort[datnum>>1]) ;
+    }
+/*
+int median(int *array, int datnum)
+    {
+    long sum ;
+    int i ;
+
+    for(i = 0, sum = 0; i < datnum; ++i)
+        sum += array[i] ;
+    sum /= datnum ;
+    return(sum) ;
+    } */
+
+/****************************************************************************
+ thresh() calculates the detection threshold from the qrs median and noise
+ median estimates.
+****************************************************************************/
+
+int thresh(int qmedian, int nmedian)
+    {
+    int thrsh, dmed ;
+    double temp ;
+    dmed = qmedian - nmedian ;
+/*  thrsh = nmedian + (dmed>>2) + (dmed>>3) + (dmed>>4); */
+    temp = dmed ;
+    temp *= TH ;
+    dmed = temp ;
+    thrsh = nmedian + dmed ; /* dmed * THRESHOLD */
+    return(thrsh) ;
+    }
+
+/***********************************************************************
+    BLSCheck() reviews data to see if a baseline shift has occurred.
+    This is done by looking for both positive and negative slopes of
+    roughly the same magnitude in a 220 ms window.
+***********************************************************************/
+
+int BLSCheck(int *dBuf,int dbPtr,int *maxder)
+    {
+    int max, min, maxt, mint, t, x ;
+    max = min = 0 ;
+
+    return(0) ;
+    
+    for(t = 0; t < MS220; ++t)
+        {
+        x = dBuf[dbPtr] ;
+        if(x > max)
+            {
+            maxt = t ;
+            max = x ;
+            }
+        else if(x < min)
+            {
+            mint = t ;
+            min = x;
+            }
+        if(++dbPtr == DER_DELAY)
+            dbPtr = 0 ;
+        }
+
+    *maxder = max ;
+    min = -min ;
+    
+    /* Possible beat if a maximum and minimum pair are found
+        where the interval between them is less than 150 ms. */
+       
+    if((max > (min>>3)) && (min > (max>>3)) &&
+        (abs(maxt - mint) < MS150))
+        return(0) ;
+        
+    else
+        return(1) ;
+    }
+