Switch to unified view

a b/mit-bih-arrhythmia-database-1.0.0/mitdbdir/src/dbnotes-html.c
1
/* file: dbnotes.c  G. Moody    25 August 1988
2
            Last revised:   24 May 1997
3
Create database notes file (HTML format)
4
5
Copyright (C) Massachusetts Institute of Technology 1997. 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
    printf("<h2>Record %s  (", record);
582
    for (i = 0; i < nsig; i++) {
583
    if (i > 0) fputs(", ", stdout);
584
    fputs(si[i].desc, stdout);
585
    }
586
    fputs("; ", stdout);
587
    switch (sex[0]) {
588
      case 'f':
589
      case 'F': fputs("female, age ", stdout); break;
590
      case 'm':
591
      case 'M': fputs("male, age ", stdout); break;
592
      default:  fputs("age ", stdout); break;
593
    }
594
    if (age > 0) printf("%d)</h2>\n", age);
595
    else fputs("not recorded)</h2>\n", stdout);
596
597
    printf("<p>\n");
598
    switch (info_type) {
599
      case MITDB:
600
    /* Print the medications section (the second line of info). */
601
    if (infop = getinfo(NULL)) {
602
        while (*infop == ' ' || *infop == '\t')
603
        infop++;
604
        printf("<i>Medications:</i> %s<br>\n", infop);
605
    }
606
    break;
607
      default:
608
    break;
609
    }
610
611
    /* Print the beat table. */
612
    printf("<p><table>\n");
613
    /*    printf(".TS\nexpand;\nlw(1i) c c c\nl r r r.\n"); */
614
    printf("<tr><th>Beats</th><th>Before 5:00</th><th>After 5:00</th>"
615
       "<th>Total</th></tr>\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("<tr align=right><td>Total</td><td>%ld</td><td>%ld</td>"
637
       "<td>%ld</td></tr>\n", lsum, tsum, lsum+tsum);
638
    /* printf("\\fITotal\t%ld\t%ld\t%ld\\fR\n", lsum, tsum, lsum+tsum); */
639
    printf("</table>\n\n");
640
    /* printf(".TE\n.KE\n"); */
641
642
    /* Print the supraventricular ectopy table, if necessary. */
