/* file: edf2mit.c G. Moody 16 October 1996 Last revised: 18 November 2008 ------------------------------------------------------------------------------- Convert EDF (European Data Format) file to MIT format header and signal files Copyright (C) 1996-2004 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/). _______________________________________________________________________________ */ #include #include char *pname; main(argc, argv) int argc; char **argv; { char buf[81], record[WFDB_MAXRNL+1], **vi, **vin; double *sigpmax, *sigpmin, *sampfreq, spr, sps, gcd(); FILE *ifile = NULL; int big_endian = 0, fpb, h, i, j, k, l, nsig, nosig = 0, *siglist = NULL, *spb, tspb = 0, ispf = 0, tspf = 0, vflag = 0, day, month, year, hour, minute, second; long adcrange, *sigdmax, *sigdmin; WFDB_Sample *vo, *vout; WFDB_Siginfo *si, *so; void help(); pname = argv[0]; record[0] = '\0'; for (i = 1; i < argc; i++) { if (*argv[i] == '-') switch (argv[i][1]) { case 'b': /* input is in big-endian byte order */ big_endian = 1; break; case 'h': /* show usage and quit */ help(); exit(0); case 'i': /* file name follows */ if (++i < argc) { if (!strcmp(argv[i], "-")) ifile = stdin; else { ifile = fopen(argv[i], "rb"); if (ifile == NULL) { fprintf(stderr, "%s: can't read %s\n", pname, argv[i]); exit(1); } if (record[0] == 0) { for (j = 0; j < WFDB_MAXRNL; j++) { char c = argv[i][j]; if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) record[j] = c; else if (c == '.') break; else record[j] = '_'; } record[j] = '\0'; } } } break; case 'r': /* record name follows */ if (++i < argc) strncpy(record, argv[i], WFDB_MAXRNL); else { fprintf(stderr, "%s: record name must follow -r\n", pname); exit(1); } break; case 's': /* signal list follows */ /* count the number of output signals */ for (j = 0; ++i < argc && *argv[i] != '-'; j++) ; if (j == 0) { (void)fprintf(stderr, "%s: signal list must follow -s\n", pname); exit(1); } /* allocate storage for the signal list */ if ((siglist=realloc(siglist, (nosig+j) * sizeof(int))) == NULL) { (void)fprintf(stderr, "%s: insufficient memory\n", pname); exit(2); } /* fill the signal list */ for (i -= j; i < argc && *argv[i] != '-'; ) siglist[nosig++] = atoi(argv[i++]); i--; break; case 'v': /* select verbose mode */ vflag = 1; break; } } if (ifile == NULL) { help(); exit(1); } if (record[0] == '\0' || newheader(record) < 0) { fprintf(stderr, "\n"); help(); (void)fprintf(stderr, "\nRun %s again, specifying a valid name for the output record,\n", argv[0]); (void)fprintf(stderr, "using the -r option as described above.\n\n"); (void)fprintf(stderr, " Valid record names may include letters, digits, and underscores only.\n"); exit(1); } fread(buf, 1, 8, ifile); /* Check to see that the input is an EDF file. (This check will detect most but not all other types of files.) */ if (strncmp(buf, "0 ", 8)) { (void)fprintf(stderr, "%s: input does not appear to be EDF -- no conversion attempted.\n", pname); exit(2); } buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("EDF version number: '%s'\n", buf); fread(buf, 1, 80, ifile); buf[80] = ' '; for (j = 80; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Patient ID: '%s'\n", buf); fread(buf, 1, 80, ifile); buf[80] = ' '; for (j = 80; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Recording ID: '%s'\n", buf); fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Recording date: '%s'\n", buf); sscanf(buf, "%d%*c%d%*c%d", &day, &month, &year); if (year < 1970) year += 1900; if (year < 1970) year += 100; /* should work for a while */ fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Recording time: '%s'\n", buf); sscanf(buf, "%d%*c%d%*c%d", &hour, &minute, &second); fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Number of bytes in header record: '%s'\n", buf); fread(buf, 1, 44, ifile); buf[44] = ' '; for (j = 44; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (j > 0) if (vflag) printf("Free space: '%s'\n", buf); fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Number of data records: '%s'\n", buf); fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Duration of each data record in seconds: '%s'\n", buf); sscanf(buf, "%lf", &spr); if (spr <= 0.0) spr = 1.0; fread(buf, 1, 4, ifile); buf[4] = ' '; for (j = 4; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Number of signals: '%s'\n", buf); sscanf(buf, "%d", &nsig); if (nsig < 1) exit(1); if ((si = calloc(nsig, sizeof(WFDB_Siginfo))) == NULL || (spb = calloc(nsig, sizeof(int))) == NULL || (sigdmax = calloc(nsig, sizeof(long))) == NULL || (sigdmin = calloc(nsig, sizeof(long))) == NULL || (sigpmax = calloc(nsig, sizeof(double))) == NULL || (sigpmin = calloc(nsig, sizeof(double))) == NULL || (sampfreq = calloc(nsig, sizeof(double))) == NULL) { fprintf(stderr, "%s: insufficient memory\n", pname); exit(2); } if (nosig == 0) { /* initialize signal list if necessary */ if ((siglist = malloc(nsig * sizeof(int))) == NULL) { fprintf(stderr, "%s: insufficient memory\n", pname); exit(2); } for ( ; nosig < nsig; nosig++) siglist[nosig] = nosig; } else { for (i = 0; i < nosig; i++) { if (siglist[i] < 0 || siglist[i] >= nsig) { fprintf(stderr, "%s: invalid signal number, %d, in signal list\n", pname, siglist[i]); exit(1); } } } if ((so = malloc(nosig * sizeof(WFDB_Siginfo))) == NULL) { fprintf(stderr, "%s: insufficient memory\n", pname); exit(2); } for (i = 0; i < nsig; i++) { fread(buf, 1, 16, ifile); buf[16] = ' '; for (j = 15; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d label: '%s'\n", i, buf); si[i].desc = (char *)malloc(strlen(buf)+1); if (si[i].desc) strcpy(si[i].desc, buf); } for (i = 0; i < nsig; i++) { fread(buf, 1, 80, ifile); buf[80] = ' '; for (j = 80; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag && j > 0) printf("Signal %d transducer type: '%s'\n", i, buf); } for (i = 0; i < nsig; i++) { fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d physical dimension: '%s'\n", i, buf); si[i].units = (char *)malloc(strlen(buf)+1); if (si[i].units) strcpy(si[i].units, buf); } for (i = 0; i < nsig; i++) { fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d physical minimum: '%s'\n", i, buf); sscanf(buf, "%lf", &sigpmin[i]); } for (i = 0; i < nsig; i++) { fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d physical maximum: '%s'\n", i, buf); sscanf(buf, "%lf", &sigpmax[i]); } for (i = 0; i < nsig; i++) { fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d digital minimum: '%s'\n", i, buf); sscanf(buf, "%ld", &sigdmin[i]); } for (i = 0; i < nsig; i++) { fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d digital maximum: '%s'\n", i, buf); sscanf(buf, "%ld", &sigdmax[i]); } for (i = 0; i < nsig; i++) { if (sigpmax[i] == sigpmin[i]) { fprintf(stderr, "gain for signal %d is undefined\n", i); continue; } si[i].adczero = (sigdmax[i]+1 + sigdmin[i])/2; adcrange = sigdmax[i] - sigdmin[i]; si[i].gain = adcrange/(sigpmax[i] - sigpmin[i]); for (j = 0; adcrange > 1; j++) adcrange /= 2; si[i].adcres = j; si[i].baseline = sigdmax[i] - sigpmax[i]*si[i].gain + 1; } for (i = 0; i < nsig; i++) { fread(buf, 1, 80, ifile); buf[80] = ' '; for (j = 80; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag && j > 0) printf("Signal %d filtering information: '%s'\n", i, buf); } sps = 0.0; for (i = 0; i < nsig; i++) { fread(buf, 1, 8, ifile); buf[8] = ' '; for (j = 8; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag) printf("Signal %d number of samples per record: '%s'\n", i, buf); sscanf(buf, "%lf", &sampfreq[i]); sscanf(buf, "%d", &spb[i]); tspb += spb[i]; sampfreq[i] /= spr; } for (i = 0; i < nsig; i++) { fread(buf, 1, 32, ifile); buf[32] = ' '; for (j = 32; j >= 0 && buf[j] == ' '; j--) buf[j] = '\0'; if (vflag && j > 0) printf("Signal %d free space: '%s'\n", i, buf); } /* Determine the base sampling frequency (the greatest common divisor of all sampling frequencies for the signals in the signal list) */ sps = sampfreq[siglist[0]]; for (i = 1; i < nosig; i++) if (sampfreq[siglist[i]] != sps) sps = gcd(sps, sampfreq[siglist[i]]); setsampfreq(sps); sprintf(buf, "%02d:%02d:%02d %02d/%02d/%04d", hour, minute, second, day, month, year); setbasetime(buf); sprintf(buf, "%s.dat", record); for (i = 0; i < nosig; i++) { si[siglist[i]].fname = buf; si[siglist[i]].fmt = 16; tspf += si[siglist[i]].spf = (int)(sampfreq[siglist[i]]/sps + 0.5); so[i] = si[siglist[i]]; } for (i = ispf = 0; i < nsig; i++) ispf += (int)(sampfreq[i]/sps + 0.5); fpb = spb[siglist[0]] / si[siglist[0]].spf; if (vflag) printf("\nOUTPUT:\nBase sampling frequency: %g Hz\n", sps); for (i = j = k = 0; i < nosig; i++) { if ((j = si[siglist[i]].spf) > k) { k = j; l = siglist[i]; } if (vflag) printf(" Signal %d samples per second: %g (%d sample%s per frame)\n", i, sampfreq[siglist[i]], j, j > 1 ? "s" : ""); } vin = (char **)malloc(nsig * sizeof(char *)); vi = (char **)malloc(nosig * sizeof(char *)); for (i = 0; i < nsig; i++) { vin[i] = (char *)malloc(spb[i] * 2); } vout = (WFDB_Sample *)malloc(tspf * sizeof(WFDB_Sample)); osigfopen(so, nosig); while (1) { for (i = 0; i < nsig; i++) j = fread(vin[i], 2, spb[i], ifile); if (j == 0) break; for (j = 0; j < nosig; j++) vi[j] = vin[siglist[j]]; for (i = 0; i < fpb; i++) { vo = vout; for (j = 0; j < nosig; j++) for (k = 0; k < so[j].spf; k++) { if (big_endian) { h = *vi[j]++; /* high byte first */ l = *vi[j]++; /* then low byte */ } else { l = *vi[j]++; /* low byte first */ h = *vi[j]++; /* then high byte */ } /* If a short int is not 16 bits, it may be necessary to modify the next line for proper sign extension. */ *vo++ = ((int)((short)((h << 8) | (l &0xff)))); } putvec(vout); } } if (ifile != stdin) fclose(ifile); newheader(record); wfdbquit(); exit(0); } /* Calculate the greatest common divisor of x and y. This function uses Euclid's algorithm, modified so that an exact answer is not required if the (possibly non-integral) arguments do not have a common divisor that can be represented exactly. */ double gcd(x, y) double x, y; { double tol; if (x > y) tol = 0.001*y; else tol = 0.001*x; while (1) { if (x > y && x-y > tol) x -= y; else if (y > x && y-x > tol) y -= x; else return (x); } } static char *help_strings[] = { "usage: %s [OPTIONS ...]\n", "where OPTIONS may include:", " -b input is in big-endian byte order (default: little-endian)", " -h print this usage summary", " -i EDFILE read the specified European Data Format file", " -r RECORD create the specified RECORD (default: input file name without", " suffix)", " -s SIGNAL [SIGNAL ...] copy only the specified signal(s) (use signal", " numbers, beginning with zero; default: copy all signals)", " -v print debugging output", "This program reads an EDF file and creates MIT-format signal and header", "files containing the same data.", 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]); }