I've got some basic filter code setup (but not yet tested).
Dependencies: BLE_API Queue mbed nRF51822
Fork of BLE_HeartRate by
qrsdet.cpp@62:8e2fbe131b53, 2015-06-28 (annotated)
- Committer:
- roysandberg
- Date:
- Sun Jun 28 03:06:00 2015 +0000
- Revision:
- 62:8e2fbe131b53
Working Beat Detection and Analysis
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
roysandberg | 62:8e2fbe131b53 | 1 | /***************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 2 | FILE: qrsdet.cpp |
roysandberg | 62:8e2fbe131b53 | 3 | AUTHOR: Patrick S. Hamilton |
roysandberg | 62:8e2fbe131b53 | 4 | REVISED: 12/04/2000 |
roysandberg | 62:8e2fbe131b53 | 5 | ___________________________________________________________________________ |
roysandberg | 62:8e2fbe131b53 | 6 | |
roysandberg | 62:8e2fbe131b53 | 7 | qrsdet.cpp: A QRS detector. |
roysandberg | 62:8e2fbe131b53 | 8 | Copywrite (C) 2000 Patrick S. Hamilton |
roysandberg | 62:8e2fbe131b53 | 9 | |
roysandberg | 62:8e2fbe131b53 | 10 | This file is free software; you can redistribute it and/or modify it under |
roysandberg | 62:8e2fbe131b53 | 11 | the terms of the GNU Library General Public License as published by the Free |
roysandberg | 62:8e2fbe131b53 | 12 | Software Foundation; either version 2 of the License, or (at your option) any |
roysandberg | 62:8e2fbe131b53 | 13 | later version. |
roysandberg | 62:8e2fbe131b53 | 14 | |
roysandberg | 62:8e2fbe131b53 | 15 | This software is distributed in the hope that it will be useful, but WITHOUT ANY |
roysandberg | 62:8e2fbe131b53 | 16 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
roysandberg | 62:8e2fbe131b53 | 17 | PARTICULAR PURPOSE. See the GNU Library General Public License for more |
roysandberg | 62:8e2fbe131b53 | 18 | details. |
roysandberg | 62:8e2fbe131b53 | 19 | |
roysandberg | 62:8e2fbe131b53 | 20 | You should have received a copy of the GNU Library General Public License along |
roysandberg | 62:8e2fbe131b53 | 21 | with this library; if not, write to the Free Software Foundation, Inc., 59 |
roysandberg | 62:8e2fbe131b53 | 22 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
roysandberg | 62:8e2fbe131b53 | 23 | |
roysandberg | 62:8e2fbe131b53 | 24 | You may contact the author by e-mail (pat@eplimited.edu) or postal mail |
roysandberg | 62:8e2fbe131b53 | 25 | (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville, |
roysandberg | 62:8e2fbe131b53 | 26 | MA 02143 USA). For updates to this software, please visit our website |
roysandberg | 62:8e2fbe131b53 | 27 | (http://www.eplimited.com). |
roysandberg | 62:8e2fbe131b53 | 28 | __________________________________________________________________________ |
roysandberg | 62:8e2fbe131b53 | 29 | |
roysandberg | 62:8e2fbe131b53 | 30 | This file contains functions for detecting QRS complexes in an ECG. The |
roysandberg | 62:8e2fbe131b53 | 31 | QRS detector requires filter functions in qrsfilt.cpp and parameter |
roysandberg | 62:8e2fbe131b53 | 32 | definitions in qrsdet.h. QRSDet is the only function that needs to be |
roysandberg | 62:8e2fbe131b53 | 33 | visable outside of these files. |
roysandberg | 62:8e2fbe131b53 | 34 | |
roysandberg | 62:8e2fbe131b53 | 35 | Syntax: |
roysandberg | 62:8e2fbe131b53 | 36 | int QRSDet(int ecgSample, int init) ; |
roysandberg | 62:8e2fbe131b53 | 37 | |
roysandberg | 62:8e2fbe131b53 | 38 | Description: |
roysandberg | 62:8e2fbe131b53 | 39 | QRSDet() implements a modified version of the QRS detection |
roysandberg | 62:8e2fbe131b53 | 40 | algorithm described in: |
roysandberg | 62:8e2fbe131b53 | 41 | |
roysandberg | 62:8e2fbe131b53 | 42 | Hamilton, Tompkins, W. J., "Quantitative investigation of QRS |
roysandberg | 62:8e2fbe131b53 | 43 | detection rules using the MIT/BIH arrhythmia database", |
roysandberg | 62:8e2fbe131b53 | 44 | IEEE Trans. Biomed. Eng., BME-33, pp. 1158-1165, 1987. |
roysandberg | 62:8e2fbe131b53 | 45 | |
roysandberg | 62:8e2fbe131b53 | 46 | Consecutive ECG samples are passed to QRSDet. QRSDet was |
roysandberg | 62:8e2fbe131b53 | 47 | designed for a 200 Hz sample rate. QRSDet contains a number |
roysandberg | 62:8e2fbe131b53 | 48 | of static variables that it uses to adapt to different ECG |
roysandberg | 62:8e2fbe131b53 | 49 | signals. These variables can be reset by passing any value |
roysandberg | 62:8e2fbe131b53 | 50 | not equal to 0 in init. |
roysandberg | 62:8e2fbe131b53 | 51 | |
roysandberg | 62:8e2fbe131b53 | 52 | Note: QRSDet() requires filters in QRSFilt.cpp |
roysandberg | 62:8e2fbe131b53 | 53 | |
roysandberg | 62:8e2fbe131b53 | 54 | Returns: |
roysandberg | 62:8e2fbe131b53 | 55 | When a QRS complex is detected QRSDet returns the detection delay. |
roysandberg | 62:8e2fbe131b53 | 56 | |
roysandberg | 62:8e2fbe131b53 | 57 | ****************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 58 | |
roysandberg | 62:8e2fbe131b53 | 59 | #include <mbed.h> |
roysandberg | 62:8e2fbe131b53 | 60 | //#include <mem.h> /* For memmov. */ |
roysandberg | 62:8e2fbe131b53 | 61 | //#include <math.h> |
roysandberg | 62:8e2fbe131b53 | 62 | #include "qrsdet.h" |
roysandberg | 62:8e2fbe131b53 | 63 | #define PRE_BLANK MS200 |
roysandberg | 62:8e2fbe131b53 | 64 | |
roysandberg | 62:8e2fbe131b53 | 65 | // External Prototypes. |
roysandberg | 62:8e2fbe131b53 | 66 | |
roysandberg | 62:8e2fbe131b53 | 67 | int QRSFilter(int datum, int init) ; |
roysandberg | 62:8e2fbe131b53 | 68 | int deriv1( int x0, int init ) ; |
roysandberg | 62:8e2fbe131b53 | 69 | |
roysandberg | 62:8e2fbe131b53 | 70 | // Local Prototypes. |
roysandberg | 62:8e2fbe131b53 | 71 | |
roysandberg | 62:8e2fbe131b53 | 72 | int Peak( int datum, int init ) ; |
roysandberg | 62:8e2fbe131b53 | 73 | int median(int *array, int datnum) ; |
roysandberg | 62:8e2fbe131b53 | 74 | int thresh(int qmedian, int nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 75 | int BLSCheck(int *dBuf,int dbPtr,int *maxder) ; |
roysandberg | 62:8e2fbe131b53 | 76 | |
roysandberg | 62:8e2fbe131b53 | 77 | int earlyThresh(int qmedian, int nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 78 | |
roysandberg | 62:8e2fbe131b53 | 79 | |
roysandberg | 62:8e2fbe131b53 | 80 | double TH = 0.475 ; |
roysandberg | 62:8e2fbe131b53 | 81 | |
roysandberg | 62:8e2fbe131b53 | 82 | int DDBuffer[DER_DELAY], DDPtr ; /* Buffer holding derivative data. */ |
roysandberg | 62:8e2fbe131b53 | 83 | int Dly = 0 ; |
roysandberg | 62:8e2fbe131b53 | 84 | |
roysandberg | 62:8e2fbe131b53 | 85 | const int MEMMOVELEN = 7*sizeof(int); |
roysandberg | 62:8e2fbe131b53 | 86 | |
roysandberg | 62:8e2fbe131b53 | 87 | int QRSDet( int datum, int init ) |
roysandberg | 62:8e2fbe131b53 | 88 | { |
roysandberg | 62:8e2fbe131b53 | 89 | static int det_thresh, qpkcnt = 0 ; |
roysandberg | 62:8e2fbe131b53 | 90 | static int qrsbuf[8], noise[8], rrbuf[8] ; |
roysandberg | 62:8e2fbe131b53 | 91 | static int rsetBuff[8], rsetCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 92 | static int nmedian, qmedian, rrmedian ; |
roysandberg | 62:8e2fbe131b53 | 93 | static int count, sbpeak = 0, sbloc, sbcount = MS1500 ; |
roysandberg | 62:8e2fbe131b53 | 94 | static int maxder, lastmax ; |
roysandberg | 62:8e2fbe131b53 | 95 | static int initBlank, initMax ; |
roysandberg | 62:8e2fbe131b53 | 96 | static int preBlankCnt, tempPeak ; |
roysandberg | 62:8e2fbe131b53 | 97 | |
roysandberg | 62:8e2fbe131b53 | 98 | int fdatum, QrsDelay = 0 ; |
roysandberg | 62:8e2fbe131b53 | 99 | int i, newPeak, aPeak ; |
roysandberg | 62:8e2fbe131b53 | 100 | |
roysandberg | 62:8e2fbe131b53 | 101 | /* Initialize all buffers to 0 on the first call. */ |
roysandberg | 62:8e2fbe131b53 | 102 | |
roysandberg | 62:8e2fbe131b53 | 103 | if( init ) |
roysandberg | 62:8e2fbe131b53 | 104 | { |
roysandberg | 62:8e2fbe131b53 | 105 | for(i = 0; i < 8; ++i) |
roysandberg | 62:8e2fbe131b53 | 106 | { |
roysandberg | 62:8e2fbe131b53 | 107 | noise[i] = 0 ; /* Initialize noise buffer */ |
roysandberg | 62:8e2fbe131b53 | 108 | rrbuf[i] = MS1000 ;/* and R-to-R interval buffer. */ |
roysandberg | 62:8e2fbe131b53 | 109 | } |
roysandberg | 62:8e2fbe131b53 | 110 | |
roysandberg | 62:8e2fbe131b53 | 111 | qpkcnt = maxder = lastmax = count = sbpeak = 0 ; |
roysandberg | 62:8e2fbe131b53 | 112 | initBlank = initMax = preBlankCnt = DDPtr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 113 | sbcount = MS1500 ; |
roysandberg | 62:8e2fbe131b53 | 114 | QRSFilter(0,1) ; /* initialize filters. */ |
roysandberg | 62:8e2fbe131b53 | 115 | Peak(0,1) ; |
roysandberg | 62:8e2fbe131b53 | 116 | } |
roysandberg | 62:8e2fbe131b53 | 117 | |
roysandberg | 62:8e2fbe131b53 | 118 | fdatum = QRSFilter(datum,0) ; /* Filter data. */ |
roysandberg | 62:8e2fbe131b53 | 119 | |
roysandberg | 62:8e2fbe131b53 | 120 | |
roysandberg | 62:8e2fbe131b53 | 121 | /* Wait until normal detector is ready before calling early detections. */ |
roysandberg | 62:8e2fbe131b53 | 122 | |
roysandberg | 62:8e2fbe131b53 | 123 | aPeak = Peak(fdatum,0) ; |
roysandberg | 62:8e2fbe131b53 | 124 | |
roysandberg | 62:8e2fbe131b53 | 125 | // Hold any peak that is detected for 200 ms |
roysandberg | 62:8e2fbe131b53 | 126 | // in case a bigger one comes along. There |
roysandberg | 62:8e2fbe131b53 | 127 | // can only be one QRS complex in any 200 ms window. |
roysandberg | 62:8e2fbe131b53 | 128 | |
roysandberg | 62:8e2fbe131b53 | 129 | newPeak = 0 ; |
roysandberg | 62:8e2fbe131b53 | 130 | if(aPeak && !preBlankCnt) // If there has been no peak for 200 ms |
roysandberg | 62:8e2fbe131b53 | 131 | { // save this one and start counting. |
roysandberg | 62:8e2fbe131b53 | 132 | tempPeak = aPeak ; |
roysandberg | 62:8e2fbe131b53 | 133 | preBlankCnt = PRE_BLANK ; // MS200 |
roysandberg | 62:8e2fbe131b53 | 134 | } |
roysandberg | 62:8e2fbe131b53 | 135 | |
roysandberg | 62:8e2fbe131b53 | 136 | else if(!aPeak && preBlankCnt) // If we have held onto a peak for |
roysandberg | 62:8e2fbe131b53 | 137 | { // 200 ms pass it on for evaluation. |
roysandberg | 62:8e2fbe131b53 | 138 | if(--preBlankCnt == 0) |
roysandberg | 62:8e2fbe131b53 | 139 | newPeak = tempPeak ; |
roysandberg | 62:8e2fbe131b53 | 140 | } |
roysandberg | 62:8e2fbe131b53 | 141 | |
roysandberg | 62:8e2fbe131b53 | 142 | else if(aPeak) // If we were holding a peak, but |
roysandberg | 62:8e2fbe131b53 | 143 | { // this ones bigger, save it and |
roysandberg | 62:8e2fbe131b53 | 144 | if(aPeak > tempPeak) // start counting to 200 ms again. |
roysandberg | 62:8e2fbe131b53 | 145 | { |
roysandberg | 62:8e2fbe131b53 | 146 | tempPeak = aPeak ; |
roysandberg | 62:8e2fbe131b53 | 147 | preBlankCnt = PRE_BLANK ; // MS200 |
roysandberg | 62:8e2fbe131b53 | 148 | } |
roysandberg | 62:8e2fbe131b53 | 149 | else if(--preBlankCnt == 0) |
roysandberg | 62:8e2fbe131b53 | 150 | newPeak = tempPeak ; |
roysandberg | 62:8e2fbe131b53 | 151 | } |
roysandberg | 62:8e2fbe131b53 | 152 | |
roysandberg | 62:8e2fbe131b53 | 153 | /* newPeak = 0 ; |
roysandberg | 62:8e2fbe131b53 | 154 | if((aPeak != 0) && (preBlankCnt == 0)) |
roysandberg | 62:8e2fbe131b53 | 155 | newPeak = aPeak ; |
roysandberg | 62:8e2fbe131b53 | 156 | else if(preBlankCnt != 0) --preBlankCnt ; */ |
roysandberg | 62:8e2fbe131b53 | 157 | |
roysandberg | 62:8e2fbe131b53 | 158 | |
roysandberg | 62:8e2fbe131b53 | 159 | |
roysandberg | 62:8e2fbe131b53 | 160 | /* Save derivative of raw signal for T-wave and baseline |
roysandberg | 62:8e2fbe131b53 | 161 | shift discrimination. */ |
roysandberg | 62:8e2fbe131b53 | 162 | |
roysandberg | 62:8e2fbe131b53 | 163 | DDBuffer[DDPtr] = deriv1( datum, 0 ) ; |
roysandberg | 62:8e2fbe131b53 | 164 | if(++DDPtr == DER_DELAY) |
roysandberg | 62:8e2fbe131b53 | 165 | DDPtr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 166 | |
roysandberg | 62:8e2fbe131b53 | 167 | /* Initialize the qrs peak buffer with the first eight */ |
roysandberg | 62:8e2fbe131b53 | 168 | /* local maximum peaks detected. */ |
roysandberg | 62:8e2fbe131b53 | 169 | |
roysandberg | 62:8e2fbe131b53 | 170 | if( qpkcnt < 8 ) |
roysandberg | 62:8e2fbe131b53 | 171 | { |
roysandberg | 62:8e2fbe131b53 | 172 | ++count ; |
roysandberg | 62:8e2fbe131b53 | 173 | if(newPeak > 0) count = WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 174 | if(++initBlank == MS1000) |
roysandberg | 62:8e2fbe131b53 | 175 | { |
roysandberg | 62:8e2fbe131b53 | 176 | initBlank = 0 ; |
roysandberg | 62:8e2fbe131b53 | 177 | qrsbuf[qpkcnt] = initMax ; |
roysandberg | 62:8e2fbe131b53 | 178 | initMax = 0 ; |
roysandberg | 62:8e2fbe131b53 | 179 | ++qpkcnt ; |
roysandberg | 62:8e2fbe131b53 | 180 | if(qpkcnt == 8) |
roysandberg | 62:8e2fbe131b53 | 181 | { |
roysandberg | 62:8e2fbe131b53 | 182 | qmedian = median( qrsbuf, 8 ) ; |
roysandberg | 62:8e2fbe131b53 | 183 | nmedian = 0 ; |
roysandberg | 62:8e2fbe131b53 | 184 | rrmedian = MS1000 ; |
roysandberg | 62:8e2fbe131b53 | 185 | sbcount = MS1500+MS150 ; |
roysandberg | 62:8e2fbe131b53 | 186 | det_thresh = thresh(qmedian,nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 187 | } |
roysandberg | 62:8e2fbe131b53 | 188 | } |
roysandberg | 62:8e2fbe131b53 | 189 | if( newPeak > initMax ) |
roysandberg | 62:8e2fbe131b53 | 190 | initMax = newPeak ; |
roysandberg | 62:8e2fbe131b53 | 191 | } |
roysandberg | 62:8e2fbe131b53 | 192 | |
roysandberg | 62:8e2fbe131b53 | 193 | else /* Else test for a qrs. */ |
roysandberg | 62:8e2fbe131b53 | 194 | { |
roysandberg | 62:8e2fbe131b53 | 195 | ++count ; |
roysandberg | 62:8e2fbe131b53 | 196 | if(newPeak > 0) |
roysandberg | 62:8e2fbe131b53 | 197 | { |
roysandberg | 62:8e2fbe131b53 | 198 | |
roysandberg | 62:8e2fbe131b53 | 199 | |
roysandberg | 62:8e2fbe131b53 | 200 | /* Check for maximum derivative and matching minima and maxima |
roysandberg | 62:8e2fbe131b53 | 201 | for T-wave and baseline shift rejection. Only consider this |
roysandberg | 62:8e2fbe131b53 | 202 | peak if it doesn't seem to be a base line shift. */ |
roysandberg | 62:8e2fbe131b53 | 203 | |
roysandberg | 62:8e2fbe131b53 | 204 | if(!BLSCheck(DDBuffer, DDPtr, &maxder)) |
roysandberg | 62:8e2fbe131b53 | 205 | { |
roysandberg | 62:8e2fbe131b53 | 206 | |
roysandberg | 62:8e2fbe131b53 | 207 | |
roysandberg | 62:8e2fbe131b53 | 208 | // Classify the beat as a QRS complex |
roysandberg | 62:8e2fbe131b53 | 209 | // if the peak is larger than the detection threshold. |
roysandberg | 62:8e2fbe131b53 | 210 | |
roysandberg | 62:8e2fbe131b53 | 211 | if(newPeak > det_thresh) |
roysandberg | 62:8e2fbe131b53 | 212 | { |
roysandberg | 62:8e2fbe131b53 | 213 | memmove(&qrsbuf[1], qrsbuf, MEMMOVELEN) ; |
roysandberg | 62:8e2fbe131b53 | 214 | qrsbuf[0] = newPeak ; |
roysandberg | 62:8e2fbe131b53 | 215 | qmedian = median(qrsbuf,8) ; |
roysandberg | 62:8e2fbe131b53 | 216 | det_thresh = thresh(qmedian,nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 217 | memmove(&rrbuf[1], rrbuf, MEMMOVELEN) ; |
roysandberg | 62:8e2fbe131b53 | 218 | rrbuf[0] = count - WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 219 | rrmedian = median(rrbuf,8) ; |
roysandberg | 62:8e2fbe131b53 | 220 | sbcount = rrmedian + (rrmedian >> 1) + WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 221 | count = WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 222 | |
roysandberg | 62:8e2fbe131b53 | 223 | sbpeak = 0 ; |
roysandberg | 62:8e2fbe131b53 | 224 | |
roysandberg | 62:8e2fbe131b53 | 225 | lastmax = maxder ; |
roysandberg | 62:8e2fbe131b53 | 226 | maxder = 0 ; |
roysandberg | 62:8e2fbe131b53 | 227 | QrsDelay = WINDOW_WIDTH + FILTER_DELAY ; |
roysandberg | 62:8e2fbe131b53 | 228 | initBlank = initMax = rsetCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 229 | |
roysandberg | 62:8e2fbe131b53 | 230 | // preBlankCnt = PRE_BLANK ; |
roysandberg | 62:8e2fbe131b53 | 231 | } |
roysandberg | 62:8e2fbe131b53 | 232 | |
roysandberg | 62:8e2fbe131b53 | 233 | // If a peak isn't a QRS update noise buffer and estimate. |
roysandberg | 62:8e2fbe131b53 | 234 | // Store the peak for possible search back. |
roysandberg | 62:8e2fbe131b53 | 235 | |
roysandberg | 62:8e2fbe131b53 | 236 | |
roysandberg | 62:8e2fbe131b53 | 237 | else |
roysandberg | 62:8e2fbe131b53 | 238 | { |
roysandberg | 62:8e2fbe131b53 | 239 | memmove(&noise[1],noise,MEMMOVELEN) ; |
roysandberg | 62:8e2fbe131b53 | 240 | noise[0] = newPeak ; |
roysandberg | 62:8e2fbe131b53 | 241 | nmedian = median(noise,8) ; |
roysandberg | 62:8e2fbe131b53 | 242 | det_thresh = thresh(qmedian,nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 243 | |
roysandberg | 62:8e2fbe131b53 | 244 | // Don't include early peaks (which might be T-waves) |
roysandberg | 62:8e2fbe131b53 | 245 | // in the search back process. A T-wave can mask |
roysandberg | 62:8e2fbe131b53 | 246 | // a small following QRS. |
roysandberg | 62:8e2fbe131b53 | 247 | |
roysandberg | 62:8e2fbe131b53 | 248 | if((newPeak > sbpeak) && ((count-WINDOW_WIDTH) >= MS360)) |
roysandberg | 62:8e2fbe131b53 | 249 | { |
roysandberg | 62:8e2fbe131b53 | 250 | sbpeak = newPeak ; |
roysandberg | 62:8e2fbe131b53 | 251 | sbloc = count - WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 252 | } |
roysandberg | 62:8e2fbe131b53 | 253 | } |
roysandberg | 62:8e2fbe131b53 | 254 | } |
roysandberg | 62:8e2fbe131b53 | 255 | } |
roysandberg | 62:8e2fbe131b53 | 256 | |
roysandberg | 62:8e2fbe131b53 | 257 | /* Test for search back condition. If a QRS is found in */ |
roysandberg | 62:8e2fbe131b53 | 258 | /* search back update the QRS buffer and det_thresh. */ |
roysandberg | 62:8e2fbe131b53 | 259 | |
roysandberg | 62:8e2fbe131b53 | 260 | if((count > sbcount) && (sbpeak > (det_thresh >> 1))) |
roysandberg | 62:8e2fbe131b53 | 261 | { |
roysandberg | 62:8e2fbe131b53 | 262 | memmove(&qrsbuf[1],qrsbuf,MEMMOVELEN) ; |
roysandberg | 62:8e2fbe131b53 | 263 | qrsbuf[0] = sbpeak ; |
roysandberg | 62:8e2fbe131b53 | 264 | qmedian = median(qrsbuf,8) ; |
roysandberg | 62:8e2fbe131b53 | 265 | det_thresh = thresh(qmedian,nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 266 | memmove(&rrbuf[1],rrbuf,MEMMOVELEN) ; |
roysandberg | 62:8e2fbe131b53 | 267 | rrbuf[0] = sbloc ; |
roysandberg | 62:8e2fbe131b53 | 268 | rrmedian = median(rrbuf,8) ; |
roysandberg | 62:8e2fbe131b53 | 269 | sbcount = rrmedian + (rrmedian >> 1) + WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 270 | QrsDelay = count = count - sbloc ; |
roysandberg | 62:8e2fbe131b53 | 271 | QrsDelay += FILTER_DELAY ; |
roysandberg | 62:8e2fbe131b53 | 272 | sbpeak = 0 ; |
roysandberg | 62:8e2fbe131b53 | 273 | lastmax = maxder ; |
roysandberg | 62:8e2fbe131b53 | 274 | maxder = 0 ; |
roysandberg | 62:8e2fbe131b53 | 275 | initBlank = initMax = rsetCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 276 | } |
roysandberg | 62:8e2fbe131b53 | 277 | } |
roysandberg | 62:8e2fbe131b53 | 278 | |
roysandberg | 62:8e2fbe131b53 | 279 | // In the background estimate threshold to replace adaptive threshold |
roysandberg | 62:8e2fbe131b53 | 280 | // if eight seconds elapses without a QRS detection. |
roysandberg | 62:8e2fbe131b53 | 281 | |
roysandberg | 62:8e2fbe131b53 | 282 | if( qpkcnt == 8 ) |
roysandberg | 62:8e2fbe131b53 | 283 | { |
roysandberg | 62:8e2fbe131b53 | 284 | if(++initBlank == MS1000) |
roysandberg | 62:8e2fbe131b53 | 285 | { |
roysandberg | 62:8e2fbe131b53 | 286 | initBlank = 0 ; |
roysandberg | 62:8e2fbe131b53 | 287 | rsetBuff[rsetCount] = initMax ; |
roysandberg | 62:8e2fbe131b53 | 288 | initMax = 0 ; |
roysandberg | 62:8e2fbe131b53 | 289 | ++rsetCount ; |
roysandberg | 62:8e2fbe131b53 | 290 | |
roysandberg | 62:8e2fbe131b53 | 291 | // Reset threshold if it has been 8 seconds without |
roysandberg | 62:8e2fbe131b53 | 292 | // a detection. |
roysandberg | 62:8e2fbe131b53 | 293 | |
roysandberg | 62:8e2fbe131b53 | 294 | if(rsetCount == 8) |
roysandberg | 62:8e2fbe131b53 | 295 | { |
roysandberg | 62:8e2fbe131b53 | 296 | for(i = 0; i < 8; ++i) |
roysandberg | 62:8e2fbe131b53 | 297 | { |
roysandberg | 62:8e2fbe131b53 | 298 | qrsbuf[i] = rsetBuff[i] ; |
roysandberg | 62:8e2fbe131b53 | 299 | noise[i] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 300 | } |
roysandberg | 62:8e2fbe131b53 | 301 | qmedian = median( rsetBuff, 8 ) ; |
roysandberg | 62:8e2fbe131b53 | 302 | nmedian = 0 ; |
roysandberg | 62:8e2fbe131b53 | 303 | rrmedian = MS1000 ; |
roysandberg | 62:8e2fbe131b53 | 304 | sbcount = MS1500+MS150 ; |
roysandberg | 62:8e2fbe131b53 | 305 | det_thresh = thresh(qmedian,nmedian) ; |
roysandberg | 62:8e2fbe131b53 | 306 | initBlank = initMax = rsetCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 307 | sbpeak = 0 ; |
roysandberg | 62:8e2fbe131b53 | 308 | } |
roysandberg | 62:8e2fbe131b53 | 309 | } |
roysandberg | 62:8e2fbe131b53 | 310 | if( newPeak > initMax ) |
roysandberg | 62:8e2fbe131b53 | 311 | initMax = newPeak ; |
roysandberg | 62:8e2fbe131b53 | 312 | } |
roysandberg | 62:8e2fbe131b53 | 313 | |
roysandberg | 62:8e2fbe131b53 | 314 | return(QrsDelay) ; |
roysandberg | 62:8e2fbe131b53 | 315 | } |
roysandberg | 62:8e2fbe131b53 | 316 | |
roysandberg | 62:8e2fbe131b53 | 317 | /************************************************************** |
roysandberg | 62:8e2fbe131b53 | 318 | * peak() takes a datum as input and returns a peak height |
roysandberg | 62:8e2fbe131b53 | 319 | * when the signal returns to half its peak height, or |
roysandberg | 62:8e2fbe131b53 | 320 | **************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 321 | |
roysandberg | 62:8e2fbe131b53 | 322 | int Peak( int datum, int init ) |
roysandberg | 62:8e2fbe131b53 | 323 | { |
roysandberg | 62:8e2fbe131b53 | 324 | static int max = 0, timeSinceMax = 0, lastDatum ; |
roysandberg | 62:8e2fbe131b53 | 325 | int pk = 0 ; |
roysandberg | 62:8e2fbe131b53 | 326 | |
roysandberg | 62:8e2fbe131b53 | 327 | if(init) |
roysandberg | 62:8e2fbe131b53 | 328 | max = timeSinceMax = 0 ; |
roysandberg | 62:8e2fbe131b53 | 329 | |
roysandberg | 62:8e2fbe131b53 | 330 | if(timeSinceMax > 0) |
roysandberg | 62:8e2fbe131b53 | 331 | ++timeSinceMax ; |
roysandberg | 62:8e2fbe131b53 | 332 | |
roysandberg | 62:8e2fbe131b53 | 333 | if((datum > lastDatum) && (datum > max)) |
roysandberg | 62:8e2fbe131b53 | 334 | { |
roysandberg | 62:8e2fbe131b53 | 335 | max = datum ; |
roysandberg | 62:8e2fbe131b53 | 336 | if(max > 2) |
roysandberg | 62:8e2fbe131b53 | 337 | timeSinceMax = 1 ; |
roysandberg | 62:8e2fbe131b53 | 338 | } |
roysandberg | 62:8e2fbe131b53 | 339 | |
roysandberg | 62:8e2fbe131b53 | 340 | else if(datum < (max >> 1)) |
roysandberg | 62:8e2fbe131b53 | 341 | { |
roysandberg | 62:8e2fbe131b53 | 342 | pk = max ; |
roysandberg | 62:8e2fbe131b53 | 343 | max = 0 ; |
roysandberg | 62:8e2fbe131b53 | 344 | timeSinceMax = 0 ; |
roysandberg | 62:8e2fbe131b53 | 345 | Dly = 0 ; |
roysandberg | 62:8e2fbe131b53 | 346 | } |
roysandberg | 62:8e2fbe131b53 | 347 | |
roysandberg | 62:8e2fbe131b53 | 348 | else if(timeSinceMax > MS95) |
roysandberg | 62:8e2fbe131b53 | 349 | { |
roysandberg | 62:8e2fbe131b53 | 350 | pk = max ; |
roysandberg | 62:8e2fbe131b53 | 351 | max = 0 ; |
roysandberg | 62:8e2fbe131b53 | 352 | timeSinceMax = 0 ; |
roysandberg | 62:8e2fbe131b53 | 353 | Dly = 3 ; |
roysandberg | 62:8e2fbe131b53 | 354 | } |
roysandberg | 62:8e2fbe131b53 | 355 | lastDatum = datum ; |
roysandberg | 62:8e2fbe131b53 | 356 | return(pk) ; |
roysandberg | 62:8e2fbe131b53 | 357 | } |
roysandberg | 62:8e2fbe131b53 | 358 | |
roysandberg | 62:8e2fbe131b53 | 359 | /******************************************************************** |
roysandberg | 62:8e2fbe131b53 | 360 | median returns the median of an array of integers. It uses a slow |
roysandberg | 62:8e2fbe131b53 | 361 | sort algorithm, but these arrays are small, so it hardly matters. |
roysandberg | 62:8e2fbe131b53 | 362 | ********************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 363 | |
roysandberg | 62:8e2fbe131b53 | 364 | int median(int *array, int datnum) |
roysandberg | 62:8e2fbe131b53 | 365 | { |
roysandberg | 62:8e2fbe131b53 | 366 | int i, j, k, temp, sort[20] ; |
roysandberg | 62:8e2fbe131b53 | 367 | for(i = 0; i < datnum; ++i) |
roysandberg | 62:8e2fbe131b53 | 368 | sort[i] = array[i] ; |
roysandberg | 62:8e2fbe131b53 | 369 | for(i = 0; i < datnum; ++i) |
roysandberg | 62:8e2fbe131b53 | 370 | { |
roysandberg | 62:8e2fbe131b53 | 371 | temp = sort[i] ; |
roysandberg | 62:8e2fbe131b53 | 372 | for(j = 0; (temp < sort[j]) && (j < i) ; ++j) ; |
roysandberg | 62:8e2fbe131b53 | 373 | for(k = i - 1 ; k >= j ; --k) |
roysandberg | 62:8e2fbe131b53 | 374 | sort[k+1] = sort[k] ; |
roysandberg | 62:8e2fbe131b53 | 375 | sort[j] = temp ; |
roysandberg | 62:8e2fbe131b53 | 376 | } |
roysandberg | 62:8e2fbe131b53 | 377 | return(sort[datnum>>1]) ; |
roysandberg | 62:8e2fbe131b53 | 378 | } |
roysandberg | 62:8e2fbe131b53 | 379 | /* |
roysandberg | 62:8e2fbe131b53 | 380 | int median(int *array, int datnum) |
roysandberg | 62:8e2fbe131b53 | 381 | { |
roysandberg | 62:8e2fbe131b53 | 382 | long sum ; |
roysandberg | 62:8e2fbe131b53 | 383 | int i ; |
roysandberg | 62:8e2fbe131b53 | 384 | |
roysandberg | 62:8e2fbe131b53 | 385 | for(i = 0, sum = 0; i < datnum; ++i) |
roysandberg | 62:8e2fbe131b53 | 386 | sum += array[i] ; |
roysandberg | 62:8e2fbe131b53 | 387 | sum /= datnum ; |
roysandberg | 62:8e2fbe131b53 | 388 | return(sum) ; |
roysandberg | 62:8e2fbe131b53 | 389 | } */ |
roysandberg | 62:8e2fbe131b53 | 390 | |
roysandberg | 62:8e2fbe131b53 | 391 | /**************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 392 | thresh() calculates the detection threshold from the qrs median and noise |
roysandberg | 62:8e2fbe131b53 | 393 | median estimates. |
roysandberg | 62:8e2fbe131b53 | 394 | ****************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 395 | |
roysandberg | 62:8e2fbe131b53 | 396 | int thresh(int qmedian, int nmedian) |
roysandberg | 62:8e2fbe131b53 | 397 | { |
roysandberg | 62:8e2fbe131b53 | 398 | int thrsh, dmed ; |
roysandberg | 62:8e2fbe131b53 | 399 | double temp ; |
roysandberg | 62:8e2fbe131b53 | 400 | dmed = qmedian - nmedian ; |
roysandberg | 62:8e2fbe131b53 | 401 | /* thrsh = nmedian + (dmed>>2) + (dmed>>3) + (dmed>>4); */ |
roysandberg | 62:8e2fbe131b53 | 402 | temp = dmed ; |
roysandberg | 62:8e2fbe131b53 | 403 | temp *= TH ; |
roysandberg | 62:8e2fbe131b53 | 404 | dmed = temp ; |
roysandberg | 62:8e2fbe131b53 | 405 | thrsh = nmedian + dmed ; /* dmed * THRESHOLD */ |
roysandberg | 62:8e2fbe131b53 | 406 | return(thrsh) ; |
roysandberg | 62:8e2fbe131b53 | 407 | } |
roysandberg | 62:8e2fbe131b53 | 408 | |
roysandberg | 62:8e2fbe131b53 | 409 | /*********************************************************************** |
roysandberg | 62:8e2fbe131b53 | 410 | BLSCheck() reviews data to see if a baseline shift has occurred. |
roysandberg | 62:8e2fbe131b53 | 411 | This is done by looking for both positive and negative slopes of |
roysandberg | 62:8e2fbe131b53 | 412 | roughly the same magnitude in a 220 ms window. |
roysandberg | 62:8e2fbe131b53 | 413 | ***********************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 414 | |
roysandberg | 62:8e2fbe131b53 | 415 | int BLSCheck(int *dBuf,int dbPtr,int *maxder) |
roysandberg | 62:8e2fbe131b53 | 416 | { |
roysandberg | 62:8e2fbe131b53 | 417 | int max, min, maxt, mint, t, x ; |
roysandberg | 62:8e2fbe131b53 | 418 | max = min = 0 ; |
roysandberg | 62:8e2fbe131b53 | 419 | |
roysandberg | 62:8e2fbe131b53 | 420 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 421 | |
roysandberg | 62:8e2fbe131b53 | 422 | for(t = 0; t < MS220; ++t) |
roysandberg | 62:8e2fbe131b53 | 423 | { |
roysandberg | 62:8e2fbe131b53 | 424 | x = dBuf[dbPtr] ; |
roysandberg | 62:8e2fbe131b53 | 425 | if(x > max) |
roysandberg | 62:8e2fbe131b53 | 426 | { |
roysandberg | 62:8e2fbe131b53 | 427 | maxt = t ; |
roysandberg | 62:8e2fbe131b53 | 428 | max = x ; |
roysandberg | 62:8e2fbe131b53 | 429 | } |
roysandberg | 62:8e2fbe131b53 | 430 | else if(x < min) |
roysandberg | 62:8e2fbe131b53 | 431 | { |
roysandberg | 62:8e2fbe131b53 | 432 | mint = t ; |
roysandberg | 62:8e2fbe131b53 | 433 | min = x; |
roysandberg | 62:8e2fbe131b53 | 434 | } |
roysandberg | 62:8e2fbe131b53 | 435 | if(++dbPtr == DER_DELAY) |
roysandberg | 62:8e2fbe131b53 | 436 | dbPtr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 437 | } |
roysandberg | 62:8e2fbe131b53 | 438 | |
roysandberg | 62:8e2fbe131b53 | 439 | *maxder = max ; |
roysandberg | 62:8e2fbe131b53 | 440 | min = -min ; |
roysandberg | 62:8e2fbe131b53 | 441 | |
roysandberg | 62:8e2fbe131b53 | 442 | /* Possible beat if a maximum and minimum pair are found |
roysandberg | 62:8e2fbe131b53 | 443 | where the interval between them is less than 150 ms. */ |
roysandberg | 62:8e2fbe131b53 | 444 | |
roysandberg | 62:8e2fbe131b53 | 445 | if((max > (min>>3)) && (min > (max>>3)) && |
roysandberg | 62:8e2fbe131b53 | 446 | (abs(maxt - mint) < MS150)) |
roysandberg | 62:8e2fbe131b53 | 447 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 448 | |
roysandberg | 62:8e2fbe131b53 | 449 | else |
roysandberg | 62:8e2fbe131b53 | 450 | return(1) ; |
roysandberg | 62:8e2fbe131b53 | 451 | } |
roysandberg | 62:8e2fbe131b53 | 452 |