Switch to unified view

a b/mit-bih-arrhythmia-database-1.0.0/mitdbdir/src/dbnotes.c
1
/* file: dbnotes.c  G. Moody    25 August 1988
2
            Last revised:   6 December 1990
3
Create database notes file (tbl/troff input format)
4
5
Copyright (C) Massachusetts Institute of Technology 1990. All rights reserved.
6
*/
7
8
#include <stdio.h>
9
#include <ecg/db.h>
10
#include <ecg/ecgmap.h>
11
12
/* Rhythm types.  These may be rearranged arbitrarily;  the order determines
13
   the order in which rhythms are printed in the rhythm table.  Add a call to
14
   `setrtab' and adjust N_RTYPES whenever another rhythm is added. */
15
#define R_UNDEF 0
16
#define NSR 1
17
#define SBR 2
18
#define BII 3
19
#define B22 4
20
#define B3  5
21
#define SAB 6
22
#define PREX    7
23
#define AB  8
24
#define EAR 9
25
#define SVTA    10
26
#define AFL 11
27
#define AFIB    12
28
#define PR  13
29
#define NOD 14
30
#define VBG 15
31
#define VTG 16
32
#define IVR 17
33
#define VT  18
34
#define VFL 19
35
36
#define N_RTYPES    20  /* number of rhythm types, including R_UNDEF */
37
38
/* ST and T change types. */
39
#define STP 0   /* increase in ST level */
40
#define STN 1   /* decrease in ST level */
41
#define TP  2   /* increase in T amplitude */
42
#define TXP 3   /* extreme increase in T amplitude */
43
#define TN  4   /* decrease in T amplitude */
44
#define TXN 5   /* extreme decrease in T amplitude */
45
#define TBP 6   /* biphasic T-wave */
46
47
#define N_STYPES    7   /* number of ST and T change types */
48
49
50
#define PLUS    1   /* positive change */
51
#define MINUS   2   /* negative change */
52
#define XPLUS   3   /* extreme positive change */
53
#define XMINUS  4   /* extreme negative change */
54
#define BIPHASIC 5  /* change to biphasic form */
55
56
#define STCH0   1   /* beginning of ST change episode */
57
#define STCH1   2   /* extremum of ST change */
58
#define STCH2   3   /* end of ST change episode */
59
60
#define TCH0    4   /* beginning of T change episode */
61
#define TCH1    5   /* extremum of T change */
62
#define TCH2    6   /* end of T change episode */
63
64
65
/* Noise type bit masks. */
66
#define C0      0x00    /* signal 0 clean */
67
#define C1      0x00    /* signal 1 clean */
68
#define C2      0x00    /* signal 2 clean */
69
#define C3      0x00    /* signal 3 clean */
70
#define N0          0x01    /* signal 0 noisy */
71
#define N1          0x02    /* signal 1 noisy */
72
#define N2          0x04    /* signal 2 noisy */
73
#define N3          0x08    /* signal 3 noisy */
74
#define U0          0x11    /* signal 0 unreadable */
75
#define U1          0x22    /* signal 1 unreadable */
76
#define U2      0x44    /* signal 2 unreadable */
77
#define U3      0x88    /* signal 3 unreadable */
78
#define UNREADABLE      0xff    /* all signals unreadable */
79
80
#define N_NTYPES    256 /* number of noise types */
81
#define N_RLEN  20      /* number of distinct run lengths */
82
83
/* Values for info_type. */
84
#define NO_INFO 0
85
#define MITDB   1
86
#define EDB 2
87
#define VALEDB  3
88
89
struct btabstruct {
90
    long lcount;    /* number of beats of this type in learning period */
91
    long tcount;    /* number of beats of this type in test period */
92
} btab[ACMAX+1];
93
94
struct rltabstruct {
95
    long len;       /* number of beats in run */
96
    long nruns;     /* number of runs of `len' beats */
97
} artab[N_RLEN], vrtab[N_RLEN];
98
99
struct rtabstruct {
100
    long episodes;  /* number of episodes of this rhythm */
101
    long duration;  /* duration of this rhythm (in sample intervals) */
102
    char *rstring;  /* rhythm string as it appears in annotation file */
103
    char *rslong;   /* rhythm name as it is printed by this program */
104
    int min3r, max3r;   /* minimum/maximum length of 3 R-R intervals */
105
} rtab[N_RTYPES];
106
107
struct sttabstruct {
108
    long episodes;  /* number of episodes of ST or T change of this type */
109
    long duration;  /* duration of episodes (in sample intervals) */
110
    char *ststring; /* description of change as printed by this program */
111
    int extremum;   /* extreme value of change */
112
} sttab[N_STYPES][MAXSIG];
113
114
struct ntabstruct {
115
    long episodes;  /* number of episodes of this noise type */
116
    long duration;  /* duration of this noise type (in sample intervals) */
117
    char *nstring;  /* noise type as it is printed by this program */
118
} ntab[N_NTYPES];
119
120
long lsum, tsum;    /* numbers of beats of all types in the learning and
121
               test periods (tallied by `bstats') */
