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

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?

UserRevisionLine numberNew contents of line
roysandberg 62:8e2fbe131b53 1 /*****************************************************************************
roysandberg 62:8e2fbe131b53 2 FILE: bdac.cpp
roysandberg 62:8e2fbe131b53 3 AUTHOR: Patrick S. Hamilton
roysandberg 62:8e2fbe131b53 4 REVISED: 5/13/2002
roysandberg 62:8e2fbe131b53 5 ___________________________________________________________________________
roysandberg 62:8e2fbe131b53 6
roysandberg 62:8e2fbe131b53 7 bdac.cpp: Beat Detection And Classification
roysandberg 62:8e2fbe131b53 8 Copywrite (C) 2001 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 bdac.cpp contains functions for handling Beat Detection And Classification.
roysandberg 62:8e2fbe131b53 31 The primary function calls a qrs detector. When a beat is detected it waits
roysandberg 62:8e2fbe131b53 32 until a sufficient number of samples from the beat have occurred. When the
roysandberg 62:8e2fbe131b53 33 beat is ready, BeatDetectAndClassify passes the beat and the timing
roysandberg 62:8e2fbe131b53 34 information on to the functions that actually classify the beat.
roysandberg 62:8e2fbe131b53 35
roysandberg 62:8e2fbe131b53 36 Functions in bdac.cpp require functions in the following files:
roysandberg 62:8e2fbe131b53 37 qrsfilt.cpp
roysandberg 62:8e2fbe131b53 38 qrsdet.cpp
roysandberg 62:8e2fbe131b53 39 classify.cpp
roysandberg 62:8e2fbe131b53 40 rythmchk.cpp
roysandberg 62:8e2fbe131b53 41 noisechk.cpp
roysandberg 62:8e2fbe131b53 42 analbeat.cpp
roysandberg 62:8e2fbe131b53 43 match.cpp
roysandberg 62:8e2fbe131b53 44 postclas.cpp
roysandberg 62:8e2fbe131b53 45
roysandberg 62:8e2fbe131b53 46 __________________________________________________________________________
roysandberg 62:8e2fbe131b53 47
roysandberg 62:8e2fbe131b53 48 Revisions:
roysandberg 62:8e2fbe131b53 49 5/13/02:
roysandberg 62:8e2fbe131b53 50 Encapsulated down sampling from input stream to beat template in
roysandberg 62:8e2fbe131b53 51 the function DownSampleBeat.
roysandberg 62:8e2fbe131b53 52
roysandberg 62:8e2fbe131b53 53 Constants related to time are derived from SAMPLE_RATE in qrsdet
roysandberg 62:8e2fbe131b53 54 and BEAT_SAMPLE_RATE in bcac.h.
roysandberg 62:8e2fbe131b53 55
roysandberg 62:8e2fbe131b53 56 *******************************************************************************/
roysandberg 62:8e2fbe131b53 57 #include "qrsdet.h" // For base SAMPLE_RATE
roysandberg 62:8e2fbe131b53 58 #include "bdac.h"
roysandberg 62:8e2fbe131b53 59
roysandberg 62:8e2fbe131b53 60 #define ECG_BUFFER_LENGTH 1000 // Should be long enough for a beat
roysandberg 62:8e2fbe131b53 61 // plus extra space to accommodate
roysandberg 62:8e2fbe131b53 62 // the maximum detection delay.
roysandberg 62:8e2fbe131b53 63 #define BEAT_QUE_LENGTH 10 // Length of que for beats awaiting
roysandberg 62:8e2fbe131b53 64 // classification. Because of
roysandberg 62:8e2fbe131b53 65 // detection delays, Multiple beats
roysandberg 62:8e2fbe131b53 66 // can occur before there is enough data
roysandberg 62:8e2fbe131b53 67 // to classify the first beat in the que.
roysandberg 62:8e2fbe131b53 68
roysandberg 62:8e2fbe131b53 69 // Internal function prototypes.
roysandberg 62:8e2fbe131b53 70
roysandberg 62:8e2fbe131b53 71 void DownSampleBeat(int *beatOut, int *beatIn) ;
roysandberg 62:8e2fbe131b53 72
roysandberg 62:8e2fbe131b53 73 // External function prototypes.
roysandberg 62:8e2fbe131b53 74
roysandberg 62:8e2fbe131b53 75 int QRSDet( int datum, int init ) ;
roysandberg 62:8e2fbe131b53 76 int NoiseCheck(int datum, int delay, int RR, int beatBegin, int beatEnd) ;
roysandberg 62:8e2fbe131b53 77 int Classify(int *newBeat,int rr, int noiseLevel, int *beatMatch, int *fidAdj, int init) ;
roysandberg 62:8e2fbe131b53 78 int GetDominantType(void) ;
roysandberg 62:8e2fbe131b53 79 int GetBeatEnd(int type) ;
roysandberg 62:8e2fbe131b53 80 int GetBeatBegin(int type) ;
roysandberg 62:8e2fbe131b53 81 int gcd(int x, int y) ;
roysandberg 62:8e2fbe131b53 82
roysandberg 62:8e2fbe131b53 83 // Global Variables
roysandberg 62:8e2fbe131b53 84
roysandberg 62:8e2fbe131b53 85 int ECGBuffer[ECG_BUFFER_LENGTH], ECGBufferIndex = 0 ; // Circular data buffer.
roysandberg 62:8e2fbe131b53 86 int BeatBuffer[BEATLGTH] ;
roysandberg 62:8e2fbe131b53 87 int BeatQue[BEAT_QUE_LENGTH], BeatQueCount = 0 ; // Buffer of detection delays.
roysandberg 62:8e2fbe131b53 88 int RRCount = 0 ;
roysandberg 62:8e2fbe131b53 89 int InitBeatFlag = 1 ;
roysandberg 62:8e2fbe131b53 90
roysandberg 62:8e2fbe131b53 91 /******************************************************************************
roysandberg 62:8e2fbe131b53 92 ResetBDAC() resets static variables required for beat detection and
roysandberg 62:8e2fbe131b53 93 classification.
roysandberg 62:8e2fbe131b53 94 *******************************************************************************/
roysandberg 62:8e2fbe131b53 95
roysandberg 62:8e2fbe131b53 96 void ResetBDAC(void)
roysandberg 62:8e2fbe131b53 97 {
roysandberg 62:8e2fbe131b53 98 int dummy ;
roysandberg 62:8e2fbe131b53 99 QRSDet(0,1) ; // Reset the qrs detector
roysandberg 62:8e2fbe131b53 100 RRCount = 0 ;
roysandberg 62:8e2fbe131b53 101 Classify(BeatBuffer,0,0,&dummy,&dummy,1) ;
roysandberg 62:8e2fbe131b53 102 InitBeatFlag = 1 ;
roysandberg 62:8e2fbe131b53 103 BeatQueCount = 0 ; // Flush the beat que.
roysandberg 62:8e2fbe131b53 104 }
roysandberg 62:8e2fbe131b53 105
roysandberg 62:8e2fbe131b53 106 /*****************************************************************************
roysandberg 62:8e2fbe131b53 107 Syntax:
roysandberg 62:8e2fbe131b53 108 int BeatDetectAndClassify(int ecgSample, int *beatType, *beatMatch) ;
roysandberg 62:8e2fbe131b53 109
roysandberg 62:8e2fbe131b53 110 Description:
roysandberg 62:8e2fbe131b53 111 BeatDetectAndClassify() implements a beat detector and classifier.
roysandberg 62:8e2fbe131b53 112 ECG samples are passed into BeatDetectAndClassify() one sample at a
roysandberg 62:8e2fbe131b53 113 time. BeatDetectAndClassify has been designed for a sample rate of
roysandberg 62:8e2fbe131b53 114 200 Hz. When a beat has been detected and classified the detection
roysandberg 62:8e2fbe131b53 115 delay is returned and the beat classification is returned through the
roysandberg 62:8e2fbe131b53 116 pointer *beatType. For use in debugging, the number of the template
roysandberg 62:8e2fbe131b53 117 that the beat was matched to is returned in via *beatMatch.
roysandberg 62:8e2fbe131b53 118
roysandberg 62:8e2fbe131b53 119 Returns
roysandberg 62:8e2fbe131b53 120 BeatDetectAndClassify() returns 0 if no new beat has been detected and
roysandberg 62:8e2fbe131b53 121 classified. If a beat has been classified, BeatDetectAndClassify returns
roysandberg 62:8e2fbe131b53 122 the number of samples since the approximate location of the R-wave.
roysandberg 62:8e2fbe131b53 123
roysandberg 62:8e2fbe131b53 124 ****************************************************************************/
roysandberg 62:8e2fbe131b53 125
roysandberg 62:8e2fbe131b53 126 int BeatDetectAndClassify(int ecgSample, int *beatType, int *beatMatch)
roysandberg 62:8e2fbe131b53 127 {
roysandberg 62:8e2fbe131b53 128 int detectDelay, rr, i, j ;
roysandberg 62:8e2fbe131b53 129 int noiseEst = 0, beatBegin, beatEnd ;
roysandberg 62:8e2fbe131b53 130 int domType ;
roysandberg 62:8e2fbe131b53 131 int fidAdj ;
roysandberg 62:8e2fbe131b53 132 int tempBeat[(SAMPLE_RATE/BEAT_SAMPLE_RATE)*BEATLGTH] ;
roysandberg 62:8e2fbe131b53 133
roysandberg 62:8e2fbe131b53 134 // Store new sample in the circular buffer.
roysandberg 62:8e2fbe131b53 135
roysandberg 62:8e2fbe131b53 136 ECGBuffer[ECGBufferIndex] = ecgSample ;
roysandberg 62:8e2fbe131b53 137 if(++ECGBufferIndex == ECG_BUFFER_LENGTH)
roysandberg 62:8e2fbe131b53 138 ECGBufferIndex = 0 ;
roysandberg 62:8e2fbe131b53 139
roysandberg 62:8e2fbe131b53 140 // Increment RRInterval count.
roysandberg 62:8e2fbe131b53 141
roysandberg 62:8e2fbe131b53 142 ++RRCount ;
roysandberg 62:8e2fbe131b53 143
roysandberg 62:8e2fbe131b53 144 // Increment detection delays for any beats in the que.
roysandberg 62:8e2fbe131b53 145
roysandberg 62:8e2fbe131b53 146 for(i = 0; i < BeatQueCount; ++i)
roysandberg 62:8e2fbe131b53 147 ++BeatQue[i] ;
roysandberg 62:8e2fbe131b53 148
roysandberg 62:8e2fbe131b53 149 // Run the sample through the QRS detector.
roysandberg 62:8e2fbe131b53 150
roysandberg 62:8e2fbe131b53 151 detectDelay = QRSDet(ecgSample,0) ;
roysandberg 62:8e2fbe131b53 152 if(detectDelay != 0)
roysandberg 62:8e2fbe131b53 153 {
roysandberg 62:8e2fbe131b53 154 BeatQue[BeatQueCount] = detectDelay ;
roysandberg 62:8e2fbe131b53 155 ++BeatQueCount ;
roysandberg 62:8e2fbe131b53 156 }
roysandberg 62:8e2fbe131b53 157
roysandberg 62:8e2fbe131b53 158 // Return if no beat is ready for classification.
roysandberg 62:8e2fbe131b53 159
roysandberg 62:8e2fbe131b53 160 if((BeatQue[0] < (BEATLGTH-FIDMARK)*(SAMPLE_RATE/BEAT_SAMPLE_RATE))
roysandberg 62:8e2fbe131b53 161 || (BeatQueCount == 0))
roysandberg 62:8e2fbe131b53 162 {
roysandberg 62:8e2fbe131b53 163 NoiseCheck(ecgSample,0,rr, beatBegin, beatEnd) ; // Update noise check buffer
roysandberg 62:8e2fbe131b53 164 return 0 ;
roysandberg 62:8e2fbe131b53 165 }
roysandberg 62:8e2fbe131b53 166
roysandberg 62:8e2fbe131b53 167 // Otherwise classify the beat at the head of the que.
roysandberg 62:8e2fbe131b53 168
roysandberg 62:8e2fbe131b53 169 rr = RRCount - BeatQue[0] ; // Calculate the R-to-R interval
roysandberg 62:8e2fbe131b53 170 detectDelay = RRCount = BeatQue[0] ;
roysandberg 62:8e2fbe131b53 171
roysandberg 62:8e2fbe131b53 172 // Estimate low frequency noise in the beat.
roysandberg 62:8e2fbe131b53 173 // Might want to move this into classify().
roysandberg 62:8e2fbe131b53 174
roysandberg 62:8e2fbe131b53 175 domType = GetDominantType() ;
roysandberg 62:8e2fbe131b53 176 if(domType == -1)
roysandberg 62:8e2fbe131b53 177 {
roysandberg 62:8e2fbe131b53 178 beatBegin = MS250 ;
roysandberg 62:8e2fbe131b53 179 beatEnd = MS300 ;
roysandberg 62:8e2fbe131b53 180 }
roysandberg 62:8e2fbe131b53 181 else
roysandberg 62:8e2fbe131b53 182 {
roysandberg 62:8e2fbe131b53 183 beatBegin = (SAMPLE_RATE/BEAT_SAMPLE_RATE)*(FIDMARK-GetBeatBegin(domType)) ;
roysandberg 62:8e2fbe131b53 184 beatEnd = (SAMPLE_RATE/BEAT_SAMPLE_RATE)*(GetBeatEnd(domType)-FIDMARK) ;
roysandberg 62:8e2fbe131b53 185 }
roysandberg 62:8e2fbe131b53 186 noiseEst = NoiseCheck(ecgSample,detectDelay,rr,beatBegin,beatEnd) ;
roysandberg 62:8e2fbe131b53 187
roysandberg 62:8e2fbe131b53 188 // Copy the beat from the circular buffer to the beat buffer
roysandberg 62:8e2fbe131b53 189 // and reduce the sample rate by averageing pairs of data
roysandberg 62:8e2fbe131b53 190 // points.
roysandberg 62:8e2fbe131b53 191
roysandberg 62:8e2fbe131b53 192 j = ECGBufferIndex - detectDelay - (SAMPLE_RATE/BEAT_SAMPLE_RATE)*FIDMARK ;
roysandberg 62:8e2fbe131b53 193 if(j < 0) j += ECG_BUFFER_LENGTH ;
roysandberg 62:8e2fbe131b53 194
roysandberg 62:8e2fbe131b53 195 for(i = 0; i < (SAMPLE_RATE/BEAT_SAMPLE_RATE)*BEATLGTH; ++i)
roysandberg 62:8e2fbe131b53 196 {
roysandberg 62:8e2fbe131b53 197 tempBeat[i] = ECGBuffer[j] ;
roysandberg 62:8e2fbe131b53 198 if(++j == ECG_BUFFER_LENGTH)
roysandberg 62:8e2fbe131b53 199 j = 0 ;
roysandberg 62:8e2fbe131b53 200 }
roysandberg 62:8e2fbe131b53 201
roysandberg 62:8e2fbe131b53 202 DownSampleBeat(BeatBuffer,tempBeat) ;
roysandberg 62:8e2fbe131b53 203
roysandberg 62:8e2fbe131b53 204 // Update the QUE.
roysandberg 62:8e2fbe131b53 205
roysandberg 62:8e2fbe131b53 206 for(i = 0; i < BeatQueCount-1; ++i)
roysandberg 62:8e2fbe131b53 207 BeatQue[i] = BeatQue[i+1] ;
roysandberg 62:8e2fbe131b53 208 --BeatQueCount ;
roysandberg 62:8e2fbe131b53 209
roysandberg 62:8e2fbe131b53 210
roysandberg 62:8e2fbe131b53 211 // Skip the first beat.
roysandberg 62:8e2fbe131b53 212
roysandberg 62:8e2fbe131b53 213 if(InitBeatFlag)
roysandberg 62:8e2fbe131b53 214 {
roysandberg 62:8e2fbe131b53 215 InitBeatFlag = 0 ;
roysandberg 62:8e2fbe131b53 216 *beatType = 13 ;
roysandberg 62:8e2fbe131b53 217 *beatMatch = 0 ;
roysandberg 62:8e2fbe131b53 218 fidAdj = 0 ;
roysandberg 62:8e2fbe131b53 219 }
roysandberg 62:8e2fbe131b53 220
roysandberg 62:8e2fbe131b53 221 // Classify all other beats.
roysandberg 62:8e2fbe131b53 222
roysandberg 62:8e2fbe131b53 223 else
roysandberg 62:8e2fbe131b53 224 {
roysandberg 62:8e2fbe131b53 225 *beatType = Classify(BeatBuffer,rr,noiseEst,beatMatch,&fidAdj,0) ;
roysandberg 62:8e2fbe131b53 226 fidAdj *= SAMPLE_RATE/BEAT_SAMPLE_RATE ;
roysandberg 62:8e2fbe131b53 227 }
roysandberg 62:8e2fbe131b53 228
roysandberg 62:8e2fbe131b53 229 // Ignore detection if the classifier decides that this
roysandberg 62:8e2fbe131b53 230 // was the trailing edge of a PVC.
roysandberg 62:8e2fbe131b53 231
roysandberg 62:8e2fbe131b53 232 if(*beatType == 100)
roysandberg 62:8e2fbe131b53 233 {
roysandberg 62:8e2fbe131b53 234 RRCount += rr ;
roysandberg 62:8e2fbe131b53 235 return(0) ;
roysandberg 62:8e2fbe131b53 236 }
roysandberg 62:8e2fbe131b53 237
roysandberg 62:8e2fbe131b53 238 // Limit the fiducial mark adjustment in case of problems with
roysandberg 62:8e2fbe131b53 239 // beat onset and offset estimation.
roysandberg 62:8e2fbe131b53 240
roysandberg 62:8e2fbe131b53 241 if(fidAdj > MS80)
roysandberg 62:8e2fbe131b53 242 fidAdj = MS80 ;
roysandberg 62:8e2fbe131b53 243 else if(fidAdj < -MS80)
roysandberg 62:8e2fbe131b53 244 fidAdj = -MS80 ;
roysandberg 62:8e2fbe131b53 245
roysandberg 62:8e2fbe131b53 246 return(detectDelay-fidAdj) ;
roysandberg 62:8e2fbe131b53 247 }
roysandberg 62:8e2fbe131b53 248
roysandberg 62:8e2fbe131b53 249 void DownSampleBeat(int *beatOut, int *beatIn)
roysandberg 62:8e2fbe131b53 250 {
roysandberg 62:8e2fbe131b53 251 int i ;
roysandberg 62:8e2fbe131b53 252
roysandberg 62:8e2fbe131b53 253 for(i = 0; i < BEATLGTH; ++i)
roysandberg 62:8e2fbe131b53 254 beatOut[i] = (beatIn[i<<2]+
roysandberg 62:8e2fbe131b53 255 beatIn[(i<<2)+1]+
roysandberg 62:8e2fbe131b53 256 beatIn[(i<<2)+2]+
roysandberg 62:8e2fbe131b53 257 beatIn[(i<<2)+3]
roysandberg 62:8e2fbe131b53 258 )>>2 ;
roysandberg 62:8e2fbe131b53 259 }
roysandberg 62:8e2fbe131b53 260
roysandberg 62:8e2fbe131b53 261
roysandberg 62:8e2fbe131b53 262 void DownSampleBeatOld(int *beatOut, int *beatIn)
roysandberg 62:8e2fbe131b53 263 {
roysandberg 62:8e2fbe131b53 264 int i ;
roysandberg 62:8e2fbe131b53 265
roysandberg 62:8e2fbe131b53 266 for(i = 0; i < BEATLGTH; ++i)
roysandberg 62:8e2fbe131b53 267 beatOut[i] = (beatIn[i<<1]+beatIn[(i<<1)+1])>>1 ;
roysandberg 62:8e2fbe131b53 268 }