Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
qrsfilt-inl.h
00001 #ifndef ECG_THIRD_EPLIMITED_QRSFILT_INL_H_ 00002 #define ECG_THIRD_EPLIMITED_QRSFILT_INL_H_ 00003 // Modified to OOP class sturcture 00004 // Author: Kasturi Rangan Raghavan 00005 /***************************************************************************** 00006 FILE: qrsfilt.cpp 00007 AUTHOR: Patrick S. Hamilton 00008 REVISED: 5/13/2002 00009 ___________________________________________________________________________ 00010 00011 qrsfilt.cpp filter functions to aid beat detecton in electrocardiograms. 00012 Copywrite (C) 2000 Patrick S. Hamilton 00013 00014 This file is free software; you can redistribute it and/or modify it under 00015 the terms of the GNU Library General Public License as published by the Free 00016 Software Foundation; either version 2 of the License, or (at your option) any 00017 later version. 00018 00019 This software is distributed in the hope that it will be useful, but WITHOUT ANY 00020 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 00021 PARTICULAR PURPOSE. See the GNU Library General Public License for more 00022 details. 00023 00024 You should have received a copy of the GNU Library General Public License along 00025 with this library; if not, write to the Free Software Foundation, Inc., 59 00026 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00027 00028 You may contact the author by e-mail (pat@eplimited.edu) or postal mail 00029 (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville, 00030 MA 02143 USA). For updates to this software, please visit our website 00031 (http://www.eplimited.com). 00032 __________________________________________________________________________ 00033 00034 This file includes QRSFilt() and associated filtering files used for QRS 00035 detection. Only QRSFilt() and deriv1() are called by the QRS detector 00036 other functions can be hidden. 00037 Revisions: 00038 5/13: Filter implementations have been modified to allow simplified 00039 modification for different sample rates. 00040 *******************************************************************************/ 00041 #include <cstdlib> 00042 00043 namespace qrsdet { 00044 00045 /****************************************************************************** 00046 * Syntax: 00047 * int QRSFilter(int datum, int init) ; 00048 * Description: 00049 * QRSFilter() takes samples of an ECG signal as input and returns a sample of 00050 * a signal that is an estimate of the local energy in the QRS bandwidth. In 00051 * other words, the signal has a lump in it whenever a QRS complex, or QRS 00052 * complex like artifact occurs. The filters were originally designed for data 00053 * sampled at 200 samples per second, but they work nearly as well at sample 00054 * frequencies from 150 to 250 samples per second. 00055 * 00056 * The filter buffers and static variables are reset if a value other than 00057 * 0 is passed to QRSFilter through init. 00058 *******************************************************************************/ 00059 00060 class RingBuffer { 00061 public: 00062 RingBuffer(size_t buf_size) { 00063 this->buf_size = buf_size; 00064 data = new int[buf_size]; 00065 for (ptr = 0; ptr < buf_size; ptr++) 00066 data[ptr] = 0; 00067 ptr = 0; 00068 } 00069 00070 ~RingBuffer() { 00071 delete[] data; 00072 } 00073 00074 int* GetRange(int begin, int end, int* n) { 00075 *n = end - begin; 00076 int* mem = new int[*n]; 00077 for (int p = begin, i = 0; p < end; p++, i++) { 00078 mem[i] = GetValue(p); 00079 } 00080 return mem; 00081 } 00082 00083 void SetValue(int value, int offset = 0) { 00084 int set_ptr = ptr + offset; 00085 if (set_ptr < 0) 00086 set_ptr += static_cast<int>(buf_size); 00087 set_ptr %= buf_size; 00088 data[set_ptr] = value; 00089 } 00090 00091 int GetValue(int offset = 0) { 00092 int get_ptr = ptr + offset; 00093 if (get_ptr < 0) 00094 get_ptr += static_cast<int>(buf_size); 00095 get_ptr %= buf_size; 00096 return data[get_ptr]; 00097 } 00098 00099 void Increment() { 00100 if (++ptr == buf_size) 00101 ptr = 0; 00102 } 00103 00104 protected: 00105 size_t buf_size; 00106 int* data; 00107 int ptr; 00108 }; 00109 00110 class DelayFilter : public RingBuffer { 00111 public: 00112 explicit DelayFilter(size_t buf_size) : RingBuffer(buf_size) {} 00113 int Process(int datum) { 00114 int output = GetValue(); 00115 SetValue(datum); 00116 Increment(); 00117 return output; 00118 } 00119 }; 00120 00121 /************************************************************************* 00122 * lpfilt() implements the digital filter represented by the difference 00123 * equation: 00124 * 00125 * y[n] = 2*y[n-1] - y[n-2] + x[n] - 2*x[t-24 ms] + x[t-48 ms] 00126 * 00127 * Note that the filter delay is (LPBUFFER_LGTH/2)-1 00128 * 00129 **************************************************************************/ 00130 class LowPassFilter : public RingBuffer { 00131 public: 00132 LowPassFilter(size_t buf_size) : RingBuffer(buf_size) { 00133 y1 = 0; 00134 y2 = 0; 00135 } 00136 00137 int Process(int datum) { 00138 int length = static_cast<int>(buf_size); 00139 long y0; 00140 int output, halfPtr; 00141 halfPtr = ptr - length / 2; 00142 if (halfPtr < 0) // to x[n-6]. 00143 halfPtr += length; 00144 y0 = (y1 << 1) - y2 + datum - (data[halfPtr] << 1) + data[ptr]; 00145 y2 = y1; 00146 y1 = y0; 00147 output = y0 / ((length * length) / 4); 00148 data[ptr] = datum; 00149 Increment(); 00150 return output; 00151 } 00152 00153 private: 00154 long y1, y2; 00155 }; 00156 00157 00158 /****************************************************************************** 00159 * hpfilt() implements the high pass filter represented by the following 00160 * difference equation: 00161 * 00162 * y[n] = y[n-1] + x[n] - x[n-128 ms] 00163 * z[n] = x[n-64 ms] - y[n] ; 00164 * 00165 * Filter delay is (buf_size-1)/2 00166 ******************************************************************************/ 00167 class HighPassFilter : public RingBuffer { 00168 public: 00169 HighPassFilter(size_t buf_size) : RingBuffer(buf_size) { 00170 y = 0; 00171 } 00172 00173 int Process(int datum) { 00174 int output, halfPtr; 00175 int length = static_cast<int>(buf_size); 00176 y += datum - data[ptr]; 00177 halfPtr = ptr - (length / 2); 00178 if (halfPtr < 0) 00179 halfPtr += length; 00180 output = data[halfPtr] - (y / length); 00181 data[ptr] = datum; 00182 Increment(); 00183 return output; 00184 } 00185 00186 private: 00187 long y; 00188 }; 00189 00190 00191 /***************************************************************************** 00192 * deriv1 and deriv2 implement derivative approximations represented by 00193 * the difference equation: 00194 * 00195 * y[n] = x[n] - x[n - 10ms] 00196 * 00197 * Filter delay is DERIV_LENGTH/2 00198 *****************************************************************************/ 00199 class DerivFilter : public RingBuffer { 00200 public: 00201 DerivFilter(size_t buf_size) : RingBuffer(buf_size) {} 00202 00203 int Process(int datum) { 00204 int y; 00205 y = datum - data[ptr]; 00206 data[ptr] = datum; 00207 Increment(); 00208 return y; 00209 } 00210 }; 00211 00212 00213 /***************************************************************************** 00214 * mvwint() implements a moving window integrator. Actually, mvwint() averages 00215 * the signal values over the last WINDOW_WIDTH samples. 00216 *****************************************************************************/ 00217 class MovingWindowAverager : public RingBuffer { 00218 public: 00219 MovingWindowAverager(size_t buf_size) : RingBuffer(buf_size) { 00220 sum = 0; 00221 } 00222 00223 int Process(int datum) { 00224 int output; 00225 int length = static_cast<int>(buf_size); 00226 sum += datum; 00227 sum -= data[ptr]; 00228 data[ptr] = datum; 00229 Increment(); 00230 if((sum / length) > 32000) 00231 output = 32000 ; 00232 else 00233 output = sum / length; 00234 return output; 00235 } 00236 private: 00237 long sum; 00238 }; 00239 00240 struct QRSFilterParams { 00241 QRSFilterParams(double ts) { 00242 this->ts = ts; 00243 } 00244 00245 size_t get_ms(size_t t) const { return static_cast<double>(t) / ts + 0.5; } 00246 00247 double get_ts() const { return ts; } 00248 00249 size_t get_lp_length() const { return get_ms(50); } 00250 00251 size_t get_hp_length() const { return get_ms(125); } 00252 00253 size_t get_d_length() const { return get_ms(10); } 00254 00255 size_t get_window_length() const { return get_ms(80); } 00256 00257 size_t get_preblank() const { return get_ms(200); } 00258 00259 size_t get_filter_delay() const { 00260 return get_d_length() / 2.0 + 00261 get_lp_length() / 2.0 - 1.0 + 00262 (get_hp_length() - 1.0) / 2.0 + 00263 get_preblank(); 00264 } 00265 00266 size_t get_der_delay() const { 00267 return get_filter_delay() + get_window_length() + get_ms(100); 00268 } 00269 00270 private: 00271 double ts; 00272 }; 00273 00274 class QRSFilter { 00275 public: 00276 QRSFilter(QRSFilterParams params) : 00277 lp_filter(params.get_lp_length()), 00278 hp_filter(params.get_hp_length()), 00279 d_filter(params.get_d_length()), 00280 mwa_filter(params.get_window_length()) {} 00281 00282 int Process(int datum) { 00283 datum = lp_filter.Process(datum); 00284 datum = hp_filter.Process(datum); 00285 datum = d_filter.Process(datum); 00286 datum = abs(datum); 00287 datum = mwa_filter.Process(datum); 00288 return datum; 00289 } 00290 00291 private: 00292 LowPassFilter lp_filter; 00293 HighPassFilter hp_filter; 00294 DerivFilter d_filter; 00295 MovingWindowAverager mwa_filter; 00296 }; 00297 00298 /************************************************************** 00299 * peak() takes a datum as input and returns a peak height 00300 * when the signal returns to half its peak height, or 00301 **************************************************************/ 00302 class PeakDetector { 00303 public: 00304 PeakDetector(const QRSFilterParams& params) { 00305 max = 0; 00306 timeSinceMax = 0; 00307 lastDatum = 0; 00308 ms_95_ = params.get_ms(95); 00309 } 00310 00311 // what is Dly used for? 00312 int Process(int datum) { 00313 int pk = 0; 00314 if(timeSinceMax > 0) 00315 ++timeSinceMax ; 00316 00317 if((datum > lastDatum) && (datum > max)) { 00318 max = datum; 00319 if(max > 2) 00320 timeSinceMax = 1; 00321 } else if(datum < (max >> 1)) { 00322 pk = max ; 00323 max = 0 ; 00324 timeSinceMax = 0 ; 00325 Dly = 0 ; 00326 } else if(timeSinceMax > ms_95_) { 00327 pk = max; 00328 max = 0; 00329 timeSinceMax = 0; 00330 Dly = 3; 00331 } 00332 lastDatum = datum ; 00333 return pk; 00334 } 00335 private: 00336 size_t ms_95_; 00337 int Dly; // unused? 00338 int max; 00339 int timeSinceMax; 00340 int lastDatum; 00341 }; 00342 00343 /******************************************************************** 00344 mean returns the mean of an array of integers. It uses a slow 00345 sort algorithm, but these arrays are small, so it hardly matters. 00346 ********************************************************************/ 00347 int mean(int *array, int n) 00348 { 00349 long sum = 0; 00350 for(int i = 0; i < n; ++i) 00351 sum += array[i]; 00352 sum /= n; 00353 return sum; 00354 } 00355 00356 /**************************************************************************** 00357 thresh() calculates the detection threshold from the qrs mean and noise 00358 mean estimates. 00359 ****************************************************************************/ 00360 int detection_thresh(int qmean, int nmean) { 00361 static const float TH = .3125; 00362 int thrsh, dmed; 00363 float temp; 00364 dmed = qmean - nmean; 00365 temp = dmed; 00366 temp *= TH; 00367 dmed = temp; 00368 thrsh = nmean + dmed; 00369 return thrsh; 00370 } 00371 00372 /*********************************************************************** 00373 BLSCheck() reviews data to see if a baseline shift has occurred. 00374 This is done by looking for both positive and negative slopes of 00375 roughly the same magnitude in a 220 ms window. 00376 ***********************************************************************/ 00377 class BLSCheckBuffer : RingBuffer { 00378 public: 00379 BLSCheckBuffer(const QRSFilterParams& params) 00380 : RingBuffer(params.get_der_delay()) { 00381 ms150_ = params.get_ms(150); 00382 ms220_ = params.get_ms(220); 00383 } 00384 00385 int Process(int datum) { 00386 data[ptr] = datum; 00387 Increment(); 00388 return datum; 00389 } 00390 00391 int BLSCheck(int *maxder) { 00392 int max = 0, min = 0; 00393 int maxt, mint; 00394 int saved_ptr = ptr; 00395 for(int t = 0; t < ms220_; t++) { 00396 00397 int x = data[ptr]; 00398 if(x > max) { 00399 maxt = t; 00400 max = x; 00401 } else if(x < min) { 00402 mint = t; 00403 min = x; 00404 } 00405 Increment(); 00406 } 00407 ptr = saved_ptr; 00408 *maxder = max; 00409 min = -min; 00410 00411 /* Possible beat if a maximum and minimum pair are found 00412 where the interval between them is less than 150 ms. */ 00413 00414 if((max > (min >> 3)) && (min > (max >> 3)) && 00415 (abs(maxt - mint) < ms150_)) 00416 return 0; 00417 else 00418 return 1; 00419 } 00420 private: 00421 size_t ms150_, ms220_; 00422 }; 00423 00424 00425 } // namespace qrsdet 00426 00427 #endif // ECG_THIRD_EPLIMITED_QRSFILT_INL_H_
Generated on Tue Jul 12 2022 20:36:21 by
1.7.2