Roy Sandberg / Mbed 2 deprecated BLE_ECG

Dependencies:   BLE_API Queue mbed nRF51822

Fork of BLE_HeartRate by Bluetooth Low Energy

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers rythmchk.cpp Source File

rythmchk.cpp

00001 /*****************************************************************************
00002 FILE:  rythmchk.cpp
00003 AUTHOR: Patrick S. Hamilton
00004 REVISED:    5/13/2002
00005     10/9/2001: 1.1 Call premature for 12.5% difference with very regular
00006                     rhythms.  If short after VV go to QQ.
00007     5/13/2002: Check for NNVNNNV pattern when last interval was QQ.
00008   ___________________________________________________________________________
00009 
00010 rythmchk.cpp: Rhythm Check
00011 Copywrite (C) 2001 Patrick S. Hamilton
00012 
00013 This file is free software; you can redistribute it and/or modify it under
00014 the terms of the GNU Library General Public License as published by the Free
00015 Software Foundation; either version 2 of the License, or (at your option) any
00016 later version.
00017 
00018 This software is distributed in the hope that it will be useful, but WITHOUT ANY
00019 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
00020 PARTICULAR PURPOSE.  See the GNU Library General Public License for more
00021 details.
00022 
00023 You should have received a copy of the GNU Library General Public License along
00024 with this library; if not, write to the Free Software Foundation, Inc., 59
00025 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00026 
00027 You may contact the author by e-mail (pat@eplimited.edu) or postal mail
00028 (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
00029 MA 02143 USA).  For updates to this software, please visit our website
00030 (http://www.eplimited.com).
00031   __________________________________________________________________________
00032 
00033     Rythmchk.cpp contains functions for classifying RR intervals as either
00034     NORMAL, PVC, or UNKNOWN.  Intervals classified as NORMAL are presumed to
00035     end with normal beats, intervals classified as PVC are presumed to end
00036     with a premature contraction, and intervals classified as unknown do
00037     not fit any pattern that the rhythm classifier recognizes.
00038 
00039     NORMAL intervals can be part of a normal (regular) rhythm, normal beats
00040     following a premature beats, or normal beats following runs of ventricular
00041     beats.  PVC intervals can be short intervals following a regular rhythm,
00042     short intervals that are part of a run of short intervals following a
00043     regular rhythm, or short intervals that are part of a bigeminal rhythm.
00044 
00045 **************************************************************************/
00046 
00047 #include <mbed.h>
00048 #include "qrsdet.h"     // For time intervals.
00049 #include "ecgcodes.h"   // Defines codes of NORMAL, PVC, and UNKNOWN.
00050 //#include <stdlib.h>     // For abs()
00051 
00052 // Define RR interval types.
00053 
00054 #define QQ  0   // Unknown-Unknown interval.
00055 #define NN  1   // Normal-Normal interval.
00056 #define NV  2   // Normal-PVC interval.
00057 #define VN  3   // PVC-Normal interval.
00058 #define VV  4   // PVC-PVC interval.
00059 
00060 #define RBB_LENGTH  8
00061 #define LEARNING    0
00062 #define READY   1
00063 
00064 #define BRADY_LIMIT MS1500
00065 
00066 // Local prototypes.
00067 int RRMatch(int rr0,int rr1) ;
00068 int RRShort(int rr0,int rr1) ;
00069 int RRShort2(int *rrIntervals, int *rrTypes) ;
00070 int RRMatch2(int rr0,int rr1) ;
00071 
00072 // Global variables.
00073 int RRBuffer[RBB_LENGTH], RRTypes[RBB_LENGTH], BeatCount = 0;
00074 int ClassifyState   = LEARNING ;
00075 
00076 int BigeminyFlag ;
00077 
00078 /***************************************************************************
00079     ResetRhythmChk() resets static variables used for rhythm classification.
00080 ****************************************************************************/
00081 
00082 void ResetRhythmChk(void)
00083     {
00084     BeatCount = 0 ;
00085     ClassifyState = LEARNING ;
00086     }
00087 
00088 /*****************************************************************************
00089     RhythmChk() takes an R-to-R interval as input and, based on previous R-to-R
00090     intervals, classifys the interval as NORMAL, PVC, or UNKNOWN.
00091 ******************************************************************************/
00092 
00093 int RhythmChk(int rr)
00094     {
00095     int i, regular = 1 ;
00096     int NNEst, NVEst ;
00097 
00098     BigeminyFlag = 0 ;
00099 
00100     // Wait for at least 4 beats before classifying anything.
00101 
00102     if(BeatCount < 4)
00103         {
00104         if(++BeatCount == 4)
00105             ClassifyState = READY ;
00106         }
00107 
00108     // Stick the new RR interval into the RR interval Buffer.
00109 
00110     for(i = RBB_LENGTH-1; i > 0; --i)
00111         {
00112         RRBuffer[i] = RRBuffer[i-1] ;
00113         RRTypes[i] = RRTypes[i-1] ;
00114         }
00115 
00116     RRBuffer[0] = rr ;
00117 
00118     if(ClassifyState == LEARNING)
00119         {
00120         RRTypes[0] = QQ ;
00121         return(UNKNOWN) ;
00122         }
00123 
00124     // If we couldn't tell what the last interval was...
00125 
00126     if(RRTypes[1] == QQ)
00127         {
00128         for(i = 0, regular = 1; i < 3; ++i)
00129             if(RRMatch(RRBuffer[i],RRBuffer[i+1]) == 0)
00130                 regular = 0 ;
00131 
00132         // If this, and the last three intervals matched, classify
00133         // it as Normal-Normal.
00134 
00135         if(regular == 1)
00136             {
00137             RRTypes[0] = NN ;
00138             return(NORMAL) ;
00139             }
00140 
00141         // Check for bigeminy.
00142         // Call bigeminy if every other RR matches and
00143         // consecutive beats do not match.
00144 
00145         for(i = 0, regular = 1; i < 6; ++i)
00146             if(RRMatch(RRBuffer[i],RRBuffer[i+2]) == 0)
00147                 regular = 0 ;
00148         for(i = 0; i < 6; ++i)
00149             if(RRMatch(RRBuffer[i],RRBuffer[i+1]) != 0)
00150                 regular = 0 ;
00151 
00152         if(regular == 1)
00153             {
00154             BigeminyFlag = 1 ;
00155             if(RRBuffer[0] < RRBuffer[1])
00156                 {
00157                 RRTypes[0] = NV ;
00158                 RRTypes[1] = VN ;
00159                 return(PVC) ;
00160                 }
00161             else
00162                 {
00163                 RRTypes[0] = VN ;
00164                 RRTypes[1] = NV ;
00165                 return(NORMAL) ;
00166                 }
00167             }
00168 
00169         // Check for NNVNNNV pattern.
00170 
00171         if(RRShort(RRBuffer[0],RRBuffer[1]) && RRMatch(RRBuffer[1],RRBuffer[2])
00172             && RRMatch(RRBuffer[2]*2,RRBuffer[3]+RRBuffer[4]) &&
00173             RRMatch(RRBuffer[4],RRBuffer[0]) && RRMatch(RRBuffer[5],RRBuffer[2]))
00174             {
00175             RRTypes[0] = NV ;
00176             RRTypes[1] = NN ;
00177             return(PVC) ;
00178             }
00179 
00180         // If the interval is not part of a
00181         // bigeminal or regular pattern, give up.
00182 
00183         else
00184             {
00185             RRTypes[0] = QQ ;
00186             return(UNKNOWN) ;
00187             }
00188         }
00189 
00190     // If the previous two beats were normal...
00191 
00192     else if(RRTypes[1] == NN)
00193         {
00194 
00195         if(RRShort2(RRBuffer,RRTypes))
00196             {
00197             if(RRBuffer[1] < BRADY_LIMIT)
00198                 {
00199                 RRTypes[0] = NV ;
00200                 return(PVC) ;
00201                 }
00202             else RRTypes[0] = QQ ;
00203                 return(UNKNOWN) ;
00204             }
00205 
00206 
00207         // If this interval matches the previous interval, then it
00208         // is regular.
00209 
00210         else if(RRMatch(RRBuffer[0],RRBuffer[1]))
00211             {
00212             RRTypes[0] = NN ;
00213             return(NORMAL) ;
00214             }
00215 
00216         // If this interval is short..
00217 
00218         else if(RRShort(RRBuffer[0],RRBuffer[1]))
00219             {
00220 
00221             // But matches the one before last and the one before
00222             // last was NN, this is a normal interval.
00223 
00224             if(RRMatch(RRBuffer[0],RRBuffer[2]) && (RRTypes[2] == NN))
00225                 {
00226                 RRTypes[0] = NN ;
00227                 return(NORMAL) ;
00228                 }
00229 
00230             // If the rhythm wasn't bradycardia, call it a PVC.
00231 
00232             else if(RRBuffer[1] < BRADY_LIMIT)
00233                 {
00234                 RRTypes[0] = NV ;
00235                 return(PVC) ;
00236                 }
00237 
00238             // If the regular rhythm was bradycardia, don't assume that
00239             // it was a PVC.
00240 
00241             else
00242                 {
00243                 RRTypes[0] = QQ ;
00244                 return(UNKNOWN) ;
00245                 }
00246             }
00247 
00248         // If the interval isn't normal or short, then classify
00249         // it as normal but don't assume normal for future
00250         // rhythm classification.
00251 
00252         else
00253             {
00254             RRTypes[0] = QQ ;
00255             return(NORMAL) ;
00256             }
00257         }
00258 
00259     // If the previous beat was a PVC...
00260 
00261     else if(RRTypes[1] == NV)
00262         {
00263 
00264         if(RRShort2(&RRBuffer[1],&RRTypes[1]))
00265             {
00266     /*      if(RRMatch2(RRBuffer[0],RRBuffer[1]))
00267                 {
00268                 RRTypes[0] = VV ;
00269                 return(PVC) ;
00270                 } */
00271 
00272             if(RRMatch(RRBuffer[0],RRBuffer[1]))
00273                 {
00274                 RRTypes[0] = NN ;
00275                 RRTypes[1] = NN ;
00276                 return(NORMAL) ;
00277                 }
00278             else if(RRBuffer[0] > RRBuffer[1])
00279                 {
00280                 RRTypes[0] = VN ;
00281                 return(NORMAL) ;
00282                 }
00283             else
00284                 {
00285                 RRTypes[0] = QQ ;
00286                 return(UNKNOWN) ;
00287                 }
00288 
00289 
00290             }
00291 
00292         // If this interval matches the previous premature
00293         // interval assume a ventricular couplet.
00294 
00295         else if(RRMatch(RRBuffer[0],RRBuffer[1]))
00296             {
00297             RRTypes[0] = VV ;
00298             return(PVC) ;
00299             }
00300 
00301         // If this interval is larger than the previous
00302         // interval, assume that it is NORMAL.
00303 
00304         else if(RRBuffer[0] > RRBuffer[1])
00305             {
00306             RRTypes[0] = VN ;
00307             return(NORMAL) ;
00308             }
00309 
00310         // Otherwise don't make any assumputions about
00311         // what this interval represents.
00312 
00313         else
00314             {
00315             RRTypes[0] = QQ ;
00316             return(UNKNOWN) ;
00317          }
00318         }
00319 
00320     // If the previous beat followed a PVC or couplet etc...
00321 
00322     else if(RRTypes[1] == VN)
00323         {
00324 
00325         // Find the last NN interval.
00326 
00327         for(i = 2; (RRTypes[i] != NN) && (i < RBB_LENGTH); ++i) ;
00328 
00329         // If there was an NN interval in the interval buffer...
00330         if(i != RBB_LENGTH)
00331             {
00332             NNEst = RRBuffer[i] ;
00333 
00334             // and it matches, classify this interval as NORMAL.
00335 
00336             if(RRMatch(RRBuffer[0],NNEst))
00337                 {
00338                 RRTypes[0] = NN ;
00339                 return(NORMAL) ;
00340                 }
00341             }
00342 
00343         else NNEst = 0 ;
00344         for(i = 2; (RRTypes[i] != NV) && (i < RBB_LENGTH); ++i) ;
00345         if(i != RBB_LENGTH)
00346             NVEst = RRBuffer[i] ;
00347         else NVEst = 0 ;
00348         if((NNEst == 0) && (NVEst != 0))
00349             NNEst = (RRBuffer[1]+NVEst) >> 1 ;
00350 
00351         // NNEst is either the last NN interval or the average
00352         // of the most recent NV and VN intervals.
00353 
00354         // If the interval is closer to NN than NV, try
00355         // matching to NN.
00356 
00357         if((NVEst != 0) &&
00358             (abs(NNEst - RRBuffer[0]) < abs(NVEst - RRBuffer[0])) &&
00359             RRMatch(NNEst,RRBuffer[0]))
00360             {
00361             RRTypes[0] = NN ;
00362             return(NORMAL) ;
00363             }
00364 
00365         // If this interval is closer to NV than NN, try
00366         // matching to NV.
00367 
00368         else if((NVEst != 0) &&
00369             (abs(NNEst - RRBuffer[0]) > abs(NVEst - RRBuffer[0])) &&
00370             RRMatch(NVEst,RRBuffer[0]))
00371             {
00372             RRTypes[0] = NV ;
00373             return(PVC) ;
00374             }
00375 
00376         // If equally close, or we don't have an NN or NV in the buffer,
00377         // who knows what it is.
00378 
00379         else
00380             {
00381             RRTypes[0] = QQ ;
00382             return(UNKNOWN) ;
00383             }
00384         }
00385 
00386     // Otherwise the previous interval must have been a VV
00387 
00388     else
00389         {
00390 
00391         // Does this match previous VV.
00392 
00393         if(RRMatch(RRBuffer[0],RRBuffer[1]))
00394             {
00395             RRTypes[0] = VV ;
00396             return(PVC) ;
00397             }
00398 
00399         // If this doesn't match a previous VV interval, assume
00400         // any new interval is recovery to Normal beat.
00401 
00402         else
00403             {
00404             if(RRShort(RRBuffer[0],RRBuffer[1]))
00405                 {
00406                 RRTypes[0] = QQ ;
00407                 return(UNKNOWN) ;
00408                 }
00409             else
00410                 {
00411                 RRTypes[0] = VN ;
00412                 return(NORMAL) ;
00413                 }
00414             }
00415         }
00416     }
00417 
00418 
00419 /***********************************************************************
00420     RRMatch() test whether two intervals are within 12.5% of their mean.
00421 ************************************************************************/
00422 
00423 int RRMatch(int rr0,int rr1)
00424     {
00425     if(abs(rr0-rr1) < ((rr0+rr1)>>3))
00426         return(1) ;
00427     else return(0) ;
00428     }
00429 
00430 /************************************************************************
00431     RRShort() tests whether an interval is less than 75% of the previous
00432     interval.
00433 *************************************************************************/
00434 
00435 int RRShort(int rr0, int rr1)
00436     {
00437     if(rr0 < rr1-(rr1>>2))
00438         return(1) ;
00439     else return(0) ;
00440     }
00441 
00442 /*************************************************************************
00443     IsBigeminy() allows external access to the bigeminy flag to check whether
00444     a bigeminal rhythm is in progress.
00445 **************************************************************************/
00446 
00447 int IsBigeminy(void)
00448     {
00449     return(BigeminyFlag) ;
00450     }
00451 
00452 /**************************************************************************
00453  Check for short interval in very regular rhythm.
00454 **************************************************************************/
00455 
00456 int RRShort2(int *rrIntervals, int *rrTypes)
00457     {
00458     int rrMean = 0, i, nnCount ;
00459 
00460     for(i = 1, nnCount = 0; (i < 7) && (nnCount < 4); ++i)
00461         if(rrTypes[i] == NN)
00462             {
00463             ++nnCount ;
00464             rrMean += rrIntervals[i] ;
00465             }
00466 
00467     // Return if there aren't at least 4 normal intervals.
00468 
00469     if(nnCount != 4)
00470         return(0) ;
00471     rrMean >>= 2 ;
00472 
00473 
00474     for(i = 1, nnCount = 0; (i < 7) && (nnCount < 4); ++i)
00475         if(rrTypes[i] == NN)
00476             {
00477             if(abs(rrMean-rrIntervals[i]) > (rrMean>>4))
00478                 i = 10 ;
00479             }
00480 
00481     if((i < 9) && (rrIntervals[0] < (rrMean - (rrMean>>3))))
00482         return(1) ;
00483     else
00484         return(0) ;
00485     }
00486 
00487 int RRMatch2(int rr0,int rr1)
00488     {
00489     if(abs(rr0-rr1) < ((rr0+rr1)>>4))
00490         return(1) ;
00491     else return(0) ;
00492     }