This program reads a single ECG signal, attempts to detect QRS complexes, and records their locations in an annotation file. The detector algorithm is based on a Pascal program written by W.A.H. Engelse and C. Zeelenberg, "A single scan algorithm for QRS-detection and feature extraction", Computers in Cardiology 6:37-42 (1979).
1 #include <stdio.h> 2 #include <ecg/db.h> 3 #include <ecg/ecgcodes.h> 4 5 #define abs(A) ((A) >= 0 ? (A) : -(A)) 6 7 main(argc, argv) 8 int argc; 9 char *argv[]; 10 { 11 int filter, time=0, slopecrit, sign, maxslope=0, nslope=0, 12 qtime, maxtime, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, 13 ms160, ms200, s2, scmax, scmin = 0, v[DB_MAXSIG]; 14 DB_Anninfo a; 15 DB_Annotation annot; 16 static DB_Siginfo s[DB_MAXSIG]; 17 18 if (argc < 2) { 19 fprintf(stderr, "usage: %s record [threshold]\n", argv[0]); 20 exit(1); 21 } 22 a.name = argv[0]; a.stat = WRITE; 23 if (dbinit(argv[1], &a, 1, s, DB_MAXSIG) < 1) exit(2); 24 if (sampfreq(NULL) != 250.) 25 fprintf(stderr, "warning: %s is designed for 250 Hz input\n", 26 argv[0]); 27 if (argc > 2) scmin = muvadu(0, atoi(argv[2])); 28 if (scmin < 1) scmin = muvadu(0, 1000); 29 slopecrit = scmax = 10 * scmin; 30 ms160 = strtim("0.16"); ms200 = strtim("0.2"); s2 = strtim("2"); 31 annot.subtyp = annot.chan = annot.num = 0; annot.aux = NULL; 32 (void)getvec(v); 33 t9 = t8 = t7 = t6 = t5 = t4 = t3 = t2 = t1 = v[0]; 34 35 do { 36 filter = (t0 = v[0]) + 4*t1 + 6*t2 + 4*t3 + t4 37 - t5 - 4*t6 - 6*t7 - 4*t8 - t9; 38 if (time % s2 == 0) { 39 if (nslope == 0) { 40 slopecrit -= slopecrit >> 4; 41 if (slopecrit < scmin) slopecrit = scmin; 42 } 43 else if (nslope >= 5) { 44 slopecrit += slopecrit >> 4; 45 if (slopecrit > scmax) slopecrit = scmax; 46 } 47 } 48 if (nslope == 0 && abs(filter) > slopecrit) { 49 nslope = 1; maxtime = ms160; 50 sign = (filter > 0) ? 1 : -1; 51 qtime = time; 52 } 53 if (nslope != 0) { 54 if (filter * sign < -slopecrit) { 55 sign = -sign; 56 maxtime = (++nslope > 4) ? ms200 : ms160; 57 } 58 else if (filter * sign > slopecrit && 59 abs(filter) > maxslope) 60 maxslope = abs(filter); 61 if (maxtime-- < 0) { 62 if (2 <= nslope && nslope <= 4) { 63 slopecrit += ((maxslope>>2) - slopecrit) >> 3; 64 if (slopecrit < scmin) slopecrit = scmin; 65 else if (slopecrit > scmax) slopecrit = scmax; 66 annot.time = strtim("i") - (time - qtime) - 4; 67 annot.anntyp = NORMAL; (void)putann(0, &annot); 68 time = 0; 69 } 70 else if (nslope >= 5) { 71 annot.time = strtim("i") - (time - qtime) - 4; 72 annot.anntyp = ARFCT; (void)putann(0, &annot); 73 } 74 nslope = 0; 75 } 76 } 77 t9 = t8; t8 = t7; t7 = t6; t6 = t5; t5 = t4; 78 t4 = t3; t3 = t2; t2 = t1; t1 = t0; time++; 79 } while (getvec(v) > 0); 80 81 dbquit(); 82 exit(0); 83 }
Notes:
scmin
and scmax
are lower and
upper bounds for the adaptive threshold slopecrit
.
slopecrit
is set to 15/16 of its
previous value if no slopes have been found; in line 45, it is set to
17/16 of its previous value if 5 or more slopes were found (suggesting
the presence of noise).
maxtime
to 160 msec, and save the sign of the slope and the current
time relative to the previous beat.
maxslope
(lines 58--60) for eventual use in updating
the threshold (lines 63--65). Once a sufficient interval has elapsed
following the last threshold crossing (line 61), if there were between
2 and 4 slopes, we have (apparently) found a QRS complex, and the program
records a NORMAL
annotation (lines 66--67). If there were 5 or more
slopes, the program records an artifact annotation (lines 71--72). If
only 1 slope was found, it is assumed to represent a baseline shift and
no output is produced.
tn
variables and another sample is read.
Go to the first, previous, next, last section, table of contents.