I've got some basic filter code setup (but not yet tested).
Dependencies: BLE_API Queue mbed nRF51822
Fork of BLE_HeartRate by
classify.cpp@62:8e2fbe131b53, 2015-06-28 (annotated)
- Committer:
- roysandberg
- Date:
- Sun Jun 28 03:06:00 2015 +0000
- Revision:
- 62:8e2fbe131b53
Working Beat Detection and Analysis
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
roysandberg | 62:8e2fbe131b53 | 1 | /***************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 2 | FILE: classify.cpp |
roysandberg | 62:8e2fbe131b53 | 3 | AUTHOR: Patrick S. Hamilton |
roysandberg | 62:8e2fbe131b53 | 4 | REVISED: 5/13/2001 |
roysandberg | 62:8e2fbe131b53 | 5 | ___________________________________________________________________________ |
roysandberg | 62:8e2fbe131b53 | 6 | |
roysandberg | 62:8e2fbe131b53 | 7 | classify.cpp: Classify a given beat. |
roysandberg | 62:8e2fbe131b53 | 8 | Copywrite (C) 2001 Patrick S. Hamilton |
roysandberg | 62:8e2fbe131b53 | 9 | |
roysandberg | 62:8e2fbe131b53 | 10 | This file is free software; you can redistribute it and/or modify it under |
roysandberg | 62:8e2fbe131b53 | 11 | the terms of the GNU Library General Public License as published by the Free |
roysandberg | 62:8e2fbe131b53 | 12 | Software Foundation; either version 2 of the License, or (at your option) any |
roysandberg | 62:8e2fbe131b53 | 13 | later version. |
roysandberg | 62:8e2fbe131b53 | 14 | |
roysandberg | 62:8e2fbe131b53 | 15 | This software is distributed in the hope that it will be useful, but WITHOUT ANY |
roysandberg | 62:8e2fbe131b53 | 16 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
roysandberg | 62:8e2fbe131b53 | 17 | PARTICULAR PURPOSE. See the GNU Library General Public License for more |
roysandberg | 62:8e2fbe131b53 | 18 | details. |
roysandberg | 62:8e2fbe131b53 | 19 | |
roysandberg | 62:8e2fbe131b53 | 20 | You should have received a copy of the GNU Library General Public License along |
roysandberg | 62:8e2fbe131b53 | 21 | with this library; if not, write to the Free Software Foundation, Inc., 59 |
roysandberg | 62:8e2fbe131b53 | 22 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
roysandberg | 62:8e2fbe131b53 | 23 | |
roysandberg | 62:8e2fbe131b53 | 24 | You may contact the author by e-mail (pat@eplimited.edu) or postal mail |
roysandberg | 62:8e2fbe131b53 | 25 | (Patrick Hamilton, E.P. Limited, 35 Medford St., Suite 204 Somerville, |
roysandberg | 62:8e2fbe131b53 | 26 | MA 02143 USA). For updates to this software, please visit our website |
roysandberg | 62:8e2fbe131b53 | 27 | (http://www.eplimited.com). |
roysandberg | 62:8e2fbe131b53 | 28 | __________________________________________________________________________ |
roysandberg | 62:8e2fbe131b53 | 29 | |
roysandberg | 62:8e2fbe131b53 | 30 | Classify.cpp contains functions for classifying beats. The only |
roysandberg | 62:8e2fbe131b53 | 31 | function that needs to be called externally from this file is Classify(). |
roysandberg | 62:8e2fbe131b53 | 32 | |
roysandberg | 62:8e2fbe131b53 | 33 | Functions in classify.cpp require functions in the following files: |
roysandberg | 62:8e2fbe131b53 | 34 | match.cpp |
roysandberg | 62:8e2fbe131b53 | 35 | rythmchk.cpp |
roysandberg | 62:8e2fbe131b53 | 36 | classify.cpp |
roysandberg | 62:8e2fbe131b53 | 37 | rythmchk.cpp |
roysandberg | 62:8e2fbe131b53 | 38 | analbeat.cpp |
roysandberg | 62:8e2fbe131b53 | 39 | postclas.cpp |
roysandberg | 62:8e2fbe131b53 | 40 | |
roysandberg | 62:8e2fbe131b53 | 41 | __________________________________________________________________________ |
roysandberg | 62:8e2fbe131b53 | 42 | |
roysandberg | 62:8e2fbe131b53 | 43 | Revisions: |
roysandberg | 62:8e2fbe131b53 | 44 | 5/13/02: |
roysandberg | 62:8e2fbe131b53 | 45 | Width constants tied to BEAT_SAMPLE_RATE in bdac.h |
roysandberg | 62:8e2fbe131b53 | 46 | |
roysandberg | 62:8e2fbe131b53 | 47 | Arrays added to track the classifications and RR intervals for the |
roysandberg | 62:8e2fbe131b53 | 48 | most recent 8 beats, allowing GetRunCount to become a local function. |
roysandberg | 62:8e2fbe131b53 | 49 | RR intervals and classifications are now passed to PostClassify. |
roysandberg | 62:8e2fbe131b53 | 50 | |
roysandberg | 62:8e2fbe131b53 | 51 | Determination of whether the dominant rhythm is regular is now made |
roysandberg | 62:8e2fbe131b53 | 52 | by examining the number of RR intervals classified as UNKNOWN in the |
roysandberg | 62:8e2fbe131b53 | 53 | last DM_BUFFER_LENGTH beats (180). If more than 60 are UNKNOWN |
roysandberg | 62:8e2fbe131b53 | 54 | the rhythm is too irregular to give any weight to whether the beat |
roysandberg | 62:8e2fbe131b53 | 55 | was premature or not. |
roysandberg | 62:8e2fbe131b53 | 56 | |
roysandberg | 62:8e2fbe131b53 | 57 | *******************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 58 | |
roysandberg | 62:8e2fbe131b53 | 59 | #include "ecgcodes.h" |
roysandberg | 62:8e2fbe131b53 | 60 | //#include <stdlib.h> // For abs() |
roysandberg | 62:8e2fbe131b53 | 61 | //#include <stdio.h> |
roysandberg | 62:8e2fbe131b53 | 62 | #include <mbed.h> |
roysandberg | 62:8e2fbe131b53 | 63 | #include "qrsdet.h" // For base sample rate. |
roysandberg | 62:8e2fbe131b53 | 64 | #include "bdac.h" |
roysandberg | 62:8e2fbe131b53 | 65 | #include "match.h" |
roysandberg | 62:8e2fbe131b53 | 66 | #include "rythmchk.h" |
roysandberg | 62:8e2fbe131b53 | 67 | #include "analbeat.h" |
roysandberg | 62:8e2fbe131b53 | 68 | #include "postclas.h" |
roysandberg | 62:8e2fbe131b53 | 69 | |
roysandberg | 62:8e2fbe131b53 | 70 | // Detection Rule Parameters. |
roysandberg | 62:8e2fbe131b53 | 71 | |
roysandberg | 62:8e2fbe131b53 | 72 | #define MATCH_LIMIT 1.3 // Threshold for template matching |
roysandberg | 62:8e2fbe131b53 | 73 | // without amplitude sensitivity. |
roysandberg | 62:8e2fbe131b53 | 74 | #define MATCH_WITH_AMP_LIMIT 2.5 // Threshold for matching index that |
roysandberg | 62:8e2fbe131b53 | 75 | // is amplitude sensitive. |
roysandberg | 62:8e2fbe131b53 | 76 | #define PVC_MATCH_WITH_AMP_LIMIT 0.9 // Amplitude sensitive limit for |
roysandberg | 62:8e2fbe131b53 | 77 | //matching premature beats |
roysandberg | 62:8e2fbe131b53 | 78 | #define BL_SHIFT_LIMIT 100 // Threshold for assuming a baseline shift. |
roysandberg | 62:8e2fbe131b53 | 79 | #define NEW_TYPE_NOISE_THRESHOLD 18 // Above this noise level, do not create |
roysandberg | 62:8e2fbe131b53 | 80 | // new beat types. |
roysandberg | 62:8e2fbe131b53 | 81 | #define NEW_TYPE_HF_NOISE_LIMIT 75 // Above this noise level, do not crate |
roysandberg | 62:8e2fbe131b53 | 82 | // new beat types. |
roysandberg | 62:8e2fbe131b53 | 83 | |
roysandberg | 62:8e2fbe131b53 | 84 | #define MATCH_NOISE_THRESHOLD 0.7 // Match threshold below which noise |
roysandberg | 62:8e2fbe131b53 | 85 | // indications are ignored. |
roysandberg | 62:8e2fbe131b53 | 86 | |
roysandberg | 62:8e2fbe131b53 | 87 | // TempClass classification rule parameters. |
roysandberg | 62:8e2fbe131b53 | 88 | |
roysandberg | 62:8e2fbe131b53 | 89 | #define R2_DI_THRESHOLD 1.0 // Rule 2 dominant similarity index threshold |
roysandberg | 62:8e2fbe131b53 | 90 | #define R3_WIDTH_THRESHOLD BEAT_MS90 // Rule 3 width threshold. |
roysandberg | 62:8e2fbe131b53 | 91 | #define R7_DI_THRESHOLD 1.2 // Rule 7 dominant similarity index threshold |
roysandberg | 62:8e2fbe131b53 | 92 | #define R8_DI_THRESHOLD 1.5 // Rule 8 dominant similarity index threshold |
roysandberg | 62:8e2fbe131b53 | 93 | #define R9_DI_THRESHOLD 2.0 // Rule 9 dominant similarity index threshold |
roysandberg | 62:8e2fbe131b53 | 94 | #define R10_BC_LIM 3 // Rule 10 beat count limit. |
roysandberg | 62:8e2fbe131b53 | 95 | #define R10_DI_THRESHOLD 2.5 // Rule 10 dominant similarity index threshold |
roysandberg | 62:8e2fbe131b53 | 96 | #define R11_MIN_WIDTH BEAT_MS110 // Rule 11 minimum width threshold. |
roysandberg | 62:8e2fbe131b53 | 97 | #define R11_WIDTH_BREAK BEAT_MS140 // Rule 11 width break. |
roysandberg | 62:8e2fbe131b53 | 98 | #define R11_WIDTH_DIFF1 BEAT_MS40 // Rule 11 width difference threshold 1 |
roysandberg | 62:8e2fbe131b53 | 99 | #define R11_WIDTH_DIFF2 BEAT_MS60 // Rule 11 width difference threshold 2 |
roysandberg | 62:8e2fbe131b53 | 100 | #define R11_HF_THRESHOLD 45 // Rule 11 high frequency noise threshold. |
roysandberg | 62:8e2fbe131b53 | 101 | #define R11_MA_THRESHOLD 14 // Rule 11 motion artifact threshold. |
roysandberg | 62:8e2fbe131b53 | 102 | #define R11_BC_LIM 1 // Rule 11 beat count limit. |
roysandberg | 62:8e2fbe131b53 | 103 | #define R15_DI_THRESHOLD 3.5 // Rule 15 dominant similarity index threshold |
roysandberg | 62:8e2fbe131b53 | 104 | #define R15_WIDTH_THRESHOLD BEAT_MS100 // Rule 15 width threshold. |
roysandberg | 62:8e2fbe131b53 | 105 | #define R16_WIDTH_THRESHOLD BEAT_MS100 // Rule 16 width threshold. |
roysandberg | 62:8e2fbe131b53 | 106 | #define R17_WIDTH_DELTA BEAT_MS20 // Rule 17 difference threshold. |
roysandberg | 62:8e2fbe131b53 | 107 | #define R18_DI_THRESHOLD 1.5 // Rule 18 dominant similarity index threshold. |
roysandberg | 62:8e2fbe131b53 | 108 | #define R19_HF_THRESHOLD 75 // Rule 19 high frequency noise threshold. |
roysandberg | 62:8e2fbe131b53 | 109 | |
roysandberg | 62:8e2fbe131b53 | 110 | // Dominant monitor constants. |
roysandberg | 62:8e2fbe131b53 | 111 | |
roysandberg | 62:8e2fbe131b53 | 112 | #define DM_BUFFER_LENGTH 180 |
roysandberg | 62:8e2fbe131b53 | 113 | #define IRREG_RR_LIMIT 60 |
roysandberg | 62:8e2fbe131b53 | 114 | |
roysandberg | 62:8e2fbe131b53 | 115 | // Local prototypes. |
roysandberg | 62:8e2fbe131b53 | 116 | |
roysandberg | 62:8e2fbe131b53 | 117 | int HFNoiseCheck(int *beat) ; |
roysandberg | 62:8e2fbe131b53 | 118 | int TempClass(int rhythmClass, int morphType, int beatWidth, int domWidth, |
roysandberg | 62:8e2fbe131b53 | 119 | int domType, int hfNoise, int noiseLevel, int blShift, double domIndex) ; |
roysandberg | 62:8e2fbe131b53 | 120 | int DomMonitor(int morphType, int rhythmClass, int beatWidth, int rr, int reset) ; |
roysandberg | 62:8e2fbe131b53 | 121 | int GetDomRhythm(void) ; |
roysandberg | 62:8e2fbe131b53 | 122 | int GetRunCount(void) ; |
roysandberg | 62:8e2fbe131b53 | 123 | |
roysandberg | 62:8e2fbe131b53 | 124 | // Local Global variables |
roysandberg | 62:8e2fbe131b53 | 125 | |
roysandberg | 62:8e2fbe131b53 | 126 | int DomType ; |
roysandberg | 62:8e2fbe131b53 | 127 | int RecentRRs[8], RecentTypes[8] ; |
roysandberg | 62:8e2fbe131b53 | 128 | |
roysandberg | 62:8e2fbe131b53 | 129 | /*************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 130 | * Classify() takes a beat buffer, the previous rr interval, and the present |
roysandberg | 62:8e2fbe131b53 | 131 | * noise level estimate and returns a beat classification of NORMAL, PVC, or |
roysandberg | 62:8e2fbe131b53 | 132 | * UNKNOWN. The UNKNOWN classification is only returned. The beat template |
roysandberg | 62:8e2fbe131b53 | 133 | * type that the beat has been matched to is returned through the pointer |
roysandberg | 62:8e2fbe131b53 | 134 | * *beatMatch for debugging display. Passing anything other than 0 in init |
roysandberg | 62:8e2fbe131b53 | 135 | * resets the static variables used by Classify. |
roysandberg | 62:8e2fbe131b53 | 136 | ****************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 137 | |
roysandberg | 62:8e2fbe131b53 | 138 | int Classify(int *newBeat,int rr, int noiseLevel, int *beatMatch, int *fidAdj, |
roysandberg | 62:8e2fbe131b53 | 139 | int init) |
roysandberg | 62:8e2fbe131b53 | 140 | { |
roysandberg | 62:8e2fbe131b53 | 141 | int rhythmClass, beatClass, i, beatWidth, blShift ; |
roysandberg | 62:8e2fbe131b53 | 142 | static int morphType, runCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 143 | double matchIndex, domIndex, mi2 ; |
roysandberg | 62:8e2fbe131b53 | 144 | int shiftAdj ; |
roysandberg | 62:8e2fbe131b53 | 145 | int domType, domWidth, onset, offset, amp ; |
roysandberg | 62:8e2fbe131b53 | 146 | int beatBegin, beatEnd, tempClass ; |
roysandberg | 62:8e2fbe131b53 | 147 | int hfNoise, isoLevel ; |
roysandberg | 62:8e2fbe131b53 | 148 | static int lastIsoLevel=0, lastRhythmClass = UNKNOWN, lastBeatWasNew = 0 ; |
roysandberg | 62:8e2fbe131b53 | 149 | |
roysandberg | 62:8e2fbe131b53 | 150 | // If initializing... |
roysandberg | 62:8e2fbe131b53 | 151 | |
roysandberg | 62:8e2fbe131b53 | 152 | if(init) |
roysandberg | 62:8e2fbe131b53 | 153 | { |
roysandberg | 62:8e2fbe131b53 | 154 | ResetRhythmChk() ; |
roysandberg | 62:8e2fbe131b53 | 155 | ResetMatch() ; |
roysandberg | 62:8e2fbe131b53 | 156 | ResetPostClassify() ; |
roysandberg | 62:8e2fbe131b53 | 157 | runCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 158 | DomMonitor(0, 0, 0, 0, 1) ; |
roysandberg | 62:8e2fbe131b53 | 159 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 160 | } |
roysandberg | 62:8e2fbe131b53 | 161 | |
roysandberg | 62:8e2fbe131b53 | 162 | hfNoise = HFNoiseCheck(newBeat) ; // Check for muscle noise. |
roysandberg | 62:8e2fbe131b53 | 163 | rhythmClass = RhythmChk(rr) ; // Check the rhythm. |
roysandberg | 62:8e2fbe131b53 | 164 | |
roysandberg | 62:8e2fbe131b53 | 165 | // Estimate beat features. |
roysandberg | 62:8e2fbe131b53 | 166 | |
roysandberg | 62:8e2fbe131b53 | 167 | AnalyzeBeat(newBeat, &onset, &offset, &isoLevel, |
roysandberg | 62:8e2fbe131b53 | 168 | &beatBegin, &beatEnd, &) ; |
roysandberg | 62:8e2fbe131b53 | 169 | |
roysandberg | 62:8e2fbe131b53 | 170 | blShift = abs(lastIsoLevel-isoLevel) ; |
roysandberg | 62:8e2fbe131b53 | 171 | lastIsoLevel = isoLevel ; |
roysandberg | 62:8e2fbe131b53 | 172 | |
roysandberg | 62:8e2fbe131b53 | 173 | // Make isoelectric level 0. |
roysandberg | 62:8e2fbe131b53 | 174 | |
roysandberg | 62:8e2fbe131b53 | 175 | for(i = 0; i < BEATLGTH; ++i) |
roysandberg | 62:8e2fbe131b53 | 176 | newBeat[i] -= isoLevel ; |
roysandberg | 62:8e2fbe131b53 | 177 | |
roysandberg | 62:8e2fbe131b53 | 178 | // If there was a significant baseline shift since |
roysandberg | 62:8e2fbe131b53 | 179 | // the last beat and the last beat was a new type, |
roysandberg | 62:8e2fbe131b53 | 180 | // delete the new type because it might have resulted |
roysandberg | 62:8e2fbe131b53 | 181 | // from a baseline shift. |
roysandberg | 62:8e2fbe131b53 | 182 | |
roysandberg | 62:8e2fbe131b53 | 183 | if( (blShift > BL_SHIFT_LIMIT) |
roysandberg | 62:8e2fbe131b53 | 184 | && (lastBeatWasNew == 1) |
roysandberg | 62:8e2fbe131b53 | 185 | && (lastRhythmClass == NORMAL) |
roysandberg | 62:8e2fbe131b53 | 186 | && (rhythmClass == NORMAL) ) |
roysandberg | 62:8e2fbe131b53 | 187 | ClearLastNewType() ; |
roysandberg | 62:8e2fbe131b53 | 188 | |
roysandberg | 62:8e2fbe131b53 | 189 | lastBeatWasNew = 0 ; |
roysandberg | 62:8e2fbe131b53 | 190 | |
roysandberg | 62:8e2fbe131b53 | 191 | // Find the template that best matches this beat. |
roysandberg | 62:8e2fbe131b53 | 192 | |
roysandberg | 62:8e2fbe131b53 | 193 | BestMorphMatch(newBeat,&morphType,&matchIndex,&mi2,&shiftAdj) ; |
roysandberg | 62:8e2fbe131b53 | 194 | |
roysandberg | 62:8e2fbe131b53 | 195 | // Disregard noise if the match is good. (New) |
roysandberg | 62:8e2fbe131b53 | 196 | |
roysandberg | 62:8e2fbe131b53 | 197 | if(matchIndex < MATCH_NOISE_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 198 | hfNoise = noiseLevel = blShift = 0 ; |
roysandberg | 62:8e2fbe131b53 | 199 | |
roysandberg | 62:8e2fbe131b53 | 200 | // Apply a stricter match limit to premature beats. |
roysandberg | 62:8e2fbe131b53 | 201 | |
roysandberg | 62:8e2fbe131b53 | 202 | if((matchIndex < MATCH_LIMIT) && (rhythmClass == PVC) && |
roysandberg | 62:8e2fbe131b53 | 203 | MinimumBeatVariation(morphType) && (mi2 > PVC_MATCH_WITH_AMP_LIMIT)) |
roysandberg | 62:8e2fbe131b53 | 204 | { |
roysandberg | 62:8e2fbe131b53 | 205 | morphType = NewBeatType(newBeat) ; |
roysandberg | 62:8e2fbe131b53 | 206 | lastBeatWasNew = 1 ; |
roysandberg | 62:8e2fbe131b53 | 207 | } |
roysandberg | 62:8e2fbe131b53 | 208 | |
roysandberg | 62:8e2fbe131b53 | 209 | // Match if within standard match limits. |
roysandberg | 62:8e2fbe131b53 | 210 | |
roysandberg | 62:8e2fbe131b53 | 211 | else if((matchIndex < MATCH_LIMIT) && (mi2 <= MATCH_WITH_AMP_LIMIT)) |
roysandberg | 62:8e2fbe131b53 | 212 | UpdateBeatType(morphType,newBeat,mi2,shiftAdj) ; |
roysandberg | 62:8e2fbe131b53 | 213 | |
roysandberg | 62:8e2fbe131b53 | 214 | // If the beat isn't noisy but doesn't match, start a new beat. |
roysandberg | 62:8e2fbe131b53 | 215 | |
roysandberg | 62:8e2fbe131b53 | 216 | else if((blShift < BL_SHIFT_LIMIT) && (noiseLevel < NEW_TYPE_NOISE_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 217 | && (hfNoise < NEW_TYPE_HF_NOISE_LIMIT)) |
roysandberg | 62:8e2fbe131b53 | 218 | { |
roysandberg | 62:8e2fbe131b53 | 219 | morphType = NewBeatType(newBeat) ; |
roysandberg | 62:8e2fbe131b53 | 220 | lastBeatWasNew = 1 ; |
roysandberg | 62:8e2fbe131b53 | 221 | } |
roysandberg | 62:8e2fbe131b53 | 222 | |
roysandberg | 62:8e2fbe131b53 | 223 | // Even if it is a noisy, start new beat if it was an irregular beat. |
roysandberg | 62:8e2fbe131b53 | 224 | |
roysandberg | 62:8e2fbe131b53 | 225 | else if((lastRhythmClass != NORMAL) || (rhythmClass != NORMAL)) |
roysandberg | 62:8e2fbe131b53 | 226 | { |
roysandberg | 62:8e2fbe131b53 | 227 | morphType = NewBeatType(newBeat) ; |
roysandberg | 62:8e2fbe131b53 | 228 | lastBeatWasNew = 1 ; |
roysandberg | 62:8e2fbe131b53 | 229 | } |
roysandberg | 62:8e2fbe131b53 | 230 | |
roysandberg | 62:8e2fbe131b53 | 231 | // If its noisy and regular, don't waste space starting a new beat. |
roysandberg | 62:8e2fbe131b53 | 232 | |
roysandberg | 62:8e2fbe131b53 | 233 | else morphType = MAXTYPES ; |
roysandberg | 62:8e2fbe131b53 | 234 | |
roysandberg | 62:8e2fbe131b53 | 235 | // Update recent rr and type arrays. |
roysandberg | 62:8e2fbe131b53 | 236 | |
roysandberg | 62:8e2fbe131b53 | 237 | for(i = 7; i > 0; --i) |
roysandberg | 62:8e2fbe131b53 | 238 | { |
roysandberg | 62:8e2fbe131b53 | 239 | RecentRRs[i] = RecentRRs[i-1] ; |
roysandberg | 62:8e2fbe131b53 | 240 | RecentTypes[i] = RecentTypes[i-1] ; |
roysandberg | 62:8e2fbe131b53 | 241 | } |
roysandberg | 62:8e2fbe131b53 | 242 | RecentRRs[0] = rr ; |
roysandberg | 62:8e2fbe131b53 | 243 | RecentTypes[0] = morphType ; |
roysandberg | 62:8e2fbe131b53 | 244 | |
roysandberg | 62:8e2fbe131b53 | 245 | lastRhythmClass = rhythmClass ; |
roysandberg | 62:8e2fbe131b53 | 246 | lastIsoLevel = isoLevel ; |
roysandberg | 62:8e2fbe131b53 | 247 | |
roysandberg | 62:8e2fbe131b53 | 248 | // Fetch beat features needed for classification. |
roysandberg | 62:8e2fbe131b53 | 249 | // Get features from average beat if it matched. |
roysandberg | 62:8e2fbe131b53 | 250 | |
roysandberg | 62:8e2fbe131b53 | 251 | if(morphType != MAXTYPES) |
roysandberg | 62:8e2fbe131b53 | 252 | { |
roysandberg | 62:8e2fbe131b53 | 253 | beatClass = GetBeatClass(morphType) ; |
roysandberg | 62:8e2fbe131b53 | 254 | beatWidth = GetBeatWidth(morphType) ; |
roysandberg | 62:8e2fbe131b53 | 255 | *fidAdj = GetBeatCenter(morphType)-FIDMARK ; |
roysandberg | 62:8e2fbe131b53 | 256 | |
roysandberg | 62:8e2fbe131b53 | 257 | // If the width seems large and there have only been a few |
roysandberg | 62:8e2fbe131b53 | 258 | // beats of this type, use the actual beat for width |
roysandberg | 62:8e2fbe131b53 | 259 | // estimate. |
roysandberg | 62:8e2fbe131b53 | 260 | |
roysandberg | 62:8e2fbe131b53 | 261 | if((beatWidth > offset-onset) && (GetBeatTypeCount(morphType) <= 4)) |
roysandberg | 62:8e2fbe131b53 | 262 | { |
roysandberg | 62:8e2fbe131b53 | 263 | beatWidth = offset-onset ; |
roysandberg | 62:8e2fbe131b53 | 264 | *fidAdj = ((offset+onset)/2)-FIDMARK ; |
roysandberg | 62:8e2fbe131b53 | 265 | } |
roysandberg | 62:8e2fbe131b53 | 266 | } |
roysandberg | 62:8e2fbe131b53 | 267 | |
roysandberg | 62:8e2fbe131b53 | 268 | // If this beat didn't match get beat features directly |
roysandberg | 62:8e2fbe131b53 | 269 | // from this beat. |
roysandberg | 62:8e2fbe131b53 | 270 | |
roysandberg | 62:8e2fbe131b53 | 271 | else |
roysandberg | 62:8e2fbe131b53 | 272 | { |
roysandberg | 62:8e2fbe131b53 | 273 | beatWidth = offset-onset ; |
roysandberg | 62:8e2fbe131b53 | 274 | beatClass = UNKNOWN ; |
roysandberg | 62:8e2fbe131b53 | 275 | *fidAdj = ((offset+onset)/2)-FIDMARK ; |
roysandberg | 62:8e2fbe131b53 | 276 | } |
roysandberg | 62:8e2fbe131b53 | 277 | |
roysandberg | 62:8e2fbe131b53 | 278 | // Fetch dominant type beat features. |
roysandberg | 62:8e2fbe131b53 | 279 | |
roysandberg | 62:8e2fbe131b53 | 280 | DomType = domType = DomMonitor(morphType, rhythmClass, beatWidth, rr, 0) ; |
roysandberg | 62:8e2fbe131b53 | 281 | domWidth = GetBeatWidth(domType) ; |
roysandberg | 62:8e2fbe131b53 | 282 | |
roysandberg | 62:8e2fbe131b53 | 283 | // Compare the beat type, or actual beat to the dominant beat. |
roysandberg | 62:8e2fbe131b53 | 284 | |
roysandberg | 62:8e2fbe131b53 | 285 | if((morphType != domType) && (morphType != 8)) |
roysandberg | 62:8e2fbe131b53 | 286 | domIndex = DomCompare(morphType,domType) ; |
roysandberg | 62:8e2fbe131b53 | 287 | else if(morphType == 8) |
roysandberg | 62:8e2fbe131b53 | 288 | domIndex = DomCompare2(newBeat,domType) ; |
roysandberg | 62:8e2fbe131b53 | 289 | else domIndex = matchIndex ; |
roysandberg | 62:8e2fbe131b53 | 290 | |
roysandberg | 62:8e2fbe131b53 | 291 | // Update post classificaton of the previous beat. |
roysandberg | 62:8e2fbe131b53 | 292 | |
roysandberg | 62:8e2fbe131b53 | 293 | PostClassify(RecentTypes, domType, RecentRRs, beatWidth, domIndex, rhythmClass) ; |
roysandberg | 62:8e2fbe131b53 | 294 | |
roysandberg | 62:8e2fbe131b53 | 295 | // Classify regardless of how the morphology |
roysandberg | 62:8e2fbe131b53 | 296 | // was previously classified. |
roysandberg | 62:8e2fbe131b53 | 297 | |
roysandberg | 62:8e2fbe131b53 | 298 | tempClass = TempClass(rhythmClass, morphType, beatWidth, domWidth, |
roysandberg | 62:8e2fbe131b53 | 299 | domType, hfNoise, noiseLevel, blShift, domIndex) ; |
roysandberg | 62:8e2fbe131b53 | 300 | |
roysandberg | 62:8e2fbe131b53 | 301 | // If this morphology has not been classified yet, attempt to classify |
roysandberg | 62:8e2fbe131b53 | 302 | // it. |
roysandberg | 62:8e2fbe131b53 | 303 | |
roysandberg | 62:8e2fbe131b53 | 304 | if((beatClass == UNKNOWN) && (morphType < MAXTYPES)) |
roysandberg | 62:8e2fbe131b53 | 305 | { |
roysandberg | 62:8e2fbe131b53 | 306 | |
roysandberg | 62:8e2fbe131b53 | 307 | // Classify as normal if there are 6 in a row |
roysandberg | 62:8e2fbe131b53 | 308 | // or at least two in a row that meet rhythm |
roysandberg | 62:8e2fbe131b53 | 309 | // rules for normal. |
roysandberg | 62:8e2fbe131b53 | 310 | |
roysandberg | 62:8e2fbe131b53 | 311 | runCount = GetRunCount() ; |
roysandberg | 62:8e2fbe131b53 | 312 | |
roysandberg | 62:8e2fbe131b53 | 313 | // Classify a morphology as NORMAL if it is not too wide, and there |
roysandberg | 62:8e2fbe131b53 | 314 | // are three in a row. The width criterion prevents ventricular beats |
roysandberg | 62:8e2fbe131b53 | 315 | // from being classified as normal during VTACH (MIT/BIH 205). |
roysandberg | 62:8e2fbe131b53 | 316 | |
roysandberg | 62:8e2fbe131b53 | 317 | if((runCount >= 3) && (domType != -1) && (beatWidth < domWidth+BEAT_MS20)) |
roysandberg | 62:8e2fbe131b53 | 318 | SetBeatClass(morphType,NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 319 | |
roysandberg | 62:8e2fbe131b53 | 320 | // If there is no dominant type established yet, classify any type |
roysandberg | 62:8e2fbe131b53 | 321 | // with six in a row as NORMAL. |
roysandberg | 62:8e2fbe131b53 | 322 | |
roysandberg | 62:8e2fbe131b53 | 323 | else if((runCount >= 6) && (domType == -1)) |
roysandberg | 62:8e2fbe131b53 | 324 | SetBeatClass(morphType,NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 325 | |
roysandberg | 62:8e2fbe131b53 | 326 | // During bigeminy, classify the premature beats as ventricular if |
roysandberg | 62:8e2fbe131b53 | 327 | // they are not too narrow. |
roysandberg | 62:8e2fbe131b53 | 328 | |
roysandberg | 62:8e2fbe131b53 | 329 | else if(IsBigeminy() == 1) |
roysandberg | 62:8e2fbe131b53 | 330 | { |
roysandberg | 62:8e2fbe131b53 | 331 | if((rhythmClass == PVC) && (beatWidth > BEAT_MS100)) |
roysandberg | 62:8e2fbe131b53 | 332 | SetBeatClass(morphType,PVC) ; |
roysandberg | 62:8e2fbe131b53 | 333 | else if(rhythmClass == NORMAL) |
roysandberg | 62:8e2fbe131b53 | 334 | SetBeatClass(morphType,NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 335 | } |
roysandberg | 62:8e2fbe131b53 | 336 | } |
roysandberg | 62:8e2fbe131b53 | 337 | |
roysandberg | 62:8e2fbe131b53 | 338 | // Save morphology type of this beat for next classification. |
roysandberg | 62:8e2fbe131b53 | 339 | |
roysandberg | 62:8e2fbe131b53 | 340 | *beatMatch = morphType ; |
roysandberg | 62:8e2fbe131b53 | 341 | |
roysandberg | 62:8e2fbe131b53 | 342 | beatClass = GetBeatClass(morphType) ; |
roysandberg | 62:8e2fbe131b53 | 343 | |
roysandberg | 62:8e2fbe131b53 | 344 | // If the morphology has been previously classified. |
roysandberg | 62:8e2fbe131b53 | 345 | // use that classification. |
roysandberg | 62:8e2fbe131b53 | 346 | // return(rhythmClass) ; |
roysandberg | 62:8e2fbe131b53 | 347 | |
roysandberg | 62:8e2fbe131b53 | 348 | if(beatClass != UNKNOWN) |
roysandberg | 62:8e2fbe131b53 | 349 | return(beatClass) ; |
roysandberg | 62:8e2fbe131b53 | 350 | |
roysandberg | 62:8e2fbe131b53 | 351 | if(CheckPostClass(morphType) == PVC) |
roysandberg | 62:8e2fbe131b53 | 352 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 353 | |
roysandberg | 62:8e2fbe131b53 | 354 | // Otherwise use the temporary classification. |
roysandberg | 62:8e2fbe131b53 | 355 | |
roysandberg | 62:8e2fbe131b53 | 356 | return(tempClass) ; |
roysandberg | 62:8e2fbe131b53 | 357 | } |
roysandberg | 62:8e2fbe131b53 | 358 | |
roysandberg | 62:8e2fbe131b53 | 359 | /************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 360 | * HFNoiseCheck() gauges the high frequency (muscle noise) present in the |
roysandberg | 62:8e2fbe131b53 | 361 | * beat template. The high frequency noise level is estimated by highpass |
roysandberg | 62:8e2fbe131b53 | 362 | * filtering the beat (y[n] = x[n] - 2*x[n-1] + x[n-2]), averaging the |
roysandberg | 62:8e2fbe131b53 | 363 | * highpass filtered signal over five samples, and finding the maximum of |
roysandberg | 62:8e2fbe131b53 | 364 | * this averaged highpass filtered signal. The high frequency noise metric |
roysandberg | 62:8e2fbe131b53 | 365 | * is then taken to be the ratio of the maximum averaged highpassed signal |
roysandberg | 62:8e2fbe131b53 | 366 | * to the QRS amplitude. |
roysandberg | 62:8e2fbe131b53 | 367 | **************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 368 | |
roysandberg | 62:8e2fbe131b53 | 369 | #define AVELENGTH BEAT_MS50 |
roysandberg | 62:8e2fbe131b53 | 370 | |
roysandberg | 62:8e2fbe131b53 | 371 | int HFNoiseCheck(int *beat) |
roysandberg | 62:8e2fbe131b53 | 372 | { |
roysandberg | 62:8e2fbe131b53 | 373 | int maxNoiseAve = 0, i ; |
roysandberg | 62:8e2fbe131b53 | 374 | int sum=0, aveBuff[AVELENGTH], avePtr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 375 | int qrsMax = 0, qrsMin = 0 ; |
roysandberg | 62:8e2fbe131b53 | 376 | |
roysandberg | 62:8e2fbe131b53 | 377 | // Determine the QRS amplitude. |
roysandberg | 62:8e2fbe131b53 | 378 | |
roysandberg | 62:8e2fbe131b53 | 379 | for(i = FIDMARK-BEAT_MS70; i < FIDMARK+BEAT_MS80; ++i) |
roysandberg | 62:8e2fbe131b53 | 380 | if(beat[i] > qrsMax) |
roysandberg | 62:8e2fbe131b53 | 381 | qrsMax = beat[i] ; |
roysandberg | 62:8e2fbe131b53 | 382 | else if(beat[i] < qrsMin) |
roysandberg | 62:8e2fbe131b53 | 383 | qrsMin = beat[i] ; |
roysandberg | 62:8e2fbe131b53 | 384 | |
roysandberg | 62:8e2fbe131b53 | 385 | for(i = 0; i < AVELENGTH; ++i) |
roysandberg | 62:8e2fbe131b53 | 386 | aveBuff[i] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 387 | |
roysandberg | 62:8e2fbe131b53 | 388 | for(i = FIDMARK-BEAT_MS280; i < FIDMARK+BEAT_MS280; ++i) |
roysandberg | 62:8e2fbe131b53 | 389 | { |
roysandberg | 62:8e2fbe131b53 | 390 | sum -= aveBuff[avePtr] ; |
roysandberg | 62:8e2fbe131b53 | 391 | aveBuff[avePtr] = abs(beat[i] - (beat[i-BEAT_MS10]<<1) + beat[i-2*BEAT_MS10]) ; |
roysandberg | 62:8e2fbe131b53 | 392 | sum += aveBuff[avePtr] ; |
roysandberg | 62:8e2fbe131b53 | 393 | if(++avePtr == AVELENGTH) |
roysandberg | 62:8e2fbe131b53 | 394 | avePtr = 0 ; |
roysandberg | 62:8e2fbe131b53 | 395 | if((i < (FIDMARK - BEAT_MS50)) || (i > (FIDMARK + BEAT_MS110))) |
roysandberg | 62:8e2fbe131b53 | 396 | if(sum > maxNoiseAve) |
roysandberg | 62:8e2fbe131b53 | 397 | maxNoiseAve = sum ; |
roysandberg | 62:8e2fbe131b53 | 398 | } |
roysandberg | 62:8e2fbe131b53 | 399 | if((qrsMax - qrsMin)>=4) |
roysandberg | 62:8e2fbe131b53 | 400 | return((maxNoiseAve * (50/AVELENGTH))/((qrsMax-qrsMin)>>2)) ; |
roysandberg | 62:8e2fbe131b53 | 401 | else return(0) ; |
roysandberg | 62:8e2fbe131b53 | 402 | } |
roysandberg | 62:8e2fbe131b53 | 403 | |
roysandberg | 62:8e2fbe131b53 | 404 | /************************************************************************ |
roysandberg | 62:8e2fbe131b53 | 405 | * TempClass() classifies beats based on their beat features, relative |
roysandberg | 62:8e2fbe131b53 | 406 | * to the features of the dominant beat and the present noise level. |
roysandberg | 62:8e2fbe131b53 | 407 | *************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 408 | |
roysandberg | 62:8e2fbe131b53 | 409 | int TempClass(int rhythmClass, int morphType, |
roysandberg | 62:8e2fbe131b53 | 410 | int beatWidth, int domWidth, int domType, |
roysandberg | 62:8e2fbe131b53 | 411 | int hfNoise, int noiseLevel, int blShift, double domIndex) |
roysandberg | 62:8e2fbe131b53 | 412 | { |
roysandberg | 62:8e2fbe131b53 | 413 | |
roysandberg | 62:8e2fbe131b53 | 414 | // Rule 1: If no dominant type has been detected classify all |
roysandberg | 62:8e2fbe131b53 | 415 | // beats as UNKNOWN. |
roysandberg | 62:8e2fbe131b53 | 416 | |
roysandberg | 62:8e2fbe131b53 | 417 | if(domType < 0) |
roysandberg | 62:8e2fbe131b53 | 418 | return(UNKNOWN) ; |
roysandberg | 62:8e2fbe131b53 | 419 | |
roysandberg | 62:8e2fbe131b53 | 420 | // Rule 2: If the dominant rhythm is normal, the dominant |
roysandberg | 62:8e2fbe131b53 | 421 | // beat type doesn't vary much, this beat is premature |
roysandberg | 62:8e2fbe131b53 | 422 | // and looks sufficiently different than the dominant beat |
roysandberg | 62:8e2fbe131b53 | 423 | // classify as PVC. |
roysandberg | 62:8e2fbe131b53 | 424 | |
roysandberg | 62:8e2fbe131b53 | 425 | if(MinimumBeatVariation(domType) && (rhythmClass == PVC) |
roysandberg | 62:8e2fbe131b53 | 426 | && (domIndex > R2_DI_THRESHOLD) && (GetDomRhythm() == 1)) |
roysandberg | 62:8e2fbe131b53 | 427 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 428 | |
roysandberg | 62:8e2fbe131b53 | 429 | // Rule 3: If the beat is sufficiently narrow, classify as normal. |
roysandberg | 62:8e2fbe131b53 | 430 | |
roysandberg | 62:8e2fbe131b53 | 431 | if(beatWidth < R3_WIDTH_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 432 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 433 | |
roysandberg | 62:8e2fbe131b53 | 434 | // Rule 5: If the beat cannot be matched to any previously |
roysandberg | 62:8e2fbe131b53 | 435 | // detected morphology and it is not premature, consider it normal |
roysandberg | 62:8e2fbe131b53 | 436 | // (probably noisy). |
roysandberg | 62:8e2fbe131b53 | 437 | |
roysandberg | 62:8e2fbe131b53 | 438 | if((morphType == MAXTYPES) && (rhythmClass != PVC)) // == UNKNOWN |
roysandberg | 62:8e2fbe131b53 | 439 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 440 | |
roysandberg | 62:8e2fbe131b53 | 441 | // Rule 6: If the maximum number of beat types have been stored, |
roysandberg | 62:8e2fbe131b53 | 442 | // this beat is not regular or premature and only one |
roysandberg | 62:8e2fbe131b53 | 443 | // beat of this morphology has been seen, call it normal (probably |
roysandberg | 62:8e2fbe131b53 | 444 | // noisy). |
roysandberg | 62:8e2fbe131b53 | 445 | |
roysandberg | 62:8e2fbe131b53 | 446 | if((GetTypesCount() == MAXTYPES) && (GetBeatTypeCount(morphType)==1) |
roysandberg | 62:8e2fbe131b53 | 447 | && (rhythmClass == UNKNOWN)) |
roysandberg | 62:8e2fbe131b53 | 448 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 449 | |
roysandberg | 62:8e2fbe131b53 | 450 | // Rule 7: If this beat looks like the dominant beat and the |
roysandberg | 62:8e2fbe131b53 | 451 | // rhythm is regular, call it normal. |
roysandberg | 62:8e2fbe131b53 | 452 | |
roysandberg | 62:8e2fbe131b53 | 453 | if((domIndex < R7_DI_THRESHOLD) && (rhythmClass == NORMAL)) |
roysandberg | 62:8e2fbe131b53 | 454 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 455 | |
roysandberg | 62:8e2fbe131b53 | 456 | // Rule 8: If post classification rhythm is normal for this |
roysandberg | 62:8e2fbe131b53 | 457 | // type and its shape is close to the dominant shape, classify |
roysandberg | 62:8e2fbe131b53 | 458 | // as normal. |
roysandberg | 62:8e2fbe131b53 | 459 | |
roysandberg | 62:8e2fbe131b53 | 460 | if((domIndex < R8_DI_THRESHOLD) && (CheckPCRhythm(morphType) == NORMAL)) |
roysandberg | 62:8e2fbe131b53 | 461 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 462 | |
roysandberg | 62:8e2fbe131b53 | 463 | // Rule 9: If the beat is not premature, it looks similar to the dominant |
roysandberg | 62:8e2fbe131b53 | 464 | // beat type, and the dominant beat type is variable (noisy), classify as |
roysandberg | 62:8e2fbe131b53 | 465 | // normal. |
roysandberg | 62:8e2fbe131b53 | 466 | |
roysandberg | 62:8e2fbe131b53 | 467 | if((domIndex < R9_DI_THRESHOLD) && (rhythmClass != PVC) && WideBeatVariation(domType)) |
roysandberg | 62:8e2fbe131b53 | 468 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 469 | |
roysandberg | 62:8e2fbe131b53 | 470 | // Rule 10: If this beat is significantly different from the dominant beat |
roysandberg | 62:8e2fbe131b53 | 471 | // there have previously been matching beats, the post rhythm classification |
roysandberg | 62:8e2fbe131b53 | 472 | // of this type is PVC, and the dominant rhythm is regular, classify as PVC. |
roysandberg | 62:8e2fbe131b53 | 473 | |
roysandberg | 62:8e2fbe131b53 | 474 | if((domIndex > R10_DI_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 475 | && (GetBeatTypeCount(morphType) >= R10_BC_LIM) && |
roysandberg | 62:8e2fbe131b53 | 476 | (CheckPCRhythm(morphType) == PVC) && (GetDomRhythm() == 1)) |
roysandberg | 62:8e2fbe131b53 | 477 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 478 | |
roysandberg | 62:8e2fbe131b53 | 479 | // Rule 11: if the beat is wide, wider than the dominant beat, doesn't |
roysandberg | 62:8e2fbe131b53 | 480 | // appear to be noisy, and matches a previous type, classify it as |
roysandberg | 62:8e2fbe131b53 | 481 | // a PVC. |
roysandberg | 62:8e2fbe131b53 | 482 | |
roysandberg | 62:8e2fbe131b53 | 483 | if( (beatWidth >= R11_MIN_WIDTH) && |
roysandberg | 62:8e2fbe131b53 | 484 | (((beatWidth - domWidth >= R11_WIDTH_DIFF1) && (domWidth < R11_WIDTH_BREAK)) || |
roysandberg | 62:8e2fbe131b53 | 485 | (beatWidth - domWidth >= R11_WIDTH_DIFF2)) && |
roysandberg | 62:8e2fbe131b53 | 486 | (hfNoise < R11_HF_THRESHOLD) && (noiseLevel < R11_MA_THRESHOLD) && (blShift < BL_SHIFT_LIMIT) && |
roysandberg | 62:8e2fbe131b53 | 487 | (morphType < MAXTYPES) && (GetBeatTypeCount(morphType) > R11_BC_LIM)) // Rev 1.1 |
roysandberg | 62:8e2fbe131b53 | 488 | |
roysandberg | 62:8e2fbe131b53 | 489 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 490 | |
roysandberg | 62:8e2fbe131b53 | 491 | // Rule 12: If the dominant rhythm is regular and this beat is premature |
roysandberg | 62:8e2fbe131b53 | 492 | // then classify as PVC. |
roysandberg | 62:8e2fbe131b53 | 493 | |
roysandberg | 62:8e2fbe131b53 | 494 | if((rhythmClass == PVC) && (GetDomRhythm() == 1)) |
roysandberg | 62:8e2fbe131b53 | 495 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 496 | |
roysandberg | 62:8e2fbe131b53 | 497 | // Rule 14: If the beat is regular and the dominant rhythm is regular |
roysandberg | 62:8e2fbe131b53 | 498 | // call the beat normal. |
roysandberg | 62:8e2fbe131b53 | 499 | |
roysandberg | 62:8e2fbe131b53 | 500 | if((rhythmClass == NORMAL) && (GetDomRhythm() == 1)) |
roysandberg | 62:8e2fbe131b53 | 501 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 502 | |
roysandberg | 62:8e2fbe131b53 | 503 | // By this point, we know that rhythm will not help us, so we |
roysandberg | 62:8e2fbe131b53 | 504 | // have to classify based on width and similarity to the dominant |
roysandberg | 62:8e2fbe131b53 | 505 | // beat type. |
roysandberg | 62:8e2fbe131b53 | 506 | |
roysandberg | 62:8e2fbe131b53 | 507 | // Rule 15: If the beat is wider than normal, wide on an |
roysandberg | 62:8e2fbe131b53 | 508 | // absolute scale, and significantly different from the |
roysandberg | 62:8e2fbe131b53 | 509 | // dominant beat, call it a PVC. |
roysandberg | 62:8e2fbe131b53 | 510 | |
roysandberg | 62:8e2fbe131b53 | 511 | if((beatWidth > domWidth) && (domIndex > R15_DI_THRESHOLD) && |
roysandberg | 62:8e2fbe131b53 | 512 | (beatWidth >= R15_WIDTH_THRESHOLD)) |
roysandberg | 62:8e2fbe131b53 | 513 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 514 | |
roysandberg | 62:8e2fbe131b53 | 515 | // Rule 16: If the beat is sufficiently narrow, call it normal. |
roysandberg | 62:8e2fbe131b53 | 516 | |
roysandberg | 62:8e2fbe131b53 | 517 | if(beatWidth < R16_WIDTH_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 518 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 519 | |
roysandberg | 62:8e2fbe131b53 | 520 | // Rule 17: If the beat isn't much wider than the dominant beat |
roysandberg | 62:8e2fbe131b53 | 521 | // call it normal. |
roysandberg | 62:8e2fbe131b53 | 522 | |
roysandberg | 62:8e2fbe131b53 | 523 | if(beatWidth < domWidth + R17_WIDTH_DELTA) |
roysandberg | 62:8e2fbe131b53 | 524 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 525 | |
roysandberg | 62:8e2fbe131b53 | 526 | // If the beat is noisy but reasonably close to dominant, |
roysandberg | 62:8e2fbe131b53 | 527 | // call it normal. |
roysandberg | 62:8e2fbe131b53 | 528 | |
roysandberg | 62:8e2fbe131b53 | 529 | // Rule 18: If the beat is similar to the dominant beat, call it normal. |
roysandberg | 62:8e2fbe131b53 | 530 | |
roysandberg | 62:8e2fbe131b53 | 531 | if(domIndex < R18_DI_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 532 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 533 | |
roysandberg | 62:8e2fbe131b53 | 534 | // If it's noisy don't trust the width. |
roysandberg | 62:8e2fbe131b53 | 535 | |
roysandberg | 62:8e2fbe131b53 | 536 | // Rule 19: If the beat is noisy, we can't trust our width estimate |
roysandberg | 62:8e2fbe131b53 | 537 | // and we have no useful rhythm information, so guess normal. |
roysandberg | 62:8e2fbe131b53 | 538 | |
roysandberg | 62:8e2fbe131b53 | 539 | if(hfNoise > R19_HF_THRESHOLD) |
roysandberg | 62:8e2fbe131b53 | 540 | return(NORMAL) ; |
roysandberg | 62:8e2fbe131b53 | 541 | |
roysandberg | 62:8e2fbe131b53 | 542 | // Rule 20: By this point, we have no rhythm information, the beat |
roysandberg | 62:8e2fbe131b53 | 543 | // isn't particularly narrow, the beat isn't particulary similar to |
roysandberg | 62:8e2fbe131b53 | 544 | // the dominant beat, so guess a PVC. |
roysandberg | 62:8e2fbe131b53 | 545 | |
roysandberg | 62:8e2fbe131b53 | 546 | return(PVC) ; |
roysandberg | 62:8e2fbe131b53 | 547 | |
roysandberg | 62:8e2fbe131b53 | 548 | } |
roysandberg | 62:8e2fbe131b53 | 549 | |
roysandberg | 62:8e2fbe131b53 | 550 | |
roysandberg | 62:8e2fbe131b53 | 551 | /**************************************************************************** |
roysandberg | 62:8e2fbe131b53 | 552 | * DomMonitor, monitors which beat morphology is considered to be dominant. |
roysandberg | 62:8e2fbe131b53 | 553 | * The dominant morphology is the beat morphology that has been most frequently |
roysandberg | 62:8e2fbe131b53 | 554 | * classified as normal over the course of the last 120 beats. The dominant |
roysandberg | 62:8e2fbe131b53 | 555 | * beat rhythm is classified as regular if at least 3/4 of the dominant beats |
roysandberg | 62:8e2fbe131b53 | 556 | * have been classified as regular. |
roysandberg | 62:8e2fbe131b53 | 557 | *******************************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 558 | |
roysandberg | 62:8e2fbe131b53 | 559 | #define DM_BUFFER_LENGTH 180 |
roysandberg | 62:8e2fbe131b53 | 560 | |
roysandberg | 62:8e2fbe131b53 | 561 | int NewDom, DomRhythm ; |
roysandberg | 62:8e2fbe131b53 | 562 | int DMBeatTypes[DM_BUFFER_LENGTH], DMBeatClasses[DM_BUFFER_LENGTH] ; |
roysandberg | 62:8e2fbe131b53 | 563 | int DMBeatRhythms[DM_BUFFER_LENGTH] ; |
roysandberg | 62:8e2fbe131b53 | 564 | int DMNormCounts[8], DMBeatCounts[8], DMIrregCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 565 | |
roysandberg | 62:8e2fbe131b53 | 566 | int DomMonitor(int morphType, int rhythmClass, int beatWidth, int rr, int reset) |
roysandberg | 62:8e2fbe131b53 | 567 | { |
roysandberg | 62:8e2fbe131b53 | 568 | static int brIndex = 0 ; |
roysandberg | 62:8e2fbe131b53 | 569 | int i, oldType, runCount, dom, max ; |
roysandberg | 62:8e2fbe131b53 | 570 | |
roysandberg | 62:8e2fbe131b53 | 571 | // Fetch the type of the beat before the last beat. |
roysandberg | 62:8e2fbe131b53 | 572 | |
roysandberg | 62:8e2fbe131b53 | 573 | i = brIndex - 2 ; |
roysandberg | 62:8e2fbe131b53 | 574 | if(i < 0) |
roysandberg | 62:8e2fbe131b53 | 575 | i += DM_BUFFER_LENGTH ; |
roysandberg | 62:8e2fbe131b53 | 576 | oldType = DMBeatTypes[i] ; |
roysandberg | 62:8e2fbe131b53 | 577 | |
roysandberg | 62:8e2fbe131b53 | 578 | // If reset flag is set, reset beat type counts and |
roysandberg | 62:8e2fbe131b53 | 579 | // beat information buffers. |
roysandberg | 62:8e2fbe131b53 | 580 | |
roysandberg | 62:8e2fbe131b53 | 581 | if(reset != 0) |
roysandberg | 62:8e2fbe131b53 | 582 | { |
roysandberg | 62:8e2fbe131b53 | 583 | for(i = 0; i < DM_BUFFER_LENGTH; ++i) |
roysandberg | 62:8e2fbe131b53 | 584 | { |
roysandberg | 62:8e2fbe131b53 | 585 | DMBeatTypes[i] = -1 ; |
roysandberg | 62:8e2fbe131b53 | 586 | DMBeatClasses[i] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 587 | } |
roysandberg | 62:8e2fbe131b53 | 588 | |
roysandberg | 62:8e2fbe131b53 | 589 | for(i = 0; i < 8; ++i) |
roysandberg | 62:8e2fbe131b53 | 590 | { |
roysandberg | 62:8e2fbe131b53 | 591 | DMNormCounts[i] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 592 | DMBeatCounts[i] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 593 | } |
roysandberg | 62:8e2fbe131b53 | 594 | DMIrregCount = 0 ; |
roysandberg | 62:8e2fbe131b53 | 595 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 596 | } |
roysandberg | 62:8e2fbe131b53 | 597 | |
roysandberg | 62:8e2fbe131b53 | 598 | // Once we have wrapped around, subtract old beat types from |
roysandberg | 62:8e2fbe131b53 | 599 | // the beat counts. |
roysandberg | 62:8e2fbe131b53 | 600 | |
roysandberg | 62:8e2fbe131b53 | 601 | if((DMBeatTypes[brIndex] != -1) && (DMBeatTypes[brIndex] != MAXTYPES)) |
roysandberg | 62:8e2fbe131b53 | 602 | { |
roysandberg | 62:8e2fbe131b53 | 603 | --DMBeatCounts[DMBeatTypes[brIndex]] ; |
roysandberg | 62:8e2fbe131b53 | 604 | DMNormCounts[DMBeatTypes[brIndex]] -= DMBeatClasses[brIndex] ; |
roysandberg | 62:8e2fbe131b53 | 605 | if(DMBeatRhythms[brIndex] == UNKNOWN) |
roysandberg | 62:8e2fbe131b53 | 606 | --DMIrregCount ; |
roysandberg | 62:8e2fbe131b53 | 607 | } |
roysandberg | 62:8e2fbe131b53 | 608 | |
roysandberg | 62:8e2fbe131b53 | 609 | // If this is a morphology that has been detected before, decide |
roysandberg | 62:8e2fbe131b53 | 610 | // (for the purposes of selecting the dominant normal beattype) |
roysandberg | 62:8e2fbe131b53 | 611 | // whether it is normal or not and update the approporiate counts. |
roysandberg | 62:8e2fbe131b53 | 612 | |
roysandberg | 62:8e2fbe131b53 | 613 | if(morphType != 8) |
roysandberg | 62:8e2fbe131b53 | 614 | { |
roysandberg | 62:8e2fbe131b53 | 615 | |
roysandberg | 62:8e2fbe131b53 | 616 | // Update the buffers of previous beats and increment the |
roysandberg | 62:8e2fbe131b53 | 617 | // count for this beat type. |
roysandberg | 62:8e2fbe131b53 | 618 | |
roysandberg | 62:8e2fbe131b53 | 619 | DMBeatTypes[brIndex] = morphType ; |
roysandberg | 62:8e2fbe131b53 | 620 | ++DMBeatCounts[morphType] ; |
roysandberg | 62:8e2fbe131b53 | 621 | DMBeatRhythms[brIndex] = rhythmClass ; |
roysandberg | 62:8e2fbe131b53 | 622 | |
roysandberg | 62:8e2fbe131b53 | 623 | // If the rhythm appears regular, update the regular rhythm |
roysandberg | 62:8e2fbe131b53 | 624 | // count. |
roysandberg | 62:8e2fbe131b53 | 625 | |
roysandberg | 62:8e2fbe131b53 | 626 | if(rhythmClass == UNKNOWN) |
roysandberg | 62:8e2fbe131b53 | 627 | ++DMIrregCount ; |
roysandberg | 62:8e2fbe131b53 | 628 | |
roysandberg | 62:8e2fbe131b53 | 629 | // Check to see how many beats of this type have occurred in |
roysandberg | 62:8e2fbe131b53 | 630 | // a row (stop counting at six). |
roysandberg | 62:8e2fbe131b53 | 631 | |
roysandberg | 62:8e2fbe131b53 | 632 | i = brIndex - 1 ; |
roysandberg | 62:8e2fbe131b53 | 633 | if(i < 0) i += DM_BUFFER_LENGTH ; |
roysandberg | 62:8e2fbe131b53 | 634 | for(runCount = 0; (DMBeatTypes[i] == morphType) && (runCount < 6); ++runCount) |
roysandberg | 62:8e2fbe131b53 | 635 | if(--i < 0) i += DM_BUFFER_LENGTH ; |
roysandberg | 62:8e2fbe131b53 | 636 | |
roysandberg | 62:8e2fbe131b53 | 637 | // If the rhythm is regular, the beat width is less than 130 ms, and |
roysandberg | 62:8e2fbe131b53 | 638 | // there have been at least two in a row, consider the beat to be |
roysandberg | 62:8e2fbe131b53 | 639 | // normal. |
roysandberg | 62:8e2fbe131b53 | 640 | |
roysandberg | 62:8e2fbe131b53 | 641 | if((rhythmClass == NORMAL) && (beatWidth < BEAT_MS130) && (runCount >= 1)) |
roysandberg | 62:8e2fbe131b53 | 642 | { |
roysandberg | 62:8e2fbe131b53 | 643 | DMBeatClasses[brIndex] = 1 ; |
roysandberg | 62:8e2fbe131b53 | 644 | ++DMNormCounts[morphType] ; |
roysandberg | 62:8e2fbe131b53 | 645 | } |
roysandberg | 62:8e2fbe131b53 | 646 | |
roysandberg | 62:8e2fbe131b53 | 647 | // If the last beat was within the normal P-R interval for this beat, |
roysandberg | 62:8e2fbe131b53 | 648 | // and the one before that was this beat type, assume the last beat |
roysandberg | 62:8e2fbe131b53 | 649 | // was noise and this beat is normal. |
roysandberg | 62:8e2fbe131b53 | 650 | |
roysandberg | 62:8e2fbe131b53 | 651 | else if(rr < ((FIDMARK-GetBeatBegin(morphType))*SAMPLE_RATE/BEAT_SAMPLE_RATE) |
roysandberg | 62:8e2fbe131b53 | 652 | && (oldType == morphType)) |
roysandberg | 62:8e2fbe131b53 | 653 | { |
roysandberg | 62:8e2fbe131b53 | 654 | DMBeatClasses[brIndex] = 1 ; |
roysandberg | 62:8e2fbe131b53 | 655 | ++DMNormCounts[morphType] ; |
roysandberg | 62:8e2fbe131b53 | 656 | } |
roysandberg | 62:8e2fbe131b53 | 657 | |
roysandberg | 62:8e2fbe131b53 | 658 | // Otherwise assume that this is not a normal beat. |
roysandberg | 62:8e2fbe131b53 | 659 | |
roysandberg | 62:8e2fbe131b53 | 660 | else DMBeatClasses[brIndex] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 661 | } |
roysandberg | 62:8e2fbe131b53 | 662 | |
roysandberg | 62:8e2fbe131b53 | 663 | // If the beat does not match any of the beat types, store |
roysandberg | 62:8e2fbe131b53 | 664 | // an indication that the beat does not match. |
roysandberg | 62:8e2fbe131b53 | 665 | |
roysandberg | 62:8e2fbe131b53 | 666 | else |
roysandberg | 62:8e2fbe131b53 | 667 | { |
roysandberg | 62:8e2fbe131b53 | 668 | DMBeatClasses[brIndex] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 669 | DMBeatTypes[brIndex] = -1 ; |
roysandberg | 62:8e2fbe131b53 | 670 | } |
roysandberg | 62:8e2fbe131b53 | 671 | |
roysandberg | 62:8e2fbe131b53 | 672 | // Increment the index to the beginning of the circular buffers. |
roysandberg | 62:8e2fbe131b53 | 673 | |
roysandberg | 62:8e2fbe131b53 | 674 | if(++brIndex == DM_BUFFER_LENGTH) |
roysandberg | 62:8e2fbe131b53 | 675 | brIndex = 0 ; |
roysandberg | 62:8e2fbe131b53 | 676 | |
roysandberg | 62:8e2fbe131b53 | 677 | // Determine which beat type has the most beats that seem |
roysandberg | 62:8e2fbe131b53 | 678 | // normal. |
roysandberg | 62:8e2fbe131b53 | 679 | |
roysandberg | 62:8e2fbe131b53 | 680 | dom = 0 ; |
roysandberg | 62:8e2fbe131b53 | 681 | for(i = 1; i < 8; ++i) |
roysandberg | 62:8e2fbe131b53 | 682 | if(DMNormCounts[i] > DMNormCounts[dom]) |
roysandberg | 62:8e2fbe131b53 | 683 | dom = i ; |
roysandberg | 62:8e2fbe131b53 | 684 | |
roysandberg | 62:8e2fbe131b53 | 685 | max = 0 ; |
roysandberg | 62:8e2fbe131b53 | 686 | for(i = 1; i < 8; ++i) |
roysandberg | 62:8e2fbe131b53 | 687 | if(DMBeatCounts[i] > DMBeatCounts[max]) |
roysandberg | 62:8e2fbe131b53 | 688 | max = i ; |
roysandberg | 62:8e2fbe131b53 | 689 | |
roysandberg | 62:8e2fbe131b53 | 690 | // If there are no normal looking beats, fall back on which beat |
roysandberg | 62:8e2fbe131b53 | 691 | // has occurred most frequently since classification began. |
roysandberg | 62:8e2fbe131b53 | 692 | |
roysandberg | 62:8e2fbe131b53 | 693 | if((DMNormCounts[dom] == 0) || (DMBeatCounts[max]/DMBeatCounts[dom] >= 2)) // == 0 |
roysandberg | 62:8e2fbe131b53 | 694 | dom = GetDominantType() ; |
roysandberg | 62:8e2fbe131b53 | 695 | |
roysandberg | 62:8e2fbe131b53 | 696 | // If at least half of the most frequently occuring normal |
roysandberg | 62:8e2fbe131b53 | 697 | // type do not seem normal, fall back on choosing the most frequently |
roysandberg | 62:8e2fbe131b53 | 698 | // occurring type since classification began. |
roysandberg | 62:8e2fbe131b53 | 699 | |
roysandberg | 62:8e2fbe131b53 | 700 | else if(DMBeatCounts[dom]/DMNormCounts[dom] >= 2) |
roysandberg | 62:8e2fbe131b53 | 701 | dom = GetDominantType() ; |
roysandberg | 62:8e2fbe131b53 | 702 | |
roysandberg | 62:8e2fbe131b53 | 703 | // If there is any beat type that has been classfied as normal, |
roysandberg | 62:8e2fbe131b53 | 704 | // but at least 10 don't seem normal, reclassify it to UNKNOWN. |
roysandberg | 62:8e2fbe131b53 | 705 | |
roysandberg | 62:8e2fbe131b53 | 706 | for(i = 0; i < 8; ++i) |
roysandberg | 62:8e2fbe131b53 | 707 | if((DMBeatCounts[i] > 10) && (DMNormCounts[i] == 0) && (i != dom) |
roysandberg | 62:8e2fbe131b53 | 708 | && (GetBeatClass(i) == NORMAL)) |
roysandberg | 62:8e2fbe131b53 | 709 | SetBeatClass(i,UNKNOWN) ; |
roysandberg | 62:8e2fbe131b53 | 710 | |
roysandberg | 62:8e2fbe131b53 | 711 | // Save the dominant type in a global variable so that it is |
roysandberg | 62:8e2fbe131b53 | 712 | // accessable for debugging. |
roysandberg | 62:8e2fbe131b53 | 713 | |
roysandberg | 62:8e2fbe131b53 | 714 | NewDom = dom ; |
roysandberg | 62:8e2fbe131b53 | 715 | return(dom) ; |
roysandberg | 62:8e2fbe131b53 | 716 | } |
roysandberg | 62:8e2fbe131b53 | 717 | |
roysandberg | 62:8e2fbe131b53 | 718 | int GetNewDominantType(void) |
roysandberg | 62:8e2fbe131b53 | 719 | { |
roysandberg | 62:8e2fbe131b53 | 720 | return(NewDom) ; |
roysandberg | 62:8e2fbe131b53 | 721 | } |
roysandberg | 62:8e2fbe131b53 | 722 | |
roysandberg | 62:8e2fbe131b53 | 723 | int GetDomRhythm(void) |
roysandberg | 62:8e2fbe131b53 | 724 | { |
roysandberg | 62:8e2fbe131b53 | 725 | if(DMIrregCount > IRREG_RR_LIMIT) |
roysandberg | 62:8e2fbe131b53 | 726 | return(0) ; |
roysandberg | 62:8e2fbe131b53 | 727 | else return(1) ; |
roysandberg | 62:8e2fbe131b53 | 728 | } |
roysandberg | 62:8e2fbe131b53 | 729 | |
roysandberg | 62:8e2fbe131b53 | 730 | |
roysandberg | 62:8e2fbe131b53 | 731 | void AdjustDomData(int oldType, int newType) |
roysandberg | 62:8e2fbe131b53 | 732 | { |
roysandberg | 62:8e2fbe131b53 | 733 | int i ; |
roysandberg | 62:8e2fbe131b53 | 734 | |
roysandberg | 62:8e2fbe131b53 | 735 | for(i = 0; i < DM_BUFFER_LENGTH; ++i) |
roysandberg | 62:8e2fbe131b53 | 736 | { |
roysandberg | 62:8e2fbe131b53 | 737 | if(DMBeatTypes[i] == oldType) |
roysandberg | 62:8e2fbe131b53 | 738 | DMBeatTypes[i] = newType ; |
roysandberg | 62:8e2fbe131b53 | 739 | } |
roysandberg | 62:8e2fbe131b53 | 740 | |
roysandberg | 62:8e2fbe131b53 | 741 | if(newType != MAXTYPES) |
roysandberg | 62:8e2fbe131b53 | 742 | { |
roysandberg | 62:8e2fbe131b53 | 743 | DMNormCounts[newType] = DMNormCounts[oldType] ; |
roysandberg | 62:8e2fbe131b53 | 744 | DMBeatCounts[newType] = DMBeatCounts[oldType] ; |
roysandberg | 62:8e2fbe131b53 | 745 | } |
roysandberg | 62:8e2fbe131b53 | 746 | |
roysandberg | 62:8e2fbe131b53 | 747 | DMNormCounts[oldType] = DMBeatCounts[oldType] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 748 | |
roysandberg | 62:8e2fbe131b53 | 749 | } |
roysandberg | 62:8e2fbe131b53 | 750 | |
roysandberg | 62:8e2fbe131b53 | 751 | void CombineDomData(int oldType, int newType) |
roysandberg | 62:8e2fbe131b53 | 752 | { |
roysandberg | 62:8e2fbe131b53 | 753 | int i ; |
roysandberg | 62:8e2fbe131b53 | 754 | |
roysandberg | 62:8e2fbe131b53 | 755 | for(i = 0; i < DM_BUFFER_LENGTH; ++i) |
roysandberg | 62:8e2fbe131b53 | 756 | { |
roysandberg | 62:8e2fbe131b53 | 757 | if(DMBeatTypes[i] == oldType) |
roysandberg | 62:8e2fbe131b53 | 758 | DMBeatTypes[i] = newType ; |
roysandberg | 62:8e2fbe131b53 | 759 | } |
roysandberg | 62:8e2fbe131b53 | 760 | |
roysandberg | 62:8e2fbe131b53 | 761 | if(newType != MAXTYPES) |
roysandberg | 62:8e2fbe131b53 | 762 | { |
roysandberg | 62:8e2fbe131b53 | 763 | DMNormCounts[newType] += DMNormCounts[oldType] ; |
roysandberg | 62:8e2fbe131b53 | 764 | DMBeatCounts[newType] += DMBeatCounts[oldType] ; |
roysandberg | 62:8e2fbe131b53 | 765 | } |
roysandberg | 62:8e2fbe131b53 | 766 | |
roysandberg | 62:8e2fbe131b53 | 767 | DMNormCounts[oldType] = DMBeatCounts[oldType] = 0 ; |
roysandberg | 62:8e2fbe131b53 | 768 | |
roysandberg | 62:8e2fbe131b53 | 769 | } |
roysandberg | 62:8e2fbe131b53 | 770 | |
roysandberg | 62:8e2fbe131b53 | 771 | /*********************************************************************** |
roysandberg | 62:8e2fbe131b53 | 772 | GetRunCount() checks how many of the present beat type have occurred |
roysandberg | 62:8e2fbe131b53 | 773 | in a row. |
roysandberg | 62:8e2fbe131b53 | 774 | ***********************************************************************/ |
roysandberg | 62:8e2fbe131b53 | 775 | |
roysandberg | 62:8e2fbe131b53 | 776 | int GetRunCount() |
roysandberg | 62:8e2fbe131b53 | 777 | { |
roysandberg | 62:8e2fbe131b53 | 778 | int i ; |
roysandberg | 62:8e2fbe131b53 | 779 | for(i = 1; (i < 8) && (RecentTypes[0] == RecentTypes[i]); ++i) ; |
roysandberg | 62:8e2fbe131b53 | 780 | return(i) ; |
roysandberg | 62:8e2fbe131b53 | 781 | } |
roysandberg | 62:8e2fbe131b53 | 782 | |
roysandberg | 62:8e2fbe131b53 | 783 | |
roysandberg | 62:8e2fbe131b53 | 784 | |
roysandberg | 62:8e2fbe131b53 | 785 | |
roysandberg | 62:8e2fbe131b53 | 786 | |
roysandberg | 62:8e2fbe131b53 | 787 | |
roysandberg | 62:8e2fbe131b53 | 788 | |
roysandberg | 62:8e2fbe131b53 | 789 | |
roysandberg | 62:8e2fbe131b53 | 790 | |
roysandberg | 62:8e2fbe131b53 | 791 | |
roysandberg | 62:8e2fbe131b53 | 792 |