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 classify.cpp Source File

classify.cpp

00001 /*****************************************************************************
00002 FILE:  classify.cpp
00003 AUTHOR: Patrick S. Hamilton
00004 REVISED:    5/13/2001
00005   ___________________________________________________________________________
00006 
00007 classify.cpp: Classify a given beat.
00008 Copywrite (C) 2001 Patrick S. Hamilton
00009 
00010 This file is free software; you can redistribute it and/or modify it under
00011 the terms of the GNU Library General Public License as published by the Free
00012 Software Foundation; either version 2 of the License, or (at your option) any
00013 later version.
00014 
00015 This software is distributed in the hope that it will be useful, but WITHOUT ANY
00016 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
00017 PARTICULAR PURPOSE.  See the GNU Library General Public License for more
00018 details.
00019 
00020 You should have received a copy of the GNU Library General Public License along
00021 with this library; if not, write to the Free Software Foundation, Inc., 59
00022 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00023 
00024 You may contact the author by e-mail (pat@eplimited.edu) or postal mail
00025 (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville,
00026 MA 02143 USA).  For updates to this software, please visit our website
00027 (http://www.eplimited.com).
00028   __________________________________________________________________________
00029 
00030     Classify.cpp contains functions for classifying beats.  The only
00031     function that needs to be called externally from this file is Classify().
00032 
00033     Functions in classify.cpp require functions in the following files:
00034         match.cpp
00035         rythmchk.cpp
00036         classify.cpp
00037         rythmchk.cpp
00038         analbeat.cpp
00039         postclas.cpp
00040 
00041   __________________________________________________________________________
00042 
00043     Revisions:
00044         5/13/02:
00045             Width constants tied to BEAT_SAMPLE_RATE in bdac.h
00046 
00047             Arrays added to track the classifications and RR intervals for the
00048             most recent 8 beats, allowing GetRunCount to become a local function.
00049             RR intervals and classifications are now passed to PostClassify.
00050 
00051             Determination of whether the dominant rhythm is regular is now made
00052             by examining the number of RR intervals classified as UNKNOWN in the
00053             last DM_BUFFER_LENGTH beats (180).  If more than 60 are UNKNOWN
00054             the rhythm is too irregular to give any weight to whether the beat
00055             was premature or not.
00056 
00057 *******************************************************************************/
00058 
00059 #include "ecgcodes.h"
00060 //#include <stdlib.h> // For abs()
00061 //#include <stdio.h>
00062 #include <mbed.h>
00063 #include "qrsdet.h" // For base sample rate.
00064 #include "bdac.h"
00065 #include "match.h"
00066 #include "rythmchk.h"
00067 #include "analbeat.h"
00068 #include "postclas.h"
00069 
00070 // Detection Rule Parameters.
00071 
00072 #define MATCH_LIMIT 1.3                 // Threshold for template matching
00073                                                 // without amplitude sensitivity.
00074 #define MATCH_WITH_AMP_LIMIT    2.5 // Threshold for matching index that
00075                                                 // is amplitude sensitive.
00076 #define PVC_MATCH_WITH_AMP_LIMIT 0.9 // Amplitude sensitive limit for
00077                                                  //matching premature beats
00078 #define BL_SHIFT_LIMIT  100         // Threshold for assuming a baseline shift.
00079 #define NEW_TYPE_NOISE_THRESHOLD    18  // Above this noise level, do not create
00080                                                 // new beat types.
00081 #define NEW_TYPE_HF_NOISE_LIMIT 75  // Above this noise level, do not crate
00082                                                 // new beat types.
00083 
00084 #define MATCH_NOISE_THRESHOLD   0.7 // Match threshold below which noise
00085                                                 // indications are ignored.
00086 
00087 // TempClass classification rule parameters.
00088 
00089 #define R2_DI_THRESHOLD 1.0     // Rule 2 dominant similarity index threshold
00090 #define R3_WIDTH_THRESHOLD  BEAT_MS90       // Rule 3 width threshold.
00091 #define R7_DI_THRESHOLD 1.2     // Rule 7 dominant similarity index threshold
00092 #define R8_DI_THRESHOLD 1.5     // Rule 8 dominant similarity index threshold
00093 #define R9_DI_THRESHOLD 2.0     // Rule 9 dominant similarity index threshold
00094 #define R10_BC_LIM  3               // Rule 10 beat count limit.
00095 #define R10_DI_THRESHOLD    2.5 // Rule 10 dominant similarity index threshold
00096 #define R11_MIN_WIDTH BEAT_MS110            // Rule 11 minimum width threshold.
00097 #define R11_WIDTH_BREAK BEAT_MS140          // Rule 11 width break.
00098 #define R11_WIDTH_DIFF1 BEAT_MS40           // Rule 11 width difference threshold 1
00099 #define R11_WIDTH_DIFF2 BEAT_MS60           // Rule 11 width difference threshold 2
00100 #define R11_HF_THRESHOLD    45      // Rule 11 high frequency noise threshold.
00101 #define R11_MA_THRESHOLD    14      // Rule 11 motion artifact threshold.
00102 #define R11_BC_LIM  1               // Rule 11 beat count limit.
00103 #define R15_DI_THRESHOLD    3.5 // Rule 15 dominant similarity index threshold
00104 #define R15_WIDTH_THRESHOLD BEAT_MS100  // Rule 15 width threshold.
00105 #define R16_WIDTH_THRESHOLD BEAT_MS100  // Rule 16 width threshold.
00106 #define R17_WIDTH_DELTA BEAT_MS20           // Rule 17 difference threshold.
00107 #define R18_DI_THRESHOLD    1.5 // Rule 18 dominant similarity index threshold.
00108 #define R19_HF_THRESHOLD    75      // Rule 19 high frequency noise threshold.
00109 
00110 // Dominant monitor constants.
00111 
00112 #define DM_BUFFER_LENGTH    180
00113 #define IRREG_RR_LIMIT  60
00114 
00115 // Local prototypes.
00116 
00117 int HFNoiseCheck(int *beat) ;
00118 int TempClass(int rhythmClass, int morphType, int beatWidth, int domWidth,
00119     int domType, int hfNoise, int noiseLevel, int blShift, double domIndex) ;
00120 int DomMonitor(int morphType, int rhythmClass, int beatWidth, int rr, int reset) ;
00121 int GetDomRhythm(void) ;
00122 int GetRunCount(void) ;
00123 
00124 // Local Global variables
00125 
00126 int DomType ;
00127 int RecentRRs[8], RecentTypes[8] ;
00128 
00129 /***************************************************************************
00130 *  Classify() takes a beat buffer, the previous rr interval, and the present
00131 *  noise level estimate and returns a beat classification of NORMAL, PVC, or
00132 *  UNKNOWN.  The UNKNOWN classification is only returned.  The beat template
00133 *  type that the beat has been matched to is returned through the pointer
00134 *  *beatMatch for debugging display.  Passing anything other than 0 in init
00135 *  resets the static variables used by Classify.
00136 ****************************************************************************/
00137 
00138 int Classify(int *newBeat,int rr, int noiseLevel, int *beatMatch, int *fidAdj,
00139     int init)
00140     {
00141     int rhythmClass, beatClass, i, beatWidth, blShift ;
00142     static int morphType, runCount = 0 ;
00143     double matchIndex, domIndex, mi2 ;
00144     int shiftAdj ;
00145     int domType, domWidth, onset, offset, amp ;
00146     int beatBegin, beatEnd, tempClass ;
00147     int hfNoise, isoLevel ;
00148     static int lastIsoLevel=0, lastRhythmClass = UNKNOWN, lastBeatWasNew = 0 ;
00149 
00150     // If initializing...
00151 
00152     if(init)
00153         {
00154         ResetRhythmChk() ;
00155         ResetMatch() ;
00156         ResetPostClassify() ;
00157         runCount = 0 ;
00158         DomMonitor(0, 0, 0, 0, 1) ;
00159         return(0) ;
00160         }
00161 
00162     hfNoise = HFNoiseCheck(newBeat) ;   // Check for muscle noise.
00163     rhythmClass = RhythmChk(rr) ;           // Check the rhythm.
00164 
00165     // Estimate beat features.
00166 
00167     AnalyzeBeat(newBeat, &onset, &offset, &isoLevel,
00168         &beatBegin, &beatEnd, &amp) ;
00169 
00170     blShift = abs(lastIsoLevel-isoLevel) ;
00171     lastIsoLevel = isoLevel ;
00172 
00173     // Make isoelectric level 0.
00174 
00175     for(i = 0; i < BEATLGTH; ++i)
00176         newBeat[i] -= isoLevel ;
00177 
00178     // If there was a significant baseline shift since
00179     // the last beat and the last beat was a new type,
00180     // delete the new type because it might have resulted
00181     // from a baseline shift.
00182 
00183     if( (blShift > BL_SHIFT_LIMIT)
00184         && (lastBeatWasNew == 1)
00185         && (lastRhythmClass == NORMAL)
00186         && (rhythmClass == NORMAL) )
00187         ClearLastNewType() ;
00188 
00189     lastBeatWasNew = 0 ;
00190 
00191     // Find the template that best matches this beat.
00192 
00193     BestMorphMatch(newBeat,&morphType,&matchIndex,&mi2,&shiftAdj) ;
00194 
00195     // Disregard noise if the match is good. (New)
00196 
00197     if(matchIndex < MATCH_NOISE_THRESHOLD)
00198         hfNoise = noiseLevel = blShift = 0 ;
00199 
00200     // Apply a stricter match limit to premature beats.
00201 
00202     if((matchIndex < MATCH_LIMIT) && (rhythmClass == PVC) &&
00203         MinimumBeatVariation(morphType) && (mi2 > PVC_MATCH_WITH_AMP_LIMIT))
00204         {
00205         morphType = NewBeatType(newBeat) ;
00206         lastBeatWasNew = 1 ;
00207         }
00208 
00209     // Match if within standard match limits.
00210 
00211     else if((matchIndex < MATCH_LIMIT) && (mi2 <= MATCH_WITH_AMP_LIMIT))
00212         UpdateBeatType(morphType,newBeat,mi2,shiftAdj) ;
00213 
00214     // If the beat isn't noisy but doesn't match, start a new beat.
00215 
00216     else if((blShift < BL_SHIFT_LIMIT) && (noiseLevel < NEW_TYPE_NOISE_THRESHOLD)
00217         && (hfNoise < NEW_TYPE_HF_NOISE_LIMIT))
00218         {
00219         morphType = NewBeatType(newBeat) ;
00220         lastBeatWasNew = 1 ;
00221         }
00222 
00223     // Even if it is a noisy, start new beat if it was an irregular beat.
00224 
00225     else if((lastRhythmClass != NORMAL) || (rhythmClass != NORMAL))
00226         {
00227         morphType = NewBeatType(newBeat) ;
00228         lastBeatWasNew = 1 ;
00229         }
00230 
00231     // If its noisy and regular, don't waste space starting a new beat.
00232 
00233     else morphType = MAXTYPES ;
00234 
00235     // Update recent rr and type arrays.
00236 
00237     for(i = 7; i > 0; --i)
00238         {
00239         RecentRRs[i] = RecentRRs[i-1] ;
00240         RecentTypes[i] = RecentTypes[i-1] ;
00241         }
00242     RecentRRs[0] = rr ;
00243     RecentTypes[0] = morphType ;
00244 
00245     lastRhythmClass = rhythmClass ;
00246     lastIsoLevel = isoLevel ;
00247 
00248     // Fetch beat features needed for classification.
00249     // Get features from average beat if it matched.
00250 
00251     if(morphType != MAXTYPES)
00252         {
00253         beatClass = GetBeatClass(morphType) ;
00254         beatWidth = GetBeatWidth(morphType) ;
00255         *fidAdj = GetBeatCenter(morphType)-FIDMARK ;
00256 
00257         // If the width seems large and there have only been a few
00258         // beats of this type, use the actual beat for width
00259         // estimate.
00260 
00261         if((beatWidth > offset-onset) && (GetBeatTypeCount(morphType) <= 4))
00262             {
00263             beatWidth = offset-onset ;
00264             *fidAdj = ((offset+onset)/2)-FIDMARK ;
00265             }
00266         }
00267 
00268     // If this beat didn't match get beat features directly
00269     // from this beat.
00270 
00271     else
00272         {
00273         beatWidth = offset-onset ;
00274         beatClass = UNKNOWN ;
00275         *fidAdj = ((offset+onset)/2)-FIDMARK ;
00276         }
00277 
00278     // Fetch dominant type beat features.
00279 
00280     DomType = domType = DomMonitor(morphType, rhythmClass, beatWidth, rr, 0) ;
00281     domWidth = GetBeatWidth(domType) ;
00282 
00283     // Compare the beat type, or actual beat to the dominant beat.
00284 
00285     if((morphType != domType) && (morphType != 8))
00286         domIndex = DomCompare(morphType,domType) ;
00287     else if(morphType == 8)
00288         domIndex = DomCompare2(newBeat,domType) ;
00289     else domIndex = matchIndex ;
00290 
00291     // Update post classificaton of the previous beat.
00292 
00293     PostClassify(RecentTypes, domType, RecentRRs, beatWidth, domIndex, rhythmClass) ;
00294 
00295     // Classify regardless of how the morphology
00296     // was previously classified.
00297 
00298     tempClass = TempClass(rhythmClass, morphType, beatWidth, domWidth,
00299         domType, hfNoise, noiseLevel, blShift, domIndex) ;
00300 
00301     // If this morphology has not been classified yet, attempt to classify
00302     // it.
00303 
00304     if((beatClass == UNKNOWN) && (morphType < MAXTYPES))
00305         {
00306 
00307         // Classify as normal if there are 6 in a row
00308         // or at least two in a row that meet rhythm
00309         // rules for normal.
00310 
00311         runCount = GetRunCount() ;
00312 
00313         // Classify a morphology as NORMAL if it is not too wide, and there
00314         // are three in a row.  The width criterion prevents ventricular beats
00315         // from being classified as normal during VTACH (MIT/BIH 205).
00316 
00317         if((runCount >= 3) && (domType != -1) && (beatWidth < domWidth+BEAT_MS20))
00318             SetBeatClass(morphType,NORMAL) ;
00319 
00320         // If there is no dominant type established yet, classify any type
00321         // with six in a row as NORMAL.
00322 
00323         else if((runCount >= 6) && (domType == -1))
00324             SetBeatClass(morphType,NORMAL) ;
00325 
00326         // During bigeminy, classify the premature beats as ventricular if
00327         // they are not too narrow.
00328 
00329         else if(IsBigeminy() == 1)
00330             {
00331             if((rhythmClass == PVC) && (beatWidth > BEAT_MS100))
00332                 SetBeatClass(morphType,PVC) ;
00333             else if(rhythmClass == NORMAL)
00334                 SetBeatClass(morphType,NORMAL) ;
00335             }
00336         }
00337 
00338     // Save morphology type of this beat for next classification.
00339 
00340     *beatMatch = morphType ;
00341 
00342     beatClass = GetBeatClass(morphType) ;
00343    
00344     // If the morphology has been previously classified.
00345     // use that classification.
00346   //    return(rhythmClass) ;
00347 
00348     if(beatClass != UNKNOWN)
00349         return(beatClass) ;
00350 
00351     if(CheckPostClass(morphType) == PVC)
00352         return(PVC) ;
00353 
00354     // Otherwise use the temporary classification.
00355 
00356     return(tempClass) ;
00357     }
00358 
00359 /**************************************************************************
00360 *  HFNoiseCheck() gauges the high frequency (muscle noise) present in the
00361 *  beat template.  The high frequency noise level is estimated by highpass
00362 *  filtering the beat (y[n] = x[n] - 2*x[n-1] + x[n-2]), averaging the
00363 *  highpass filtered signal over five samples, and finding the maximum of
00364 *  this averaged highpass filtered signal.  The high frequency noise metric
00365 *  is then taken to be the ratio of the maximum averaged highpassed signal
00366 *  to the QRS amplitude.
00367 **************************************************************************/
00368 
00369 #define AVELENGTH   BEAT_MS50
00370 
00371 int HFNoiseCheck(int *beat)
00372     {
00373     int maxNoiseAve = 0, i ;
00374     int sum=0, aveBuff[AVELENGTH], avePtr = 0 ;
00375     int qrsMax = 0, qrsMin = 0 ;
00376 
00377     // Determine the QRS amplitude.
00378 
00379     for(i = FIDMARK-BEAT_MS70; i < FIDMARK+BEAT_MS80; ++i)
00380         if(beat[i] > qrsMax)
00381             qrsMax = beat[i] ;
00382         else if(beat[i] < qrsMin)
00383             qrsMin = beat[i] ;
00384 
00385     for(i = 0; i < AVELENGTH; ++i)
00386         aveBuff[i] = 0 ;
00387 
00388     for(i = FIDMARK-BEAT_MS280; i < FIDMARK+BEAT_MS280; ++i)
00389         {
00390         sum -= aveBuff[avePtr] ;
00391         aveBuff[avePtr] = abs(beat[i] - (beat[i-BEAT_MS10]<<1) + beat[i-2*BEAT_MS10]) ;
00392         sum += aveBuff[avePtr] ;
00393         if(++avePtr == AVELENGTH)
00394             avePtr = 0 ;
00395         if((i < (FIDMARK - BEAT_MS50)) || (i > (FIDMARK + BEAT_MS110)))
00396             if(sum > maxNoiseAve)
00397                 maxNoiseAve = sum ;
00398         }
00399     if((qrsMax - qrsMin)>=4)
00400         return((maxNoiseAve * (50/AVELENGTH))/((qrsMax-qrsMin)>>2)) ;
00401     else return(0) ;
00402     }
00403 
00404 /************************************************************************
00405 *  TempClass() classifies beats based on their beat features, relative
00406 *  to the features of the dominant beat and the present noise level.
00407 *************************************************************************/
00408 
00409 int TempClass(int rhythmClass, int morphType,
00410     int beatWidth, int domWidth, int domType,
00411     int hfNoise, int noiseLevel, int blShift, double domIndex)
00412     {
00413 
00414     // Rule 1:  If no dominant type has been detected classify all
00415     // beats as UNKNOWN.
00416 
00417     if(domType < 0)
00418         return(UNKNOWN) ;
00419 
00420     // Rule 2:  If the dominant rhythm is normal, the dominant
00421     // beat type doesn't vary much, this beat is premature
00422     // and looks sufficiently different than the dominant beat
00423     // classify as PVC.
00424 
00425     if(MinimumBeatVariation(domType) && (rhythmClass == PVC)
00426         && (domIndex > R2_DI_THRESHOLD) && (GetDomRhythm() == 1))
00427         return(PVC) ;
00428 
00429     // Rule 3:  If the beat is sufficiently narrow, classify as normal.
00430 
00431     if(beatWidth < R3_WIDTH_THRESHOLD)
00432         return(NORMAL) ;
00433 
00434     // Rule 5:  If the beat cannot be matched to any previously
00435     // detected morphology and it is not premature, consider it normal
00436     // (probably noisy).
00437 
00438     if((morphType == MAXTYPES) && (rhythmClass != PVC)) // == UNKNOWN
00439         return(NORMAL) ;
00440 
00441     // Rule 6:  If the maximum number of beat types have been stored,
00442     // this beat is not regular or premature and only one
00443     // beat of this morphology has been seen, call it normal (probably
00444     // noisy).
00445 
00446     if((GetTypesCount() == MAXTYPES) && (GetBeatTypeCount(morphType)==1)
00447              && (rhythmClass == UNKNOWN))
00448         return(NORMAL) ;
00449 
00450     // Rule 7:  If this beat looks like the dominant beat and the
00451     // rhythm is regular, call it normal.
00452 
00453     if((domIndex < R7_DI_THRESHOLD) && (rhythmClass == NORMAL))
00454         return(NORMAL) ;
00455 
00456     // Rule 8:  If post classification rhythm is normal for this
00457     // type and its shape is close to the dominant shape, classify
00458     // as normal.
00459 
00460     if((domIndex < R8_DI_THRESHOLD) && (CheckPCRhythm(morphType) == NORMAL))
00461         return(NORMAL) ;
00462 
00463     // Rule 9:  If the beat is not premature, it looks similar to the dominant
00464     // beat type, and the dominant beat type is variable (noisy), classify as
00465     // normal.
00466 
00467     if((domIndex < R9_DI_THRESHOLD) && (rhythmClass != PVC) && WideBeatVariation(domType))
00468         return(NORMAL) ;
00469 
00470     // Rule 10:  If this beat is significantly different from the dominant beat
00471     // there have previously been matching beats, the post rhythm classification
00472     // of this type is PVC, and the dominant rhythm is regular, classify as PVC.
00473 
00474     if((domIndex > R10_DI_THRESHOLD)
00475         && (GetBeatTypeCount(morphType) >= R10_BC_LIM) &&
00476         (CheckPCRhythm(morphType) == PVC) && (GetDomRhythm() == 1))
00477         return(PVC) ;
00478 
00479     // Rule 11: if the beat is wide, wider than the dominant beat, doesn't
00480     // appear to be noisy, and matches a previous type, classify it as
00481     // a PVC.
00482 
00483     if( (beatWidth >= R11_MIN_WIDTH) &&
00484         (((beatWidth - domWidth >= R11_WIDTH_DIFF1) && (domWidth < R11_WIDTH_BREAK)) ||
00485         (beatWidth - domWidth >= R11_WIDTH_DIFF2)) &&
00486         (hfNoise < R11_HF_THRESHOLD) && (noiseLevel < R11_MA_THRESHOLD) && (blShift < BL_SHIFT_LIMIT) &&
00487         (morphType < MAXTYPES) && (GetBeatTypeCount(morphType) > R11_BC_LIM))   // Rev 1.1
00488 
00489         return(PVC) ;
00490 
00491     // Rule 12:  If the dominant rhythm is regular and this beat is premature
00492     // then classify as PVC.
00493 
00494     if((rhythmClass == PVC) && (GetDomRhythm() == 1))
00495         return(PVC) ;
00496 
00497     // Rule 14:  If the beat is regular and the dominant rhythm is regular
00498     // call the beat normal.
00499 
00500     if((rhythmClass == NORMAL) && (GetDomRhythm() == 1))
00501         return(NORMAL) ;
00502 
00503     // By this point, we know that rhythm will not help us, so we
00504     // have to classify based on width and similarity to the dominant
00505     // beat type.
00506 
00507     // Rule 15: If the beat is wider than normal, wide on an
00508     // absolute scale, and significantly different from the
00509     // dominant beat, call it a PVC.
00510 
00511     if((beatWidth > domWidth) && (domIndex > R15_DI_THRESHOLD) &&
00512         (beatWidth >= R15_WIDTH_THRESHOLD))
00513         return(PVC) ;
00514 
00515     // Rule 16:  If the beat is sufficiently narrow, call it normal.
00516 
00517     if(beatWidth < R16_WIDTH_THRESHOLD)
00518         return(NORMAL) ;
00519 
00520     // Rule 17:  If the beat isn't much wider than the dominant beat
00521     // call it normal.
00522 
00523     if(beatWidth < domWidth + R17_WIDTH_DELTA)
00524         return(NORMAL) ;
00525 
00526     // If the beat is noisy but reasonably close to dominant,
00527     // call it normal.
00528 
00529     // Rule 18:  If the beat is similar to the dominant beat, call it normal.
00530 
00531     if(domIndex < R18_DI_THRESHOLD)
00532         return(NORMAL) ;
00533 
00534     // If it's noisy don't trust the width.
00535 
00536     // Rule 19:  If the beat is noisy, we can't trust our width estimate
00537     // and we have no useful rhythm information, so guess normal.
00538 
00539     if(hfNoise > R19_HF_THRESHOLD)
00540         return(NORMAL) ;
00541 
00542     // Rule 20:  By this point, we have no rhythm information, the beat
00543     // isn't particularly narrow, the beat isn't particulary similar to
00544     // the dominant beat, so guess a PVC.
00545 
00546     return(PVC) ;
00547 
00548     }
00549 
00550 
00551 /****************************************************************************
00552 *  DomMonitor, monitors which beat morphology is considered to be dominant.
00553 *  The dominant morphology is the beat morphology that has been most frequently
00554 *  classified as normal over the course of the last 120 beats.  The dominant
00555 *  beat rhythm is classified as regular if at least 3/4 of the dominant beats
00556 *  have been classified as regular.
00557 *******************************************************************************/
00558 
00559 #define DM_BUFFER_LENGTH    180
00560 
00561 int NewDom, DomRhythm ;
00562 int DMBeatTypes[DM_BUFFER_LENGTH], DMBeatClasses[DM_BUFFER_LENGTH] ;
00563 int DMBeatRhythms[DM_BUFFER_LENGTH] ;
00564 int DMNormCounts[8], DMBeatCounts[8], DMIrregCount = 0 ;
00565 
00566 int DomMonitor(int morphType, int rhythmClass, int beatWidth, int rr, int reset)
00567     {
00568     static int brIndex = 0 ;
00569     int i, oldType, runCount, dom, max ;
00570 
00571     // Fetch the type of the beat before the last beat.
00572 
00573     i = brIndex - 2 ;
00574     if(i < 0)
00575         i += DM_BUFFER_LENGTH ;
00576     oldType = DMBeatTypes[i] ;
00577 
00578     // If reset flag is set, reset beat type counts and
00579     // beat information buffers.
00580 
00581     if(reset != 0)
00582         {
00583         for(i = 0; i < DM_BUFFER_LENGTH; ++i)
00584             {
00585             DMBeatTypes[i] = -1 ;
00586             DMBeatClasses[i] = 0 ;
00587             }
00588 
00589         for(i = 0; i < 8; ++i)
00590             {
00591             DMNormCounts[i] = 0 ;
00592             DMBeatCounts[i] = 0 ;
00593             }
00594         DMIrregCount = 0 ;
00595         return(0) ;
00596         }
00597 
00598     // Once we have wrapped around, subtract old beat types from
00599     // the beat counts.
00600 
00601     if((DMBeatTypes[brIndex] != -1) && (DMBeatTypes[brIndex] != MAXTYPES))
00602         {
00603         --DMBeatCounts[DMBeatTypes[brIndex]] ;
00604         DMNormCounts[DMBeatTypes[brIndex]] -= DMBeatClasses[brIndex] ;
00605         if(DMBeatRhythms[brIndex] == UNKNOWN)
00606             --DMIrregCount ;
00607         }
00608 
00609     // If this is a morphology that has been detected before, decide
00610     // (for the purposes of selecting the dominant normal beattype)
00611     // whether it is normal or not and update the approporiate counts.
00612 
00613     if(morphType != 8)
00614         {
00615 
00616         // Update the buffers of previous beats and increment the
00617         // count for this beat type.
00618 
00619         DMBeatTypes[brIndex] = morphType ;
00620         ++DMBeatCounts[morphType] ;
00621         DMBeatRhythms[brIndex] = rhythmClass ;
00622 
00623         // If the rhythm appears regular, update the regular rhythm
00624         // count.
00625 
00626         if(rhythmClass == UNKNOWN)
00627             ++DMIrregCount ;
00628 
00629         // Check to see how many beats of this type have occurred in
00630         // a row (stop counting at six).
00631 
00632         i = brIndex - 1 ;
00633         if(i < 0) i += DM_BUFFER_LENGTH ;
00634         for(runCount = 0; (DMBeatTypes[i] == morphType) && (runCount < 6); ++runCount)
00635             if(--i < 0) i += DM_BUFFER_LENGTH ;
00636 
00637         // If the rhythm is regular, the beat width is less than 130 ms, and
00638         // there have been at least two in a row, consider the beat to be
00639         // normal.
00640 
00641         if((rhythmClass == NORMAL) && (beatWidth < BEAT_MS130) && (runCount >= 1))
00642             {
00643             DMBeatClasses[brIndex] = 1 ;
00644             ++DMNormCounts[morphType] ;
00645             }
00646 
00647         // If the last beat was within the normal P-R interval for this beat,
00648         // and the one before that was this beat type, assume the last beat
00649         // was noise and this beat is normal.
00650 
00651         else if(rr < ((FIDMARK-GetBeatBegin(morphType))*SAMPLE_RATE/BEAT_SAMPLE_RATE)
00652             && (oldType == morphType))
00653             {
00654             DMBeatClasses[brIndex] = 1 ;
00655             ++DMNormCounts[morphType] ;
00656             }
00657 
00658         // Otherwise assume that this is not a normal beat.
00659 
00660         else DMBeatClasses[brIndex] = 0 ;
00661         }
00662 
00663     // If the beat does not match any of the beat types, store
00664     // an indication that the beat does not match.
00665 
00666     else
00667         {
00668         DMBeatClasses[brIndex] = 0 ;
00669         DMBeatTypes[brIndex] = -1 ;
00670         }
00671 
00672     // Increment the index to the beginning of the circular buffers.
00673 
00674     if(++brIndex == DM_BUFFER_LENGTH)
00675         brIndex = 0 ;
00676 
00677     // Determine which beat type has the most beats that seem
00678     // normal.
00679 
00680     dom = 0 ;
00681     for(i = 1; i < 8; ++i)
00682         if(DMNormCounts[i] > DMNormCounts[dom])
00683             dom = i ;
00684 
00685     max = 0 ;
00686     for(i = 1; i < 8; ++i)
00687         if(DMBeatCounts[i] > DMBeatCounts[max])
00688             max = i ;
00689 
00690     // If there are no normal looking beats, fall back on which beat
00691     // has occurred most frequently since classification began.
00692 
00693     if((DMNormCounts[dom] == 0) || (DMBeatCounts[max]/DMBeatCounts[dom] >= 2))          // == 0
00694         dom = GetDominantType() ;
00695 
00696     // If at least half of the most frequently occuring normal
00697     // type do not seem normal, fall back on choosing the most frequently
00698     // occurring type since classification began.
00699 
00700     else if(DMBeatCounts[dom]/DMNormCounts[dom] >= 2)
00701         dom = GetDominantType() ;
00702 
00703     // If there is any beat type that has been classfied as normal,
00704     // but at least 10 don't seem normal, reclassify it to UNKNOWN.
00705 
00706     for(i = 0; i < 8; ++i)
00707         if((DMBeatCounts[i] > 10) && (DMNormCounts[i] == 0) && (i != dom)
00708             && (GetBeatClass(i) == NORMAL))
00709             SetBeatClass(i,UNKNOWN) ;
00710 
00711     // Save the dominant type in a global variable so that it is
00712     // accessable for debugging.
00713 
00714     NewDom = dom ;
00715     return(dom) ;
00716     }
00717 
00718 int GetNewDominantType(void)
00719     {
00720     return(NewDom) ;
00721     }
00722 
00723 int GetDomRhythm(void)
00724     {
00725     if(DMIrregCount > IRREG_RR_LIMIT)
00726         return(0) ;
00727     else return(1) ;
00728     }
00729 
00730 
00731 void AdjustDomData(int oldType, int newType)
00732     {
00733     int i ;
00734 
00735     for(i = 0; i < DM_BUFFER_LENGTH; ++i)
00736         {
00737         if(DMBeatTypes[i] == oldType)
00738             DMBeatTypes[i] = newType ;
00739         }
00740 
00741     if(newType != MAXTYPES)
00742         {
00743         DMNormCounts[newType] = DMNormCounts[oldType] ;
00744         DMBeatCounts[newType] = DMBeatCounts[oldType] ;
00745         }
00746 
00747     DMNormCounts[oldType] = DMBeatCounts[oldType] = 0 ;
00748 
00749     }
00750 
00751 void CombineDomData(int oldType, int newType)
00752     {
00753     int i ;
00754 
00755     for(i = 0; i < DM_BUFFER_LENGTH; ++i)
00756         {
00757         if(DMBeatTypes[i] == oldType)
00758             DMBeatTypes[i] = newType ;
00759         }
00760 
00761     if(newType != MAXTYPES)
00762         {
00763         DMNormCounts[newType] += DMNormCounts[oldType] ;
00764         DMBeatCounts[newType] += DMBeatCounts[oldType] ;
00765         }
00766 
00767     DMNormCounts[oldType] = DMBeatCounts[oldType] = 0 ;
00768 
00769     }
00770 
00771 /***********************************************************************
00772     GetRunCount() checks how many of the present beat type have occurred
00773     in a row.
00774 ***********************************************************************/
00775 
00776 int GetRunCount()
00777     {
00778     int i ;
00779     for(i = 1; (i < 8) && (RecentTypes[0] == RecentTypes[i]); ++i) ;
00780     return(i) ;
00781     }
00782 
00783 
00784 
00785 
00786 
00787 
00788 
00789 
00790 
00791 
00792