/* file: a2m.c G. Moody 9 June 1983
Last revised: 27 July 2010
-------------------------------------------------------------------------------
a2m: Convert an AHA format annotation file to MIT format
Copyright (C) 1983-2010 George B. Moody
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, see .
You may contact the author by e-mail (wfdb@physionet.org) or postal mail
(MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software,
please visit PhysioNet (http://www.physionet.org/).
_______________________________________________________________________________
This program converts four types of AHA format annotation files to MIT format:
Type Description
0 AHA-format annotation files produced by WFDB library-based
programs that use AHA_WRITE mode and `putann' (the `m2a'
translator is an example of such a program). Time is
measured in sample intervals from the beginning of the record,
and MIT annotation codes are included and read into the `chan'
field of the annotation by `getann'; if such codes are
present (i.e., if `chan' is non-zero), the AHA code is
ignored.
1 Standard AHA annotation files from short-format AHA DB
distribution tapes. Time is measured in milliseconds from
the beginning of the test period (five minutes, or 75000 sample
intervals at 4 ms/sample interval, after the first sample).
2 Standard AHA annotation files from long-format AHA DB
distribution tapes. Time is measured as for filetype 1,
except that the test period begins 2.5 hours (2,225,000 sample
intervals) after the first sample.
3 `Compressed' (*.ANO) annotation files from AHA DB CD-ROMs or floppy
diskettes. Time is measured as for files of type 1 or 2, depending
on the record name (x2xx or x3xx: short format, x0xx or x1xx: long
format).
*/
#include
#ifdef __STDC__
#define RB "rb"
#else
#ifdef MSDOS
#define RB "rb"
#else
#define RB "r"
extern void exit();
#endif
#endif
#include
#define isqrs
#define map1
#define map2
#define mamap
#define annpos
#include
char *pname;
main(argc, argv)
int argc;
char *argv[];
{
char *ifname = NULL, *oann = "atr", *p, *record = NULL, *prog_name();
int i, type = -1, getcann();
long offset = 0L;
unsigned int nann = 2;
WFDB_Anninfo afarray[2];
static WFDB_Annotation annot;
void help();
/* Read and interpret command-line arguments. */
pname = prog_name(argv[0]);
for (i = 1; i < argc; i++) {
if (*argv[i] == '-') switch (*(argv[i]+1)) {
case 'a': /* annotator name follows */
if (++i >= argc) {
(void)fprintf(stderr,
"%s: output annotator name must follow -a\n",
pname);
exit(1);
}
oann = argv[i];
break;
case 'h': /* help requested */
help();
exit(1);
break;
case 'i': /* input file name follows */
if (++i >= argc) {
(void)fprintf(stderr,
"%s: name of AHA-format input file must follow -i\n",
pname);
exit(1);
}
ifname = argv[i];
p = ifname + strlen(ifname) - 4;
if (type < 0 && p > ifname &&
(strcmp(p, ".ANO") == 0 || strcmp(p, ".ano") == 0))
type = 3;
break;
case 'r': /* record name follows */
if (++i >= argc) {
(void)fprintf(stderr, "%s: record name must follow -a\n",
pname);
exit(1);
}
record = argv[i];
break;
case 's': /* time shift follows */
if (++i >= argc) {
(void)fprintf(stderr, "%s: time shift must follow -s\n",
pname);
exit(1);
}
offset = i; /* save position in arg list, convert to time
later when sampling frequency is known */
break;
case 't': /* file type follows */
if (++i >= argc || (type = atoi(argv[i])) < 0 || type > 3) {
(void)fprintf(stderr, "%s: file type (0-3) must follow -t\n",
pname);
exit(1);
}
break;
default:
(void)fprintf(stderr, "%s: unrecognized option %s\n", pname,
argv[i]);
exit(1);
}
else {
(void)fprintf(stderr, "%s: unrecognized argument %s\n", pname,
argv[i]);
exit(1);
}
}
/* Quit if input filename was not specified. */
if (ifname == NULL) {
help();
exit(1);
}
/* Attach the input file to the standard input. */
if (strcmp(ifname, "-") != 0 && freopen(ifname, RB, stdin) == NULL) {
(void)fprintf(stderr, "%s: can't read input file `%s'\n", pname,
ifname);
exit(2);
}
/* If no output record name was specified, use the basename of the input
file (prog_name strips off any path info from ifname, and any extension
is stripped off in the 'for' loop. */
if (record == NULL) {
record = prog_name(ifname);
for (p = record+1; *p; p++)
if (*p == '.') {
*p = '\0';
break;
}
}
if (type < 0) type = 0;
/* Open the annotation files. */
afarray[0].name = oann;
afarray[0].stat = WFDB_WRITE;
if (type != 3) {
afarray[1].name = "-";
afarray[1].stat = WFDB_AHA_READ;
}
else
nann = 1;
if (annopen(record, afarray, nann) < 0)
exit(2);
/* Set the time shift. */
if (sampfreq(record) == 0.)
(void)setsampfreq(250.);
if (offset) offset = strtim(argv[offset]);
else {
switch (type) {
case 0:
break;
case 1:
offset = strtim("5:0");
break;
case 2:
offset = strtim("2:30:0");
break;
case 3:
if (strlen(record) == 4 && (record[1] == '0' || record[1] == '1'))
offset = strtim("2:30:0"); /* long format */
else if (strlen(record) == 4 && (record[1] =='2'||record[1]=='3'))
offset = strtim("5:0"); /* short format */
/* otherwise, leave offset at zero (unrecognized format) */
break;
}
}
/* Reformat the annotations. */
switch (type) {
case 0:
while (getann(0, &annot) >= 0) {
if (annot.chan) { /* MIT annotation code present */
annot.anntyp = annot.chan;
annot.chan = 0;
}
annot.time += offset;
if (putann(0, &annot) < 0)
break;
}
break;
case 1:
case 2:
while (getann(0, &annot) >= 0) {
annot.time = (annot.time >> 2) + offset; /* 4 msec/sample */
(void)putann(0, &annot);
}
break;
case 3:
while (getcann(&annot) >= 0) {
annot.time = (annot.time >> 2) + offset; /* 4 msec/sample */
(void)putann(0, &annot);
}
break;
}
wfdbquit();
exit(0); /*NOTREACHED*/
}
int getcann(ap)
WFDB_Annotation *ap;
{
char buf[6];
long h, m, l;
if (fread(buf, 1, 6, stdin) != 6) { /* hard EOF */
/* (void)fprintf(stderr, "unexpected EOF in input file\n"); */
/* The warning message was removed because the CD-ROM version of these
files does not include the soft EOF as on the floppies. */
return (-1);
}
/* Compressed AHA format:
buf[0] AHA label (`N', `V', `F', `R', `E', `P', `Q', `[', `]', or `U')
buf[1] high byte of ahatime, or 0xff if at soft EOF
buf[2] middle byte of ahatime
buf[3] low byte of ahatime
buf[4] high byte of annotation serial number (ignored here)
buf[5] low byte of serial number
*/
if ((unsigned)(buf[1] & 0xff) == 0xff) /* soft EOF */
return (-1);
ap->anntyp = ammap(buf[0]); /* convert AHA label to annotation code */
if (ap->anntyp == 0)
return (-1); /* alternate soft EOF, used in a few AHA
DB CD-ROM annotation files */
if (buf[0] == 'U') /* AHA `U' (unreadable data) label */
ap->subtyp = -1; /* decode as NOISE, subtype = -1 */
else
ap->subtyp = 0; /* all others have subtype = 0 */
h = (long)buf[1] & 0xff;
m = (long)buf[2] & 0xff;
l = (long)buf[3] & 0xff;
ap->time = (h << 16) | (m << 8) | l; /* time in milliseconds from the
start of the annotated segment */
return (0);
}
char *prog_name(s)
char *s;
{
char *p = s + strlen(s);
#ifdef MSDOS
while (p >= s && *p != '\\' && *p != ':') {
if (*p == '.')
*p = '\0'; /* strip off extension */
if ('A' <= *p && *p <= 'Z')
*p += 'a' - 'A'; /* convert to lower case */
p--;
}
#else
while (p >= s && *p != '/')
p--;
#endif
return (p+1);
}
static char *help_strings[] = {
"usage: %s -i AHAFILE [ OPTIONS ... ]\n",
"where AHAFILE is the name of the AHA-format input annotation file,",
"and OPTIONS may include:",
" -a ANNOTATOR specify the output annotator name (default: 'atr')",
" -h print this usage summary",
" -r RECORD specify the name of the output RECORD (default: use the",
" input file name, less path info and extension, as RECORD)",
" -s TIME shift annotations by TIME",
" (defaults: 0 for TYPE 0,",
" 5:0 for TYPE 1, (or TYPE 3 if RECORD=x2xx or x3xx)",
" 2:30:0 for TYPE 2, (or TYPE 3 if RECORD=x0xx or x1xx)",
" -t TYPE specify type of file to be converted:",
" 0 for files produced by WFDB applications in AHA format,",
" 1 for short-format tape files,",
" 2 for long-format tape files, or",
" 3 for compressed files from AHA DB CD-ROMs or diskettes",
" (defaults: 3 if AHAFILE ends in .ANO or .ano, else 0)",
NULL
};
void help()
{
int i;
(void)fprintf(stderr, help_strings[0], pname);
for (i = 1; help_strings[i] != NULL; i++)
(void)fprintf(stderr, "%s\n", help_strings[i]);
}