I've got some basic filter code setup (but not yet tested).
Dependencies: BLE_API Queue mbed nRF51822
Fork of BLE_HeartRate by
qrsfilt.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: qrsfilt.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 | qrsfilt.cpp filter functions to aid beat detecton in electrocardiograms. |
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 includes QRSFilt() and associated filtering files used for QRS |
roysandberg | 62:8e2fbe131b53 | 31 | detection. Only QRSFilt() and deriv1() are called by the QRS detector |
roysandberg | 62:8e2fbe131b53 | 32 | other functions can be hidden. |
roysandberg | 62:8e2fbe131b53 | 33 | |
roysandberg | 62:8e2fbe131b53 | 34 | Revisions: |
roysandberg | 62:8e2fbe131b53 | 35 | 5/13: Filter implementations have been modified to allow simplified |
roysandberg | 62:8e2fbe131b53 | 36 | modification for different sample rates. |
roysandberg | 62:8e2fbe131b53 | 37 | |
roysandberg | 62:8e2fbe131b53 | 38 | *******************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 39 | |
roysandberg | 62:8e2fbe131b53 | 40 | #include <mbed.h> |
roysandberg | 62:8e2fbe131b53 | 41 | //#include <math.h> |
roysandberg | 62:8e2fbe131b53 | 42 | #include "qrsdet.h" |
roysandberg | 62:8e2fbe131b53 | 43 | |
roysandberg | 62:8e2fbe131b53 | 44 | // Local Prototypes. |
roysandberg | 62:8e2fbe131b53 | 45 | |
roysandberg | 62:8e2fbe131b53 | 46 | int lpfilt( int datum ,int init) ; |
roysandberg | 62:8e2fbe131b53 | 47 | int hpfilt( int datum, int init ) ; |
roysandberg | 62:8e2fbe131b53 | 48 | int deriv1( int x0, int init ) ; |
roysandberg | 62:8e2fbe131b53 | 49 | int deriv2( int x0, int init ) ; |
roysandberg | 62:8e2fbe131b53 | 50 | int mvwint(int datum, int init) ; |
roysandberg | 62:8e2fbe131b53 | 51 | |
roysandberg | 62:8e2fbe131b53 | 52 | /****************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 53 | * Syntax: |
roysandberg | 62:8e2fbe131b53 | 54 | * int QRSFilter(int datum, int init) ; |
roysandberg | 62:8e2fbe131b53 | 55 | * Description: |
roysandberg | 62:8e2fbe131b53 | 56 | * QRSFilter() takes samples of an ECG signal as input and returns a sample of |
roysandberg | 62:8e2fbe131b53 | 57 | * a signal that is an estimate of the local energy in the QRS bandwidth. In |
roysandberg | 62:8e2fbe131b53 | 58 | * other words, the signal has a lump in it whenever a QRS complex, or QRS |
roysandberg | 62:8e2fbe131b53 | 59 | * complex like artifact occurs. The filters were originally designed for data |
roysandberg | 62:8e2fbe131b53 | 60 | * sampled at 200 samples per second, but they work nearly as well at sample |
roysandberg | 62:8e2fbe131b53 | 61 | * frequencies from 150 to 250 samples per second. |
roysandberg | 62:8e2fbe131b53 | 62 | * |
roysandberg | 62:8e2fbe131b53 | 63 | * The filter buffers and static variables are reset if a value other than |
roysandberg | 62:8e2fbe131b53 | 64 | * 0 is passed to QRSFilter through init. |
roysandberg | 62:8e2fbe131b53 | 65 | *******************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 66 | |
roysandberg | 62:8e2fbe131b53 | 67 | int QRSFilter(int datum,int init) |
roysandberg | 62:8e2fbe131b53 | 68 | { |
roysandberg | 62:8e2fbe131b53 | 69 | int fdatum ; |
roysandberg | 62:8e2fbe131b53 | 70 | |
roysandberg | 62:8e2fbe131b53 | 71 | if(init) |
roysandberg | 62:8e2fbe131b53 | 72 | { |
roysandberg | 62:8e2fbe131b53 | 73 | hpfilt( 0, 1 ) ; // Initialize filters. |
roysandberg | 62:8e2fbe131b53 | 74 | lpfilt( 0, 1 ) ; |
roysandberg | 62:8e2fbe131b53 | 75 | mvwint( 0, 1 ) ; |
roysandberg | 62:8e2fbe131b53 | 76 | deriv1( 0, 1 ) ; |
roysandberg | 62:8e2fbe131b53 | 77 | deriv2( 0, 1 ) ; |
roysandberg | 62:8e2fbe131b53 | 78 | } |
roysandberg | 62:8e2fbe131b53 | 79 | |
roysandberg | 62:8e2fbe131b53 | 80 | fdatum = lpfilt( datum, 0 ) ; // Low pass filter data. |
roysandberg | 62:8e2fbe131b53 | 81 | fdatum = hpfilt( fdatum, 0 ) ; // High pass filter data. |
roysandberg | 62:8e2fbe131b53 | 82 | fdatum = deriv2( fdatum, 0 ) ; // Take the derivative. |
roysandberg | 62:8e2fbe131b53 | 83 | fdatum = abs(fdatum) ; // Take the absolute value. |
roysandberg | 62:8e2fbe131b53 | 84 | fdatum = mvwint( fdatum, 0 ) ; // Average over an 80 ms window . |
roysandberg | 62:8e2fbe131b53 | 85 | return(fdatum) ; |
roysandberg | 62:8e2fbe131b53 | 86 | } |
roysandberg | 62:8e2fbe131b53 | 87 | |
roysandberg | 62:8e2fbe131b53 | 88 | |
roysandberg | 62:8e2fbe131b53 | 89 | /************************************************************************* |
roysandberg | 62:8e2fbe131b53 | 90 | * lpfilt() implements the digital filter represented by the difference |
roysandberg | 62:8e2fbe131b53 | 91 | * equation: |
roysandberg | 62:8e2fbe131b53 | 92 | * |
roysandberg | 62:8e2fbe131b53 | 93 | * y[n] = 2*y[n-1] - y[n-2] + x[n] - 2*x[t-24 ms] + x[t-48 ms] |
roysandberg | 62:8e2fbe131b53 | 94 | * |
roysandberg | 62:8e2fbe131b53 | 95 | * Note that the filter delay is (LPBUFFER_LGTH/2)-1 |
roysandberg | 62:8e2fbe131b53 | 96 | * |
roysandberg | 62:8e2fbe131b53 | 97 | **************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 98 | |
roysandberg | 62:8e2fbe131b53 | 99 | int lpfilt( int datum ,int init) |
roysandberg | 62:8e2fbe131b53 | 100 | { |
roysandberg | 62:8e2fbe131b53 | 101 | static long y1 = 0, y2 = 0 ; |
roysandberg | 62:8e2fbe131b53 | 102 | static int data[LPBUFFER_LGTH], ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 103 | long y0 ; |
roysandberg | 62:8e2fbe131b53 | 104 | int output, halfPtr ; |
roysandberg | 62:8e2fbe131b53 | 105 | if(init) |
roysandberg | 62:8e2fbe131b53 | 106 | { |
roysandberg | 62:8e2fbe131b53 | 107 | for(ptr = 0; ptr < LPBUFFER_LGTH; ++ptr) |
roysandberg | 62:8e2fbe131b53 | 108 | data[ptr] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 109 | y1 = y2 = 0 ; |
roysandberg | 62:8e2fbe131b53 | 110 | ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 111 | } |
roysandberg | 62:8e2fbe131b53 | 112 | halfPtr = ptr-(LPBUFFER_LGTH/2) ; // Use halfPtr to index |
roysandberg | 62:8e2fbe131b53 | 113 | if(halfPtr < 0) // to x[n-6]. |
roysandberg | 62:8e2fbe131b53 | 114 | halfPtr += LPBUFFER_LGTH ; |
roysandberg | 62:8e2fbe131b53 | 115 | y0 = (y1 << 1) - y2 + datum - (data[halfPtr] << 1) + data[ptr] ; |
roysandberg | 62:8e2fbe131b53 | 116 | y2 = y1; |
roysandberg | 62:8e2fbe131b53 | 117 | y1 = y0; |
roysandberg | 62:8e2fbe131b53 | 118 | output = y0 / ((LPBUFFER_LGTH*LPBUFFER_LGTH)/4); |
roysandberg | 62:8e2fbe131b53 | 119 | data[ptr] = datum ; // Stick most recent sample into |
roysandberg | 62:8e2fbe131b53 | 120 | if(++ptr == LPBUFFER_LGTH) // the circular buffer and update |
roysandberg | 62:8e2fbe131b53 | 121 | ptr = 0 ; // the buffer pointer. |
roysandberg | 62:8e2fbe131b53 | 122 | return(output) ; |
roysandberg | 62:8e2fbe131b53 | 123 | } |
roysandberg | 62:8e2fbe131b53 | 124 | |
roysandberg | 62:8e2fbe131b53 | 125 | |
roysandberg | 62:8e2fbe131b53 | 126 | /****************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 127 | * hpfilt() implements the high pass filter represented by the following |
roysandberg | 62:8e2fbe131b53 | 128 | * difference equation: |
roysandberg | 62:8e2fbe131b53 | 129 | * |
roysandberg | 62:8e2fbe131b53 | 130 | * y[n] = y[n-1] + x[n] - x[n-128 ms] |
roysandberg | 62:8e2fbe131b53 | 131 | * z[n] = x[n-64 ms] - y[n] ; |
roysandberg | 62:8e2fbe131b53 | 132 | * |
roysandberg | 62:8e2fbe131b53 | 133 | * Filter delay is (HPBUFFER_LGTH-1)/2 |
roysandberg | 62:8e2fbe131b53 | 134 | ******************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 135 | |
roysandberg | 62:8e2fbe131b53 | 136 | int hpfilt( int datum, int init ) |
roysandberg | 62:8e2fbe131b53 | 137 | { |
roysandberg | 62:8e2fbe131b53 | 138 | static long y=0 ; |
roysandberg | 62:8e2fbe131b53 | 139 | static int data[HPBUFFER_LGTH], ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 140 | int z, halfPtr ; |
roysandberg | 62:8e2fbe131b53 | 141 | |
roysandberg | 62:8e2fbe131b53 | 142 | if(init) |
roysandberg | 62:8e2fbe131b53 | 143 | { |
roysandberg | 62:8e2fbe131b53 | 144 | for(ptr = 0; ptr < HPBUFFER_LGTH; ++ptr) |
roysandberg | 62:8e2fbe131b53 | 145 | data[ptr] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 146 | ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 147 | y = 0 ; |
roysandberg | 62:8e2fbe131b53 | 148 | } |
roysandberg | 62:8e2fbe131b53 | 149 | |
roysandberg | 62:8e2fbe131b53 | 150 | y += datum - data[ptr]; |
roysandberg | 62:8e2fbe131b53 | 151 | halfPtr = ptr-(HPBUFFER_LGTH/2) ; |
roysandberg | 62:8e2fbe131b53 | 152 | if(halfPtr < 0) |
roysandberg | 62:8e2fbe131b53 | 153 | halfPtr += HPBUFFER_LGTH ; |
roysandberg | 62:8e2fbe131b53 | 154 | z = data[halfPtr] - (y / HPBUFFER_LGTH); |
roysandberg | 62:8e2fbe131b53 | 155 | |
roysandberg | 62:8e2fbe131b53 | 156 | data[ptr] = datum ; |
roysandberg | 62:8e2fbe131b53 | 157 | if(++ptr == HPBUFFER_LGTH) |
roysandberg | 62:8e2fbe131b53 | 158 | ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 159 | |
roysandberg | 62:8e2fbe131b53 | 160 | return( z ); |
roysandberg | 62:8e2fbe131b53 | 161 | } |
roysandberg | 62:8e2fbe131b53 | 162 | |
roysandberg | 62:8e2fbe131b53 | 163 | /***************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 164 | * deriv1 and deriv2 implement derivative approximations represented by |
roysandberg | 62:8e2fbe131b53 | 165 | * the difference equation: |
roysandberg | 62:8e2fbe131b53 | 166 | * |
roysandberg | 62:8e2fbe131b53 | 167 | * y[n] = x[n] - x[n - 10ms] |
roysandberg | 62:8e2fbe131b53 | 168 | * |
roysandberg | 62:8e2fbe131b53 | 169 | * Filter delay is DERIV_LENGTH/2 |
roysandberg | 62:8e2fbe131b53 | 170 | *****************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 171 | |
roysandberg | 62:8e2fbe131b53 | 172 | int deriv1(int x, int init) |
roysandberg | 62:8e2fbe131b53 | 173 | { |
roysandberg | 62:8e2fbe131b53 | 174 | static int derBuff[DERIV_LENGTH], derI = 0 ; |
roysandberg | 62:8e2fbe131b53 | 175 | int y ; |
roysandberg | 62:8e2fbe131b53 | 176 | |
roysandberg | 62:8e2fbe131b53 | 177 | if(init != 0) |
roysandberg | 62:8e2fbe131b53 | 178 | { |
roysandberg | 62:8e2fbe131b53 | 179 | for(derI = 0; derI < DERIV_LENGTH; ++derI) |
roysandberg | 62:8e2fbe131b53 | 180 | derBuff[derI] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 181 | derI = 0 ; |
roysandberg | 62:8e2fbe131b53 | 182 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 183 | } |
roysandberg | 62:8e2fbe131b53 | 184 | |
roysandberg | 62:8e2fbe131b53 | 185 | y = x - derBuff[derI] ; |
roysandberg | 62:8e2fbe131b53 | 186 | derBuff[derI] = x ; |
roysandberg | 62:8e2fbe131b53 | 187 | if(++derI == DERIV_LENGTH) |
roysandberg | 62:8e2fbe131b53 | 188 | derI = 0 ; |
roysandberg | 62:8e2fbe131b53 | 189 | return(y) ; |
roysandberg | 62:8e2fbe131b53 | 190 | } |
roysandberg | 62:8e2fbe131b53 | 191 | |
roysandberg | 62:8e2fbe131b53 | 192 | int deriv2(int x, int init) |
roysandberg | 62:8e2fbe131b53 | 193 | { |
roysandberg | 62:8e2fbe131b53 | 194 | static int derBuff[DERIV_LENGTH], derI = 0 ; |
roysandberg | 62:8e2fbe131b53 | 195 | int y ; |
roysandberg | 62:8e2fbe131b53 | 196 | |
roysandberg | 62:8e2fbe131b53 | 197 | if(init != 0) |
roysandberg | 62:8e2fbe131b53 | 198 | { |
roysandberg | 62:8e2fbe131b53 | 199 | for(derI = 0; derI < DERIV_LENGTH; ++derI) |
roysandberg | 62:8e2fbe131b53 | 200 | derBuff[derI] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 201 | derI = 0 ; |
roysandberg | 62:8e2fbe131b53 | 202 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 203 | } |
roysandberg | 62:8e2fbe131b53 | 204 | |
roysandberg | 62:8e2fbe131b53 | 205 | y = x - derBuff[derI] ; |
roysandberg | 62:8e2fbe131b53 | 206 | derBuff[derI] = x ; |
roysandberg | 62:8e2fbe131b53 | 207 | if(++derI == DERIV_LENGTH) |
roysandberg | 62:8e2fbe131b53 | 208 | derI = 0 ; |
roysandberg | 62:8e2fbe131b53 | 209 | return(y) ; |
roysandberg | 62:8e2fbe131b53 | 210 | } |
roysandberg | 62:8e2fbe131b53 | 211 | |
roysandberg | 62:8e2fbe131b53 | 212 | |
roysandberg | 62:8e2fbe131b53 | 213 | |
roysandberg | 62:8e2fbe131b53 | 214 | |
roysandberg | 62:8e2fbe131b53 | 215 | /***************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 216 | * mvwint() implements a moving window integrator. Actually, mvwint() averages |
roysandberg | 62:8e2fbe131b53 | 217 | * the signal values over the last WINDOW_WIDTH samples. |
roysandberg | 62:8e2fbe131b53 | 218 | *****************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 219 | |
roysandberg | 62:8e2fbe131b53 | 220 | int mvwint(int datum, int init) |
roysandberg | 62:8e2fbe131b53 | 221 | { |
roysandberg | 62:8e2fbe131b53 | 222 | static long sum = 0 ; |
roysandberg | 62:8e2fbe131b53 | 223 | static int data[WINDOW_WIDTH], ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 224 | int output; |
roysandberg | 62:8e2fbe131b53 | 225 | if(init) |
roysandberg | 62:8e2fbe131b53 | 226 | { |
roysandberg | 62:8e2fbe131b53 | 227 | for(ptr = 0; ptr < WINDOW_WIDTH ; ++ptr) |
roysandberg | 62:8e2fbe131b53 | 228 | data[ptr] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 229 | sum = 0 ; |
roysandberg | 62:8e2fbe131b53 | 230 | ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 231 | } |
roysandberg | 62:8e2fbe131b53 | 232 | sum += datum ; |
roysandberg | 62:8e2fbe131b53 | 233 | sum -= data[ptr] ; |
roysandberg | 62:8e2fbe131b53 | 234 | data[ptr] = datum ; |
roysandberg | 62:8e2fbe131b53 | 235 | if(++ptr == WINDOW_WIDTH) |
roysandberg | 62:8e2fbe131b53 | 236 | ptr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 237 | if((sum / WINDOW_WIDTH) > 32000) |
roysandberg | 62:8e2fbe131b53 | 238 | output = 32000 ; |
roysandberg | 62:8e2fbe131b53 | 239 | else |
roysandberg | 62:8e2fbe131b53 | 240 | output = sum / WINDOW_WIDTH ; |
roysandberg | 62:8e2fbe131b53 | 241 | return(output) ; |
roysandberg | 62:8e2fbe131b53 | 242 | } |