diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/app/wrsamp.c wfdb-10.5.6/app/wrsamp.c --- wfdb-10.5.5/app/wrsamp.c 2010-10-06 22:04:19.000000000 -0400 +++ wfdb-10.5.6/app/wrsamp.c 2010-11-22 09:44:13.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wrsamp.c G. Moody 10 August 1993 - Last revised: 6 October 2010 + Last revised: 22 November 2010 ------------------------------------------------------------------------------- wrsamp: Select fields or columns from a file and generate a WFDB record @@ -63,9 +63,17 @@ { char *p; - for (p = line; *p; p++) - if (('A' <= *p && *p <= 'Z') || ('a' <= *p && *p <= 'z')) - return (1); + if (line) + for (p = line; *p; p++) { + if (*p == 'e' || *p == 'E') { + /* might be a number in printf %f form */ + if (('0' <= *(p++) && *p <= '9') || *p == '-' || *p == '+') + continue; + /* ignore 'e' or 'E' if followed by a digit or a sign */ + } + if (('A' <= *p && *p <= 'Z') || ('a' <= *p && *p <= 'z')) + return (1); + } return (0); } @@ -73,9 +81,10 @@ { char *p; - for (p = line; *p; p++) - if (*p == '\t') - return (1); + if (line) + for (p = line; *p; p++) + if (*p == '\t') + return (1); return (0); } @@ -406,16 +415,16 @@ /* Unless -s was given, note if line 0 contains any tab characters. */ if (sflag == 0 && line_has_tab(line)) defpmode.delim = "\t"; + ta = parseline(line, NULL); + ncols = ta->ntokens; /* If it contains any alphabetic characters, save the signal names. */ if (line_has_alpha(line)) { - ta = parseline(line, NULL); SALLOC(dstrings, ta->ntokens, sizeof(char *)); for (i = 0; i < ta->ntokens; i++) { while (*(ta->token[i]) == ' ') (ta->token[i])++; SSTRCPY(dstrings[i], ta->token[i]); } - ncols = ta->ntokens; /* Read the second line. */ if ((line = read_line(ifile, rsep)) == NULL) { (void)fprintf(stderr, "%s: no data\n", pname); diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/app/xform.c wfdb-10.5.6/app/xform.c --- wfdb-10.5.5/app/xform.c 2010-04-18 18:54:26.000000000 -0400 +++ wfdb-10.5.6/app/xform.c 2010-11-03 17:59:04.000000000 -0400 @@ -1,5 +1,5 @@ /* file: xform.c G. Moody 8 December 1983 - Last revised: 12 March 2010 + Last revised: 3 November 2010 ------------------------------------------------------------------------------- xform: Sampling frequency, amplitude, and format conversion for WFDB records Copyright (C) 1983-2010 George B. Moody @@ -682,8 +682,10 @@ if (gain[i] != 1.) gflag = 1; deltav[i] = dfout[i].adczero - gain[i] * dfin[j].adczero; - if (dfout[i].gain != dfin[j].gain * gain[i]) { + if (dfout[i].gain != dfin[j].gain * gain[i] || + dfout[i].baseline != dfin[j].baseline * gain[i]) { dfout[i].gain = dfin[j].gain * gain[i]; + dfout[i].baseline = dfin[j].baseline * gain[i]; reopen = 1; } /* Note that dfout[i].gain is 0 (indicating that the signal is diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/appcheck wfdb-10.5.6/checkpkg/appcheck --- wfdb-10.5.5/checkpkg/appcheck 2010-01-21 14:30:01.000000000 -0500 +++ wfdb-10.5.6/checkpkg/appcheck 2010-11-26 11:51:55.000000000 -0500 @@ -1,6 +1,6 @@ #!/bin/sh # file: appcheck G. Moody 7 September 2001 -# Last revised: 21 January 2010 +# Last revised: 26 November 2010 # # This script checks the basic functionality of most of the WFDB applications # in the 'app' directory. These programs are not (yet) tested by this script: @@ -9,7 +9,9 @@ # Suggestions for additional checks are welcome; please send them to the # author (george@mit.edu). -WFDB=". ../data http://www.physionet.org/physiobank/database" +cp -pr ../data . + +WFDB=". data http://www.physionet.org/physiobank/database" export WFDB INCDIR=$1 @@ -492,6 +494,8 @@ fi rm -f grep.out +rm -rf data + ( if [ $PASS = $TESTS ] then echo "`basename $0`: all $TESTS tests passed." diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/expected/lcheck_cal wfdb-10.5.6/checkpkg/expected/lcheck_cal --- wfdb-10.5.5/checkpkg/expected/lcheck_cal 2009-10-06 11:44:14.000000000 -0400 +++ wfdb-10.5.6/checkpkg/expected/lcheck_cal 2010-11-22 09:11:53.000000000 -0500 @@ -39,6 +39,7 @@ Atip - - undefined 1 mV HVA - - undefined 5 mV Vtip - - undefined 10 mV +CS - - undefined 1 mV Thorax - - undefined 2 mV Abdomen - - undefined 200 uV F - - undefined 100 uV diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/expected/lcheck.log-NETFILES wfdb-10.5.6/checkpkg/expected/lcheck.log-NETFILES --- wfdb-10.5.5/checkpkg/expected/lcheck.log-NETFILES 2010-02-26 17:28:23.000000000 -0500 +++ wfdb-10.5.6/checkpkg/expected/lcheck.log-NETFILES 2010-11-29 12:02:31.000000000 -0500 @@ -3,9 +3,9 @@ [OK]: WFDB_MAXANN = 2 [OK]: WFDB_MAXSIG = 32 [OK]: WFDB_MAXSPF = 4 -[OK]: WFDB_MAXRNL = 20 -[OK]: WFDB_MAXUSL = 20 -[OK]: WFDB_MAXDSL = 60 +[OK]: WFDB_MAXRNL = 50 +[OK]: WFDB_MAXUSL = 50 +[OK]: WFDB_MAXDSL = 100 [OK]: Signal formats = {0, 8, 16, 61, 80, 160, 212, 310, 311, 24, 32} [OK]: WFDB_DEFFREQ = 250 [OK]: WFDB_DEFGAIN = 200 diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/expected/lcheck.log-no-NETFILES wfdb-10.5.6/checkpkg/expected/lcheck.log-no-NETFILES --- wfdb-10.5.5/checkpkg/expected/lcheck.log-no-NETFILES 2010-02-26 17:28:20.000000000 -0500 +++ wfdb-10.5.6/checkpkg/expected/lcheck.log-no-NETFILES 2010-11-29 12:02:29.000000000 -0500 @@ -3,9 +3,9 @@ [OK]: WFDB_MAXANN = 2 [OK]: WFDB_MAXSIG = 32 [OK]: WFDB_MAXSPF = 4 -[OK]: WFDB_MAXRNL = 20 -[OK]: WFDB_MAXUSL = 20 -[OK]: WFDB_MAXDSL = 60 +[OK]: WFDB_MAXRNL = 50 +[OK]: WFDB_MAXUSL = 50 +[OK]: WFDB_MAXDSL = 100 [OK]: Signal formats = {0, 8, 16, 61, 80, 160, 212, 310, 311, 24, 32} [OK]: WFDB_DEFFREQ = 250 [OK]: WFDB_DEFGAIN = 200 diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/expected/wfdbwhich.out wfdb-10.5.6/checkpkg/expected/wfdbwhich.out --- wfdb-10.5.5/checkpkg/expected/wfdbwhich.out 2001-09-10 16:53:13.000000000 -0400 +++ wfdb-10.5.6/checkpkg/expected/wfdbwhich.out 2010-11-26 07:31:22.000000000 -0500 @@ -1 +1 @@ -../data/100s.atr +data/100s.atr diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/lcheck.c wfdb-10.5.6/checkpkg/lcheck.c --- wfdb-10.5.5/checkpkg/lcheck.c 2002-11-26 10:17:47.000000000 -0500 +++ wfdb-10.5.6/checkpkg/lcheck.c 2010-11-26 11:25:38.000000000 -0500 @@ -1,8 +1,8 @@ /* file: lcheck.c G. Moody 7 September 2001 - Last revised: 26 November 2002 + Last revised: 26 November 2010 ------------------------------------------------------------------------------- wfdbcheck: test WFDB library -Copyright (C) 2002 George B. Moody +Copyright (C) 2001-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 @@ -113,7 +113,7 @@ /* *** setwfdb *** */ dbpath = calloc(sizeof(char), strlen(defpath)+10); - sprintf(dbpath, "../data %s\n", defpath); + sprintf(dbpath, "data %s\n", defpath); setwfdb(dbpath); if ((p = getwfdb()) == NULL || strcmp(p, dbpath)) { printf("Error: Could not set WFDB path\n"); diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/checkpkg/libcheck wfdb-10.5.6/checkpkg/libcheck --- wfdb-10.5.5/checkpkg/libcheck 2006-02-27 11:53:43.000000000 -0500 +++ wfdb-10.5.6/checkpkg/libcheck 2010-11-26 12:07:59.000000000 -0500 @@ -1,6 +1,6 @@ #!/bin/sh # file: libcheck G. Moody 8 September 2001 -# Last revised: 27 February 2006 +# Last revised: 26 November 2010 # # This script checks the functionality of the WFDB library by comparing the # outputs of 'lcheck' with expected outputs. See 'lcheck.c' for details of @@ -15,6 +15,8 @@ # Suggestions for additional checks are welcome; please send them to the # author (george@mit.edu). +cp -pr ../data . + if [ \! -s ../data/100s.hea ] then echo "Run this program from the 'checkpkg' directory of the WFDB sources." @@ -94,6 +96,8 @@ TESTS=`expr $TESTS + 1` done +rm -rf data + if [ $PASS = $TESTS ] then echo "`basename $0`: all $TESTS tests passed." diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/convert/wfdb2mat.c wfdb-10.5.6/convert/wfdb2mat.c --- wfdb-10.5.5/convert/wfdb2mat.c 2010-07-27 14:16:20.000000000 -0400 +++ wfdb-10.5.6/convert/wfdb2mat.c 2010-11-29 11:01:10.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdb2mat.c G. Moody 26 February 2009 - Last revised: 27 July 2010 + Last revised: 29 November 2010 ------------------------------------------------------------------------------- wfdb2mat: Convert (all or part of) a WFDB signal file to Matlab .mat format Copyright (C) 2009-2010 George B. Moody @@ -90,7 +90,7 @@ char *matname, *orec, *p, *q, *record = NULL, *search = NULL, *prog_name(); static char prolog[24]; int highres = 0, i, isiglist, mattype, nisig, nosig = 0, pflag = 0, - s, *sig = NULL, stat = 0, vflag = 0, wfdbtype; + s, sfname = 0, *sig = NULL, stat = 0, vflag = 0, wfdbtype; WFDB_Frequency freq; WFDB_Sample *vi, *vo; WFDB_Siginfo *si, *so; @@ -240,19 +240,21 @@ } /* Generate the names for the output .mat file and the output record. */ - SUALLOC(matname, strlen(record)+6, sizeof(char)); - sprintf(matname, "%sm.mat", record); - SUALLOC(orec, strlen(record)+2, sizeof(char)); - sprintf(orec, "%sm", record); + p = record + strlen(record) - 1; /* *p = final char of record name */ + if (*p == '/') /* short form name ('rec/' rather than 'rec/rec') */ + sfname = 1; + while (--p > record) + if (*p == '/') { p++; break; } /* omit path components from orec */ + SUALLOC(orec, strlen(p)+2, sizeof(char)); + strncpy(orec, p, strlen(p) - sfname); /* If the input record is an EDF file, it will have a '.' in its name. The output record will not be an EDF file, so it may not have a '.' in its name. Replace any '.' with '_' here. */ - for (p = orec + strlen(orec) - 1; p > orec && *p != '/'; p--) - if (*p == '.') *p = '_'; - /* To avoid confusion, also make this replacement in matname, but leave - the '.' just before 'mat' alone. */ - for (p = matname + strlen(matname) - 5; p > matname && *p != '/'; p--) + for (p = orec; *p; p++) if (*p == '.') *p = '_'; + *p = 'm'; /* append 'm' to the output record name */ + SUALLOC(matname, strlen(record)+6, sizeof(char)); + sprintf(matname, "%s.mat", orec); /* Determine if we can write 8-bit unsigned samples, or if 16 or 32 bits are needed per sample. */ diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/data/wfdbcal wfdb-10.5.6/data/wfdbcal --- wfdb-10.5.5/data/wfdbcal 2009-09-09 17:58:38.000000000 -0400 +++ wfdb-10.5.6/data/wfdbcal 2010-10-25 11:05:23.000000000 -0400 @@ -66,6 +66,9 @@ HVA - - undefined 5 mV Vtip - - undefined 10 mV +# Intracardiac electrograms +CS - - undefined 1 mV + # ECG from Non-Invasive Fetal ECG Database Thorax - - undefined 2 mV Abdomen - - undefined 200 uV diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/doc/wpg-src/wpg0.tex wfdb-10.5.6/doc/wpg-src/wpg0.tex --- wfdb-10.5.5/doc/wpg-src/wpg0.tex 2010-07-14 01:10:47.000000000 -0400 +++ wfdb-10.5.6/doc/wpg-src/wpg0.tex 2010-11-29 19:24:38.000000000 -0500 @@ -8982,6 +8982,21 @@ changes that may not be described here. @unnumberedsec WFDB 10.5 +@unnumberedsubsec Changes in version 10.5.6 (29 November 2010) + +WFDB records with names of the form @file{nnn/nnn} can now be identified +using the short form @file{nnn/} in applications built using the WFDB library +(e.g., @code{rdsamp -r mimicdb/037/} and @code{rdsamp -r mimicdb/037/037} are +now equivalent). + +In @file{}, the maximum lengths of record names, units strings, and +signal description (@file{.desc}) strings have been increased (to 50, 50, +and 100 characters respectively). In @file{lib/wfdbio.c}, the maximum length +of a WFDB file name (including path information) has been increased to +1024 characters. + +(WFDB library version 10.5.5 was identical to version 10.5.4.) + @unnumberedsubsec Changes in version 10.5.4 (13 July 2010) Function @code{getseginfo()} has been introduced in WFDB library version diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/lib/signal.c wfdb-10.5.6/lib/signal.c --- wfdb-10.5.5/lib/signal.c 2010-07-02 00:28:51.000000000 -0400 +++ wfdb-10.5.6/lib/signal.c 2010-11-29 11:58:47.000000000 -0500 @@ -1,5 +1,5 @@ /* file: signal.c G. Moody 13 April 1989 - Last revised: 29 June 2010 wfdblib 10.5.4 + Last revised: 29 November 2010 wfdblib 10.5.6 WFDB library functions for signals _______________________________________________________________________________ @@ -862,13 +862,13 @@ a directory separator (whether valid or not for this OS); if so, compare only the final portion of the argument against the name in the header file. */ - const char *r, *s; + const char *q, *r, *s; - for (r = record, s = r + strlen(r) - 1; r != s; s--) + for (r = record, q = s = r + strlen(r) - 1; r != s; s--) if (*s == '/' || *s == '\\' || *s == ':') break; - if (r > s || strcmp(p, s+1) != 0) { + if (q > s && (r > s || strcmp(p, s+1) != 0)) { wfdb_error("init: record name in record %s header is incorrect\n", record); return (-2); @@ -2174,12 +2174,12 @@ /* Prescan siarray to check the signal specifications and to determine the number of signal groups. */ for (s = 0, si = siarray; s < nsig; s++, si++) { - /* The combined lengths of the fname and desc strings should be 80 + /* The combined lengths of the fname and desc strings should be 200 characters or less, the bsize field must not be negative, the format should be legal, group numbers should be the same if and only if file names are the same, and group numbers should begin at zero and increase in steps of 1. */ - if (strlen(si->fname) + strlen(si->desc) > 80 || + if (strlen(si->fname) + strlen(si->desc) > 200 || si->bsize < 0 || !isfmt(si->fmt)) { wfdb_error("osigfopen: error in specification of signal %d\n", s); @@ -2208,11 +2208,11 @@ op = os; os = osd[nosig]; /* Check signal specifications. The combined lengths of the fname - and desc strings should be 80 characters or less, the bsize field + and desc strings should be 200 characters or less, the bsize field must not be negative, the format should be legal, group numbers should be the same if and only if file names are the same, and group numbers should begin at zero and increase in steps of 1. */ - if (strlen(siarray->fname) + strlen(siarray->desc) > 80 || + if (strlen(siarray->fname) + strlen(siarray->desc) > 200 || siarray->bsize < 0 || !isfmt(siarray->fmt)) { wfdb_error("osigfopen: error in specification of signal %d\n", nosig); diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/lib/wfdb.h wfdb-10.5.6/lib/wfdb.h --- wfdb-10.5.5/lib/wfdb.h 2010-10-06 22:45:29.000000000 -0400 +++ wfdb-10.5.6/lib/wfdb.h 2010-11-29 21:22:52.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdb.h G. Moody 13 June 1983 - Last revised: 29 June 2010 wfdblib 10.5.4 + Last revised: 29 November 2010 wfdblib 10.5.6 WFDB library type, constant, structure, and function interface definitions _______________________________________________________________________________ wfdb: a library for reading and writing annotated waveforms (time series data) @@ -32,7 +32,7 @@ /* WFDB library version. */ #define WFDB_MAJOR 10 #define WFDB_MINOR 5 -#define WFDB_RELEASE 5 +#define WFDB_RELEASE 6 #define WFDB_NETFILES 1 /* if 1, library includes code for HTTP, FTP clients */ #define WFDB_NETFILES_LIBCURL 1 @@ -95,9 +95,9 @@ #define WFDB_MAXANN 2 /* maximum number of input or output annotators */ #define WFDB_MAXSIG 32 /* maximum number of input or output signals */ #define WFDB_MAXSPF 4 /* maximum number of samples per signal per frame */ -#define WFDB_MAXRNL 20 /* maximum length of record name */ -#define WFDB_MAXUSL 20 /* maximum length of WFDB_siginfo `.units' string */ -#define WFDB_MAXDSL 60 /* maximum length of WFDB_siginfo `.desc' string */ +#define WFDB_MAXRNL 50 /* maximum length of record name */ +#define WFDB_MAXUSL 50 /* maximum length of WFDB_siginfo `.units' string */ +#define WFDB_MAXDSL 100 /* maximum length of WFDB_siginfo `.desc' string */ /* WFDB_anninfo '.stat' values */ #define WFDB_READ 0 /* standard input annotation file */ diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/lib/wfdb.h0 wfdb-10.5.6/lib/wfdb.h0 --- wfdb-10.5.5/lib/wfdb.h0 2010-07-01 17:12:58.000000000 -0400 +++ wfdb-10.5.6/lib/wfdb.h0 2010-11-29 12:00:20.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdb.h G. Moody 13 June 1983 - Last revised: 29 June 2010 wfdblib 10.5.4 + Last revised: 29 November 2010 wfdblib 10.5.6 WFDB library type, constant, structure, and function interface definitions _______________________________________________________________________________ wfdb: a library for reading and writing annotated waveforms (time series data) @@ -95,9 +95,9 @@ #define WFDB_MAXANN 2 /* maximum number of input or output annotators */ #define WFDB_MAXSIG 32 /* maximum number of input or output signals */ #define WFDB_MAXSPF 4 /* maximum number of samples per signal per frame */ -#define WFDB_MAXRNL 20 /* maximum length of record name */ -#define WFDB_MAXUSL 20 /* maximum length of WFDB_siginfo `.units' string */ -#define WFDB_MAXDSL 60 /* maximum length of WFDB_siginfo `.desc' string */ +#define WFDB_MAXRNL 50 /* maximum length of record name */ +#define WFDB_MAXUSL 50 /* maximum length of WFDB_siginfo `.units' string */ +#define WFDB_MAXDSL 100 /* maximum length of WFDB_siginfo `.desc' string */ /* WFDB_anninfo '.stat' values */ #define WFDB_READ 0 /* standard input annotation file */ diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/lib/wfdbio.c wfdb-10.5.6/lib/wfdbio.c --- wfdb-10.5.5/lib/wfdbio.c 2010-06-22 10:54:48.000000000 -0400 +++ wfdb-10.5.6/lib/wfdbio.c 2010-11-29 21:16:46.000000000 -0500 @@ -1,5 +1,5 @@ /* file: wfdbio.c G. Moody 18 November 1988 - Last revised: 22 June 2010 wfdblib 10.5.3 + Last revised: 29 November 2010 wfdblib 10.5.6 Low-level I/O functions for the WFDB library _______________________________________________________________________________ @@ -211,7 +211,7 @@ error_print = 1; } -#define MFNLEN 256 /* max length of WFDB filename, including '\0' */ +#define MFNLEN 1024 /* max length of WFDB filename, including '\0' */ static char wfdb_filename[MFNLEN]; /* wfdbfile returns the pathname or URL of a WFDB file. */ @@ -878,7 +878,8 @@ WFDB_FILE *wfdb_open(const char *s, const char *record, int mode) { - char *wfdb, *p; + char *wfdb, *p, *q, *r; + int rlen; struct wfdb_path_component *c0; WFDB_FILE *ifile; @@ -910,11 +911,30 @@ return (&wfdb_stdout); } + /* If the record name ends with '/', expand it by adding another copy of + the final element (e.g., 'abc/123/' becomes 'abc/123/123'). */ + rlen = strlen(record); + p = (char *)(record + rlen - 1); + if (*p == '/') { + for (q = p-1; q > record; q--) + if (*q == '/') { q++; break; } + if (q < p-1) { + SUALLOC(r, rlen + p-q + 1, 1); /* p-q is length of final element */ + strcpy(r, record); + strncpy(r + rlen, q, p-q); + } + } + else { + SUALLOC(r, rlen + 1, 1); + strcpy(r, record); + } + /* If the file is to be opened for output, use the current directory. An output file can be opened in another directory if the path to that directory is the first part of 'record'. */ if (mode == WFDB_WRITE) { - spr1(wfdb_filename, record, s); + spr1(wfdb_filename, r, s); + SFREE(r); return (wfdb_fopen(wfdb_filename, WB)); } @@ -924,7 +944,7 @@ if (wfdb_path_list == NULL) (void)getwfdb(); for (c0 = wfdb_path_list; c0; c0 = c0->next) { - char long_filename[MFNLEN]; + static char long_filename[MFNLEN]; p = wfdb_filename; wfdb = c0->prefix; @@ -972,26 +992,29 @@ #endif *p++ = DSEP; } - if (p + strlen(record) + (s ? strlen(s) : 0) > wfdb_filename + MFNLEN-5) + if (p + strlen(r) + (s ? strlen(s) : 0) > wfdb_filename + MFNLEN-5) continue; /* name too long -- skip */ - spr1(p, record, s); + spr1(p, r, s); if ((ifile = wfdb_fopen(wfdb_filename, RB)) != NULL) { /* Found it! Add its path info to the WFDB path. */ wfdb_addtopath(wfdb_filename); + SFREE(r); return (ifile); } /* Not found -- try again, using an alternate form of the name, provided that that form is distinct. */ strcpy(long_filename, wfdb_filename); - spr2(p, record, s); + spr2(p, r, s); if (strcmp(wfdb_filename, long_filename) && (ifile = wfdb_fopen(wfdb_filename, RB)) != NULL) { wfdb_addtopath(wfdb_filename); + SFREE(r); return (ifile); } } /* If the file was not found in any of the directories listed in wfdb, return a null file pointer to indicate failure. */ + SFREE(r); return (NULL); } @@ -1914,6 +1937,8 @@ char *p = fname; WFDB_FILE *wp; + if (p == NULL || strstr(p, "..")) + return (NULL); SUALLOC(wp, 1, sizeof(WFDB_FILE)); while (*p) if (*p++ == ':' && *p++ == '/' && *p++ == '/') { diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/MANIFEST wfdb-10.5.6/MANIFEST --- wfdb-10.5.5/MANIFEST 2010-10-06 22:38:59.000000000 -0400 +++ wfdb-10.5.6/MANIFEST 2010-11-04 10:38:16.000000000 -0400 @@ -575,6 +575,12 @@ wave/xview-patches wave/xvwave.c wave/xvwave.h +wfdbmap +wfdbmap/Makefile +wfdbmap/map-record +wfdbmap/README +wfdbmap/signal-colors.h +wfdbmap/wfdbmap.c wfdb.spec xml xml/annxml.c diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/NEWS wfdb-10.5.6/NEWS --- wfdb-10.5.5/NEWS 2010-10-06 23:37:51.000000000 -0400 +++ wfdb-10.5.6/NEWS 2010-11-29 22:07:06.000000000 -0500 @@ -1,3 +1,38 @@ +10.5.6 (29 November 2010): + WFDB records with names of the form 'nnn/nnn' can now be identified + using the short form 'nnn/' in applications built using the WFDB library + (e.g., 'rdsamp -r mimicdb/037/' and 'rdsamp -r mimicdb/037/037' are now + equivalent). + + In , the maximum lengths of record names, units strings, and + signal description ('.desc') strings have been increased (to 50, 50, + and 100 characters respectively). In lib/wfdbio.c, the maximum length + of a WFDB file name (including path information) has been increased to + 1024 characters. + + When given an input record with a name containing path information, + previous versions of wfdb2mat preserved the path information in the + output '.hea' file, which made it difficult to use (although the '.mat' + output file was not affected). The current version strips the unneeded + path information. + + The previous version of wrsamp did not work if signal numbers were + specified, and the input did not include column headers. Thanks to + Jesus Maria Rodriguez Presedo for reporting this problem. Also, + the previous version of wrsamp did not copy initial samples represented + in E notation (e.g. 1.23e+02). Both of these bugs are corrected in + this release. + + If signals with a non-zero baseline were scaled using previous versions + of xform, baseline shifts were possible in the output. This problem + has been corrected in this release. + + This release includes wfdbmap, an application that can be used + together with other open-source software to create synoptic maps of + WFDB-compatible records, in the format of those presented by the + PhysioBank ATM. 'wfdbmap' is not compiled by default; see + wfdbmap/README for additional information. + 10.5.5 (6 October 2010): WFDB-compatible signals can now be converted to a simple XML format using rdsamp's new '-X' option, and XML files in this format can be @@ -7,12 +42,12 @@ and header files, is available at http://physionet.org/physiobank/database/XML/wfdb.dtd This file, together with a set of applications (annxml, heaxml, xmlann, - and xmlhea) that convert WFDB-compatible annotation and header files + and xmlhea) that convert WFDB-compatible annotation and header files to and from these XML formats, are included in the 'xml' subdirectory of this package. Since version 10.4.14, app/sortann has not properly sorted annotations - containing non-zero 'chan' or 'num' fields. This bug has now been + containing non-zero 'chan' or 'num' fields. This bug has now been fixed; thanks to Jesus Maria Rodriguez Presedo for reporting it and for providing a test case. @@ -30,13 +65,13 @@ a formatting error; both problems have been corrected, thanks to a report from Isaac Henry. - Errors in the formatting of a variety of error and warning messages + Errors in the formatting of a variety of error and warning messages have been corrected in app/ecgeval.c, app/psfd.c, app/rdsamp.c, app/snip.c, app/sumstats.c, app/wrann.c, convert/mit2edf.c, convert/mit2wav.c, convert/wfdb2mat.c, and psd/lomb.c. 10.5.4 (13 July 2010): - Function getseginfo() has been introduced in WFDB library version + Function getseginfo() has been introduced in WFDB library version 10.5.4, to allow applications to obtain information about the segments belonging to the current (multi-segment) input record. @@ -55,7 +90,7 @@ WAVE version 6.11, included in this release of the WFDB software package, allows the user to change display scales using keyboard - commands. This allows one to resize signals independently, a frequently + commands. This allows one to resize signals independently, a frequently requested feature. For details, see "How can I enlarge or reduce the size of individual signals?" in the WAVE FAQ (in WAVE, click on Help and select "Frequently asked questions"). @@ -69,7 +104,7 @@ been renamed as _lw. A comment in each of the *.def files in conf, now corrected, erroneously referred to LN, rather than LL. - Tony Ricke reported that test builds (./configure; make) of recent + Tony Ricke reported that test builds (./configure; make) of recent versions of the WFDB package fail on Cygwin. This problem has been corrected (in conf/cygwin-slib.def) in 10.5.3. @@ -162,16 +197,16 @@ WFDB application 'rdsamp' can now write CSV (comma-separated value) format output using its new '-c' option, and 'wrsamp' can read CSV format input. If no input columns are specified, 'wrsamp' converts - all of them. If the input contains signal names, units, or timestamps, - 'wrsamp' saves them in the header file it creates. 'wrsamp' also has - two new options ('-O', to select the output format, and '-z', to - suppress output of column 0). + all of them. If the input contains signal names, units, or timestamps, + 'wrsamp' saves them in the header file it creates. 'wrsamp' also has + two new options ('-O', to select the output format, and '-z', to + suppress output of column 0). WFDB application 'sigamp' has new options for printing individual measurements. The script 'install-wave32', used to build and install a 32-bit version - of WAVE on some 64-bit platforms, has been updated so that it can be + of WAVE on some 64-bit platforms, has been updated so that it can be used on Fedora 12 (as well as on previously supported platforms). In WFDB library versions 10.4.5 through 10.4.24, strtim("e") did not @@ -228,9 +263,9 @@ mixture of valid and invalid samples. This bug has been corrected; thanks to Omar Abdala for a report that brought this problem to light. - A declaration for WFDB library function findsig() has been added - to wfdb.h. Thanks to Thomas Heldt for reporting a warning - that resulted from its omission in previous versions. + A declaration for WFDB library function findsig() has been added + to wfdb.h. Thanks to Thomas Heldt for reporting a warning + that resulted from its omission in previous versions. Omar also found and corrected a bug in app/wabp.c that resulted in premature termination of processing some variable-layout multisegment @@ -424,8 +459,8 @@ The WFDB library once again correctly interprets a hyphen ('-') (used in place of a record name, annotator name, signal file name, or calibration file name) as a reference to the standard input or - output, for platforms that support POSIX standard I/O; see section - 5.2, "Using Standard I/O for Database Files", in the WFDB Programmer's + output, for platforms that support POSIX standard I/O; see section + 5.2, "Using Standard I/O for Database Files", in the WFDB Programmer's Guide. This feature was broken in 10.4.5 as a side effect of changes in wfdb_open (an internal WFDB library function defined in lib/wfdbio.c). @@ -462,11 +497,11 @@ that encountered them returned error values that permitted the application to handle them). These errors occur when there is insufficient memory available to the WFDB library. To obtain the - old behavior, in which the calling function will continue execution - if possible after a memory allocation error, invoke wfdbmemerr(0). - By default, however, such an error will cause the process to terminate. - In either case, the WFDB library emits an appropriate error message to - aid in troubleshooting. + old behavior, in which the calling function will continue execution + if possible after a memory allocation error, invoke wfdbmemerr(0). + By default, however, such an error will cause the process to terminate. + In either case, the WFDB library emits an appropriate error message to + aid in troubleshooting. New macros for handling dynamically allocated memory are defined in lib/wfdblib.h and used throughout the WFDB library, eliminating most @@ -506,14 +541,14 @@ Alexander Khaustov reported a bug in convert/ad2m.c and provided a fix for it. The bug caused ad2m to truncate its output prematurely if the - "-f" option had been used. + "-f" option had been used. Changes in the build system make it easier to build WFDB using Cygwin gcc (with or without the Cygwin POSIX library). Handle missing samples properly in pschart.c and psfd.c. - Removed unused variables from app/wapb.c (thanks to Thomas Heldt). + Removed unused variables from app/wapb.c (thanks to Thomas Heldt). Corrected threshold update in app/wqrs.c, and added a check to avoid problems when the sampling frequency is too low. diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/wfdbmap/map-record wfdb-10.5.6/wfdbmap/map-record --- wfdb-10.5.5/wfdbmap/map-record 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.6/wfdbmap/map-record 2010-07-30 15:37:34.000000000 -0400 @@ -0,0 +1,12 @@ +#! /bin/bash + +# make a map of a WFDB record + +RL=$1 +RS=`basename $RL` +shift + +wfdbmap -r $RL $* >$RS.script +bash $RS.script >$RS.plt 2>/dev/null +lwcat -strip -eps $RS.plt >$RS.ps +convert -density 150 $RS.ps $RS.png diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/wfdbmap/README wfdb-10.5.6/wfdbmap/README --- wfdb-10.5.5/wfdbmap/README 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.6/wfdbmap/README 2010-11-04 10:28:34.000000000 -0400 @@ -0,0 +1,38 @@ +file: README G. Moody 30 July 2010 + +Software for creating 'maps' of PhysioBank records + +To build and install the software in this directory, first install the +WFDB and plt software packages from PhysioNet, ImageMagick (from +www.imagemagick.org), then return to this directory and type 'make'. + +This directory contains 'wfdbmap', a program that reads a WFDB record, +optionally including one or more associated annotation files, and generates a +script containing embedded data. When the script is run, it creates (using +'plt' and 'lwcat', from the plt package) a PostScript-format 'map' of the WFDB +record and its annotations. The maps displayed by the PhysioBank ATM are +created in this way. + +Also in this directory is 'map-record', a shell script that illustrates how +'wfdbmap' and (indirectly) 'plt are used to create a map, and how to convert +the PostScript map into a PNG-format map (using 'convert' from ImageMagick). +For example, to make a map of mitdb/200 and its associated 'atr' annotations, +run the command: + map-record mitdb/200 -a atr +The outputs of this command are 200.ps and 200.png. If other annotation files +are available, their annotator names can be given as additional command-line +arguments: + map-record RECORD -a ANN1 ANN2 ANN3 ... + +*** A MAJOR LIMITATION of this version of wfdbmap is that it cannot properly +map segments of a variable-layout multisegment record unless all of the +.hea files for that record are local (i.e., they can't be retrieved from a +web server as needed). For the ATM, this is not a limitation at all, since +the ATM is mapping local records on physionet.org. For general use, it's +a serious limitation, since variable-layout multisegment records are often +the records for which a map would be most useful. + +The symptoms of this problem are a warning message of the form: + wfdbmap: can't open http://physionet.org/physiobank/database/... +and an output map that shows all signals as missing. (The annotations are +mapped properly, however.) diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/wfdbmap/signal-colors.h wfdb-10.5.6/wfdbmap/signal-colors.h --- wfdb-10.5.5/wfdbmap/signal-colors.h 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.6/wfdbmap/signal-colors.h 2009-04-09 16:44:18.000000000 -0400 @@ -0,0 +1,311 @@ +struct sigcolor { + char *color; + char *class; + char *name; +} ctab[] = { +"red", "ECG", "Abdomen_1", +"red", "ECG", "Abdomen_2", +"red", "ECG", "Abdomen_3", +"red", "ECG", "Abdomen_4", +"green", "Resp", "Abdomen [ABMV]", +"green", "Resp", "ABDO RES", +"blue", "BP", "ABP", +"blue", "BP", "ABP_1/2", +"blue", "BP", "ABP_2/2", +"blue", "BP", "ABPDias", +"blue", "BP", "ABPMean", +"blue", "BP", "ABPSys", +"blue", "BP", "AOBP", +"blue", "BP", "AOBP_1/2", +"blue", "BP", "AOBP_2/2", +"blue", "BP", "AOBP Dias", +"blue", "BP", "AOBP Mean", +"blue", "BP", "AOBP Sys", +"blue", "BP", "NBP_1/2", +"blue", "BP", "NBP_2/2", +"blue", "BP", "NBP", +"blue", "BP", "NBPDias", +"blue", "BP", "NBPMean", +"blue", "BP", "NBPSys", +"blue", "BP", "ABP Dias", +"blue", "BP", "ABP Mean", +"blue", "BP", "ABP Sys", +"blue", "BP", "NBP Dias", +"blue", "BP", "NBP Mean", +"blue", "BP", "NBP Sys", +"blue", "BP", "ABP ", +"red", "ECG", "A-I", +"green", "Resp", "AIRFLOW", +"blue", "BP", "ART", +"blue", "BP", "ART_1/2", +"blue", "BP", "ART Dias", +"blue", "BP", "ART Mean", +"blue", "BP", "ART Sys", +"blue", "BP", "ART ", +"blue", "BP", "ART 1", +"blue", "BP", "ART1 ", +"blue", "BP", "ART^M ", +"red", "ECG", "A-S", +"red", "ECG", "avf", +"red", "ECG", "aVF", +"red", "ECG", "AVF", +"red", "ECG", "AVF+", +"red", "ECG", "avl", +"red", "ECG", "aVL", +"red", "ECG", "AVL", +"red", "ECG", "avr", +"red", "ECG", "aVR", +"red", "ECG", "AVR", +"darkgreen", "Temp", "BLOODT", +"darkgreen", "Temp", "BLOODT_1/3", +"darkgreen", "Temp", "BLOODT_2/3", +"darkgreen", "Temp", "BLOODT_3/3", +"blue", "BP", "BP", +"blue", "BP", " BP", +"pink", "noise", "BW noise, signal 0", +"pink", "noise", "BW noise, signal 1", +"darkred", "CO", "CO", +"darkred", "CO", "CO_1/2", +"darkred", "CO", "CO_1/3", +"darkred", "CO", "CO_2/2", +"darkred", "CO", "CO_2/3", +"darkred", "CO", "CO_3/3", +"purple", "CO2", "C02", +"blue", "BP", "CVP", +"blue", "BP", "CVP_1/2", +"blue", "BP", "CVP_1/3", +"blue", "BP", "CVP_2/2", +"blue", "BP", "CVP_2/3", +"blue", "BP", "CVP_3/3", +"red", "ECG", "CC5", +"red", "ECG", "chan 1", +"red", "ECG", "chan 2", +"red", "ECG", "chan 3", +"red", "ECG", "CM2", +"red", "ECG", "CM4", +"red", "ECG", "CM5", +"purple", "CO2", "Co2", +"purple", "CO2", "CO2", +"purple", "CO2", "CO2^M ", +"red", "ECG", "CS12", +"red", "ECG", "CS34", +"red", "ECG", "CS56", +"red", "ECG", "CS78", +"red", "ECG", "CS90", +"blue", "BP", "CVP", +"blue", "BP", "CVP 3", +"red", "ECG", "D3", +"red", "ECG", "D4", +"red", "ECG", "ECG", +"red", "ECG", " ECG", +"red", "ECG", "ECG0", +"red", "ECG", "ECG1", +"red", "ECG", "ECG 1", +"red", "ECG", "ECG 2", +"red", "ECG", "ECG 3", +"red", "ECG", "ECG AVF", +"red", "ECG", "ECG [ECG1]", +"red", "ECG", "ECG F", +"red", "ECG", "ECG I", +"red", "ECG", "ECG II", +"red", "ECG", "ECG III", +"red", "ECG", "ECG lead 1", +"red", "ECG", "ECG lead 2", +"red", "ECG", "ECG lead 3", +"red", "ECG", "ECG Lead AVF", +"red", "ECG", "ECG lead AVL", +"red", "ECG", "ECG lead I", +"red", "ECG", "ECG Lead I", +"red", "ECG", "ECG lead II", +"red", "ECG", "ECG lead II ", +"red", "ECG", "ECG LeadII", +"red", "ECG", "ECG Lead II", +"red", "ECG", "ECG lead III", +"red", "ECG", "ECG Lead III", +"red", "ECG", "ECG lead V", +"red", "ECG", "ECG Lead V", +"red", "ECG", "ECG Lead V ", +"red", "ECG", "ECG Lead V3", +"red", "ECG", "ECG Lead V4", +"red", "ECG", "ECG lead V5", +"red", "ECG", "ECG Lead V5", +"red", "ECG", "ECG lead V6", +"red", "ECG", "ECG MCL", +"red", "ECG", "ECG signal 0", +"red", "ECG", "ECG signal 1", +"red", "ECG", "ECG V", +"red", "ECG", "ECG V3", +"red", "ECG", "ECG V Lead", +"grey", "Annot", "EDF Annotations", +"yellow", "EEG", "EEG", +"yellow", "EEG", "EEG C3-A2 [C3A2]", +"yellow", "EEG", "EEG (C3-O1)", +"yellow", "EEG", "EEG (C4-A1)", +"yellow", "EEG", "EEG C4-A1 [C4A1]", +"yellow", "EEG", "EEG Fpz-Cz", +"yellow", "EEG", "EEG (O2-A1)", +"yellow", "EEG", "EEG Pz-Oz", +"yellow", "EEG", "EEG(sec)", +"orange", "EMG", "EMG", +"orange", "EMG", "EMG-Chin [EMYG]", +"orange", "EMG", "EMG submental", +"orange", "EMG", "EMG Submental", +"pink", "noise", "EM noise, signal 0", +"pink", "noise", "EM noise, signal 1", +"lightblue", "EOG", "EOG", +"lightblue", "EOG", "EOG E1-A1 [EOGL]", +"lightblue", "EOG", "EOG E2-A1 [EOGR]", +"lightblue", "EOG", "EOG horizontal", +"lightblue", "EOG", "EOG(L)", +"lightblue", "EOG", "EOG(R)", +"lightblue", "EOG", "EOG (right)", +"red", "ECG", "E-S", +"grey", "Annot", "Event marker", +"green", "Resp", "Flow [AFLO]", +"grey", "Annot", "Hypnogram", +"red", "ECG", "i", +"red", "ECG", "I", +"red", "ECG", "I ", +"red", "ECG", "I+", +"blue", "BP", "ICP", +"grey", "Annot", "ID+Sync+Error", +"red", "ECG", "ii", +"red", "ECG", "II", +"red", "ECG", "II ", +"red", "ECG", "II+", +"red", "ECG", "iii", +"red", "ECG", "III", +"red", "ECG", "III ", +"red", "ECG", "III+", +"blue", "BP", "LAP", +"red", "ECG", "lead I", +"red", "ECG", "lead II", +"red", "ECG", "lead V", +"pink", "noise", "MA noise, signal 0", +"pink", "noise", "MA noise, signal 1", +"red", "ECG", "MCL1", +"red", "ECG", "MCL1 ", +"red", "ECG", "MCL1+", +"red", "ECG", "ML2", +"red", "ECG", "ML5", +"red", "ECG", "MLI", +"red", "ECG", "MLII", +"red", "ECG", "MLIII", +"red", "ECG", "mod.V1", +"red", "ECG", "MV2", +"red", "ECG", "MV2 ", +"blue", "BP", "P1", +"blue", "BP", "P1 Dias", +"blue", "BP", "P1 Mean", +"blue", "BP", "P1 Sys", +"blue", "BP", "PA", +"blue", "BP", "PAP", +"blue", "BP", "PAP_1/2", +"blue", "BP", "PAP_2/2", +"blue", "BP", "PAP ", +"blue", "BP", "PAP Dias", +"blue", "BP", "PAP Mean", +"blue", "BP", "PAP Sys", +"blue", "BP", "PAP 2", +"blue", "BP", "PAWP", +"blue", "BP", "PAWP_1/2", +"blue", "BP", "PAWP_1/3", +"blue", "BP", "PAWP_2/2", +"blue", "BP", "PAWP_2/3", +"blue", "BP", "PAWP_3/3", +"darkblue", "PLETH", "PLETH", +"darkblue", "PLETH", "PLETH ", +"grey", "Pos", "Position", +"orangered", "HR", "HR", +"orangered", "HR", "HR_1/2", +"orangered", "HR", "HR_1/3", +"orangered", "HR", "HR_2/2", +"orangered", "HR", "HR_2/3", +"orangered", "HR", "HR_3/3", +"orangered", "HR", "PR", +"blue", "BP", "Pressure", +"blue", "BP", "Pressure ", +"blue", "BP", "Pressure1", +"blue", "BP", "Pressure 1", +"blue", "BP", "Pressure 2", +"blue", "BP", "Pressure 3", +"blue", "BP", "Pressure 4", +"orangered", "HR", "Pulse", +"orangered", "HR", "PULSE", +"orangered", "HR", "PULSE_1/2", +"orangered", "HR", "PULSE_1/3", +"orangered", "HR", "PULSE_2/2", +"orangered", "HR", "PULSE_2/3", +"orangered", "HR", "PULSE_3/3", +"orangered", "HR", "PVC Rate per Minute", +"orangered", "HR", "PVC Rate per Minute_1/2", +"orangered", "HR", "PVC Rate per Minute_1/3", +"orangered", "HR", "PVC Rate per Minute_2/3", +"orangered", "HR", "PVC Rate per Minute_3/3", +"blue", "BP", "RAP", +"green", "Resp", "RESP", +"green", "Resp", "RESP_1/2", +"green", "Resp", "RESP_1/3", +"green", "Resp", "RESP_2/2", +"green", "Resp", "RESP_2/3", +"green", "Resp", "RESP_3/3", +"green", "Resp", " RESP", +"green", "Resp", "RESP ", +"green", "Resp", "Resp A", +"green", "Resp", "Resp (abdomen)", +"green", "Resp", "Resp (abdominal)", +"green", "Resp", "Resp C", +"green", "Resp", "Resp (chest)", +"green", "Resp", "Resp Imp", +"green", "Resp", "Resp. Imp.", +"green", "Resp", "Resp.Imp.", +"green", "Resp", "RESP IMP", +"green", "Resp", "Resp Inp.", +"green", "Resp", "Resp N", +"green", "Resp", "Resp (nasal)", +"green", "Resp", "Resp oro-nasal", +"green", "Resp", "Resp (sum)", +"darkred", "O2", "SaO2", +"darkred", "O2", "SaO2 [OSAT]", +"darkred", "O2", "SO2", +"grey", "Sound", "Sound", +"darkred", "O2", "SpO2", +"darkred", "O2", "SpO2_1/2", +"darkred", "O2", "SpO2_1/3", +"darkred", "O2", "SpO2_2/2", +"darkred", "O2", "SpO2_2/3", +"darkred", "O2", "SpO2_3/3", +"green", "Resp", "Sum", +"magenta", "SV", "SV", +"darkgreen", "Temp", "Temp body", +"green", "Resp", "Thorax_1", +"green", "Resp", "Thorax_2", +"green", "Resp", "Thorax [CHMV]", +"green", "Resp", "THOR RES", +"blue", "BP", "UAP", +"blue", "BP", "UAP Dias", +"blue", "BP", "UAP Mean", +"blue", "BP", "UAP Sys", +"red", "ECG", "V", +"red", "ECG", "V ", +"red", "ECG", "V+", +"red", "ECG", "v1", +"red", "ECG", "V1", +"red", "ECG", "V1-V2", +"red", "ECG", "v2", +"red", "ECG", "V2", +"red", "ECG", "V2-V3", +"red", "ECG", "v3", +"red", "ECG", "V3", +"red", "ECG", "v4", +"red", "ECG", "V4", +"red", "ECG", "V4-V5", +"red", "ECG", "v5", +"red", "ECG", "V5", +"red", "ECG", "v6", +"red", "ECG", "V6", +"red", "ECG", "vx", +"red", "ECG", "vy", +"red", "ECG", "vz", +NULL, NULL, NULL +}; diff -Naur --exclude Makefile --exclude info wfdb-10.5.5/wfdbmap/wfdbmap.c wfdb-10.5.6/wfdbmap/wfdbmap.c --- wfdb-10.5.5/wfdbmap/wfdbmap.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.6/wfdbmap/wfdbmap.c 2009-03-30 02:22:56.000000000 -0400 @@ -0,0 +1,469 @@ +/* file: wfdbmap.c G. Moody 22 March 2009 + Last revised: 30 March 2009 + +------------------------------------------------------------------------------- +wfdbmap: generates a 'plt' script to make a PostScript map of a WFDB record +Copyright (C) 2009 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, write to the Free Software Foundation, Inc., 59 Temple +Place - Suite 330, Boston, MA 02111-1307, USA. + +You may contact the author by e-mail (george@mit.edu) 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 +#include +#include + +char *pname; +WFDB_Anninfo *ai = NULL; +WFDB_Siginfo *si = NULL; +int mflag = 0; +int spm; + +main(argc, argv) +int argc; +char *argv[]; +{ + char *record = NULL, *prog_name(); + int i, j, length, **map = NULL, nann = 0, nsig; + void help(); + void map_sig(char *record, WFDB_Siginfo *si, int nsig,int **map,int length); + void map_ann(char *record, WFDB_Anninfo *ai, int nann,int **map,int length); + void write_map(int **map, int nsig, int nann, int length); + void write_script(int **map, int nsig, int nann, int length); + + pname = prog_name(argv[0]); + + /* Interpret command-line options. */ + for (i = 1; i < argc; i++) { + if (*argv[i] == '-') switch (*(argv[i]+1)) { + case 'a': /* annotators follow */ + if (++i >= argc) { + (void)fprintf(stderr, "%s: annotators must follow -a\n", + pname); + exit(1); + } + /* how may annotators are there? */ + for (j = i; j < argc && *argv[j] != '-'; j++) + ; + nann = j - i; + /* allocate *ai and initialize it */ + if (nann > 0) { + if ((ai = malloc(nann * sizeof(WFDB_Anninfo))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + exit(2); + } + for (j = 0; j < nann; j++) { + ai[j].name = argv[i++]; + ai[j].stat = WFDB_READ; + } + } + i--; + break; + case 'h': /* print usage summary and quit */ + help(); + exit(0); + break; + case 'm': /* print map only */ + mflag = 1; + break; + case 'r': /* input record name follows */ + if (++i >= argc) { + (void)fprintf(stderr, + "%s: input record name must follow -r\n", + pname); + exit(1); + } + record = argv[i]; + 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); + } + } + if (record == NULL) { + help(); + exit(1); + } + + nsig = isigopen(record, NULL, 0); + if (nsig > 0) { + if ((si = malloc(nsig * sizeof(WFDB_Siginfo))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + if (ai) { wfdbquit(); free(ai); } + exit(2); + } + if ((nsig = isigopen(record, si, nsig)) < 0) + nsig = 0; + } + spm = strtim("60"); + length = (strtim("e") + (spm-1))/spm; /* number of minutes in record */ + + if (length < 1 && nsig > 0) { + /* calculate the length of the record from the signal file size */ + char *p; + double fs = 0; + FILE *ifile; + long bpm; + + for (i = 0; i < nsig && si[i].group == 0; i++) + switch (si[i].fmt) { + case 8: + case 80: fs += si[i].spf; break; + case 310: + case 311: fs += 1.33333333*si[i].spf; break; + case 212: fs += 1.5*si[i].spf; break; + default: fs += 2*si[i].spf; break; + } + bpm = fs * spm + 0.5; /* bytes per minute */ + p = wfdbfile(si[i].fname, NULL); + ifile = fopen(p, "r"); + fseek(ifile, 0L, SEEK_END); + length = ftell(ifile)/bpm; + fclose(ifile); + } + + if (length < 1) { /* try to get length from annotation file(s) */ + int alen; + WFDB_Annotation annot; + WFDB_Time t; + + for (i = 0; i < nann; i++) { + if (annopen(record, &ai[i], 1) < 0) continue; + while (getann(0, &annot) >= 0) + t = annot.time; + alen = (t + (spm-1))/spm; + if (alen > length) length = alen; + } + } + + if (length > 60000) /* 1000 hours > 41 days */ + length = 60000; /* truncate extremely long records */ + + if (length < 1) { + fprintf(stderr, "%s: can't map record %s (length unspecified)\n", + pname, record); + exit(1); + } + + if ((map = malloc((nsig + 4*nann) * sizeof(int *))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + wfdbquit(); + if (ai) free(ai); + if (si) free(si); + exit(2); + } + for (i = 0; i < nsig + 4*nann; i++) + if ((map[i] = calloc(length, sizeof(int))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + while (--i > 0) + if (map[i]) free(map[i]); + free(map); + wfdbquit(); + if (ai) free(ai); + if (si) free(si); + exit(2); + } + + if (nsig > 0) map_sig(record, si, nsig, map, length); + if (nann > 0) map_ann(record, ai, nann, &map[nsig], length); + + if (mflag) { + printf("T"); + for (i = 0; i < nsig; i++) + printf("\t%s", si[i].desc); + for (i = 0; i < nann; i++) + printf("\t%s\tQRS\tEctopic\tVE", ai[i].name); + printf("\n"); + write_map(map, nsig, nann, length); + } + else + write_script(map, nsig, nann, length); + + for (i = 0; i < nsig + 4*nann; i++) + free(map[i]); + free(map); + wfdbquit(); + if (ai) free(ai); + if (si) free(si); + + exit(0); +} + +void map_sig(char *record, WFDB_Siginfo *si, int nsig, int **map, int length) +{ + if (si[0].nsamp == strtim("e") || si[0].nsamp != 0) { + /* fixed-layout record */ + int i, t; + for (i = 0; i < nsig; i++) + for (t = 0; t < length; t++) + map[i][t] = 1; + } + else { /* variable-layout record */ + char buf[256], *d, *p, *q, *r, *hfname, *shfname; + long m, m0, mf, spm = strtim("60"), t = 0, tf; + FILE *ifile, *sfile; + + p = wfdbfile("hea", record); + hfname = calloc(strlen(p) + 1, 1); + strcpy(hfname, p); + if ((ifile = fopen(hfname, "r")) == NULL) { + fprintf(stderr, "%s: can't open %s\n", pname, hfname); + free(hfname); + return; + } + for (d = p + strlen(p); d > p; d--) + if (*(d-1) == '/') { + *d = '\0'; + break; + } + shfname = calloc(strlen(hfname) + 16, 1); + strcpy(shfname, p); + d = shfname + strlen(shfname); /* d points to first char after '/' */ + fgets(buf, sizeof(buf), ifile); /* read and ignore first two lines */ + fgets(buf, sizeof(buf), ifile); + + m0 = 0; + while (fgets(buf, sizeof(buf), ifile)) {/* read a segment descriptor */ + char *tp; + + if (buf[0] == '~') { /* segment is null (all signals off) */ + t += atol(buf+2); + m0 = t/spm; + continue; + } + for (tp = buf+1; *tp != ' '; tp++) + ; + *tp = '\0'; + tf = t + atol(tp+1); + if ((mf = tf/spm) > length) mf = length; + sprintf(d, "%s.hea", buf); + if (sfile = fopen(shfname, "r")) {/* open the segment header file */ + char sbuf[256]; + int i; + + fgets(sbuf, sizeof(sbuf), sfile);/* read & ignore first line */ + + while ((p = fgets(sbuf, sizeof(sbuf), sfile)) && *sbuf != '#') { + /* signal description line */ + for (q = sbuf, i = 0; *q; q++) + if (*q == ' ' && ++i == 8) break; + q++; + *(q + strlen(q) - 2) = '\0'; + for (i = 0; i < nsig; i++) + if (strcmp(si[i].desc, q) == 0) + for (m = m0; m < mf; m++) + map[i][m] = 1; + } + fclose(sfile); + t = tf; + m0 = t/spm; + } + } + fclose(ifile); + free(shfname); + free(hfname); + } +} + +int *namax; + +void map_ann(char *record, WFDB_Anninfo *ai, int nann, int **map, int length) +{ + int i; + + namax = malloc(nann * sizeof(int)); + wfdbquiet(); /* suppress warnings if an annotator can't be opened */ + for (i = 0; i < nann; i++) { + char tstring[10]; + int minutes, *na, *nq, *ne, *nv; + WFDB_Annotation annot; + WFDB_Time end_of_epoch; + + na = &map[4*i][0]; + namax[i] = 1; + + /* Open the annotators one at a time, and do not quit if any fail + to open. */ + if (annopen(record, &ai[i], 1) < 0) continue; + minutes = end_of_epoch = 0; + iannsettime(1L); + while (getann(0, &annot) >= 0) { + if (annot.time > end_of_epoch) { + if (*na > namax[i]) namax[i] = *na; + minutes = annot.time/spm; + na = &map[4*i][minutes]; + nq = &map[4*i + 1][minutes]; + ne = &map[4*i + 2][minutes]; + nv = &map[4*i + 3][minutes]; + if (++minutes > length) + break; /* stop at the end of the record */ + sprintf(tstring, "%d", 60*minutes); + end_of_epoch = strtim(tstring); + } + switch (annot.anntyp) { + case PVC: + case FUSION: + case VESC: + case RONT: + case FLWAV: (*nv)++; /* fall through, no break! */ + case APC: + case ABERR: + case NPC: + case SVPB: + case NESC: + case AESC: + case SVESC: (*ne)++; /* fall through, no break! */ + case NORMAL: + case LBBB: + case RBBB: + case PACE: + case UNKNOWN: + case BBB: + case LEARN: + case PFUS: (*nq)++; /* fall through, no break! */ + default: (*na)++; break; + } + } + if (*na > namax[i]) namax[i] = *na; + } + return; +} + +void write_map(int **map, int nsig, int nann, int length) +{ + int i, imax = nsig + 4*nann - 1, t; + + for (t = 0; t < length; t++) { + if (mflag == 0) printf("%d\t0\t", t); /* x0, y0 for histogram plots */ + printf("%d\t", t+1); + for (i = 0; i < imax; i++) + printf("%d\t", map[i][t]); + printf("%d\n", map[i][t]); + } +} + +#include "signal-colors.h" + +char *color(char *type) +{ + int i; + + for (i = 0; ctab[i].name != NULL; i++) + if (strcmp(type, ctab[i].name) == 0) return (ctab[i].color); + return ("grey"); +} + +void write_script(int **map, int nsig, int nann, int length) +{ + char *p, tstring[2][30]; + double ylow, ymax; + int i, j; + WFDB_Time tf = strtim("e"); + + strcpy(tstring[0], timstr(0)); + for (p = tstring[0]; *p == ' '; p++) + ; + if (tf) strcpy(tstring[1], timstr(-tf)); + else sprintf(tstring[1], "%d:%02d:00", length/60, length%60); + + ymax = 2*(nsig + 2*nann + 1); + ylow = 0.01 * ymax; + + printf("#! /bin/sh\n" + "cat >map.txt <= 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 -r RECORD -a ANNOTATOR [OPTIONS ...]\n", + "where RECORD and ANNOTATOR specify the input, and OPTIONS may include:", + " -h print this usage summary", +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]); +}