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

match.cpp

00001 /*****************************************************************************
00002 FILE:  match.cpp
00003 AUTHOR: Patrick S. Hamilton
00004 REVISED:    5/13/2002
00005   ___________________________________________________________________________
00006 
00007 match.cpp: Match beats to previous beats.
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 Match.cpp contains functions for managing template matching of beats and
00031 managing of feature data associated with each beat type.  These
00032 functions are called functions in classify.cpp.  Beats are matched to
00033 previoiusly detected beats types based on how well they match point by point
00034 in a MATCH_LENGTH region centered on FIDMARK (R-wave location).  The following
00035 is a list of functions that are available for calling by classify.
00036 
00037     ResetMatch -- Resets global variables used in template matching.
00038     CompareBeats -- Measures the difference between two beats with
00039                             beats scaled to produce the best match.
00040     CompareBeats2 -- Measures the difference between two beats without
00041                             beat scaling.
00042     NewBeatType -- Start a new beat type with the present beat.
00043     BestMorphMatch -- Finds the beat template that best matches a new beat.
00044     UpdateBeatType -- Updates existing beat template and associated features
00045                             based on a new beat.
00046     GetDominantType -- Returns the NORMAL beat type that has occorred most often.
00047     ClearLastNewType -- Removes the last new beat type from the possible beat
00048                             types.
00049     DomCompare -- Compares the template for a given beat type to the template
00050                         of the dominant normal beat type.
00051     DomCompare2 -- Compares a given beat template to the templat of the
00052                         dominant normal beat type.
00053 
00054     PostClassify -- Classifies beats based on preceding and following beats
00055                         and R-to-R intervals.
00056 
00057     ResetPostClassify -- Resets variables used for post classification.
00058 
00059     CheckPostClass -- Check type classification based on last eight post
00060                         classifications.
00061 
00062     CheckPCClass -- Check post beat rhythm classification for the last eight
00063                         beats.
00064 
00065 A number of simple functions allow access to beat features while maintaining
00066 some level of encapsulation:
00067 
00068     GetTypesCount -- Returns number of beat types that have been detected.
00069     GetBeatTypeCount -- Returns the number of beats of a given type
00070                               that have been detected.
00071     GetBeatWidth -- Returns the width estimate for a given beat type.
00072     SetBeatClass -- Associates a beat classification with a beat type.
00073     GetBeatBegin -- Returns the beginning point for a given beat type.
00074     GetBeatEnd -- Returns the ending point for a given beat type.
00075 
00076 ******************************************************************************/
00077 //#include <stdlib.h>
00078 //#include <stdio.h>
00079 #include <mbed.h>
00080 #include "ecgcodes.h"
00081 
00082 #include "bdac.h"
00083 #define MATCH_LENGTH    BEAT_MS300  // Number of points used for beat matching.
00084 #define MATCH_LIMIT 1.2         // Match limit used testing whether two
00085                                             // beat types might be combined.
00086 #define COMBINE_LIMIT   0.8     // Limit used for deciding whether two types
00087                                             // can be combined.
00088 
00089 #define MATCH_START (FIDMARK-(MATCH_LENGTH/2))  // Starting point for beat matching
00090 #define MATCH_END   (FIDMARK+(MATCH_LENGTH/2))      // End point for beat matching.
00091 #define MAXPREV 8   // Number of preceeding beats used as beat features.
00092 #define MAX_SHIFT   BEAT_MS40
00093 
00094 // Local prototypes.
00095 
00096 int NoiseCheck(int *beat) ;
00097 double CompareBeats(int *beat1, int *beat2, int *shiftAdj) ;
00098 double CompareBeats2(int *beat1, int *beat2, int *shiftAdj) ;
00099 void UpdateBeat(int *aveBeat, int *newBeat, int shift) ;
00100 void BeatCopy(int srcBeat, int destBeat) ;
00101 int MinimumBeatVariation(int type) ;
00102 
00103 // External prototypes.
00104 
00105 void AnalyzeBeat(int *beat, int *onset, int *offset, int *isoLevel,
00106     int *beatBegin, int *beatEnd, int *amp) ;
00107 void AdjustDomData(int oldType, int newType) ;
00108 void CombineDomData(int oldType, int newType) ;
00109 
00110 // Global variables.
00111 
00112 int BeatTemplates[MAXTYPES][BEATLGTH] ;
00113 int BeatCounts[MAXTYPES] ;
00114 int BeatWidths[MAXTYPES] ;
00115 int BeatClassifications[MAXTYPES] ;
00116 int BeatBegins[MAXTYPES] ;
00117 int BeatEnds[MAXTYPES] ;
00118 int BeatsSinceLastMatch[MAXTYPES] ;
00119 int BeatAmps[MAXTYPES] ;
00120 int BeatCenters[MAXTYPES] ;
00121 double MIs[MAXTYPES][8] ;
00122 
00123 // Need access to these in postclas.cpp when beat types are combined
00124 // and moved.
00125 
00126 extern int PostClass[MAXTYPES][8] ;
00127 extern int PCRhythm[MAXTYPES][8] ;
00128 
00129 int TypeCount = 0 ;
00130 
00131 /***************************************************************************
00132 ResetMatch() resets static variables involved with template matching.
00133 ****************************************************************************/
00134 
00135 void ResetMatch(void)
00136     {
00137     int i, j ;
00138     TypeCount = 0 ;
00139     for(i = 0; i < MAXTYPES; ++i)
00140         {
00141         BeatCounts[i] = 0 ;
00142         BeatClassifications[i] = UNKNOWN ;
00143         for(j = 0; j < 8; ++j)
00144             {
00145             MIs[i][j] = 0 ;
00146             }
00147         }
00148     }
00149 
00150 /**************************************************************************
00151     CompareBeats() takes two beat buffers and compares how well they match
00152     point-by-point.  Beat2 is shifted and scaled to produce the closest
00153     possible match.  The metric returned is the sum of the absolute
00154     differences between beats divided by the amplitude of the beats.  The
00155     shift used for the match is returned via the pointer *shiftAdj.
00156 ***************************************************************************/
00157 
00158 #define MATCH_START (FIDMARK-(MATCH_LENGTH/2))
00159 #define MATCH_END   (FIDMARK+(MATCH_LENGTH/2))
00160 
00161 double CompareBeats(int *beat1, int *beat2, int *shiftAdj)
00162     {
00163     int i, max, min, magSum, shift ;
00164     long beatDiff, meanDiff, minDiff, minShift ;
00165     double metric, scaleFactor, tempD ;
00166 
00167     // Calculate the magnitude of each beat.
00168 
00169     max = min = beat1[MATCH_START] ;
00170     for(i = MATCH_START+1; i < MATCH_END; ++i)
00171         if(beat1[i] > max)
00172             max = beat1[i] ;
00173         else if(beat1[i] < min)
00174             min = beat1[i] ;
00175 
00176     magSum = max - min ;
00177 
00178     i = MATCH_START ;
00179     max = min = beat2[i] ;
00180     for(i = MATCH_START+1; i < MATCH_END; ++i)
00181         if(beat2[i] > max)
00182             max = beat2[i] ;
00183         else if(beat2[i] < min)
00184             min = beat2[i] ;
00185 
00186     // magSum += max - min ;
00187     scaleFactor = magSum ;
00188     scaleFactor /= max-min ;
00189     magSum *= 2 ;
00190 
00191     // Calculate the sum of the point-by-point
00192     // absolute differences for five possible shifts.
00193 
00194     for(shift = -MAX_SHIFT; shift <= MAX_SHIFT; ++shift)
00195         {
00196         for(i = FIDMARK-(MATCH_LENGTH>>1), meanDiff = 0;
00197             i < FIDMARK + (MATCH_LENGTH>>1); ++i)
00198             {
00199             tempD = beat2[i+shift] ;
00200             tempD *= scaleFactor ;
00201             meanDiff += beat1[i]- tempD ; // beat2[i+shift] ;
00202             }
00203         meanDiff /= MATCH_LENGTH ;
00204 
00205         for(i = FIDMARK-(MATCH_LENGTH>>1), beatDiff = 0;
00206             i < FIDMARK + (MATCH_LENGTH>>1); ++i)
00207             {
00208             tempD = beat2[i+shift] ;
00209             tempD *= scaleFactor ;
00210             beatDiff += abs(beat1[i] - meanDiff- tempD) ; // beat2[i+shift]  ) ;
00211             }
00212 
00213 
00214         if(shift == -MAX_SHIFT)
00215             {
00216             minDiff = beatDiff ;
00217             minShift = -MAX_SHIFT ;
00218             }
00219         else if(beatDiff < minDiff)
00220             {
00221             minDiff = beatDiff ;
00222             minShift = shift ;
00223             }
00224         }
00225 
00226     metric = minDiff ;
00227     *shiftAdj = minShift ;
00228     metric /= magSum ;
00229 
00230     // Metric scales inversely with match length.
00231     // algorithm was originally tuned with a match
00232     // length of 30.
00233 
00234     metric *= 30 ;
00235     metric /= MATCH_LENGTH ;
00236     return(metric) ;
00237     }
00238 
00239 /***************************************************************************
00240     CompareBeats2 is nearly the same as CompareBeats above, but beat2 is
00241     not scaled before calculating the match metric.  The match metric is
00242     then the sum of the absolute differences divided by the average amplitude
00243     of the two beats.
00244 ****************************************************************************/
00245 
00246 double CompareBeats2(int *beat1, int *beat2, int *shiftAdj)
00247     {
00248     int i, max, min, shift ;
00249     int mag1, mag2 ;
00250     long beatDiff, meanDiff, minDiff, minShift ;
00251     double metric ;
00252 
00253     // Calculate the magnitude of each beat.
00254 
00255     max = min = beat1[MATCH_START] ;
00256     for(i = MATCH_START+1; i < MATCH_END; ++i)
00257         if(beat1[i] > max)
00258             max = beat1[i] ;
00259         else if(beat1[i] < min)
00260             min = beat1[i] ;
00261 
00262     mag1 = max - min ;
00263 
00264     i = MATCH_START ;
00265     max = min = beat2[i] ;
00266     for(i = MATCH_START+1; i < MATCH_END; ++i)
00267         if(beat2[i] > max)
00268             max = beat2[i] ;
00269         else if(beat2[i] < min)
00270             min = beat2[i] ;
00271 
00272     mag2 = max-min ;
00273 
00274     // Calculate the sum of the point-by-point
00275     // absolute differences for five possible shifts.
00276 
00277     for(shift = -MAX_SHIFT; shift <= MAX_SHIFT; ++shift)
00278         {
00279         for(i = FIDMARK-(MATCH_LENGTH>>1), meanDiff = 0;
00280             i < FIDMARK + (MATCH_LENGTH>>1); ++i)
00281             meanDiff += beat1[i]- beat2[i+shift] ;
00282         meanDiff /= MATCH_LENGTH ;
00283 
00284         for(i = FIDMARK-(MATCH_LENGTH>>1), beatDiff = 0;
00285             i < FIDMARK + (MATCH_LENGTH>>1); ++i)
00286             beatDiff += abs(beat1[i] - meanDiff- beat2[i+shift]) ; ;
00287 
00288         if(shift == -MAX_SHIFT)
00289             {
00290             minDiff = beatDiff ;
00291             minShift = -MAX_SHIFT ;
00292             }
00293         else if(beatDiff < minDiff)
00294             {
00295             minDiff = beatDiff ;
00296             minShift = shift ;
00297             }
00298         }
00299 
00300     metric = minDiff ;
00301     *shiftAdj = minShift ;
00302     metric /= (mag1+mag2) ;
00303 
00304     // Metric scales inversely with match length.
00305     // algorithm was originally tuned with a match
00306     // length of 30.
00307 
00308     metric *= 30 ;
00309     metric /= MATCH_LENGTH ;
00310 
00311     return(metric) ;
00312     }
00313 
00314 /************************************************************************
00315 UpdateBeat() averages a new beat into an average beat template by adding
00316 1/8th of the new beat to 7/8ths of the average beat.
00317 *************************************************************************/
00318 
00319 void UpdateBeat(int *aveBeat, int *newBeat, int shift)
00320     {
00321     int i ;
00322     long tempLong ;
00323 
00324     for(i = 0; i < BEATLGTH; ++i)
00325         {
00326         if((i+shift >= 0) && (i+shift < BEATLGTH))
00327             {
00328             tempLong = aveBeat[i] ;
00329             tempLong *= 7 ;
00330             tempLong += newBeat[i+shift] ;
00331             tempLong >>= 3 ;
00332             aveBeat[i] = tempLong ;
00333             }
00334         }
00335     }
00336 
00337 /*******************************************************
00338     GetTypesCount returns the number of types that have
00339     been detected.
00340 *******************************************************/
00341 
00342 int GetTypesCount(void)
00343     {
00344     return(TypeCount) ;
00345     }
00346 
00347 /********************************************************
00348     GetBeatTypeCount returns the number of beats of a
00349     a particular type have been detected.
00350 ********************************************************/
00351 
00352 int GetBeatTypeCount(int type)
00353     {
00354     return(BeatCounts[type]) ;
00355     }
00356 
00357 /*******************************************************
00358     GetBeatWidth returns the QRS width estimate for
00359     a given type of beat.
00360 *******************************************************/
00361 int GetBeatWidth(int type)
00362     {
00363     return(BeatWidths[type]) ;
00364     }
00365 
00366 /*******************************************************
00367     GetBeatCenter returns the point between the onset and
00368     offset of a beat.
00369 ********************************************************/
00370 
00371 int GetBeatCenter(int type)
00372     {
00373     return(BeatCenters[type]) ;
00374     }
00375 
00376 /*******************************************************
00377     GetBeatClass returns the present classification for
00378     a given beat type (NORMAL, PVC, or UNKNOWN).
00379 ********************************************************/
00380 
00381 int GetBeatClass(int type)
00382     {
00383     if(type == MAXTYPES)
00384         return(UNKNOWN) ;
00385     return(BeatClassifications[type]) ;
00386     }
00387 
00388 /******************************************************
00389     SetBeatClass sets up a beat classifation for a
00390     given type.
00391 ******************************************************/
00392 
00393 void SetBeatClass(int type, int beatClass)
00394     {
00395     BeatClassifications[type] = beatClass ;
00396     }
00397 
00398 /******************************************************************************
00399     NewBeatType starts a new beat type by storing the new beat and its
00400     features as the next available beat type.
00401 ******************************************************************************/
00402 
00403 int NewBeatType(int *newBeat )
00404     {
00405     int i, onset, offset, isoLevel, beatBegin, beatEnd ;
00406     int mcType, amp ;
00407 
00408     // Update count of beats since each template was matched.
00409 
00410     for(i = 0; i < TypeCount; ++i)
00411         ++BeatsSinceLastMatch[i] ;
00412 
00413     if(TypeCount < MAXTYPES)
00414         {
00415         for(i = 0; i < BEATLGTH; ++i)
00416             BeatTemplates[TypeCount][i] = newBeat[i] ;
00417 
00418         BeatCounts[TypeCount] = 1 ;
00419         BeatClassifications[TypeCount] = UNKNOWN ;
00420         AnalyzeBeat(&BeatTemplates[TypeCount][0],&onset,&offset, &isoLevel,
00421             &beatBegin, &beatEnd, &amp) ;
00422         BeatWidths[TypeCount] = offset-onset ;
00423         BeatCenters[TypeCount] = (offset+onset)/2 ;
00424         BeatBegins[TypeCount] = beatBegin ;
00425         BeatEnds[TypeCount] = beatEnd ;
00426         BeatAmps[TypeCount] = amp ;
00427 
00428         BeatsSinceLastMatch[TypeCount] = 0 ;
00429 
00430         ++TypeCount ;
00431         return(TypeCount-1) ;
00432         }
00433 
00434     // If we have used all the template space, replace the beat
00435     // that has occurred the fewest number of times.
00436 
00437     else
00438         {
00439         // Find the template with the fewest occurances,
00440         // that hasn't been matched in at least 500 beats.
00441 
00442         mcType = -1 ;
00443 
00444         if(mcType == -1)
00445             {
00446             mcType = 0 ;
00447             for(i = 1; i < MAXTYPES; ++i)
00448                 if(BeatCounts[i] < BeatCounts[mcType])
00449                     mcType = i ;
00450                 else if(BeatCounts[i] == BeatCounts[mcType])
00451                     {
00452                     if(BeatsSinceLastMatch[i] > BeatsSinceLastMatch[mcType])
00453                         mcType = i ;
00454                     }
00455             }
00456 
00457         // Adjust dominant beat monitor data.
00458 
00459         AdjustDomData(mcType,MAXTYPES) ;
00460 
00461         // Substitute this beat.
00462 
00463         for(i = 0; i < BEATLGTH; ++i)
00464             BeatTemplates[mcType][i] = newBeat[i] ;
00465 
00466         BeatCounts[mcType] = 1 ;
00467         BeatClassifications[mcType] = UNKNOWN ;
00468         AnalyzeBeat(&BeatTemplates[mcType][0],&onset,&offset, &isoLevel,
00469             &beatBegin, &beatEnd, &amp) ;
00470         BeatWidths[mcType] = offset-onset ;
00471         BeatCenters[mcType] = (offset+onset)/2 ;
00472         BeatBegins[mcType] = beatBegin ;
00473         BeatEnds[mcType] = beatEnd ;
00474         BeatsSinceLastMatch[mcType] = 0 ;
00475       BeatAmps[mcType] = amp ;
00476         return(mcType) ;
00477         }
00478     }
00479 
00480 /***************************************************************************
00481     BestMorphMatch tests a new beat against all available beat types and
00482     returns (via pointers) the existing type that best matches, the match
00483     metric for that type, and the shift used for that match.
00484 ***************************************************************************/
00485 
00486 void BestMorphMatch(int *newBeat,int *matchType,double *matchIndex, double *mi2,
00487     int *shiftAdj)
00488     {
00489     int type, i, bestMatch, nextBest, minShift, shift, temp ;
00490     int bestShift2, nextShift2 ;
00491     double bestDiff2, nextDiff2;
00492     double beatDiff, minDiff, nextDiff=10000 ;
00493 
00494     if(TypeCount == 0)
00495         {
00496         *matchType = 0 ;
00497         *matchIndex = 1000 ;        // Make sure there is no match so a new beat is
00498         *shiftAdj = 0 ;         // created.
00499         return ;
00500         }
00501 
00502     // Compare the new beat to all type beat
00503     // types that have been saved.
00504 
00505     for(type = 0; type < TypeCount; ++type)
00506         {
00507         beatDiff = CompareBeats(&BeatTemplates[type][0],newBeat,&shift) ;
00508         if(type == 0)
00509             {
00510             bestMatch = 0 ;
00511             minDiff = beatDiff ;
00512             minShift = shift ;
00513             }
00514         else if(beatDiff < minDiff)
00515             {
00516             nextBest = bestMatch ;
00517             nextDiff = minDiff ;
00518             bestMatch = type ;
00519             minDiff = beatDiff ;
00520             minShift = shift ;
00521             }
00522         else if((TypeCount > 1) && (type == 1))
00523             {
00524             nextBest = type ;
00525             nextDiff = beatDiff ;
00526             }
00527         else if(beatDiff < nextDiff)
00528             {
00529             nextBest = type ;
00530             nextDiff = beatDiff ;
00531             }
00532         }
00533 
00534     // If this beat was close to two different
00535     // templates, see if the templates which template
00536     // is the best match when no scaling is used.
00537     // Then check whether the two close types can be combined.
00538 
00539     if((minDiff < MATCH_LIMIT) && (nextDiff < MATCH_LIMIT) && (TypeCount > 1))
00540         {
00541         // Compare without scaling.
00542 
00543         bestDiff2 = CompareBeats2(&BeatTemplates[bestMatch][0],newBeat,&bestShift2) ;
00544         nextDiff2 = CompareBeats2(&BeatTemplates[nextBest][0],newBeat,&nextShift2) ;
00545         if(nextDiff2 < bestDiff2)
00546             {
00547             temp = bestMatch ;
00548             bestMatch = nextBest ;
00549             nextBest = temp ;
00550             temp = minDiff ;
00551             minDiff = nextDiff ;
00552             nextDiff = temp ;
00553             minShift = nextShift2 ;
00554             *mi2 = bestDiff2 ;
00555             }
00556         else *mi2 = nextDiff2 ;
00557 
00558         beatDiff = CompareBeats(&BeatTemplates[bestMatch][0],&BeatTemplates[nextBest][0],&shift) ;
00559 
00560         if((beatDiff < COMBINE_LIMIT) &&
00561             ((*mi2 < 1.0) || (!MinimumBeatVariation(nextBest))))
00562             {
00563 
00564             // Combine beats into bestMatch
00565 
00566             if(bestMatch < nextBest)
00567                 {
00568                 for(i = 0; i < BEATLGTH; ++i)
00569                     {
00570                     if((i+shift > 0) && (i + shift < BEATLGTH))
00571                         {
00572                         BeatTemplates[bestMatch][i] += BeatTemplates[nextBest][i+shift] ;
00573                         BeatTemplates[bestMatch][i] >>= 1 ;
00574                         }
00575                     }
00576 
00577                 if((BeatClassifications[bestMatch] == NORMAL) || (BeatClassifications[nextBest] == NORMAL))
00578                     BeatClassifications[bestMatch] = NORMAL ;
00579                 else if((BeatClassifications[bestMatch] == PVC) || (BeatClassifications[nextBest] == PVC))
00580                     BeatClassifications[bestMatch] = PVC ;
00581 
00582                 BeatCounts[bestMatch] += BeatCounts[nextBest] ;
00583 
00584                 CombineDomData(nextBest,bestMatch) ;
00585 
00586                 // Shift other templates over.
00587 
00588                 for(type = nextBest; type < TypeCount-1; ++type)
00589                     BeatCopy(type+1,type) ;
00590 
00591                 }
00592 
00593             // Otherwise combine beats it nextBest.
00594 
00595             else
00596                 {
00597                 for(i = 0; i < BEATLGTH; ++i)
00598                     {
00599                     BeatTemplates[nextBest][i] += BeatTemplates[bestMatch][i] ;
00600                     BeatTemplates[nextBest][i] >>= 1 ;
00601                     }
00602 
00603                 if((BeatClassifications[bestMatch] == NORMAL) || (BeatClassifications[nextBest] == NORMAL))
00604                     BeatClassifications[nextBest] = NORMAL ;
00605                 else if((BeatClassifications[bestMatch] == PVC) || (BeatClassifications[nextBest] == PVC))
00606                     BeatClassifications[nextBest] = PVC ;
00607 
00608                 BeatCounts[nextBest] += BeatCounts[bestMatch] ;
00609 
00610                 CombineDomData(bestMatch,nextBest) ;
00611 
00612                 // Shift other templates over.
00613 
00614                 for(type = bestMatch; type < TypeCount-1; ++type)
00615                     BeatCopy(type+1,type) ;
00616 
00617 
00618                 bestMatch = nextBest ;
00619                 }
00620             --TypeCount ;
00621             BeatClassifications[TypeCount] = UNKNOWN ;
00622             }
00623         }
00624     *mi2 = CompareBeats2(&BeatTemplates[bestMatch][0],newBeat,&bestShift2) ;
00625     *matchType = bestMatch ;
00626     *matchIndex = minDiff ;
00627     *shiftAdj = minShift ;
00628     }
00629 
00630 /***************************************************************************
00631     UpdateBeatType updates the beat template and features of a given beat type
00632     using a new beat.
00633 ***************************************************************************/
00634 
00635 void UpdateBeatType(int matchType,int *newBeat, double mi2,
00636      int shiftAdj)
00637     {
00638     int i,onset,offset, isoLevel, beatBegin, beatEnd ;
00639     int amp ;
00640 
00641     // Update beats since templates were matched.
00642 
00643     for(i = 0; i < TypeCount; ++i)
00644         {
00645         if(i != matchType)
00646             ++BeatsSinceLastMatch[i] ;
00647         else BeatsSinceLastMatch[i] = 0 ;
00648         }
00649 
00650     // If this is only the second beat, average it with the existing
00651     // template.
00652 
00653     if(BeatCounts[matchType] == 1)
00654         for(i = 0; i < BEATLGTH; ++i)
00655             {
00656             if((i+shiftAdj >= 0) && (i+shiftAdj < BEATLGTH))
00657                 BeatTemplates[matchType][i] = (BeatTemplates[matchType][i] + newBeat[i+shiftAdj])>>1 ;
00658             }
00659 
00660     // Otherwise do a normal update.
00661 
00662     else
00663         UpdateBeat(&BeatTemplates[matchType][0], newBeat, shiftAdj) ;
00664 
00665     // Determine beat features for the new average beat.
00666 
00667     AnalyzeBeat(&BeatTemplates[matchType][0],&onset,&offset,&isoLevel,
00668         &beatBegin, &beatEnd, &amp) ;
00669 
00670     BeatWidths[matchType] = offset-onset ;
00671     BeatCenters[matchType] = (offset+onset)/2 ;
00672     BeatBegins[matchType] = beatBegin ;
00673     BeatEnds[matchType] = beatEnd ;
00674     BeatAmps[matchType] = amp ;
00675 
00676     ++BeatCounts[matchType] ;
00677 
00678     for(i = MAXPREV-1; i > 0; --i)
00679         MIs[matchType][i] = MIs[matchType][i-1] ;
00680     MIs[matchType][0] = mi2 ;
00681 
00682     }
00683 
00684 
00685 /****************************************************************************
00686     GetDominantType returns the NORMAL beat type that has occurred most
00687     frequently.
00688 ****************************************************************************/
00689 
00690 int GetDominantType(void)
00691     {
00692     int maxCount = 0, maxType = -1 ;
00693     int type, totalCount ;
00694 
00695     for(type = 0; type < MAXTYPES; ++type)
00696         {
00697         if((BeatClassifications[type] == NORMAL) && (BeatCounts[type] > maxCount))
00698             {
00699             maxType = type ;
00700             maxCount = BeatCounts[type] ;
00701             }
00702         }
00703 
00704     // If no normals are found and at least 300 beats have occurred, just use
00705     // the most frequently occurring beat.
00706 
00707     if(maxType == -1)
00708         {
00709         for(type = 0, totalCount = 0; type < TypeCount; ++type)
00710             totalCount += BeatCounts[type] ;
00711         if(totalCount > 300)
00712             for(type = 0; type < TypeCount; ++type)
00713                 if(BeatCounts[type] > maxCount)
00714                     {
00715                     maxType = type ;
00716                     maxCount = BeatCounts[type] ;
00717                     }
00718         }
00719 
00720     return(maxType) ;
00721     }
00722 
00723 
00724 /***********************************************************************
00725     ClearLastNewType removes the last new type that was initiated
00726 ************************************************************************/
00727 
00728 void ClearLastNewType(void)
00729     {
00730     if(TypeCount != 0)
00731         --TypeCount ;
00732     }
00733 
00734 /****************************************************************
00735     GetBeatBegin returns the offset from the R-wave for the
00736     beginning of the beat (P-wave onset if a P-wave is found).
00737 *****************************************************************/
00738 
00739 int GetBeatBegin(int type)
00740     {
00741     return(BeatBegins[type]) ;
00742     }
00743 
00744 /****************************************************************
00745     GetBeatEnd returns the offset from the R-wave for the end of
00746     a beat (T-wave offset).
00747 *****************************************************************/
00748 
00749 int GetBeatEnd(int type)
00750     {
00751     return(BeatEnds[type]) ;
00752     }
00753 
00754 int GetBeatAmp(int type)
00755     {
00756     return(BeatAmps[type]) ;
00757     }
00758 
00759 
00760 /************************************************************************
00761     DomCompare2 and DomCompare return similarity indexes between a given
00762     beat and the dominant normal type or a given type and the dominant
00763     normal type.
00764 ************************************************************************/
00765 
00766 double DomCompare2(int *newBeat, int domType)
00767     {
00768     int shift ;
00769     return(CompareBeats2(&BeatTemplates[domType][0],newBeat,&shift)) ;
00770     }
00771 
00772 double DomCompare(int newType, int domType)
00773     {
00774     int shift ;
00775     return(CompareBeats2(&BeatTemplates[domType][0],&BeatTemplates[newType][0],
00776         &shift)) ;
00777     }
00778 
00779 /*************************************************************************
00780 BeatCopy copies beat data from a source beat to a destination beat.
00781 *************************************************************************/
00782 
00783 void BeatCopy(int srcBeat, int destBeat)
00784     {
00785     int i ;
00786 
00787     // Copy template.
00788 
00789     for(i = 0; i < BEATLGTH; ++i)
00790         BeatTemplates[destBeat][i] = BeatTemplates[srcBeat][i] ;
00791 
00792     // Move feature information.
00793 
00794     BeatCounts[destBeat] = BeatCounts[srcBeat] ;
00795     BeatWidths[destBeat] = BeatWidths[srcBeat] ;
00796     BeatCenters[destBeat] = BeatCenters[srcBeat] ;
00797     for(i = 0; i < MAXPREV; ++i)
00798         {
00799         PostClass[destBeat][i] = PostClass[srcBeat][i] ;
00800         PCRhythm[destBeat][i] = PCRhythm[srcBeat][i] ;
00801         }
00802 
00803     BeatClassifications[destBeat] = BeatClassifications[srcBeat] ;
00804     BeatBegins[destBeat] = BeatBegins[srcBeat] ;
00805     BeatEnds[destBeat] = BeatBegins[srcBeat] ;
00806     BeatsSinceLastMatch[destBeat] = BeatsSinceLastMatch[srcBeat];
00807     BeatAmps[destBeat] = BeatAmps[srcBeat] ;
00808 
00809     // Adjust data in dominant beat monitor.
00810 
00811     AdjustDomData(srcBeat,destBeat) ;
00812     }
00813 
00814 /********************************************************************
00815     Minimum beat variation returns a 1 if the previous eight beats
00816     have all had similarity indexes less than 0.5.
00817 *********************************************************************/
00818 
00819 int MinimumBeatVariation(int type)
00820     {
00821     int i ;
00822     for(i = 0; i < MAXTYPES; ++i)
00823         if(MIs[type][i] > 0.5)
00824             i = MAXTYPES+2 ;
00825     if(i == MAXTYPES)
00826         return(1) ;
00827     else return(0) ;
00828     }
00829 
00830 /**********************************************************************
00831     WideBeatVariation returns true if the average similarity index
00832     for a given beat type to its template is greater than WIDE_VAR_LIMIT.
00833 ***********************************************************************/
00834 
00835 #define WIDE_VAR_LIMIT  0.50
00836 
00837 int WideBeatVariation(int type)
00838     {
00839     int i, n ;
00840     double aveMI ;
00841 
00842     n = BeatCounts[type] ;
00843     if(n > 8)
00844         n = 8 ;
00845 
00846     for(i = 0, aveMI = 0; i <n; ++i)
00847         aveMI += MIs[type][i] ;
00848 
00849     aveMI /= n ;
00850     if(aveMI > WIDE_VAR_LIMIT)
00851         return(1) ;
00852     else return(0) ;
00853     }
00854 
00855 
00856