diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/bxb.c wfdb-10.5.5/app/bxb.c --- wfdb-10.5.4/app/bxb.c 2002-11-24 15:15:04.000000000 -0500 +++ wfdb-10.5.5/app/bxb.c 2010-08-11 08:51:18.000000000 -0400 @@ -1,9 +1,9 @@ /* file: bxb.c G. Moody 14 December 1987 - Last revised: 24 November 2002 + Last revised: 10 August 2010 ------------------------------------------------------------------------------- bxb: ANSI/AAMI-standard beat-by-beat annotation file comparator -Copyright (C) 2002 George B. Moody +Copyright (C) 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 @@ -1074,7 +1074,13 @@ sstat(" F missed in shutdown", "%6.2f", Fx, Fn+Fs+Fv+Ff+Fq+Fo+Fx); if (fflag == 1 || fflag == 3 || fflag == 4 || fflag == 6) (void)fprintf(sfile, " Total shutdown time: "); - (void)fprintf(sfile, "%5ld seconds\n", shut_down); + if (fflag != 2 && fflag != 5) + (void)fprintf(sfile, "%5ld seconds\n", shut_down); + else + (void)fprintf(sfile, "%5ld seconds %ld %ld %ld %ld %ld\n", shut_down, + Nn+Ns+Nv+Nf+Nq+No+Nx, Sn+Ss+Sv+Sf+Sq+So+Sx, + Vn+Vs+Vv+Vf+Vq+Vo+Vx, Fn+Fs+Fv+Ff+Fq+Fo+Fx, + Qn+Qs+Qv+Qf+Qq+Qo+Qx); } static char *help_strings[] = { diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/ecgeval.c wfdb-10.5.5/app/ecgeval.c --- wfdb-10.5.4/app/ecgeval.c 2006-11-30 10:09:01.000000000 -0500 +++ wfdb-10.5.5/app/ecgeval.c 2010-07-27 13:52:41.000000000 -0400 @@ -1,9 +1,9 @@ /* file: ecgeval.c G. Moody 22 March 1992 - Last revised: 30 November 2006 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- ecgeval: Generate and run a script of commands to compare sets of annotations -Copyright (C) 1992-2006 George B. Moody +Copyright (C) 1992-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 @@ -384,17 +384,19 @@ getans(tans, 20); if (tans[0] == 'a') { if ((sfile = fopen(scriptname, "a")) == NULL) - (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n"); + (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n", + scriptname); } else if (tans[0] == 'r') { if ((sfile = fopen(scriptname, "w")) == NULL) - (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n"); + (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n", + scriptname); } else sfile = NULL; } else if ((sfile = fopen(scriptname, "w")) == NULL) - (void)fprintf(stderr, "Sorry, I can't create `%s'.\n"); + (void)fprintf(stderr, "Sorry, I can't create `%s'.\n", scriptname); if (sfile == NULL) scriptname[0] = '\0'; @@ -424,17 +426,19 @@ getans(tans, 20); if (tans[0] == 'a') { if ((rfile = fopen(reportname, "a")) == NULL) - (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n"); + (void)fprintf(stderr, "Sorry, I can't append to `%s'.\n", + reportname); } else if (tans[0] == 'r') { if ((rfile = fopen(reportname, "w")) == NULL) - (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n"); + (void)fprintf(stderr, "Sorry, I can't replace `%s'.\n", + reportname); } else rfile = NULL; } else if ((rfile = fopen(reportname, "w")) == NULL) - (void)fprintf(stderr, "Sorry, I can't create `%s'.\n"); + (void)fprintf(stderr, "Sorry, I can't create `%s'.\n", reportname); if (rfile == NULL) reportname[0] = '\0'; diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/psfd.c wfdb-10.5.5/app/psfd.c --- wfdb-10.5.4/app/psfd.c 2009-05-27 15:47:52.000000000 -0400 +++ wfdb-10.5.5/app/psfd.c 2010-07-27 13:53:48.000000000 -0400 @@ -1,9 +1,9 @@ /* file: psfd.c G. Moody 9 August 1988 - Last revised: 27 May 2009 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- psfd: Produces annotated full-disclosure ECG plots on a PostScript device -Copyright (C) 1988-2009 George B. Moody +Copyright (C) 1988-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 @@ -379,7 +379,7 @@ if (Mflag < 0 || Mflag > 3) { (void)fprintf(stderr, "%s: incorrect format (%d) specified after -M\n", - pname); + pname, Mflag); exit(1); } } diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/rdsamp.c wfdb-10.5.5/app/rdsamp.c --- wfdb-10.5.4/app/rdsamp.c 2010-07-02 00:39:48.000000000 -0400 +++ wfdb-10.5.5/app/rdsamp.c 2010-10-06 22:01:25.000000000 -0400 @@ -1,5 +1,5 @@ /* file: rdsamp.c G. Moody 23 June 1983 - Last revised: 16 March 2010 + Last revised: 6 October 2010 ------------------------------------------------------------------------------- rdsamp: Print an arbitrary number of samples from each signal @@ -38,6 +38,12 @@ #define HHMMSS 7 #define SAMPLES 8 +#define WFDBXMLPROLOG "\n" \ + "\n" \ + "\n" + char *pname; main(argc, argv) @@ -47,7 +53,7 @@ char *record = NULL, *search = NULL, *escapify(), *prog_name(); char *invalid, *snfmt, *tfmt, *tnfmt, *tufmt, *vfmt, speriod[16], tustr[16]; int cflag = 0, highres = 0, i, isiglist, nsig, nosig = 0, pflag = 0, s, - *sig = NULL, timeunits = SECONDS, vflag = 0; + *sig = NULL, timeunits = SECONDS, vflag = 0, xflag = 0; WFDB_Frequency freq; WFDB_Sample *v; WFDB_Siginfo *si; @@ -132,6 +138,9 @@ case 'v': /* verbose output -- include column headings */ vflag = 1; break; + case 'X': /* output in WFDB-XML format */ + xflag = cflag = vflag = 1; /* format is CSV inside an XML wrapper */ + break; default: (void)fprintf(stderr, "%s: unrecognized option %s\n", pname, argv[i]); @@ -245,7 +254,6 @@ } else { /* output in raw units */ tnfmt = "'sample #'"; - // sprintf(tufmt, "'%g sec'", 1./freq); tfmt = "%ld"; vfmt = ",%d"; } @@ -293,12 +301,20 @@ } } + /* Print WFDB-XML prolog if '-x' option selected. */ + if (xflag) { + printf(WFDBXMLPROLOG); + printf("\n" + "%g\n" + "%d\n", + freq, nsig); + } /* Print column headers if '-v' option selected. */ if (vflag) { char *p, *t; int j, l; - (void)printf(tnfmt); + (void)printf("%s", tnfmt); for (i = 0; i < nsig; i++) { /* Check if a default signal description was provided by looking @@ -325,7 +341,7 @@ p = escapify(p); (void)printf(snfmt, p); } - + if (xflag) (void)printf(""); (void)printf("\n"); } @@ -338,7 +354,8 @@ if (vflag) { char s[12]; - (void)printf(tufmt); + if (xflag) (void)printf(""); + (void)printf("%s", tufmt); for (i = 0; i < nsig; i++) { p = si[sig[i]].units; @@ -360,10 +377,11 @@ (void)printf(",'%s'", p); } } - + if (xflag) (void)printf(""); (void)printf("\n"); } + if (xflag) (void)printf("\n", nsig+1); while ((to == 0L || from < to) && getvec(v) >= 0) { if (cflag == 0) { switch (timeunits) { @@ -412,13 +430,14 @@ (void)printf(vfmt, ((double)v[sig[i]] - si[sig[i]].baseline)/si[sig[i]].gain); else - (void)printf(invalid); + (void)printf("%s", invalid); } (void)printf("\n"); } } else { /* output in raw units */ + if (xflag) (void)printf("\n", nsig+1); while ((to == 0L || from < to) && getvec(v) >= 0) { (void)printf(tfmt, from++); for (i = 0; i < nsig; i++) @@ -427,6 +446,9 @@ } } + if (xflag) /* print trailer if WFDB-XML output was selected */ + printf("\n\n"); + exit(0); } @@ -494,6 +516,7 @@ " the time specified with -f, and begin printing then", " -t TIME stop at specified time", " -v print column headings", + " -X output in WFDB-XML format", NULL }; diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/snip.c wfdb-10.5.5/app/snip.c --- wfdb-10.5.4/app/snip.c 2009-04-17 01:50:17.000000000 -0400 +++ wfdb-10.5.5/app/snip.c 2010-07-27 13:59:43.000000000 -0400 @@ -1,8 +1,8 @@ /* file: snip.c G. Moody 30 July 1989 - Last revised: 17 April 2009 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- snip: Copy an excerpt of a database record -Copyright (C) 1989-2009 George B. Moody +Copyright (C) 1989-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 @@ -341,7 +341,7 @@ wfdb_fgets(buf, sizeof(buf), ihfile); /* Start writing the master output header. */ - fprintf(ohfile, "%s/%d %d %.12g %d", nrec, nseg, nsig, sfreq, to-from); + fprintf(ohfile, "%s/%d %d %.12g %ld", nrec, nseg, nsig, sfreq, to-from); if (tstring[0]) fprintf(ohfile, " %s", tstring); fprintf(ohfile, "\r\n"); diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/sortann.c wfdb-10.5.5/app/sortann.c --- wfdb-10.5.4/app/sortann.c 2009-02-04 10:54:28.000000000 -0500 +++ wfdb-10.5.5/app/sortann.c 2010-10-04 14:06:44.000000000 -0400 @@ -1,8 +1,8 @@ /* file sortann.c G. Moody 7 April 1997 - Last revised: 4 February 2009 + Last revised: 4 October 2010 ------------------------------------------------------------------------------- sortann: Rearrange annotations in canonical order -Copyright (C) 1997-2009 George B. Moody +Copyright (C) 1997-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 @@ -162,9 +162,9 @@ exit(1); } - /* By setting WFDBNOSORT, we ensure that wfdbquit won't invoke this program + /* By setting WFDBANNSORT, we ensure that wfdbquit won't invoke this program recursively if something goes wrong. */ - putenv("WFDBNOSORT=1"); + putenv("WFDBANNSORT=0"); if ((sps = sampfreq(record)) < 0.) (void)setsampfreq(sps = WFDB_DEFFREQ); @@ -245,9 +245,12 @@ (newp->annotation).aux = p; } if (lastp == &annlist || - pa->chan > (lastp->annotation).chan || - pa->num > (lastp->annotation).num || - pa->time > (lastp->annotation).time) { + pa->time > (lastp->annotation).time || + (pa->time == (lastp->annotation).time && + pa->num > (lastp->annotation).num) || + (pa->time == (lastp->annotation).time && + pa->num == (lastp->annotation).num && + pa->chan > (lastp->annotation).chan)) { /* this annotation is in order -- add to end of list */ newp->prev = lastp; lastp->next = newp; diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/sumstats.c wfdb-10.5.5/app/sumstats.c --- wfdb-10.5.4/app/sumstats.c 2005-08-19 21:25:59.000000000 -0400 +++ wfdb-10.5.5/app/sumstats.c 2010-08-10 22:28:17.000000000 -0400 @@ -1,8 +1,8 @@ /* file: sumstats.c G. Moody 17 August 1989 - Last revised: 19 August 2005 + Last revised: 10 August 2010 ------------------------------------------------------------------------------- sumstats: Derive aggregate statistics from bxb, rxr, or epic line-format output -Copyright (C) 1989-2005 George B. Moody +Copyright (C) 1989-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 @@ -34,7 +34,8 @@ #include #include -static int nrec, NQS, NQP, NVS, NVP, NVF, NSVS, NSVP, NRRE; +static int nrec, Nrec, Vrec, Frec; +static int NQS, NQP, NVS, NVP, NVF, NSVS, NSVP, NRRE; static long Nn, Ns, Nv, No, Nx, Sn, Ss, Sv, So, Sx, Vn, Vs, Vv, Vo, Vx, @@ -45,8 +46,9 @@ static long CTS, CFN, CTP, CFP, STS, SFN, STP, SFP, LTS, LFN, LTP, LFP; static long ETS, EFN, ETP, EFP; static long NCS, NCP, NSS, NSP, NLS, NLP, NES, NEP, NDS, NDP; +static long NT, VT, FT, QT; static long detected_episode_length, overlap, total_episode_length; -static double CQS, CQP, CVS, CVP, CVF, CSVS, CSVP, CRRE; +static double CQS, CQP, CVS, CVP, CVF, CSVS, CSVP, CRRE, CBM, CNM, CVM, CFM; static double CCS, CCP, CSS, CSP, CLS, CLP, CES, CEP, CDS, CDP, CERR, CMREF; char *pname; /* name by which this program was invoked */ @@ -178,6 +180,18 @@ (void)printf("______________\n"); (void)printf("Sum %4ld %4ld %4ld %4ld", Nx, Vx, Fx, Qx); (void)printf(" %4ld seconds\n", ST); + (void)printf("Gross "); + pstat(" %6.2f", (double)Nx+Vx+Fx+Qx, (double)(NT+VT+FT+QT)); + pstat(" %6.2f", (double)Nx, (double)(NT)); + pstat(" %6.2f", (double)Vx, (double)(VT)); + pstat(" %6.2f", (double)Fx, (double)(FT)); + (void)printf("\n"); + (void)printf("Average "); + pstat(" %6.2f", CBM, (double)nrec); + pstat(" %6.2f", CNM, (double)Nrec); + pstat(" %6.2f", CVM, (double)Vrec); + pstat(" %6.2f", CFM, (double)Frec); + (void)printf("\n"); break; case 3: /* rxr VE run-by-run table */ case 6: /* rxr SVE run-by-run table */ @@ -287,7 +301,8 @@ static char rts[20], tts[20]; static double rre, ds, dp, err, mref; static int cts, cfn, ctp, cfp, sts, sfn, stp, sfp, lts, lfn, ltp, lfp; - static int ets, efn, etp, efp; + static int dummy, nt, vt, ft, qt; + static long ets, efn, etp, efp; static long nn, sn, vn, fn, on, ns, ss, vs, fs, os; static long nv, sv, vv, fv, ov, no, so, vo, fo; static long nx, sx, vx, fx, qx, st; @@ -326,11 +341,19 @@ return (1); case 2: /* bxb -l shutdown report */ st = -1L; - (void)sscanf(s, "%s%ld%ld%ld%ld%s%s%s%s%ld", rec, - &nx, &vx, &fx, &qx, mb, mn, mv, mf, &st); + (void)sscanf(s, "%s%ld%ld%ld%ld%s%s%s%s%ld seconds %ld%ld%ld%ld%ld", + rec, &nx, &vx, &fx, &qx, mb, mn, mv, mf, &st, + &nt, &dummy, &vt, &ft, &qt); if (st < 0L) return (0); Nx += nx; Vx += vx; Fx += fx; Qx += qx; ST += st; - nrec++; + NT += nt; VT += vt; FT += ft; QT += qt; + if (nt + vt + ft + qt) { + CBM += (nx + vx + fx + qx)/(double)(nt + vt + ft + qt); nrec++; + if (nt) { CNM += nx/(double)nt; Nrec++; NT += nt; } + if (vt) { CVM += vx/(double)vt; Vrec++; VT += vt; } + if (ft) { CFM += fx/(double)ft; Frec++; FT += ft; } + QT += qt; + } return (1); case 3: /* rxr run-by-run report */ case 6: diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/wrann.c wfdb-10.5.5/app/wrann.c --- wfdb-10.5.4/app/wrann.c 2009-08-06 14:56:35.000000000 -0400 +++ wfdb-10.5.5/app/wrann.c 2010-07-27 14:06:58.000000000 -0400 @@ -1,9 +1,9 @@ /* file wrann.c G. Moody 6 July 1983 - Last revised: 6 August 2009 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- wrann: Translate an ASCII file in 'rdann' output format to an annotation file -Copyright (C) 1983-2009 George B. Moody +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 @@ -128,11 +128,11 @@ /* Create an aux string if needed. */ if (*annstr || ap) { if (*annstr && ap) - sprintf(a+1, "%s %s\0", annstr, ap+1); + sprintf(a+1, "%s %s", annstr, ap+1); else if (*annstr) - sprintf(a+1, "%s\0", annstr); + sprintf(a+1, "%s", annstr); else - sprintf(a+1, "%s\0", ap+1); + sprintf(a+1, "%s", ap+1); *a = strlen(a+1); annot.aux = a; } diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/app/wrsamp.c wfdb-10.5.5/app/wrsamp.c --- wfdb-10.5.4/app/wrsamp.c 2010-07-02 00:17:38.000000000 -0400 +++ wfdb-10.5.5/app/wrsamp.c 2010-10-06 22:04:19.000000000 -0400 @@ -1,5 +1,5 @@ /* file: wrsamp.c G. Moody 10 August 1993 - Last revised: 1 July 2010 + Last revised: 6 October 2010 ------------------------------------------------------------------------------- wrsamp: Select fields or columns from a file and generate a WFDB record @@ -33,6 +33,8 @@ #define DITHER (((double)rand() + (double)rand())/RAND_MAX - 1.0) char *pname; +unsigned int ncols = 0; +unsigned int nosig = 0; char *read_line(FILE *ifile, char rsep) { @@ -130,6 +132,8 @@ int i, m = (strlen(line) + 1)/2, n = 0, state = 0; char d, *p, *q = NULL; + if (m < nosig) m = nosig; + if (m < ncols) m = ncols; SSTRCPY(tbuf, line); p = tbuf-1; SREALLOC(ta, sizeof(int)*2 + sizeof(char *)*m, 1); @@ -205,13 +209,13 @@ { char **ap, *cp, **desc, *gain = "", *ifname = "(stdin)", *line = NULL, ofname[40], *p, *record = NULL, rsep = '\n', - *scale = "", sflag = 0, trim = 0, **units, *prog_name(); - static char btime[25]; + *scale = "", sflag = 0, trim = 0, Xflag = 0, **units, *prog_name(); + static char btime[25], **dstrings, **ustrings; double freq = WFDB_DEFFREQ, *scalef, v; #ifndef atof double atof(); #endif - int c, cf = 0, dflag = 0, format = 16, *fv = NULL, i, labels, mf, zflag = 0; + int c, cf = 0, dflag = 0, format = 16, *fv = NULL, i, j, mf, zflag = 0; FILE *ifile = stdin; long t = 0L, t0 = 0L, t1 = 0L; #ifndef atol @@ -220,7 +224,6 @@ Tokenarray *ta; WFDB_Sample *vout; WFDB_Siginfo *si; - unsigned int nf = 0; void help(); pname = prog_name(argv[0]); @@ -338,7 +341,11 @@ } } - /* read the first line of the input file */ + nosig = argc - i; /* any remaining arguments are column numbers */ + + /* Determine the number of input columns, and (if present) the signal + names and the units for each signal. Start by reading the first line + of the input file. */ if ((line = read_line(ifile, rsep)) == NULL) { if (rsep != '\n') { if (record == NULL) @@ -355,69 +362,130 @@ (void)fprintf(stderr, "%s: no newlines in input\n", pname); exit(3); } - - /* unless -s was given, note if it contains any tab characters */ - if (sflag == 0 && line_has_tab(line)) - defpmode.delim = "\t"; - - /* note if it contains any alphabetic characters */ - labels = line_has_alpha(line); - - /* parse it into tokens */ - ta = parseline(line, NULL); + /* Recognize WFDB-XML format if present. */ + if (strncmp(line, " found\n", + pname); + exit(1); + } + } while (strcmp(line, "")); + do { + if ((line = read_line(ifile, rsep)) == NULL) { + (void)fprintf(stderr, "%s: empty \n", pname); + exit(1); + } + if (strncmp(line, "", 19) == 0) + sscanf(line+19, "%lf", &freq); + else if (strncmp(line, "", 13) == 0) { + line[strlen(line)-14] = '\0'; /* strip */ + ta = parseline(line+13, NULL); /* skip */ + SUALLOC(dstrings, ta->ntokens, sizeof(char *)); + for (i = 0; i < ta->ntokens; i++) + SSTRCPY(dstrings[i], ta->token[i]); + ncols = ta->ntokens; + } + else if (strncmp(line, "", 7) == 0) { + line[strlen(line)-8] = '\0'; /* strip */ + ta = parseline(line+7, NULL); /* skip */ + SUALLOC(ustrings, ta->ntokens, sizeof(char *)); + for (i = 0; i < ta->ntokens; i++) + SSTRCPY(ustrings[i], ta->token[i]); + } + } while (strcmp(line, "")); + if ((line = read_line(ifile, rsep)) == NULL) { + (void)fprintf(stderr, "%s: empty \n", pname); + exit(1); + } + } + else { /* non-XML input */ + /* Unless -s was given, note if line 0 contains any tab characters. */ + if (sflag == 0 && line_has_tab(line)) + defpmode.delim = "\t"; + /* 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); + exit(1); + } + /* If it has any alphabetic characters, save the unit strings. */ + else if (line_has_alpha(line)) { + ta = parseline(line, NULL); + SALLOC(ustrings, ta->ntokens, sizeof(char *)); + for (i = 0; i < ta->ntokens; i++) { + while (*(ta->token[i]) == ' ') + (ta->token[i])++; + if (*(ta->token[i]) == '(') + (ta->token[i])++; + SSTRCPY(ustrings[i], ta->token[i]); + } + /* Read the third line. */ + if ((line = read_line(ifile, rsep)) == NULL) { + (void)fprintf(stderr, "%s: no data\n", pname); + exit(1); + } + } + } + } + /* read selected column numbers into fv[...] */ - if (i < argc) { - SUALLOC(fv, argc - i, sizeof(int)); - while (i < argc) { - if (sscanf(argv[i++], "%d", &fv[nf]) != 1 || - fv[nf] < 0 || fv[nf] >= ta->ntokens) { + if (nosig) { + SUALLOC(fv, nosig, sizeof(int)); + for (i = argc - nosig, j = 0; i < argc; i++, j++) { + if (sscanf(argv[i], "%d", &fv[j]) != 1 || + fv[j] < 0 || fv[j] >= ncols) { (void)fprintf(stderr, "%s: unrecognized argument %s\n", - pname, argv[--i]); + pname, argv[i]); exit(1); } - nf++; } } - /* if no columns were specified, copy all columns (or all except 0) */ - else { - int i, j; - - nf = ta->ntokens - zflag; - SUALLOC(fv, nf, sizeof(int)); - for (i = 0, j = zflag; i < nf; i++, j++) + else { /* copy all column numbers (or all except 0) into fv[...] */ + nosig = ncols - zflag; + SUALLOC(fv, nosig, sizeof(int)); + for (i = 0, j = zflag; i < nosig; i++, j++) fv[i] = j; } - /* allocate arrays */ - SUALLOC(vout, nf, sizeof(WFDB_Sample)); - SUALLOC(si, nf, sizeof(WFDB_Siginfo)); - SUALLOC(scalef, nf, sizeof(double)); + SUALLOC(vout, nosig, sizeof(WFDB_Sample)); + SUALLOC(si, nosig, sizeof(WFDB_Siginfo)); + SUALLOC(scalef, nosig, sizeof(double)); - /* open the output record */ + /* open the output record */ if (record == NULL) (void)sprintf(ofname, "-"); else (void)sprintf(ofname, "%s.dat", record); - for (i = 0; i < nf; i++) { + + for (i = 0; i < nosig; i++) { si[i].fname = ofname; - si[i].desc = NULL; - if (labels) { /* set the signal descriptions from the column headings */ - char *p = ta->token[fv[i]], *q; - - while (*p == ' ') p++; - q = p + strlen(p); - while (*(q-1) == ' ') q--; - *q = '\0'; - SSTRCPY(si[i].desc, p); + if (dstrings) { + SSTRCPY(si[i].desc, dstrings[fv[i]]); } else { char tdesc[16]; - - (void)sprintf(tdesc, "olumn %d", fv[i]); + + (void)sprintf(tdesc, "col %d", fv[i]); SSTRCPY(si[i].desc, tdesc); } - si[i].units = ""; + if (ustrings) { + SSTRCPY(si[i].units, ustrings[fv[i]]); + } + else + si[i].units = ""; si[i].group = 0; si[i].fmt = format; si[i].spf = 1; @@ -449,39 +517,11 @@ scale++; } - if (labels) { /* read the second line of input */ - if ((line = read_line(ifile, rsep)) == NULL) { - (void)fprintf(stderr, "%s: no input data\n", pname); - exit(4); - } - if (line_has_alpha(line)) { - ta = parseline(line, NULL); - /* Copy units strings after trimming any surrounding spaces, - parentheses, or brackets, and after replacing embedded spaces - with underscores. */ - for (i = 0; i < nf; i++) { - char *p = ta->token[fv[i]], *q; - - while (*p == ' ') p++; - if (*p == '(' || *p == '[') p++; - q = p + strlen(p); - while (*(q-1) == ' ') q--; - if (*(q-1) == ')' || *(q-1) == ']') q--; - *q = '\0'; - while (--q > p) - if (*q == ' ') *q = '_'; - si[i].units = NULL; - SSTRCPY(si[i].units, p); - } - line = read_line(ifile, rsep); - } - } - /* discard any additional lines containing text */ while (line_has_alpha(line)) line = read_line(ifile, rsep); - if (osigfopen(si, nf) < nf || setsampfreq(freq) < 0) + if (osigfopen(si, nosig) < nosig || setsampfreq(freq) < 0) exit(2); /* skip any unwanted samples at the beginning */ @@ -503,7 +543,8 @@ /* read and copy samples */ while (line != NULL && (t1 == 0L || t++ < t1)) { ta = parseline(line, NULL); - for (i = 0; i < nf; i++) { + + for (i = 0; i < nosig; i++) { double v; if (sscanf(ta->token[fv[i]], "%lf", &v) == 1) { @@ -517,7 +558,16 @@ vout[i] = WFDB_INVALID_SAMPLE; } if (putvec(vout) < 0) break; + line = read_line(ifile, rsep); + if (line && Xflag && strncmp(line, "", 16) == 0) { + if (line = read_line(ifile, rsep)) + if (strcmp(line, "")) { + fprintf(stderr, + "%s: (warning) unexpected EOF in XML input\n", pname); + } + break; + } } /* write the header */ diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/check-manifest wfdb-10.5.5/check-manifest --- wfdb-10.5.4/check-manifest 2005-08-12 01:53:16.000000000 -0400 +++ wfdb-10.5.5/check-manifest 2010-07-27 17:17:18.000000000 -0400 @@ -1,14 +1,16 @@ #!/bin/sh # file: check-manifest G. Moody 14 December 2001 -# Last revised: 1 November 2002 +# Last revised: 27 July 2010 # Verify tarball contents PACKAGE=$1 -sort -f <../${PACKAGE}-MANIFEST >../sort.$$ +sort -f ../expected-MANIFEST +sort -f <../${PACKAGE}-MANIFEST | grep . | sed 's+/$++' | \ + grep -v ${PACKAGE} >../sort.$$ mv ../sort.$$ ../${PACKAGE}-MANIFEST -if diff MANIFEST ../${PACKAGE}-MANIFEST +if diff ../expected-MANIFEST ../${PACKAGE}-MANIFEST then - rm -f ../${PACKAGE}-MANIFEST + rm -f ../expected-MANIFEST ../${PACKAGE}-MANIFEST cat <lib/Makefile fi for D in app checkpkg convert data doc doc/wag-src doc/wpg-src doc/wug-src \ - examples fortran psd wave waverc . + examples fortran psd wave waverc xml . do if [ -s $D/Makefile.top ] then diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/a2m.c wfdb-10.5.5/convert/a2m.c --- wfdb-10.5.4/convert/a2m.c 2002-07-24 23:15:46.000000000 -0400 +++ wfdb-10.5.5/convert/a2m.c 2010-07-27 14:10:16.000000000 -0400 @@ -1,9 +1,9 @@ /* file: a2m.c G. Moody 9 June 1983 - Last revised: 24 July 2002 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- a2m: Convert an AHA format annotation file to MIT format -Copyright (C) 2002 George B. Moody +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 @@ -135,7 +135,8 @@ 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"); + (void)fprintf(stderr, "%s: file type (0-3) must follow -t\n", + pname); exit(1); } break; diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/ahaecg2mit.c wfdb-10.5.5/convert/ahaecg2mit.c --- wfdb-10.5.4/convert/ahaecg2mit.c 2008-07-23 15:03:14.000000000 -0400 +++ wfdb-10.5.5/convert/ahaecg2mit.c 2010-08-24 23:15:06.000000000 -0400 @@ -1,5 +1,5 @@ /* file: ahaecg2mit.c G. Moody 7 May 2008 - +. Last revised: 24 August 2010 Convert a *.ecg file from an AHA Database DVD to WFDB-compatible format */ @@ -10,7 +10,7 @@ main(int argc, char **argv) { char *p, *record; - int i, sflag; + int i, sflag = 0; FILE *ifile; void process(char *r, FILE *f); @@ -84,6 +84,6 @@ } (void)newheader(record); wfdbquit(); - fprintf(stderr, "wrote %s.atr, $s.dat, and %s.hea\n", record,record,record); + fprintf(stderr, "wrote %s.atr, %s.dat, and %s.hea\n", record,record,record); return; } diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/mit2edf.c wfdb-10.5.5/convert/mit2edf.c --- wfdb-10.5.4/convert/mit2edf.c 2009-03-14 23:44:35.000000000 -0400 +++ wfdb-10.5.5/convert/mit2edf.c 2010-07-27 14:13:11.000000000 -0400 @@ -1,8 +1,8 @@ /* file: mit2edf.c G. Moody 2 November 2002 - Last revised: 14 March 2009 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- Convert MIT format header and signal files to EDF (European Data Format) file -Copyright (C) 2002-2009 George B. Moody +Copyright (C) 2002-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 @@ -328,7 +328,7 @@ /* Number of samples per block. */ for (i = 0; i < nsig; i++, p += 8) { - sprintf(buf, "%d", frames_per_block * si[i].spf); + sprintf(buf, "%ld", frames_per_block * si[i].spf); strncpy(p, buf, strlen(buf)); } @@ -345,7 +345,7 @@ if (header[i] < 32 || header[i] > 126) fprintf(stderr, "WARNING (%s): output contains an invalid character, %d," - " at byte %ld\n", pname, header[i], i); + " at byte %d\n", pname, header[i], i); /* In verbose mode, summarize what we've done so far. */ if (vflag) { diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/mit2wav.c wfdb-10.5.5/convert/mit2wav.c --- wfdb-10.5.4/convert/mit2wav.c 2009-06-10 12:21:03.000000000 -0400 +++ wfdb-10.5.5/convert/mit2wav.c 2010-07-27 14:14:36.000000000 -0400 @@ -1,8 +1,8 @@ /* file: mit2wav.c G. Moody 12 February 2003 - + Last revised: 27 July 2010 ------------------------------------------------------------------------------- mit2wav: Convert WFDB format signal file(s) to .wav format -Copyright (C) 2003 George B. Moody +Copyright (C) 2003-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 @@ -254,14 +254,15 @@ (void)strcpy(tfname, "wavXXXXXX"); (void)mkstemp(tfname); if ((ofile = fopen(tfname, "wb")) == NULL) { - (void)fprintf(stderr, "%s: can't create temporary file %s\n", tfname); + (void)fprintf(stderr, "%s: can't create temporary file %s\n", + pname, tfname); exit(1); } /* Write the header and format chunks, and the first 8 bytes of the data chunk, to the temporary file. */ if (fwrite("RIFF", 1, 4, ofile) != 4) { - fprintf(stderr, "%s: can't write to %s\n", ofname); + fprintf(stderr, "%s: can't write to %s\n", pname, ofname); exit(2); } out32(nsamp*framelen + 36); /* nsamp*framelen sample bytes, and 36 more diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/convert/wfdb2mat.c wfdb-10.5.5/convert/wfdb2mat.c --- wfdb-10.5.4/convert/wfdb2mat.c 2010-03-16 11:37:26.000000000 -0400 +++ wfdb-10.5.5/convert/wfdb2mat.c 2010-07-27 14:16:20.000000000 -0400 @@ -1,5 +1,5 @@ /* file: wfdb2mat.c G. Moody 26 February 2009 - Last revised: 16 March 2010 + Last revised: 27 July 2010 ------------------------------------------------------------------------------- wfdb2mat: Convert (all or part of) a WFDB signal file to Matlab .mat format Copyright (C) 2009-2010 George B. Moody @@ -337,7 +337,7 @@ /* Summarize the contents of the .mat file. */ printf("%s\n", p); - printf("val has %d row%s (signal%s) and %d column%s (sample%s/signal)\n", + printf("val has %d row%s (signal%s) and %ld column%s (sample%s/signal)\n", nosig, nosig == 1 ? "" : "s", nosig == 1 ? "" : "s", to-from, to == from+1 ? "" : "s", to == from+1 ? "" : "s"); printf("Duration: %s\n", timstr(to-from)); diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/doc/wag-src/rdsamp.1 wfdb-10.5.5/doc/wag-src/rdsamp.1 --- wfdb-10.5.4/doc/wag-src/rdsamp.1 2009-11-06 12:18:06.000000000 -0500 +++ wfdb-10.5.5/doc/wag-src/rdsamp.1 2010-08-25 23:31:06.000000000 -0400 @@ -1,4 +1,4 @@ -.TH RDSAMP 1 "6 November 2009" "WFDB 10.4.25" "WFDB Applications Guide" +.TH RDSAMP 1 "25 August 2010" "WFDB 10.5.5" "WFDB Applications Guide" .SH NAME rdsamp \- read WFDB signal files .SH SYNOPSIS @@ -91,6 +91,11 @@ columns identifiable). Names of units are shortened when necessary by omitting the final characters, since the initial characters are usually most important for distinguishing different units. +.TP +\fB-X\fR +Produce output in WFDB-XML format (same as the CSV format produced using +the \fB-c\fR option, but wrapped within an XML header and trailer). This +format is recognized and parsed automatically by \fBwrsamp\fR. .SH ENVIRONMENT .PP diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/lib/wfdb.h wfdb-10.5.5/lib/wfdb.h --- wfdb-10.5.4/lib/wfdb.h 2010-07-14 01:28:54.000000000 -0400 +++ wfdb-10.5.5/lib/wfdb.h 2010-10-06 22:45:29.000000000 -0400 @@ -32,7 +32,7 @@ /* WFDB library version. */ #define WFDB_MAJOR 10 #define WFDB_MINOR 5 -#define WFDB_RELEASE 4 +#define WFDB_RELEASE 5 #define WFDB_NETFILES 1 /* if 1, library includes code for HTTP, FTP clients */ #define WFDB_NETFILES_LIBCURL 1 diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/Makefile.tpl wfdb-10.5.5/Makefile.tpl --- wfdb-10.5.4/Makefile.tpl 2009-10-28 13:19:00.000000000 -0400 +++ wfdb-10.5.5/Makefile.tpl 2010-10-06 22:30:57.000000000 -0400 @@ -1,5 +1,5 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 10 October 2008 +# Last revised: 6 October 2010 # This section of the Makefile should not need to be changed. # 'make' or 'make all': compile the WFDB applications without installing them @@ -16,6 +16,7 @@ cd psd; $(MAKE) install -( cd wave; $(MAKE) install ) cd waverc; $(MAKE) install + -( cd xml; $(MAKE) install ) test -d doc && ( cd doc; $(MAKE) install ) # 'make collect': collect the installed files into /tmp/wfdb/ @@ -28,6 +29,7 @@ cd psd; $(MAKE) collect -( cd wave; $(MAKE) collect ) cd waverc; $(MAKE) collect + -( cd xml; $(MAKE) collect ) test -d doc && ( cd doc; $(MAKE) collect ) uninstall: config.cache @@ -39,6 +41,7 @@ cd psd; $(MAKE) uninstall cd wave; $(MAKE) uninstall cd waverc; $(MAKE) uninstall + cd xml; $(MAKE) uninstall test -d doc && ( cd doc; $(MAKE) uninstall ) ./uninstall.sh $(WFDBROOT) @@ -54,6 +57,7 @@ cd psd; $(MAKE) clean cd wave; $(MAKE) clean cd waverc; $(MAKE) clean + cd xml; $(MAKE) clean test -d doc && ( cd doc; $(MAKE) clean ) cd conf; rm -f *~ prompt site.def site-slib.def rm -f *~ config.cache */*.exe $(PACKAGE)-*.spec @@ -116,9 +120,11 @@ rm -f ../$(PACKAGE)-MANIFEST ../$(PACKAGE).tar.gz \ ../$(PACKAGE)-no-docs.tar.gz cd lib; $(SETPERMISSIONS) *.h - cd ..; tar --create --file $(PACKAGE).tar.gz --verbose --gzip \ - '--exclude=$(PACKAGE)/*CVS' $(PACKAGE) | sed s+${PACKAGE}/++ | \ - tee $(PACKAGE)-MANIFEST + cd ..; export COPYFILE_DISABLE=true; \ + tar --create --file $(PACKAGE).tar.gz --verbose --gzip \ + '--exclude=$(PACKAGE)/*CVS' $(PACKAGE) 2>&1 | \ + sed "s+^a ++" | sed s+${PACKAGE}/++ | \ + tee $(PACKAGE)-MANIFEST cd ..; tar --create --file $(PACKAGE)-no-docs.tar.gz \ --verbose --gzip \ '--exclude=$(PACKAGE)/*doc' \ diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/MANIFEST wfdb-10.5.5/MANIFEST --- wfdb-10.5.4/MANIFEST 2009-05-03 15:22:59.000000000 -0400 +++ wfdb-10.5.5/MANIFEST 2010-10-06 22:38:59.000000000 -0400 @@ -1,5 +1,4 @@ - -app/ +app app/12lead.pro app/ann2rr.c app/bxb.c @@ -55,10 +54,10 @@ app/wrsamp.c app/xform.c check-manifest -checkpkg/ +checkpkg checkpkg/appcheck checkpkg/checkfile -checkpkg/expected/ +checkpkg/expected checkpkg/expected/100a.nguess checkpkg/expected/100s.a2r checkpkg/expected/100s.mix @@ -97,7 +96,7 @@ checkpkg/expected/sumann.out checkpkg/expected/sumstats.out checkpkg/expected/tach.out -checkpkg/expected/udb/ +checkpkg/expected/udb checkpkg/expected/udb/100z.hea checkpkg/expected/wfd00001.dat checkpkg/expected/wfd00001.hea @@ -113,7 +112,7 @@ checkpkg/expected/xform.dat checkpkg/expected/xform.hea checkpkg/expected/xform.wabp -checkpkg/input/ +checkpkg/input checkpkg/input/100x.hea checkpkg/input/ecgeval checkpkg/input/sumstats @@ -123,8 +122,8 @@ checkpkg/Makefile checkpkg/Makefile.top checkpkg/Makefile.tpl -checkpkg/mitdb/ -conf/ +checkpkg/mitdb +conf conf/archname conf/collect.sh conf/cygwin.def @@ -150,7 +149,7 @@ conf/solaris.def conf/solaris-slib.def conf/version.def -convert/ +convert convert/a2m.c convert/ad2m.c convert/ahaconvert @@ -171,7 +170,7 @@ convert/wav2mit.c convert/wfdb2mat.c COPYING -data/ +data data/100a.atr data/100a.hea data/100s.atr @@ -194,7 +193,7 @@ data/multi.hea data/nstlist data/null.hea -data/pipe/ +data/pipe data/pipe/16x10.hea data/pipe/16x11.hea data/pipe/16x12.hea @@ -228,7 +227,7 @@ data/pipe/8x8.hea data/pipe/8x9.hea data/README -data/tape/ +data/tape data/tape/10240.hea data/tape/1024.hea data/tape/4096.hea @@ -238,13 +237,13 @@ data/tape/mittape.hea data/wfdbcal data/wfdbpath.mac -doc/ +doc doc/Makefile doc/Makefile.top doc/Makefile.tpl -doc/misc/ +doc/misc doc/misc/foot.ht0 -doc/misc/icons/ +doc/misc/icons doc/misc/icons/contents.png doc/misc/icons/cross_ref.png doc/misc/icons/foot.png @@ -257,8 +256,8 @@ doc/misc/icons/up.png doc/misc/index.ht0 doc/README -doc/wag/ -doc/wag-src/ +doc/wag +doc/wag-src doc/wag-src/a2m.1 doc/wag-src/ann2rr.1 doc/wag-src/annot.5 @@ -353,10 +352,10 @@ doc/wag-src/wrsamp.1 doc/wag-src/xform.1 doc/wag-src/xview.7 -doc/wpg/ -doc/wpg/info/ +doc/wpg +doc/wpg/info doc/wpg/info/README.info -doc/wpg-src/ +doc/wpg-src doc/wpg-src/ctotexi.c doc/wpg-src/dir.top doc/wpg-src/dir.wpg @@ -372,25 +371,25 @@ doc/wpg-src/wpg.cover doc/wpg-src/wpg.hlp doc/wpg-src/wpg.ht0 -doc/wug/ -doc/wug-src/ +doc/wug +doc/wug-src doc/wug-src/fancybox.perl doc/wug-src/.latex2html-init doc/wug-src/Makefile doc/wug-src/Makefile.top doc/wug-src/Makefile.tpl doc/wug-src/README -doc/wug-src/wave/ -doc/wug-src/wave/misc/ +doc/wug-src/wave +doc/wug-src/wave/misc doc/wug-src/wave/misc/example.xws doc/wug-src/wave/misc/html.sty doc/wug-src/wave/misc/pstoimg doc/wug-src/wave/misc/wave.inf -doc/wug-src/wave/png/ +doc/wug-src/wave/png doc/wug-src/wave/png/chart2.png doc/wug-src/wave/png/fulldisc.png doc/wug-src/wave/png/wave.png -doc/wug-src/wave/ppm/ +doc/wug-src/wave/ppm doc/wug-src/wave/ppm/allow-edit.ppm.gz doc/wug-src/wave/ppm/analysis-commands.ppm.gz doc/wug-src/wave/ppm/analyze-window.ppm.gz @@ -432,11 +431,11 @@ doc/wug-src/wave/ppm/view-window.ppm.gz doc/wug-src/wave/ppm/wave-icon.ppm.gz doc/wug-src/wave/ppm/wave-menu.ppm.gz -doc/wug-src/wave/ps/ +doc/wug-src/wave/ps doc/wug-src/wave/ps/chart1.ps doc/wug-src/wave/ps/chart2.ps doc/wug-src/wave/ps/fulldisc.ps -doc/wug-src/wave/scripts/ +doc/wug-src/wave/scripts doc/wug-src/wave/scripts/dossify-html doc/wug-src/wave/scripts/fixinfo doc/wug-src/wave/scripts/fixlinks @@ -446,7 +445,7 @@ doc/wug-src/wave/scripts/wugfigures doc/wug-src/wug0.tex doc/wug-src/wug.cover -examples/ +examples examples/example10.c examples/example1.c examples/example2.c @@ -471,7 +470,7 @@ examples/refhr.c examples/stdev.c examples/wfdbversion.c -fortran/ +fortran fortran/example.f fortran/fsamples.f fortran/Makefile @@ -482,7 +481,7 @@ INSTALL install.sh install-wave32 -lib/ +lib lib/annot.c lib/calib.c lib/COPYING.LIB @@ -504,7 +503,7 @@ Makefile.tpl MANIFEST NEWS -psd/ +psd psd/coherence.c psd/fft.c psd/hrfft @@ -524,7 +523,7 @@ README.NETFILES README.WINDOWS uninstall.sh -wave/ +wave wave/analysis.hlp wave/analyze.c wave/annot.c @@ -551,7 +550,7 @@ wave/modepan.c wave/nomake wave/printing.hlp -waverc/ +waverc waverc/Makefile waverc/Makefile.top waverc/Makefile.tpl @@ -577,3 +576,15 @@ wave/xvwave.c wave/xvwave.h wfdb.spec +xml +xml/annxml.c +xml/heaxml.c +xml/Makefile +xml/Makefile.top +xml/Makefile.tpl +xml/README +xml/wfdb.dtd +xml/wfdb.xsl +xml/xmlann.c +xml/xmlhea.c +xml/xmlproc.h diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/NEWS wfdb-10.5.5/NEWS --- wfdb-10.5.4/NEWS 2010-07-14 02:29:21.000000000 -0400 +++ wfdb-10.5.5/NEWS 2010-10-06 23:37:51.000000000 -0400 @@ -1,3 +1,40 @@ +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 + converted to WFDB-compatible format using wrsamp (which recognizes + this format automatically, requiring no special option). A document + type description of this format, and of XML formats for annotation + 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 + 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 + fixed; thanks to Jesus Maria Rodriguez Presedo for reporting it and + for providing a test case. + + Changes in conf/darwin.def and conf/darwin-slib.def allow creation + of multi-architecture (i386, x86_64, and ppc) binaries on Mac OS X. + + Virginia Faro-Maza pointed out that sumstats did not derive aggregate + shutdown statistics described in ANSI/AAMI EC57, and that bxb did not + preserve the raw data needed to derive these statistics. Both bxb.c + and sumstats.c have been revised in this release to remedy these + omissions. + + An uninitialized flag may have made ahaecg2mit behave as if its -s + option was always given, and an informational message contained + 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 + 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 10.5.4, to allow applications to obtain information about the diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/psd/lomb.c wfdb-10.5.5/psd/lomb.c --- wfdb-10.5.4/psd/lomb.c 2005-06-10 09:52:21.000000000 -0400 +++ wfdb-10.5.5/psd/lomb.c 2010-07-27 14:20:51.000000000 -0400 @@ -1,9 +1,8 @@ /* file: lomb.c G. Moody 12 February 1992 - Last revised: 10 June 2005 - + Last revised: 27 July 2010 ------------------------------------------------------------------------------- lomb: Lomb periodogram of real data -Copyright (C) 1992-2005 George B. Moody +Copyright (C) 1992-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 @@ -469,34 +468,34 @@ if (64 * nmaxt * sizeof(float) < nmax) { fprintf(stderr, - "%s: insufficient memory, truncating input at row %d\n", + "%s: insufficient memory, truncating input at row %lu\n", pname, npts); break; } if ((xt = (float *)realloc(x, nmaxt * sizeof(float))) == NULL) { fprintf(stderr, - "%s: insufficient memory, truncating input at row %d\n", + "%s: insufficient memory, truncating input at row %lu\n", pname, npts); break; } x = xt; if ((yt = (float *)realloc(y, nmaxt * sizeof(float))) == NULL) { fprintf(stderr, - "%s: insufficient memory, truncating input at row %d\n", + "%s: insufficient memory, truncating input at row %lu\n", pname, npts); break; } y = yt; if ((w1t = (float *)realloc(wk1,64*nmaxt*sizeof(float))) == NULL){ fprintf(stderr, - "%s: insufficient memory, truncating input at row %d\n", + "%s: insufficient memory, truncating input at row %lu\n", pname, npts); break; } wk1 = w1t; if ((w2t = (float *)realloc(wk2,64*nmaxt*sizeof(float))) == NULL){ fprintf(stderr, - "%s: insufficient memory, truncating input at row %d\n", + "%s: insufficient memory, truncating input at row %lu\n", pname, npts); break; } diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/wfdb.spec wfdb-10.5.5/wfdb.spec --- wfdb-10.5.4/wfdb.spec 2009-05-03 23:39:12.000000000 -0400 +++ wfdb-10.5.5/wfdb.spec 2010-10-06 22:45:00.000000000 -0400 @@ -15,6 +15,9 @@ BuildRoot: /var/tmp/%{name}-root %changelog +* Wed Oct 6 2010 George B Moody +- added annxml, heaxml, xmlann, xmlhea + * Sun May 3 2009 George B Moody - moved wfdb-config from devel to apps @@ -139,6 +142,7 @@ %{_bindir}/ahaconvert %{_bindir}/ahaecg2mit %{_bindir}/ann2rr +%{_bindir}/annxml %{_bindir}/bxb %{_bindir}/calsig %{_bindir}/coherence @@ -148,6 +152,7 @@ %{_bindir}/epicmp %{_bindir}/fft %{_bindir}/fir +%{_bindir}/heaxml %{_bindir}/hrfft %{_bindir}/hrlomb %{_bindir}/hrmem @@ -208,6 +213,8 @@ %{_bindir}/wrann %{_bindir}/wrsamp %{_bindir}/xform +%{_bindir}/xmlann +%{_bindir}/xmlhea %{_libdir}/ps %{_mandir} diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/annxml.c wfdb-10.5.5/xml/annxml.c --- wfdb-10.5.4/xml/annxml.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/annxml.c 2010-07-01 21:22:14.000000000 -0400 @@ -0,0 +1,212 @@ +/* file: annxml.c G. Moody 28 June 2010 + +------------------------------------------------------------------------------- +heaxml: Convert a WFDB annotation file to XML format +Copyright (C) 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, 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 + +#define WFDBXMLPROLOG "\n" \ + "\n" \ + "\n" + +char *token(char *p) +{ + if (p) { + while (*p && *p != ' ' && *p != '\t' && *p != '\n') + p++; /* find whitespace */ + while (*p && (*p == ' ' || *p == '\t' || *p == '\n')) + p++; /* find first non-whitespace */ + if (*p == '\0') p = NULL; + } + return (p); +} + +void output_xml(FILE *ofile, char *tag, char *p) +{ + if (p) { + fprintf(ofile, "<%s>", tag); + while (*p) { + if (*p == '<') fprintf(ofile, "<"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '&') fprintf(ofile, "&"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '"') fprintf(ofile, """); + else if (*p == '\'') fprintf(ofile, "'"); + else fprintf(ofile, "%c", *p); + p++; + } + fprintf(ofile, "", tag); + } + return; +} + +int nsig; +FILE *ofile; +WFDB_Annotation annot; +WFDB_Frequency sfreq; + +void process_start(char *tstring); +void process_anntab(void); +void process_annotation(void); + +main(int argc, char **argv) +{ + char *annotator, *ofname, *p, *pname, *record, *prog_name(); + WFDB_Anninfo ai; + + pname = prog_name(argv[0]); + if (argc < 3) { + (void)fprintf(stderr, "usage: %s RECORD ANNOTATOR\n", pname); + exit(1); + } + record = argv[1]; + annotator = argv[2]; + + /* Discover the number of signals defined in the header. */ + if ((nsig = isigopen(record, NULL, 0)) < 0) exit(2); + sfreq = sampfreq(record); + + /* Open the input annotation file. */ + ai.name = annotator; + ai.stat = WFDB_READ; + if (annopen(record, &ai, 1) < 0) + exit(3); + + /* The name of the output file is of the form 'RECORD.ANNOTATOR.xml'. Any + directory separators (/) in the record name are replaced by hyphens (-) + in the output file name, so that the output file is always written into + the current directory. */ + ofname = calloc(strlen(record)+strlen(annotator)+6, sizeof(char)); + sprintf(ofname, "%s.%s.xml", record, annotator); + for (p = ofname; *p; p++) + if (*p == '/') *p = '-'; + + /* Open the output file and write the XML prolog. */ + if ((ofile = fopen(ofname, "wt")) == NULL) { + fprintf(stderr, "%s: can't create %s\n", pname, ofname); + exit(4); + } + fprintf(ofile, WFDBXMLPROLOG); + + (void)fprintf(ofile, "\n", annotator, record); + + process_start(mstimstr(0)); + + (void)fprintf(ofile, "%.12g\n", + sfreq); + + while (getann(0, &annot) >= 0) + process_annotation(); + + process_anntab(); + + fprintf(ofile, "\n"); + wfdbquit(); + exit(0); +} + +void process_start(char *p) +{ + if (*p == '[') { + int day = -1, month = -1, year = -1; + double hour = -1.0, minute = -1.0, second = -1.0; + + fprintf(ofile, "\n"); + sscanf(p+1, "%lf:%lf:%lf %d/%d/%d", + &hour, &minute, &second, &day, &month, &year); + if (year >= 0) { + if (year < 100) year += 1900; + if (year < 1975) year += 100; + fprintf(ofile, "%d\n", year); + } + if (month > 0) fprintf(ofile, "%d\n", month); + if (day > 0) fprintf(ofile, "%d\n", day); + + if (second < 0) { /* incomplete start time in MM:SS or SS format */ + if (minute < 0) { second = hour; hour = -1; } /* SS format */ + else { second = minute; minute = hour; hour = -1; } /* MM:SS */ + } + + if (hour >= 0) fprintf(ofile, "%g\n", hour); + if (minute >= 0) fprintf(ofile, "%g\n", minute); + if (second >= 0) fprintf(ofile, "%g\n", second); + fprintf(ofile, "\n"); + } +} + +static long anncount[ACMAX+1]; + +void process_anntab() +{ + int i; + + fprintf(ofile, ""); + for (i = 0; i <= ACMAX; i++) { + if (anncount[i]) { + fprintf(ofile, "%d", i); + output_xml(ofile, "anncode", annstr(i)); + output_xml(ofile, "anndescription", anndesc(i)); + fprintf(ofile, "%ld\n", anncount[i]); + } + } + fprintf(ofile, ""); +} + +void process_annotation() +{ + fprintf(ofile, "%s", + annot.time, annstr(annot.anntyp)); + if (annot.subtyp) fprintf(ofile, "%d", annot.subtyp); + if (annot.chan) fprintf(ofile, "%d", annot.chan); + if (annot.num) fprintf(ofile, "%d", annot.num); + if (annot.aux) output_xml(ofile, "aux", annot.aux+1); + fprintf(ofile, "\n"); + anncount[annot.anntyp]++; +} + +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); +} diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/heaxml.c wfdb-10.5.5/xml/heaxml.c --- wfdb-10.5.4/xml/heaxml.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/heaxml.c 2010-07-01 17:58:42.000000000 -0400 @@ -0,0 +1,400 @@ +/* file: heaxml.c G. Moody 28 June 2010 + Last revised: 1 July 2010 +------------------------------------------------------------------------------- +heaxml: Convert a WFDB .hea (header) file to XML format +Copyright (C) 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, 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 + +#define WFDBXMLPROLOG "\n" \ + "\n" \ + "\n" + +char *token(char *p) +{ + if (p) { + while (*p && *p != ' ' && *p != '\t' && *p != '\n') + p++; /* find whitespace */ + while (*p && (*p == ' ' || *p == '\t' || *p == '\n')) + p++; /* find first non-whitespace */ + if (*p == '\0') p = NULL; + } + return (p); +} + +void output_xml(FILE *ofile, char *tag, char *p) +{ + if (p) { + fprintf(ofile, "<%s>", tag); + while (*p) { + if (*p == '<') fprintf(ofile, "<"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '&') fprintf(ofile, "&"); + else if (*p == '>') fprintf(ofile, ">"); + else if (*p == '"') fprintf(ofile, """); + else if (*p == '\'') fprintf(ofile, "'"); + else fprintf(ofile, "%c", *p); + p++; + } + fprintf(ofile, "\n", tag); + } + return; +} + +int nsig; +FILE *ofile; +WFDB_Siginfo *s; +WFDB_Time t; + +void process_start(char *tstring); + +main(int argc, char **argv) +{ + char *ofname, *info, *p, *pname, *record, *prog_name(); + int i, in_msrec = 0, nsegments; + WFDB_Seginfo *seg, *sp; + + pname = prog_name(argv[0]); + if (argc < 2) { + (void)fprintf(stderr, "usage: %s RECORD\n", pname); + exit(1); + } + record = argv[1]; + + /* Discover the number of signals defined in the header. */ + if ((nsig = isigopen(record, NULL, 0)) < 0) exit(2); + + /* The name of the output file is the record name with an + appended ".hea.xml". Any directory separators (/) in + the record name are replaced by hyphens (-) in the + output file name, so that the output file is always + written into the current directory. */ + ofname = calloc(strlen(record)+9, sizeof(char)); + strcpy(ofname, record); + for (p = ofname; *p; p++) + if (*p == '/') *p = '-'; + strcat(ofname, ".hea.xml"); + + /* Open the output file and write the XML prolog. */ + if ((ofile = fopen(ofname, "wt")) == NULL) { + fprintf(stderr, "%s: can't create %s\n", pname, ofname); + exit(3); + } + fprintf(ofile, WFDBXMLPROLOG); + + /* Allocate storage for nsig signal information structures. */ + if (nsig > 0 && (s = malloc(nsig * sizeof(WFDB_Siginfo))) == NULL) { + fprintf(stderr, "%s: insufficient memory\n", pname); + exit(4); + } + nsig = isigopen(record, s, -nsig); + + (void)fprintf(ofile, "\n\n", record); + setgvmode(WFDB_LOWRES); + t = strtim("e"); + if (nsig > 0 && (s[0].fmt == 0 || s[0].nsamp != 0) && s[0].nsamp != t) { + in_msrec = 1; + nsegments = getseginfo(&sp); + seg = calloc(nsegments, sizeof(WFDB_Seginfo)); + for (i = 0; i < nsegments; i++) + seg[i] = sp[i]; + sp = seg; + } + + process_record(); + + if (in_msrec) { + static char *segname, nextts[30]; + + segname = calloc(strlen(record)+20, sizeof(char)); + for (p = record + strlen(record) - 1; p > record && *p != '/'; p--) + ; + *p = '\0'; + /* If segment 0 has 0 samples, it's a layout segment, and the information + in it has already been output above, so skip it; otherwise, it's a + regular segment, so process it. The initialization of i in the for + loop below tests for this. */ + for (i = sp[0].nsamp ? 0 : 1; i < nsegments; i++) { + fprintf(stderr, "segment %d: %s\n", i, sp[i].recname); + if (strcmp("~", sp[i].recname) == 0) { + fprintf(ofile, "\n\n\n", i); + process_start(nextts); + fprintf(ofile, "%ld\n", sp[i].nsamp); + } + else { + sprintf(segname, "%s/%s", record, sp[i].recname); + wfdbquit(); + nsig = isigopen(segname, NULL, 0); + nsig = isigopen(segname, s, -nsig); + fprintf(ofile, "\n\n", sp[i].recname); + setgvmode(WFDB_LOWRES); + t = strtim("e"); + strcpy(nextts, mstimstr(-t)); + process_record(); + } + fprintf(ofile, "\n"); + } + } + fprintf(ofile, "\n"); + exit(0); +} + +void process_info(void) +{ + char *info, *p; + + if (info = getinfo((char *)NULL)) { + double age = -1.0; + char *sex = NULL; + + fprintf(ofile, "\n\n"); + /* Find the first non-space in the first info string. */ + for (p = info; *p && *p == ' '; p++) + ; + if ('0' <= *p && *p <= '9') { + /* If the first token of the first info string is numeric, the + current .hea file does not have tagged info, and the first + and second tokens are the age and sex; and the second info + string (if present) contains the medications. Handle this + case first. */ + sscanf(p, "%lf", &age); + if (age >= 0) fprintf(ofile, "%g\n", age); + p = token(p); /* go to the next token */ + if (p && (*p == 'm' || *p == 'M')) sex = "M"; + else if (p && (*p == 'f' || *p == 'F')) sex = "F"; + if (sex) fprintf(ofile, "%s\n", sex); + /* If there are any more tokens, save them as 'extra' info. */ + if (p = token(p)) + output_xml(ofile, "extra", p); + if (info = getinfo((char *)NULL)) { + output_xml(ofile, "medication", info); + info = getinfo((char *)NULL); + } + } + /* process standard (tagged) info */ + while (info) { + if (age < 0) { + if ((p = strstr(info, "age")) || (p = strstr(info, "Age"))) { + if (p = token(p)) { + sscanf(p, "%lf", &age); + if (age >= 0) fprintf(ofile, "%g\n", age); + } + /* Additional tagged data may follow age. Continue processing + the remainder of this info string below. */ + if (!(info = token(p))) + /* If there is nothing else, get the next info if any. */ + info = getinfo((char *)NULL); + } + } + if (sex == NULL) { + if (info && + ((p = strstr(info, "sex"))||(p = strstr(info, "Sex")))) { + if ((p = token(p)) && (*p == 'm' || *p == 'M')) sex = "M"; + else if (p && (*p == 'f' || *p == 'F')) sex = "F"; + if (sex) fprintf(ofile, "%s\n", sex); + /* Additional tagged data may follow sex. Continue processing + the remainder of this info string. */ + if (!(info = token(p))) + /* If there is nothing else, get the next info if any. */ + info = getinfo((char *)NULL); + } + } + /* Diagnoses may be present in more than one info string. */ + if (info && *info && + ((p=strstr(info,"diagnos")) || (p=strstr(info,"Diagnos")))) { + if ((p = token(p)) == NULL) + /* If nothing follows the 'diagnosis' tag, assume the next info + is the diagnosis. */ + p = getinfo((char *)NULL); + if (p) { + output_xml(ofile, "diagnosis", p); + /* This info has been consumed; get the next info if any. */ + info = getinfo((char *)NULL); + continue; + } + } + if (info && *info && + ((p=strstr(info,"medication"))||(p=strstr(info,"Medication")))) { + if ((p = token(p)) == NULL) + /* If nothing follows the 'medication' tag, assume the next info + is the medication. */ + p = getinfo((char *)NULL); + if (p) { + output_xml(ofile, "medication", p); + /* This info has been consumed; get the next info if any. */ + info = getinfo((char *)NULL); + continue; + } + } + /* Process any info that was not identified above. */ + if (info && *info) + output_xml(ofile, "other", info); + info = getinfo((char *)NULL); + } + fprintf(ofile, "\n"); + } +} + +void process_start(char *p) +{ + if (*p == '[') { + int day = -1, month = -1, year = -1; + double hour = -1.0, minute = -1.0, second = -1.0; + + fprintf(ofile, "\n"); + sscanf(p+1, "%lf:%lf:%lf %d/%d/%d", + &hour, &minute, &second, &day, &month, &year); + if (year >= 0) { + if (year < 100) year += 1900; + if (year < 1975) year += 100; + fprintf(ofile, "%d\n", year); + } + if (month > 0) fprintf(ofile, "%d\n", month); + if (day > 0) fprintf(ofile, "%d\n", day); + + if (second < 0) { /* incomplete start time in MM:SS or SS format */ + if (minute < 0) { second = hour; hour = -1; } /* SS format */ + else { second = minute; minute = hour; hour = -1; } /* MM:SS */ + } + + if (hour >= 0) fprintf(ofile, "%g\n", hour); + if (minute >= 0) fprintf(ofile, "%g\n", minute); + if (second >= 0) fprintf(ofile, "%g\n", second); + fprintf(ofile, "\n"); + } +} + +int process_record(void) +{ + char *p; + double cfreq, sfreq; + int i, skew; + FILE *ifile; + + /* Process and output info. */ + process_info(); + + /* Output the section if the starting time is specified. */ + process_start(mstimstr(0L)); + + if (nsig < 1 || t > 0L) + (void)fprintf(ofile, "%ld\n", t); + else if (s[0].fmt && (ifile = fopen(s[0].fname, "rb")) && + (fseek(ifile, 0L, 2) == 0)) { + int framesize = 0; + long nbytes = ftell(ifile) - wfdbgetstart(0); + + fclose(ifile); + for (i = 0; i < nsig && s[i].group == 0; i++) + framesize += s[i].spf; + switch (s[0].fmt) { + case 8: + case 80: + t = nbytes / framesize; + break; + default: + case 16: + case 61: + case 160: + t = nbytes / (2*framesize); + break; + case 212: + t = (2L * nbytes) / (3*framesize); + break; + case 310: + case 311: + t = (3L * nbytes) / (4*framesize); + break; + } + (void)fprintf(ofile, "%ld\n", t); + } + + (void)fprintf(ofile, "%.12g\n", + sfreq = sampfreq(NULL)); + (void)fprintf(ofile, "%d\n", nsig); + cfreq = getcfreq(); + if (sfreq != cfreq) { + double basecount = getbasecount(); + fprintf(ofile, " 0.0) fprintf(ofile, " basecount=\"%g\"", basecount); + fprintf(ofile, ">%.12g\n", cfreq); + } + + if (nsig > 0) { + for (i = 0; i < nsig; i++) { + if (i == 0 || s[i].group != s[i-1].group) { + long plen; + + if (i > 0) fprintf(ofile, "\n\n"); + fprintf(ofile, "\n%s\n\n", + s[i].fmt ? s[i].fname : "[none]"); + if (plen = wfdbgetstart(i)) + fprintf(ofile, "%ld\n", plen); + } + fprintf(ofile, "\n%s\n", + s[i].desc); + fprintf(ofile, "%.12g\n", + s[i].gain == 0. ? WFDB_DEFGAIN : s[i].gain); + fprintf(ofile, "%s\n", + s[i].units ? s[i].units : "mV"); + fprintf(ofile, "%d\n", s[i].initval); + fprintf(ofile, "%d\n", s[i].fmt); + if (s[i].spf > 1) + fprintf(ofile,"%d\n", s[i].spf); + if (skew = wfdbgetskew(i)) + fprintf(ofile, "%ld\n", skew); + fprintf(ofile, "%d\n", s[i].bsize); + fprintf(ofile, "%d\n", s[i].adcres); + fprintf(ofile, "%d\n", s[i].adczero); + fprintf(ofile, "%d\n", s[i].baseline); + fprintf(ofile, "%d\n", s[i].cksum); + fprintf(ofile, "\n\n"); + } + } + if (nsig > 0) fprintf(ofile, "\n\n"); + 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); +} diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/Makefile.top wfdb-10.5.5/xml/Makefile.top --- wfdb-10.5.4/xml/Makefile.top 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/Makefile.top 2010-10-06 22:18:20.000000000 -0400 @@ -0,0 +1,30 @@ +# file: Makefile G. Moody 22 August 2010 +# +# 'make' description file for WFDB-XML applications +# +# ----------------------------------------------------------------------------- +# WFDB-XML applications: programs for conversion between WFDB and XML formats +# Copyright (C) 2010 George B. Moody +# +# These programs are free software; you can redistribute them and/or modify +# them 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. +# +# These programs are distributed in the hope that they 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 +# these programs; 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/). +# _____________________________________________________________________________ +# +# All of the converters require the WFDB library, and those that read +# XML also require libexpat (http://expat.sourceforge.net). + diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/Makefile.tpl wfdb-10.5.5/xml/Makefile.tpl --- wfdb-10.5.4/xml/Makefile.tpl 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/Makefile.tpl 2010-10-06 22:26:19.000000000 -0400 @@ -0,0 +1,50 @@ +# file: Makefile.tpl G. Moody 22 August 2010 +# +# This section of the Makefile should not need to be changed. + +CFILES = annxml.c heaxml.c xmlann.c xmlhea.c +HFILES = xmlproc.h +MFILES = Makefile +XFILES = annxml heaxml xmlann xmlhea + +# General rule for compiling C sources into executable files. This is +# redundant for most versions of `make', but at least one System V version +# needs it. +.c: + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + +# `make all': build applications +all: $(XFILES) + $(STRIP) $(XFILES) + +# `make' or `make install': build and install applications +install: all $(BINDIR) + $(SETXPERMISSIONS) $(XFILES) + ../install.sh $(BINDIR) $(XFILES) + +# 'make collect': retrieve the installed applications +collect: + ../conf/collect.sh $(BINDIR) $(XFILES) + +uninstall: + ../uninstall.sh $(BINDIR) $(XFILES) + +# Create directories for installation if necessary. +$(BINDIR): + mkdir -p $(BINDIR); $(SETDPERMISSIONS) $(BINDIR) + +# `make clean': remove intermediate and backup files +clean: + rm -f $(XFILES) *.o *~ + +# `make listing': print a listing of WFDB-XML applications sources +listing: + $(PRINT) README $(MFILES) $(CFILES) $(HFILES) + +# Rules for compiling WFDB-XML applications that require non-standard options + +xmlann: xmlann.c xmlproc.h + $(CC) $(CFLAGS) xmlann.c -o $@ $(LDFLAGS) -lexpat + +xmlhea: xmlhea.c xmlproc.h + $(CC) $(CFLAGS) xmlhea.c -o $@ $(LDFLAGS) -lexpat diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/README wfdb-10.5.5/xml/README --- wfdb-10.5.4/xml/README 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/README 2010-10-06 21:51:04.000000000 -0400 @@ -0,0 +1,86 @@ +file: README G. Moody 2 July 2010 + Last revised: 6 October 2010 + +This directory contains software for interchange between WFDB native formats +and XML formats. It includes: + +wfdb.dtd a DTD for WFDB-XML, an XML schema for + expressing the contents of WFDB + header and annotation files + +wfdb.xsl an XSLT style sheet that a web browser + (or other XSLT engine) can use to transform + a WFDB-XML header or annotation file into + a human-readable representation of its + contents + +heaxml.c a C program that converts a native + WFDB .hea file into a WFDB-XML file + +annxml.c a C program that converts a native + WFDB annotation file into a WFDB-XML file + +xmlhea.c a C program that converts a WFDB-XML header + file into a native WFDB .hea file + +xmlann.c a C program that converts a WFDB-XML annotation + file into a native WFDB annotation file + +xmlproc.h generic functions for processing XML files, shared by + xmlhea and xmlann + +Makefile a 'make' description file for automating the + process of compiling and installing this software + +A copy of the most recent version of wfdb.dtd is posted on PhysioNet at + http://physionet.org/physiobank/database/XML/wfdb.dtd +This on-line copy is referenced in WFDB-XML files generated by heaxml +and annxml. + +Note that these programs require the WFDB library version 10.5.4 or +later, and that those that read WFDB-XML files also require libexpat +(http://expat.sourceforge.net/). To compile them, install gcc, make, +the WFDB software package, and libexpat if you have not already done so, +then run 'make' from within this directory. If you don't have 'make', +you can compile these programs manually; see 'Makefile' for the commands +needed to do so. + +It is not necessary to download the native WFDB files in order to use +heaxml and annxml, since they can obtain their inputs directly from +the PhysioNet web server via HTTP. + +For example, these commands: + heaxml mitdb/200 + annxml mitdb/200 atr +produce the WFDB-XML files + mitdb-200.hea.xml + mitdb-200.atr.xml +in the current directory. + +If you put a copy of wfdb.xsl in the same directory with these output +files, you should be able to view the HTML generated from them via +wfdb.xsl by opening them with Firefox or another web browser that +incorporates an XSLT engine. + +Try running heaxml on a mimicdb or mimic2db record, for +example like this: + heaxml mimic2db/a40006/a40006 +This command produces + mimic2db-a40006-a40006.hea.xml +which includes information about each segment in the +multi-segment record as well as a summary of the entire +record. The inverse operation, which can be performed by reading +the .xml file using xmlhea, generates a complete set of segment +headers together with the master header in this case. + +Warning: the XML outputs generated by these commands are huge. There is a +good reason that I don't keep these data in XML format on PhysioBank! + +There are no 'datxml' or 'xmldat' applications to convert signal files +to and from WFDB-XML format, because the standard 'rdsamp' and 'wrsamp' +can perform these conversions. Use rdsamp's '-X' option to generate +WFDB-XML output; wrsamp can recognize WFDB-XML input automatically, so +no special option is required. WFDB-XML signal format is the CSV format +that can be produced by rdsamp's '-c' option, wrapped within an XML header +and trailer. + diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/wfdb.dtd wfdb-10.5.5/xml/wfdb.dtd --- wfdb-10.5.4/xml/wfdb.dtd 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/wfdb.dtd 2010-10-06 21:58:53.000000000 -0400 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/wfdb.xsl wfdb-10.5.5/xml/wfdb.xsl --- wfdb-10.5.4/xml/wfdb.xsl 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/wfdb.xsl 2010-07-01 21:51:51.000000000 -0400 @@ -0,0 +1,231 @@ + + + + + + + + + + + + +Description of record <xsl:value-of select="@name"/> + +

+Description of record +

+

General information

+

+ + +Sampling frequency: + +samples per signal per second
+ +Counter frequency: + +counts per second (starting from +)
+
+Signals:

+ + + + + + +
+ + + + +Record <xsl:value-of select="@record"/>, +Annotator <xsl:value-of select="@annotator"/> + +

+Record , +Annotator +

+ +

+ + +Time resolution: +ticks per second
+Annotations: + + + + + + + + +
CountType (Description)
Total
+

+
+ + + + + + + + + + + +
TimeTypeSubChanNumNote
+ + + +
+ + + + + + +() + + + + + + + + + + + + + + + + + +

Notes

+

+Age: + + () + years + +
+Sex: + +male +female + + +
+Diagnoses: + +; + + +
+Medications: + +; + + () + +
+ +
+
+

+
+ + +Start: + +// at :: +
+
+ + +Duration: +:: +( sample intervals)
+
+ + + + +

Each of the signals listed below appears in at least one segment +of this record. See the details for each segment (following this +list of available signals) for additional information.

+
+ +

Signal file:

+ +Preamble: bytes
+
+
+
+ + +

Signal :

+

+Gain: +adu/
+Initial value: adu
+ +Storage format:
+
+ +Samples per frame:
+
+ +Skew: sample intervals
+
+ADC resolution: bits
+ +ADC zero: adu
+
+ +Baseline: adu
+
+ +Block size:
+
+ +Checksum: + +

+
+
+ + +

Segment :

+

+ + + + +Sampling frequency: + +samples per signal per second
+ +Counter frequency: + +counts per second (starting from +)
+
+Signals: + +
+ + + + +
+

+ + +
+ +
diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/xmlann.c wfdb-10.5.5/xml/xmlann.c --- wfdb-10.5.4/xml/xmlann.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/xmlann.c 2010-08-22 23:16:47.000000000 -0400 @@ -0,0 +1,188 @@ +/* file: xmlann.c G. Moody 22 August 2010 + +------------------------------------------------------------------------------- +xmlann: Convert a WFDB-XML file to a WFDB-compatible annotation file +Copyright (C) 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, 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/). +_______________________________________________________________________________ + +The input to xmlann should be an XML file containing a , +as specified by 'wfdb.dtd'; see '100s.atr.xml' for a sample in this format. +The inverse transformation can be performed by 'annxml'. + +TODO: collect anntabentry items and write anntab (rearrange in annhea +so this can be done in one pass). Optionally write a simple header +file and include sampling frequency/length/start attributes in it. +Define and handle new annotation dur and url items. */ + +#include +#include +#include +#include "xmlproc.h" /* provides main(), process(), DATALEN, qflag, vflag */ + +char *content; +int depth, plen, output_not_open; + +WFDB_Anninfo ai; +WFDB_Annotation a; +char *record, *rec; +double sps; + +void XMLCALL start(void *data, const char *el, const char **attr) +{ + int i; + + sprintf(data + strlen(data), "/%s", el); + if (vflag) { + printf("\n%s", data); + for (i = 0; attr[i]; i += 2) + printf(" %s='%s'", attr[i], attr[i + 1]); + fflush(stdout); + } + + if (strcmp("wfdbannotationset", el) == 0) { + if (depth) { + fprintf(stderr, "Malformed input: wfdbannotationset not at root\n"); + // exit(1); + } + for (i = 0; attr[i]; i += 2) { + if (attr[i] && strcmp("annotator", attr[i]) == 0) + SSTRCPY(ai.name, attr[i+1]); + if (attr[i] && strcmp("record", attr[i]) == 0) + SSTRCPY(record, attr[i+1]); + } + if (ai.name == NULL || record == NULL) { + fprintf(stderr, "Malformed input: wfdbannotationset is missing a" + " required annotator or record attribute\n"); + // exit(1); + } + for (rec = record + strlen(record); rec > record; rec--) + if (*rec == '/') { rec++; break; } + output_not_open = 1; + } + depth++; +} + +void XMLCALL middle(void *data, const char *el, int len) +{ + if (plen == 0) { /* not in the same element as last time */ + if (len == 1 && *el == '\n') + return; /* ignore newlines outside of tags */ + SALLOC(content, plen = len + 1, sizeof(char)); + strncpy(content, el, len); + } + else { + SREALLOC(content, plen + len, sizeof(char)); + strncpy(content + plen - 1, el, len); + plen += len; + } + if (vflag) { printf("\t|%s|", content); fflush(stdout); } +} + +void XMLCALL end(void *data, const char *el) +{ + char *p; + int i; + long t; + + depth--; + if (depth == 0) { + if (strcmp("wfdbannotationset", el) == 0) { + wfdbquit(); + if (!qflag) printf("%s.%s\n", rec, ai.name); + } + else if (record) { + fprintf(stderr, "Malformed input ends without /wfdbannotationset\n"); + // exit(1); + } + else { + fprintf(stderr, "No wfdbannotationset in input\n"); + // exit(1); + } + } + else if (strcmp("samplingfrequency", el) == 0) + sscanf(content, "%lf", &sps); + else if (strcmp("/wfdbannotationset/annotation", data) == 0) { + if (output_not_open) { + if (sps > 0) setafreq(sps); + ai.stat = WFDB_WRITE; + annopen(rec, &ai, 1); + output_not_open = 0; + } + putann(0, &a); + a.anntyp = a.subtyp = a.chan = a.num = 0; + a.aux = NULL; + } + else if (strcmp("/wfdbannotationset/annotation/time", data) == 0) { + sscanf(content, "%ld", &t); + a.time = t; + } + else if (strcmp("/wfdbannotationset/annotation/anncode", data) == 0) { + a.anntyp = strann(content); + } + else if (strcmp("/wfdbannotationset/annotation/subtype", data) == 0) { + sscanf(content, "%d", &i); + a.subtyp = i; + } + else if (strcmp("/wfdbannotationset/annotation/chan", data) == 0) { + sscanf(content, "%d", &i); + a.chan = i; + } + else if (strcmp("/wfdbannotationset/annotation/num", data) == 0) { + sscanf(content, "%d", &i); + a.num = i; + } + else if (strcmp("/wfdbannotationset/annotation/aux", data) == 0) { + static char auxbuf[256]; + + if ((i = strlen(content)) > 254) { i = 254; } + a.aux = auxbuf; + auxbuf[0] = i; + strncpy(auxbuf+1, content, i); + auxbuf[i+1] = '\0'; + } + + plen = 0; + for (p = data + strlen(data) - 1; p > (char *)data; p--) + if (*p == '/') { *p = '\0'; break; } + if (vflag) { printf("."); fflush(stdout); } +} + +void cleanup() +{ + content = NULL; + depth = plen = output_not_open = 0; + ai.name = NULL; + a.anntyp = a.subtyp = a.chan = a.num = 0; + a.aux = record = rec = NULL; + sps = 0; +} + +void help() +{ + fprintf(stderr, "usage: %s [ OPTION ...] [ FILE ... ]\n", pname); + fprintf(stderr, " OPTIONs may include:\n" + " -h print this usage summary\n" + " -q quiet mode (print errors only)\n" + " -v verbose mode\n" + " FILE is the name of a WFDB-XML annotation file to be converted\n" + " into one or more WFDB annotation files, or '-' to convert the\n" + " standard input.\n"); + exit(1); +} diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/xmlhea.c wfdb-10.5.5/xml/xmlhea.c --- wfdb-10.5.4/xml/xmlhea.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/xmlhea.c 2010-08-22 23:19:23.000000000 -0400 @@ -0,0 +1,463 @@ +/* file: xmlhea.c G. Moody 20 August 2010 + Last revised: 22 August 2010 +------------------------------------------------------------------------------- +xmlhea: Convert an XML file to a WFDB-compatible .hea (header) file +Copyright (C) 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, 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/). +_______________________________________________________________________________ + +The input to this program should be an XML file containing a , +as specified by 'wfdb.dtd'; see '100s.hea.xml' for a sample in this format. +The inverse transformation can be performed by 'heaxml'. +*/ + +#include +#include +#include +#include "xmlproc.h" /* provides main(), process(), DATALEN, qflag, vflag */ + +void write_header(char *recname); +void writeinfo(char *tag, char *data); + +char *content, *dp; +int depth, plen; + +WFDB_Siginfo *si; +char *record, *rec, *sfname, *age, *sex, **diagnosis, *extra, **medication, + **other; +double sps, cps, cbase, second; +int nseg, nsig, sig, dx, rx, ox; +int year, month, day, hour, minute; +long length, offset; +WFDB_Seginfo *segi; + +int mnsig; +double msps, mcps, mcbase, msecond; +int myear, mmonth, mday, mhour, mminute; +long mlength; + +struct asiginfo { + char *fname; + int fmt; + int skew; + long offset; +} *sa; +static char info[254], *ip; + +void XMLCALL start(void *data, const char *el, const char **attr) +{ + int i; + + sprintf(data + strlen(data), "/%s", el); + if (vflag) { + printf("\n%s", data); + for (i = 0; attr[i]; i += 2) + printf(" %s='%s'", attr[i], attr[i + 1]); + fflush(stdout); + } + + if (depth == 0) + dp = data; + if (strcmp("wfdbrecord", el) == 0) { + if (depth) { + fprintf(stderr, "Malformed input: wfdbrecord not at root\n"); + // exit(1); + } + if (!attr[0] || strcmp("name", attr[0])) { + fprintf(stderr, "Malformed input: no name for wfdbrecord\n"); + // exit(1); + } + dp = data + strlen(data); + SSTRCPY(record, attr[1]); + for (rec = record + strlen(record); rec > record; rec--) + if (*rec == '/') { rec++; break; } + SUALLOC(sfname, strlen(rec)+5, sizeof(char)); + sprintf(sfname, "%s.dat", rec); /* default signal file name */ + } + if (strcmp("counterfrequency", el) == 0) { + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "basecount") == 0) + sscanf(attr[i+1], "%lf", &cbase); + } + } + if (strcmp("/wfdbrecord/segment", data) == 0) { + dp = data + strlen(data); + if (!attr[0] || strcmp("name", attr[0])) { + fprintf(stderr, "Malformed input: no name for segment %d\n", nseg); + // exit(1); + } + if (nseg == 0) { + SUALLOC(segi, 1, sizeof(WFDB_Seginfo)); + + mnsig = nsig; msps = sps; mcps = cps; mcbase = cbase; + myear = year; mmonth = month; mday = day; + mhour = hour; mminute = minute; msecond = second; + mlength = length; + + if (si && strcmp(sfname, "[none]") == 0) { + char *layout_rec; + + SUALLOC(layout_rec, strlen(rec) + 8, sizeof(char)); + sprintf(layout_rec, "%s_layout", rec); + strncpy(segi[0].recname, layout_rec, WFDB_MAXRNL); + for (i = 0; i < nsig; i++) { + SSTRCPY(sa[i].fname,"~"); + segi[0].nsamp = si[i].nsamp = si[i].cksum = 0; + } + write_header(layout_rec); + nseg = 1; + } + } + if (nseg > 0) { + SREALLOC(segi, nseg+1, sizeof(WFDB_Seginfo)); + } + strncpy(segi[nseg].recname, attr[1], WFDB_MAXRNL); + sig = dx = rx = ox = 0; + SFREE(diagnosis); SFREE(medication); SFREE(other); + } + + depth++; +} + +void XMLCALL middle(void *data, const char *el, int len) +{ + if (plen == 0) { /* not in the same element as last time */ + if (len == 1 && *el == '\n') + return; /* ignore newlines outside of tags */ + SALLOC(content, plen = len + 1, sizeof(char)); + strncpy(content, el, len); + } + else { + SREALLOC(content, plen + len, sizeof(char)); + strncpy(content + plen - 1, el, len); + plen += len; + } + if (vflag) { printf("\t|%s|", content); fflush(stdout); } +} + +void XMLCALL end(void *data, const char *el) +{ + char *p; + + depth--; + if (depth == 0) { + if (strcmp("wfdbrecord", el) == 0) { + if (nseg == 0) + write_header(rec); + else { + char *ofname; + FILE *ofile; + int i; + + SUALLOC(ofname, strlen(rec)+5, sizeof(char)); + sprintf(ofname, "%s.hea", rec); + ofile = fopen(ofname, "wt"); + fprintf(ofile, "%s/%d %d %.12g", rec, nseg, mnsig, msps); + if (mcps) { + fprintf(ofile, "%.12g", mcps); + if (mcbase) + fprintf(ofile, "(%.12g)", mcbase); + } + fprintf(ofile, " %ld %02d:%02d:%02d", + mlength, mhour, mminute, (int)msecond); + if (i = ((int)(msecond*1000. + 0.5)) % 1000) + fprintf(ofile, ".%03d", i); + if (myear || mmonth || mday) + fprintf(ofile, " %02d/%02d/%d", mday, mmonth, myear); + fprintf(ofile, "\r\n"); + for (i = 0; i < nseg; i++) + fprintf(ofile, "%s %ld\r\n", segi[i].recname, segi[i].nsamp); + if (!qflag) printf("%s\n", ofname); + } + } + else if (record) { + fprintf(stderr, "Malformed input ends without /wfdbrecord\n"); + // exit(1); + } + else { + fprintf(stderr, "No wfdbrecord in input\n"); + // exit(1); + } + } + else if (depth == 1) { + if (strcmp("length", el) == 0) + sscanf(content, "%ld", &length); + else if (strcmp("samplingfrequency", el) == 0) + sscanf(content, "%lf", &sps); + else if (strcmp("counterfrequency", el) == 0) + sscanf(content, "%lf", &cps); + else if (strcmp("signals", el) == 0) { + sscanf(content, "%ld", &nsig); + SUALLOC(si, sizeof(WFDB_Siginfo), nsig); + SUALLOC(sa, sizeof(struct asiginfo), nsig); + } + else if (strcmp("segment", el) == 0) { + if (strcmp(segi[nseg].recname, "~")) + write_header(segi[nseg].recname); + nseg++; + } + } + + else if (strcmp("/wfdbrecord/segment/length", data) == 0) { + sscanf(content, "%ld", &length); + segi[nseg].nsamp = length; + } + else if (strcmp("/wfdbrecord/segment/gap/length", data) == 0) { + strcpy(segi[nseg].recname, "~"); + sscanf(content, "%ld", &length); + segi[nseg].nsamp = length; + } + else if (strcmp("/wfdbrecord/segment/signals", data) == 0) + sscanf(content, "%ld", &nsig); + + else if (strcmp("/info/age", dp) == 0) { + SSTRCPY(age, content); + } + else if (strcmp("/info/sex", dp) == 0) { + SSTRCPY(sex, content); + } + else if (strcmp("/info/extra", dp) == 0) { + SSTRCPY(extra, content); + } + else if (strcmp("/info/diagnosis", dp) == 0) { + if (dx == 0) { SUALLOC(diagnosis, 1, sizeof(char *)); } + else { SREALLOC(diagnosis, dx+1, sizeof(char *)); } + if (diagnosis[dx]) { + SREALLOC(diagnosis[dx], strlen(content)+1, sizeof(char)); + } + else { + SUALLOC(diagnosis[dx], strlen(content)+1, sizeof(char)); + } + strcpy(diagnosis[dx++], content); + } + else if (strcmp("/info/medication", dp) == 0) { + if (rx == 0) { SUALLOC(medication, 1, sizeof(char *)); } + else { SREALLOC(medication, rx+1, sizeof(char *)); } + if (medication[rx]) { + SREALLOC(medication[rx], strlen(content)+1, sizeof(char)); + } + else { + SUALLOC(medication[rx], strlen(content)+1, sizeof(char)); + } + strcpy(medication[rx++], content); + } + else if (strcmp("/info/other", dp) == 0) { + if (ox == 0) { SUALLOC(other, 1, sizeof(char *)); } + else { SREALLOC(other, ox+1, sizeof(char *)); } + SUALLOC(other[ox], strlen(content)+1, sizeof(char)); + strcpy(other[ox++], content); + } + + else if (strcmp("/start/year", dp) == 0) { + sscanf(content, "%ld", &year); + } + else if (strcmp("/start/month", dp) == 0) { + sscanf(content, "%ld", &month); + } + else if (strcmp("/start/day", dp) == 0) { + sscanf(content, "%ld", &day); + } + else if (strcmp("/start/hour", dp) == 0) { + sscanf(content, "%ld", &hour); + } + else if (strcmp("/start/minute", dp) == 0) { + sscanf(content, "%ld", &minute); + } + else if (strcmp("/start/second", dp) == 0) { + sscanf(content, "%lf", &second); + } + + else if (strcmp("/signalfile/filename", dp) == 0) { + SSTRCPY(sfname, content); + } + else if (strcmp("/signalfile/preamblelength", dp) == 0) { + sscanf(content, "%ld", &offset); + } + else if (strcmp("/signalfile/signal", dp) == 0) { + SSTRCPY(sa[sig].fname, sfname); + sa[sig].offset = offset; + si[sig].nsamp = length; + sig++; + } + else if (strcmp("/signalfile/signal/description", dp) == 0) { + SSTRCPY(si[sig].desc, content); + } + else if (strcmp("/signalfile/signal/gain", dp) == 0) { + sscanf(content, "%lf", &(si[sig].gain)); + } + else if (strcmp("/signalfile/signal/units", dp) == 0) { + SSTRCPY(si[sig].units, content); + } + else if (strcmp("/signalfile/signal/initialvalue", dp) == 0) { + sscanf(content, "%d", &(si[sig].initval)); + } + else if (strcmp("/signalfile/signal/storageformat", dp) == 0) { + sscanf(content, "%d", &sa[sig].fmt); + } + else if (strcmp("/signalfile/signal/blocksize", dp) == 0) { + sscanf(content, "%d", &(si[sig].bsize)); + } + else if (strcmp("/signalfile/signal/adcresolution", dp) == 0) { + sscanf(content, "%d", &(si[sig].adcres)); + } + else if (strcmp("/signalfile/signal/adczero", dp) == 0) { + sscanf(content, "%d", &(si[sig].adczero)); + } + else if (strcmp("/signalfile/signal/baseline", dp) == 0) { + sscanf(content, "%d", &(si[sig].baseline)); + } + else if (strcmp("/signalfile/signal/checksum", dp) == 0) { + sscanf(content, "%d", &(si[sig].cksum)); + } + else if (strcmp("/signalfile/signal/samplesperframe", dp) == 0) { + sscanf(content, "%d", &(si[sig].spf)); + } + else if (strcmp("/signalfile/signal/skew", dp) == 0) { + sscanf(content, "%d", &sa[sig].skew); + } + + plen = 0; + for (p = data + strlen(data) - 1; p > (char *)data; p--) + if (*p == '/') { *p = '\0'; break; } + if (vflag) { printf("."); fflush(stdout); } +} + +void write_header(char *recname) +{ + int i, dxlen, rxlen, oxlen; + + if (!qflag) printf("%s.hea\n", recname); + if (sps > 0) + setsampfreq(sps); + if (cps > 0) { + setcfreq(cps); + if (cbase > 0) setbasecount(cbase); + } + if (year || month || day || hour || minute || second) { + char tstart[24]; + if (year || month || day) + sprintf(tstart, "%02d:%02d:%g %02d/%02d/%d", + hour, minute, second, day, month, year); + else + sprintf(tstart, "%02d:%02d:%g", hour, minute, second); + setbasetime(tstart); + } + + /* IMPORTANT: Before invoking osigopen, si[*].fname is set to "~", and + si[*].fmt is set to 0, to ensure that osigfopen does not actually create + (empty) output signal files (which would destroy any existing files of + the same names). It's necessary to call osigfopen in order to allow + skews and offsets to be set, however. si[*].fname and si[*].fmt must be + set to their intended values (which have been preserved in sa[*]) after + calling osigfopen and before calling setheader. + */ + for (i = 0; i < nsig; i++) { + si[i].fname = "~"; + si[i].fmt = 0; + } + if (osigfopen(si, nsig) == nsig) { + for (i = 0; i < nsig; i++) { + si[i].fname = sa[i].fname; + si[i].fmt = sa[i].fmt; + wfdbsetskew(i, sa[i].skew); + wfdbsetstart(i, sa[i].offset); + } + } + setheader(recname, si, nsig); + + /* After calling setheader, the info strings are written to the output. */ + if (age == NULL || strlen(age) > 5) age = "?"; + if (sex == NULL || strlen(sex) > 20) sex = "?"; + sprintf(info, " : %s : %s", age, sex); + if (dx == 0) sprintf(info + strlen(info), " : ?"); + if (dx == 1 && strlen(diagnosis[0]) < 78 - (strlen(info) + 14)) { + sprintf(info + strlen(info), " : %s", diagnosis[0]); + dx = 0; + } + if (rx == 0) sprintf(info + strlen(info), " : ?"); + if (rx == 1 && strlen(medication[0]) < 78 - (strlen(info) + 16)) { + sprintf(info + strlen(info), " : %s", medication[0]); + rx = 0; + } + putinfo(info); + if (dx) + for (i = 0; i < dx; i++) + writeinfo(": ", diagnosis[i]); + if (rx) + for (i = 0; i < rx; i++) + writeinfo(": ", medication[i]); + if (extra) + writeinfo(" ", extra); + if (ox > 1) + for (i = 0; i < ox; i++) + writeinfo("", other[i]); + + wfdbquit(); /* flush any pending writes and close files */ +} + +void writeinfo(char *tag, char *data) +{ + char info[80]; + int len = strlen(tag) + strlen(data); + + while (len >= 80) { + strcpy(info, tag); + strncat(info, data, 79-strlen(tag)); + putinfo(info); + data += 79-strlen(tag); + tag = "# "; + } + strcpy(info, tag); + strcat(info, data); + putinfo(info); +} + +void cleanup() +{ + content = dp = NULL; + depth = plen = 0; + si = NULL; + record = rec = sfname = age = sex = extra = NULL; + diagnosis = medication = other = NULL; + sps = cps = cbase = second = 0; + nseg = nsig = sig = dx = rx = ox = 0; + year = month = day = hour = minute = 0; + length = offset = 0; + segi = NULL; + mnsig = 0; + msps = mcps = mcbase = msecond = 0; + myear = mmonth = mday = mhour = mminute = 0; + mlength = 0; + sa = NULL; + info[0] = '\0'; + ip = NULL; +} + +void help() +{ + fprintf(stderr, "usage: %s [ OPTION ...] [ FILE ... ]\n", pname); + fprintf(stderr, " OPTIONs may include:\n" + " -h print this usage summary\n" + " -q quiet mode (print errors only)\n" + " -v verbose mode\n" + " FILE is the name of a WFDB-XML header file to be converted\n" + " into one or more WFDB .hea files, or '-' to convert the\n" + " standard input.\n"); + exit(1); +} diff -Naur --exclude Makefile --exclude info wfdb-10.5.4/xml/xmlproc.h wfdb-10.5.5/xml/xmlproc.h --- wfdb-10.5.4/xml/xmlproc.h 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.5/xml/xmlproc.h 2010-08-22 18:58:26.000000000 -0400 @@ -0,0 +1,130 @@ +/* file: xmlproc.h G. Moody 20 August 2010 + Last revised: 22 August 2010 +------------------------------------------------------------------------------- +xmlproc: generic functions for processing XML files +Copyright (C) 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, 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/). +_______________________________________________________________________________ + +This code should be compiled together with one of xmlhea.c, xmlann.c, or +xmldat.c, which provide implementations of the callback functions start(), +middle(), and end() for processing WFDB-XML header, annotation, and signal +files respectively, as well as the functions cleanup() and help(). +*/ + +#include + +/* functions including callbacks defined in xmlhea.c, xmlann.c, xmldat.c */ +void XMLCALL start(void *data, const char *el, const char **attr); +void XMLCALL middle(void *data, const char *el, int len); +void XMLCALL end(void *data, const char *el); +void cleanup(void); +void help(void); + +#define DATALEN 1024 /* max length of *data passed among callbacks, + in characters */ + +char *pname; +int qflag, vflag; + +int main(int argc, char **argv) +{ + FILE *ifile; + int i; + void process(FILE *ifile); + + pname = argv[0]; + if (argc < 2) help(); + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case '\0': process(stdin); break; + case 'h': help(); break; + case 'q': qflag = 1; break; + case 'v': vflag = 1; break; + default: fprintf(stderr, "%s: unrecognized option %s\n", + argv[0], argv[i]); + exit(1); + break; + } + } + else { + ifile = fopen(argv[i], "rt"); + if (ifile) { + process(ifile); + fclose(ifile); + } + else { + fprintf(stderr, "%s: can't open %s\n", argv[0], argv[i]); + exit(1); + } + } + } + exit(0); +} + +/* Definitions needed by process(). XML_LARGE_SIZE and XML_USE_MSC_EXTENSIONS + may be defined in . */ +#ifdef XML_LARGE_SIZE +#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +#define XML_FMT_INT_MOD "I64" +#else +#define XML_FMT_INT_MOD "ll" +#endif +#else +#define XML_FMT_INT_MOD "l" +#endif + +#define BUFLEN 8192 + +void process(FILE *ifile) +{ + int done = 0, len; + static char buf[BUFLEN], userdata[DATALEN]; + XML_Parser p = XML_ParserCreate(NULL); + + if (! p) { + fprintf(stderr, "Couldn't allocate memory for parser\n"); + exit(2); + } + + XML_SetUserData(p, userdata); + XML_SetElementHandler(p, start, end); + XML_SetCharacterDataHandler(p, middle); + + do { + len = (int)fread(buf, 1, BUFLEN, ifile); + if (ferror(ifile)) { + fprintf(stderr, "Read error\n"); + exit(-1); + } + done = feof(ifile); + + if (XML_Parse(p, buf, len, done) == XML_STATUS_ERROR) { + fprintf(stderr, "Parse error at line %" XML_FMT_INT_MOD "u:\n%s\n", + XML_GetCurrentLineNumber(p), + XML_ErrorString(XML_GetErrorCode(p))); + exit(-1); + } + } while (!done); + XML_ParserFree(p); + cleanup(); + userdata[0] = '\0'; + if (vflag) printf("\n"); +}