[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following program is considerably more complex than the previous examples in this chapter. It reads an annotation file (for which the annotator name is specified in its first argument, and the record name in the second argument) and selects beats of a specified type to be averaged. The program selects segments of the signals that are within 50 milliseconds of the time of the specified beat annotations, subtracts a baseline estimate from each sample, and calculates an average waveform (by default, the average normal QRS complex).
1 #include <stdio.h> 2 #include <wfdb/wfdb.h> 3 #include <wfdb/ecgmap.h> 4 5 main(argc, argv) 6 int argc; 7 char *argv[]; 8 { 9 int btype, i, j, nbeats = 0, nsig, hwindow, window; 10 long stoptime = 0L, **sum; 11 WFDB_Anninfo a; 12 WFDB_Annotation annot; 13 WFDB_Sample *v, *vb; 14 WFDB_Siginfo *s; 15 void *calloc(); 16 17 if (argc < 3) { 18 fprintf(stderr, 19 "usage: %s annotator record [beat-type from to]\n", 20 argv[0]); 21 exit(1); 22 } 23 a.name = argv[1]; a.stat = WFDB_READ; 24 if ((nsig = isigopen(argv[2], NULL, 0)) < 1) exit(2); 25 s = (WFDB_Siginfo *)malloc(nsig * sizeof(WFDB_Siginfo)); 26 v = (WFDB_Sample *)malloc(nsig * sizeof(WFDB_Sample)); 27 vb = (WFDB_Sample *)malloc(nsig * sizeof(WFDB_Sample)); 28 sum = (long **)malloc(nsig * sizeof(long *)); 29 if (s == NULL || v == NULL || vb == NULL || sum == NULL) { 30 fprintf(stderr, "%s: insufficient memory\n", argv[0]); 31 exit(2); 32 } 33 if (wfdbinit(argv[2], &a, 1, s, nsig) != nsig) exit(3); 34 hwindow = strtim(".05"); window = 2*hwindow + 1; 35 for (i = 0; i < nsig; i++) 36 if ((sum[i]=(long *)calloc(window,sizeof(long))) == NULL) { 37 fprintf(stderr, "%s: insufficient memory\n", argv[0]); 38 exit(2); 39 } 40 btype = (argc > 3) ? strann(argv[3]) : NORMAL; 41 if (argc > 4) iannsettime(strtim(argv[4])); 42 if (argc > 5) { 43 if ((stoptime = strtim(argv[5])) < 0L) 44 stoptime = -stoptime; 45 if (s[0].nsamp > 0L && stoptime > s[0].nsamp) 46 stoptime = s[0].nsamp; 47 } 48 else stoptime = s[0].nsamp; 49 if (stoptime > 0L) stoptime -= hwindow; 50 while (getann(0, &annot) == 0 && annot.time < hwindow) 51 ; 52 do { 53 if (annot.anntyp != btype) continue; 54 isigsettime(annot.time - hwindow - 1); 55 getvec(vb); 56 for (j = 0; j < window && getvec(v) > 0; j++) 57 for (i = 0; i < nsig; i++) 58 sum[i][j] += v[i] - vb[i]; 59 nbeats++; 60 } while (getann(0, &annot) == 0 && 61 (stoptime == 0L || annot.time < stoptime)); 62 if (nbeats < 1) { 63 fprintf(stderr, "%s: no `%s' beats found\n", 64 argv[0], annstr(btype)); 65 exit(4); 66 } 67 printf("Average of %d `%s' beats:\n", nbeats, annstr(btype)); 68 for (j = 0; j < window; j++) 69 for (i = 0; i < nsig; i++) 70 printf("%g%c", (double)sum[i][j]/nbeats, 71 (i == nsig-1) ? '\n' : '\t'); 72 exit(0); 73 } |
(See http://physionet.org/physiotools/wfdb/examples/example9.c for a copy of this program.)
Notes:
The “half-window” is 50 milliseconds wide, and the “window” (the duration of a segment to be entered into the average) is one sample more than twice that amount (i.e., 50 milliseconds to either side of the fiducial point defined by the annotation).
Here we allocate memory for the sum
vectors that will be used to
store the running totals. See K&R, page 167, for a description
of calloc
.
If a third argument is present on the command line, it is taken as an
annotation code mnemonic for the desired beat type; otherwise, the
program will average NORMAL
QRS complexes.
If a fourth argument is present on the command line, it is taken as the
start time; we arrange for the first annotation to be read by
getann
to be the first annotation that occurs after the chosen
start time.
This code similarly determines when the averaging should stop. Unless
no stop time was specified on the command line and the signal length is
not defined in the ‘hea’ file for the record, stoptime
will have a positive value in line 49, which makes a tiny adjustment so
that if a beat annotation occurs within 50 milliseconds of the end of the
averaging period, the beat will not be included in the average.
This code addresses the (admittedly unlikely) prospect that the first annotation(s) might occur within the first 50 milliseconds of the record; any such annotations will be excluded from the average.
Here we read annotations (the first is already in annot
when we
enter the loop, and subsequent annotations are read in line 60); select
the desired ones (line 53); skip to the correct spot in the signals (line
54; the sample selected there is the one just before the beginning of
the window); read a sample from each signal (line 55) into the vb
vector, which will be used as a crude baseline estimate; read
window
samples from each signal (line 56), subtracting the
baseline from each and adding the result into the running totals; update
a beat counter (line 59); and check for loop termination conditions
(line 61).
This is the output section. If no beats of type btype
were
found, obviously no average can be printed; note that the message goes
to the standard error output, so the user will notice it even if the
standard output has been redirected to a file. In the usual case, the
averages are printed out as a table, with a column allocated to each
signal. Note the cast in line 70 (necessary to preserve precision), and
the trick used in line 71 to print a tab after each column but the last
in each line.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] |
PhysioNet (wfdb@physionet.org)