add MIT-BIT

This commit is contained in:
weiyuqing committed 2023-03-13 19:11:51 +08:00
1 parent fcbea99d01
commit fdf7d4f840
708 files changed
+11062 -4

No files matched your search

@@ -0,0 +1,46 @@
.ce 999
.ps 16
\fBMIT-BIH ARRHYTHMIA DATABASE DIRECTORY
.ps 14
.TS
c s
l r.
Table of Contents
\fBForeword iii
Introduction v
Symbols viii
\&``Full-Disclosure'' ECG Plots 1
Examples 98
Notes 178
Tables 189
Index 194
.TE
@@ -0,0 +1,59 @@
.ce 999
.ps 12
\fBHARVARD UNIVERSITY \- MASSACHUSETTS INSTITUTE OF TECHNOLOGY
DIVISION OF HEALTH SCIENCES AND TECHNOLOGY
.ps 24
MIT-BIH ARRHYTHMIA DATABASE
DIRECTORY
.ps 18
JULY 1992
BMEC TR010 (Revised)
@@ -0,0 +1,956 @@
/* file: dbnotes.c G. Moody 25 August 1988
Last revised: 24 May 1997
Create database notes file (HTML format)
Copyright (C) Massachusetts Institute of Technology 1997. All rights reserved.
*/
#include <stdio.h>
#include <ecg/db.h>
#include <ecg/ecgmap.h>
/* Rhythm types. These may be rearranged arbitrarily; the order determines
the order in which rhythms are printed in the rhythm table. Add a call to
`setrtab' and adjust N_RTYPES whenever another rhythm is added. */
#define R_UNDEF 0
#define NSR 1
#define SBR 2
#define BII 3
#define B22 4
#define B3 5
#define SAB 6
#define PREX 7
#define AB 8
#define EAR 9
#define SVTA 10
#define AFL 11
#define AFIB 12
#define PR 13
#define NOD 14
#define VBG 15
#define VTG 16
#define IVR 17
#define VT 18
#define VFL 19
#define N_RTYPES 20 /* number of rhythm types, including R_UNDEF */
/* ST and T change types. */
#define STP 0 /* increase in ST level */
#define STN 1 /* decrease in ST level */
#define TP 2 /* increase in T amplitude */
#define TXP 3 /* extreme increase in T amplitude */
#define TN 4 /* decrease in T amplitude */
#define TXN 5 /* extreme decrease in T amplitude */
#define TBP 6 /* biphasic T-wave */
#define N_STYPES 7 /* number of ST and T change types */
#define PLUS 1 /* positive change */
#define MINUS 2 /* negative change */
#define XPLUS 3 /* extreme positive change */
#define XMINUS 4 /* extreme negative change */
#define BIPHASIC 5 /* change to biphasic form */
#define STCH0 1 /* beginning of ST change episode */
#define STCH1 2 /* extremum of ST change */
#define STCH2 3 /* end of ST change episode */
#define TCH0 4 /* beginning of T change episode */
#define TCH1 5 /* extremum of T change */
#define TCH2 6 /* end of T change episode */
/* Noise type bit masks. */
#define C0 0x00 /* signal 0 clean */
#define C1 0x00 /* signal 1 clean */
#define C2 0x00 /* signal 2 clean */
#define C3 0x00 /* signal 3 clean */
#define N0 0x01 /* signal 0 noisy */
#define N1 0x02 /* signal 1 noisy */
#define N2 0x04 /* signal 2 noisy */
#define N3 0x08 /* signal 3 noisy */
#define U0 0x11 /* signal 0 unreadable */
#define U1 0x22 /* signal 1 unreadable */
#define U2 0x44 /* signal 2 unreadable */
#define U3 0x88 /* signal 3 unreadable */
#define UNREADABLE 0xff /* all signals unreadable */
#define N_NTYPES 256 /* number of noise types */
#define N_RLEN 20 /* number of distinct run lengths */
/* Values for info_type. */
#define NO_INFO 0
#define MITDB 1
#define EDB 2
#define VALEDB 3
struct btabstruct {
long lcount; /* number of beats of this type in learning period */
long tcount; /* number of beats of this type in test period */
} btab[ACMAX+1];
struct rltabstruct {
long len; /* number of beats in run */
long nruns; /* number of runs of `len' beats */
} artab[N_RLEN], vrtab[N_RLEN];
struct rtabstruct {
long episodes; /* number of episodes of this rhythm */
long duration; /* duration of this rhythm (in sample intervals) */
char *rstring; /* rhythm string as it appears in annotation file */
char *rslong; /* rhythm name as it is printed by this program */
int min3r, max3r; /* minimum/maximum length of 3 R-R intervals */
} rtab[N_RTYPES];
struct sttabstruct {
long episodes; /* number of episodes of ST or T change of this type */
long duration; /* duration of episodes (in sample intervals) */
char *ststring; /* description of change as printed by this program */
int extremum; /* extreme value of change */
} sttab[N_STYPES][MAXSIG];
struct ntabstruct {
long episodes; /* number of episodes of this noise type */
long duration; /* duration of this noise type (in sample intervals) */
char *nstring; /* noise type as it is printed by this program */
} ntab[N_NTYPES];
long lsum, tsum; /* numbers of beats of all types in the learning and
test periods (tallied by `bstats') */
long ttest; /* time of the end of the test period */
main(argc, argv)
int argc;
char *argv[];
{
double t3min;
int a=0, age, c, i, info_type, magnitude, maxrate, minrate, noise = 0,
nsig, rhythm = R_UNDEF, rlen = -1, rr3, sign, st_calibrated = 0,
st_data = 0, t_data = 0, type, v=0;
static long n0, r0, tq0, tq1, tq2, tq3, s0[N_STYPES][MAXSIG];
struct siginfo si[MAXSIG];
struct anninfo ai;
struct ann annot;
static char buf[256], *infop, *p, *record, sex[10], umask;
/* Check command-line arguments. */
if (argc < 3) {
fprintf(stderr, "usage: %s annotator record\n", argv[0]);
exit(1);
}
/* Open the database files. */
ai.name = argv[1]; ai.stat = READ; record = argv[2];
if (annopen(record, &ai, 1) < 0 ||
(nsig = isigopen(record, si, -MAXSIG)) < 0)
exit(2);
/* Initialize variables. */
t3min = strtim("3:0");
ttest = strtim("5:0");
setrtab(R_UNDEF, NULL, "Unspecified rhythm");
setrtab(NSR, "N", "Normal sinus rhythm");
setrtab(SBR, "SBR", "Sinus bradycardia");
setrtab(BII, "BII", "2\\(de heart block");
setrtab(B22, "B22", "2\\(de heart block (Mobitz type II)");
setrtab(B3, "B3", "3\\(de heart block");
setrtab(SAB, "SAB", "Sino-atrial block");
setrtab(PREX, "PREX", "Pre-excitation (WPW)");
setrtab(AB, "AB", "Atrial bigeminy");
setrtab(EAR, "EAR", "Ectopic atrial rhythm");
setrtab(SVTA, "SVTA", "SVTA");
setrtab(AFL, "AFL", "Atrial flutter");
setrtab(AFIB, "AFIB", "Atrial fibrillation");
setrtab(PR, "P", "Paced rhythm");
setrtab(NOD, "NOD", "Nodal (junctional) rhythm");
setrtab(VBG, "B", "Ventricular bigeminy");
setrtab(VTG, "T", "Ventricular trigeminy");
setrtab(IVR, "IVR", "Idioventricular rhythm");
setrtab(VT, "VT", "Ventricular tachycardia");
setrtab(VFL, "VFL", "Ventricular flutter");
for (c = 0; c < nsig; c++) {
setstab(STP, c, "Positive ST dev.");
setstab(STN, c, "Negative ST dev.");
setstab(TP, c, "Positive T dev.");
setstab(TXP, c, "T dev. \\(>= 400 \\(*mV");
setstab(TN, c, "Negative T dev.");
setstab(TXN, c, "T dev. \\(<= \\(mi400 \\(*mV");
setstab(TBP, c, "Biphasic T");
}
switch (nsig) {
case 1:
setntab(C0, "Clean");
setntab(N0, "Noisy");
setntab(U0, "Unreadable");
setntab(UNREADABLE, "Unreadable");
umask = U0;
break; /* so far, so good ... */
case 2:
setntab(C0|C1, "Both clean");
setntab(C0|N1, "Lower noisy");
setntab(C0|U1, "Lower unreadable");
setntab(N0|C1, "Upper noisy");
setntab(N0|N1, "Both noisy");
setntab(N0|U1, "Upper noisy, lower unreadable");
setntab(U0|C1, "Upper unreadable");
setntab(U0|N1, "Upper unreadable, lower noisy");
setntab(U0|U1, "Both unreadable");
setntab(UNREADABLE, "Unreadable");
umask = U0|U1;
break; /* yikes! */
case 3:
setntab(C0|C1|C2, "All clean");
setntab(C0|C1|N2, "Lower noisy");
setntab(C0|C1|U2, "Lower unreadable");
setntab(C0|N1|C2, "Middle noisy");
setntab(C0|N1|N2, "Middle and lower noisy");
setntab(C0|N1|U2, "Middle noisy, lower unreadable");
setntab(C0|U1|C2, "Middle unreadable");
setntab(C0|U1|N2, "Middle unreadable, lower noisy");
setntab(C0|U1|U2, "Middle and lower unreadable");
setntab(N0|C1|C2, "Upper noisy");
setntab(N0|C1|N2, "Upper and lower noisy");
setntab(N0|C1|U2, "Upper noisy, lower unreadable");
setntab(N0|N1|C2, "Upper and middle noisy");
setntab(N0|N1|N2, "All noisy");
setntab(N0|N1|U2, "Lower unreadable, others noisy");
setntab(N0|U1|C2, "Upper noisy, middle unreadable");
setntab(N0|U1|N2, "Middle unreadable, others noisy");
setntab(N0|U1|U2, "Upper noisy, others unreadable");
setntab(U0|C1|C2, "Upper unreadable");
setntab(U0|C1|N2, "Upper unreadable, lower noisy");
setntab(U0|C1|U2, "Upper and lower unreadable");
setntab(U0|N1|C2, "Upper unreadable, middle noisy");
setntab(U0|N1|N2, "Upper unreadable, others noisy");
setntab(U0|N1|U2, "Middle noisy, others unreadable");
setntab(U0|U1|C2, "Upper and middle unreadable");
setntab(U0|U1|N2, "Lower noisy, others unreadable");
setntab(U0|U1|U2, "All unreadable");
setntab(UNREADABLE, "Unreadable");
umask = U0|U1|U2;
break; /* arggggh! */
/* case 4: nonononononononononono!!!!!!! */
default:
umask = UNREADABLE;
break;
}
/* Read the annotation file. */
while (getann(0, &annot) >= 0) {
/* Count the annotation in the test or learning period as appropriate. */
if (annot.time >= ttest) btab[annot.anntyp].tcount++;
else btab[annot.anntyp].lcount++;
if (isqrs(annot.anntyp)) {
/* Update the ectopic activity tables if appropriate. */
switch (map2(annot.anntyp)) {
case NORMAL: if (a > 0) { aupdate(a); a = 0; }
if (v > 0) { vupdate(v); v = 0; }
break;
case SVPB: if (v > 0) { vupdate(v); v = 0; }
a++;
break;
case PVC:
case FUSION: if (a > 0) { aupdate(a); a = 0; }
v++;
break;
}
/* Keep track of the times of the last 4 beats. */
tq0 = tq1; tq1 = tq2; tq2 = tq3; tq3 = annot.time;
/* Update the count of consecutive beats in the current rhythm. */
rlen++;
/* Check various conditions which might make a heart rate
measurement invalid. */
switch (rhythm) {
case NSR:
case SBR:
case BII:
case B22:
case B3:
case SAB:
case PREX:
if (map2(annot.anntyp) != NORMAL) rlen = -1; break;
case AB:
case NOD:
if (map1(annot.anntyp) != NORMAL) rlen = -1; break;
case EAR:
case SVTA:
if (map2(annot.anntyp) != SVPB) rlen = -1; break;
case PR:
if (annot.anntyp != PACE) rlen = -1; break;
case IVR:
case VT:
if (map1(annot.anntyp) == NORMAL) rlen = -1; break;
case VFL:
if (annot.anntyp != FLWAV) rlen = -1; break;
default:
break;
}
/* If a valid heart rate can be measured, update the records of
maximum and minimum rate for the current rhythm. Note that
these can be determined from the minimum and maximum R-R
intervals; what is actually used here is the sum of three
consecutive intervals. */
if (rlen >= 3) {
rr3 = tq3 - tq0;
if (rr3 > rtab[rhythm].max3r) rtab[rhythm].max3r = rr3;
if (rr3 < rtab[rhythm].min3r) rtab[rhythm].min3r = rr3;
}
}
else if (annot.anntyp == RHYTHM) {
/* Don't count the interval from the beginning of the record to
the first rhythm label as `unspecified rhythm' unless the first
beat label occurs before the first rhythm label. */
if (tq3 > 0L) {
rtab[rhythm].episodes++;
rtab[rhythm].duration += annot.time - r0;
r0 = annot.time;
}
/* Identify the new rhythm. */
for (i = 1, rhythm = R_UNDEF; i < N_RTYPES; i++)
if (strcmp(annot.aux+2, rtab[i].rstring) == 0) {
rhythm = i;
break;
}
if (rhythm == SVTA || rhythm == VT) rlen = 0;
else rlen = -1;
}
else if (annot.anntyp == STCH || annot.anntyp == TCH) {
/* Identify the nature of the ST or T change from the aux field.
The syntax of the aux field is one of:
"(" <type> [<signal designation>] <sign>
"A" <type> [<signal designation>] <sign> [<magnitude>]
<type> [<signal designation>] <sign> ")"
where <type> is "ST" or "T", <signal designation> is 1 or 2
decimal digits, <sign> is "+", "++", "-", "--", or "BP", and
<magnitude> is 2-4 decimal digits. ("++" and "--" appear only
in T-wave change annotations; they designate extreme (>= 0.4 mV
changes in T-wave amplitude.) The signal designation is
the signal number (e.g., signal 0 is designated by "0",
etc.); in records which contain only one signal (such as those
in the VALE DB), the signal designation is omitted. The
magnitude is given in microvolts, and may also be omitted (as
in the VALE DB).
*/
if (annot.aux == NULL)
fprintf(stderr,
"warning: %s annotation without text field at %s\n",
ecgstr(annot.anntyp), timstr(annot.time));
else {
p = annot.aux+1;
switch (*p) {
case '(':
if (annot.anntyp == STCH) {
type = STCH0;
p += 3; /* skip `(ST' */
}
else {
type = TCH0;
p += 2; /* skip `(T' */
}
break;
case 'A':
if (annot.anntyp == STCH) {
type = STCH1;
p += 3; /* skip `(ST' */
}
else {
type = TCH1;
p += 2; /* skip `(T' */
}
break;
case 'S':
type = STCH2;
p += 2; /* skip `ST' */
break;
case 'T':
type = TCH2;
p++; /* skip `T' */
break;
default:
fprintf(stderr,
"warning: %s annotation with text field %s at %s\n",
ecgstr(annot.anntyp), annot.aux, timstr(annot.time));
type = 0;
break;
}
if (nsig > 1) {
if ((c = atoi(p)) > 9) {
p++;
}
p++;
}
else c = 0;
switch (*p++) {
case '+':
if (*p == '+') {
sign = XPLUS;
p++;
}
else
sign = PLUS;
break;
case '-':
if (*p == '-') {
sign = XMINUS;
p++;
}
else
sign = MINUS;
break;
case 'B':
if (*p == 'P') {
sign = BIPHASIC;
p++;
}
break;
default:
fprintf(stderr,
"warning: %s annotation with text field %s at %s\n",
ecgstr(annot.anntyp), annot.aux, timstr(annot.time));
sign = 0;
break;
}
if (*p && *p != ')') {
if ((magnitude = atoi(p)) > 0)
st_calibrated = 1;
}
else magnitude = 0;
if (0 <= c && c < MAXSIG) switch (type) {
case STCH0: /* beginning of ST change episode */
if (sign == PLUS) {
st_data = 1;
s0[STP][c] = annot.time;
}
else if (sign == MINUS) {
st_data = 1;
s0[STN][c] = annot.time;
}
break;
case STCH1: /* extremum of ST change */
if (sign == PLUS && magnitude > sttab[STP][c].extremum)
sttab[STP][c].extremum = magnitude;
else if (sign == MINUS &&
magnitude > sttab[STN][c].extremum)
sttab[STN][c].extremum = magnitude;
break;
case STCH2: /* end of ST change episode */
if (sign == PLUS) {
sttab[STP][c].episodes++;
sttab[STP][c].duration += annot.time - s0[STP][c];
s0[STP][c] = 0L;
}
else if (sign == MINUS) {
sttab[STN][c].episodes++;
sttab[STN][c].duration += annot.time - s0[STN][c];
s0[STN][c] = 0L;
}
break;
case TCH0: /* beginning of T change episode */
if (sign == PLUS) {
t_data = 1;
s0[TP][c] = annot.time;
}
else if (sign == MINUS) {
t_data = 1;
s0[TN][c] = annot.time;
}
else if (sign == XPLUS) {
t_data = 1;
s0[TXP][c] = annot.time;
}
else if (sign == XMINUS) {
t_data = 1;
s0[TXN][c] = annot.time;
}
else if (sign == BIPHASIC) {
t_data = 1;
s0[TBP][c] = annot.time;
}
break;
case TCH1: /* extremum of T change */
if ((sign == PLUS || sign == XPLUS) &&
magnitude > sttab[TP][c].extremum)
sttab[TP][c].extremum = magnitude;
else if ((sign == MINUS || sign == XMINUS) &&
magnitude > sttab[TN][c].extremum)
sttab[TN][c].extremum = magnitude;
break;
case TCH2: /* end of T change episode */
if (sign == PLUS) {
sttab[TP][c].episodes++;
sttab[TP][c].duration += annot.time - s0[TP][c];
s0[TP][c] = 0L;
}
else if (sign == MINUS) {
sttab[TN][c].episodes++;
sttab[TN][c].duration += annot.time - s0[TN][c];
s0[TN][c] = 0L;
}
else if (sign == XPLUS) {
sttab[TXP][c].episodes++;
sttab[TXP][c].duration += annot.time - s0[TXP][c];
s0[TXP][c] = 0L;
}
else if (sign == XMINUS) {
sttab[TXN][c].episodes++;
sttab[TXN][c].duration += annot.time - s0[TXN][c];
s0[TXN][c] = 0L;
}
else if (sign == BIPHASIC) {
sttab[TBP][c].episodes++;
sttab[TBP][c].duration += annot.time - s0[TBP][c];
s0[TBP][c] = 0L;
}
break;
default:
break;
}
}
}
else if (annot.anntyp == NOISE) {
/* Don't count the interval from the beginning of the record to
the first signal quality label as `clean' unless the first beat
label occurs before the first signal quality label. */
if (tq3 > 0L) {
ntab[noise].episodes++;
ntab[noise].duration += annot.time - n0;
n0 = annot.time;
}
noise = annot.subtyp & 0xff;
/* Unreadable segments invalidate rate measurements. */
if ((noise & umask) == umask) rlen = -1;
}
}
/* At the end of the annotation file, count any run in progress and adjust
the counters for the current rhythm and noise type, and ST and T states.
*/
if (a) aupdate(a);
if (v) aupdate(v);
if (strtim("e") > annot.time) annot.time = strtim("e");
rtab[rhythm].episodes++;
rtab[rhythm].duration += annot.time - r0;
for (c = 0; c < nsig; c++) {
for (i = 0; i < N_STYPES; i++)
if (s0[i][c] > 0L) {
sttab[i][c].episodes++;
sttab[i][c].duration += annot.time - s0[i][c];
}
if (sttab[TXP][c].episodes > 0)
sttab[TXP][c].extremum = sttab[TP][c].extremum;
if (sttab[TXN][c].episodes > 0)
sttab[TXN][c].extremum = sttab[TN][c].extremum;
}
ntab[noise].episodes++;
ntab[noise].duration += annot.time - n0;
/* Read the first line of info, if any. */
infop = getinfo(record);
if (infop == NULL)
info_type = NO_INFO;
else {
while (*infop == ' ' || *infop == '\t')
infop++;
if (*infop == 'A') { /* `Age: ...' as in European ST-T DB */
sscanf(infop, "Age: %d Sex: %s", &age, sex);
info_type = EDB;
}
else if (*infop == 'R') { /* `Reference: ...' as in VALE DB */
age = -1; sex[0] = '?';
info_type = VALEDB;
}
else { /* fixed format as in MIT DB */
sscanf(infop, "%d%s", &age, sex);
info_type = MITDB;
}
}
printf("<h2>Record %s (", record);
for (i = 0; i < nsig; i++) {
if (i > 0) fputs(", ", stdout);
fputs(si[i].desc, stdout);
}
fputs("; ", stdout);
switch (sex[0]) {
case 'f':
case 'F': fputs("female, age ", stdout); break;
case 'm':
case 'M': fputs("male, age ", stdout); break;
default: fputs("age ", stdout); break;
}
if (age > 0) printf("%d)</h2>\n", age);
else fputs("not recorded)</h2>\n", stdout);
printf("<p>\n");
switch (info_type) {
case MITDB:
/* Print the medications section (the second line of info). */
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf("<i>Medications:</i> %s<br>\n", infop);
}
break;
default:
break;
}
/* Print the beat table. */
printf("<p><table>\n");
/* printf(".TS\nexpand;\nlw(1i) c c c\nl r r r.\n"); */
printf("<tr><th>Beats</th><th>Before 5:00</th><th>After 5:00</th>"
"<th>Total</th></tr>\n");
/* printf("\\fIBeats\tBefore 5:00\tAfter 5:00\tTotal\\fR\n"); */
bstats(NORMAL, "Normal");
bstats(LBBB, "Left BBB");
bstats(RBBB, "Right BBB");
bstats(APC, "APC");
bstats(ABERR, "Aberrated APC");
bstats(NPC, "Junctional premature");
bstats(SVPB, "SVPC");
bstats(PVC, "PVC");
bstats(RONT, "R-on-T PVC");
bstats(FUSION, "Fusion PVC");
bstats(FLWAV, "Ventricular flutter wave");
bstats(AESC, "Atrial escape");
bstats(NESC, "Junctional escape");
bstats(SVESC, "Supraventricular escape");
bstats(VESC, "Ventricular escape");
bstats(PACE, "Paced");
bstats(PFUS, "Pacemaker fusion");
bstats(NAPC, "Blocked APC");
bstats(UNKNOWN, "Unclassifiable");
printf("<tr align=right><td>Total</td><td>%ld</td><td>%ld</td>"
"<td>%ld</td></tr>\n", lsum, tsum, lsum+tsum);
/* printf("\\fITotal\t%ld\t%ld\t%ld\\fR\n", lsum, tsum, lsum+tsum); */
printf("</table>\n\n");
/* printf(".TE\n.KE\n"); */
/* Print the supraventricular ectopy table, if necessary. */
if (artab[0].len > 1 || artab[0].nruns > 1 || artab[1].len > 0) {
int i, j, k, l;
/* printf(".KS\n.TS\nlw(1.125i) l.\n"); */
printf("<p><i>Supraventricular ectopy</i><br>\n<ul>");
/* printf("\\fISupraventricular ectopy:\\fR"); */
for (i = 0; i < N_RLEN; i++) {
for (j = 0, l = 9999; j < N_RLEN; j++)
if (0 < artab[j].len && artab[j].len < l) l = artab[k = j].len;
if (l == 9999) break;
switch (l) {
case 1:
printf("<li> %d isolated beat%s\n",
artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
break;
case 2:
printf("<li> %d couplet%s\n",
artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
break;
default:
printf("<li> %d run%s of %d beats\n",
artab[k].nruns, (artab[k].nruns > 1) ? "s" : "",
artab[k].len);
break;
}
artab[k].len = 0;
}
printf("</ul>\n");
/* printf(".TE\n.KE\n"); */
}
/* Print the ventricular ectopy table, if necessary. */
if (vrtab[0].len > 1 || vrtab[0].nruns > 1 || vrtab[1].len > 0) {
int i, j, k, l;
/* printf(".KS\n.TS\nlw(1.125i) l.\n"); */
printf("<p><i>Ventricular ectopy</i><br>\n<ul>");
/* printf("\\fIVentricular ectopy:\\fR"); */
for (i = 0; i < N_RLEN; i++) {
for (j = 0, l = 9999; j < N_RLEN; j++)
if (0 < vrtab[j].len && vrtab[j].len < l) l = vrtab[k = j].len;
if (l == 9999) break;
switch (l) {
case 1:
printf("<li> %d isolated beat%s\n",
vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
break;
case 2:
printf("<li> %d couplet%s\n",
vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
break;
default:
printf("<li> %d run%s of %d beats\n",
vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "",
vrtab[k].len);
break;
}
vrtab[k].len = 0;
}
printf("</ul>\n");
/* printf(".TE\n.KE\n"); */
}
/* Print the rhythm table. */
printf("<p><table>\n");
/* printf(".KS\n.TS\nexpand;\nlw c rw(0.4i) rw(0.4i)\nl c n r.\n"); */
printf("<tr><th>Rhythm</th><th>Rate</th><th>Episodes</th>"
"<th>Duration</th></tr>\n");
/* printf("\\fIRhythm\tRate\tEpisodes\tDuration\\fR\n"); */
for (i = 0; i < N_RTYPES; i++)
if (rtab[i].episodes != 0L) {
printf("<tr><td>%s</td><td align=center>", rtab[i].rslong);
if (rtab[i].max3r > 0)
printf("%d", minrate = (int)(t3min/rtab[i].max3r + 0.5));
else
minrate = 0;
if (rtab[i].min3r != 0xffff) {
maxrate = (int)(t3min/rtab[i].min3r + 0.5);
if (maxrate > minrate) printf("-%d", maxrate);
}
else if (minrate == 0)
printf("-");
printf("</td><td align=right>%ld</td><td align=right>%s</td>"
"</tr>\n", rtab[i].episodes, timstr(rtab[i].duration));
}
printf("</table>\n\n");
/* printf(".TE\n.KE\n"); */
/* Print the ST and T-change table, if available. */
if (st_data || t_data) {
printf(".KS\n.TS\nexpand;\nl");
if (st_calibrated) printf(" r");
printf(" rw(0.4i) rw(0.4i)\nl");
if (st_calibrated) printf(" n");
printf(" n r.\n\\fIST\\-T state");
if (st_calibrated) printf("\tExtremum");
printf("\tEpisodes\tDuration\\fR\n");
for (c = 0; c < nsig; c++) {
int header_printed = 0;
for (i = 0; i < N_STYPES; i++) {
if (sttab[i][c].episodes != 0L) {
if (header_printed == 0) {
switch (nsig) {
case 1:
break;
case 2:
if (c == 0) printf("\\fI(upper signal)\\fR\n");
else printf("\\fI(lower signal)\\fR\n");
break;
case 3:
if (c == 0) printf("\\fI(upper signal)\\fR\n");
else if (c == 1)
printf("\\fI(middle signal)\\fR\n");
else printf("\\fI(lower signal)\\fR\n");
break;
default:
printf("(signal %d)\n", c);
break;
}
header_printed = 1;
}
printf("%s\t", sttab[i][c].ststring);
if (st_calibrated) {
if (sttab[i][c].extremum > 0) {
switch (i) {
case STP:
case TP:
case TXP: printf("\\(pl"); break;
case STN:
case TN:
case TXN: printf("\\(mi"); break;
}
printf("%d\t", sttab[i][c].extremum);
}
else
printf("\\-\t");
}
printf("%ld\t%s\n", sttab[i][c].episodes,
timstr(sttab[i][c].duration));
}
}
}
printf(".TE\n.KE\n");
}
/* Print the signal quality table. */
printf("<p><table>\n");
/* printf(".KS\n.TS\nexpand;\nl rw(0.4i) rw(0.4i)\nl n r.\n"); */
printf("<tr><th>Signal quality</th><th>Episodes</th>"
"<th>Duration</th></tr>\n");
/* printf("\\fISignal quality\tEpisodes\tDuration\\fR\n"); */
for (i = 0; i < N_NTYPES; i++)
if (ntab[i].episodes != 0L) {
if (ntab[i].nstring == NULL) {
static char ns[40];
sprintf(ns, "Signal quality code %d", i);
ntab[i].nstring = ns;
}
printf("<tr><td>%s</td><td align=right>%ld</td>"
"<td align=right>%s</td></tr>\n",
ntab[i].nstring, ntab[i].episodes,
timstr(ntab[i].duration));
}
printf ("</table>\n\n");
/* printf(".TE\n"); */ /* (the "keep" ends after the "notes" section) */
/* Print a "notes" section if there is any additional info available. */
switch (info_type) {
case EDB:
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
if ('a' <= *infop && *infop <= 'z') *infop += 'A' - 'a';
printf("<p><dl><dt>Notes:</dt>\n<dd>%s", infop);
while (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf("<br>\n%s", infop);
}
printf("</dd></dl>\n");
}
/* else
printf(".LP\n"); */
break;
case MITDB:
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf("<p><dl><dt>Notes:</dt>\n<dd>%s\n", infop);
while (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf("%s\n", infop);
}
printf("</dd></dl>\n");
}
/* else
printf(".LP\n"); */
break;
case VALEDB:
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
if (strncmp(infop, "Reference:", 10) == 0)
infop = getinfo(NULL);
}
if (infop) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
while (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".br\n%s\n", infop);
}
}
else
printf(".LP\n");
break;
default:
printf(".LP\n");
break;
}
/* printf(".KE\n"); */
/* Close the database files and quit. */
dbquit();
exit(0);
}
setrtab(i, rst, rsl)
int i;
char *rst, *rsl;
{
if (0 <= i && i < N_RTYPES) {
rtab[i].episodes = 0L;
rtab[i].duration = strtim("0.5");
rtab[i].rstring = rst;
rtab[i].rslong = rsl;
rtab[i].min3r = 0xffff;
rtab[i].max3r = 0;
}
}
setstab(i, signal, sst)
int i, signal;
char *sst;
{
if (0 <= i && i < N_STYPES && 0 <= signal && signal < MAXSIG) {
sttab[i][signal].episodes = 0L;
sttab[i][signal].duration = strtim("0.5");
sttab[i][signal].ststring = sst;
sttab[i][signal].extremum = 0;
}
}
setntab(i, nst)
int i;
char *nst;
{
if (0 <= i && i < N_NTYPES) {
ntab[i].episodes = 0L;
ntab[i].duration = strtim("0.5");
ntab[i].nstring = nst;
}
}
bstats(i, s) /* print a line of the beat table if beats of type i were seen */
int i;
char *s; /* English description of beat type i */
{
if (btab[i].tcount > 0L || btab[i].lcount > 0L) {
printf("<tr align=right><td>%s</td>", s);
if (btab[i].lcount > 0L) {
printf("<td>%ld</td>", btab[i].lcount);
lsum += btab[i].lcount;
}
else
printf("<td>-</td>");
if (btab[i].tcount > 0L) {
printf("<td>%ld</td>", btab[i].tcount);
tsum += btab[i].tcount;
}
else
printf("<td>-</td>");
printf("<td>%ld</td></tr>\n", btab[i].lcount + btab[i].tcount);
}
}
aupdate(i) /* Record that an i-beat SV run was observed */
int i;
{
int j;
for (j = 0; j < N_RLEN-1; j++) {
if (artab[j].len == i) break;
else if (artab[j].len == 0) { artab[j].len = i; break; }
}
artab[j].nruns++;
}
vupdate(i) /* Record that an i-beat V run was observed */
int i;
{
int j;
for (j = 0; j < N_RLEN-1; j++) {
if (vrtab[j].len == i) break;
else if (vrtab[j].len == 0) { vrtab[j].len = i; break; }
}
vrtab[j].nruns++;
}
@@ -0,0 +1,936 @@
/* file: dbnotes.c G. Moody 25 August 1988
Last revised: 6 December 1990
Create database notes file (tbl/troff input format)
Copyright (C) Massachusetts Institute of Technology 1990. All rights reserved.
*/
#include <stdio.h>
#include <ecg/db.h>
#include <ecg/ecgmap.h>
/* Rhythm types. These may be rearranged arbitrarily; the order determines
the order in which rhythms are printed in the rhythm table. Add a call to
`setrtab' and adjust N_RTYPES whenever another rhythm is added. */
#define R_UNDEF 0
#define NSR 1
#define SBR 2
#define BII 3
#define B22 4
#define B3 5
#define SAB 6
#define PREX 7
#define AB 8
#define EAR 9
#define SVTA 10
#define AFL 11
#define AFIB 12
#define PR 13
#define NOD 14
#define VBG 15
#define VTG 16
#define IVR 17
#define VT 18
#define VFL 19
#define N_RTYPES 20 /* number of rhythm types, including R_UNDEF */
/* ST and T change types. */
#define STP 0 /* increase in ST level */
#define STN 1 /* decrease in ST level */
#define TP 2 /* increase in T amplitude */
#define TXP 3 /* extreme increase in T amplitude */
#define TN 4 /* decrease in T amplitude */
#define TXN 5 /* extreme decrease in T amplitude */
#define TBP 6 /* biphasic T-wave */
#define N_STYPES 7 /* number of ST and T change types */
#define PLUS 1 /* positive change */
#define MINUS 2 /* negative change */
#define XPLUS 3 /* extreme positive change */
#define XMINUS 4 /* extreme negative change */
#define BIPHASIC 5 /* change to biphasic form */
#define STCH0 1 /* beginning of ST change episode */
#define STCH1 2 /* extremum of ST change */
#define STCH2 3 /* end of ST change episode */
#define TCH0 4 /* beginning of T change episode */
#define TCH1 5 /* extremum of T change */
#define TCH2 6 /* end of T change episode */
/* Noise type bit masks. */
#define C0 0x00 /* signal 0 clean */
#define C1 0x00 /* signal 1 clean */
#define C2 0x00 /* signal 2 clean */
#define C3 0x00 /* signal 3 clean */
#define N0 0x01 /* signal 0 noisy */
#define N1 0x02 /* signal 1 noisy */
#define N2 0x04 /* signal 2 noisy */
#define N3 0x08 /* signal 3 noisy */
#define U0 0x11 /* signal 0 unreadable */
#define U1 0x22 /* signal 1 unreadable */
#define U2 0x44 /* signal 2 unreadable */
#define U3 0x88 /* signal 3 unreadable */
#define UNREADABLE 0xff /* all signals unreadable */
#define N_NTYPES 256 /* number of noise types */
#define N_RLEN 20 /* number of distinct run lengths */
/* Values for info_type. */
#define NO_INFO 0
#define MITDB 1
#define EDB 2
#define VALEDB 3
struct btabstruct {
long lcount; /* number of beats of this type in learning period */
long tcount; /* number of beats of this type in test period */
} btab[ACMAX+1];
struct rltabstruct {
long len; /* number of beats in run */
long nruns; /* number of runs of `len' beats */
} artab[N_RLEN], vrtab[N_RLEN];
struct rtabstruct {
long episodes; /* number of episodes of this rhythm */
long duration; /* duration of this rhythm (in sample intervals) */
char *rstring; /* rhythm string as it appears in annotation file */
char *rslong; /* rhythm name as it is printed by this program */
int min3r, max3r; /* minimum/maximum length of 3 R-R intervals */
} rtab[N_RTYPES];
struct sttabstruct {
long episodes; /* number of episodes of ST or T change of this type */
long duration; /* duration of episodes (in sample intervals) */
char *ststring; /* description of change as printed by this program */
int extremum; /* extreme value of change */
} sttab[N_STYPES][MAXSIG];
struct ntabstruct {
long episodes; /* number of episodes of this noise type */
long duration; /* duration of this noise type (in sample intervals) */
char *nstring; /* noise type as it is printed by this program */
} ntab[N_NTYPES];
long lsum, tsum; /* numbers of beats of all types in the learning and
test periods (tallied by `bstats') */
long ttest; /* time of the end of the test period */
main(argc, argv)
int argc;
char *argv[];
{
double t3min;
int a=0, age, c, i, info_type, magnitude, maxrate, minrate, noise = 0,
nsig, rhythm = R_UNDEF, rlen = -1, rr3, sign, st_calibrated = 0,
st_data = 0, t_data = 0, type, v=0;
static long n0, r0, tq0, tq1, tq2, tq3, s0[N_STYPES][MAXSIG];
struct siginfo si[MAXSIG];
struct anninfo ai;
struct ann annot;
static char buf[256], *infop, *p, *record, sex[10], umask;
/* Check command-line arguments. */
if (argc < 3) {
fprintf(stderr, "usage: %s annotator record\n", argv[0]);
exit(1);
}
/* Open the database files. */
ai.name = argv[1]; ai.stat = READ; record = argv[2];
if (annopen(record, &ai, 1) < 0 ||
(nsig = isigopen(record, si, -MAXSIG)) < 0)
exit(2);
/* Initialize variables. */
t3min = strtim("3:0");
ttest = strtim("5:0");
setrtab(R_UNDEF, NULL, "Unspecified rhythm");
setrtab(NSR, "N", "Normal sinus rhythm");
setrtab(SBR, "SBR", "Sinus bradycardia");
setrtab(BII, "BII", "2\\(de heart block");
setrtab(B22, "B22", "2\\(de heart block (Mobitz type II)");
setrtab(B3, "B3", "3\\(de heart block");
setrtab(SAB, "SAB", "Sino-atrial block");
setrtab(PREX, "PREX", "Pre-excitation (WPW)");
setrtab(AB, "AB", "Atrial bigeminy");
setrtab(EAR, "EAR", "Ectopic atrial rhythm");
setrtab(SVTA, "SVTA", "SVTA");
setrtab(AFL, "AFL", "Atrial flutter");
setrtab(AFIB, "AFIB", "Atrial fibrillation");
setrtab(PR, "P", "Paced rhythm");
setrtab(NOD, "NOD", "Nodal (junctional) rhythm");
setrtab(VBG, "B", "Ventricular bigeminy");
setrtab(VTG, "T", "Ventricular trigeminy");
setrtab(IVR, "IVR", "Idioventricular rhythm");
setrtab(VT, "VT", "Ventricular tachycardia");
setrtab(VFL, "VFL", "Ventricular flutter");
for (c = 0; c < nsig; c++) {
setstab(STP, c, "Positive ST dev.");
setstab(STN, c, "Negative ST dev.");
setstab(TP, c, "Positive T dev.");
setstab(TXP, c, "T dev. \\(>= 400 \\(*mV");
setstab(TN, c, "Negative T dev.");
setstab(TXN, c, "T dev. \\(<= \\(mi400 \\(*mV");
setstab(TBP, c, "Biphasic T");
}
switch (nsig) {
case 1:
setntab(C0, "Clean");
setntab(N0, "Noisy");
setntab(U0, "Unreadable");
setntab(UNREADABLE, "Unreadable");
umask = U0;
break; /* so far, so good ... */
case 2:
setntab(C0|C1, "Both clean");
setntab(C0|N1, "Lower noisy");
setntab(C0|U1, "Lower unreadable");
setntab(N0|C1, "Upper noisy");
setntab(N0|N1, "Both noisy");
setntab(N0|U1, "Upper noisy, lower unreadable");
setntab(U0|C1, "Upper unreadable");
setntab(U0|N1, "Upper unreadable, lower noisy");
setntab(U0|U1, "Both unreadable");
setntab(UNREADABLE, "Unreadable");
umask = U0|U1;
break; /* yikes! */
case 3:
setntab(C0|C1|C2, "All clean");
setntab(C0|C1|N2, "Lower noisy");
setntab(C0|C1|U2, "Lower unreadable");
setntab(C0|N1|C2, "Middle noisy");
setntab(C0|N1|N2, "Middle and lower noisy");
setntab(C0|N1|U2, "Middle noisy, lower unreadable");
setntab(C0|U1|C2, "Middle unreadable");
setntab(C0|U1|N2, "Middle unreadable, lower noisy");
setntab(C0|U1|U2, "Middle and lower unreadable");
setntab(N0|C1|C2, "Upper noisy");
setntab(N0|C1|N2, "Upper and lower noisy");
setntab(N0|C1|U2, "Upper noisy, lower unreadable");
setntab(N0|N1|C2, "Upper and middle noisy");
setntab(N0|N1|N2, "All noisy");
setntab(N0|N1|U2, "Lower unreadable, others noisy");
setntab(N0|U1|C2, "Upper noisy, middle unreadable");
setntab(N0|U1|N2, "Middle unreadable, others noisy");
setntab(N0|U1|U2, "Upper noisy, others unreadable");
setntab(U0|C1|C2, "Upper unreadable");
setntab(U0|C1|N2, "Upper unreadable, lower noisy");
setntab(U0|C1|U2, "Upper and lower unreadable");
setntab(U0|N1|C2, "Upper unreadable, middle noisy");
setntab(U0|N1|N2, "Upper unreadable, others noisy");
setntab(U0|N1|U2, "Middle noisy, others unreadable");
setntab(U0|U1|C2, "Upper and middle unreadable");
setntab(U0|U1|N2, "Lower noisy, others unreadable");
setntab(U0|U1|U2, "All unreadable");
setntab(UNREADABLE, "Unreadable");
umask = U0|U1|U2;
break; /* arggggh! */
/* case 4: nonononononononononono!!!!!!! */
default:
umask = UNREADABLE;
break;
}
/* Read the annotation file. */
while (getann(0, &annot) >= 0) {
/* Count the annotation in the test or learning period as appropriate. */
if (annot.time >= ttest) btab[annot.anntyp].tcount++;
else btab[annot.anntyp].lcount++;
if (isqrs(annot.anntyp)) {
/* Update the ectopic activity tables if appropriate. */
switch (map2(annot.anntyp)) {
case NORMAL: if (a > 0) { aupdate(a); a = 0; }
if (v > 0) { vupdate(v); v = 0; }
break;
case SVPB: if (v > 0) { vupdate(v); v = 0; }
a++;
break;
case PVC:
case FUSION: if (a > 0) { aupdate(a); a = 0; }
v++;
break;
}
/* Keep track of the times of the last 4 beats. */
tq0 = tq1; tq1 = tq2; tq2 = tq3; tq3 = annot.time;
/* Update the count of consecutive beats in the current rhythm. */
rlen++;
/* Check various conditions which might make a heart rate
measurement invalid. */
switch (rhythm) {
case NSR:
case SBR:
case BII:
case B22:
case B3:
case SAB:
case PREX:
if (map2(annot.anntyp) != NORMAL) rlen = -1; break;
case AB:
case NOD:
if (map1(annot.anntyp) != NORMAL) rlen = -1; break;
case EAR:
case SVTA:
if (map2(annot.anntyp) != SVPB) rlen = -1; break;
case PR:
if (annot.anntyp != PACE) rlen = -1; break;
case IVR:
case VT:
if (map1(annot.anntyp) == NORMAL) rlen = -1; break;
case VFL:
if (annot.anntyp != FLWAV) rlen = -1; break;
default:
break;
}
/* If a valid heart rate can be measured, update the records of
maximum and minimum rate for the current rhythm. Note that
these can be determined from the minimum and maximum R-R
intervals; what is actually used here is the sum of three
consecutive intervals. */
if (rlen >= 3) {
rr3 = tq3 - tq0;
if (rr3 > rtab[rhythm].max3r) rtab[rhythm].max3r = rr3;
if (rr3 < rtab[rhythm].min3r) rtab[rhythm].min3r = rr3;
}
}
else if (annot.anntyp == RHYTHM) {
/* Don't count the interval from the beginning of the record to
the first rhythm label as `unspecified rhythm' unless the first
beat label occurs before the first rhythm label. */
if (tq3 > 0L) {
rtab[rhythm].episodes++;
rtab[rhythm].duration += annot.time - r0;
r0 = annot.time;
}
/* Identify the new rhythm. */
for (i = 1, rhythm = R_UNDEF; i < N_RTYPES; i++)
if (strcmp(annot.aux+2, rtab[i].rstring) == 0) {
rhythm = i;
break;
}
if (rhythm == SVTA || rhythm == VT) rlen = 0;
else rlen = -1;
}
else if (annot.anntyp == STCH || annot.anntyp == TCH) {
/* Identify the nature of the ST or T change from the aux field.
The syntax of the aux field is one of:
"(" <type> [<signal designation>] <sign>
"A" <type> [<signal designation>] <sign> [<magnitude>]
<type> [<signal designation>] <sign> ")"
where <type> is "ST" or "T", <signal designation> is 1 or 2
decimal digits, <sign> is "+", "++", "-", "--", or "BP", and
<magnitude> is 2-4 decimal digits. ("++" and "--" appear only
in T-wave change annotations; they designate extreme (>= 0.4 mV
changes in T-wave amplitude.) The signal designation is
the signal number (e.g., signal 0 is designated by "0",
etc.); in records which contain only one signal (such as those
in the VALE DB), the signal designation is omitted. The
magnitude is given in microvolts, and may also be omitted (as
in the VALE DB).
*/
if (annot.aux == NULL)
fprintf(stderr,
"warning: %s annotation without text field at %s\n",
ecgstr(annot.anntyp), timstr(annot.time));
else {
p = annot.aux+1;
switch (*p) {
case '(':
if (annot.anntyp == STCH) {
type = STCH0;
p += 3; /* skip `(ST' */
}
else {
type = TCH0;
p += 2; /* skip `(T' */
}
break;
case 'A':
if (annot.anntyp == STCH) {
type = STCH1;
p += 3; /* skip `(ST' */
}
else {
type = TCH1;
p += 2; /* skip `(T' */
}
break;
case 'S':
type = STCH2;
p += 2; /* skip `ST' */
break;
case 'T':
type = TCH2;
p++; /* skip `T' */
break;
default:
fprintf(stderr,
"warning: %s annotation with text field %s at %s\n",
ecgstr(annot.anntyp), annot.aux, timstr(annot.time));
type = 0;
break;
}
if (nsig > 1) {
if ((c = atoi(p)) > 9) {
p++;
}
p++;
}
else c = 0;
switch (*p++) {
case '+':
if (*p == '+') {
sign = XPLUS;
p++;
}
else
sign = PLUS;
break;
case '-':
if (*p == '-') {
sign = XMINUS;
p++;
}
else
sign = MINUS;
break;
case 'B':
if (*p == 'P') {
sign = BIPHASIC;
p++;
}
break;
default:
fprintf(stderr,
"warning: %s annotation with text field %s at %s\n",
ecgstr(annot.anntyp), annot.aux, timstr(annot.time));
sign = 0;
break;
}
if (*p && *p != ')') {
if ((magnitude = atoi(p)) > 0)
st_calibrated = 1;
}
else magnitude = 0;
if (0 <= c && c < MAXSIG) switch (type) {
case STCH0: /* beginning of ST change episode */
if (sign == PLUS) {
st_data = 1;
s0[STP][c] = annot.time;
}
else if (sign == MINUS) {
st_data = 1;
s0[STN][c] = annot.time;
}
break;
case STCH1: /* extremum of ST change */
if (sign == PLUS && magnitude > sttab[STP][c].extremum)
sttab[STP][c].extremum = magnitude;
else if (sign == MINUS &&
magnitude > sttab[STN][c].extremum)
sttab[STN][c].extremum = magnitude;
break;
case STCH2: /* end of ST change episode */
if (sign == PLUS) {
sttab[STP][c].episodes++;
sttab[STP][c].duration += annot.time - s0[STP][c];
s0[STP][c] = 0L;
}
else if (sign == MINUS) {
sttab[STN][c].episodes++;
sttab[STN][c].duration += annot.time - s0[STN][c];
s0[STN][c] = 0L;
}
break;
case TCH0: /* beginning of T change episode */
if (sign == PLUS) {
t_data = 1;
s0[TP][c] = annot.time;
}
else if (sign == MINUS) {
t_data = 1;
s0[TN][c] = annot.time;
}
else if (sign == XPLUS) {
t_data = 1;
s0[TXP][c] = annot.time;
}
else if (sign == XMINUS) {
t_data = 1;
s0[TXN][c] = annot.time;
}
else if (sign == BIPHASIC) {
t_data = 1;
s0[TBP][c] = annot.time;
}
break;
case TCH1: /* extremum of T change */
if ((sign == PLUS || sign == XPLUS) &&
magnitude > sttab[TP][c].extremum)
sttab[TP][c].extremum = magnitude;
else if ((sign == MINUS || sign == XMINUS) &&
magnitude > sttab[TN][c].extremum)
sttab[TN][c].extremum = magnitude;
break;
case TCH2: /* end of T change episode */
if (sign == PLUS) {
sttab[TP][c].episodes++;
sttab[TP][c].duration += annot.time - s0[TP][c];
s0[TP][c] = 0L;
}
else if (sign == MINUS) {
sttab[TN][c].episodes++;
sttab[TN][c].duration += annot.time - s0[TN][c];
s0[TN][c] = 0L;
}
else if (sign == XPLUS) {
sttab[TXP][c].episodes++;
sttab[TXP][c].duration += annot.time - s0[TXP][c];
s0[TXP][c] = 0L;
}
else if (sign == XMINUS) {
sttab[TXN][c].episodes++;
sttab[TXN][c].duration += annot.time - s0[TXN][c];
s0[TXN][c] = 0L;
}
else if (sign == BIPHASIC) {
sttab[TBP][c].episodes++;
sttab[TBP][c].duration += annot.time - s0[TBP][c];
s0[TBP][c] = 0L;
}
break;
default:
break;
}
}
}
else if (annot.anntyp == NOISE) {
/* Don't count the interval from the beginning of the record to
the first signal quality label as `clean' unless the first beat
label occurs before the first signal quality label. */
if (tq3 > 0L) {
ntab[noise].episodes++;
ntab[noise].duration += annot.time - n0;
n0 = annot.time;
}
noise = annot.subtyp & 0xff;
/* Unreadable segments invalidate rate measurements. */
if ((noise & umask) == umask) rlen = -1;
}
}
/* At the end of the annotation file, count any run in progress and adjust
the counters for the current rhythm and noise type, and ST and T states.
*/
if (a) aupdate(a);
if (v) aupdate(v);
if (strtim("e") > annot.time) annot.time = strtim("e");
rtab[rhythm].episodes++;
rtab[rhythm].duration += annot.time - r0;
for (c = 0; c < nsig; c++) {
for (i = 0; i < N_STYPES; i++)
if (s0[i][c] > 0L) {
sttab[i][c].episodes++;
sttab[i][c].duration += annot.time - s0[i][c];
}
if (sttab[TXP][c].episodes > 0)
sttab[TXP][c].extremum = sttab[TP][c].extremum;
if (sttab[TXN][c].episodes > 0)
sttab[TXN][c].extremum = sttab[TN][c].extremum;
}
ntab[noise].episodes++;
ntab[noise].duration += annot.time - n0;
/* Read the first line of info, if any. */
infop = getinfo(record);
if (infop == NULL)
info_type = NO_INFO;
else {
while (*infop == ' ' || *infop == '\t')
infop++;
if (*infop == 'A') { /* `Age: ...' as in European ST-T DB */
sscanf(infop, "Age: %d Sex: %s", &age, sex);
info_type = EDB;
}
else if (*infop == 'R') { /* `Reference: ...' as in VALE DB */
age = -1; sex[0] = '?';
info_type = VALEDB;
}
else { /* fixed format as in MIT DB */
sscanf(infop, "%d%s", &age, sex);
info_type = MITDB;
}
}
/* Start a "keep" (to force troff to print the following section in a
single column), and print the section header line. */
printf(".KS\n.SH\n");
printf("Record %s (", record);
for (i = 0; i < nsig; i++) {
if (i > 0) fputs(", ", stdout);
fputs(si[i].desc, stdout);
}
fputs("; ", stdout);
switch (sex[0]) {
case 'f':
case 'F': fputs("female, age ", stdout); break;
case 'm':
case 'M': fputs("male, age ", stdout); break;
default: fputs("age ", stdout); break;
}
if (age > 0) printf("%d)\n", age);
else fputs("not recorded)\n", stdout);
switch (info_type) {
case MITDB:
/* Print the medications section (the second line of info). */
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".LP\n\\fIMedications:\\fR %s\n", infop);
}
break;
default:
break;
}
/* Print the beat table. */
printf(".TS\nexpand;\nlw(1i) c c c\nl r r r.\n");
printf("\\fIBeats\tBefore 5:00\tAfter 5:00\tTotal\\fR\n");
bstats(NORMAL, "Normal");
bstats(LBBB, "Left BBB");
bstats(RBBB, "Right BBB");
bstats(APC, "APC");
bstats(ABERR, "Aberrated APC");
bstats(NPC, "Junctional premature");
bstats(SVPB, "SVPC");
bstats(PVC, "PVC");
bstats(RONT, "R-on-T PVC");
bstats(FUSION, "Fusion PVC");
bstats(FLWAV, "Ventricular flutter wave");
bstats(AESC, "Atrial escape");
bstats(NESC, "Junctional escape");
bstats(SVESC, "Supraventricular escape");
bstats(VESC, "Ventricular escape");
bstats(PACE, "Paced");
bstats(PFUS, "Pacemaker fusion");
bstats(NAPC, "Blocked APC");
bstats(UNKNOWN, "Unclassifiable");
printf("\\fITotal\t%ld\t%ld\t%ld\\fR\n", lsum, tsum, lsum+tsum);
printf(".TE\n.KE\n");
/* Print the supraventricular ectopy table, if necessary. */
if (artab[0].len > 1 || artab[0].nruns > 1 || artab[1].len > 0) {
int i, j, k, l;
printf(".KS\n.TS\nlw(1.125i) l.\n");
printf("\\fISupraventricular ectopy:\\fR");
for (i = 0; i < N_RLEN; i++) {
for (j = 0, l = 9999; j < N_RLEN; j++)
if (0 < artab[j].len && artab[j].len < l) l = artab[k = j].len;
if (l == 9999) break;
switch (l) {
case 1:
printf("\t%d isolated beat%s\n",
artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
break;
case 2:
printf("\t%d couplet%s\n",
artab[k].nruns, (artab[k].nruns > 1) ? "s" : "");
break;
default:
printf("\t%d run%s of %d beats\n",
artab[k].nruns, (artab[k].nruns > 1) ? "s" : "",
artab[k].len);
break;
}
artab[k].len = 0;
}
printf(".TE\n.KE\n");
}
/* Print the ventricular ectopy table, if necessary. */
if (vrtab[0].len > 1 || vrtab[0].nruns > 1 || vrtab[1].len > 0) {
int i, j, k, l;
printf(".KS\n.TS\nlw(1.125i) l.\n");
printf("\\fIVentricular ectopy:\\fR");
for (i = 0; i < N_RLEN; i++) {
for (j = 0, l = 9999; j < N_RLEN; j++)
if (0 < vrtab[j].len && vrtab[j].len < l) l = vrtab[k = j].len;
if (l == 9999) break;
switch (l) {
case 1:
printf("\t%d isolated beat%s\n",
vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
break;
case 2:
printf("\t%d couplet%s\n",
vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "");
break;
default:
printf("\t%d run%s of %d beats\n",
vrtab[k].nruns, (vrtab[k].nruns > 1) ? "s" : "",
vrtab[k].len);
break;
}
vrtab[k].len = 0;
}
printf(".TE\n.KE\n");
}
/* Print the rhythm table. */
printf(".KS\n.TS\nexpand;\nlw c rw(0.4i) rw(0.4i)\nl c n r.\n");
printf("\\fIRhythm\tRate\tEpisodes\tDuration\\fR\n");
for (i = 0; i < N_RTYPES; i++)
if (rtab[i].episodes != 0L) {
printf("%s\t", rtab[i].rslong);
if (rtab[i].max3r > 0)
printf("%d", minrate = (int)(t3min/rtab[i].max3r + 0.5));
else
minrate = 0;
if (rtab[i].min3r != 0xffff) {
maxrate = (int)(t3min/rtab[i].min3r + 0.5);
if (maxrate > minrate) printf("\\-%d", maxrate);
}
else if (minrate == 0)
printf("\\-");
printf("\t%ld\t%s\n", rtab[i].episodes, timstr(rtab[i].duration));
}
printf(".TE\n.KE\n");
/* Print the ST and T-change table, if available. */
if (st_data || t_data) {
printf(".KS\n.TS\nexpand;\nl");
if (st_calibrated) printf(" r");
printf(" rw(0.4i) rw(0.4i)\nl");
if (st_calibrated) printf(" n");
printf(" n r.\n\\fIST\\-T state");
if (st_calibrated) printf("\tExtremum");
printf("\tEpisodes\tDuration\\fR\n");
for (c = 0; c < nsig; c++) {
int header_printed = 0;
for (i = 0; i < N_STYPES; i++) {
if (sttab[i][c].episodes != 0L) {
if (header_printed == 0) {
switch (nsig) {
case 1:
break;
case 2:
if (c == 0) printf("\\fI(upper signal)\\fR\n");
else printf("\\fI(lower signal)\\fR\n");
break;
case 3:
if (c == 0) printf("\\fI(upper signal)\\fR\n");
else if (c == 1)
printf("\\fI(middle signal)\\fR\n");
else printf("\\fI(lower signal)\\fR\n");
break;
default:
printf("(signal %d)\n", c);
break;
}
header_printed = 1;
}
printf("%s\t", sttab[i][c].ststring);
if (st_calibrated) {
if (sttab[i][c].extremum > 0) {
switch (i) {
case STP:
case TP:
case TXP: printf("\\(pl"); break;
case STN:
case TN:
case TXN: printf("\\(mi"); break;
}
printf("%d\t", sttab[i][c].extremum);
}
else
printf("\\-\t");
}
printf("%ld\t%s\n", sttab[i][c].episodes,
timstr(sttab[i][c].duration));
}
}
}
printf(".TE\n.KE\n");
}
/* Print the signal quality table. */
printf(".KS\n.TS\nexpand;\nl rw(0.4i) rw(0.4i)\nl n r.\n");
printf("\\fISignal quality\tEpisodes\tDuration\\fR\n");
for (i = 0; i < N_NTYPES; i++)
if (ntab[i].episodes != 0L) {
if (ntab[i].nstring == NULL) {
static char ns[40];
sprintf(ns, "Signal quality code %d", i);
ntab[i].nstring = ns;
}
printf("%s\t%ld\t%s\n", ntab[i].nstring, ntab[i].episodes,
timstr(ntab[i].duration));
}
printf(".TE\n"); /* (the "keep" ends after the "notes" section) */
/* Print a "notes" section if there is any additional info available. */
switch (info_type) {
case EDB:
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
if ('a' <= *infop && *infop <= 'z') *infop += 'A' - 'a';
printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
while (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".br\n%s\n", infop);
}
}
else
printf(".LP\n");
break;
case MITDB:
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
while (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf("%s\n", infop);
}
}
else
printf(".LP\n");
break;
case VALEDB:
if (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
if (strncmp(infop, "Reference:", 10) == 0)
infop = getinfo(NULL);
}
if (infop) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".IP \\fINotes:\\fR .375i\n%s\n", infop);
while (infop = getinfo(NULL)) {
while (*infop == ' ' || *infop == '\t')
infop++;
printf(".br\n%s\n", infop);
}
}
else
printf(".LP\n");
break;
default:
printf(".LP\n");
break;
}
printf(".KE\n");
/* Close the database files and quit. */
dbquit();
exit(0);
}
setrtab(i, rst, rsl)
int i;
char *rst, *rsl;
{
if (0 <= i && i < N_RTYPES) {
rtab[i].episodes = 0L;
rtab[i].duration = strtim("0.5");
rtab[i].rstring = rst;
rtab[i].rslong = rsl;
rtab[i].min3r = 0xffff;
rtab[i].max3r = 0;
}
}
setstab(i, signal, sst)
int i, signal;
char *sst;
{
if (0 <= i && i < N_STYPES && 0 <= signal && signal < MAXSIG) {
sttab[i][signal].episodes = 0L;
sttab[i][signal].duration = strtim("0.5");
sttab[i][signal].ststring = sst;
sttab[i][signal].extremum = 0;
}
}
setntab(i, nst)
int i;
char *nst;
{
if (0 <= i && i < N_NTYPES) {
ntab[i].episodes = 0L;
ntab[i].duration = strtim("0.5");
ntab[i].nstring = nst;
}
}
bstats(i, s) /* print a line of the beat table if beats of type i were seen */
int i;
char *s; /* English description of beat type i */
{
if (btab[i].tcount > 0L || btab[i].lcount > 0L) {
printf("%s\t", s);
if (btab[i].lcount > 0L) {
printf("%ld\t", btab[i].lcount);
lsum += btab[i].lcount;
}
else
printf("\\-\t");
if (btab[i].tcount > 0L) {
printf("%ld\t", btab[i].tcount);
tsum += btab[i].tcount;
}
else
printf("\\-\t");
printf("%ld\n", btab[i].lcount + btab[i].tcount);
}
}
aupdate(i) /* Record that an i-beat SV run was observed */
int i;
{
int j;
for (j = 0; j < N_RLEN-1; j++) {
if (artab[j].len == i) break;
else if (artab[j].len == 0) { artab[j].len = i; break; }
}
artab[j].nruns++;
}
vupdate(i) /* Record that an i-beat V run was observed */
int i;
{
int j;
for (j = 0; j < N_RLEN-1; j++) {
if (vrtab[j].len == i) break;
else if (vrtab[j].len == 0) { vrtab[j].len = i; break; }
}
vrtab[j].nruns++;
}
@@ -0,0 +1,159 @@
/* file: dbtab.c G. Moody 25 August 1988
Last revised: 22 July 1992
Add a line to the database summary table
Copyright (C) Massachusetts Institute of Technology 1992. All rights reserved.
This program generates tabular data that are eventually stuffed into a
tbl/troff format file to be printed in the MIT-BIH Arrhythmia Database
Directory. The code is *not* pretty. Sorry about that...
*/
#include <stdio.h>
#include <ecg/db.h>
#include <ecg/ecgmap.h>
/* Rhythm codes. These may be rearranged arbitrarily, but the table rstring[]
must be kept in agreement. The order of these codes determines the order
in which rhythms are printed in the rhythm table. */
#define NSR 1
#define SBR 2
#define BII 3
#define PREX 4
#define AB 5
#define SVTA 6
#define AFL 7
#define AFIB 8
#define PR 9
#define NOD 10
#define VBG 11
#define VTG 12
#define IVR 13
#define VT 14
#define VFL 15
#define MAXR 15 /* largest assigned rhythm code */
static char *rstring[] = { /* rhythm strings in DB annotation files */
"", "N", "SBR", "BII", "PREX",
"AB", "SVTA", "AFL", "AFIB", "P",
"NOD", "B", "T", "IVR", "VT",
"VFL" };
/* Beat tables and counters: the number of beats of annotation type i during
the learning period (first 5 minutes) is ltab[i], and during the test period
(the remainder of the record) is ttab[i]; bstats() accumulates the sum of
all elements of ltab[] in lsum, and the sum of all elements of ttab[] in
tsum. */
static long ltab[ACMAX+1], ttab[ACMAX+1], lsum, tsum;
static int tflag = 0;
main(argc, argv)
int argc;
char *argv[];
{
int i, rhythm = 0;
long r0 = 0L;
static struct siginfo si[MAXSIG];
struct anninfo af[1];
struct ann annot;
static char *tape;
static long rtab[MAXR+2], rtime[MAXR+2], ttest;
/* Check command-line arguments. */
if (argc < 3) {
fprintf(stderr, "usage: %s annotator tape [-t]\n", argv[0]);
exit(1);
}
/* Open the database files. */
af[0].name = argv[1]; af[0].stat = READ; tape = argv[2];
if (dbinit(tape, af, 1, si, MAXSIG) < 0)
exit(2);
/* Initialize variables. */
ttest = strtim("5:0");
rtime[0] = strtim("0.5");
for (i = 1; i <= MAXR; i++)
rtime[i] = rtime[0];
/* If specified, skip the learning period. */
if (argc > 3 && strcmp("-t", argv[3]) == 0) {
tflag = 1;
r0 = ttest;
}
/* Read the annotation file. */
while (getann(0, &annot) >= 0) {
/* Count the annotation in the test or learning period as appropriate. */
if (annot.time >= ttest) ttab[annot.anntyp]++;
else ltab[annot.anntyp]++;
if (annot.anntyp == RHYTHM) {
if (rhythm) { /* This condition is true except for the first
rhythm label. For the second and subsequent
rhythm labels, update the number of episodes
and duration of the rhythm just ended. */
if (annot.time > r0) {
rtab[rhythm]++;
rtime[rhythm] += annot.time - r0;
r0 = annot.time;
}
}
/* Identify the new rhythm. */
for (i = 1, rhythm = MAXR+1; i <= MAXR; i++)
if (strcmp(annot.aux+2, rstring[i]) == 0) {
rhythm = i;
break;
}
}
}
/* At the end of the annotation file, adjust the counter for the current
rhythm. */
if (si[0].nsamp > annot.time) annot.time = si[0].nsamp;
if (rhythm) {
rtab[rhythm]++;
rtime[rhythm] += annot.time - r0;
}
printf("%s", tape);
/* Print the beat table. */
bstats(NORMAL);
bstats(LBBB);
bstats(RBBB);
bstats(APC);
bstats(ABERR);
bstats(NPC);
bstats(SVPB);
bstats(PVC);
bstats(FUSION);
bstats(FLWAV);
bstats(AESC);
bstats(NESC);
bstats(VESC);
bstats(PACE);
bstats(PFUS);
bstats(NAPC);
bstats(UNKNOWN);
/* Print the rhythm table. */
for (i = 1; i <= MAXR; i++)
if (rtab[i] != 0L)
printf("\t%s", timstr(rtime[i]));
else
printf("\t\\-");
printf("\n");
exit(0);
}
bstats(i) /* print an entry in the beat table */
int i;
{
if (tflag) ltab[i] = 0L;
if (ltab[i] > 0L || ttab[i] > 0L)
printf("\t%ld", ltab[i]+ttab[i]);
else printf("\t\\-");
}
@@ -0,0 +1,9 @@
trap "" 1 15
COMMAND=$1
shift
for R in 100 101 102 103 104 105 106 107 108 109 111 112 113 114 115 116 117 \
118 119 121 122 123 124 200 201 202 203 205 207 208 209 210 212 213 214 215 \
217 219 220 221 222 223 228 230 231 232 233 234
do
$COMMAND $R $*
done
@@ -0,0 +1,469 @@
100 11:03 Normal sinus rhythm
100 25:13 PVC
100 26:09 APCs
100 27:55 Normal sinus rhythm
101 1:34 Normal sinus rhythm
101 1:48 Noise
101 5:13 Noise/artifact
101 9:54 APC
101 24:32 APC
102 0:55 Paced rhythm
102 1:12 Transition from paced to normal sinus rhythm
102 1:28 PVC
102 2:30 Normal sinus rhythm
102 4:51 Pacemaker fusion beats
102 9:35 PVC
102 16:12 Paced rhythm
103 1:09 Normal sinus rhythm
103 17:21 Normal sinus rhythm
103 19:15 APC, noise in lower signal
103 22:13 Noise in lower signal
103 23:33 Noise in lower signal
103 28:58 Noise in lower signal
104 3:42 PVC
104 5:13 Noise
104 5:52 Transition from paced to normal sinus rhythm
104 6:17 Noise
104 8:22 Noise in lower signal
104 26:51 Paced, normal, and pacemaker fusion beats
104 29:10 Paced, normal, and pacemaker fusion beats
105 5:27 Artifact
105 7:57 PVC
105 15:16 Normal sinus rhythm
105 17:52 Artifacts
105 22:02 Noise
105 26:45 PVC
105 27:27 Noise
105 28:08 Noise
105 29:07 Noise
106 0:19 Normal sinus rhythm, noise in lower signal
106 1:37 Ventricular couplets
106 2:53 Ventricular tachycardia, 3 beats
106 4:23 PVC
106 7:57 Noise in lower signal
106 10:52 Noise in lower signal
106 12:27 Ventricular bigeminy (two types)
106 16:17 Multiform PVCs, ventricular couplet
106 25:13 Ventricular couplet
106 25:52 Ventricular couplets
107 0:44 Paced rhythm
107 12:30 PVC
107 19:54 PVC
107 20:38 Noise
107 25:52 PVC
108 0:22 PVC, blocked APC, noise
108 4:51 Interpolated PVC
108 7:41 Axis shift
108 8:13 PVC, blocked APC, fusion PVC
108 10:55 Sinus arrhythmia
108 18:08 Fusion PVC-PVC couplet
108 20:05 Junctional escape beat
108 24:20 Blocked APC, APCs
108 28:10 Noise
108 29:00 Noise
109 0:13 Fusion PVC
109 1:28 PVC
109 3:36 Normal sinus rhythm with left bundle branch block
109 4:46 PVC
109 5:27 Noise
109 14:01 PVC
109 17:13 PVC
109 19:21 PVC
109 26:09 Noise
109 28:03 PVC
109 28:28 Normal sinus rhythm with left bundle branch block
109 29:10 PVC
111 0:25 Normal sinus rhythm, first degree AVB
111 2:41 Baseline shift noise
111 3:52 Noise
111 4:04 Axis shift (in upper signal)
111 8:31 PVC
111 11:53 Axis shift
111 15:41 Noise
111 19:54 Noise
111 27:02 Noise
112 11:40 APC
112 12:30 Axis shift
112 12:49 APC, noise
113 4:20 Moderate baseline wander
113 8:22 Aberrated APC
113 11:48 Sinus arrhythmia with variation in P-wave morphology
113 12:27 Sinus arrhythmia
113 22:10 Aberrated APC
113 29:01 Sinus arrhythmia
114 0:00 Normal sinus rhythm
114 1:20 PVC
114 3:39 PVC
114 3:56 Late-cycle PVC
114 4:35 Late-cycle PVC
114 5:36 Ventricular couplet
114 8:31 PVCs
114 9:26 PVC, muscle noise
114 11:37 SVTA, PVC, junctional premature beat
114 20:02 Noise
114 29:29 Baseline artifact
115 0:55 Normal sinus rhythm
115 3:22 Sinus arrhythmia
115 15:52 Baseline wander in lower signal
115 21:28 Noise in lower signal
115 27:05 Artifacts in lower signal
115 28:42 Normal sinus rhythm
116 0:44 Normal sinus rhythm
116 1:31 PVCs
116 10:00 Ventricular couplet
116 12:32 PVC
116 16:37 Noise, saturation in upper signal
116 23:08 Noise, saturation in upper signal
116 25:47 Ventricular couplet
117 3:56 Normal sinus rhythm
117 11:58 Noise, APC
117 22:27 Normal sinus rhythm
118 3:39 PVC
118 8:31 Noise
118 9:23 PVC
118 13:47 Blocked APC
118 22:32 APC, blocked APC, PVC
118 23:25 APCs
118 25:41 PVC, APC
118 26:23 PVCs
118 26:48 Noise
118 28:58 Atrial couplet
118 29:53 APCs
119 1:55 PVC
119 2:38 Ventricular trigeminy
119 4:51 Ventricular bigeminy
119 8:42 Normal sinus rhythm
119 20:05 Noise
119 25:33 Noise
121 1:06 Normal sinus rhythm
121 3:33 Noise in lower signal
121 16:48 APC, PVC
121 24:32 Noise
121 26:01 Noise
122 1:09 Noise in lower signal
122 13:47 Normal sinus rhythm
122 28:25 Normal sinus rhythm
123 22:10 Sinus arrhythmia
123 25:11 Interpolated PVC
123 27:41 Interpolated PVC
123 28:36 Sinus arrhythmia
124 4:43 Accelerated junctional rhythm
124 5:09 Accelerated idioventricular rhythm, 20 beats
124 5:21 Accelerated idioventricular rhythm (continued from previous strip)
124 9:05 Noise in lower signal
124 10:27 Accelerated idioventricular rhythm
124 17:38 Accelerated idioventricular rhythm
124 20:13 APC
124 23:22 Ventricular trigeminy
124 26:03 PVC
124 27:41 PVC, junctional escape beats
200 1:42 Ventricular tachycardia, 3 beats
200 5:38 Noise
200 18:14 Ventricular tachycardia, 4 beats
200 20:52 Noise
200 24:49 Ventricular tachycardia, 3 beats
200 26:12 Ventricular couplets
200 28:31 Ventricular couplet
200 29:01 APC, ventricular bigeminy
200 29:18 APC, PVC
200 29:51 PVCs
201 0:00 Atrial fibrillation
201 6:15 Transition from atrial fibrillation to nodal rhythm
201 7:52 Onset of atrial fibrillation with aberrated beats
201 8:53 Blocked APCs
201 18:14 Noise
201 20:16 PVCs, atrial couplet
201 22:33 Ventricular trigeminy
201 23:50 Normal sinus rhythm
201 24:15 Aberrated atrial couplet, fusion PVC
201 25:19 Aberrated atrial couplets with right and left bundle branch block
201 26:20 Supraventricular tachyarrhythmia with aberration, 3 beats
201 28:03 Atrial fibrillation with aberration
202 10:16 Normal sinus rhythm, PVCs
202 12:24 APCs, PVC
202 12:41 Aberrated APCs, PVC
202 18:22 Normal sinus rhythm, bradycardia
202 18:45 Aberrated APCs
202 18:59 Onset of atrial fibrillation with aberrated beats
202 21:10 Atrial fibrillation, PVC
202 21:26 End of atrial fibrillation
202 22:13 Atrial fibrillation, aberrated beats
202 25:58 Atrial flutter with 2:1 conduction
202 27:55 Atrial fibrillation, aberrated beat
202 29:35 Atrial fibrillation
203 5:00 Ventricular tachycardia, 4 beats and 9 beats
203 13:14 Atrial fibrillation, ventricular couplets
203 15:02 Noise
203 22:02 Ventricular couplet, PVCs
203 23:25 Noise
203 24:04 PVCs
203 24:46 Noise
203 26:39 Ventricular tachycardia, 7 beats
203 26:51 Ventricular couplet, PVCs
203 27:15 Ventricular tachycardia, 3 beats
205 4:57 Ventricular tachycardia, 8 beats and 10 beats
205 14:54 Noise in lower signal
205 15:22 Ventricular tachycardia, 3 beats
205 16:03 PVC
205 16:15 Fusion PVCs
205 19:57 PVC, APC
205 24:18 Ventricular tachycardia, 12 beats, start of VR, 13 beats
205 24:30 End of VR, 3-beat VT (continued from previous strip)
205 27:57 PVCs
207 0:00 Ventricular bigeminy with right bundle branch block
207 0:33 Ventricular couplet, ventricular tachycardia, ventricular flutter
207 0:45 Ventricular flutter (continued from previous strip)
207 0:57 Ventricular flutter, VT (continued from previous strip)
207 1:28 Normal sinus rhythm with first degree AV block
207 2:07 Ventricular couplet, left and right bundle branch block
207 2:41 Ventricular bigeminy with right bundle branch block
207 4:25 Ventricular couplet, ventricular flutter
207 5:19 Noise
207 6:56 Noise
207 17:35 Normal sinus rhythm with first degree AV block
207 25:36 Ventricular bigeminy with LBBB, ventricular flutter
207 26:10 Ventricular flutter
207 27:14 End of ventricular flutter
207 29:10 Atrial couplet, SVTA (104 beats, to end of record)
208 14:57 Noise/artifact
208 17:02 Ventricular couplets
208 19:10 Ventricular trigeminy
208 21:06 Noise
208 23:00 Noise
208 28:08 Ventricular triplet
208 28:58 Ventricular couplets, isolated PVCs
208 29:37 Ventricular triplet
209 0:47 APC
209 5:50 Noise
209 12:57 PVC
209 14:21 SVTA, 14 beats
209 23:25 APCs
209 28:17 Noise, APC
209 28:33 SVTA, 4 beats, 13 beats
209 29:10 SVTA, 6 beats
210 3:47 Noise
210 6:56 Ventricular tachycardia, 6 beats
210 13:14 Ventricular couplets
210 15:55 Noise
210 17:32 Aberrated beat
210 17:57 Ventricular tachycardia, 6 beats
210 20:33 PVCs
210 29:15 PVCs, aberrated beat, ventricular escape beat, fusion PVC
212 0:05 Normal sinus rhythm with right bundle branch block
212 18:42 Noise
212 24:21 End of RBBB in normal sinus rhythm as heart rate decreases
212 24:38 Sinus arrhythmia
212 26:45 Noise
212 28:46 Start of RBBB in normal sinus rhythm as heart rate increases
213 3:39 Fusion PVC couplet
213 14:48 Ventricular tachycardia, 3 beats
213 15:05 Fusion PVC/PVC couplet
213 15:38 APC
213 17:30 Ventricular tachycardia, 4 beats
213 17:55 PVCs, fusion PVCs, APC
213 24:43 Fusion PVC, PVCs, aberrated APC
213 25:58 Aberrated atrial couplet
213 26:51 Ventricular bigeminy
213 28:56 Fusion PVCs, PVC
214 0:30 Normal sinus rhythm with left bundle branch block, PVC
214 2:21 Multiform PVCs
214 2:35 Ventricular couplet
214 3:25 Artifactual amplitude changes
214 3:56 Ventricular couplet
214 5:08 Tape slippage
214 5:38 Ventricular tachycardia, 3 beats
214 13:53 Ventricular tachycardia, 3 beats
214 23:17 Ventricular couplet
214 27:52 PVC/fusion PVC couplet
215 2:55 Ventricular tachycardia, 3 beats
215 3:11 Noise, ventricular couplet
215 9:46 PVC, APC
215 15:58 PVC
215 20:25 Ventricular tachycardia, 3 beats
215 22:16 Tape slippage
215 24:57 Ventricular couplet
215 27:30 Ventricular triplet
215 27:57 Noise in lower signal
215 29:42 Tape slippage, ventricular couplet
217 0:00 Paced rhythm, pacemaker fusion beat
217 0:33 PVC
217 1:23 PVCs, pacemaker fusion beats
217 4:18 Paced beats, pacemaker fusion beats, normal beats, PVCs
217 6:12 Ventricular couplet
217 6:51 Ventricular couplet
217 7:05 Ventricular tachycardia, 3 beats
217 12:27 Ventricular bigeminy with paced beats
217 14:01 Noise
217 15:30 Atrial fibrillation
217 16:56 Ventricular couplet
217 22:27 Atrial fibrillation
219 2:49 Fusion PVC
219 5:08 APC
219 5:22 Onset of atrial fibrillation
219 13:50 End of atrial fibrillation, conversion pause, APC, blocked APC
219 19:00 End of atrial fibrillation
219 19:57 Normal sinus rhythm, blocked APCs
219 22:11 End of atrial fibrillation, conversion pause, blocked APCs
219 22:43 Blocked APCs
219 24:43 PVC
219 26:09 Atrial fibrillation
219 28:55 Atrial fibrillation, PVCs
220 6:46 SVTA (3 runs)
220 10:41 SVTA (2 runs)
220 18:10 Atrial couplets
220 20:29 SVTA, atrial couplets
220 23:58 Noise in lower signal
220 25:44 Atrial couplet
220 29:40 APC
221 0:00 Atrial fibrillation, PVCs
221 2:43 Ventricular couplet
221 13:00 Ventricular tachycardia, 3 beats
221 13:56 Ventricular tachycardia, 3 beats
221 17:49 Ventricular couplet
221 19:12 PVCs
221 19:42 Noise
221 20:44 Ventricular couplet
222 6:45 Normal sinus rhythm
222 17:32 Atrial fibrillation
222 19:48 Atrial couplet
222 20:07 Atrial bigeminy
222 22:32 Paroxysmal atrial fibrillation, nodal rhythm
222 24:43 Noise
222 25:43 End of atrial fibrillation, nodal rhythm, normal sinus rhythm
222 26:09 Paroxysmal atrial flutter, nodal rhythm
223 9:35 Bidirectional ventricular tachycardia
223 12:35 Noise in lower signal
223 13:39 Atrial couplet
223 13:51 Ventricular bigeminy
223 17:21 Bidirectional ventricular tachycardia
223 17:55 End of ventricular tachycardia
223 19:07 APC, ventricular couplet, atrial escape beat, aberrated APC
223 23:58 Ventricular tachycardia, 3 beats
223 25:47 Ventricular tachycardia, 5 beats
223 29:16 APCs, atrial escape beat, ventricular couplets
223 29:51 Multiform ventricular couplets
228 0:19 Ventricular bigeminy
228 0:38 Artifacts
228 0:50 PVC, ventricular couplet
228 4:35 Noise, PVCs
228 19:18 PVC, APC
228 20:08 Baseline noise
228 21:37 Tape slippage
228 21:49 Tape slippage
228 26:42 Ventricular bigeminy
230 0:00 Onset of pre-excitation
230 0:12 End of pre-excitation (continued from previous strip)
230 5:11 WPW
230 9:26 Normal sinus rhythm
230 29:04 PVC
231 0:02 Normal sinus rhythm, Mobitz II block, right bundle branch block
231 2:24 Blocked APCs, APC, ventricular couplet
231 20:11 2:1 heart block with right bundle branch block
231 21:10 Onset of 2:1 heart block
231 23:22 Transition from 2:1 block with normal conduction to NSR with RBBB
232 0:58 Noise
232 1:51 Atrial couplets
232 11:51 Noise
232 13:06 Atrial couplets
232 15:09 Short runs of SVTA
232 18:17 SVTA, pause
232 22:31 Pauses
232 22:47 Pauses
232 23:20 Pauses
232 23:34 Pause
232 23:53 Sinus bradycardia
233 0:11 APCs, PVCs
233 0:25 Ventricular tachycardia, 3 beats
233 2:18 Ventricular bigeminy
233 2:43 Fusion PVC/PVC couplet
233 3:39 Ventricular couplets
233 5:02 Transient amplitude decrease on upper signal
233 9:43 Ventricular tachycardia, 3 beats
233 16:20 PVCs, ventricular couplet
233 18:02 PVCs, APC
233 22:23 Ventricular tachycardia, 2 runs of 3 beats, ventricular couplets
234 0:16 Normal sinus rhythm
234 6:37 Noise
234 14:01 Onset of junctional tachycardia
234 14:26 End of junctional tachycardia
234 17:02 PVC
234 21:26 PVC
234 23:17 Noise
@@ -0,0 +1 @@
200 18:14 Ventricular tachycardia, 4 beats
@@ -0,0 +1,42 @@
.LP
.ds LH MIT-BIH A\s-2RRHYTHMIA\s+2 D\s-2ATABASE\s+2
.ds RH E\s-2XAMPLES\s+2
.bp 98
.DS
.DE
.ce 1
\fB\s12EXAMPLES\s10\fP
.PP
The following pages contain a set of illustrative examples from each
database record, shown at higher resolution than in the preceding
``full-disclosure'' plots. Most of these examples are those shown in the
first edition of this book.
.PP
The strips are each 12 seconds long; they cover the interval specified in the
margins to the left and right of each strip. The scale is 12.5 mm/sec and
5 mm/mV (i.e., half of the standard scale for ECG chart recordings).
Grid lines appear at the standard intervals of 0.2 seconds and 0.5 mV.
Note that the vertical position of each trace has been adjusted to
center it in the available space; thus it is not meaningful to compare
absolute voltage levels between strips. Unlike the strips in the first
edition, however, no adjustments in gain have been made to compensate for
amplitude differences between signals; thus waveform amplitudes can be
measured directly from these strips.
@@ -0,0 +1,95 @@
100 0-e
101 0-e
102 0-e
103 0-e
104 0-e
105 0-e
106 0-e
107 0-e
108 0-e
109 0-e
111 0-e
112 0-e
113 0-e
114 0-e
115 0-e
116 0-e
117 0-e
118 0-e
119 0-e
121 0-e
122 0-e
123 0-e
124 0-e
200 0-e
201 0-e
202 0-e
203 0-e
205 0-e
207 0-e
208 0-e
209 0-e
210 0-e
212 0-e
213 0-e
214 0-e
215 0-e
217 0-e
219 0-e
220 0-e
221 0-e
222 0-e
223 0-e
228 0-e
230 0-e
231 0-e
232 0-e
233 0-e
234 0-e
@@ -0,0 +1,46 @@
.LP
.ds LH MIT-BIH A\s-2RRHYTHMIA\s+2 D\s-2ATABASE\s+2
.ds CH - 1 -
.ds RH F\s-2ULL\-DISCLOSURE \s+2ECG P\s-2LOTS\s+2
.bp 2
.DS
.DE
.ce 1
\fB\s+2FULL\-DISCLOSURE ECG PLOTS\s-2\fR
.PP
This section contains annotated ``full-disclosure'' ECG plots for the
entire database. Each record is presented on two facing pages. Each
page contains fifteen one-minute segments. Each segment contains a pair
of ECG traces with annotation labels placed between them. Each label is
placed so that the fiducial mark for the annotation corresponds to the
left edge of the label. The meanings of the various labels are given
in the table on page viii.
.PP
The scale is 2.5 mm/sec and 1 mm/mV (i.e., one-tenth the standard scale
for ECG chart recordings). To improve the legibility of these plots, the
ECG signals were digitally low-pass filtered with a nine-point moving average
filter before plotting them; for this reason, high-frequency noise appears
smaller in amplitude in these plots than in the unfiltered high-resolution
strips which begin on page 98.
.PP
At the top and bottom of
each page is a time scale with one-second intervals marked; five-second
intervals are designated with large ticks on these scales. To the left
and right of each segment are the times of the beginning and end
of the segment (in minutes and seconds from
the beginning of the record). The last segment for each record begins at
29 minutes, and ends at 30 minutes and 5.556 seconds (the end of the record).
@@ -0,0 +1,63 @@
.nr PS 10
.nr VS 12
.LP
.ds LH \s10MIT-BIH A\s8RRHYTHMIA\s10 D\s8ATABASE\s10
.ds RH \s10I\s8NDEX\s10
.bp 194
.ce 1
\fB\s12INDEX OF RECORDS IN THE MIT\-BIH ARRHYTHMIA DATABASE\s10\fP
.LP
.TS
center;
c c c c
c n n n.
\fIRecord Full disclosure Examples Notes\fP
100 2 99 178
101 4 99 178
102 6 100 178
103 8 102 178
104 10 103 178
105 12 104 179
106 14 106 179
107 16 108 179
108 18 109 179
109 20 111 179
111 22 114 180
112 24 115 180
113 26 116 180
114 28 117 180
115 30 119 180
116 32 121 180
117 34 122 181
118 36 123 181
119 38 125 181
121 40 126 181
122 42 127 181
123 44 128 181
124 46 128 182
200 48 130 182
201 50 132 182
202 52 135 182
203 54 137 183
205 56 139 183
207 58 141 183
208 60 144 184
209 62 146 184
210 64 147 184
212 66 149 184
213 68 150 185
214 70 152 185
215 72 154 185
217 74 156 185
219 76 158 186
220 78 161 186
221 80 162 186
222 82 164 186
223 84 165 186
228 86 167 187
230 88 169 187
231 90 170 187
232 92 171 187
233 94 173 188
234 96 175 188
.TE
@@ -0,0 +1,456 @@
.LP
.af PN i
.ds LH MIT-BIH A\s-2RRHYTHMIA\s+2 D\s-2ATABASE\s+2
.ds RH F\s-2OREWORD\s+2
.bp 3
\s+2
\fB
.ce 1
FOREWORD
.PP
For a number of years our group has been investigating methods for real-time
ECG rhythm analysis. In the course of this work, we have developed an
extensive annotated digital ECG database. The database has been enormously
helpful to us in algorithm development and evaluation. The creation of this
resource required a major effort, and was funded, in part, by both government
and industry. We feel it is highly desirable to make this database available
to other academic and industrial groups, and hence have prepared it for
distribution. This catalog contains detailed descriptions of the database
tapes.
.PP
We acknowledge with gratitude the many dedicated hours of work which
went into this project on the part of cardiologists, Holter technicians,
laboratory assistants, and engineers in our laboratories at MIT and at
Beth Israel Hospital. We also acknowledge the help of our colleagues at
Washington University, St. Louis (particularly Russell Hermes) in
assuring a compatible data format. We especially wish to recognize:
.IP \(bu
\fIPaul Schluter\fR, who did the original design and implementation of the
database.
.IP \(bu
\fIScott Peterson\fR, who supervised the detailed data selection, digitization,
annotation, and editing at Beth Israel Hospital. He also contributed
substantially to the development of software needed for using the database in
our evaluation.
.IP \(bu
\fIGeorge Moody\fR, who converted the database to a format compatible with that
used by the AHA database, and who contributed in a major way to the directory.
.IP \(bu
\fILarry Siegal\fR, who contributed to development of the waveform editor
system.
.IP \(bu
\fICheryl Jackson\fR, who was responsible for most of the detailed
transcription of cardiologist annotations, the comparison and quality control
functions, and who assembled the final manuscript for the directory.
.IP \(bu
\fIDiane Perry\fR, who as chief technician in the Arrhythmia Laboratory helped
to identify suitable data, and who helped in the annotation process.
.IP \(bu
The group of physicians who helped with the difficult task of beat-by-beat
annotation of the ECGs:
Dr. Esmerey Acarturk, Dr. John Aumiller, Dr. Sidney Blake, Dr. Alvin
Blaustein, Dr. Chester Conrad, Dr. Gary Heller, Dr. Michael Malagold,
Dr. Roger Mark, and Dr. Candice Miklozek.
.nf
.in +3i
Roger Mark
Walter Olson
Cambridge, Massachusetts
September, 1980
.in -3i
.fi
\s+2
\fB
.ce 1
NOTES ON THE SECOND EDITION
.PP
During the eight years since we first published this book, nearly one hundred
academic and industrial research groups worldwide have used the MIT-BIH
Arrhythmia Database. We thank these organizations for their support.
.PP
In addition to those listed above, we also wish to recognize the
contributions of:
.IP \(bu
\fIJoe Mietus\fR, who handled correspondence as well as production and
distribution of the database from the Beth Israel Hospital, and who analyzed
the mechanical sources of analog tape wow and flutter.
.IP \(bu
\fITed Baker\fR, who helped in the first ``port'' of the database from our
homebrew 8080-based systems.
.PP
In lieu of reprinting the first edition of this volume, we have made use of
modern printing technology to produce a far more readable and complete record
of the contents of the database.
.PP
In June, 1987, the Association for the Advancement of Medical Instrumentation
published its \fIRecommended Practice for Testing and Reporting
Performance Results of Ventricular Arrhythmia Detection Algorithms\fR
(AAMI ECAR-1987), which may be obtained from AAMI, 3330 Washington Boulevard,
Suite 400, Arlington, VA 22201.
The MIT-BIH and AHA Databases provide developers and evaluators of arrhythmia
detectors with standard test data; the AAMI Recommended Practice provides
guidelines for using these databases in a standard way, and for describing
detector performance in a manner that facilitates comparisons between
detectors.
We urge all users of our database to follow the AAMI recommendations when
preparing performance statistics for publication.
.PP
A package of C-language software for using the MIT-BIH and AHA Databases
is available from MIT. The package
includes programs for plotting ECGs with annotations, sampling rate
conversion, and beat-by-beat comparison of annotation files following the
AAMI recommended practice, as well as a variety of other useful programs.
All the programs access the database via a common library of subroutines,
which are also provided as part of the package and which may be used with
user programs.
.PP
We have made several other sets of ECG recordings available, including
specialized databases for ventricular fibrillation, atrial fibrillation,
and ST segment changes. These databases are not annotated beat-by-beat.
.nf
.in +3i
George Moody
Roger Mark
Cambridge, Massachusetts
August, 1988
.in -3i
.fi
\s+2
\fB
.ce 1
NOTES ON THE THIRD EDITION
.PP
Since the publication of the second edition, the database has been made
available in CD-ROM format. Continued strong interest in the database has made
it possible to prepare a second edition of the CD-ROM and a third edition of
this book. The CD-ROM includes the additional specialized databases mentioned
above, and the second edition of the CD-ROM also contains the software
package mentioned above.
.PP
Compatible databases of ECGs and other physiologic signals are beginning to
appear. Of particular interest to users of our database is the European ST-T
Database, which consists of ninety two-hour ECG recordings with beats, rhythms,
and signal quality annotated as in the MIT-BIH Arrhythmia Database, and with
additional annotations to indicate ST and T-wave morphology changes. For
information, write to: CNR Institute of Clinical Physiology, Computer
Laboratory, via Trieste, 41, 56100 Pisa, Italy.
.PP
I wish to thank all of those who have supported this project over these years,
especially Roger Mark, who has guided it from its inception in 1975.
.nf
.in +3i
George Moody
Cambridge, Massachusetts
July, 1992
.in -3i
.fi
.ds RH I\s-2NTRODUCTION\s+2
.bp
.SH
INTRODUCTION
.PP
This introduction describes how the database records were
obtained, and discusses the characteristics of the recorded signals.
Following these notes are annotated ``full disclosure'' plots of the entire
database. These can be useful for obtaining an overall
impression of the contents of individual records. Following the
``full disclosure'' plots are sample ECG strips. These strips
were chosen to illustrate the salient features of each record.
Next are notes on the important features of each record.
These notes also include background information on the subjects,
including their medications.
At the end of the book are tables of rhythms and annotations, which
summarize the contents of the database.
These tables can be helpful in finding a record with a specific
set of characteristics.
.SH
Selection criteria
.PP
The source of the ECGs included in the MIT-BIH Arrhythmia Database is a
set of over 4000 long-term Holter recordings that were obtained
by the Beth Israel Hospital Arrhythmia Laboratory between 1975 and 1979.
Approximately 60% of these recordings were obtained from inpatients.
The database contains 23 records
(numbered from 100 to 124 inclusive with some numbers missing)
chosen at random from this set, and 25 records
(numbered from 200 to 234 inclusive, again with some numbers missing)
selected from the same set to include a variety of rare but clinically
important phenomena that would not be well-represented
by a small random sample of Holter recordings.
Each of the 48 records is slightly over 30 minutes long.
.PP
The first group is intended to serve as a representative sample of the
variety of waveforms and artifact that an arrhythmia detector might
encounter in routine clinical use. A table of random numbers was used
to select tapes, and then to select half-hour segments of them.
Segments selected in this way were excluded only if neither of the two
ECG signals was of adequate quality for analysis by human experts.
.PP
Records in the second group were chosen to
include complex ventricular, junctional,
and supraventricular arrhythmias and conduction abnormalities. Several
of these records were selected because features of the rhythm, QRS
morphology variation, or signal quality may be expected to present
significant difficulty to arrhythmia detectors; these records have
gained considerable notoriety among database users.
.PP
The subjects were 25 men aged 32 to 89 years, and 22 women aged 23 to 89
years. (Records 201 and 202 came from the same male subject.)
.SH
ECG lead configuration
.PP
In most records, the upper signal is a modified limb lead II (MLII),
obtained by placing the electrodes on the chest. The lower signal is
usually a modified lead V1 (occasionally V2 or V5, and in one instance V4);
as for the upper signal, the electrodes are also placed on the chest.
This configuration is routinely used by the BIH Arrhythmia Laboratory.
Normal QRS complexes are usually prominent in the upper signal.
The lead axis for the lower signal may be nearly orthogonal to the mean
cardiac electrical axis, however (i.e., normal beats are usually
biphasic and may be nearly isoelectric).
Thus normal beats are frequently difficult to discern in the lower signal,
although ectopic beats will often be more prominent (see, for example, record
106).
A notable exception is record 114, for which the signals were reversed.
Since this happens occasionally in clinical practice, arrhythmia detectors
should be equipped to deal with this situation.
In records 102 and 104, it was not possible to use modified lead II because of
surgical dressings on the patients;
modified lead V5 was used for the upper signal in these records.
.SH
Analog recording and playback
.PP
The original analog recordings were made using nine Del Mar Avionics
model 445 two-channel recorders, designated \fIA\fP through \fII\fP:
.TS
center box;
c | l.
\fIRecorder Records\fP
_
A 102, 107, 111, 115, 121
B 212
C 203
D 118, 124, 217
E 101, 103, 106, 108, 112, 117, 119, 122, 209, 219, 220, 223, 233
F 104, 109, 123, 205, 207, 210, 215, 221
G 100, 105, 114, 116, 213, 214, 222, 228
H 113, 201, 202, 231
I 200, 230, 232, 234
.TE
(It is not known which recorder was used for record 208.)
.PP
During the digitization process, the analog recordings were played back
on a Del Mar Avionics model 660 unit. The analog tapes used for records
112, 115 through 124, 205, 220, 223, and 230 through 234 were played back
and digitized at twice real time; the rest were played back at real time
using a specially constructed capstan for the model 660 unit.
Skew between the two signals was found to be as great as 40
milliseconds for some of the analog recorders.
In addition to the fixed skew that results from extremely small differences
in the orientations of the tape heads on the recorder and the playback unit,
microscopic vertical wobbling of the tape, either during recording or playback,
introduces a variable skew, which may be comparable in magnitude to the fixed
skew.
This problem (which also
occurs on the AHA database) may present difficulties for certain two-channel
analysis methods designed for real-time applications.
.PP
Minor tape speed variations should not pose problems for typical arrhythmia
detectors. It is difficult to avoid tape sticking or slippage during low-speed
playback, and several episodes of tape slippage were noted and marked with
comment annotations. Wow and flutter should be studied carefully in the
context of heart-rate variability studies, since flutter compensation
was not possible in these recordings. A number of frequency-domain artifacts
have been identified and related to specific mechanical components of the
recorders and the playback unit:
.TS
center box;
c | l.
\fIFrequency (Hz) Source\fR
_
0.042 Recorder pressure wheel
0.083 Playback unit capstan (for twice real-time playback)
0.090 Recorder capstan
0.167 Playback unit capstan (for real-time playback)
0.18\-0.10 Takeup reel (frequency decreases over time)
0.20\-0.36 Supply reel (frequency increases over time)
.TE
The most significant of these artifacts by far is the 0.167 Hz artifact on
recordings that were played back at real time. The next largest is the
0.090 Hz artifact; the 0.083 Hz artifact on recordings that were played back
at twice real-time is of roughly the same magnitude as the 0.090 Hz artifact.
The 0.042 Hz artifact is of much lower magnitude. Other frequencies
related to the drive train (at 0.42 Hz, 1.96 Hz, 9.1 Hz, and
42 Hz) do not appear as noticeable artifacts.
The frequencies of the last two artifacts listed in the table depend on
how much tape is on the supply and takeup reels; the supply reel causes
a much more noticeable artifact than does the takeup reel. Other
frequency-domain artifacts generated by the supply reel appear in the
0.10\-0.18 Hz and 0.30\-0.54 Hz bands.
.PP
Four of the 48 records (102, 104, 107, and 217) include paced beats.
The original analog recordings do not represent the pacemaker artifacts
with sufficient fidelity to permit them to be recognized by pulse amplitude
(or slew rate) and duration alone, the method commonly used for real-time
processing. The database records reproduce the analog recordings with
sufficient fidelity to permit use of pacemaker artifact detectors designed for
tape analysis, however.
.SH
Digitization
.PP
The analog outputs of the playback unit
were filtered to limit analog-to-digital converter (ADC)
saturation and for anti-aliasing,
using a passband from 0.1 to 100 Hz relative to real time, well
beyond the lowest and highest frequencies recoverable from the recordings.
The bandpass-filtered signals were
digitized at 360 Hz per signal relative to real time
using hardware constructed at the MIT Biomedical Engineering Center and
at the BIH Biomedical Engineering Laboratory.
The sampling frequency was chosen to facilitate implementations of 60 Hz
(mains frequency) digital notch filters in arrhythmia detectors.
Since the recorders were battery-powered, most of the 60 Hz noise present
in the database arose during playback.
In those records that were digitized at twice real time, this noise appears
at 30 Hz (and multiples of 30 Hz) relative to real time.
.PP
Samples were acquired from each signal almost simultaneously (the intersignal
sampling skew was on the order of a few microseconds). As noted above, analog
tape skew was several orders of magnitude larger. The ADCs were
unipolar, with 11-bit resolution over a \(+-5 mV range. Sample values thus
range from 0 to 2047 inclusive, with a value of 1024 corresponding to zero
volts.
.PP
The 11-bit samples were recorded in 8-bit first difference format (this was
necessary because of limited mass storage capacity).
Given the sampling frequency and the resolution of the ADC,
the difference encoding implies a maximum recordable slew rate of \(+-225 mV/s.
In practice, this limit was exceeded by the input signals very infrequently,
only during severe noise on a small number of records.
The effect on the quality of the recorded signals is totally negligible.
Since the ECG data files are reconstructed from difference data,
no information is lost and considerable savings in storage may be
obtained by converting them back into difference form; a program
for doing so is included in the package of C-language software available
from the BIH Biomedical Engineering Laboratory.
.SH
Annotations
.PP
An initial set of beat labels was produced by a simple slope-sensitive
QRS detector, which marked each detected event as a normal beat. Two
identical 150-foot chart recordings were printed for each 30-minute record,
with these initial beat labels in the margin.
For each record, the two charts were given to two cardiologists, who worked
on them independently. The cardiologists added additional beat labels
where the detector missed beats, deleted false detections as necessary,
and changed the labels for all abnormal beats. They also added rhythm
labels, signal quality labels, and comments.
.PP
The annotations were transcribed from the paper chart recordings.
Once both sets of cardiologists' annotations for a given record
had been transcribed and verified, they were automatically compared
beat-by-beat, and another chart recording was printed. This chart showed
the cardiologists' annotations in the margin, with all discrepancies
highlighted. Each discrepancy was reviewed and resolved by consensus.
The corrections were transcribed, and the annotations were then analyzed
by an auditing program, which checked them for consistency and which
located the ten longest and shortest R-R intervals in each record (to
identify possible missing or falsely detected beats).
.PP
In early copies of the database, most beat labels were placed
at the R-wave peak, but manually inserted labels were not always
located precisely at the peak.
In copies of the database made since 1983, the beat labels have been shifted
from their original locations.
The ECG (usually the upper signal) was digitally bandpass-filtered to
emphasize the QRS complexes, and each beat label was moved to the major local
extremum, after correction for phase shift in the filter.
A few noisy beats were manually realigned.
The result is that annotations generally appear at the R-wave peak, and
are located with sufficient accuracy to make the reference annotation
files usable for studies requiring waveform averaging and for
heart rate variability studies (but note the comments
with respect to analog tape wow and flutter above).
In the annotated ECG plots in this book, each label is placed so that the
fiducial mark for the annotation corresponds to the left edge of the label.
.PP
The database contains approximately 109,000 beat labels. Sixteen
have been corrected in the eight years since the database was first
released (in records 104, 108, 114, 203, 207, 217, and 222); in addition,
all of the left bundle branch block beats in record 214 were originally
labelled as normal beats. The rhythm labels
have been more substantially revised and now include notations for paced
rhythm, bigeminy, and trigeminy, which were missing in early copies.
.ds RH S\s-2YMBOLS\s+2
.bp
.ce 1
\fB\s+2SYMBOLS USED IN ECG PLOTS\s-2\fR
.TS
center box;
c | lw(4.5i).
\fISymbol Meaning\fR
_
\(bu Normal beat
L Left bundle branch block beat
R Right bundle branch block beat
A Atrial premature beat
a Aberrated atrial premature beat
J Nodal (junctional) premature beat
S Supraventricular premature beat
V Premature ventricular contraction
F Fusion of ventricular and normal beat
[ Start of ventricular flutter/fibrillation
! Ventricular flutter wave
] End of ventricular flutter/fibrillation
e Atrial escape beat
j Nodal (junctional) escape beat
E Ventricular escape beat
P Paced beat
f Fusion of paced and normal beat
p Non-conducted P-wave (blocked APB)
Q Unclassifiable beat
| Isolated QRS-like artifact
_
T{
Rhythm annotations appear \fIbelow\fP the level used for beat
annotations:
T}
(AB Atrial bigeminy
(AFIB Atrial fibrillation
(AFL Atrial flutter
(B Ventricular bigeminy
(BII 2\(de heart block
(IVR Idioventricular rhythm
(N Normal sinus rhythm
(NOD Nodal (A-V junctional) rhythm
(P Paced rhythm
(PREX Pre-excitation (WPW)
(SBR Sinus bradycardia
(SVTA Supraventricular tachyarrhythmia
(T Ventricular trigeminy
(VFL Ventricular flutter
(VT Ventricular tachycardia
_
T{
Signal quality and comment annotations appear \fIabove\fR the level
used for beat annotations:
T}
\fIqq\fR T{
Signal quality change: the first character (`c' or `n') indicates the quality
of the upper signal (clean or noisy), and the second character indicates the
quality of the lower signal
T}
U Extreme noise or signal loss in both signals: ECG is unreadable
M Missed beat (`MISSB' in examples, pp. 99\-177)
P Pause (`PSE' in examples)
T Tape slippage (`TS' in examples)
.TE
@@ -0,0 +1,216 @@
# file: makefile G. Moody 22 August 1988
# Last revised: 19 May 1997
# 'make' description file for printing MIT-BIH Arrhythmia Database Directory
#
# Copyright(C) Massachusetts Institute of Technology 1997. All rights reserved.
# This file is used with the UNIX `make' command to print a copy of the MIT-BIH
# Arrhythmia Database Directory. To do so, you will need a PostScript printer,
# the GNU `gzip' utility (see ../../software/gzip124.tar), `pschart' and `psfd'
# (sources are in `../../software/db/app'), about 220 sheets of paper, and
# about 15 Mb for working space in the file system to which TMP (see below)
# belongs. The directory containing the MIT-BIH Arrhythmia Database must be
# named in the DB environment variable.
# If your printer is an Apple LaserWriter or equivalent, it will take about 10
# hours to print the entire directory (and you will need to reload the paper
# tray after about 5 hours). In theory, at least, a Linotronic phototypesetter
# should be able to do the job in about 20 hours (send me a copy of the output
# if you try this). Using a Sun SPARCprinter, the directory can be printed in
# about 45 minutes. Your printer will not be busy throughout this period; it
# will take a significant amount of time to generate the PostScript files to be
# printed.
# To recreate the compressed PostScript files (for example, if you wish to
# change the page format), you will also need `troff' and the `ms' macro
# package, `tbl', a troff-to-PostScript translator (e.g., GNU `groff' or Adobe
# `ptroff'), the System V `cut' command, and a TeX DVI-to-PostScript translator
# (such as `dvips', included in the UNIX TeX distribution).
# Many (perhaps all) versions of the troff `ms' macros will generate a blank
# page at the beginning or end of each section. Be sure to pull out the blank
# sheets from the printer output before trying to make two-sided photocopies.
# (Look for them between pages viii and 1, 97 and 98, 177 and 178, 188 and 189,
# and 193 and 194; there should be a blank sheet following the table of
# contents, however.) If your printer is capable of two-sided printing, I
# suggest printing each section separately; note in particular that the
# section of full disclosure plots begins on a left-hand page (so that each
# record can be viewed in its entirety without turning the page).
# Before using this file for the first time, check that the site-specific
# variables below are appropriate for your system. If you need to change any
# of them, you may either:
# 1. copy this entire directory to a writable file system, and edit this
# file, or
# 2. use command-line options to reset variables when running `make' (for
# example, `make "PRINT=bogoprint -with -lots -of -options" ...' resets
# the value of the PRINT variable; see your `make' documentation)
# Print the test page by typing `make test'; if you get text rather than a
# plot, add the `-u' option to variables CO and FO (see below) and try again.
# Once you have successfully printed the test page, print the directory, by
# typing `make directory'.
# Printed copies of the directory may also be obtained from MIT (see file
# `../../ORDER.4M' for information).
# Site-specific variables.
# PRINT is the command that prints Postscript files. If possible, avoid
# copying the files to a spooling directory -- this will save a little time
# and a lot of space.
PRINT = lpr -s
# TMP is the name of a directory in which temporary files can be written.
# The full disclosure plots and the examples are each about 7 Mb; make sure
# that TMP's file system has at least 15 Mb free if you are attempting to
# print the entire directory in one operation.
TMP = /tmp
# CO specifies options for `pschart'. You might want to remove the `-e' option
# if you don't intend to make two-sided copies. Add a `-d' option if your
# printer's resolution is higher than 300 dpi (e.g., add `-d 600' for a 600 dpi
# printer). Some UNIX print spoolers cannot deal properly with `structured'
# PostScript -- if the test page contains text rather than plots, add a `-u'
# option (and be prepared to re-order the output if your printer delivers it
# printed side down). See the `man' page for `pschart' (in
# `../../software/db/doc/pschart.1', or the ECG Database Applications Guide)
# for details.
CO = -b 5 -e -g -l -r -R -V
# FO specifies options for `psfd'. As above, you might want to remove
# the `-e' option, or add a `-d' or `-u' option.
FO = -b 5 -e -g -l -R -V -x -1
# Uncomment the next line if your printer stacks output printed side up.
SECTIONS = index tables notes ex extext fd fdtext intro contents title cover
# Uncomment the next line if your printer stacks output printed side down.
#SECTIONS = cover title contents intro fdtext fd extext ch notes tables index
# .............................................................................
# Ignore these variables unless you want to regenerate the compressed
# PostScript files in this directory.
# DVITPS is the name of the TeX DVI-to-PostScript translator.
# `make title' only).
DVITPS = dvitps
# TROFF is the name of the troff-to-PostScript translator.
TROFF = ptroff -t
# TMS is the TROFF option needed to load the `ms' macro package. This should
# not need to be changed unless your system is non-standard.
TMS = -ms
# It should not be necessary to modify anything below this line.
# -----------------------------------------------------------------------------
ZFILES = cover.gz title.gz contents.gz intro.gz fdtext.gz extext.gz \
notes.gz tables.gz index.gz
.IGNORE:
# `make' or `make explain': describe this file
explain:
@echo If you have a PostScript printer, you can print a copy of the
@echo MIT-BIH Arrhythmia Database Directory by typing:
@echo " make directory"
@echo Before attempting to doing so, read "'Makefile'" carefully.
# `make test': print a test page
test:
pschart $(CO) -n 999 -T "Test Page" extest >$(TMP)/ex.ps
$(PRINT) $(TMP)/ex.ps
# `make directory': print the entire directory
directory: $(SECTIONS) clean
# `make psz': regenerate compressed PostScript files
psz: $(ZFILES)
# `make' targets for printing each section of the directory
cover: # outside cover
gzip -d <cover.gz | $(PRINT)
cover.gz: cover.tr
$(TROFF) cover.tr | gzip >cover.gz
title: # title page, copyright notice
gzip -d <title.gz | $(PRINT)
title.gz: title.dvi
dvips title.dvi | gzip >title.gz
$(DVIPRINT) title.dvi
contents: # table of contents
gzip -d <contents.gz | $(PRINT)
contents.gz: contents.tr
tbl contents.tr | $(TROFF) | gzip >contents.gz
intro: # foreword, introduction, table of symbols
gzip -d <intro.gz | $(PRINT)
intro.gz:
tbl intro.tr | $(TROFF) $(TMS) | gzip >intro.gz
fdtext: # introduction to section of full disclosures
gzip -d <fdtext.gz | $(PRINT)
fdtext.gz: fdtext.tr
$(TROFF) $(TMS) fdtext.tr | gzip >fdtext.gz
fd: # annotated full disclosure plots
psfd $(FO) -n 2 -T "MIT-BIH Arrhythmia Database" fdlist >$(TMP)/fd.ps
$(PRINT) $(TMP)/fd.ps
extext: # introduction to section of example strips
gzip -d <extext.gz | $(PRINT)
extext.gz: extext.tr
$(TROFF) $(TMS) extext.tr | gzip >extext.gz
ex: # annotated example strips
pschart $(CO) -n 99 -T Examples exlist >$(TMP)/ex.ps
$(PRINT) $(TMP)/ex.ps
notes: # demographic and statistical summaries
gzip -d <notes.gz | $(PRINT)
notes.gz: $(TMP)/notes.tr
tbl $(TMP)/notes.tr | $(TROFF) $(TMS) | gzip >notes.gz
$(TMP)/notes.tr: $(TMP)/dbnotes # generate tbl/troff file of notes
cp notes.h $(TMP)/notes.tr
chmod u+w $(TMP)/notes.tr
domit "$(TMP)/dbnotes atruth" >>$(TMP)/notes.tr
$(TMP)/dbnotes: dbnotes.c # notes file generating program
cp dbnotes.c $(TMP)
cd $(TMP); cc -o dbnotes -O dbnotes.c -ldb
rm -f $(TMP)/dbnotes.c
tables: # tables of beats and rhythms
gzip -d <tables.gz | $(PRINT)
tables.gz: $(TMP)/tables.tr
tbl $(TMP)/tables.tr | $(TROFF) $(TMS) | gzip >tables.gz
$(TMP)/tables.tr: $(TMP)/dbtab # generate tbl/troff file of tables
sh ./domit "$(TMP)/dbtab atruth" >$(TMP)/dbt.out
cp tab.1 $(TMP)/tables.tr
chmod u+w $(TMP)/tables.tr
cut -f1-18 <$(TMP)/dbt.out >>$(TMP)/tables.tr
cat tab.2 >>$(TMP)/tables.tr
cut -f1,19-33 <$(TMP)/dbt.out | tr -d " " >>$(TMP)/tables.tr
sh ./domit "$(TMP)/dbtab atruth" -t >$(TMP)/dbt.out
cat tab.3 >>$(TMP)/tables.tr
cut -f1-18 <$(TMP)/dbt.out >>$(TMP)/tables.tr
cat tab.4 >>$(TMP)/tables.tr
cut -f1,19-33 <$(TMP)/dbt.out | tr -d " " >>$(TMP)/tables.tr
cat tab.5 >>$(TMP)/tables.tr
rm -f $(TMP)/dbt.out
$(TMP)/dbtab: dbtab.c # table generating program
cp dbtab.c $(TMP)
cd $(TMP); cc -o dbtab -O dbtab.c -ldb
rm -f $(TMP)/dbtab.c
index: # index of full disclosures, examples, notes
gzip -d <index.gz | $(PRINT)
index.gz:
tbl index.tr | $(TROFF) $(TMS) | gzip >index.gz
# `make clean': remove output files from TMP
clean:
cd $(TMP); rm -f fd.ps ex.ps notes.tr tables.tr dbnotes dbtab
@@ -0,0 +1,222 @@
# file: makefile G. Moody 22 August 1988
# Last revised: 19 May 1997
# 'make' description file for printing MIT-BIH Arrhythmia Database Directory
#
# Copyright(C) Massachusetts Institute of Technology 1997. All rights reserved.
# This file is used with the UNIX `make' command to print a copy of the MIT-BIH
# Arrhythmia Database Directory. To do so, you will need a PostScript printer,
# the GNU `gzip' utility (see ../../software/gzip124.tar), `pschart' and `psfd'
# (sources are in `../../software/db/app'), about 220 sheets of paper, and
# about 15 Mb for working space in the file system to which TMP (see below)
# belongs. The directory containing the MIT-BIH Arrhythmia Database must be
# named in the DB environment variable.
# If your printer is an Apple LaserWriter or equivalent, it will take about 10
# hours to print the entire directory (and you will need to reload the paper
# tray after about 5 hours). In theory, at least, a Linotronic phototypesetter
# should be able to do the job in about 20 hours (send me a copy of the output
# if you try this). Using a Sun SPARCprinter, the directory can be printed in
# about 45 minutes. Your printer will not be busy throughout this period; it
# will take a significant amount of time to generate the PostScript files to be
# printed.
# To recreate the compressed PostScript files (for example, if you wish to
# change the page format), you will also need `troff' and the `ms' macro
# package, `tbl', a troff-to-PostScript translator (e.g., GNU `groff' or Adobe
# `ptroff'), the System V `cut' command, and a TeX DVI-to-PostScript translator
# (such as `dvips', included in the UNIX TeX distribution).
# Many (perhaps all) versions of the troff `ms' macros will generate a blank
# page at the beginning or end of each section. Be sure to pull out the blank
# sheets from the printer output before trying to make two-sided photocopies.
# (Look for them between pages viii and 1, 97 and 98, 177 and 178, 188 and 189,
# and 193 and 194; there should be a blank sheet following the table of
# contents, however.) If your printer is capable of two-sided printing, I
# suggest printing each section separately; note in particular that the
# section of full disclosure plots begins on a left-hand page (so that each
# record can be viewed in its entirety without turning the page).
# Before using this file for the first time, check that the site-specific
# variables below are appropriate for your system. If you need to change any
# of them, you may either:
# 1. copy this entire directory to a writable file system, and edit this
# file, or
# 2. use command-line options to reset variables when running `make' (for
# example, `make "PRINT=bogoprint -with -lots -of -options" ...' resets
# the value of the PRINT variable; see your `make' documentation)
# Print the test page by typing `make test'; if you get text rather than a
# plot, add the `-u' option to variables CO and FO (see below) and try again.
# Once you have successfully printed the test page, print the directory, by
# typing `make directory'.
# Printed copies of the directory may also be obtained from MIT (see file
# `../../ORDER.4M' for information).
# Site-specific variables.
# PRINT is the command that prints Postscript files. If possible, avoid
# copying the files to a spooling directory -- this will save a little time
# and a lot of space.
PRINT = lpr -s
# TMP is the name of a directory in which temporary files can be written.
# The full disclosure plots and the examples are each about 7 Mb; make sure
# that TMP's file system has at least 15 Mb free if you are attempting to
# print the entire directory in one operation.
TMP = .
# CO specifies options for `pschart'. You might want to remove the `-e' option
# if you don't intend to make two-sided copies. Add a `-d' option if your
# printer's resolution is higher than 300 dpi (e.g., add `-d 600' for a 600 dpi
# printer). Some UNIX print spoolers cannot deal properly with `structured'
# PostScript -- if the test page contains text rather than plots, add a `-u'
# option (and be prepared to re-order the output if your printer delivers it
# printed side down). See the `man' page for `pschart' (in
# `../../software/db/doc/pschart.1', or the ECG Database Applications Guide)
# for details.
CO = -b 5 -e -g -l -r -R -V
# FO specifies options for `psfd'. As above, you might want to remove
# the `-e' option, or add a `-d' or `-u' option.
FO = -b 5 -e -g -l -R -V -x -1
# Uncomment the next line if your printer stacks output printed side up.
SECTIONS = index tables notes ex extext fd fdtext intro contents title cover
# Uncomment the next line if your printer stacks output printed side down.
#SECTIONS = cover title contents intro fdtext fd extext ch notes tables index
# .............................................................................
# Ignore these variables unless you want to regenerate the compressed
# PostScript files in this directory.
# DVITPS is the name of the TeX DVI-to-PostScript translator.
# `make title' only).
DVITPS = dvitps
# TROFF is the name of the troff-to-PostScript translator.
TROFF = ptroff -t
# TMS is the TROFF option needed to load the `ms' macro package. This should
# not need to be changed unless your system is non-standard.
TMS = -ms
# It should not be necessary to modify anything below this line.
# -----------------------------------------------------------------------------
ZFILES = cover.gz title.gz contents.gz intro.gz fdtext.gz extext.gz \
notes.gz tables.gz index.gz
.IGNORE:
# `make' or `make explain': describe this file
explain:
@echo If you have a PostScript printer, you can print a copy of the
@echo MIT-BIH Arrhythmia Database Directory by typing:
@echo " make directory"
@echo Before attempting to doing so, read "'Makefile'" carefully.
# `make test': print a test page
test:
pschart $(CO) -n 999 -T "Test Page" extest >$(TMP)/ex.ps
$(PRINT) $(TMP)/ex.ps
# `make directory': print the entire directory
directory: $(SECTIONS) clean
# `make psz': regenerate compressed PostScript files
psz: $(ZFILES)
# `make' targets for printing each section of the directory
cover: # outside cover
gzip -d <cover.gz | $(PRINT)
cover.gz: cover.tr
$(TROFF) cover.tr | gzip >cover.gz
title: # title page, copyright notice
gzip -d <title.gz | $(PRINT)
title.gz: title.dvi
dvips title.dvi | gzip >title.gz
$(DVIPRINT) title.dvi
contents: # table of contents
gzip -d <contents.gz | $(PRINT)
contents.gz: contents.tr
tbl contents.tr | $(TROFF) | gzip >contents.gz
intro: # foreword, introduction, table of symbols
gzip -d <intro.gz | $(PRINT)
intro.gz:
tbl intro.tr | $(TROFF) $(TMS) | gzip >intro.gz
fdtext: # introduction to section of full disclosures
gzip -d <fdtext.gz | $(PRINT)
fdtext.gz: fdtext.tr
$(TROFF) $(TMS) fdtext.tr | gzip >fdtext.gz
fd: # annotated full disclosure plots
psfd $(FO) -n 2 -T "MIT-BIH Arrhythmia Database" fdlist >$(TMP)/fd.ps
$(PRINT) $(TMP)/fd.ps
extext: # introduction to section of example strips
gzip -d <extext.gz | $(PRINT)
extext.gz: extext.tr
$(TROFF) $(TMS) extext.tr | gzip >extext.gz
ex: # annotated example strips
pschart $(CO) -n 99 -T Examples exlist >$(TMP)/ex.ps
$(PRINT) $(TMP)/ex.ps
notes: # demographic and statistical summaries
gzip -d <notes.gz | $(PRINT)
notes.gz: $(TMP)/notes.tr
tbl $(TMP)/notes.tr | $(TROFF) $(TMS) | gzip >notes.gz
$(TMP)/notes.tr: $(TMP)/dbnotes # generate tbl/troff file of notes
cp notes.h $(TMP)/notes.tr
chmod u+w $(TMP)/notes.tr
domit "$(TMP)/dbnotes atruth" >>$(TMP)/notes.tr
$(TMP)/dbnotes: dbnotes.c # notes file generating program
cp dbnotes.c $(TMP)
cd $(TMP); cc -o dbnotes -O dbnotes.c -ldb
$(TMP)/notes.htm: $(TMP)/dbnotes # generate HTML file of notes
cp notes.ht0 $(TMP)/notes.htm
chmod u+w $(TMP)/notes.htm
domit "$(TMP)/dbnotes atruth" >>$(TMP)/notes.htm
$(TMP)/dbnotes: dbnotes.c # notes file generating program
cp dbnotes.c $(TMP)
cd $(TMP); cc -o dbnotes -O dbnotes.c -ldb
tables: # tables of beats and rhythms
gzip -d <tables.gz | $(PRINT)
tables.gz: $(TMP)/tables.tr
tbl $(TMP)/tables.tr | $(TROFF) $(TMS) | gzip >tables.gz
$(TMP)/tables.tr: $(TMP)/dbtab # generate tbl/troff file of tables
sh ./domit "$(TMP)/dbtab atruth" >$(TMP)/dbt.out
cp tab.1 $(TMP)/tables.tr
chmod u+w $(TMP)/tables.tr
cut -f1-18 <$(TMP)/dbt.out >>$(TMP)/tables.tr
cat tab.2 >>$(TMP)/tables.tr
cut -f1,19-33 <$(TMP)/dbt.out | tr -d " " >>$(TMP)/tables.tr
sh ./domit "$(TMP)/dbtab atruth" -t >$(TMP)/dbt.out
cat tab.3 >>$(TMP)/tables.tr
cut -f1-18 <$(TMP)/dbt.out >>$(TMP)/tables.tr
cat tab.4 >>$(TMP)/tables.tr
cut -f1,19-33 <$(TMP)/dbt.out | tr -d " " >>$(TMP)/tables.tr
cat tab.5 >>$(TMP)/tables.tr
rm -f $(TMP)/dbt.out
$(TMP)/dbtab: dbtab.c # table generating program
cp dbtab.c $(TMP)
cd $(TMP); cc -o dbtab -O dbtab.c -ldb
index: # index of full disclosures, examples, notes
gzip -d <index.gz | $(PRINT)
index.gz:
tbl index.tr | $(TROFF) $(TMS) | gzip >index.gz
# `make clean': remove output files from TMP
clean:
cd $(TMP); rm -f fd.ps ex.ps notes.tr tables.tr dbnotes dbtab
@@ -0,0 +1,38 @@
#!/usr/bin/perl
# file: makelinks.pl G. Moody 30 October 1996
#
# This program rewrites an HTML file of MIMIC database clinical data, adding
# links to wavescript .xws files (also generated here) to any flagged date/time
# stamps found in the input file. The `@' character should be placed before
# and after each date/time stamp in order to flag it for processing. The
# rewritten HTML appears on the standard output.
#
# Note: date/time stamps in the input should be in hh:mm:ss dd/mm/yyyy format.
while (<>) {
if (/\d{3} /) {
chop();
@fields = split(/ /, $_);
$record = shift(@fields);
$tstamp = shift(@fields);
if (! $tstamp =~ /\d/) {
$tstamp = shift(@fields);
}
@tparts = split(/:/, $tstamp);
$minute = shift(@tparts);
$second = shift(@tparts);
$xws = sprintf("samples/%3d%02d%02d.xws", $record, $minute, $second);
print "<a href=\"$xws\">$tstamp</a>";
while ($word = shift(@fields)) {
print " $word";
}
print "<br>\n";
open(XWS, ">$xws");
print XWS "-r $record\n";
print XWS "-a atr\n";
print XWS "-f $minute:$second\n";
close(XWS);
} else {
print;
}
}
@@ -0,0 +1,24 @@
.LP
.ds LH \s10MIT-BIH A\s8RRHYTHMIA\s10 D\s8ATABASE\s10
.ds RH \s10N\s8OTES\s10
.bp 178
.ce 1
\fB\s+2NOTES\s-2\fP
.PP
The following pages contain notes and statistics that describe the contents of
each record.
The leads used for the upper and lower signals are given for each record
immediately following the record number.
Beat counts are given for the first five minutes of each record and
the remainder of the record (the AAMI-defined ``learning'' and ``test''
periods, respectively).
Note that the totals include two types of non-beats (blocked APCs and
ventricular flutter waves) for those few records in which they occur.
The total duration of each record is 30 minutes and 5.556 seconds; durations
noted below are rounded to the nearest second, and may not sum to exactly
30:06 because of accumulated rounding error.
Heart rates are given in beats per minute measured over 3 R-R intervals.
.nr PS 8
.nr VS 10
.LP
.2C
@@ -0,0 +1,116 @@
@echo off
: file: printdir.bat G. Moody 11 August 1992
: MS-DOS batch file for printing the MIT-BIH Arrhythmia Database Directory
: Copyright(C) Massachusetts Institute of Technology 1992. All rights reserved.
echo If you have a PostScript printer, you can use this program to print a copy
echo of the MIT-BIH Arrhythmia Database Directory on it. It will take about 10
echo hours to print the entire 200-page directory. Make sure that the current
echo drive contains the MIT-BIH Arrhythmia Database CD-ROM, and that the TEMP
echo variable names a writable directory with at least 15 Mb free. The current
echo value of TEMP is %TEMP%.
echo If you want to do anything other than print the entire directory, see the
echo UNIX 'makefile' in this directory; it can be used with Microsoft or
echo Borland 'make' with only minor changes.
pause
echo This program needs to change the DB and PATH variables. When it finishes,
echo you may wish to reset them to their current values, which are
echo DB=%DB%
echo PATH=%PATH%
pause
: DB gets cleared out, so that 'psfd' and 'pschart' use their built-in default
: DB paths to find the records. This works only if the CD-ROM is in the
: current drive.
set DB=
: The 'bin' directory of the CD-ROM goes at the front of the new PATH, so that
: the correct versions of 'gzip', 'pschart', and 'psfd' will be found.
set PATH=\bin;%PATH%
echo OK, let's see if your printer can print a test page. This may take
echo up to 5 minutes ... when it's done, you should get a plot with a 12-second
echo ECG strip at the top of a (mostly empty) page. If you get lots of text
echo instead, you may need to modify this batch file. CHECK THE OUTPUT BEFORE
echo CONTINUING.
pschart -b 5 -e -g -l -r -R -V -n 999 -T "Test Page" extest >%TEMP%\ex.ps
print %TEMP%\ex.ps
pause
echo Now printing the directory ...
: outside cover
copy cover.z %TEMP%
gzip -d %TEMP%\cover
print %TEMP%\cover
: title page, copyright notice
copy title.z %TEMP%
gzip -d %TEMP%\title
print %TEMP%\title
: table of contents
copy contents.z %TEMP%
gzip -d %TEMP%\contents
print %TEMP%\contents
: foreword, introduction, table of symbols
copy intro.z %TEMP%
gzip -d %TEMP%\intro
print %TEMP%\intro
: introduction to section of full disclosures
copy fdtext.z %TEMP%
gzip -d %TEMP%\fdtext
print %TEMP%\fdtext
: annotated full disclosure plots
: If disk space is low, you might try sending the output directly to PRN
psfd -b 5 -e -g -l -R -V -x -1 -n 2 -T "MIT-BIH Arrhythmia Database" fdlist >%TEMP%\fd.ps
print %TEMP%\fd.ps
: introduction to section of example strips
copy extext.z %TEMP%
gzip -d %TEMP%\extext
print %TEMP%\extext
: annotated example strips
: If disk space is low, you might try sending the output directly to PRN
pschart -b 5 -e -g -l -r -R -V -n 99 -T Examples exlist >%TEMP%\ex.ps
print %TEMP%\ex.ps
: demographic and statistical summaries
copy notes.z %TEMP%
gzip -d %TEMP%\notes
print %TEMP%\notes
: tables of beats and rhythms
copy tables.z %TEMP%
gzip -d %TEMP%\tables
print %TEMP%\tables
: index of full disclosures, examples, notes
copy index.z %TEMP%
gzip -d %TEMP%\index
print %TEMP%\index
echo The entire directory has now been printed. Temporary files will be
echo removed from %TEMP% next.
pause
del %TEMP%\cover
del %TEMP%\title
del %TEMP%\contents
del %TEMP%\intro
del %TEMP%\fdtext
del %TEMP%\fd.ps
del %TEMP%\extext
del %TEMP%\ex.ps
del %TEMP%\notes
del %TEMP%\tables
del %TEMP%\index
@@ -0,0 +1,63 @@
.nr PS 10
.nr VS 12
.LP
.ds LH \s10MIT-BIH A\s8RRHYTHMIA\s10 D\s8ATABASE\s10
.ds RH \s10T\s8ABLES\s10
.bp 189
.1C
.DS
.DE
.ce 1
\fB\s12TABLES OF BEATS AND RHYTHMS\s10\fP
.PP
The following pages contain tables that summarize the contents of the
database.
These tables duplicate some of the contents of the previous
section (Notes), to which reference should be made for more details.
The first two tables list the beat types and rhythms for
each half-hour record in its entirety. The last two tables show the
same information, but for the AAMI-defined ``test periods'' only
(i.e., excluding the first five minutes of each record).
.PP
The column headings for the beat tables show the AHA annotation codes
(N, V, F, E, P, Q, and O) above the symbols used for the database annotations
elsewhere in this book.
A complete list of these symbols, as well as the abbreviations used as
the column headings for the rhythm tables, may be found on page viii.
.PP
The beat tables show the number of beats of each type in each record.
The rhythm tables show the total duration of each rhythm observed in each
record in minutes and seconds (rounded to the nearest second).
.bp
.ce 1
\fB\s12TABLE OF BEAT TYPES (ENTIRE RECORDS)\s10\fP
.nr PS 7
.nr VS 9
.LP
.TS
center box;
c | ce se se se se se se | ce | ce | ce | ce se | ce | ce | ce | ce | ce
c | ce ce ce ce ce ce ce ce ce ce ce ce ce ce ce ce ce
c | re re re re re re re re re re re re re re re re re.
\fIRecord\fP \fIN V F O N E P F O Q\fP
\^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
\^ \fB\(bu L R A a J S V F ! e j E P f p Q\fP
_
@@ -0,0 +1,12 @@
.TE
.bp
.ce 1
\fB\s12TABLE OF RHYTHMS (ENTIRE RECORDS)\s10\fP
.nr PS 7
.nr VS 9
.LP
.TS
center box;
c | re re re re re re re re re re re re re re re.
\fIRecord\fB N SBR BII PREX AB SVTA AFL AFIB P NOD B T IVR VT VFL\fR
_
@@ -0,0 +1,16 @@
.TE
.bp
.ce 1
\fB\s12TABLE OF BEAT TYPES (TEST PERIODS ONLY)\s10\fP
.nr PS 7
.nr VS 9
.LP
.TS
center box;
c | ce se se se se se se | ce | ce | ce | ce se | ce | ce | ce | ce | ce
c | ce ce ce ce ce ce ce ce ce ce ce ce ce ce ce ce ce
c | re re re re re re re re re re re re re re re re re.
\fIRecord\fP \fIN V F O N E P F O Q\fP
\^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
\^ \fB\(bu L R A a J S V F ! e j E P f p Q\fP
_
@@ -0,0 +1,12 @@
.TE
.bp
.ce 1
\fB\s12TABLE OF RHYTHMS (TEST PERIODS ONLY)\s10\fP
.nr PS 7
.nr VS 9
.LP
.TS
center box;
c | re re re re re re re re re re re re re re re.
\fIRecord\fB N SBR BII PREX AB SVTA AFL AFIB P NOD B T IVR VT VFL\fR
_
@@ -0,0 +1 @@
.TE