/* file: distill.c G. Moody 12 April 2000 This program "distills" a set of annotation files for the CinC Challenge 2000 in one of two ways: 1. Using the -s option, it classifies the records as 'A' (containing at least 100 minutes of detected apnea), 'B' (containing between 5 and 99 minutes of detected apnea), or 'C' (containing between 0 and 4 minutes of detected apnea). In this case, the output contains one line for each record; each line contains the record name, followed by a space, followed by the classification ('A', 'B', or 'C'). The output obtained in this way for a set of annotation files for the training set (x01, x02, ..., x35) can be submitted to obtain a score for event 1 (apnea screening). Note that this is provided as a convenience only. You may use any algorithm you wish to classify records for event 1 -- it is not necessary to follow the criteria described above, and it is not necessary even to have created a set of annotation files. If you do not use this program to prepare your submission for event 1, download: http://www.physionet.org/physiobank/database/apnea-ecg/challenge/template-test-1 and change the '?' characters to 'A', 'B', or 'C' as appropriate based on your classifications. Submit the edited version of this template to obtain your scores. 2. If the -s option is omitted, it produces a condensed minute-by-minute account of each annotation file on the standard output. For each record, it prints an empty line, followed by the record name on a line by itself, then prints a line of data for each hour in the record. Each data line begins with an hour number and is followed by a space and 60 characters that indicate for each minute if apnea was detected ('A') or not ('N'). The output obtained in this way for a set of annotation files for the training set (x01, x02, ..., x35) can be submitted to obtain a score for event 2 (quantitative assessment of apnea). Important: The input annotation files should contain N (anntyp = 1) or A (anntyp = 8) annotations at one-minute (6000-sample) intervals, beginning at sample 0. Any other annotations are ignored. If neither an N nor an A annotation is found where one is expected, an 'X' is recorded in the output; this will be treated as an error when scores are calculated for event 2. Note that you do not need to use this program to enter event 2, either. If you do not, download: http://www.physionet.org/physiobank/database/apnea-ecg/challenge/template-test-2 and change the '?' characters to 'A' or 'N' as appropriate based on your classifications. Submit the edited version of this template to obtain your scores. Compile this program using the WFDB library (which may be downloaded from http://www.physionet.org/physiotools/wfdb.shtml): cc -o distill distill.c -lwfdb This program has been used to produce the summaries that can be downloaded from http://www.physionet.org/physiobank/database/apnea-ecg/challenge/. Within that directory, summary-training-1 was produced using the command distill -a apn -s a01 a02 a03 a04 a05 a06 a07 a08 a09 a10 \ a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 \ b01 b02 b03 b04 b05 \ c01 c02 c03 c04 c05 c06 c07 c08 c09 c10 >summary-training-1 and summary-training-2 was produced using the command distill -a apn a01 a02 a03 a04 a05 a06 a07 a08 a09 a10 \ a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 \ b01 b02 b03 b04 b05 \ c01 c02 c03 c04 c05 c06 c07 c08 c09 c10 >summary-training-2 Similar files were produced for the test set (x01 ... x35); these will be posted after the conclusion of the competition in September. The template files that can be found in the same directory were produced from the summary files by translating all of the 'A', 'B', 'C', and 'N' characters into '?' characters, using the UNIX 'tr' utility: tr ABCN \? template-training-1 etc. */ #include #include /* The definitions below were chosen for convenience, because the mnemonic for a type 1 annotation is "N" and for a type 8 annotation is "A" (see for details if you're curious). */ #define NOAPNEA 1 #define APNEA 8 char *pname; int sflag; WFDB_Anninfo ai; main(int argc, char **argv) { int i; void distill(), help(); pname = argv[0]; if (argc < 4) help(); /* Interpret command-line options. */ for (i = 1; i < argc; i++) { if (*argv[i] == '-') switch (*(argv[i]+1)) { case 'a': /* annotator follows */ if (++i >= argc) { (void)fprintf(stderr, "%s: annotator must follow -a\n", pname); exit(1); } ai.name = argv[i]; ai.stat = WFDB_READ; break; case 'h': /* print help and quit */ help(); break; case 's': /* short mode */ sflag = 1; break; default: (void)fprintf(stderr, "%s: unrecognized option %s\n", pname, argv[i]); exit(1); } else /* argument must be a record name */ distill(argv[i]); } exit(0); } void help() { fprintf(stderr, "Usage:\n" " %s -a ANNOTATOR [-s] RECORD1 [RECORD2 ...]\n" "where ANNOTATOR is the annotator name (suffix) of the files" " to be 'distilled'\n", "and RECORD1, RECORD2, etc. are the record names for these" " files.\n", pname); exit(1); } void distill(char *record) { int apnea_minutes = 0, hours = -1, minutes = 60; WFDB_Annotation annot; WFDB_Time texpected = 0L; if (ai.name == NULL) help(); printf("%s", record); if (annopen(record, &ai, 1) < 0) { printf(" [no annotations]\n"); return; } while (getann(0, &annot) == 0) { if (annot.time < texpected) continue; /* ignore extra annotations */ while (texpected < annot.time) { /* fill in for missing annotations */ texpected += 6000L; /* one minute at 100 samples/second */ if (!sflag) { if (++minutes >= 60) { printf("\n%2d ", ++hours); minutes = 0; } printf("X"); } } if (annot.time == texpected) { /* a valid annotation -- record it */ texpected += 6000L; /* one minute at 100 samples/second */ if (!sflag) { if (++minutes >= 60) { printf("\n%2d ", ++hours); minutes = 0; } if (annot.anntyp == NOAPNEA) printf("N"); else if (annot.anntyp == APNEA) printf("A"); else printf("X"); } else if (annot.anntyp == APNEA) apnea_minutes++; } } if (sflag) { /* classify the record */ if (apnea_minutes >= 100) printf(" A"); else if (apnea_minutes >= 5) printf(" B"); else printf(" C"); } printf("\n"); if (!sflag) printf("\n"); }