643
    if (artab[0].len > 1 || artab[0].nruns > 1 || artab[1].len > 0) {
644
    int i, j, k, l;
645
646
    /* printf(".KS\n.TS\nlw(1.125i) l.\n"); */
647
    printf("<p><i>Supraventricular ectopy</i><br>\n<ul>");
648
    /* printf("\\fISupraventricular ectopy:\\fR"); */
649
        for (i = 0; i < N_RLEN; i++) {
650
        for (j = 0, l = 9999; j < N_RLEN; j++)
651
        if (0 < artab[j].len && artab[j].len < l) l = artab[k = j].len;
652
        if (l == 9999) break;
653
        switch (l) {
654
          case 1:
655
        printf("<li> %d isolated beat%s\n",
656
               artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
657
        break;
658
          case 2:
659
        printf("<li> %d couplet%s\n",
660
               artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
661
        break;
662
          default:
663
        printf("<li> %d run%s of %d beats\n",
664
               artab[k].nruns, (artab[k].nruns > 1) ? "s" : "",
665
               artab[k].len);
666
        break;
667
        }
668
        artab[k].len = 0;
669
    }
670
    printf("</ul>\n");
671
    /* printf(".TE\n.KE\n"); */
672
    }
673
674
    /* Print the ventricular ectopy table, if necessary. */
675
    if (vrtab[0].len > 1 || vrtab[0].nruns > 1 || vrtab[1].len > 0) {
676
    int i, j, k, l;
677
678
    /* printf(".KS\n.TS\nlw(1.125i) l.\n"); */
679
    printf("<p><i>Ventricular ectopy</i><br>\n<ul>");
680
    /* printf("\\fIVentricular ectopy:\\fR"); */
681
        for (i = 0; i < N_RLEN; i++) {
682
        for (j = 0, l = 9999; j < N_RLEN; j++)
683
        if (0 < vrtab[j].len && vrtab[j].len < l) l = vrtab[k = j].len;
684
        if (l == 9999) break;
685
        switch (l) {
686
          case 1:
687
        printf("<li> %d isolated beat%s\n",
688
               vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
689
        break;
690
          case 2:
691
        printf("<li> %d couplet%s\n",
692
               vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
693
        break;
694
          default:
695
        printf("<li> %d run%s of %d beats\n",
696
               vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "",
697
               vrtab[k].len);
698
        break;
699
        }
700
        vrtab[k].len = 0;
701
    }
702
    printf("</ul>\n");
703
    /* printf(".TE\n.KE\n"); */
704
    }
705
706
    /* Print the rhythm table. */
707
    printf("<p><table>\n");
708
    /*  printf(".KS\n.TS\nexpand;\nlw c rw(0.4i) rw(0.4i)\nl c n r.\n"); */
709
    printf("<tr><th>Rhythm</th><th>Rate</th><th>Episodes</th>"
710
       "<th>Duration</th></tr>\n");
711
    /* printf("\\fIRhythm\tRate\tEpisodes\tDuration\\fR\n"); */
712
    for (i = 0; i < N_RTYPES; i++)
713
    if (rtab[i].episodes != 0L) {
714
        printf("<tr><td>%s</td><td align=center>", rtab[i].rslong);
715
        if (rtab[i].max3r > 0)
716
        printf("%d", minrate = (int)(t3min/rtab[i].max3r + 0.5));
717
        else
718
        minrate = 0;
719
        if (rtab[i].min3r != 0xffff) {
720
        maxrate = (int)(t3min/rtab[i].min3r + 0.5);
721
        if (maxrate > minrate) printf("-%d", maxrate);
722
        }
723
        else if (minrate == 0)
724
        printf("-");
725
        printf("</td><td align=right>%ld</td><td align=right>%s</td>"
726
           "</tr>\n", rtab[i].episodes, timstr(rtab[i].duration));
727
    }
728
    printf("</table>\n\n");
729
    /* printf(".TE\n.KE\n"); */
730
731
    /* Print the ST and T-change table, if available. */
732
    if (st_data || t_data) {
733
    printf(".KS\n.TS\nexpand;\nl");
734
    if (st_calibrated) printf(" r");
735
    printf(" rw(0.4i) rw(0.4i)\nl");
736
    if (st_calibrated) printf(" n");
737
    printf(" n r.\n\\fIST\\-T state");
738
    if (st_calibrated) printf("\tExtremum");
739
    printf("\tEpisodes\tDuration\\fR\n");
740
    for (c = 0; c < nsig; c++) {
741
        int header_printed = 0;
742
743
        for (i = 0; i < N_STYPES; i++) {
744
        if (sttab[i][c].episodes != 0L) {
745
            if (header_printed == 0) {
746
            switch (nsig) {
747
              case 1:
748
                break;
749
              case 2:
750
                if (c == 0) printf("\\fI(upper signal)\\fR\n");
751
                else printf("\\fI(lower signal)\\fR\n");
752
                break;
753
              case 3:
754
                if (c == 0) printf("\\fI(upper signal)\\fR\n");
755
                else if (c == 1)
756
                printf("\\fI(middle signal)\\fR\n");
757
                else printf("\\fI(lower signal)\\fR\n");
758
                break;
759
              default:
760
                printf("(signal %d)\n", c);
761
                break;
762
            }
763
            header_printed = 1;
764
            }
765
            printf("%s\t", sttab[i][c].ststring);
766
            if (st_calibrated) {
767
            if (sttab[i][c].extremum > 0) {
768
                switch (i) {
769
                  case STP:
770
                  case TP:
771
                  case TXP: printf("\\(pl"); break;
772
                  case STN:
773
                  case TN:
774
                  case TXN: printf("\\(mi"); break;
775
                }
776
                printf("%d\t", sttab[i][c].extremum);
777
            }
778
            else
779
                printf("\\-\t");
780
            }
781
            printf("%ld\t%s\n", sttab[i][c].episodes,
782
               timstr(sttab[i][c].duration));
783
        }
784
        }
785
    }
786
    printf(".TE\n.KE\n");
787
    }
788
789
    /* Print the signal quality table. */
790
    printf("<p><table>\n");
791
    /* printf(".KS\n.TS\nexpand;\nl rw(0.4i) rw(0.4i)\nl n r.\n"); */
792
    printf("<tr><th>Signal quality</th><th>Episodes</th>"
793
       "<th>Duration</th></tr>\n");
794
    /* printf("\\fISignal quality\tEpisodes\tDuration\\fR\n"); */
795
    for (i = 0; i < N_NTYPES; i++)
796
    if (ntab[i].episodes != 0L) {
797
        if (ntab[i].nstring == NULL) {
798
        static char ns[40];
799
800
        sprintf(ns, "Signal quality code %d", i);
801
        ntab[i].nstring = ns;
802
        }
803
        printf("<tr><td>%s</td><td align=right>%ld</td>"
804
           "<td align=right>%s</td></tr>\n",
805
           ntab[i].nstring, ntab[i].episodes,
806
           timstr(ntab[i].duration));
807
    }
808
    printf ("</table>\n\n");
809
    /* printf(".TE\n"); */ /* (the "keep" ends after the "notes" section) */
810
811
    /* Print a "notes" section if there is any additional info available. */
812
    switch (info_type) {
813
      case EDB:
814
    if (infop = getinfo(NULL)) {
815
        while (*infop == ' ' || *infop == '\t')
816
        infop++;
817
        if ('a' <= *infop && *infop <= 'z') *infop += 'A' - 'a';
818
        printf("<p><dl><dt>Notes:</dt>\n<dd>%s", infop);
819
        while (infop = getinfo(NULL)) {
820
        while (*infop == ' ' || *infop == '\t')
821
            infop++;
822
        printf("<br>\n%s", infop);
823
        }
824
        printf("</dd></dl>\n");
825
    }
826
/*  else
827
        printf(".LP\n");  */
828
    break;
829
      case MITDB:
830
    if (infop = getinfo(NULL)) {
831
        while (*infop == ' ' || *infop == '\t')
832
        infop++;
833
        printf("<p><dl><dt>Notes:</dt>\n<dd>%s\n", infop);
834
        while (infop = getinfo(NULL)) {
835
        while (*infop == ' ' || *infop == '\t')
836
            infop++;
837
        printf("%s\n", infop);
838
        }
839
        printf("</dd></dl>\n");
840
    }
841
/*  else
842
        printf(".LP\n");  */
843
    break;
844
      case VALEDB:
845
    if (infop = getinfo(NULL)) {
846
        while (*infop == ' ' || *infop == '\t')
847
        infop++;
848
        if (strncmp(infop, "Reference:", 10) == 0)
849
        infop = getinfo(NULL);
850
    }
851
    if (infop) {
852
        while (*infop == ' ' || *infop == '\t')
853
        infop++;
854
        printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
855
        while (infop = getinfo(NULL)) {
856
        while (*infop == ' ' || *infop == '\t')
857
            infop++;
858
        printf(".br\n%s\n", infop);
859
        }
860
    }
861
    else
862
        printf(".LP\n");
863
    break;
864
      default:
865
    printf(".LP\n");
866
    break;
867
    }
868
    /*    printf(".KE\n"); */
869
870
/* Close the database files and quit. */
871
    dbquit();
872
    exit(0);
873
}
874
875
setrtab(i, rst, rsl)
876
int i;
877
char *rst, *rsl;
878
{
879
    if (0 <= i && i < N_RTYPES) {
880
    rtab[i].episodes = 0L;
881
    rtab[i].duration = strtim("0.5");
882
    rtab[i].rstring = rst;
883
    rtab[i].rslong = rsl;
884
    rtab[i].min3r = 0xffff;
885
    rtab[i].max3r = 0;
886
    }
887
}
888
889
setstab(i, signal, sst)
890
int i, signal;
891
char *sst;
892
{
893
    if (0 <= i && i < N_STYPES && 0 <= signal && signal < MAXSIG) {
894
    sttab[i][signal].episodes = 0L;
895
    sttab[i][signal].duration = strtim("0.5");
896
    sttab[i][signal].ststring = sst;
897
    sttab[i][signal].extremum = 0;
898
    }
899
}
900
901
setntab(i, nst)
902
int i;
903
char *nst;
904
{
905
    if (0 <= i && i < N_NTYPES) {
906
    ntab[i].episodes = 0L;
907
    ntab[i].duration = strtim("0.5");
908
    ntab[i].nstring = nst;
909
    }
910
}
911
    
912
bstats(i, s)  /* print a line of the beat table if beats of type i were seen */
913
int i;
914
char *s;        /* English description of beat type i */
915
{
916
    if (btab[i].tcount > 0L || btab[i].lcount > 0L) {
917
    printf("<tr align=right><td>%s</td>", s);
918
    if (btab[i].lcount > 0L) {
919
        printf("<td>%ld</td>", btab[i].lcount);
920
        lsum += btab[i].lcount;
921
    }
922
    else
923
        printf("<td>-</td>");
924
    if (btab[i].tcount > 0L) {
925
        printf("<td>%ld</td>", btab[i].tcount);
926
        tsum += btab[i].tcount;
927
    }
928
    else
929
        printf("<td>-</td>");
930
    printf("<td>%ld</td></tr>\n", btab[i].lcount + btab[i].tcount);
931
    }
932
}
933
934
aupdate(i)  /* Record that an i-beat SV run was observed */
935
int i;
936
{
937
    int j;
938
939
    for (j = 0; j < N_RLEN-1; j++) {
940
    if (artab[j].len == i) break;
941
    else if (artab[j].len == 0) { artab[j].len = i; break; }
942
    }
943
    artab[j].nruns++;
944
}
945
946
vupdate(i)  /* Record that an i-beat V run was observed */
947
int i;
948
{
949
    int j;
950
951
    for (j = 0; j < N_RLEN-1; j++) {
952
    if (vrtab[j].len == i) break;
953
    else if (vrtab[j].len == 0) { vrtab[j].len = i; break; }
954
    }
955
    vrtab[j].nruns++;
956
}