122
long ttest;     /* time of the end of the test period */
123
124
main(argc, argv)
125
int argc;
126
char *argv[];
127
{
128
    double t3min;
129
    int a=0, age, c, i, info_type, magnitude, maxrate, minrate, noise = 0,
130
    nsig, rhythm = R_UNDEF, rlen = -1, rr3, sign, st_calibrated = 0,
131
    st_data = 0, t_data = 0, type, v=0;
132
    static long n0, r0, tq0, tq1, tq2, tq3, s0[N_STYPES][MAXSIG];
133
    struct siginfo si[MAXSIG];
134
    struct anninfo ai;
135
    struct ann annot;
136
    static char buf[256], *infop, *p, *record, sex[10], umask;
137
138
    /* Check command-line arguments. */
139
    if (argc < 3) {
140
    fprintf(stderr, "usage: %s annotator record\n", argv[0]);
141
    exit(1);
142
    }
143
144
    /* Open the database files. */
145
    ai.name = argv[1]; ai.stat = READ; record = argv[2];
146
    if (annopen(record, &ai, 1) < 0 ||
147
    (nsig = isigopen(record, si, -MAXSIG)) < 0)
148
    exit(2);
149
150
    /* Initialize variables. */
151
    t3min = strtim("3:0");
152
    ttest = strtim("5:0");
153
154
    setrtab(R_UNDEF, NULL, "Unspecified rhythm");
155
    setrtab(NSR, "N", "Normal sinus rhythm");
156
    setrtab(SBR, "SBR", "Sinus bradycardia");
157
    setrtab(BII, "BII", "2\\(de heart block");
158
    setrtab(B22, "B22", "2\\(de heart block (Mobitz type II)");
159
    setrtab(B3, "B3", "3\\(de heart block");
160
    setrtab(SAB, "SAB", "Sino-atrial block");
161
    setrtab(PREX, "PREX", "Pre-excitation (WPW)");
162
    setrtab(AB, "AB", "Atrial bigeminy");
163
    setrtab(EAR, "EAR", "Ectopic atrial rhythm");
164
    setrtab(SVTA, "SVTA", "SVTA");
165
    setrtab(AFL, "AFL", "Atrial flutter");
166
    setrtab(AFIB, "AFIB", "Atrial fibrillation");
167
    setrtab(PR, "P", "Paced rhythm");
168
    setrtab(NOD, "NOD", "Nodal (junctional) rhythm");
169
    setrtab(VBG, "B", "Ventricular bigeminy");
170
    setrtab(VTG, "T", "Ventricular trigeminy");
171
    setrtab(IVR, "IVR", "Idioventricular rhythm");
172
    setrtab(VT, "VT", "Ventricular tachycardia");
173
    setrtab(VFL, "VFL", "Ventricular flutter");
174
175
    for (c = 0; c < nsig; c++) {
176
    setstab(STP, c, "Positive ST dev.");
177
    setstab(STN, c, "Negative ST dev.");
178
    setstab(TP, c, "Positive T dev.");
179
    setstab(TXP, c, "T dev. \\(>= 400 \\(*mV");
180
    setstab(TN, c, "Negative T dev.");
181
    setstab(TXN, c, "T dev. \\(<= \\(mi400 \\(*mV");
182
    setstab(TBP, c, "Biphasic T");
183
    }
184
185
    switch (nsig) {
186
      case 1:
187
    setntab(C0, "Clean");
188
    setntab(N0, "Noisy");
189
    setntab(U0, "Unreadable");
190
    setntab(UNREADABLE, "Unreadable");
191
    umask = U0;
192
    break;      /* so far, so good ... */
193
      case 2:
194
    setntab(C0|C1, "Both clean");
195
    setntab(C0|N1, "Lower noisy");
196
    setntab(C0|U1, "Lower unreadable");
197
    setntab(N0|C1, "Upper noisy");
198
    setntab(N0|N1, "Both noisy");
199
    setntab(N0|U1, "Upper noisy, lower unreadable");
200
    setntab(U0|C1, "Upper unreadable");
201
    setntab(U0|N1, "Upper unreadable, lower noisy");
202
    setntab(U0|U1, "Both unreadable");
203
    setntab(UNREADABLE, "Unreadable");
204
    umask = U0|U1;
205
    break;      /* yikes! */
206
      case 3:
207
    setntab(C0|C1|C2, "All clean");
208
    setntab(C0|C1|N2, "Lower noisy");
209
    setntab(C0|C1|U2, "Lower unreadable");
210
    setntab(C0|N1|C2, "Middle noisy");
211
    setntab(C0|N1|N2, "Middle and lower noisy");
212
    setntab(C0|N1|U2, "Middle noisy, lower unreadable");
213
    setntab(C0|U1|C2, "Middle unreadable");
214
    setntab(C0|U1|N2, "Middle unreadable, lower noisy");
215
    setntab(C0|U1|U2, "Middle and lower unreadable");
216
    setntab(N0|C1|C2, "Upper noisy");
217
    setntab(N0|C1|N2, "Upper and lower noisy");
218
    setntab(N0|C1|U2, "Upper noisy, lower unreadable");
219
    setntab(N0|N1|C2, "Upper and middle noisy");
220
    setntab(N0|N1|N2, "All noisy");
221
    setntab(N0|N1|U2, "Lower unreadable, others noisy");
222
    setntab(N0|U1|C2, "Upper noisy, middle unreadable");
223
    setntab(N0|U1|N2, "Middle unreadable, others noisy");
224
    setntab(N0|U1|U2, "Upper noisy, others unreadable");
225
    setntab(U0|C1|C2, "Upper unreadable");
226
    setntab(U0|C1|N2, "Upper unreadable, lower noisy");
227
    setntab(U0|C1|U2, "Upper and lower unreadable");
228
    setntab(U0|N1|C2, "Upper unreadable, middle noisy");
229
    setntab(U0|N1|N2, "Upper unreadable, others noisy");
230
    setntab(U0|N1|U2, "Middle noisy, others unreadable");
231
    setntab(U0|U1|C2, "Upper and middle unreadable");
232
    setntab(U0|U1|N2, "Lower noisy, others unreadable");
233
    setntab(U0|U1|U2, "All unreadable");
234
    setntab(UNREADABLE, "Unreadable");
235
    umask = U0|U1|U2;
236
    break;      /* arggggh! */
237
/*    case 4:          nonononononononononono!!!!!!! */
238
      default:
239
    umask = UNREADABLE;
240
    break;
241
    }
242
243
    /* Read the annotation file. */
244
    while (getann(0, &annot) >= 0) {
245
246
    /* Count the annotation in the test or learning period as appropriate. */
247
    if (annot.time >= ttest) btab[annot.anntyp].tcount++;
248
    else btab[annot.anntyp].lcount++;
249
250
    if (isqrs(annot.anntyp)) {
251
252
        /* Update the ectopic activity tables if appropriate. */
253
        switch (map2(annot.anntyp)) {
254
        case NORMAL:    if (a > 0) { aupdate(a); a = 0; }
255
                if (v > 0) { vupdate(v); v = 0; }
256
                break;
257
        case SVPB:  if (v > 0) { vupdate(v); v = 0; }
258
                a++;
259
                break;
260
        case PVC:
261
        case FUSION:    if (a > 0) { aupdate(a); a = 0; }
262
                v++;
263
                break;
264
        }
265
266
        /* Keep track of the times of the last 4 beats. */
267
        tq0 = tq1; tq1 = tq2; tq2 = tq3; tq3 = annot.time;
268
269
        /* Update the count of consecutive beats in the current rhythm. */
270
        rlen++;
271
272
        /* Check various conditions which might make a heart rate
273
           measurement invalid. */
274
        switch (rhythm) {
275
          case NSR:
276
          case SBR:
277
          case BII:
278
          case B22:
279
          case B3:
280
          case SAB:
281
          case PREX: 
282
        if (map2(annot.anntyp) != NORMAL) rlen = -1; break;
283
          case AB:
284
          case NOD:
285
        if (map1(annot.anntyp) != NORMAL) rlen = -1; break;
286
          case EAR:
287
          case SVTA:
288
        if (map2(annot.anntyp) != SVPB) rlen = -1; break;
289
          case PR:
290
        if (annot.anntyp != PACE) rlen = -1; break;
291
          case IVR:
292
          case VT:
293
        if (map1(annot.anntyp) == NORMAL) rlen = -1; break;
294
          case VFL:
295
        if (annot.anntyp != FLWAV) rlen = -1; break;
296
          default:
297
        break;
298
        }   
299
300
        /* If a valid heart rate can be measured, update the records of
301
           maximum and minimum rate for the current rhythm.  Note that
302
           these can be determined from the minimum and maximum R-R
303
           intervals;  what is actually used here is the sum of three
304
           consecutive intervals. */
305
        if (rlen >= 3) {
306
        rr3 = tq3 - tq0;
307
        if (rr3 > rtab[rhythm].max3r) rtab[rhythm].max3r = rr3;
308
        if (rr3 < rtab[rhythm].min3r) rtab[rhythm].min3r = rr3;
309
        }
310
    }
311
    else if (annot.anntyp == RHYTHM) {
312
        /* Don't count the interval from the beginning of the record to
313
           the first rhythm label as `unspecified rhythm' unless the first
314
           beat label occurs before the first rhythm label. */
315
        if (tq3 > 0L) {
316
        rtab[rhythm].episodes++;
317
        rtab[rhythm].duration += annot.time - r0;
318
        r0 = annot.time;
319
        }
320
        /* Identify the new rhythm. */
321
        for (i = 1, rhythm = R_UNDEF; i < N_RTYPES; i++)
322
        if (strcmp(annot.aux+2, rtab[i].rstring) == 0) {
323
            rhythm = i;
324
            break;
325
        }
326
        if (rhythm == SVTA || rhythm == VT) rlen = 0;
327
        else rlen = -1;
328
    }
329
    else if (annot.anntyp == STCH || annot.anntyp == TCH) {
330
        /* Identify the nature of the ST or T change from the aux field.
331
           The syntax of the aux field is one of:
332
             "(" <type> [<signal designation>] <sign>
333
         "A" <type> [<signal designation>] <sign> [<magnitude>]
334
         <type> [<signal designation>] <sign> ")"
335
           where <type> is "ST" or "T", <signal designation> is 1 or 2
336
           decimal digits, <sign> is "+", "++", "-", "--", or "BP", and
337
           <magnitude> is 2-4 decimal digits.  ("++" and "--" appear only
338
           in T-wave change annotations;  they designate extreme (>= 0.4 mV
339
           changes in T-wave amplitude.)  The signal designation is
340
           the signal number (e.g., signal 0 is designated by "0",
341
           etc.);  in records which contain only one signal (such as those
342
           in the VALE DB), the signal designation is omitted.  The
343
           magnitude is given in microvolts, and may also be omitted (as
344
           in the VALE DB).
345
         */
346
        if (annot.aux == NULL)
347
        fprintf(stderr,
348
            "warning: %s annotation without text field at %s\n",
349
            ecgstr(annot.anntyp), timstr(annot.time));
350
        else {
351
        p = annot.aux+1;
352
        switch (*p) {
353
          case '(':
354
            if (annot.anntyp == STCH) {
355
            type = STCH0;
356
            p += 3;     /* skip `(ST' */
357
            }
358
            else {
359
            type = TCH0;
360
            p += 2;     /* skip `(T' */
361
            }
362
            break;
363
          case 'A':
364
            if (annot.anntyp == STCH) {
365
            type = STCH1;
366
            p += 3;     /* skip `(ST' */
367
            }
368
            else {
369
            type = TCH1;
370
            p += 2;     /* skip `(T' */
371
            }
372
            break;
373
          case 'S':
374
            type = STCH2;
375
            p += 2;     /* skip `ST' */
376
            break;
377
          case 'T':
378
            type = TCH2;
379
            p++;        /* skip `T' */
380
            break;
381
          default:
382
            fprintf(stderr,
383
            "warning: %s annotation with text field %s at %s\n",
384
            ecgstr(annot.anntyp), annot.aux, timstr(annot.time));
385
            type = 0;
386
            break;
387
        }
388
        if (nsig > 1) {
389
            if ((c = atoi(p)) > 9) {
390
            p++;
391
            }
392
            p++;
393
        }
394
        else c = 0;
395
        switch (*p++) {
396
          case '+':
397
            if (*p == '+') {
398
            sign = XPLUS;
399
            p++;
400
            }
401
            else
402
            sign = PLUS;
403
            break;
404
          case '-':
405
            if (*p == '-') {
406
            sign = XMINUS;
407
            p++;
408
            }
409
            else
410
            sign = MINUS;
411
            break;
412
          case 'B':
413
            if (*p == 'P') {
414
            sign = BIPHASIC;
415
            p++;
416
            }
417
            break;
418
          default:
419
            fprintf(stderr,
420
            "warning: %s annotation with text field %s at %s\n",
421
            ecgstr(annot.anntyp), annot.aux, timstr(annot.time));
422
            sign = 0;
423
            break;
424
        }
425
        if (*p && *p != ')') {
426
            if ((magnitude = atoi(p)) > 0)
427
            st_calibrated = 1;
428
        }
429
        else magnitude = 0;
430
        if (0 <= c && c < MAXSIG) switch (type) {
431
          case STCH0:   /* beginning of ST change episode */
432
            if (sign == PLUS) {
433
            st_data = 1;
434
            s0[STP][c] = annot.time;
435
            }
436
            else if (sign == MINUS) {
437
            st_data = 1;
438
            s0[STN][c] = annot.time;
439
            }
440
            break;
441
          case STCH1:   /* extremum of ST change */
442
            if (sign == PLUS && magnitude > sttab[STP][c].extremum)
443
            sttab[STP][c].extremum = magnitude;
444
            else if (sign == MINUS &&
445
                 magnitude > sttab[STN][c].extremum)
446
            sttab[STN][c].extremum = magnitude;
447
            break;
448
          case STCH2:   /* end of ST change episode */
449
            if (sign == PLUS) {
450
            sttab[STP][c].episodes++;
451
            sttab[STP][c].duration += annot.time - s0[STP][c];
452
            s0[STP][c] = 0L;
453
            }
454
            else if (sign == MINUS) {
455
            sttab[STN][c].episodes++;
456
            sttab[STN][c].duration += annot.time - s0[STN][c];
457
            s0[STN][c] = 0L;
458
            }
459
            break;
460
          case TCH0:    /* beginning of T change episode */
461
            if (sign == PLUS) {
462
            t_data = 1;
463
            s0[TP][c] = annot.time;
464
            }
465
            else if (sign == MINUS) {
466
            t_data = 1;
467
            s0[TN][c] = annot.time;
468
            }
469
            else if (sign == XPLUS) {
470
            t_data = 1;
471
            s0[TXP][c] = annot.time;
472
            }
473
            else if (sign == XMINUS) {
474
            t_data = 1;
475
            s0[TXN][c] = annot.time;
476
            }
477
            else if (sign == BIPHASIC) {
478
            t_data = 1;
479
            s0[TBP][c] = annot.time;
480
            }
481
            break;
482
          case TCH1:    /* extremum of T change */
483
            if ((sign == PLUS || sign == XPLUS) &&
484
            magnitude > sttab[TP][c].extremum)
485
            sttab[TP][c].extremum = magnitude;
486
            else if ((sign == MINUS || sign == XMINUS) &&
487
                 magnitude > sttab[TN][c].extremum)
488
            sttab[TN][c].extremum = magnitude;
489
            break;
490
          case TCH2:    /* end of T change episode */
491
            if (sign == PLUS) {
492
            sttab[TP][c].episodes++;
493
            sttab[TP][c].duration += annot.time - s0[TP][c];
494
            s0[TP][c] = 0L;
495
            }
496
            else if (sign == MINUS) {
497
            sttab[TN][c].episodes++;
498
            sttab[TN][c].duration += annot.time - s0[TN][c];
499
            s0[TN][c] = 0L;
500
            }
501
            else if (sign == XPLUS) {
502
            sttab[TXP][c].episodes++;
503
            sttab[TXP][c].duration += annot.time - s0[TXP][c];
504
            s0[TXP][c] = 0L;
505
            }
506
            else if (sign == XMINUS) {
507
            sttab[TXN][c].episodes++;
508
            sttab[TXN][c].duration += annot.time - s0[TXN][c];
509
            s0[TXN][c] = 0L;
510
            }
511
            else if (sign == BIPHASIC) {
512
            sttab[TBP][c].episodes++;
513
            sttab[TBP][c].duration += annot.time - s0[TBP][c];
514
            s0[TBP][c] = 0L;
515
            }
516
            break;
517
          default:
518
            break;
519
        }
520
        }
521
    }
522
    else if (annot.anntyp == NOISE) {
523
        /* Don't count the interval from the beginning of the record to
524
           the first signal quality label as `clean' unless the first beat
525
           label occurs before the first signal quality label. */
526
        if (tq3 > 0L) {
527
        ntab[noise].episodes++;
528
        ntab[noise].duration += annot.time - n0;
529
        n0 = annot.time;
530
        }
531
        noise = annot.subtyp & 0xff;
532
533
        /* Unreadable segments invalidate rate measurements. */
534
        if ((noise & umask) == umask) rlen = -1;
535
    }
536
    }
537
538
    /* At the end of the annotation file, count any run in progress and adjust
539
       the counters for the current rhythm and noise type, and ST and T states.
540
    */
541
    if (a) aupdate(a);
542
    if (v) aupdate(v);
543
    if (strtim("e") > annot.time) annot.time = strtim("e");
544
    rtab[rhythm].episodes++;
545
    rtab[rhythm].duration += annot.time - r0;
546
    for (c = 0; c < nsig; c++) {
547
    for (i = 0; i < N_STYPES; i++)
548
        if (s0[i][c] > 0L) {
549
        sttab[i][c].episodes++;
550
        sttab[i][c].duration += annot.time - s0[i][c];
551
        }
552
    if (sttab[TXP][c].episodes > 0)
553
        sttab[TXP][c].extremum = sttab[TP][c].extremum;
554
    if (sttab[TXN][c].episodes > 0)
555
        sttab[TXN][c].extremum = sttab[TN][c].extremum;
556
    }
557
    ntab[noise].episodes++;
558
    ntab[noise].duration += annot.time - n0;
559
560
    /* Read the first line of info, if any. */
561
    infop = getinfo(record);
562
    if (infop == NULL)
563
    info_type = NO_INFO;
564
    else {
565
    while (*infop == ' ' || *infop == '\t')
566
        infop++;
567
    if (*infop == 'A') {    /* `Age: ...' as in European ST-T DB */
568
        sscanf(infop, "Age: %d Sex: %s", &age, sex);
569
        info_type = EDB;
570
    }
571
    else if (*infop == 'R') {   /* `Reference: ...' as in VALE DB */
572
        age = -1; sex[0] = '?';
573
        info_type = VALEDB;
574
    }
575
    else {          /* fixed format as in MIT DB */
576
        sscanf(infop, "%d%s", &age, sex);
577
        info_type = MITDB;
578
    }
579
    }
580
581
    /* Start a "keep" (to force troff to print the following section in a
582
       single column), and print the section header line. */
583
    printf(".KS\n.SH\n");
584
585
    printf("Record %s  (", record);
586
    for (i = 0; i < nsig; i++) {
587
    if (i > 0) fputs(", ", stdout);
588
    fputs(si[i].desc, stdout);
589
    }
590
    fputs("; ", stdout);
591
    switch (sex[0]) {
592
      case 'f':
593
      case 'F': fputs("female, age ", stdout); break;
594
      case 'm':
595
      case 'M': fputs("male, age ", stdout); break;
596
      default:  fputs("age ", stdout); break;
597
    }
598
    if (age > 0) printf("%d)\n", age);
599
    else fputs("not recorded)\n", stdout);
600
601
    switch (info_type) {
602
      case MITDB:
603
    /* Print the medications section (the second line of info). */
604
    if (infop = getinfo(NULL)) {
605
        while (*infop == ' ' || *infop == '\t')
606
        infop++;
607
        printf(".LP\n\\fIMedications:\\fR %s\n", infop);
608
    }
609
    break;
610
      default:
611
    break;
612
    }
613
614
    /* Print the beat table. */
615
    printf(".TS\nexpand;\nlw(1i) c c c\nl r r r.\n");
616
    printf("\\fIBeats\tBefore 5:00\tAfter 5:00\tTotal\\fR\n");
617
    bstats(NORMAL, "Normal");
618
    bstats(LBBB, "Left BBB");
619
    bstats(RBBB, "Right BBB");
620
    bstats(APC, "APC");
621
    bstats(ABERR, "Aberrated APC");
622
    bstats(NPC, "Junctional premature");
623
    bstats(SVPB, "SVPC");
624
    bstats(PVC, "PVC");
625
    bstats(RONT, "R-on-T PVC");
626
    bstats(FUSION, "Fusion PVC");
627
    bstats(FLWAV, "Ventricular flutter wave");
628
    bstats(AESC, "Atrial escape");
629
    bstats(NESC, "Junctional escape");
630
    bstats(SVESC, "Supraventricular escape");
631
    bstats(VESC, "Ventricular escape");
632
    bstats(PACE, "Paced");
633
    bstats(PFUS, "Pacemaker fusion");
634
    bstats(NAPC, "Blocked APC");
635
    bstats(UNKNOWN, "Unclassifiable");
636
    printf("\\fITotal\t%ld\t%ld\t%ld\\fR\n", lsum, tsum, lsum+tsum);
637
    printf(".TE\n.KE\n");
638
639
    /* Print the supraventricular ectopy table, if necessary. */
640
    if (artab[0].len > 1 || artab[0].nruns > 1 || artab[1].len > 0) {
641
    int i, j, k, l;
642
643
    printf(".KS\n.TS\nlw(1.125i) l.\n");
644
    printf("\\fISupraventricular ectopy:\\fR");
645
        for (i = 0; i < N_RLEN; i++) {
646
        for (j = 0, l = 9999; j < N_RLEN; j++)
647
        if (0 < artab[j].len && artab[j].len < l) l = artab[k = j].len;
648
        if (l == 9999) break;
649
        switch (l) {
650
          case 1:
651
        printf("\t%d isolated beat%s\n",
652
               artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
653
        break;
654
          case 2:
655
        printf("\t%d couplet%s\n",
656
               artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
657
        break;
658
          default:
659
        printf("\t%d run%s of %d beats\n",
660
               artab[k].nruns, (artab[k].nruns > 1) ? "s" : "",
661
               artab[k].len);
662
        break;
663
        }
664
        artab[k].len = 0;
665
    }
666
    printf(".TE\n.KE\n");
667
    }
668
669
    /* Print the ventricular ectopy table, if necessary. */
670
    if (vrtab[0].len > 1 || vrtab[0].nruns > 1 || vrtab[1].len > 0) {
671
    int i, j, k, l;
672
673
    printf(".KS\n.TS\nlw(1.125i) l.\n");
674
    printf("\\fIVentricular ectopy:\\fR");
675
        for (i = 0; i < N_RLEN; i++) {
676
        for (j = 0, l = 9999; j < N_RLEN; j++)
677
        if (0 < vrtab[j].len && vrtab[j].len < l) l = vrtab[k = j].len;
678
        if (l == 9999) break;
679
        switch (l) {
680
          case 1:
681
        printf("\t%d isolated beat%s\n",
682
               vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
683
        break;
684
          case 2:
685
        printf("\t%d couplet%s\n",
686
               vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
687
        break;
688
          default:
689
        printf("\t%d run%s of %d beats\n",
690
               vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "",
691
               vrtab[k].len);
692
        break;
693
        }
694
        vrtab[k].len = 0;
695
    }
696
    printf(".TE\n.KE\n");
697
    }
698
699
    /* Print the rhythm table. */
700
    printf(".KS\n.TS\nexpand;\nlw c rw(0.4i) rw(0.4i)\nl c n r.\n");
701
    printf("\\fIRhythm\tRate\tEpisodes\tDuration\\fR\n");
702
    for (i = 0; i < N_RTYPES; i++)
703
    if (rtab[i].episodes != 0L) {
704
        printf("%s\t", rtab[i].rslong);
705
        if (rtab[i].max3r > 0)
706
        printf("%d", minrate = (int)(t3min/rtab[i].max3r + 0.5));
707
        else
708
        minrate = 0;
709
        if (rtab[i].min3r != 0xffff) {
710
        maxrate = (int)(t3min/rtab[i].min3r + 0.5);
711
        if (maxrate > minrate) printf("\\-%d", maxrate);
712
        }
713
        else if (minrate == 0)
714
        printf("\\-");
715
        printf("\t%ld\t%s\n", rtab[i].episodes, timstr(rtab[i].duration));
716
    }
717
    printf(".TE\n.KE\n");
718
719
    /* Print the ST and T-change table, if available. */
720
    if (st_data || t_data) {
721
    printf(".KS\n.TS\nexpand;\nl");
722
    if (st_calibrated) printf(" r");
723
    printf(" rw(0.4i) rw(0.4i)\nl");
724
    if (st_calibrated) printf(" n");
725
    printf(" n r.\n\\fIST\\-T state");
726
    if (st_calibrated) printf("\tExtremum");
727
    printf("\tEpisodes\tDuration\\fR\n");
728
    for (c = 0; c < nsig; c++) {
729
        int header_printed = 0;
730
731
        for (i = 0; i < N_STYPES; i++) {
732
        if (sttab[i][c].episodes != 0L) {
733
            if (header_printed == 0) {
734
            switch (nsig) {
735
              case 1:
736
                break;
737
              case 2:
738
                if (c == 0) printf("\\fI(upper signal)\\fR\n");
739
                else printf("\\fI(lower signal)\\fR\n");
740
                break;
741
              case 3:
742
                if (c == 0) printf("\\fI(upper signal)\\fR\n");
743
                else if (c == 1)
744
                printf("\\fI(middle signal)\\fR\n");
745
                else printf("\\fI(lower signal)\\fR\n");
746
                break;
747
              default:
748
                printf("(signal %d)\n", c);
749
                break;
750
            }
751
            header_printed = 1;
752
            }
753
            printf("%s\t", sttab[i][c].ststring);
754
            if (st_calibrated) {
755
            if (sttab[i][c].extremum > 0) {
756
                switch (i) {
757
                  case STP:
758
                  case TP:
759
                  case TXP: printf("\\(pl"); break;
760
                  case STN:
761
                  case TN:
762
                  case TXN: printf("\\(mi"); break;
763
                }
764
                printf("%d\t", sttab[i][c].extremum);
765
            }
766
            else
767
                printf("\\-\t");
768
            }
769
            printf("%ld\t%s\n", sttab[i][c].episodes,
770
               timstr(sttab[i][c].duration));
771
        }
772
        }
773
    }
774
    printf(".TE\n.KE\n");
775
    }
776
777
    /* Print the signal quality table. */
778
    printf(".KS\n.TS\nexpand;\nl rw(0.4i) rw(0.4i)\nl n r.\n");
779
    printf("\\fISignal quality\tEpisodes\tDuration\\fR\n");
780
    for (i = 0; i < N_NTYPES; i++)
781
    if (ntab[i].episodes != 0L) {
782
        if (ntab[i].nstring == NULL) {
783
        static char ns[40];
784
785
        sprintf(ns, "Signal quality code %d", i);
786
        ntab[i].nstring = ns;
787
        }
788
        printf("%s\t%ld\t%s\n", ntab[i].nstring, ntab[i].episodes,
789
           timstr(ntab[i].duration));
790
    }
791
    printf(".TE\n");    /* (the "keep" ends after the "notes" section) */
792
793
    /* Print a "notes" section if there is any additional info available. */
794
    switch (info_type) {
795
      case EDB:
796
    if (infop = getinfo(NULL)) {
797
        while (*infop == ' ' || *infop == '\t')
798
        infop++;
799
        if ('a' <= *infop && *infop <= 'z') *infop += 'A' - 'a';
800
        printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
801
        while (infop = getinfo(NULL)) {
802
        while (*infop == ' ' || *infop == '\t')
803
            infop++;
804
        printf(".br\n%s\n", infop);
805
        }
806
    }
807
    else
808
        printf(".LP\n");
809
    break;
810
      case MITDB:
811
    if (infop = getinfo(NULL)) {
812
        while (*infop == ' ' || *infop == '\t')
813
        infop++;
814
        printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
815
        while (infop = getinfo(NULL)) {
816
        while (*infop == ' ' || *infop == '\t')
817
            infop++;
818
        printf("%s\n", infop);
819
        }
820
    }
821
    else
822
        printf(".LP\n");
823
    break;
824
      case VALEDB:
825
    if (infop = getinfo(NULL)) {
826
        while (*infop == ' ' || *infop == '\t')
827
        infop++;
828
        if (strncmp(infop, "Reference:", 10) == 0)
829
        infop = getinfo(NULL);
830
    }
831
    if (infop) {
832
        while (*infop == ' ' || *infop == '\t')
833
        infop++;
834
        printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
835
        while (infop = getinfo(NULL)) {
836
        while (*infop == ' ' || *infop == '\t')
837
            infop++;
838
        printf(".br\n%s\n", infop);
839
        }
840
    }
841
    else
842
        printf(".LP\n");
843
    break;
844
      default:
845
    printf(".LP\n");
846
    break;
847
    }
848
    printf(".KE\n");
849
850
/* Close the database files and quit. */
851
    dbquit();
852
    exit(0);
853
}
854
855
setrtab(i, rst, rsl)
856
int i;
857
char *rst, *rsl;
858
{
859
    if (0 <= i && i < N_RTYPES) {
860
    rtab[i].episodes = 0L;
861
    rtab[i].duration = strtim("0.5");
862
    rtab[i].rstring = rst;
863
    rtab[i].rslong = rsl;
864
    rtab[i].min3r = 0xffff;
865
    rtab[i].max3r = 0;
866
    }
867
}
868
869
setstab(i, signal, sst)
870
int i, signal;
871
char *sst;
872
{
873
    if (0 <= i && i < N_STYPES && 0 <= signal && signal < MAXSIG) {
874
    sttab[i][signal].episodes = 0L;
875
    sttab[i][signal].duration = strtim("0.5");
876
    sttab[i][signal].ststring = sst;
877
    sttab[i][signal].extremum = 0;
878
    }
879
}
880
881
setntab(i, nst)
882
int i;
883
char *nst;
884
{
885
    if (0 <= i && i < N_NTYPES) {
886
    ntab[i].episodes = 0L;
887
    ntab[i].duration = strtim("0.5");
888
    ntab[i].nstring = nst;
889
    }
890
}
891
    
892
bstats(i, s)  /* print a line of the beat table if beats of type i were seen */
893
int i;
894
char *s;        /* English description of beat type i */
895
{
896
    if (btab[i].tcount > 0L || btab[i].lcount > 0L) {
897
    printf("%s\t", s);
898
    if (btab[i].lcount > 0L) {
899
        printf("%ld\t", btab[i].lcount);
900
        lsum += btab[i].lcount;
901
    }
902
    else
903
        printf("\\-\t");
904
    if (btab[i].tcount > 0L) {
905
        printf("%ld\t", btab[i].tcount);
906
        tsum += btab[i].tcount;
907
    }
908
    else
909
        printf("\\-\t");
910
    printf("%ld\n", btab[i].lcount + btab[i].tcount);
911
    }
912
}
913
914
aupdate(i)  /* Record that an i-beat SV run was observed */
915
int i;
916
{
917
    int j;
918
919
    for (j = 0; j < N_RLEN-1; j++) {
920
    if (artab[j].len == i) break;
921
    else if (artab[j].len == 0) { artab[j].len = i; break; }
922
    }
923
    artab[j].nruns++;
924
}
925
926
vupdate(i)  /* Record that an i-beat V run was observed */
927
int i;
928
{
929
    int j;
930
931
    for (j = 0; j < N_RLEN-1; j++) {
932
    if (vrtab[j].len == i) break;
933
    else if (vrtab[j].len == 0) { vrtab[j].len = i; break; }
934
    }
935
    vrtab[j].nruns++;
936
}