diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/app/Makefile.tpl wfdb-10.5.23/app/Makefile.tpl --- wfdb-10.5.22/app/Makefile.tpl 2012-08-12 16:34:42.000000000 -0400 +++ wfdb-10.5.23/app/Makefile.tpl 2014-02-28 13:40:51.000000000 -0500 @@ -1,23 +1,23 @@ # file: Makefile.tpl G. Moody 23 May 2000 -# Last revised: 10 August 2012 +# Last revised: 28 February 2014 # This section of the Makefile should not need to be changed. CFILES = ann2rr.c bxb.c calsig.c ecgeval.c epicmp.c fir.c gqfuse.c gqpost.c \ gqrs.c hrstats.c ihr.c mfilt.c mrgann.c mxm.c nguess.c nst.c plotstm.c \ pscgen.c pschart.c psfd.c rdann.c rdsamp.c rr2ann.c rxr.c sampfreq.c sigamp.c \ sigavg.c signame.c signum.c skewedit.c snip.c sortann.c sqrs.c sqrs125.c \ - sumann.c sumstats.c tach.c time2sec.c wabp.c wfdb-config.c wfdbcat.c \ - wfdbcollate.c wfdbdesc.c wfdbmap.c wfdbsignals.c wfdbtime.c wfdbwhich.c \ - wqrs.c wrann.c wrsamp.c xform.c + stepdet.c sumann.c sumstats.c tach.c time2sec.c wabp.c wfdb-config.c \ + wfdbcat.c wfdbcollate.c wfdbdesc.c wfdbmap.c wfdbsignals.c wfdbtime.c \ + wfdbwhich.c wqrs.c wrann.c wrsamp.c xform.c CFFILES = gqrs.conf HFILES = signal-colors.h XFILES = ann2rr bxb calsig ecgeval epicmp fir gqfuse gqpost \ gqrs hrstats ihr mfilt mrgann mxm nguess nst plotstm \ pscgen pschart psfd rdann rdsamp rr2ann rxr sampfreq sigamp \ sigavg signame signum skewedit snip sortann sqrs sqrs125 \ - sumann sumstats tach time2sec wabp wfdb-config wfdbcat \ - wfdbcollate wfdbdesc wfdbmap wfdbsignals wfdbtime wfdbwhich \ - wqrs wrann wrsamp xform + stepdet sumann sumstats tach time2sec wabp wfdb-config \ + wfdbcat wfdbcollate wfdbdesc wfdbmap wfdbsignals wfdbtime \ + wfdbwhich wqrs wrann wrsamp xform SCRIPTS = cshsetwfdb setwfdb pnwlogin PSFILES = pschart.pro psfd.pro 12lead.pro MFILES = Makefile diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/app/stepdet.c wfdb-10.5.23/app/stepdet.c --- wfdb-10.5.22/app/stepdet.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/app/stepdet.c 2014-02-28 13:33:38.000000000 -0500 @@ -0,0 +1,243 @@ +/* file: stepdet.c G. Moody 28 February 2014 + +------------------------------------------------------------------------------- +stepann: detect and annotate step changes in a signal +Copyright (C) 1990-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 program analyzes one signal of a PhysioBank-compatible record, detecting +and annotating rising and falling step changes. + +TUP is the threshold for detecting a rising step change (annotated as 'R'), +and TDOWN is the threshold for detecting a falling ('F') step change. This +program requires that TUP > TDOWN. Using its -m option, set TUP to a value +significantly greater than TDOWN to avoid false detections of transitions due +to noise in the signal. Noise spikes that still cause false detections can +often be avoided by median-filtering the signal (see mfilt(1)) before using it +as input to stepdet. +*/ + +#include +#include +#include + +char *pname; + +main(argc, argv) +int argc; +char *argv[]; +{ + char *p, *record = NULL, *prog_name(); + int i, minutes = 0, nsig, signal = -1, time, tdown = 450, tup = 550, *v, + v0, v1; + long from = 0L, next_minute, now, spm, to = 0L; + WFDB_Anninfo a; + WFDB_Annotation annot; + static int gvmode = WFDB_LOWRES; + static WFDB_Siginfo *s; + void help(); + + pname = prog_name(argv[0]); + a.name = "steps"; a.stat = WFDB_WRITE; + + for (i = 1; i < argc; i++) { + if (*argv[i] == '-') switch (*(argv[i]+1)) { + case 'a': /* annotator name */ + if (++i >= argc) { + (void)fprintf(stderr, "%s: annotator name must follow -a\n", + pname); + exit(1); + } + a.name = argv[i]; + break; + case 'f': /* starting time */ + if (++i >= argc) { + (void)fprintf(stderr, "%s: time must follow -f\n", pname); + exit(1); + } + from = i; + break; + case 'h': /* help requested */ + help(); + exit(0); + break; + case 'H': /* operate in WFDB_HIGHRES mode */ + gvmode = WFDB_HIGHRES; + break; + case 'm': /* threshold */ + if (++i >= argc-1) { + (void)fprintf(stderr, "%s: TUP and TDOWN must follow -m\n", + pname); + exit(1); + } + tup = atoi(argv[i++]); + tdown = atoi(argv[i]); + if (tup <= tdown) { + (void)fprintf(stderr, "%s: TUP (%d) must be greater than TDOWN" + " (%d)\n", pname, tup, tdown); + exit(1); + } + break; + case 'r': /* record name */ + if (++i >= argc) { + (void)fprintf(stderr, "%s: input record name must follow -r\n", + pname); + exit(1); + } + record = argv[i]; + break; + case 's': /* signal */ + if (++i >= argc) { + (void)fprintf(stderr, + "%s: signal number or name must follow -s\n", + pname); + exit(1); + } + signal = i; + break; + case 't': /* end time */ + if (++i >= argc) { + (void)fprintf(stderr, "%s: time must follow -t\n",pname); + exit(1); + } + to = 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); + } + + if (gvmode == 0 && (p = getenv("WFDBGVMODE"))) + gvmode = atoi(p); + setgvmode(gvmode|WFDB_GVPAD); + + if ((nsig = isigopen(record, NULL, 0)) < 1) exit(2); + if ((s = malloc(nsig * sizeof(WFDB_Siginfo))) == NULL || + (v = malloc(nsig * sizeof(WFDB_Sample))) == NULL) { + (void)fprintf(stderr, "%s: insufficient memory\n", pname); + exit(2); + } + if ((nsig = isigopen(record, s, nsig)) < 1) exit(2); + + if (annopen(record, &a, 1) < 0) exit(2); + + if (from > 0L) { + if ((from = strtim(argv[from])) < 0L) + from = -from; + if (isigsettime(from) < 0) + exit(2); + } + if (to > 0L) { + if ((to = strtim(argv[to])) < 0L) + to = -to; + } + spm = strtim("1:0"); + next_minute = from + spm; + if (signal >= 0) signal = findsig(argv[signal]); + if (signal < 0 || signal >= nsig) signal = 0; + now = from; + + annot.subtyp = annot.chan = annot.num = 0; annot.aux = NULL; + (void)getvec(v); + v1 = v[signal]; + ++now; + + while (getvec(v) > 0 && (to == 0L || now <= to)) { + v0 = v1; + v1 = v[signal]; + if (v0 < v1 && v0 < tup && v1 >= tup) { + annot.anntyp = RBBB; + annot.time = now; + putann(0, &annot); + } + else if (v0 > v1 && v0 > tdown && v1 <= tdown) { + annot.anntyp = FUSION; + annot.time = now; + putann(0, &annot); + } + v0 = v1; + if (++now >= next_minute) { + next_minute += spm; + (void)fprintf(stderr, "."); + (void)fflush(stderr); + if (++minutes >= 60) { + (void)fprintf(stderr, " %s\n", timstr(now)); + minutes = 0; + } + } + } while (getvec(v) > 0 && (to == 0L || now <= to)); + if (minutes) (void)fprintf(stderr, " %s\n", timstr(now)); + wfdbquit(); + exit(0); /*NOTREACHED*/ +} + +char *prog_name(s) +char *s; +{ + char *p = s + strlen(s); + +#ifdef MSDOS + while (p >= s && *p != '\\' && *p != ':') { + if (*p == '.') + *p = '\0'; /* strip off extension */ + if ('A' <= *p && *p <= 'Z') + *p += 'a' - 'A'; /* convert to lower case */ + p--; + } +#else + while (p >= s && *p != '/') + p--; +#endif + return (p+1); +} + +static char *help_strings[] = { + "usage: %s -r RECORD [ OPTIONS ]\n", + "where RECORD is the record name, and OPTIONS may include:", + " -a ANNOTATOR specify output ANNOTATOR name (default: steps)", + " -f TIME begin at specified TIME", + " -h show (this) help", + " -H analyze multifrequency input in high-resolution mode", + " -m TUP TDOWN set thresholds for transitions from low to high (TUP,", + " default: 550) and from high to low (TDOWN, default: 450)", + " -s SIGNAL specify the SIGNAL to be analyzed (default: 0)", + " -t TIME stop at specified TIME", +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]); +} diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/app/wrsamp.c wfdb-10.5.23/app/wrsamp.c --- wfdb-10.5.22/app/wrsamp.c 2010-11-22 09:44:13.000000000 -0500 +++ wfdb-10.5.23/app/wrsamp.c 2014-02-27 17:32:24.000000000 -0500 @@ -1,9 +1,9 @@ /* file: wrsamp.c G. Moody 10 August 1993 - Last revised: 22 November 2010 + Last revised: 27 February 2014 ------------------------------------------------------------------------------- wrsamp: Select fields or columns from a file and generate a WFDB record -Copyright (C) 1993-2010 George B. Moody +Copyright (C) 1993-2014 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 @@ -92,7 +92,6 @@ char *delim; /* characters that delimit tokens */ char collapse; /* if non-zero, collapse consecutive delimiters */ char esc; /* if non-zero, next character is literal unless null */ - char *quotepair[]; /* pairs of characters that open and close tokens */ }; struct tokenarray { @@ -130,7 +129,6 @@ " \t\r\n,", /* delimiter characters can be space, tab, CR, LF, or comma */ 1, /* collapse consecutive delimiters */ '\\', /* treat any character following a backslash as a literal */ - { "''", "\"\"", "()", "[]", "{}", "<>", NULL } /* quote characters */ }; static char *tbuf; @@ -139,12 +137,12 @@ Tokenarray *parseline(char *line, Parsemode *pmode) { int i, m = (strlen(line) + 1)/2, n = 0, state = 0; - char d, *p, *q = NULL; + char d, *p, q = '\0'; if (m < nosig) m = nosig; if (m < ncols) m = ncols; SSTRCPY(tbuf, line); - p = tbuf-1; + p = tbuf; SREALLOC(ta, sizeof(int)*2 + sizeof(char *)*m, 1); ta->maxtokens = m; @@ -153,58 +151,35 @@ else if (pmode->delim == NULL) pmode->delim = defpmode.delim; - while (*(++p)) { /* for each character in the line */ - - /* is *p an escape character? */ - if (pmode->esc && *p == pmode->esc) { - if (*(p+1) == '\0') - break; - if (state == 0) { /* start a new token */ - state = 1; - ta->token[n++] = p; - } - p++; /* include the next character in the token */ - continue; - } - - /* is *p the character needed to complete a quoted string? */ - if (q) { - if (*p == *q) { *p = '\0'; q = NULL; } - continue; - } - - /* is *p a delimiter character? */ - i = 0; - while (d = pmode->delim[i++]) { - if (*p == d) { - *p = '\0'; /* replace delimiter with null */ - if (state == 0) { /* not in a token */ - if (pmode->collapse == 0) - ta->token[n++] = p; /* count an empty token */ - } - state = 0; - break; + while (*p) { /* for each character in the line */ + /* is *p a field or record separator? */ + while (strchr(pmode->delim, *p)) { + *p = '\0'; /* replace delimiter with null */ + if (pmode->collapse == 0) + ta->token[n++] = p; /* count an empty token */ + p++; + } + + /* at start of the next token */ + if (*p == '"' || *p == '\'') + q = *p++; /* if token is quoted, skip and save quote char */ + else if (*p == '<') { p++; q = '>'; } + ta->token[n++] = p; + + /* search for the end of the token */ + while (q || !strchr(pmode->delim, *p)) { + /* is *p an escape character? */ + if (pmode->esc && *p == pmode->esc) { + if (*(p+1) == '\0') /* escape at end of line -- ignore */ + break; + for (i = 0; *(p+i); i++) + *(p+i) = *(p+i+1); /* skip esc, next char is literal */ } - } - - /* is *p an open-quote character? */ - i = 0; - while (q = pmode->quotepair[i++]) { /* q is an open-quote character */ - if (*p == *q) { /* *p is first character of a quoted string */ - if (state == 0) { /* start a new token */ - ta->token[n++] = p+1; - state = 1; - } - q++; /* *q is now the matching close-quote character */ + else if (*p == q) { /* found the closing quote */ + *p++ = q = '\0'; break; } - } - - if (d == '\0' && q == NULL) { /* p must be part of a token */ - if (state == 0) { - ta->token[n++] = p; /* start a new token */ - state = 1; - } + p++; } } @@ -437,8 +412,11 @@ for (i = 0; i < ta->ntokens; i++) { while (*(ta->token[i]) == ' ') (ta->token[i])++; - if (*(ta->token[i]) == '(') + if (*(ta->token[i]) == '(') { + p = ta->token[i] + strlen(ta->token[i]) - 1; (ta->token[i])++; + if (*p == ')') *p = '\0'; + } SSTRCPY(ustrings[i], ta->token[i]); } /* Read the third line. */ @@ -504,6 +482,9 @@ case 310: case 311: si[i].adcres = 10; break; case 212: si[i].adcres = 12; break; + case 16: + case 160: + case 61: si[i].adcres = 16; break; case 24: si[i].adcres = 24; break; case 32: si[i].adcres = 32; break; default: si[i].adcres = WFDB_DEFRES; @@ -557,7 +538,6 @@ double v; if (sscanf(ta->token[fv[i]], "%lf", &v) == 1) { - // if (strcmp(ta->token[fv[i]], "-")) { v *= scalef[i]; if (dflag) v+= DITHER; if (v >= 0) vout[i] = (WFDB_Sample)(v + 0.5); diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/appcheck wfdb-10.5.23/checkpkg/appcheck --- wfdb-10.5.22/checkpkg/appcheck 2013-11-19 12:32:18.000000000 -0500 +++ wfdb-10.5.23/checkpkg/appcheck 2014-03-08 17:50:01.000000000 -0500 @@ -1,6 +1,6 @@ #!/bin/sh # file: appcheck G. Moody 7 September 2001 -# Last revised: 26 November 2010 +# Last revised: 8 March 2014 # # 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: @@ -477,6 +477,20 @@ fi TESTS=`expr $TESTS + 1` +echo Testing parsescp ... +parsescp -o test -w xform-1.out 2>&1 for F in xform-1.out 100x.atr 100x.dat 100x.hea diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/dosify wfdb-10.5.23/checkpkg/dosify --- wfdb-10.5.22/checkpkg/dosify 2013-11-19 13:17:39.000000000 -0500 +++ wfdb-10.5.23/checkpkg/dosify 2014-03-08 17:55:35.000000000 -0500 @@ -1,6 +1,6 @@ #! /bin/bash # file: dosify G. Moody 19 November 2013 -# +# Last revised: 8 March 2014 # Convert expected test results to MS-DOS/MS-Windows format for MinGW, using # mv, sed, u2d, and bash builtins. @@ -12,7 +12,7 @@ u2d wfdbwhich.out fi -for F in lcheck.log-NETFILES lcheck.log-no-NETFILES bxb.out rxr.out psfd.ps +for F in lcheck.log-NETFILES lcheck.log-no-NETFILES bxb.out rxr.out psfd.ps test.des test.key do if [ ! -e $F.orig ] then diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/expected/test.dat wfdb-10.5.23/checkpkg/expected/test.dat --- wfdb-10.5.22/checkpkg/expected/test.dat 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/checkpkg/expected/test.dat 2014-03-08 17:35:31.000000000 -0500 @@ -0,0 +1,1466 @@ +              +            +         + +  +   +     + +   + + + + + + +   +     +  +  + +  +  + + +  + + + + + +   +   + + + +        *()  +  + +   +          +     +   +       +    !" !% + ! +  +  +     + + + +      +    +    +  +  +   +         +  +  +   +   + +    +   + +   +   +  50  + + +  ! " !     10CJ_fuyh![X6LIJEe<\UI7gk>;_{33>W&D*,u#- + +c-834R&Y@1Cv#lH)2c&yP('V+U3.Z+U:9b rI%A?ig%;9LOvZ--Q\_~H&#{tx2$ +     +  +       +  + +   +                  +   + +   " +  +  + +  + #  ! + +  !!     + "  + ! +& +' +$ +$!% & +  % "  +"$ + %  # + # $ %! '#)" + $  +&   +#!(# *& )( '&   "( + + #&  ),+2! +-1 +0"-3  +5% .3#02% ++5# +)9% /7( -:* 09) +.;* -=$  *;% ,<% 1A+ +4C+ 2H0 +/E-   ,B) -F. 1J- 4K0 3J3 6I1 0C( +#.M. .K0 +"0L2# !2J0"  1I. %3O1&4M2! #1M3# %2P3 ! /I/ %,F( %-F) $.C) '/G* ',C' ')?())@%+)@#% +)=  ( &9 &':  #2 $/ "!/  0 +0  $, ( !* ' +#  +$    +  +   +    +    + +   + +             +  +    +      +     +    + + +        +  + + + +  + + +   +      +         +  + +  + +  + + + +    +     +  +      + + + + + + +   +    +   +   +  + + +  + +  + + +   +       + +  +  +  + + + + +   +   + + + + + +      + +       +   +          +  + + +        +       +  + +    +   + +           +    +    +  +   + + ($.<.GX;czu"Nb+cR8{OG +\PvN$u<`/H&\08 7>>>BiV|DdxJpzNvOwLsGm@g&8 +^ 72| WJ+rO#X" $lH.d(fB6l,`=>r0Y8C v3S3Hv3N.Ls.H(Pj$F%T^C!"X +S@$ZJ?&ZB>)Z:@*Z4=)Z0;(X,6'X'0&W"*% T#$O "JC <4. ( #      + + + +   +         + + +   + + + +  +   + +  +  +  + +           +       +   + +   + +     + + + + +    +  + +   + + +     + +      +    +   +    +   + + +          +  +          + +  +     +  + + + +   + + + + + + + +   +   +  +   +  + +   + +   + +   + +  + + + + +        + +  + + + +  + +  + +  +  + +  + +     + +  + +  + + +   +  + +  +     +  +   + +    + +  +  + +  +  + +   +   +   +  +  +  +  + + + +  + + + + + + + +  +        +      +    +    +             +  +       +      +        +    +  +        + + +  + +   + +     +  + +    +   + + "A #&#   + +      + +       +  $/>M"^ox5}kh +J\~U^]]HpiEK#>Kwrg/?N;0&8#3k8)(JY4'bl38 G +}4D>.KF&O "!R~'K13as#E-DGr ^#5I\_L$'sx|>!0)& +   +  +       + +      +     + + +   +     + +    +  +           +  + +   +   + + + +     +  + +              +   + + +   +          +   +    +  +      +   + +!!& # +  +    "  $   !!   $  +!$ +#" ##  +$$  +%)  '+" )*$  (%# + +*(! +')#  +(+$  +*.#  -0% +/2*! .0( 32) 54+% 36-# +59+78.5;0$ + 7=5):?/" <>1$ +?A3% AE5& +?G8( AF6% +@E9'  BK>1EM<2JK9.HL9+IQ?0HWC."NXD2KYE3LVE3MVB2IVA1PWD4SZG3NYE2%L[D1#NYF7 NWD5&LTB3%JVD2$MYJ4% KSE3*"HQB1' IQ?/)!JT?0-#GQ;-)DN8*)AJ5#(>G5$ ' +;C2! '8?/#9?0%%9<1#5</! !15( $.5& "/2# !'.! $' !#  "$  #  +  +  +   +  +   +    +  +   +      +         +      + +  +      +    +    +  +  + +        +      +   + +    +     +   + +  +  +  +   +   + + +           + + + + +  +  +  +     +    + + +   + + + + +   + + + +       +   + + +  +     +   +       + +  +  +     +      +     +   + +     + +         +        +  +  + +   + +       +     +   +    +    +    +    +  +      +  +   +  +  +  + + +    +  +  +        +   +        + +   +u i&za+qY}-fMq-Y8`- LI}.90j.. X+FE._"vh}5R7`fY,{LJD-A5<8:">I<H\I  \p&b x~/(Y* zpB.N?Z +>.$ p(M( A^.%\t 3'-2u814M:89f:<;888666243,0.'.+ +&(!" +  +     + +         " " + +" #  $  +&  '  (#  +*&  +' ++& *$ +'#&"&"'#(# +(# +*$ + + +' +' *& + *& + +' + ,( + +,* .* + /*  /( .( .( + .( .( + .* + +0+" + +3,# + 4,# 4.$ + 6/' 82( :3( 83'  62&  40&  40& 62' +73( 84* :4* ;6(  <7* >8+ +?:. @;/  @<0  + B<0  +C<0  D>0  D@2  FC3" GF6$ HH8( + HJ;+  HH;+ + JH;*"LK<+ +#PP?. #SSB0#TTC0"TTD0#VWF2#W[G4$Z_J7$\`K7$_bL6$`cL6&bfN7'dgP:(gjS< (hlV?!*jpW@ #+lsX@ $,otZ@ %,rv\B"&,sx_D#( ,v|`F#( .wcG#)"/zfG$)"0{hH')"2~hH',#3jJ&.%4jK$/&4kN$.%4lP$.%4oR#0&4pR#4)6rR# 7,7sR$"9.7sR#"9.7rP#":.8pP##>1;oP#"B2;nO B0:lNA.8kKD/8jJF28hH"I6:gG$K8<fF$N9>dD#Q:<|cD O8:x_CK58t~[@ G48rxW; D28otT7E27lpP4E06hnL3 F /4dkJ/ G 04`gF+ +G 04^cC*G 03Z_@*H 00V\?(I 0.RX<&F-.OT:$D+.LP7$C*,HL4#D+(DJ2"B+'BG0 @ *'?F0 ? *'>C/= (&;?+8 $#8:'5 " 44$ 0 02". ./,,.*++ '(( +& $$&  & # +!   + +  +  +   +  +  +   +  + +       +  +  + +  +  + +   +  + +  +   + +  + + + + + + +   +   + + + +   +  + + + + +  +   + + +  + +         +  +  + + + + + +  +  + +        +    +  +  + +   +  + + +      +  +   + + +       + +    +    + + + +   +  + +    +  +       + + +  + +  + + + + +       +  +     +   +        +   +   + +          +  +    +  +}~!zr%ui'la)`T~+T>j,D!Q,#06n)>Y&j%}^D@Bb}2b|KeX)}9E@*.*65.v:F8 tJYNyfi nnE0 pvzEtOGZ0}4 $nF +  +<]0' +Xr4.1%q777>8:9V798p476042.41+2/'/+ *%$!  +  + +  +    +  +     +   + +  +   "" # # + $ $ $# + # + # $ + +$ +&  +'" + +(" + + (  (  (  +(  (" *# +& +,& ,$   +,# + ,# + ,$ ,&  +,'" ,(" +.(" /*"  +2+#  3,$ 3,# 0+"  /*" /*#  0,$  3/$ +60& +70' 70' 70' 82' 83( 84( 83( :3( ;4* >8," ?:.# ?:.$ ?:.$ @<0$ C?3& + FB6'  FB6(  GC7( +GD7* JG8* KH:+ +LJ<,  LL?/  NN@/  OO@. RPB. STD0 VWG3 +"XZJ4"[[J6"\\J6"^^J7#``L:$cdP>&ggS? 'kjV?" (olX@" *rr\D""+st^F #,tw_G"%,vx`G#' ,w|dJ$*!,{gL&.$.~kO(/&/lP*1(0nP(.'0kP&,%0kP$*$2lR&"-(3pT(&1,4pV('3-4pV($3,4oT&"4+6pS&"5,6oP$"6,6lN#"8-4kL" 7,4jL "</4hK "=06fJ <.6dH8*7cG9*6bF;,7~_D@/7{|^CB17xx[@B04ttX<D12ppT:B/2lnR8B/2jjN7B/2gfK4D1/c`G2 E2._^F0 D1,[ZC/ B/+XW@. A.*SS>, ?+'LO:* < +(&GH4$: &$CC/: &#B@.: +'"??.7%<<+2 !87' . 42# +- 0, + ,+ +* (* '&(%$&"$#!#      +  +     +    + +  +   + + +  + +   + +   +  + +  + + +   +    +  + +  +  + + +  + +      +           +  +     + +  +   +          +    +           +    +! +   +! +      + + +   + +    +   + + + +  + + +  +  +  3 ""       + +    !(5BSbyu-_lBHv] +ZEUThY5SssWsvkOR;. +L+K!</@f]<5Czt+B)*[2H$N 6G+"R:E2-^~7CA;is3=POzf/51`cP*'Stx:",%  +    +  +     + +    +  +  +   +  +          + + + +  + +  +   +   +  +   +   +  + + + +     +              + + + + +  + + +            +      + + + + + + +        !  "  "  $  +! + +# +( +!#!"  $! +&  +'% +,#   + +*" )$! +&" +.($ /*)  -,'!  3.%  1-'! +  +30*# 93,$ + 71-!  :7/&  + <6-(;53)=95*  ?70$  =62% <95+ + +>?6-   A=8*  + F?9*  + +D@9+ +  EA;, +  HC;.ND<.  KE9+PF=/#QJB2# +QJA5& TK@4%SNC3$RQE2'TKD1RM>/ VOD5&TLB3$VND6%"$#UQF8$"!!WSI;(" XQJ9#! QMC3!!NH?4OE;-LB<. IB9+ FC9, C?6) @;7+   =;4)=81+ 94/)950' 61.% +/.+$ +.)" ,'&! + + +)'$$ " "    +        +           +  + +   + +   +  + +   +  +  +   +   + + + + +   +       + +     + +  + +  +  + +  + + +  + + + +  + +  + +   +  +  +   + + +    + + +    + + + +     +     +  + +  +    + +     +  +      +  + +       + +    +  +   + + +   + + +       +      + +  +      +   +    +   + + + +   +      +       + + +  + +  +  +     +  +   +    +   + + + +  + + +  + +      + +   +   +    +         +   + +  +   +    + +       +  +  +  + +  +  + + + + +         +-yf,HbEHviP&jhT: Z;|P0yI3~BBx5 +_|!pZf X&/F >2>N + F ["3V^ 709l\8NVV7*jtL +4ND1p=.6)0%( $  +   + + +         +   +    +  +  +           +  +   +  + + + + + + + + " " + +"      +  +" +# +# "     " # + $ $ +$ # # + # +$ +& '  ( (*++ * ( (  +( +( (* *+ + + ,  ." 0$2& 2& 0$ 0& 0' 0( 2*" 4,$ 8/' :/( :/( 8/( +80(:0(;0( <2( + <3* + <4+ + >6. @8/ C;0  D<2"D<3# D<4#D>4#F@6$GC7'JF:(KH;(NL<*ON>+PN?,RN@,RO@,RRB,RTD. TWG/"WXG/ WXF. VZF,"T[G.$W\H/"Z^J2# \_K3(#\`L4,%^`L4-&^`K3-&^_J20'\_J22'\_K43'\bN72'\cO8 0&\cO70&[`L6/%Z^H20%W[C.1&VX?*3(TV>+5*TT?,7+SS>+:,RR;(;,OP8'9*NO8(5(KL8'2'GG6$1&DC3#2'C@0#3'C@0#3'@?/"0$>>.*<;+&;8($83& #4/# !0+" .*  ,( +' +*$ + &" + +$  #  # +" +    + +  +    +  +  +     + +      + + +  +  + + + + +  +  +  + +  +  +    + +  +  +  + +  + + + + + + +   +      + + + +   + +   +       +  + + + +  + + + +    + + + + +  + +  + + +  +  + + + + +     + +    +        + +  + +   + + + + + +  + + +   +     + + + +  + +           +   +       +  +              + + +  +     + + + +  +   +    + +        + +  + +           "x%m(e'|]z)mQn,]>^0 I$Hr11-^2) H /A.'e&xazH=ahRk LJq8@0Z);J&? +B0)NDF>hNeN1aYp.v`ZSFl0 ]4y+ q&J3,/Da8:9`x<B?%z>FBA>FB];C?u7A<0;6+71'5.#2++$" + +    + +  + + +  +    +      +   +      +    +  +            + + + + "   #  +# +"  "  $   +&#$ +&$& $$$ $$" $$" +$$#&$$'&$('$ (($ ('$ (&$ +($$ *$# +&# ,'# +,($ +,($ ,($,(#,*$ ,+' .,+ /.,  0/.  + 02/" 020# +220# 300# 400# +300$ + 322& 444(776+886+8:4(8;4&:<6$<>7&??:' ?B;* +@C>+BD?, +DD?+ FG@+HKB,KOF0"LRG3"LSJ4 NVL6"PWN7#SXN8#TZN:#V[R<$W\V?& [^X@& ^`Z@&_c[@&_f^B'bh_C' dl`D'$ "hp`D&(##js`D$ *%$lvbD&#-($lxfG($.)$o{jK+#-(&r~nO,",'&toP, ,&&vpP. /($wpP. /($xrR. .''{sS. ,&*|sT/"-(,~rS/$/*,~rS0&2,.tS2$4,/wT3 3*/wS01(/vR./&/tP,0&2tP,5*3vR,7+3vR+6+2sP(5*0oN&6)0kL#8*2jK#:,2|hJ":-2x|gG ?02vwcD F33ts^B"L73roX?#M82onV<"J6/klS:F3,gjP7A.+cgL6?-+`cH3?.+^_F0@.*ZZC,A.'TVB* ?,'RS@' >+'OR?& ; ('LN<# 9 +&&HJ8" 5 "$FD6 1 + #C@3 0 +"B<0 -?8, ,<4( '80$ &4," $0( $,$ #(" +"' &# +    + +    + + + + +   +  +  +     +  +  +  + + + +   + + + + + +   + + +  +  +  + +      +  +  +  +  +  +  +  +    +  + +   +  +   +   + + +   +      +     + + + +    +  +  + + + +  +      + + + +  + + + +  +  +  +      +     +    +         + +   +   + +     + +   +       +   +     +     +   + +       +  +    +         +  +   +      +   +    + +E.'#   +      '0AF[bqyl)W\BLQVUeHleIG{>Gorc3CF7(*<7k@-0Na>%/n|'JS.PJ0M#J0G*!R v/A!5/] +k+;1D?j^'3ITWzH&#gpp6 ,  +      + +   +   + +          +   + + + + + + +  + + + +       +   +   + +  + + + + +  +     +            +            +! +"%   %  ' "*#%,! #* &,  '.!  ),#! '.% +)-#  +'0& + )3(  +5)!  +*7+ 0:-  + +/9+! 1=-" + /;0$ 1:.% 0=1' + 2C2) 1E8* 6G5* 4D9' 5I7( <K;. +:L<. 7M9+  8J9+  9N>.9N=-<O@0;N?/>Q=. @O@1>UB/>S@-<T>/ >R@2  AU>0! ?SA/" @Q>-!#" AQ?/ +:L7,  3E3% 4F4& 9F5' +6G5( 3C2% +0?/# -;,!  +1<--8+)5(*5* '6' + '2%$/" %+   &," +$ + $ +  + +" #       +  +  +       +  +   +   +           +     +     +  +     +  + +   + + + + + + + + + +     + + +    +     +     + + +    + +              +     +   + +  +  +      + + +    +  + + +       + +       + +      +  + + +     +y#p%h&~^'qPv)d:e*RN)%<1n'>V&p"[@BJk|n0`& VaM*DD90;*2=:8QE zIeZ }bt,xy'J%y{:yVJP,}7#%d>#+ v2T2' +Pj 7,1(l;79A<<<Y;=<p8<:3;7/94+50&0+*%$     +  +                "#  +$ # +#"## #"#"# &"'" (" +("'"&#$#&$&#'#($+'+*" + +*" + +( ,'.'/( /( .( .* .*.*,( ,(  ,*  .+"/+"2+#3,#3/$32&22'32( 32( 63( 74(86*:7+:8, +:7+ :7+ ;7+ + <:.  <;/  ><0"  ?>0" @>/ @>/  B>0  +C@4# DD7$ CD6$ BD4$ BD3$DG6$ +GJ8& JN>* +NSB.PWD0  SXF2  TZH3 W\K6 Z`N8"[cO:"^dP:#`fR;"bgT>"ckXB dn[C gp\D  jr^D  ks^D  lt_F""ow`H$$s{dK''whL((xkL((xlL'(zlO&*|nR&,oS&/pR&2rR$4sS$8tV$<vW$?vW$@vW$ @tV$#%$BtV$$(&DsS$$,(FrR$#/)DoO""1*@lL 2)<hH 6+8wdD=.6r`BE23l^@I43k\@ J52j[? I40h{X< +H2.dvT8J 2,brR6L 3,_oO3J +3+\kK/I 2+ZgG+J 2+XcC(M2,X`@&L0+W\<"J/*TX8E,'PT4? )'LP2: &&HL/8 $'FJ/: &&CH/; +'&@G/; +'$<C,8 $$:?*5 "#7;( 2 "48&2 04#3 +!.2 2 +!+/ . *,( &($#&"#      + + + +  +    +  +  +  + +  +      +     +  +  #  +  +  + +      + +   +    }ty ld"dMy(Z6b.NJ}1@0f3 +.N597$9#x8/nq2>Xm}*MqDl#q$Ya1|l,h ~bRr!nl6`{kDZem?[zr5EbqJX +}y(4exRZ|%mX^}&xZd{0Zjx 8Znv @[ov H\qvQ^rv]_uu(lbtw4ydqz@gj"Phd*ah`4vjZ:oRA!wJI*|HT8|J^IwRj -]p]y*2ItjkIQhb|senWd|JT>F4:+."#  +   +  '   $*# *,&  ..(  + 00+# 00+# 00*" +02*" +23+# 44,$ +74,# :6.# :7/# :80$ :82&;82'<82'<80&<:2&<;3' <<4' +><4'?<4' +B<6( C<7(D<7'D<6'F>6(F@8,FB8.DB8/FB8/GB;/HB;. JB</KD>0 LG@3 LH@2 NH@0OH@.PH@.PJ@/PKB0 PNC2 ROD4"SRF7#VSH8$WTK: #XTL:"WVL:"XXN;#[[O? &_\SC#&`^VD$ &b_XD$!&cbXD$'fcXD$(gfZF$(jg[G&!*lj^J'!+ok`K("+pncL*#+ppdN+&!+ssfO,(#.wvhP. ($.{xkP/"(%.|{nR/"(%.~~oS/ '$/rV/ '$/tW0 '$/wZ2 ($0z\4 +&2|_6 ,&3b7 ,&3c8".(6f:#/)7h;&1,8k;(2-8n<,30:p>.52<v@.83@zD,;4C~F,?6D~D.@7D~C0>7DD3>9DG4?:FJ3A:GK2D;JL2I>KL3NANL4PBNL3NANK3K?LH6J@NG8KBPH:MDSK8LBTK7OCTH6UFTF6ZHVF8\JWG;]LWH<[LVH;ZKTG:ZJTF8[JTD8\JTD:^LTC:]LT~B:]LT|?8ZIR{>8[JOx;8[JLv:8\JKs87[IHo76YHDj43UDBf22TC@c00SB?{`00SB>w^00P@;s[/0N?:~nX//K=7|whS.,H:4xrdN.(C62sn`J+&@30lj\G*$;0/fdWD'#:/,_\RB$ 8,*ZWN?":-'SSJ< :-$NPG: 9,"HKC64(FF@42'BB<20&>>801(::4,1(762(.%43/&) 00,$#,,(#!(($"$$  !!    + +   +    +        + + + + + + + + +         +   + +    + +   +    + \ No newline at end of file diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/expected/test.des wfdb-10.5.23/checkpkg/expected/test.des --- wfdb-10.5.22/checkpkg/expected/test.des 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/checkpkg/expected/test.des 2014-03-08 17:35:31.000000000 -0500 @@ -0,0 +1,60 @@ +#ECG/1.0 description +Record: test (1 January 2004 02:13:18) +Age: 49 +Sex: M +Referring Dr: +Comments: CP TECH JLR RM 7 +Cart ID: 146 +Department: xx +Department #: xx +Institution: +Institution #: 25 +Report 0: Ectopic atrial rhythm +Report 1: - ventricular couplets +Report 2: Long QTc interval +Report 3: Left axis deviation +Report 4: Extensive infarction - age undetermined +Report 5: +Report 6: Abnormal ECG +Bandwidth: 0.05-150 Hz +Filtering: 60 Hz notch Baseline filter + +RR interval: 869 +P onset: 40 +P end: 188 +QRS onset: 210 +QRS end: 314 +T end: 656 +P axis: -18 +QRS axis: -41 +T axis: 118 +Parameter Units I II III aVR aVL aVF V1 V2 V3 V4 V5 V6 +P duration ms 134 96 50 126 118 110 128 98 148 128 126 84 +PR interval ms n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +QRS duration ms 90 82 76 86 88 80 82 88 96 94 84 76 +QT interval ms 446 416 416 432 428 420 418 426 426 422 418 416 +Q duration ms 39 41 42 0 16 44 82 88 98 94 84 47 +R duration ms 50 40 33 35 39 35 0 0 0 0 0 7 +S duration ms 0 0 0 50 31 0 0 0 0 0 0 20 +R' duration ms 0 0 0 0 0 0 0 0 0 0 0 0 +S' duration ms 0 0 0 0 0 0 0 0 0 0 0 0 +Q amplitude uV -111 -597 -1517 0 -49 -1003 -1221 -1917 -2299 -1652 -1110 -449 +R amplitude uV 1130 300 255 186 1306 277 0 0 0 0 0 121 +S amplitude uV 0 0 0 -441 -110 0 0 0 0 0 0 -129 +J amplitude uV n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +P+ amplitude uV 102 55 0 17 82 14 60 190 55 47 50 46 +P- amplitude uV -24 -30 -73 -74 -9 -70 -85 0 -129 -132 -39 -34 +T+ amplitude uV n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +T- amplitude uV n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +ST slope uV/s -3 1 5 1 -3 3 14 36 33 29 21 9 +P morphology -- n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +T morphology -- n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +Isoelec (I) ms n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +Isoelec (K) ms n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +Intrins defl ms 57 55 50 26 38 54 0 0 0 0 0 51 +Quality -- 0 0 0 0 0 0 0 0 0 0 0 0 +ST (J+20ms) uV n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +ST (J+60ms) uV n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +ST (J+80ms) uV n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp n/comp +ST (J+RRm/16) uV 0 0 0 0 0 0 0 0 0 0 0 0 +ST (J+RRm/8) uV 0 0 0 0 0 0 0 0 0 0 0 0 diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/expected/test.ecg wfdb-10.5.23/checkpkg/expected/test.ecg --- wfdb-10.5.22/checkpkg/expected/test.ecg 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/checkpkg/expected/test.ecg 2014-03-08 17:35:31.000000000 -0500 @@ -0,0 +1,503 @@ +#ECG/1.0 signals +Record: test (1 January 2004 02:13:18) + +  !!  0JfW# +    + +.Gc`0      +   + + + +$>^}r; +   + +      +   + + +   +    +  &+---..+" + + + +       +        !%')+,,)% + + + + + +  + +  + +  + + +  + + +   +  + + + + +   ++26:73/*"    + + + + + + +  + + + + + + + + +  +   +      + +  +   +  +    +  + + + + + + ~x| +   + + + + + + + + + + + + + + + + + + + + + + +hJ0 +.F]q +   "#$$$&&&$$'*,,.////2332002222332/,+++*''''&$#"  + + + + + + + + + + + + + + + + + + + + + +  + + + + + + +   + +    + ' kN/!/?Wp  +   + + + + + +  + + #! !#%#&()+-+-0.128597;<99>=@?=@B@>@>A>?734552/,-+(*'%" "  + +  +   +veN1,>Tj ""   ""##$&'((((*+,+++./00//04764368>BDFHKNOPRTX[\^^_`dhkllnoprstvvvttsrolhd`^\[XTROKGC@<842////,*(&#  +ybJ0|nebemx-Ih #&(++**+,,./0222023444677668888;;<>@@@@@@BCDFHKLLNOSVXXXZ[^`cdfhknortwz|{wsnhd`\WRNJGC@<842/,($  +   +      +   + + +  + + + + + +  + +   *    "%! &--&#&++    +   +  + + +$/8>BDJNOLG@82+$ + + + + + + + + + +  + + + + + + + +    + + +   +   + +  .Fh p?#>^zG$ + + +  !5Suk.!""$!#(!#!$&',*)+./-31397:<;=?=<>AFDEHNKPQQTSRTRVTVUWXQNOLIFC@==996/+,)"   + +  +  ybP:*Np + """ "##" "#$$$###$&'((*++*(((((**++,.0220000248::88:;<<<>@CDDDDFGJKNOPRRRRTWWVTWZ\\^^^\\\\\[ZWVTTSRONKGDCC@><;840.,+*&$##"    + +  + + + + +  + + +   +|m]I1qZJBDNav%A]u + "##""$&&$$$$&'(((((*+,,,,,,,./0002343347888:<??@BDFHKLLNPSTVW[^__bdhjllortvwx{|~~|xvtrokgc`^ZTROLHFCB?<840,('&#  + + + + + + + + + + + +  + +  + + + +  + +  + + # nSJJR]jz  + +  + + +    +   ! ! !"$%')**'(..++.-0/.1/-/20/-/,%&'(%#!  +   + + nV@0*0=Qetyyy} ""  #$$$$$&*.02368::;>BCDDDFHKLLLORSRRSVWWWVVSROLHDB@@?<863/+(&" + + + }fN9$!*8I]t ##""#$###$&''&&''''((''(,.///./0320../02478:::;?CDDDDFGJKLNOPPRSVWZ\_bcfhknpvz~~~~|{xvsojfc`^[XSNJGDB?<:6420,(&$#" +  + +    + +  + + (   +   u[LEUk +8YlyrgZH2$ +  #"! %&#%!%%$''')+%(&"   +    zbRO\v>i|rlf`YSNHFC@?>@=;60*#   +   + +    + + + + + +   + +   +   +  +  # + +  + k\]i8Yl}~s^L>0)& +  + +   + +  +   + + +   + " " !#  + +     + +  + +  + + + + +  + + + + + + +  + +   + + +   +   + v`LA:@CFHLNNNORTWXXZ[\^_```___bcc`^[XVTSRPOLGC@@?>;83/+*('$"   + +   + + + +  + + + + + + + + + +  + +  + xme]Q>$zR8)&0Fe&D`z +   #$$$$$$&'('&$$&'((((*+,./2220002478:;<>?BCDDGKORSVWXZ[\^`cfhlpsvx{~|wsonljgc_ZVSRNJD@<840,($"  + +  + +   +    )BVl{oF  +   +     +  + + + +  + +,' + + + +  "$'(('&&&&$$$$$$$$$$" +xqmlllmqx   #$$$$$&'(*+,.////024678:;;<>@DFDCDGJKLLLLKHGHKKHFFGHHGFDDCB?>;:8742000//..+*'$"  +   +      +   + + +  + + + + + +  + +   *    "%! &--&#&++    +   +  + + +$/8>BDJNOLG@82+$ + + + + + + + + + +  + + + + + + + +    + + +   +   + +    + + + + + +$(.38::862,'  + + + +  """#""$$#    + + + + + + + +  + +|} +0478740.+'   + + +  +"&'$"""" ""  + +       !       +  """!!    +     + + + +  + +  +   +  +   + + + +   + + +    + + +38<>>;70+'# + + + +  + +  #$#" "$&$  "#" + +  + + +  !  +    #+27;<;83/+&  + #$$#"     + +  + + + +  + +   "" "#&(,..,,.034322343368:87668;<;:88:::888876320000/,(&$#  + +  + + \ No newline at end of file diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/expected/test.hea wfdb-10.5.23/checkpkg/expected/test.hea --- wfdb-10.5.22/checkpkg/expected/test.hea 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/checkpkg/expected/test.hea 2014-03-08 17:35:31.000000000 -0500 @@ -0,0 +1,14 @@ +test 12 500 5000 0:01 +test.dat 16 200/mV 16 0 -12 -23649 0 I +test.dat 16 200/mV 16 0 8 8963 0 II +test.dat 16 200/mV 16 0 20 32612 0 III +test.dat 16 200/mV 16 0 2 7010 0 aVR +test.dat 16 200/mV 16 0 14 21307 0 aVF +test.dat 16 200/mV 16 0 -16 -28202 0 aVL +test.dat 16 200/mV 16 0 -4 19544 0 V1 +test.dat 16 200/mV 16 0 -20 18227 0 V2 +test.dat 16 200/mV 16 0 -12 17044 0 V3 +test.dat 16 200/mV 16 0 -12 14214 0 V4 +test.dat 16 200/mV 16 0 -4 16182 0 V5 +test.dat 16 200/mV 16 0 -8 15059 0 V6 +#: 49 : M diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/expected/test.key wfdb-10.5.23/checkpkg/expected/test.key --- wfdb-10.5.22/checkpkg/expected/test.key 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/checkpkg/expected/test.key 2014-03-08 17:35:31.000000000 -0500 @@ -0,0 +1,4 @@ +#ECG/1.0 key +Record: test (1 January 2004 02:13:18) +Name: xxxxxxx, xxxx +MRN: 0000000 diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/checkpkg/input/test.scp wfdb-10.5.23/checkpkg/input/test.scp --- wfdb-10.5.22/checkpkg/input/test.scp 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/checkpkg/input/test.scp 2014-03-08 17:34:56.000000000 -0500 @@ -0,0 +1,29 @@ +$VD Z#3o< d'= += GD(FPLUSI +255.255.255.255"PLUSI1.93/3.18/16.10/1.620000146<d 5433 0000000xxxxxxxxxxx1  + +   xxUser Field :CP TECH JLR RM 7ECGPPPP  = '> ? @    L L L { AbnormalN=ZE + m^)R S + c { _l{ui~|so*˒ qRM nM ++nj܋RUrFԕYnԔ9v޻9YK-0TD+ !Eb&K{$.ąDJUdVDEDаIV"6ˋ-V*!ArV E!b[U?}țejž||m}ݶ#$ddIlB-,. +f-]ɗlYnXʅKZ, VJYr%-%XMReLy6lJc}VնQaV%PX"(AH@Ep(K*YbKbTJ,EV.PK)b#a%[5e2J*ʕdT~yy>ovg>g?y]omʳrT,YW,؉jK6YIf˖)TeJ*I` !VeKZ$"˖QrN̋7E+ xv&U6. +Ye,TjRH)!PBlR*jJۋ),)ȨYn2K`ݲˊ@ a,Ib5,lȱlIaeܩebG_znDRڶ퉿_On-)*X e!**-*[efPK,H)*3k@ ?lP,,%BY)R͖,D7,P7/,,6R^~?'s91n,l +Ebl%X R"UdblUeK.U%*bbrR*,,@-,e%%"Q=l-n~f/9ZYeE%KnJBX6!W X *XK)%,JrLU,J)$[e(:v/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/u/uEvh6Bj/u\=J%F,͸(U ,6 &l "ڻLX۔l˹o,R(R-L,,d.T $bf"SdRȶ.[5eJ٩.mȶ(TT*&ʒS6ɷd6Ml5.-6䨶ZJXv2T-BdU wY)eq3$˹e,K[%JX ln)-͕ʶ+&bRZ%r㸩dS^]D,J%bR.XJAٍ"[%{y-ʺĠVNĥ注^lYMˬY=1ן]?63Qvg'I.f7%L#e͓e7%*UIY؝H,\-MLN6JceuvXeYVmTK,rTjJ-%dY-ɱ-e +ٍچ]Qb]DZYmyK#˩bl3K,ԲvUfHm+ơ,[%\ɳq)dk!shMr +ʐRTbZhŨKK6VYq{?_nE*«dl`eť$Y%- IJ(dK5ȱEY*Ya6"-- jKblfe\[,ʹQ-IBķ,Xܛ76 ذ\%IJ+f^e_uJ*GŚ*lVM[.Yl.P.P ,@,\[g.0XԫKbU5.Ui9~~cQ/l\$+.e %Zɶ,5a-[%.[bYYTɹnYeMK&r`Ka6%VYr5%*ZrK-\% lQ,ŀl-ʔ W(Me,Kd|ݫ6Uk&r64B͔&¬XK*JXKcr*6 l&SPn]@,XY*nlM)*l*ɲ[%\bn[&Y-"ʖ rlR%`jMjVo?'6EA1̛ffR,j.( % Se&X&lQ6Y6K-͒**SrʛII[.-+(,"MblM͓`J,`%\l%Zk6J @6KRŅJ"J_O"Jom̗<3J楤enRZD(XR nnP%J n-5-X,* \M͒eʱ(\؛srMI@lqlMܷ5#ddr(MŹ7s)` W,r%Źv l(Ea3D*-%MʹPd Ar,[ @%J%MRle)%\XlrV\`ɬ%b6,˛ * Ej|΀Dݩ{{\6\R3ef)r6%)bRKPMf&唲U͒ɲl ,`\`.PE.TY@؝n[Y-knUk&bU3De**TV[&BQ-YܤV[*\f39[6XYRܳd`Jd)rY(.Tl3InYl&l\,ņd-͒͂[&-[fllKb˖PU e %)΢JXXXUal7|ԛ%[e9瓻I7M, %[%MlbnnlM dܶ\eMe⒉;ܩD[% n)RnTlb[ ˔V-\͗ k&X%` ؔ*[ RmT-؊-/?rnY—2kzFrsQ6Jb` @\6M6X(M̀"HЗ5 eͅYe͋A6,eR%"ͱT!fUrlYv)e Bl W(%JJYR[lrB͈[n)YQRXX.PW-BRl,%V5bl #`MM\ؖPrQ\\(UJ*k(PX .PlFJXJf7ɲP l&[:sRRk-fͼ;˸Ƕ?Y5^ۗ>]ԗq{M %ܖj&,l,Ųl, ˖JI-$Y,T,SrJ-ƲYnQ-%%X` \͖dd.;Mԋa)dؖ6J**l,XRMv,%I_Cie&]&.,"qBT-PBU)r%XJnn[[%jM%a-Jrn]XKdK&,6&%X&RKa5[rܪ͉N̻ٝC?Y Xye'6䨈dMnEd(%%1jMrذ,6\ؼ"ERlE%lrf,,J6KarRVYjJl6-- PfYWP"cdXM,% gIivK s[8yVf,l*[,Il"J))(%,JKQaD6J%E6VY.P PKbbRʼ,\Eܠ eeeY`)JJqlM?N_߼X[%fKי3\Vm(7(I@Yk+6 erK,T\T6 %UP!Ib%X+(-ʗyD&ijYIw.l**MEĻ|ǿg?O'ڻ9œY9{[2[,͂\[%Pe)XXMBɱblMʕ.lPK-,R)))-k$XTk(ؖl7-ʔ @Jnl %\Pr5qlܩe%+-"%bnlKRPwo_ 5!mc~&dԲY &ƲMIle\ +@XRlTn,(EJ77,UʊJ MƳrܫ" .Rl6䤥%7"f%'y[@Ro Yl.U`Dܶ&őrwԶ%\.m%l)rPXVPsomm̓[. +.Vflre\ؕ,RT喥EYW,T7,6,%%[ dblY,.Y,l(,M6%TX6 .U*yIe***J%KPW*?G;d%*M׬1vSsRʔJ7JY Vf*YLYRbʋpcr,qlʍve--4vd+("ԝĶ9ۖ `.[.Uy +KR&ȯ?_s=Xow33fOn, V\a-f\\ʹl ܻcyKXTr).lRTRnX,A*UEr55#`Y6 +ʹeKؕa-V%*l-͉XU J 6ebsnYDw&nl' ܗUĴ% ۽ymSnj K6MʸYDKqfRYSd;Œ*\Ԭ6&l-JJ@b,k?K]Klna&d$j 2ڶ4hdPYHeXe` jKRͅKdeL&A InU%d%Y5eQd%F \(-ErsqKdܠYwBDQ?R`²͒%EnK(KnP]c:l,wecdmYDi57=LuI8yEq6(Fŗ&-BK% bnU[JMؕ,I1BlY*[6Krܷrl6E U\%ʰe6T*Ll,[.jK+7?y"y/w>yʑmP$ ,V*6%,M-eY`MTY6'pY5"EJ)*-KqIW*n5q%W*n-"da5q[qlar6[F &I&.Ne(ͰJ夲]lJES(Ylsw?lm*74&5|o-vJDJ.Q-6MIJn,e"ʊ&⥱bŋrP2b.[&ɨY"ʹlMEXNU*URk,&KsrEX%ܫd^u%7 ll&ܕdJ{tYJ%S\_lbeYYQn)6*P,a.u%MnTR, eüERnYSY6ff˔.l5rnk7*n5lYf\.͗qqY %JY6,K|Wws["˶'{yf& ĤrJj jJ*Ke .E6%RP"7)dj&Ķ&S;,X`k*l4奀&˛S4l%VlMS`\ܷ6%܍&wbX*lQ7-+*Y,d5&N?W-vLZJIeՙ~ddV֢&mfen)R6Eyb%)RK-.K7 *X7KY,V+--ʲUnR,Q6%JMIU%ؖ[ZIl,,X"fŒؖ_?kcfhPog^g V-K,ؖ3@EA eejYD)-l&ܱlJ%J ټvV56&7d e35;5J,.Uܖlvsn5UB4̈́a,rKPM34|2ٟ0ϙ#B˚sar\j DeŹMlXJܵة5bܶK**YfRTTų;[K*[%&ɲKarA-7*EbE,eUV, (X6&nnQ&R;2-X%JKbɲ.n?K] eg;dbMbJ%Mb 5.RnT*唸]剨SpRTR[%QܱYl6̿]M;#Fhd2Fbس6V-Ģ[#[TR\,TYbł7&Ƥed.[ɹBPfKb[&MʹW6;dJ&lȵ UbPlbTs`%Qd3"Q7,6 _JT)5jKr)6X2Yeo &Y(eDܛ-"56m*嬤TYSrR[7-sĶKzӿOIf6Mr%%3iRVRTKe%P+f(-Mw%RnlVk,*e*r͋,X5%jf)r-X%f-&ܫ.)a-ʸlKr悑Hȶ&\{f[Y!e-ݶ|?9[mʨRvIB[,؛ "U6ĶKbQLQy*ȢYA`PNd(X\KŸ̽ĩqB[˖l&6brd,M(XXܶM)5)r,nlJ nQYK b7'E6Mebʤ)6]#&\RTsqD-ʲ\I(lJ\lKdYDrZeK,,Ҡc5QEʛ& %U7 sc4n5Yf{J.YK&%VF&ȥedIA*&iO, ,Ue#>K;c"YlYY-Z܊MB[5*@K-6Fd*5lX,*)6.6&ܥegs;,ܔ n[ WY*-\6FԹ37dfC~M!&_*dԫM Vgaa-bʹV.RKbKʝfYe\lB[.PMjMlMeRfqR*l  M͖%Kd k7*عR[ƲY*lwwPͱ7(XaPjJ, ?ŲɩdlNDwSUdPeMsfjTVJJh,7(\l l\U VYYVMY*ܡ6.[@ Y.lf,⬛%,ĻŹ Il++,[&Ų6[",M̀Y575 r7|l.l,UɶK|̒\Bl{2#`fJ`#beؖdܶ"dPTQsP.P*Nw^^rJ &Ȼ2ܬXͩ-bl VMXl*EMIlM,7-͑l*Pcs?w'_RybbKb[lo`&Kܷ*JKisadؖMECrd[bUb&l6Kra7-e6Kb6\FYSY@ ,`,%Kd&J.[&˖[%))6JKabS7l +9-e, eNfb{0&V-Ų["Q-ܩr(U%"Mɲ,7͗&bRTɰe-Mv5lDjةn6QrYBɲ*D"TnTn7*Ŕ+X.laaeIjRlXKa-`KesBlEdǗKR;&ɰb[.[3E͂bY,-f: Ectopic atrial rhythm - ventricular coupletsLong QTc intervalLeft axis deviation(Extensive infarction - age undetermined Abnormal ECG dQ 24.6ENGLISH<@` ? +al rhyth /uZ'2j/uf/u/u/u/u/u/u9/u/u/u@v ]2 -`/uR)(,/u7/u/u/u/u/u/u7/u/u/uVL! )@RAb=2/uL*!/u/u/u/u/u/u/u2/u/u/u\ +15:>>~/uV#2G/u/u/u/u/u/u/u/u/u/uN s4Z?v/uX'/uR/u/u/u/u/u/u&/u/u/uPp&2@n/uP,#/u/u/u/u/u/u/u6/u/u/uX8 ,@/uRR;/u</u/u/u/u/u/u/u/u/uP,&dr> dZb/uXX/u/u/u$/u/u/u/u/u/u/uHb/q<>&N/u`b/u7/u/u!/u/u/u/u/u/u/u@4) .20g}}/u^^/u/|/u/u/u/u/u/u/u/u/uB&-_2&{KA~/uTT/u2/u/u/u/u/u/u/u/u/uHHde2PZT/uL/?y/u./u/u /u/u/u/u3/u/u/uH>:2?@#ZH  \ No newline at end of file diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/conf/linux.def wfdb-10.5.23/conf/linux.def --- wfdb-10.5.22/conf/linux.def 2010-05-13 11:01:52.000000000 -0400 +++ wfdb-10.5.23/conf/linux.def 2014-03-13 15:27:29.000000000 -0400 @@ -1,5 +1,5 @@ # file: linux.def G. Moody 31 May 2000 -# Last revised: 13 May 2010 +# Last revised: 13 March 2014 # 'make' definitions for compiling the WFDB Software Package under Linux # Choose a value for WFDBROOT to determine where the WFDB Software Package will @@ -63,17 +63,26 @@ # CCDEFS is the set of C compiler options needed to set preprocessor variables # while compiling the WFDB Software Package. CCDEFS should always include -# VDEFS (the version definitions). Under Linux, -fno-stack-protector is needed -# when compiling the WFDB Toolkit for Matlab; it's harmless otherwise. -CCDEFS = $(VDEFS) -fno-stack-protector +# VDEFS (the version definitions). Under Linux, add -fno-stack-protector +# when compiling the original (SWIG-based) WFDB Toolkit for Matlab. +CCDEFS = $(VDEFS) # MFLAGS is the set of architecture-dependent (-m*) compiler options, which # is usually empty. See the gcc manual for information about gcc's -m options. MFLAGS = +# CWLOPT is a set of options for CC to pass to the linker. Recent versions of +# gcc/ld do not load shared libraries named on the command line if they are +# needed only by other shared libraries, unless these options include +# '-Wl,--no-as-needed'; this is needed to force loading of libcurl or libwww +# with WFDB applications if the WFDB library was compiled with NETFILES support. +# Older versions of gcc/ld do not recognize this option, however; remove it +# here and in linux-slib.def if your CC doesn't support it. +CWLOPT = -Wl,--no-as-needed,-rpath,$(LIBDIR) + # CFLAGS is the set of C compiler options. CFLAGS should always include # CCDEFS. -CFLAGS = $(MFLAGS) -g -O $(CCDEFS) $(LC) -I$(INCDIR) +CFLAGS = $(MFLAGS) -g -O $(CCDEFS) $(LC) -I$(INCDIR) $(CWLOPT) # LDFLAGS is appended to the C compiler command line to specify loading the # WFDB library. diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/conf/linux-slib.def wfdb-10.5.23/conf/linux-slib.def --- wfdb-10.5.22/conf/linux-slib.def 2010-12-16 18:18:10.000000000 -0500 +++ wfdb-10.5.23/conf/linux-slib.def 2014-03-13 15:17:02.000000000 -0400 @@ -1,5 +1,5 @@ # file: linux-slib.def G. Moody 31 May 2000 -# Last revised: 12 December 2010 +# Last revised: 13 March 2014 # This section contains settings suitable for generating an ELF-format shared # library under Linux. @@ -50,24 +50,33 @@ # LDFLAGS is appended to the C compiler command line to specify loading the # WFDB library. -LDFLAGS = -L$(LIBDIR) -lwfdb $(LL) +LDFLAGS = -lwfdb $(LL) # CC is the name of your C compiler. CC = gcc # CCDEFS is the set of C compiler options needed to set preprocessor variables # while compiling the WFDB Software Package. CCDEFS should always include -# VDEFS (the version definitions). Under Linux, -fno-stack-protector is needed -# when compiling the WFDB Toolkit for Matlab; it's harmless otherwise. -CCDEFS = $(VDEFS) -fno-stack-protector +# VDEFS (the version definitions). Under Linux, add -fno-stack-protector +# when compiling the original (SWIG-based) WFDB Toolkit for Matlab. +CCDEFS = $(VDEFS) # MFLAGS is the set of architecture-dependent (-m*) compiler options, which # is usually empty. See the gcc manual for information about gcc's -m options. MFLAGS = +# CWLOPT is a set of options for CC to pass to the linker. Recent versions of +# gcc/ld do not load shared libraries named on the command line if they are +# needed only by other shared libraries, unless these options include +# '-Wl,--no-as-needed'; this is needed to force loading of libcurl or libwww +# with WFDB applications if the WFDB library was compiled with NETFILES support. +# Older versions of gcc/ld do not recognize this option, however; remove it +# here and in linux-slib.def if your CC doesn't support it. +CWLOPT = -Wl,--no-as-needed,-rpath,$(LIBDIR) + # CFLAGS is the set of C compiler options used when compiling the shared # library. CFLAGS should always include CCDEFS. -CFLAGS = $(MFLAGS) -fpic -g -O $(CCDEFS) $(LC) -I$(INCDIR) -fno-stack-protector +CFLAGS = $(MFLAGS) -fpic -g -O $(CCDEFS) $(LC) -I$(INCDIR) # WFDBLIB_BASENAME is the name, without version numbers, of the alternate # library. WFDBLIB_SONAME is the shared object name ("soname") of the @@ -90,12 +99,12 @@ # BUILDLIB is the command that creates the shared WFDB library once its # components have been compiled separately; the list of *.o files that # make up the library will be appended to BUILDLIB. -BUILDLIB = gcc $(MFLAGS) -shared -Wl,-soname,$(WFDBLIB_SONAME) $(LL) \ +BUILDLIB = gcc $(MFLAGS) -shared -Wl,-soname,$(WFDBLIB_SONAME) \ -o $(WFDBLIB) # BUILDLIB_LDFLAGS is a list of arguments appended to BUILDLIB following -# the list of *.o files (for most platforms, BUILDLIB_LDFLAGS is empty). -BUILDLIB_LDFLAGS = +# the list of *.o files. +BUILDLIB_LDFLAGS = $(LL) # LDCONFIG is the name of the program needed to refresh the system's cached # index of shared libraries. diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/configure wfdb-10.5.23/configure --- wfdb-10.5.22/configure 2013-11-19 11:29:43.000000000 -0500 +++ wfdb-10.5.23/configure 2014-03-06 02:11:57.000000000 -0500 @@ -1,6 +1,6 @@ #! /bin/sh # file: configure G. Moody 24 May 2000 -# Last revised: 19 November 2013 +# Last revised: 6 March 2014 # Configuration script for the WFDB Software Package # This script was not generated using 'autoconf'. If you can implement @@ -15,7 +15,7 @@ # Defaults (overridden by --interactive): INTERACTIVE=no LIBTYPE=dynamic -DIR=/usr +DIR=/usr/local MANDIR=unknown NETLIB=unknown @@ -141,6 +141,7 @@ echo "SRCDIR = \"$SRCDIR\"" >>site.def echo LONGDATE = `date '+%e %B %Y'` >>site.def echo SHORTDATE = `date '+%B %Y' | tr a-z A-Z` >>site.def +echo YEAR = `date '+%Y'` >>site.def echo "ARCH = $ARCH" >>site.def echo "# _____________________________________________________________________________" >>site.def diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/convert/Makefile.tpl wfdb-10.5.23/convert/Makefile.tpl --- wfdb-10.5.22/convert/Makefile.tpl 2009-02-26 18:06:18.000000000 -0500 +++ wfdb-10.5.23/convert/Makefile.tpl 2014-03-06 01:49:38.000000000 -0500 @@ -1,11 +1,11 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 23 July 2008 +# Last revised: 5 March 2014 # This section of the Makefile should not need to be changed. CFILES = a2m.c ad2m.c ahaecg2mit.c m2a.c md2a.c readid.c makeid.c edf2mit.c \ - mit2edf.c rdedfann.c wav2mit.c mit2wav.c wfdb2mat.c revise.c -XFILES = a2m ad2m ahaecg2mit m2a md2a readid makeid edf2mit mit2edf rdedfann \ - wav2mit mit2wav wfdb2mat revise + mit2edf.c parsescp.c rdedfann.c wav2mit.c mit2wav.c wfdb2mat.c revise.c +XFILES = a2m ad2m ahaecg2mit m2a md2a readid makeid edf2mit \ + mit2edf parsescp rdedfann wav2mit mit2wav wfdb2mat revise SCRIPTS = ahaconvert MFILES = Makefile diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/convert/mit2edf.c wfdb-10.5.23/convert/mit2edf.c --- wfdb-10.5.22/convert/mit2edf.c 2010-07-27 14:13:11.000000000 -0400 +++ wfdb-10.5.23/convert/mit2edf.c 2014-03-12 09:32:56.000000000 -0400 @@ -1,8 +1,8 @@ /* file: mit2edf.c G. Moody 2 November 2002 - Last revised: 27 July 2010 + Last revised: 12 March 2014 ------------------------------------------------------------------------------- Convert MIT format header and signal files to EDF (European Data Format) file -Copyright (C) 2002-2010 George B. Moody +Copyright (C) 2002-2014 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 @@ -138,10 +138,9 @@ year = 1985; /* beginning of EDF epoch */ } - /* Calculate block duration. (In the EDF spec, blocks are called - "records" or "data records", but this would be confusing here - since "record" refers to the entire recording -- so here we say - "blocks". */ + /* Calculate block duration. (In the EDF spec, blocks are called "records" + or "data records", but this would be confusing here since "record" + refers to the entire recording -- so here we say "blocks".) */ for (i = samples_per_frame = 0; i < nsig; i++) samples_per_frame += si[i].spf; frames_per_second = strtim("1:0")/60.0; /* i.e., the number of frames @@ -169,7 +168,8 @@ " EDF blocks cannot be larger than %d bytes, but each input frame requires\n", EDFMAXBLOCK); fprintf(stderr, - " %d bytes. Use the -s option to select a subset of the input signals.\n", + " %d bytes. Use 'snip' to select a subset of the input signals, or use\n" + " 'xform' to reduce the sampling frequency.\n", samples_per_frame * 2); exit(5); } @@ -202,6 +202,17 @@ /* Calculate physical and digital extrema. */ for (i = 0; i < nsig; i++) { + if (si[i].adcres < 1) { /* invalid ADC resolution in input .hea file */ + switch (si[i].fmt) { /* guess ADC resolution based on format */ + case 24: si[i].adcres = 24; break; + case 32: si[i].adcres = 32; break; + case 80: si[i].adcres = 8; break; + case 212: si[i].adcres = 12; break; + case 310: + case 311: si[i].adcres = 10; break; + default: si[i].adcres = 16; break; + } + } dmax[i] = si[i].adczero + (1 << (si[i].adcres - 1)) - 1; dmin[i] = si[i].adczero - (1 << (si[i].adcres - 1)); pmax[i] = aduphys(i, dmax[i]); diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/convert/parsescp.c wfdb-10.5.23/convert/parsescp.c --- wfdb-10.5.22/convert/parsescp.c 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/convert/parsescp.c 2014-03-08 16:17:51.000000000 -0500 @@ -0,0 +1,2469 @@ +/* file: parsescp.c G. Moody and E. Moody 10 January 2000 + Last revised: 8 March 2014 +------------------------------------------------------------------------------- +parsescp: parse an SCP-ECG file (read from the standard input) +Copyright (C) 2000-2014 George B. Moody and Edna S. 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/). +_______________________________________________________________________________ + +'parsescp' converts SCP-ECG files produced by SpaceLabs/Burdick ECG carts into +more easily usable formats. It was written in 2000 and has been used to convert +about a million ECGs collected at Boston's Beth Israel Deaconess Medical Center +since then. + +'parsescp' can also be used to create deidentified SCP-ECG files, although +it does not perform this function by default; see the comments below. + +Compiling and installing 'parsescp' +=================================== + +Prerequisites: + * gcc (the free GNU C Compiler, available for all popular platforms; + other ANSI/ISO C compilers should also work but you are on your own) + * parsescp.c + * (optional) the WFDB library (included in the free WFDB software package, + from http://physionet.org/physiotools/wfdb.shtml) + +To compile 'parsescp' using the GNU C compiler (gcc), use one of these two +methods: + (1) If you have installed the WFDB Software Package, use this command: + gcc -o parsescp -O parsescp.c -lwfdb + (2) Otherwise, use this command: + gcc -o parsescp -DNOWFDB -O parsescp.c + +To install 'parsescp', compile it first, then copy the executable 'parsescp' +generated by gcc into a directory in your PATH. + +Using 'parsescp' to convert an SCP-ECG file +=========================================== + +Run 'parsescp' within a directory containing the SCP-ECG file to be converted, +which can have any name. For example, if the file is named 'RECORD.scp', use +a command such as: + parsescp -o RECORD anonymous.scp +In this case, none of the other output files are produced. + +parsescp removes all of the PHI as well as names of physicians and technicians, +names of hospitals or clinics, and room numbers, replacing them with 'xxx'. +It changes all dates to January 1, and if the age is over 90, it resets the +age to 90 and the birth year to 90 years before the recording year. Finally, +it recalculates the SCP-ECG CRCs so that the output is still a valid +SCP-ECG file. Note that the original input file is not modified. + +Note that parsescp does not deidentify other types of data (including its +own .des and .key files); it can only deidentify SCP-ECG files. +_______________________________________________________________________________ + +21 Sep 2000 -- added referring dr to .des file esm +24 Jan 2001 -- added comments to .des files esm +25 Jan 2001 -- added -s, -b, -t, -x debugging options gbm + 9 Dec 2002 -- added GPL notice gbm +28 Oct 2004 -- added -w option to create WFDB record gbm + 4 Nov 2004 -- added -a option to create an anonymized copy of the input gbm +19 Jun 2007 -- added -S debugging option for parsing defective inputs gbm +21 Nov 2008 -- added -z option to zero-mean the signals gbm +11 Dec 2008 -- added Institution # and Department # esm +19 May 2009 -- added ncompare to zflag esm + 4 May 2010 -- added check for missing comments in section 1 gbm +24 Jun 2010 -- added checks for missing patid and refdr in section 1 gbm +21 Feb 2011 -- added age (if under 90) and sex to .hea file gbm + 8 Mar 2014 -- corrected comments above gbm +*/ + +#include + +#ifndef NOWFDB +#include +#endif + +#define VERSION_STRING "#ECG/1.0" +#define min(A, B) (((A) <= (B)) ? (A) : (B)) + +/* Define SWAP if this program is compiled for a little-endian CPU (e.g., HP) + but don't do so on a big-endian CPU (e.g., Intel x86) */ +#ifndef SWAP +#define fwriteswapped fwrite +#else +size_t fwriteswapped(void *p, size_t size, size_t nmemb, FILE *stream) +{ + char *q; + size_t n = nmemb*size; + + if ((q = (char *)malloc(n)) == NULL) { + fprintf(stderr, + "insufficient memory (%d bytes needed) in fwriteswapped\n", + n); + return (0); + } + swab(p, q, n); /* Copy n bytes from *p to *q, swapping adjacent bytes + swab() is standard in System V and BSD Unix but is + not in the ANSI C library, so you may need to provide + your own implementation if this program is ported to + a non-Unix environment. */ + n = fwrite(q, size, nmemb, stream); + free(q); + return (n); +} +#endif + +unsigned short get16(char *p) +{ + unsigned short result; + + result = *p++ & 0xff; + result |= *p << 8; + return (result); +} + +void put16(unsigned short val, char *p) +{ + *p++ = val & 0xff; + *p = val >> 8; +} + +unsigned long get32(char *p) +{ + unsigned long result; + + result = *p++ & 0xff; + result |= (*p++ & 0xff) << 8; + result |= (*p++ & 0xff) << 16; + result |= *p << 24; + return (result); +} + +int bflag = 0; /* 0: normal operation, 1: show baselines only */ +int fflag = 0; /* 0: quit on CRC error, 1: continue if possible */ +int tflag = 0; /* 0: normal operation, 1: show templates only */ +int vflag = 0; /* 0: run quietly, 1: print (lots of) messages */ +int xflag = 0; /* 0: run quietly, 1: print hexdump */ + +int shift = 0; /* 0: normal operation; otherwise, shift templates + by this many samples before adding to residuals */ + +unsigned short getcrc(char *p, long length) +{ + unsigned char a, b, crchigh = 0xff, crclow = 0xff; + unsigned short crc; + + while (length-- > 0L) { + crchigh = a = *p++ ^ crchigh; + a >>= 4; /* top 4 bits of a are now zero */ + a ^= crchigh; + + /* swap crchigh, crclow */ + crchigh = crclow; + crclow = a; + + /* rotate a left by four bits (swap nibbles) */ + b = (a & 0xf) << 4; + a >>= 4; + b = a |= b; + + /* rotate a left by one more bit */ + if (a & 0x80) a = (a << 1) | 1; + else a <<= 1; + + crchigh ^= a &= 0x1f; + a = b & 0xf0; + crchigh ^= a; /* crchigh complete */ + + /* rotate b left by one bit */ + if (b & 0x80) b = (b << 1) | 1; + else b <<= 1; + + crclow ^= b &= 0xe0; /* crclow complete */ + } + crc = (crchigh << 8) | crclow; + return (crc); +} + +int crcok(char *p, long length, unsigned short crcref, char *description) +{ + unsigned short crc; + + crc = getcrc(p, length); + + if (crc == crcref) { + if (vflag) printf("%s: correct (%d)\n", description, crc); + return (1); + } + else { + if (vflag) printf("%s: error (calculated: %d, expected: %d)\n", + description, crc, crcref); + if (fflag) + return (0); + else + exit(1); /* quit on CRC error unless in -f (force) mode */ + } +} + +char *pname; /* the name of this program (for use in error messages) */ +int nleads = -1; /* number of ECG leads */ +int nqrs = -1; /* number of beats detected */ +unsigned long *nsamp; /* nsamp[i]: number of samples in lead i */ +long rblen; /* number of samples in refbeat (template) */ +long rblenms; /* length of refbeat in milliseconds */ +int fcM; /* index of fiducial within refbeat */ +double gref, gres; /* gains for refbeat and for residuals (nanovolt/unit) */ +double sfreq = -1.; /* sampling frequency (samples/second/lead) */ +char recdate[20]; /* date of recording */ +char rectime[20]; /* time of recording */ +char recname[12]; /* record name */ +char patient_name[100]; /* patient's name (LAST, FIRST) */ +char referring_dr[100]; /* referring dr */ +char comments[200]; /* comments */ +char patient_id[30]; /* patient ID (medical record number) */ +int age = -1; /* patient's age */ +int sex = -1; /* 1: male, 2: female, 0: unknown, 9: unspecified */ +int RRint = -1; /* RR interval */ +int Pon = -1; /* P onset */ +int Pend = -1; /* P end */ +int QRSon = -1; /* QRS onset */ +int QRSend = -1; /* QRS end */ +int Tend = -1; /* T end */ +short Paxis = 999; /* P axis */ +short QRSaxis = 999; /* QRS axis */ +short Taxis = 999; /* T axis */ +int blfilt = -1; /* baseline filter -3 dB point */ +int lpfilt = -1; /* low-pass filter -3 dB point */ +int filter_bit = -1; /* see SCP spec */ +int tdevid = -1; /* ID number of acquiring device */ +int tinst = -1; /* institution number */ +int tdept = -1; /* department number */ +char *dept = "";/* location description or code */ +char *inst = "";/* institution description */ +int stmnts = -1; /* number of statements in report */ +char *report[1200]; /* report */ +int lflag = 0; /* if non-zero, this program should low-pass filter + its output */ +int wflag = 0; /* if non-zero, this program should produce a WFDB + format record */ +int zflag = 0; /* if non-zero, remove final transients and zero-mean + the signals */ +struct subzone { /* reference beat subtraction zone */ + short type; /* beat type (0: dominant) */ + long t0; /* first sample in zone */ + long t1; /* fiducial */ + long t2; /* last sample in zone */ +} *subz; + +short **ecg; /* ecg[i]: reconstructed ECG for ith lead */ +short **refbeat; /* reference beat (template) */ + +/* Indices of the leads within ecg[]; e.g. ecg[aVR][] is the array of samples + of lead aVR, etc. */ +short leadI = -1, leadII = -1, leadIII = -1, aVR = -1, aVF = -1, aVL = -1, + V1 = -1, V2 = -1, V3 = -1, V4 = -1, V5 = -1, V6 = -1; + +struct ECGmeas { /* sets of measurements in section 10 */ + short leadid; /* Lead ID (index into leadname[]) */ + short vlen; /* length of the rest of the data for this lead */ + short Pdur; /* P duration */ + short PRint; /* PR interval */ + short QRSdur; /* QRS duration */ + short QTint; /* QT interval */ + short Qdur; /* Q duration */ + short Rdur; /* R duration */ + short Sdur; /* S duration */ + short Rpdur; /* R' duration */ + short Spdur; /* S' duration */ + short Qamp; /* Q amplitude */ + short Ramp; /* R amplitude */ + short Samp; /* S amplitude */ + short Rpamp; /* R' amplitude */ + short Spamp; /* S' amplitude */ + short Jamp; /* J-point amplitude */ + short Ppamp; /* P+ amplitude */ + short Pnamp; /* P- amplitude */ + short Tpamp; /* T+ amplitude */ + short Tnamp; /* T- amplitude */ + short STslope; /* ST slope */ + short Pmorph; /* P morphology */ + short Tmorph; /* T morphology */ + short isoI; /* isoelectric segment at QRS onset */ + short isoK; /* isoelectric segment at QRS end */ + short tind; /* time of intrinsicoid deflection */ + short qual; /* recording quality */ + short st20; /* ST amplitude at J + 20 ms */ + short st60; /* ST amplitude at J + 60 ms */ + short st80; /* ST amplitude at J + 80 ms */ + short strr16; /* ST amplitude at J + RRmean/16 */ + short strr8; /* ST amplitude at J + RRmean/8 */ +} *ecgmeas; + +/* This low-pass filter is implemented as a 5-tap weighted moving average + FIR filter. The -3 dB point is at roughly 1/10 the sampling frequency; + hence it behaves as a 50 Hz low-pass filter given 500 samples/second + input. + + This doesn't really belong in this program, but it's easily and efficiently + implemented here. It exists so that users who prefer to look at low-pass + filtered output can do so, by using the -l option. Note that the filter + is applied only to the .ecg file, not to the text-format samples. */ + +void lowpass() +{ + int i, j; + short *in, *out; + + for (i = 0; i < nleads; i++) { + if (ecg[i] == NULL) continue; + if ((out = (short *)calloc(nsamp[i], sizeof(short))) == NULL) { + fprintf(stderr, "Insufficient memory for filtering\n"); + break; + } + in = ecg[i]; + out[0] = in[0] * 0.67 + in[1] * 0.22 + in[2] * 0.11; + out[1] = in[0] * 0.33 + in[1] * 0.34 + in[2] * 0.22 + in[3] * 0.11; + for (j = 2; j < nsamp[i] - 2; j++) + out[j] = in[j]*0.34+(in[j-1]+in[j+1])*0.22+(in[j-2]+in[j+2])*0.11; + out[j] = in[j-2]*0.11 + in[j-1]*0.22 + in[j]*0.34 + in[j+1]*0.33; + j++; + out[j] = in[j-2]*0.11 + in[j-1]*0.22 + in[j]*0.67; + ecg[i] = out; + free(in); + } +} + +/* ncompare is used by qsort() (in the calculation of the signal medians). + It returns the difference between the values pointed to by its arguments + (two samples of a signal). */ +int ncompare(const void *a, const void *b) +{ + return (*(const short *)a - *(const short *)b); +} + +int write_output() +{ + char dname[15]; /* name of .des file */ + char ename[15]; /* name of .ecg file */ + char kname[15]; /* name of .key file */ + char tname[15]; /* name of .txt file */ + FILE *dfile; /* (text) description file: measurements, diagnoses, etc. */ + FILE *efile; /* (binary) ECG signal file */ + FILE *kfile; /* (text) key file: patient name, medical record number */ + FILE *tfile; /* (optional, text) ECG signal file (for debugging) */ + int i; + static char ecgprolog[512]; /* prolog for .ecg file */ + void precgmeas(); + + if (recname[0] == '\0') strcpy(recname, "ecg"); /* default record name */ + sprintf(dname, "%s.des", recname); + sprintf(ename, "%s.ecg", recname); + sprintf(kname, "%s.key", recname); + if (vflag) sprintf(tname, "%s.txt", recname); + if ((dfile = fopen(dname, "wt")) == NULL || + (efile = fopen(ename, "wb")) == NULL || + (kfile = fopen(kname, "wt")) == NULL || + (vflag && ((tfile = fopen(tname, "wt")) == NULL))) { + if (dfile) fclose(dfile); + else { + fprintf(stderr, "%s: can't create '%s'\n", pname, dname); + return (0); + } + if (efile) fclose(efile); + else { + fprintf(stderr, "%s: can't create '%s'\n", pname, ename); + return (0); + } + if (kfile) fclose(kfile); + else { + fprintf(stderr, "%s: can't create '%s'\n", pname, kname); + return (0); + } + fprintf(stderr, "%s: can't create '%s'\n", pname, tname); + return (0); + } + + /* Write the description file first. */ + fprintf(dfile, VERSION_STRING " description\n"); + fprintf(dfile, "Record: %s (%s %s)\n", recname, recdate, rectime); + fprintf(dfile, "Age: %d\n", age); + fprintf(dfile, "Sex: "); + switch (sex) { + case 1: fprintf(dfile, "M\n"); break; + case 2: fprintf(dfile, "F\n"); break; + case 0: fprintf(dfile, "not known\n"); break; + case 9: fprintf(dfile, "unspecified\n"); break; + default: fprintf(dfile, "\n"); break; /* shouldn't happen */ + } + fprintf(dfile, "Referring Dr: %s\n", referring_dr); + fprintf(dfile, "Comments: %s\n", comments); + if (tdevid == 0) + fprintf(dfile, "Cart ID: \n"); + else + fprintf(dfile, "Cart ID: %d\n", tdevid); + fprintf(dfile, "Department: %s\n", dept); + if (tdept == 0) + fprintf(dfile, "Department #: \n"); + else + fprintf(dfile, "Department #: %s\n", dept); + fprintf(dfile, "Institution: %s\n", inst); + if (tinst == 0) + fprintf(dfile, "Institution #: \n"); + else + fprintf(dfile, "Institution #: %d\n", tinst); + + for (i=0; i < stmnts; i++) + fprintf(dfile, "Report %d: %s\n", i, report[i]); + + /* FIXME -- according to the SCP spec, units for blfilt should be + .01 Hz, not .0001 Hz; but files from Spacelabs carts have blfilt + equal to 500 and clearly are diagnostic (0.05-100 Hz) bandwidth. */ + fprintf(dfile, "Bandwidth: %g-%d Hz\n", blfilt*0.0001, lpfilt); + fprintf(dfile, "Filtering:"); + if (filter_bit & 1) fprintf(dfile, " 60 Hz notch"); + if (filter_bit & 2) fprintf(dfile, " 50 Hz notch"); + if (filter_bit & 4) fprintf(dfile, " Artifact filter"); + if (filter_bit & 8) fprintf(dfile, " Baseline filter"); + if (filter_bit & 0xf0) fprintf(dfile, " "); + if (filter_bit == 0) fprintf(dfile, " "); + fprintf(dfile, "\n\n"); + + fprintf(dfile, "RR interval: %d\n", RRint); + fprintf(dfile, "P onset: %d\n", Pon); + fprintf(dfile, "P end: %d\n", Pend); + fprintf(dfile, "QRS onset: %d\n", QRSon); + fprintf(dfile, "QRS end: %d\n", QRSend); + fprintf(dfile, "T end: %d\n", Tend); + fprintf(dfile, "P axis: %d\n", Paxis); + fprintf(dfile, "QRS axis: %d\n", QRSaxis); + fprintf(dfile, "T axis: %d\n", Taxis); + + precgmeas(dfile); + fclose(dfile); + + /* Generate the key file. The patient's name and ID number should + appear only in this file (and in the debugging output, if + enabled using the -v option). */ + fprintf(kfile, VERSION_STRING " key\n"); + fprintf(kfile, "Record: %s (%s %s)\n", recname, recdate, rectime); + fprintf(kfile, "Name: %s\n", patient_name); + fprintf(kfile, "MRN: %s\n", patient_id); + fclose(kfile); + + /* If the -z option was selected, then for each signal: + 1. Calculate the median amplitude of the first 5000 samples in the + signal + 2. If its final sample is zero, replace that sample with the previous + one. This step eliminates transients that occur occasionally. + 3. Subtract the calculated median amplitude from all of the samples. + This step is meant to make it as likely as possible that the signal + will be visible and neatly centered on a chart. + + If the result after subtracting the median falls outside of the + +/- 10 millivolt range (+/- 2000 A/D units), it is assumed to be + invalid, and the sample is stored with the (unique) value + WFDB_INVALID_SAMPLE (-32768). Software designed to interpret or + display the data should avoid displaying samples with this value, + or using them in calculations; pschart (used by printchart) obeys + this rule. + */ + + if (zflag) { + int i, imax, s; + static short buf[5000], m, v; + + for (s = 0; s < nleads; s++) { + imax = (nsamp[s] > 5000) ? 5000 : nsamp[s]; + for (i = 0; i < imax; i++) + buf[i] = ecg[s][i]; + qsort(buf, imax, sizeof(short), ncompare); + m = buf[i/2]; + if (ecg[s][nsamp[s]-1] == 0) + ecg[s][nsamp[s]-1] = ecg[s][nsamp[s]-2]; + for (i = 0; i < nsamp[0]; i++) { + v = ecg[s][i] -= m; + if (v < -2000 || v > 2000) + ecg[s][i] = WFDB_INVALID_SAMPLE; + } + } + } + + /* Write the WFDB-format record if requested (-w option). */ + if (wflag) + write_wfdb_record(); + + /* Write the ECG in text form if requested (-v option). */ + if (vflag) { + int i; + + if (vflag > 1) + fprintf(tfile, + "samp# I II III aVR aVF aVL" + " V1 V2 V3 V4 V5 V6"); + for (i = 0; i < nsamp[0]; i++) { + fprintf(tfile, "%5d", i); + fprintf(tfile, "%7d", ecg[ leadI][i]); + fprintf(tfile, "%7d", ecg[ leadII][i]); + fprintf(tfile, "%7d", ecg[leadIII][i]); + fprintf(tfile, "%7d", ecg[ aVR][i]); + fprintf(tfile, "%7d", ecg[ aVF][i]); + fprintf(tfile, "%7d", ecg[ aVL][i]); + fprintf(tfile, "%7d", ecg[ V1][i]); + fprintf(tfile, "%7d", ecg[ V2][i]); + fprintf(tfile, "%7d", ecg[ V3][i]); + fprintf(tfile, "%7d", ecg[ V4][i]); + fprintf(tfile, "%7d", ecg[ V5][i]); + fprintf(tfile, "%7d", ecg[ V6][i]); + fprintf(tfile, "\n"); + } + fclose(tfile); + } + + /* Finally, output the ECG in vector (binary) form. + ** FIXME ** This assumes that the sampling frequency is 500 Hz, that + nsamp[leadII] >= 5000, and that nsamp[all other leads] >= 1250 */ + if (lflag == 0) { + sprintf(ecgprolog, VERSION_STRING " signals\n" + "Record: %s (%s %s)\n", recname, recdate, rectime); + } + else { + sprintf(ecgprolog, VERSION_STRING " signals\n" + "Record: %s (%s %s)\nLow-pass filtered\n", + recname, recdate, rectime); + lowpass(); + } + fwrite(ecgprolog, 1, sizeof(ecgprolog), efile); + fwriteswapped(ecg[ leadI] , 2, 1250, efile); + fwriteswapped(ecg[ aVR]+1250, 2, 1250, efile); + fwriteswapped(ecg[ V1]+2500, 2, 1250, efile); + fwriteswapped(ecg[ V4]+3750, 2, 1250, efile); + fwriteswapped(ecg[ leadII] , 2, 1250, efile); + fwriteswapped(ecg[ aVL]+1250, 2, 1250, efile); + fwriteswapped(ecg[ V2]+2500, 2, 1250, efile); + fwriteswapped(ecg[ V5]+3750, 2, 1250, efile); + fwriteswapped(ecg[leadIII] , 2, 1250, efile); + fwriteswapped(ecg[ aVF]+1250, 2, 1250, efile); + fwriteswapped(ecg[ V3]+2500, 2, 1250, efile); + fwriteswapped(ecg[ V6]+3750, 2, 1250, efile); + fwriteswapped(ecg[ leadII] , 2, 5000, efile); + fclose(efile); + + return (1); +} + + +int write_wfdb_record() +{ +#ifdef NOWFDB + fprintf(stderr, "Warning: this version of %s was not compiled with" + " WFDB support\n", pname); + return (0); +#else + char *filename = malloc(strlen(recname) + 5), ptime[42]; + int i; + static char *leadname[12] = { "I", "II", "III", "aVR", "aVF", "aVL", + "V1", "V2", "V3", "V4", "V5", "V6" }; + static char info[20]; + static WFDB_Sample v[12]; + static WFDB_Siginfo s[12]; + + if (filename == NULL) { + fprintf(stderr, "insufficient memory in write_wfdb_record\n"); + return (0); + } + sprintf(filename, "%s.dat", recname); + + if (newheader(recname) < 0) + return(0); + + setsampfreq(500.); /* FIXME: we should calculate this! */ + for (i = 0; i < 12; i++) { + s[i].fname = filename; + s[i].desc = leadname[i]; + s[i].units = "mV"; + s[i].fmt = 16; + s[i].gain = 200; /* FIXME: we should calculate this! */ + s[i].adcres = 16; /* FIXME: we should calculate this! */ + } + if (osigfopen(s, 12) < 12) + return (0); + sprintf(ptime, "%s %s\n", recdate, rectime); + setbasetime(ptime); + + /* write the ECG samples */ + for (i = 0; i < nsamp[0]; i++) { + v[ 0] = ecg[ leadI][i]; + v[ 1] = ecg[ leadII][i]; + v[ 2] = ecg[leadIII][i]; + v[ 3] = ecg[ aVR][i]; + v[ 4] = ecg[ aVF][i]; + v[ 5] = ecg[ aVL][i]; + v[ 6] = ecg[ V1][i]; + v[ 7] = ecg[ V2][i]; + v[ 8] = ecg[ V3][i]; + v[ 9] = ecg[ V4][i]; + v[10] = ecg[ V5][i]; + v[11] = ecg[ V6][i]; + if (putvec(v) < 0) break; + } + (void)newheader(recname); + sprintf(info, ": %d : %c", (age < 90) ? age : 90, + ((sex == 1) ? 'M' : (sex == 2) ? 'F' : '?')); + putinfo(info); + wfdbquit(); + return (1); +#endif +} + +void help() +{ + fprintf(stderr, "usage: %s -o RECORD [OPTIONS ...] 0) + bytesread += i = fread(data+bytesread, 1, length-bytesread, stdin); + if (bytesread < length) { + fprintf(stderr, "%s: input too short (%d byte%s missing)\n", + pname, length-bytesread, (length-bytesread == 1) ? "" : "s"); + exit(3); + } + if (vflag) printf("Record length: %ld bytes\n", length); + crcok(data+2, length-2, crc, "Top-level CRC"); + + /* Now check the sections. */ + p = data + 6; /* skip the record header -- p points to section 0 */ + + while (p < data + length) { /* true if there is at least 1 more section */ + crc = get16(p); + sec_id = get16(p+2); + sec_len = get32(p+4); + if (vflag) printf("\nSection %d length: %ld bytes\n", sec_id, sec_len); + if (sec_len < 8) { + if (vflag) printf( + " Warning: section length too short (must be at least 8 bytes)\n" + " Remaining data (%d bytes) following short section will not be read\n", + data+length-(p+8)); + sec_len = 8; + // break; /* don't attempt to read any further */ + } + if (sec_len > data + length - p) { + if (vflag) printf( + " Warning: section length exceeds amount of remaining data\n" + " This section will not be parsed\n"); + break; + } + sprintf(desc, " Section %d CRC", sec_id); + crcok(p+2, sec_len-2, crc, desc); + if (vflag) printf(" Section version number: %d\n", p[8]); + if (vflag) printf(" Protocol version number: %d\n", p[9]); + if (vflag) printf(" Contents: "); + switch (sec_id) { + case 0: if (vflag) printf(" Pointers to data areas in the record\n"); + if (skip[0]) printf(" [skipped]\n"); + else section0(p, sec_len); + break; + case 1: if (vflag) printf(" Header information - Patient data/ECG acquisition" + " data\n"); + if (skip[1]) printf(" [skipped]\n"); + else section1(p, sec_len); + break; + case 2: if (vflag) printf(" Huffman tables used in encoding of ECG data\n"); + if (skip[2]) printf(" [skipped]\n"); + else section2(p, sec_len); + break; + case 3: if (vflag) printf(" ECG lead definition\n"); + if (skip[3]) printf(" [skipped]\n"); + else section3(p, sec_len); + break; + case 4: if (vflag) printf(" QRS locations\n"); + if (skip[4]) printf(" [skipped]\n"); + else section4(p, sec_len); + break; + case 5: if (vflag) printf(" Encoded reference beat data\n"); + if (skip[5]) printf(" [skipped]\n"); + else section5(p, sec_len); + break; + case 6: if (vflag) printf(" Residual signal after reference beat subtraction (if" + " reference beats\n" + " are stored); otherwise, encoded rhythm data\n"); + if (skip[6]) printf(" [skipped]\n"); + else section6(p, sec_len); + break; + case 7: if (vflag) printf(" Global measurements\n"); + if (skip[7]) printf(" [skipped]\n"); + else section7(p, sec_len); + break; + case 8: if (vflag) printf(" Textual diagnosis from the interpretive device\n"); + if (skip[8]) printf(" [skipped]\n"); + else section8(p, sec_len); + break; + case 9: if (vflag) printf(" Manufacturer specific diagnostic and overreading" + " data\n"); + if (skip[9]) printf(" [skipped]\n"); + else section9(p, sec_len); + break; + case 10:if (vflag) printf(" Lead measurement results\n"); + if (skip[10]) printf(" [skipped]\n"); + else section10(p, sec_len); + break; + case 11:if (vflag) printf(" Universal statement codes resulting from the" + " interpretation\n"); + if (skip[11]) printf(" [skipped]\n"); + else section11(p, sec_len); + break; + default:if (vflag) printf(" Manufacturer-defined (non-standard) section %d\n", sec_id); + break; + } + p += sec_len; + } + + /* Low-pass filter the residuals. */ + for (i = 0; i < nleads; i++) { + short *out, *in; + int v; + + if (ecg[i] == NULL) continue; + if ((out = (short *)calloc(nsamp[i], sizeof(short))) == NULL) { + fprintf(stderr, "Insufficient memory for filtering\n"); + break; + } + in = ecg[i]; + if (tflag == 0) { /* true unless -t debugging option was given */ + out[0] = in[0]; + for (j = 1; j < nsamp[i] - 1; j++) { + v = (in[j-1] + in[j] + in[j+1]); + if (v > 0) out[j] = (v + 2)/3; + else if (v < 0) out[j] = (v - 2)/3; + else out[j] = 0; + } + out[j] = in[j]; + for (k = 0; k < nqrs; k++) + if (subz[k].type == 0) + for (j = subz[k].t0 - 1; j <= subz[k].t2 + 1; j++) + out[j] = in[j]; /* restore unfiltered samples within + protected zones and adjacent samples + on either end of protected zones */ + } + ecg[i] = out; + free(in); + } + + /* Add the reference beats back into the residuals to obtain the raw ECG */ + if (bflag == 0) { /* true unless -b debugging option was given */ + for (k = 0; k < nqrs; k++) + for (i = 0; subz[k].type == 0 && i < nleads; i++) { + if (subz[k].t2 - subz[k].t0 + 1 > rblen || + (m = fcM - subz[k].t1 + subz[k].t0 - 1) < 0) { + fprintf(stderr, + "error: subtraction zone is larger than template\n"); + exit(1); + } + for (j = subz[k].t0-1+shift; j <= subz[k].t2-1+shift; j++, m++) + ecg[i][j] += refbeat[i][m]; + } + } + + /* Now calculate the missing leads. Normally the input contains only two + of the three limb leads (I, II, and III) and the missing one (usually + III) must be calculated from the other two, using the relationship: + lead III = lead II - lead I */ + if (leadIII < 0) { /* we need to calculate lead III */ + if (leadI >= 0 && leadII >= 0) { + if (vflag) printf(" Calculating lead III\n"); + leadIII = nleads++; + nsamp[leadIII] = min(nsamp[leadI], nsamp[leadII]); + for (j = 0; j < nsamp[leadIII]; j++) + ecg[leadIII][j] = ecg[leadII][j] - ecg[leadI][j]; + } + else if (leadI < 0) { + if (leadII < 0) + fprintf(stderr, "Leads I, II, and III are missing\n"); + else + fprintf(stderr, "Leads I and III are missing\n"); + } + else + fprintf(stderr, "Leads II and III are missing\n"); + } + else if (leadII < 0) { /* we need to calculate lead II */ + if (leadI >= 0) { + if (vflag) printf(" Calculating lead II\n"); + leadII = nleads++; + nsamp[leadII] = min(nsamp[leadI], nsamp[leadIII]); + for (j = 0; j < nsamp[0]; j++) + ecg[leadII][j] = ecg[leadI][j] + ecg[leadIII][j]; + } + else + fprintf(stderr, "Leads I and II are missing\n"); + } + else if (leadI < 0) { /* we need to calculate lead I */ + if (vflag) printf(" Calculating lead I\n"); + leadI = nleads++; + nsamp[leadI] = min(nsamp[leadII], nsamp[leadIII]); + for (j = 0; j < nsamp[0]; j++) + ecg[leadI][j] = ecg[leadII][j] - ecg[leadIII][j]; + } + + /* We need both lead I and lead II to derive aVR. */ + if (aVR < 0) { + if (leadI >= 0 && leadII >= 0) { + if (vflag) printf(" Calculating lead aVR\n"); + aVR = nleads++; + nsamp[aVR] = min(nsamp[leadI], nsamp[leadII]); + for (j = 0; j < nsamp[0]; j++) + ecg[aVR][j] = - (ecg[leadI][j] + ecg[leadII][j])/2; + } + else + fprintf(stderr, "Lead aVR cannot be derived\n"); + } + + /* We need both lead II and lead III to derive aVL. */ + if (aVL < 0) { + if (leadII >= 0 && leadIII >= 0) { + if (vflag) printf(" Calculating lead aVL\n"); + aVL = nleads++; + nsamp[aVL] = min(nsamp[leadII], nsamp[leadIII]); + for (j = 0; j < nsamp[0]; j++) + ecg[aVL][j] = ecg[leadII][j]/2 - ecg[leadIII][j]; + } + else + fprintf(stderr, "Lead aVL cannot be derived\n"); + } + + /* We need both lead I and lead III to derive aVF. */ + if (aVF < 0) { + if (leadI >= 0 && leadII >= 0) { + if (vflag) printf(" Calculating lead aVF\n"); + aVF = nleads++; + nsamp[aVF] = min(nsamp[leadI], nsamp[leadIII]); + for (j = 0; j < nsamp[0]; j++) + ecg[aVF][j] = ecg[leadI][j]/2 + ecg[leadIII][j]; + } + else + fprintf(stderr, "Lead aVF cannot be derived\n"); + } + + if (V1 < 0) fprintf(stderr, "Lead V1 is missing\n"); + if (V2 < 0) fprintf(stderr, "Lead V2 is missing\n"); + if (V3 < 0) fprintf(stderr, "Lead V3 is missing\n"); + if (V4 < 0) fprintf(stderr, "Lead V4 is missing\n"); + if (V5 < 0) fprintf(stderr, "Lead V5 is missing\n"); + if (V6 < 0) fprintf(stderr, "Lead V6 is missing\n"); + + if (nleads > 0 && (leadI < 0 || leadII < 0 || leadIII < 0 || + aVR < 0 || aVF < 0 || aVL < 0 || + V1 < 0 || V2 < 0 || V3 < 0 || + V4 < 0 || V5 < 0 || V6 < 0)) { + /* Allocate an array of zero samples to use for the missing lead(s). */ + if (ecg[nleads] == NULL && + (ecg[nleads] = (short *)calloc(nsamp[i], sizeof(short))) == NULL) { + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + if (leadI < 0) leadI = nleads; + if (leadII < 0) leadII = nleads; + if (leadIII < 0) leadIII = nleads; + if (aVR < 0) aVR = nleads; + if (aVF < 0) aVF = nleads; + if (aVL < 0) aVL = nleads; + if (V1 < 0) V1 = nleads; + if (V2 < 0) V2 = nleads; + if (V3 < 0) V3 = nleads; + if (V4 < 0) V4 = nleads; + if (V5 < 0) V5 = nleads; + if (V6 < 0) V6 = nleads; + } + + if (aflag) { + /* Recalculate the top-level CRC. */ + crc = getcrc(data+2, bytesread-2); + put16(crc, data); + fwrite(data, 1, bytesread, stdout); + } + else if (nleads > 0) { + write_output(); + } + + /* Release all allocated memory. */ + if (refbeat) { + for (i = 0; i < nleads && refbeat[i] != NULL; i++) + free(refbeat[i]); + free(refbeat); + } + if (ecg) { + for (i = 0; i <= nleads && ecg[i] != NULL; i++) + free(ecg[i]); + free(ecg); + } + if (nsamp) free(nsamp); + if (subz) free(subz); + if (ecgmeas) free(ecgmeas); + if (data) free(data); + + exit(0); +} + +void hexdump(unsigned char *p, long len) +{ + int i; + + printf("Hex dump of data area:\n\n"); + for (i = 0; i < len; i++) { + if (i % 16 == 0) printf("\n%4d: ", i); + printf(" %2x", p[i]); + } + printf("\n\n"); +} + +int section0(unsigned char *p, long len) +{ + if (strncmp("SCPECG", p+10, 6) != 0) + if (vflag) printf(" Warning: SCPECG identifier is missing in header\n"); + p += 16; len -= 16; /* move to data area */ + if (len < 10) { + if (vflag) printf(" Error: section 0 contains no data\n"); + return (0); + } + + while (len > 0) { + unsigned short sec_id; + unsigned long sec_len, sec_index; + + sec_id = get16(p); + sec_len = get32(p+2); + sec_index = get32(p+6); + if (vflag) printf(" Section %2d:%6ld bytes beginning at byte %6ld\n", + sec_id, sec_len, sec_index); + len -= 10; + p += 10; + } + if (len < 0) { + if (vflag) printf(" Error: section 0 overlaps the next section\n"); + return (0); + } + return (1); +} + +char *month[] = { "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" }; + +void censor(char *p, unsigned short len, char c) +{ + while (--len) + *p++ = c; + *p = '\0'; +} + +int section1(unsigned char *p, long len) +{ + FILE *pfile; + unsigned short vlen; + char format[16], *tpatid = NULL, *trefdr = NULL, *tcomments = NULL, *tdate = NULL, *ttime = NULL, *pyoa = NULL, *pyob = NULL; + char *firstname = NULL, *lastname = NULL; + enum { text, date, time, b1, b2, b2b1, bn, mixed } vtype; + unsigned char *p_save = p; + long len_save = len; + + p += 16; len -= 16; /* move to data area */ + if (len < 3) { + if (vflag) printf(" Error: section 1 contains no data\n"); + return (0); + } + + while (len > 2) { + if (*p == 255) break; /* tag 255 is the terminator */ + vlen = get16(p+1); + if (vlen > len) { + if (vflag) printf(" Error: tagged field (%d bytes) overlaps next section\n"); + return (0); + } + switch (*p) { + case 0: if (vflag) printf("\n Last name\n"); + vtype = text; lastname = p+3; + if (aflag) censor(lastname, vlen, 'x'); + break; + case 1: if (vflag) printf("\n First name\n"); + vtype = text; firstname = p+3; + if (aflag) censor(firstname, vlen, 'x'); + break; + case 2: if (vflag) printf("\n Patient ID number\n"); + vtype = text; tpatid = p+3; + if (aflag) censor(tpatid, vlen, '0'); + break; + case 3: if (vflag) printf("\n Second last name\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 4: if (vflag) printf("\n Age\n"); + vtype = b2b1; age = get16(p+3); /* FIXME */ + if (aflag && age > 90) + put16(90, p+3); /* censor age if over 90 */ + break; + case 5: if (vflag) printf("\n Date of birth\n"); + vtype = date; + if (aflag) { + pyob = p+3; /* save pointer to year of birth */ + put16(257, p+5); /* censor month and day (257 => 1 Jan) */ + } + break; + case 6: if (vflag) printf("\n Height\n"); + vtype = b2b1; break; + case 7: if (vflag) printf("\n Weight\n"); + vtype = b2b1; break; + case 8: if (vflag) printf("\n Sex\n"); + vtype = b1; sex = *(p+3); break; + case 9: if (vflag) printf("\n Race\n"); + vtype = b1; break; + case 10: if (vflag) printf("\n Drug\n"); + vtype = mixed; break; + case 11: if (vflag) printf("\n Systolic blood pressure\n"); + vtype = b2; break; + case 12: if (vflag) printf("\n Diastolic blood pressure\n"); + vtype = b2; break; + case 13: if (vflag) printf("\n Diagnosis or referral indication\n"); + vtype = text; break; + case 14: if (vflag) printf("\n ID of the acquiring device\n"); + tdevid = get16(p+7); + tinst = get16(p+3); /* institution # */ + tdept = get16(p+5); /* department # */ + if (vflag) { + char *q; + + printf(" Institution number: %d\n", get16(p+3)); + printf(" Department number: %d\n", get16(p+5)); + printf(" Device ID: %d\n", get16(p+7)); + printf(" Device type: %d (%s)\n", *(p+9), + (*(p+9) == 0 ? "cart" : + (*(p+9) == 1 ? "system or host" : ""))); + printf(" Manufacturer code: %d\n", *(p+10)); + printf(" Text model description: [%d]", *(p+11)); + printf(" SCP-ECG protocol revision number: %g\n", + (double)(*(p+17))/10.0); + printf(" SCP-ECG protocol compatibility level: %d\n", + *(p+18)); + printf(" Language support code: %d\n", *(p+19)); + printf(" Device capabilities: %d\n", *(p+20)); + printf(" AC mains frequency environment: %d ", *(p+21)); + switch (*(p+21)) { + case 0: printf("(unspecified)\n"); break; + case 1: printf("(50 Hz)\n"); break; + case 2: printf("(60 Hz)\n"); break; + default: printf(""); break; + } + q = p+39; + printf(" Analyzing program revision number: [%s]\n", q); + q += strlen(q) + 1; + printf(" Serial number of acquisition device: [%s]\n", q); + q += strlen(q) + 1; + printf(" Acquisition device system software ID: [%s]\n", q); + q += strlen(q) + 1; + printf(" Acquisition device SCP implementation ID: [%s]\n", + q); + q += strlen(q) + 1; + printf(" Manufacturer of acquisition device: [%s]\n", q); + } + vtype = mixed; break; + case 15: if (vflag) printf("\n ID of the analyzing device\n"); + vtype = mixed; break; + case 16: if (vflag) printf("\n Acquiring institution\n"); + vtype = text; inst=p+3; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 17: if (vflag) printf("\n Analyzing institution\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 18: if (vflag) printf("\n Acquiring department\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + dept = p+3; break; + case 19: if (vflag) printf("\n Analyzing department\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 20: if (vflag) printf("\n Referring physician\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + trefdr = p+3; break; + case 21: if (vflag) printf("\n Latest confirming physician\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 22: if (vflag) printf("\n Technician\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 23: if (vflag) printf("\n Room\n"); + vtype = text; + if (aflag) censor(p+3, vlen, 'x'); + break; + case 24: if (vflag) printf("\n Stat code (urgency)\n"); + vtype = b1; break; + case 25: if (vflag) printf("\n Date of acquisition\n"); tdate = p+3; + if (aflag) { + pyoa = p+3; /* save pointer to year of acquisition */ + put16(257, p+5); /* censor month and day (257 => 1 Jan) */ + } + vtype = date; break; + case 26: if (vflag) printf("\n Time of acquisition\n"); ttime = p+3; + vtype = time; break; + case 27: if (vflag) printf("\n Baseline filter\n"); + vtype = b2; blfilt = get16(p+3); break; + case 28: if (vflag) printf("\n Low-pass filter\n"); + vtype = b2; lpfilt = get16(p+3); break; + case 29: if (vflag) printf("\n Filter bit map\n"); + vtype = b1; filter_bit = *(p+3); break; + case 30: if (vflag) printf("\n Free text field (comments)\n"); tcomments = p+3; + vtype = text; break; + case 31: if (vflag) printf("\n Sequence number\n"); + vtype = text; break; + case 32: if (vflag) printf("\n Medical history codes\n"); + vtype = bn; break; + case 33: if (vflag) printf("\n Electrode configuration code\n"); + vtype = b2; break; + case 34: if (vflag) printf("\n Time zone\n"); + vtype = mixed; break; + case 35: if (vflag) printf("\n Free text medical history\n"); + vtype = text; break; + default: if (vflag) printf("\n \n"); + vtype = mixed; break; + } + if (vflag) printf(" Tag%3d (%2d bytes): ", *p, vlen); + p += 3; len -= vlen+3; + if (vlen == 0) { if (vflag) printf("\n"); } + else switch (vtype) { + case text: + if (vlen > 0) { + sprintf(format, "[%%%ds]", vlen-1); + if (vflag) printf(format, p); + p += vlen; + } + else + if (vflag) printf(""); + break; + case date: + if (vlen != 4) { + if (vflag) printf(" Error: incorrect date format (%d bytes)\n", vlen); + } + else { + if (vflag) printf("%4d/%02d/%02d", get16(p), *(p+2), *(p+3)); + } + p += vlen; + break; + case time: + if (vlen != 3) { + if (vflag) printf(" Error: incorrect time format (%d bytes)\n", vlen); + } + else + if (vflag) printf("%02d:%02d:%02d", *p, *(p+1), *(p+2)); + p += vlen; + break; + case b1: + if (vlen != 1) { + if (vflag) printf(" Error: incorrect format (%d bytes instead of 1)\n", + vlen); + } + else + if (vflag) printf("%d", *p); + p += vlen; + break; + case b2: + if (vlen != 2) { + if (vflag) printf(" Error: incorrect format (%d bytes instead of 2)\n", + vlen); + } + else + if (vflag) printf("%d", get16(p)); + p += vlen; + break; + case b2b1: + if (vlen != 3) { + if (vflag) printf(" Error: incorrect format (%d bytes instead of 3)\n", + vlen); + } + else + if (vflag) printf("%d (code %d)", get16(p), *(p+2)); + p += vlen; + break; + case bn: + while (vlen-- > 0) { + if (vflag) printf("%d, ", *p); + p++; + } + break; + default: + if (vflag) printf("oops! undefined variable type\n"); + case mixed: + if (vflag) { + while (vlen-- > 0) { + if (isprint(*p)) putchar(*p); + else if (vflag) printf("\\%03o", *p); + p++; + } + printf("\n"); + } + else + p += vlen; + } + } + + sprintf(recdate, "%d %s %d",*(tdate+3), month[*(tdate+2)-1], get16(tdate)); + sprintf(rectime, "%02d:%02d:%02d", *ttime, *(ttime+1), *(ttime+2)); + if (lastname == NULL) lastname = ""; + if (firstname == NULL) firstname = ""; + if (strlen(lastname) + strlen(firstname) < sizeof(patient_name) - 3) + sprintf(patient_name, "%s, %s", lastname, firstname); + else { + fprintf(stderr, "Patient name (%s, %s) is too long\n", lastname, + firstname); + strcpy(patient_name, "Name not available -- too long"); + } + if (tpatid == NULL) tpatid = ""; + strncpy(patient_id, tpatid, sizeof(patient_id)); + if (trefdr == NULL) trefdr = ""; + strncpy(referring_dr, trefdr, sizeof(referring_dr)); + if (tcomments == NULL) tcomments = ""; + strncpy(comments, tcomments, sizeof(comments)); + + if (*p != 255) + if (vflag) printf(" Error: header terminator (tag 255) is missing\n"); + if (tpatid == NULL) + if (vflag) printf(" Error: patient ID number (tag 2) is missing\n"); + if (tdevid == -1 || tinst == -1 || tdept == -1) + if (vflag) printf(" Error: device ID number (tag 14) is missing\n"); + if (tdate == NULL) + if (vflag) printf(" Error: date of acquisition (tag 25) is missing\n"); + if (ttime == NULL) + if (vflag) printf(" Error: time of acquisition (tag 26) is missing\n"); + + if (aflag && pyob != NULL) { + int yoa = 2004, yob; + unsigned short crc; + + if (pyoa) yoa = get16(pyoa); + yob = get16(pyob); + if (yoa - yob > 90) yob = yoa - 90; /* censor year of birth if >90 + years before acquisition */ + + /* Recalculate the CRC for this section. */ + crc = getcrc(p_save+2, len_save-2); + put16(crc, p_save); + } + + if (*p != 255 || tpatid == NULL || tdevid == -1 || + tdate == NULL || ttime == NULL) + return (0); + return (1); +} + +int section2(unsigned char *p, long len) +{ + unsigned short ntables; + + p += 16; len -= 16; /* move to data area */ + if (len < 2) { + if (vflag) printf(" Error: section 2 contains no data\n"); + return (0); + } + + ntables = get16(p); + if (ntables == 19999) { + if (vflag) printf(" Using standard Huffman table only\n"); + } + else { + if (vflag) printf(" Number of Huffman tables defined: %d\n", get16(p)); + if (vflag) printf(" Number of code structures in table 1: %d\n", get16(p+2)); + /* ** more to do here ** */ + } + return (1); +} + +char *leadname[] = { /* standard lead names */ + "", "I", "II", "V1", "V2", /* 0 - 4 */ + "V3", "V4", "V5", "V6", "V7", /* 5 - 9 */ + "V2R", "V3R", "V4R", "V5R", "V6R", /* 10 - 14 */ + "V7R", "X", "Y", "Z", "CC5", /* 15 - 19 */ + "CM5", "Left Arm", "Right Arm", "Left Leg", "I (Frank)", /* 20 - 24 */ + "E", "C", "A", "M", "F", /* 25 - 29 */ + "H", "I-cal", "II-cal", "V1-cal", "V2-cal", /* 30 - 34 */ + "V3-cal", "V4-cal", "V5-cal", "V6-cal", "V7-cal", /* 35 - 39 */ + "V2R-cal", "V3R-cal", "V4R-cal", "V5R-cal", "V6R-cal", /* 40 - 44 */ + "V7R-cal", "X-cal", "Y-cal", "Z-cal", "CC5-cal", /* 45 - 49 */ + "CM5-cal", "Left Arm-cal", "Right Arm-cal", "Left Leg-cal","I-cal (Frank)", + /* 50 - 54 */ + "E-cal", "C-cal", "A-cal", "M-cal", "F-cal", /* 55 - 59 */ + "H-cal", "III", "aVR", "aVL", "aVF", /* 60 - 64 */ + "-aVR", "V8", "V9", "V8R", "V9R", /* 65 - 69 */ + "D (Nehb - Dorsal)", "A (Nehb - Anterior)", "J (Nehb - Inferior", + "Defibrillator lead: anterior-lateral", + "External pacing lead: anterior-posterior", /* 70 - 74 */ + "A1 (Auxiliary unipolar lead 1)", "A2 (Auxiliary unipolar lead 2)", + "A3 (Auxiliary unipolar lead 3)", "A4 (Auxiliary unipolar lead 4)", + "V8-cal", /* 75 - 79 */ + "V9-cal", "V8R-cal", "V9R-cal", "D-cal (Nehb - Dorsal)", + "A-cal (Nehb - Anterior)", /* 80 - 84 */ + "J-cal (Nehb - Inferior)" /* 85 */ +}; + +int section3(unsigned char *p, long len) +{ + int i = 0, nsmax = 0; + + p += 16; len -= 16; /* move to data area */ + + if (len < 2) { + if (vflag) printf(" Error: section3 contains no data\n"); + return (0); + } + + nleads = *p++; + if (vflag) printf(" Number of leads: %d\n", nleads); + len--; + + if ((nsamp=(unsigned long *)malloc((nleads+4)*sizeof(unsigned long))) == + NULL){ + if (vflag) printf(" Error: too many (%ld) leads\n", nleads); + return (0); + } + + if (vflag) printf(" Flag byte: %xH\n", *p); len--; + if (*p & 1) { + if (vflag) + printf(" Reference beat subtraction used for compression\n"); + } + else + if (vflag) + printf(" Reference beat subtraction not used for compression\n"); + if (*p & 4) { + if (vflag) printf(" All leads recorded simultaneously\n"); + } + else + if (vflag) printf(" %d leads recorded simultaneously\n", (*p >>3) & 0xf); + p++; + + while (len > 8) { + int j = *(p+8); + if (j < sizeof(leadname)/sizeof(char *)) { + if (vflag) printf(" %s", leadname[j]); + /* yes, there are more efficient ways to do this -- but this one + won't break */ + if (strcmp( "I", leadname[j]) == 0) leadI = i; + else if (strcmp( "II", leadname[j]) == 0) leadII = i; + else if (strcmp("III", leadname[j]) == 0) leadIII = i; + else if (strcmp("aVR", leadname[j]) == 0) aVR = i; + else if (strcmp("aVF", leadname[j]) == 0) aVF = i; + else if (strcmp("aVL", leadname[j]) == 0) aVL = i; + else if (strcmp( "V1", leadname[j]) == 0) V1 = i; + else if (strcmp( "V2", leadname[j]) == 0) V2 = i; + else if (strcmp( "V3", leadname[j]) == 0) V3 = i; + else if (strcmp( "V4", leadname[j]) == 0) V4 = i; + else if (strcmp( "V5", leadname[j]) == 0) V5 = i; + else if (strcmp( "V6", leadname[j]) == 0) V6 = i; + } + else if (*(p+8) < 100) { + if (vflag) printf(" "); + } + else + if (vflag) printf(" "); + if (vflag) printf(": ID %d, samples %d to %d\n", *(p+8), + get32(p), get32(p+4)); + nsamp[i++] = get32(p+4) - get32(p) + 1; + p += 9; len -= 9; + } + + /* allocate space for 4 extra signals that will be calculated later, + and for a NULL pointer to signal the end of the array */ + if ((ecg = (short **)malloc((nleads+5)*sizeof(short *))) == NULL) { + if (vflag) printf(" Error: too many (%ld) leads\n", nleads); + return (0); + } + + for (i = 0; i < nleads+4; i++) { + if (i >= nleads) nsamp[i] = nsmax; + else if (nsamp[i] > nsmax) nsmax = nsamp[i]; + if (nsamp[i] < 1) nsamp[i] = 1; + if ((ecg[i] = (short *)calloc(nsamp[i], sizeof(short))) == NULL) { + if (vflag) printf(" Error: too many (%ld) leads\n", nleads); + return (0); + } + } + ecg[nleads+4] = NULL; + + if (len < 0) return (0); + return (1); +} + +int section4(unsigned char *p, long len) +{ + unsigned short i, stat = 1; + + p += 16; len -= 16; /* move to data area */ + if (len < 6) { + if (vflag) printf(" Error: section 4 contains no data\n"); + return (0); + } + rblenms = get16(p); + fcM = get16(p+2); + nqrs = get16(p+4); + if (vflag) { + printf(" Length of reference beat type 0 data: %d msec\n", rblenms); + printf( + " Sample # of fiducial relative to start of ref beat type 0: %d\n", + fcM); + printf(" Number of QRS complexes in the entire record: %d\n", nqrs); + } + + if (nqrs < 1) + return (0); /* no beats -- nothing else to do here */ + + p += 6; len -= 6; + if ((subz=(struct subzone *)malloc(sizeof(struct subzone)*nqrs)) == NULL) { + fprintf(stderr, " Error: too many (%d) beats\n", nqrs); + return (0); + } + + if (len >= nqrs*14) { + if (vflag) printf(" Reference beat subtraction zones:\n"); + for (i = 0; i < nqrs && len >= 14; i++) { + subz[i].type = get16(p); + subz[i].t0 = get32(p+2); + subz[i].t1 = get32(p+6); + subz[i].t2 = get32(p+10); + if ((subz[i].type == 0) && (subz[i].t2 == 0)) + subz[i].type = 9; + + if (vflag) { + printf( + " %2d Type: %2d start: %7d fiducial: %7d end: %7d\n", + i+1, subz[i].type, subz[i].t0, subz[i].t1, subz[i].t2); + if (subz[i].type != 0 && (subz[i].t0 != 0 || + subz[i].t2 != 0)) + printf(" Error: start and end should be zero for beats " + "of non-zero types\n"); + } + p += 14; len -= 14; + } + } + else { + if (vflag) + printf(" Error: reference beat subtraction zones are missing\n"); + stat = 0; + } + if (len >= nqrs*8) { + if (vflag) printf(" QRS locations:\n"); + for (i = 0; i < nqrs; i++) { + if (vflag) printf(" %2d start: %7d end: %7d\n", + i+1, get32(p), get32(p+4)); + p += 8; len -= 8; + } + } + else { + if (vflag) printf(" Error: QRS locations are missing\n"); + stat = 0; + } + return (stat); +} + +int section5(unsigned char *p, long len) +{ + int i; + p += 16; len -= 16; /* move to data area */ + + + if (len <= 0) { + if (vflag) printf(" Error: section 5 contains no data\n"); + return (0); + } + + /* Calculate length of template in samples */ + rblen = rblenms * 1000.0 / get16(p+2); + + if ((refbeat = (short **)calloc((nleads+1), sizeof(short *))) == NULL) { + fprintf(stderr, " Error: too many (%d) leads\n", nleads); + return (0); + } + + for (i = 0; i < nleads; i++) { + if ((refbeat[i] = (short *)calloc(rblen, sizeof(short))) == NULL) { + fprintf(stderr, " Error: too many (%d) leads\n", nleads); + return (0); + } + } + + i = Huffdecode(p, len, 5); + return (i); +} + + +struct htentry { + unsigned short pattern; + unsigned short suffixbits; + short basevalue; +} htdefaultentries[] = { + { 0, 0, 0 }, + { 4, 0, 1 }, + { 5, 0, -1 }, + { 0xc, 0, 2 }, + { 0xd, 0, -2 }, + { 0x1c, 0, 3 }, + { 0x1d, 0, -3 }, + { 0x3c, 0, 4 }, + { 0x3d, 0, -4 }, + { 0x7c, 0, 5 }, + { 0x7d, 0, -5 }, + { 0xfc, 0, 6 }, + { 0xfd, 0, -6 }, + { 0x1fc, 0, 7 }, + { 0x1fd, 0, -7 }, + { 0x3fc, 0, 8 }, + { 0x3fd, 0, -8 }, + { 0x3fe, 8, 0 }, + { 0x3ff, 16, 0 } +}; + +struct htable { + int npatterns; + struct htentry *entry; +} htdefault = { 19, htdefaultentries }; + +struct htable *htab = &htdefault; + +/* This function reads bits from the input buffer, beginning 'ibits' bits + from the MSB of *p, until it recognizes a Huffman-encoded value. The + decoded value is then stored into x, and the function returns the number + of bits it has read. */ +unsigned long getbits(short *x, unsigned char *p, unsigned long ibits) +{ + unsigned short bitbuffer = 0L; + static unsigned char w; + unsigned int nbits = 0; /* number of bits read on this call to getbits */ + short i, j, v; + + if (ibits == 0) { /* first call for this lead -- initialize */ + ; + } + + do { + bitbuffer <<= 1; /* make room for another bit */ + + if ((ibits & 7) == 0) /* once every 8 bits ... */ + w = p[ibits / 8]; /* ... get the next 8 bits */ + /* copy next bit from w into bitbuffer ... */ + if (w & 0x80) bitbuffer |= 1; + /* ... and discard it from w */ + w <<= 1; + + ibits++; + nbits++; + + /* check bitbuffer against all of the Huffman table entries */ + for (i = 0; i < htab->npatterns; i++) { + if (bitbuffer == htab->entry[i].pattern) { + /* we have a match */ + if ((j = htab->entry[i].suffixbits) == 0) + *x = htab->entry[i].basevalue; + else if (j < 0) { + /* table mode switch */ + /* ** not implemented ** */ + ; + } + else { /* j is the number of additional bits we need */ + /* as above, get 8 more bits if needed */ + if ((ibits & 7) == 0) + w = p[ibits / 8]; + /* Get first (most significant) bit of value. This + bit is 0 if the value is positive or zero, and 1 + if the value is negative. If the value is negative, + and the number of additional bits is less than the + number of bits in a 'short', we need to sign-extend + the number by shifting additional ones into it before + reading the rest of the value from the bit stream. */ + if (w & 0x80) { /* if it's a 1, sign-extend it */ + int jj = j; + for (v = 1; jj < 8 * sizeof(short); jj++) { + v <<= 1; /* make room for another bit */ + v |= 1; /* shift in another 1 */ + } + } + else v = 0; + w <<= 1; + ibits++; + nbits++; + for (j--; j > 0; j--, ibits++, nbits++) { + v <<= 1; + if ((ibits & 7) == 0) + w = p[ibits / 8]; + if (w & 0x80) v |= 1; + w <<= 1; + } + *x = v; + } + return (nbits); + } + } + } while (nbits < sizeof(unsigned long) * 8 - 1); + if (vflag) printf("Huffman encoding error\n"); + return (1000000L); +} + +int section6(unsigned char *p, long len) +{ + int i; + + p += 16; len -= 16; /* move to data area */ + + if (len <= 6) { + if (vflag) printf(" Error: section 6 contains no data\n"); + return (0); + } + i=Huffdecode(p, len, 6); + return (i); +} + +int Huffdecode(unsigned char *p, long len, int section) +{ + int i, comptype = 1, enctype = 2, sampint; + unsigned long ibits, tbits, *leadlen, tleadlen = 0L; + double g = get16(p); + + if (section == 5) gref = g / 5000.; + else if (section == 6) gres = g / 5000.; + else { + fprintf(stderr, "oops! shouldn't call Huffdecode with section = %d\n", + section); + exit(2); + } + if (vflag) printf(" Amplitude value multiplier: %g nanovolts per unit\n", + g); + sampint = get16(p+2); + if (vflag) printf(" Sample time interval: %d microseconds\n", sampint); + if (sampint > 0) { + if (sfreq > 0 && sfreq != 1.0e6/sampint) + printf("Warning: section 5 and 6 sample intervals don't match"); + /* FIXME: If this ever happens, we need to redo the reconstruction + code in main() */ + sfreq = 1.0e6/sampint; + } + else { + printf("Error: sampling frequency not specified; assuming 500 Hz\n"); + sfreq = 500; + } + if (vflag) printf(" Encoding type: %d ", *(p+3)); + switch (*(p+3)) { + case 0: if (vflag) printf("(amplitudes)\n"); enctype = 0; break; + case 1: if (vflag) printf("(first differences)\n"); enctype = 1; break; + case 2: if (vflag) printf("(second differences)\n"); enctype = 2; break; + default: if (vflag) printf("\n"); enctype = 2; + break; + } + if (section == 6) { + if (vflag) printf(" Compression type: %d ", *(p+4)); + switch (*(p+4)) { + case 0: if (vflag) printf("(non-bimodal)\n"); comptype = 0; break; + case 1: if (vflag) printf("(bimodal)\n"); comptype = 1; break; + default: if (vflag) printf("\n"); comptype = 1; + break; + } + } + p += 6; len -= 6; + + if (nleads < 0) { + if (vflag) printf(" Error: number of leads undefined -- section 3 missing" + " or out of order\n"); + return (0); + } + if (nleads == 0) { + if (vflag) printf(" Error: no leads recorded\n"); + return (0); + } + if ((leadlen=(unsigned long *)malloc(nleads*sizeof(unsigned long)))==NULL){ + if (vflag) printf(" Error: too many (%ld) leads\n", nleads); + return (0); + } + + for (i = 0; i < nleads; i++, p += 2, len -= 2) { + tleadlen += leadlen[i] = get16(p); + if (vflag) printf(" Lead %d:%5d bytes\n", i, leadlen[i]); + } + + if (len < tleadlen) { + if (vflag) printf(" Error: %d data byte%s missing from section %d\n", + tleadlen - len, + (tleadlen - len == 1) ? "" : "s", + section); + free(leadlen); + return (0); + } + else if (len > ((tleadlen + 1) & ~1)) + /* the expression above rounds tleadlen up to an even number */ + if (vflag) printf(" Warning: %d extra byte%s in section %d\n", + len - tleadlen, + (len - tleadlen == 1) ? "" : "s", + section); + + for (i = 0; i < nleads; i++) { + char fname[6]; + int explen, j; + static short dx, x, xx, xxx, xout; + + if (vflag) printf(" Lead %d data\n", i); + for (j=0; j < 10; j++) + if (vflag) printf(" %02x", p[j]); + if (vflag) printf("\n"); + xxx = xx = 0; + if (section == 6) explen = nsamp[i]; + else explen = rblen; + for (j=0, ibits=0, tbits=leadlen[i]*8; j 1) printf(" %5d %5d %5d %5d\n", j, x, xx, xxx); + } + if (j < explen) + if (vflag) printf(" Error: %ld sample%s missing in lead %d\n", + leadlen[i] - j, + (leadlen[i] - j == 1) ? "" : "s", + i); + if (ibits < tbits) + if (vflag) printf(" Warning: %ld extra bit%s in lead %d\n", + tbits - ibits, + (tbits - ibits == 1) ? "" : "s", + i); + p += leadlen[i]; + if (vflag) printf("\n"); + } + free(leadlen); + return (1); +} + +int section7(unsigned char *p, long len) +{ + unsigned char nmb, nps, sec7qrs, *q, *r; + short i, n, sec7qrs_offset; + + p += 16; len -= 16; /* move to data area */ + + if (len <= 0) { + if (vflag) printf(" Error: section 7 contains no data\n"); + return (0); + } + + if (xflag && vflag) hexdump(p, len); + + if (len < 20) { /* there isn't enough room left for measurements */ + if (vflag) printf(" Error: section 7 is too short (len = %d)\n", len); + return (0); + } + + RRint = get16(p+2); + +/* get P onset, etc from first measurement block */ + q = p + 6; + Pon = get16(q); + Pend = get16(q+2); + QRSon = get16(q+4); + QRSend = get16(q+6); + Tend = get16(q+8); + Paxis = get16(q+10); + QRSaxis = get16(q+12); + Taxis = get16(q+14); + + nmb = *p; + nps = *(p+1); + sec7qrs_offset = 6 + 16*nmb + 10*nps; + if (sec7qrs_offset > len) sec7qrs_offset = len; + + if (sec7qrs_offset > len) { + if (vflag) printf(" Error: section 7 is too short" + " (nmb = %d, nps = %d)\n", nmb, nps); + return (0); + } + + sec7qrs = get16(p + sec7qrs_offset); + + if (vflag) { + printf(" QRS complexes measured: %d\n", sec7qrs); + printf(" Measurement blocks: %d\n", nmb); + if (nmb == sec7qrs + 1) + printf(" (First measurement block contains measurements for" + " reference beat type 0;\n" + " Others contain measurements for each individual beat)\n"); + else + printf(" (Measurements for each reference beat type)\n"); + printf(" Number of measurement blocks: %d\n", nmb); + printf(" Number of pacemaker spikes: %d\n", nps); + printf(" Mean RR interval: %d ms\n", RRint); + printf(" Mean PP interval: %d ms\n", get16(p+4)); + for (i = 0, q = p+6; i < nmb; i++, q += 16) { + printf(" Block %d\n", i); + printf(" P onset: %d ms\n", get16(q)); + printf(" P end: %d ms\n", get16(q+2)); + printf(" QRS onset: %d ms\n", get16(q+4)); + printf(" QRS end: %d ms\n", get16(q+6)); + printf(" T end: %d ms\n", get16(q+8)); + if ((n = get16(q+10)) == 999) printf(" P axis: undefined\n"); + else printf(" P axis: %d degrees\n", n); + if ((n = get16(q+12)) == 999) printf(" QRS axis: undefined\n"); + else printf(" QRS axis: %d degrees\n", n); + if ((n = get16(q+14)) == 999) printf(" T axis: undefined\n"); + else printf(" T axis: %d degrees\n", n); + } + + if (nps > 0) + printf(" Pacemaker spike measurement data\n"); + for (i = 0, r = q + nps*4; i < nps; i++, q += 4, r += 6) { + printf("Spike %d: %u ms, %d microvolts ", + i, (unsigned)get16(q), get16(q+2)); + switch (*r) { + case 0: printf("(unknown spike type, "); break; + case 1: printf("(triggers neither P nor QRS, "); break; + case 2: printf("(triggers a QRS, "); break; + case 3: printf("(triggers a P, "); break; + default: printf("(spike type %d, ", *r); break; + } + switch (*(r+1)) { + case 0: printf("source unknown, "); break; + case 1: printf("internal, "); break; + case 2: printf("external, "); break; + default: printf("source type %d, ", *(r+1)); break; + } + if ((n = get16(r+2)) == 0) + printf(" not linked to a QRS, "); + else + printf(" linked to QRS #%d, ", n); + if ((n = get16(r+4)) == 0) + printf("width unknown)\n"); + else + printf("%d microseconds\n)", n); + } + + + /* The next part doesn't seem to be right ... */ + printf(" sec7qrs = <%d>\n", sec7qrs); + if (sec7qrs > 0) + printf(" QRS type information (%d beats)\n", get16(r)); + for (i = 0, q = r+2; i < sec7qrs; i++, q++) + printf(" QRS %d: reference beat type %d\n", i, *q); +#if 0 + printf(" Additional global measurements\n"); + printf(" Ventricular rate: %d bpm\n", get16(q)); + printf(" Atrial rate: %d bpm\n", get16(q+2)); + printf(" QTc: %d ms ", get16(q+4)); + switch (*(q+6)) { + case 0: printf("(unspecified correction formula)\n"); break; + case 1: printf("(Bazett correction)\n"); break; + case 2: printf("(Hodges correction)\n"); break; + default: printf("(correction type %d)\n", *(q+6)); break; + } +#endif + + + printf("%d bytes remaining in section 7\n", len - (q - p)); + while (q < p + len) { + printf(" %5d", get16(q)); + printf(" [%3d]", *q++); + } printf("\n"); + + } + + /* ** more to do here ** */ + return (1); +} + +int section8(unsigned char *p, long len) +{ + int i, n; + + p += 16; len -= 16; /* move to data area */ + + if (len <= 0) { + if (vflag) printf(" Error: section 8 contains no data\n"); + return (0); + } + + if (vflag) printf(" Status: %d ", *p); + switch (*p) { + case 0: if (vflag) printf("(original report, not confirmed)\n"); break; + case 1: if (vflag) printf("(confirmed report)\n"); break; + case 2: if (vflag) printf("(overread report, but not confirmed)\n"); break; + default: if (vflag) printf("\n"); break; + } + + if (vflag) printf(" Date: %4d/%02d/%02d\n", get16(p+1), *(p+3), *(p+4)); + if (vflag) printf(" Time: %02d:%02d:%02d\n", *(p+5), *(p+6), *(p+7)); + n = *(p+8); + stmnts = n; + if (vflag) printf(" Number of statements: %d\n", n); + p += 9; len -= 9; + + for (i = 0; i < n && len > 2; i++) { + char format[16]; + int slen; + + report[i] = p+3; + slen = get16(p+1); + if (vflag) printf(" Statement %2d: (%d bytes)\n", *p, slen); + sprintf(format, " [%%%ds]\n", slen); + if (vflag) printf(format, p+3); + p += slen+3; len -= slen+3; + } + + if (i < n) { + if (vflag) printf(" Error: data missing in section 8\n"); + return (0); + } + + return (1); +} + +int section9(unsigned char *p, long len) +{ + p += 16; len -= 16; /* move to data area */ + + if (len <= 0) { + if (vflag) printf(" Error: section 9 contains no data\n"); + return (0); + } + + /* FIXME: more to do here */ + if (vflag) { + printf(" Data:\n "); + while (len-- > 0) { + if (isprint(*p)) putchar(*p); + else printf("\\%03o", *p); + p++; + } + printf("\n"); + } + + return (1); +} + +int nsecleads; /* number of leads with measurements in section 10 */ + +void prval(FILE *ofile, short val) +{ + if (val == 29999) + fprintf(ofile, " n/comp"); /* not computed (ever) by analyzer */ + else if (val == 29998) + fprintf(ofile, " rej"); /* lead rejected, not computed */ + else if (val == 19999) + fprintf(ofile, " absent"); /* correponding wave not present */ + else + fprintf(ofile, "%8d", val); +} + +void precgmeas(FILE *ofile) +{ + int i; + + fprintf(ofile, "Parameter Units"); + for (i = 0; i < nsecleads; i++) { + if (0 < ecgmeas[i].leadid && ecgmeas[i].leadid <= 85) + fprintf(ofile, "%8s", leadname[ecgmeas[i].leadid]); + else + fprintf(ofile, " <%4d>", ecgmeas[i].leadid); + } + fprintf(ofile, "\nP duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Pdur); + fprintf(ofile, "\nPR interval ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].PRint); + fprintf(ofile, "\nQRS duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].QRSdur); + fprintf(ofile, "\nQT interval ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].QTint); + fprintf(ofile, "\nQ duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Qdur); + fprintf(ofile, "\nR duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Rdur); + fprintf(ofile, "\nS duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Sdur); + fprintf(ofile, "\nR' duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Rpdur); + fprintf(ofile, "\nS' duration ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Spdur); + fprintf(ofile, "\nQ amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Qamp); + fprintf(ofile, "\nR amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Ramp); + fprintf(ofile, "\nS amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Samp); + fprintf(ofile, "\nJ amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Jamp); + fprintf(ofile, "\nP+ amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Ppamp); + fprintf(ofile, "\nP- amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Pnamp); + fprintf(ofile, "\nT+ amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Tpamp); + fprintf(ofile, "\nT- amplitude uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Tnamp); + fprintf(ofile, "\nST slope uV/s"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].STslope); + fprintf(ofile, "\nP morphology --"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Pmorph); + fprintf(ofile, "\nT morphology --"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].Tmorph); + fprintf(ofile, "\nIsoelec (I) ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].isoI); + fprintf(ofile, "\nIsoelec (K) ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].isoK); + fprintf(ofile, "\nIntrins defl ms"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].tind); + fprintf(ofile, "\nQuality --"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].qual); + /* FIXME: SCP spec doesn't specify units of st20, 60, 80, rr16, rr8 -- + assume they are microvolts */ + fprintf(ofile, "\nST (J+20ms) uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].st20); + fprintf(ofile, "\nST (J+60ms) uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].st60); + fprintf(ofile, "\nST (J+80ms) uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].st80); + fprintf(ofile, "\nST (J+RRm/16) uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].strr16); + fprintf(ofile, "\nST (J+RRm/8) uV"); + for (i = 0; i < nsecleads; i++) + prval(ofile, ecgmeas[i].strr8); + fprintf(ofile, "\n"); +} + +int section10(unsigned char *p, long len) +{ + int i; + FILE *ofile; + + p += 16; len -= 16; /* move to data area */ + + if (len <= 0) { + if (vflag) printf(" Error: section 10 contains no data\n"); + return (0); + } + + /* Section 10 data area begins with a 4-byte header. The first 2 bytes + specify the number of leads for which measurements are stored. */ + nsecleads = get16(p); + /* FIXME: the draft standard doesn't say what the next 2 bytes are -- + so they are ignored here. */ + + /* Allocate structures to hold all of the parsed measurements. */ + ecgmeas = (struct ECGmeas *)calloc(nsecleads, sizeof(struct ECGmeas)); + if (ecgmeas == NULL) { + fprintf(stderr, "Error: too many (%d) leads\n", nsecleads); + return (0); + } + + /* Fill the array of structures. */ + for (i = 0, p += 4; i < nsecleads; i++) { + ecgmeas[i].leadid = get16(p); + ecgmeas[i].vlen = get16(p+2); + ecgmeas[i].Pdur = get16(p+4); + ecgmeas[i].PRint = get16(p+6); + ecgmeas[i].QRSdur = get16(p+8); + ecgmeas[i].QTint = get16(p+10); + ecgmeas[i].Qdur = get16(p+12); + ecgmeas[i].Rdur = get16(p+14); + ecgmeas[i].Sdur = get16(p+16); + ecgmeas[i].Rpdur = get16(p+18); + ecgmeas[i].Spdur = get16(p+20); + ecgmeas[i].Qamp = get16(p+22); + ecgmeas[i].Ramp = get16(p+24); + ecgmeas[i].Samp = get16(p+26); + ecgmeas[i].Rpamp = get16(p+28); + ecgmeas[i].Spamp = get16(p+30); + ecgmeas[i].Jamp = get16(p+32); + ecgmeas[i].Ppamp = get16(p+34); + ecgmeas[i].Pnamp = get16(p+36); + ecgmeas[i].Tpamp = get16(p+38); + ecgmeas[i].Tnamp = get16(p+40); + ecgmeas[i].STslope = get16(p+42); + ecgmeas[i].Pmorph = get16(p+44); + ecgmeas[i].Tmorph = get16(p+46); + ecgmeas[i].isoI = get16(p+48); + ecgmeas[i].isoK = get16(p+50); + ecgmeas[i].tind = get16(p+52); + ecgmeas[i].qual = get16(p+54); + ecgmeas[i].st20 = get16(p+56); + ecgmeas[i].st60 = get16(p+58); + ecgmeas[i].st80 = get16(p+60); + ecgmeas[i].strr16 = get16(p+62); + ecgmeas[i].strr8 = get16(p+64); + p += ecgmeas[i].vlen + 4; + } + + if (vflag) { + printf(" Results for %d leads\n", nsecleads); + precgmeas(stdout); + } + return (1); +} + +int section11(unsigned char *p, long len) +{ + p += 16; len -= 16; /* move to data area */ + + if (len <= 0) { + if (vflag) printf(" Error: section 11 contains no data\n"); + return (0); + } + + /* ** more to do here ** */ + return (1); +} + +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.22/convert/rdedfann.c wfdb-10.5.23/convert/rdedfann.c --- wfdb-10.5.22/convert/rdedfann.c 2013-10-22 10:18:53.000000000 -0400 +++ wfdb-10.5.23/convert/rdedfann.c 2014-03-05 22:31:34.000000000 -0500 @@ -1,9 +1,9 @@ /* file: rdedfann.c G. Moody 14 March 2008 - Last revised: 22 October 2013 + Last revised: 5 March 2014 ------------------------------------------------------------------------------- rdedfann: Print annotations from an EDF+ file -Copyright (C) 2008-2013 George B. Moody +Copyright (C) 2008-2014 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 @@ -44,8 +44,7 @@ main(int argc, char **argv) { char *record = NULL, *prog_name(); - int aindex = 0, alen = 0, framelen = 0; - int i, nsig, s, vflag = 0; + int aindex = 0, alen = 0, framelen = 0, i, nsig, s, vflag = 0, xflag = 0; WFDB_Sample *frame; WFDB_Siginfo *si; void help(); @@ -77,6 +76,9 @@ case 'v': /* verbose output -- include column headings */ vflag = 1; break; + case 'x': /* save EDF annotation text in aux rather than anntyp */ + xflag = 1; + break; default: (void)fprintf(stderr, "%s: unrecognized option %s\n", pname, argv[i]); @@ -137,9 +139,9 @@ state = 0; for (i = 0, p = (frame + aindex); i < alen; i++, p++) { - if (*p) { - proc(*p); - proc(*p >> 8); + if (*p || state) { + proc(*p, xflag); + proc(*p >> 8, xflag); } else break; @@ -152,7 +154,7 @@ exit(0); /*NOTREACHED*/ } -proc(int x) +proc(int x, int xflag) { static char onset[1024], duration[1024], text[1024]; static char *onsetp, *durationp, *textp; @@ -206,10 +208,12 @@ /* replace whitespace with '_' */ for (textp = text; *textp; textp++) if (*textp == ' ' || *textp == '\t') *textp = '_'; - printf("%s %7ld %5s%5d%5d%5d", - t ? mstimstr(t) : " 0:00.000", t, text, 0, 0, 0); - if (duration[0]) printf("\tduration: %s", duration); - printf("\n"); + printf("%s %7ld %5s%5d%5d%5d\t%s", + t ? mstimstr(t) : " 0:00.000", t, + xflag ? "\"" : text, 0, 0, 0, xflag ? text : ""); + if (duration[0]) + printf("%cduration: %s", xflag ? ' ' : '\t', duration); + printf("\n"); } if (x) { text[0] = x; @@ -248,6 +252,7 @@ " -F FREQ set the sampling frequency to FREQ Hz\n", " -h print this usage summary", " -v print column headings", + " -x print EDF+ annotation text in aux (default: print in anntyp)", NULL }; diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/Makefile.tpl wfdb-10.5.23/doc/Makefile.tpl --- wfdb-10.5.22/doc/Makefile.tpl 2006-05-11 15:05:00.000000000 -0400 +++ wfdb-10.5.23/doc/Makefile.tpl 2014-03-12 16:15:10.000000000 -0400 @@ -1,5 +1,5 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 11 May 2006 +# Last revised: 12 March 2014 # Change the settings below as appropriate for your setup. # Set COLORS to 'color' if you have a color printer and would like to print @@ -151,7 +151,7 @@ wag.pdf: cd wag-src; $(MAKE) wag.pdf -# 'make ag.ps': format the WFDB Applications Guide as PostScript +# 'make wag.ps': format the WFDB Applications Guide as PostScript wag.ps: cd wag-src; $(MAKE) wag.ps diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/blankpage wfdb-10.5.23/doc/wag-src/blankpage --- wfdb-10.5.22/doc/wag-src/blankpage 2002-08-06 09:46:14.000000000 -0400 +++ wfdb-10.5.23/doc/wag-src/blankpage 2014-03-13 11:56:00.000000000 -0400 @@ -1,9 +1,93 @@ -%!PS-Adobe-2.0 -%%Title: blank -%%Pages: 1 -%%EndProlog -%%Page: 1 1 -0 0 moveto -/Courier findfont 10 scalefont setfont -( )show showpage +%PDF-1.4 +%쏢 +5 0 obj +<> +stream +x+T03T0A(˥d^eUeTRɹ +N!@ 4.˥T`1endstream +endobj +6 0 obj +70 +endobj +4 0 obj +<> +/Contents 5 0 R +>> +endobj +3 0 obj +<< /Type /Pages /Kids [ +4 0 R +] /Count 1 +>> +endobj +1 0 obj +<> +endobj +7 0 obj +<>endobj +9 0 obj +<> +endobj +10 0 obj +<> +endobj +8 0 obj +<> +endobj +11 0 obj +<>stream + + + + + +2014-03-13T11:56:00-04:00 +2014-03-13T11:56:00-04:00 +UnknownApplication + +blank + + + + + +endstream +endobj +2 0 obj +<>endobj +xref +0 12 +0000000000 65535 f +0000000391 00000 n +0000002036 00000 n +0000000332 00000 n +0000000173 00000 n +0000000015 00000 n +0000000155 00000 n +0000000456 00000 n +0000000556 00000 n +0000000497 00000 n +0000000526 00000 n +0000000618 00000 n +trailer +<< /Size 12 /Root 1 0 R /Info 2 0 R +/ID [<31A48259DA1E781401C6E8E0E1F2F5DE><31A48259DA1E781401C6E8E0E1F2F5DE>] +>> +startxref +2173 %%EOF diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/ecgeval.1 wfdb-10.5.23/doc/wag-src/ecgeval.1 --- wfdb-10.5.22/doc/wag-src/ecgeval.1 2002-11-22 14:54:19.000000000 -0500 +++ wfdb-10.5.23/doc/wag-src/ecgeval.1 2014-03-12 16:00:59.000000000 -0400 @@ -1,4 +1,3 @@ -'\" t .TH ECGEVAL 1 "22 November 2002" "WFDB 10.3.0" "WFDB Applications Guide" .SH NAME ecgeval \- generate and run ECG analyzer evaluation script diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/fixag.sh wfdb-10.5.23/doc/wag-src/fixag.sh --- wfdb-10.5.22/doc/wag-src/fixag.sh 2005-08-20 22:14:14.000000000 -0400 +++ wfdb-10.5.23/doc/wag-src/fixag.sh 2014-03-13 13:30:28.000000000 -0400 @@ -56,7 +56,7 @@ sed "s+>\\(ann[1-3]\\)\\([ <]\\)+>\\1\\2+g" | sed "s+>time+>time+g" | sed "s+>signal-list+>signal-list+g" | - sed "s+>signal+>signal+g" | + sed "s+>signalsignal$PREVT+" >tmp.$$ fi mv tmp.$$ $THISU diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/intro.ht0 wfdb-10.5.23/doc/wag-src/intro.ht0 --- wfdb-10.5.22/doc/wag-src/intro.ht0 2002-11-26 21:20:33.000000000 -0500 +++ wfdb-10.5.23/doc/wag-src/intro.ht0 2014-03-13 13:59:56.000000000 -0400 @@ -45,18 +45,18 @@ version of the source(s) for each application.

-Under GNU/Linux or UNIX, if the WFDB Software Package has been installed -on your system, you can also access the information contained in the -main sections of this guide using man and related programs. For -example, to see the manual page for rdsamp, run the command +Under GNU/Linux, Mac OS X, or UNIX, if the WFDB Software Package has been +installed on your system, you can also access the information contained in the +main sections of this guide using man and related programs. For +example, to see the manual page for rdsamp, run the command

 	man rdsamp
 
(This also works under MS-Windows if you have installed the -Cygwin package, which includes the man utility for formatting +Cygwin package, which includes the man utility for formatting and reading manual pages.) In some cases you may need to add -/usr/local/man to your MANPATH environment variable, in -order to make these pages accessible to man. +/usr/local/man to your MANPATH environment variable, in +order to make these pages accessible to man.

Using WFDB Applications

@@ -66,6 +66,7 @@ doing this; a more detailed discussion may be found in the first chapter of the WFDB Programmer's Guide, in the section about the database path. +Most users will not need to do this, however.

Certain types of command arguments are used by many of the applications @@ -98,7 +99,7 @@ names.

-Each PhysioBank database directory includes a text file named RECORDS, +Each PhysioBank database directory includes a text file named RECORDS, which lists the record names for all records in that directory. @@ -135,23 +136,23 @@

signal -
Where this appears, substitute a signal number. Signal numbers -are integers; the first signal in each record is signal 0. In printed -documentation for the databases, signals always appear with signal 0 -at the top, signal 1 beneath, etc. +
Where this appears, substitute a signal number or (in most cases) a signal +name. Signal numbers are integers; the first signal in each record is signal 0. +In printed documentation for the databases, signals always appear with signal 0 +at the top, signal 1 beneath, etc. Signal names are the strings printed by +signame(1).
signal-list -
Where this (or signal ...) appears, you may specify more -than one signal number in any desired order; separate -the signal numbers using spaces. Unless otherwise noted, a signal may -appear more than once, or not at all, in a signal list. In most cases, -the end of the signal list is unambiguous (since signal numbers are never -negative, an option argument beginning with '-' is a reliable indicator). -In unusual cases, you may need to arrange options so that the signal list -is at the end of the command, or so that it is followed by an argument that -cannot be interpreted as a signal number. - +
Where this (or signal ...) appears, you may specify more than one +signal number in any desired order; separate the signal numbers or names using +spaces. Unless otherwise noted, a signal may appear more than once, or not at +all, in a signal list. In most cases, the end of the signal list is +unambiguous (since signal numbers are never negative, and signal names rarely +if ever begin with '-', an option argument beginning with '-' is a reliable +indicator). In unusual cases, you may need to arrange options so that the +signal list is at the end of the command, or so that it is followed by an +argument that cannot be interpreted as a signal number.

diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/Makefile.tpl wfdb-10.5.23/doc/wag-src/Makefile.tpl --- wfdb-10.5.22/doc/wag-src/Makefile.tpl 2008-07-23 12:33:52.000000000 -0400 +++ wfdb-10.5.23/doc/wag-src/Makefile.tpl 2014-03-13 12:45:41.000000000 -0400 @@ -1,5 +1,5 @@ -# file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 23 July 2008 +# file: Makefile.tpl G. Moody 24 May 2000 +# Last revised: 13 March 2014 # Change the settings below as appropriate for your setup. # D2PARGS is a list of options for dvips. Uncomment one of these to set the @@ -38,7 +38,7 @@ # sufficient to use a non-PostScript dvi translator such as dvilj). PSPRINT = lpr -# TROFF is the name of the program that prints UNIX troff files (needed to +# TROFF is the name of the program that formats UNIX troff files (needed to # 'make ag' and for the covers of the guides). Use 'groff' if you have # GNU groff (the preferred formatter). TROFF = groff @@ -57,40 +57,20 @@ # of each page. TMAN = -rC1 -rD1 -man -# WAGPSREQ is the target that must be made in order to make the PostScript -# version of the manual (wag.ps), and MAKEWAGPS is the command that must be -# run in order to do this. The process is a bit convoluted, because the -# simple PostScript version (wag0.ps) is concatenated from several PostScript -# files and thus lacks DSCs (document structuring comments). wag0.ps can be -# printed or viewed directly, but most (perhaps all) viewers are incapable of -# allowing the user to jump to a random page in a PostScript file that lacks -# DSCs, and it's not easy to select a subset of pages to print in such a -# file. If you have ghostscript version 7.x or later (earlier versions will -# not work properly), ps2pdf (included with ghostscript) and acroread (or -# pdftops), you can translate wag0.ps into PDF (adding the DSCs in the -# process), and then translate the PDF file back into PostScript with DSCs. -# A disadvantage of this is that the PDF version is roughly 25% larger than -# wag0.ps, and the final PostScript version is nearly twice as large as -# wag0.ps, and takes longer to render as a result. To enable creation of -# PostScript with DSCs in this way, uncomment the next two lines: -WAGPSREQ = wag.pdf -# The latest versions of acroread no longer support the -level1 and -fast -# options used in previous versions of this Makefile (level 2 is the default). -MAKEWAGPS = acroread -toPostScript wag.pdf -# You can use pdftops instead of acroread by commenting out the previous line -# and uncommenting the next one. -# MAKEWAGPS = pdftops wag.pdf -# Otherwise, uncomment the next two lines instead: -# WAGPSREQ = wag0.ps -# MAKEWAGPS = cp wag0.ps wag.ps +# PDFPS converts a PDF file to a PostScript file. Use either of these: +# PDFPS = pdf2ps +PDFPS = pdftops + +# PSPDF converts a PostScript file to a PDF file. +PSPDF = ps2pdf # It should not be necessary to modify anything below this line. # ----------------------------------------------------------------------------- .IGNORE: -all: wag.html wag.ps wag.pdf - cp -p wag.ps wag.pdf ../wag +all: wag.html wag.pdf + cp -p wag.pdf ../wag install: wag.man @@ -107,13 +87,13 @@ rm -f ../wag/* # 'make wag-book': print a copy of the WFDB Applications Guide -wag-book: wag0.ps +wag-book: wag.ps cp wag.cover wagcover echo $(SHORTDATE) >>wagcover echo .bp >>wagcover $(TROFF) wagcover >wagcover.ps $(PSPRINT) wagcover.ps - $(PSPRINT) wag0.ps + $(PSPRINT) wag.ps # 'make wag.html': format the WFDB Applications Guide as HTML wag.html: @@ -158,47 +138,38 @@ cd $(MAN1); $(LN) setwfdb.1 cshsetwfdb.1 cd $(MAN1); $(LN) wav2mit.1 mit2wav.1 +# 'make wag.ps': format the WFDB Applications Guide as PostScript +wag.ps: wag.pdf + $(PDFPS) wag.pdf + # 'make wag.pdf': format the WFDB Applications Guide as PDF -wag.pdf: wag0.ps - ps2pdf wag0.ps wag.pdf +wag.pdf: wag.tex + $(MAKE) wag1.pdf # also makes wag2.pdf, wag3.pdf, wag4.pdf + pdftk wag[1234].pdf cat output wag.pdf # concatenate sections $(SETPERMISSIONS) wag.pdf -# 'make wag.ps': format the WFDB Applications Guide as PostScript -wag.ps: $(WAGPSREQ) - $(MAKEWAGPS) - $(SETPERMISSIONS) wag.ps - -wag0.ps: wag.tex - $(MAKE) wag2.ps - $(MAKE) wag1.toc +wag1.pdf: wag2.pdf + $(MAKE) getpagenos maketoclines + ./maketoc-tex.sh >wag1.toc # TOC, wag3.pdf, wag4.pdf sed 's/VERSION/$(VERSION)/' wag1.tex - latex wag1 # front matter - dvips $(D2PARGS) -o wag1.ps wag1 - cat wag[1234].ps | grep -v '^%%' >wag0.ps # concatenate sections - -wag1.toc: wag2.ps - $(MAKE) getpagenos maketoclines - ./maketoc-tex.sh >wag1.toc # TOC and appendices + pdflatex wag1 # front matter -wag2.ps: +wag2.pdf: tbl *.1 *.3 *.5 | $(TROFF) $(TMAN) >wag2.ps # man pages + $(PSPDF) wag2.ps wag2.pdf -install.tex: wag1.toc -wag3.ps: install.tex +wag3.pdf: install.tex sed "s/LONGDATE/$(LONGDATE)/" wag3.tex - latex wag3 - dvips $(D2PARGS) -o wag3.ps wag3.dvi + pdflatex wag3 -eval.tex: wag1.toc -wag4.ps: eval.tex +wag4.pdf: eval.tex sed "s/LONGDATE/$(LONGDATE)/" wag4.tex - latex wag4 - dvips $(D2PARGS) -o wag4.ps wag4.dvi + pdflatex wag4 # 'make clean': remove intermediate and backup files clean: - rm -f *.aux *.dvi *.log *.ps *.toc intro.htm faq.htm wag.pdf wagcover \ + rm -f *.aux *.dvi *.log *.ps *.toc intro.htm faq.htm wag*pdf wagcover \ eval.tex install.tex wag[1234].tex *~ diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/maketoc-tex.sh wfdb-10.5.23/doc/wag-src/maketoc-tex.sh --- wfdb-10.5.22/doc/wag-src/maketoc-tex.sh 2011-11-16 14:08:19.000000000 -0500 +++ wfdb-10.5.23/doc/wag-src/maketoc-tex.sh 2014-03-13 13:03:04.000000000 -0400 @@ -1,8 +1,13 @@ #! /bin/sh # file: maketoc-tex.sh G. Moody 29 October 2002 -# Last revised: 16 November 2011 +# Last revised: 13 March 2014 # Generate the table of contents and appendices for the WFDB Applications Guide +countpages() +{ + pdftk $1 dump_data | grep NumberOfPages | cut -c 16- +} + prep() { ( for F in *.1 *.3 *.5; do grep "\\\\-" $F | head -1; done ) >namelines.out @@ -15,20 +20,24 @@ appendices() { - P=`grep '%%Pages: ' wag2b.ps; P=`expr $P + 2`;; + *[13579]) pdftk wag2.pdf blankpage cat output wag2b.pdf + mv wag2b.pdf wag2.pdf + P=`expr $P + 2`;; *) P=`expr $P + 1`;; esac sed s/FIRSTPAGE/$P/ install.tex - make wag3.ps - N=`grep '%%Pages: ' wag3b.ps; Q=`expr $Q + 1`;; + *[02468]) pdftk wag3.pdf blankpage cat output wag3b.pdf; + mv wag3b.pdf wag3.pdf + Q=`expr $Q + 1`;; esac sed s/FIRSTPAGE/$Q/ eval.tex - make wag4.ps + make wag4.pdf } prep >toc-log.$$ 2>&1 @@ -45,15 +54,6 @@ head -$N1 >toc-log.$$ 2>&1 -if [ -e wag2b.ps ] -then - mv -f wag2b.ps wag2.ps -fi -if [ -e wag3b.ps ] -then - mv -f wag3b.ps wag3.ps -fi - cat <anonymous.scp\fR +.br +In this case, none of the other output files are produced. +.PP +\fBparsescp\fR removes all of the PHI as well as names of physicians and +technicians, names of hospitals or clinics, and room numbers, replacing them +with 'xxx'. It changes all dates to January 1, and if the age is over 90, it +resets the age to 90 and the birth year to 90 years before the recording year. +Finally, it recalculates the SCP-ECG CRCs so that the output is still a valid +SCP-ECG file. Note that the original input file is not modified. +.PP +Note that \fBparsescp\fR does not deidentify other types of data (including its +own \fB.des\fR and \fB.key\fR files); it can only deidentify SCP-ECG files. + +.SH ENVIRONMENT +.PP +It may be necessary to set and export the shell variable \fBWFDB\fR (see +\fBsetwfdb\fR(1)). +.SS Examples +.br + \fBparsescp -o 12345 <12345.scp\fR +.br +The command above converts an SCP-ECG file named \fB12345.scp\fR into a set of +three files (\fB12345.des\fR, \fB12345.ecg\fR, and \fB12345.key\fR), as +described above. The argument following \fB-o\fR need not match the name of +the input file as in this example, but such a choice may reduce opportunities +for confusion. +.br + \fBparsescp -o 12345 -w <12345.scp\fR +.br +Same as the first example, but this command also creates a PhysioBank-compatible +record named \fB12345\fR (consisting of two files named \fB12345.dat\fR +and \fB12345.hea\fR). +.br + \fBparsescp -a <12345.scp >a001.scp\fR +.br +The final example reads its input (\fB12345.scp\fR), removes all PHI, and writes +the deidentified data to a new SCP-ECG file (\fBa001.scp\fR). +.PP +Note that none of these commands modify the original input file +(\fB12345.scp\fR). +.SH SEE ALSO +\fBrdsamp\fR(1), \fBsetwfdb\fR(1), \fBxform\fR(1), \fBsignal\fR(5) +.SH AUTHOR +George B. Moody (george@mit.edu) and Edna S. Moody +.SH SOURCE +http://www.physionet.org/physiotools/wfdb/convert/parsescp.c diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/rdedfann.1 wfdb-10.5.23/doc/wag-src/rdedfann.1 --- wfdb-10.5.22/doc/wag-src/rdedfann.1 2013-10-31 10:03:58.000000000 -0400 +++ wfdb-10.5.23/doc/wag-src/rdedfann.1 2014-03-05 22:34:17.000000000 -0500 @@ -1,4 +1,4 @@ -.TH RDEDFANN 1 "9 April 2008" "WFDB 10.4.6" "WFDB Applications Guide" +.TH RDEDFANN 1 "5 March 2014" "WFDB 10.5.23" "WFDB Applications Guide" .SH NAME rdedfann \- extract annotations from an EDF+ file .SH SYNOPSIS @@ -18,17 +18,20 @@ .TP \fB-v\fR Verbose mode (print column headings). +.TP +\fB-x\fR +Print EDF+ annotation text in 'aux' rather than 'anntyp' column. .PP Note that the annotation mnemonics in EDF+ files do not in general match those used in WFDB-compatible annotation files, so that it will -often be desirable to translate those that come from EDF+ file +often be desirable to translate those that come from EDF+ files before converting the text with \fBwrann\fR. For example, this command can be used to extract annotations from \fBfoo.edf\fR, change the EDF+ annotation type "QRS" to the WFDB type "N", and then produce a WFDB-compatible annotation file \fBfoo.edf.qrs\fR: .br - \fBrdedfann -r foo.edf | sed "s/QRS/ N" | wrann -r foo.edf -a qrs\fR + \fBrdedfann -r foo.edf | sed s/QRS/N/ | wrann -r foo.edf -a qrs\fR .PP Recent versions of \fBwrann\fR copy unrecognized mnemonics into @@ -53,4 +56,3 @@ George B. Moody (george@mit.edu) .SH SOURCES http://www.physionet.org/physiotools/wfdb/convert/rdedfann.c - diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/stepdet.1 wfdb-10.5.23/doc/wag-src/stepdet.1 --- wfdb-10.5.22/doc/wag-src/stepdet.1 1969-12-31 19:00:00.000000000 -0500 +++ wfdb-10.5.23/doc/wag-src/stepdet.1 2014-03-06 10:55:37.000000000 -0500 @@ -0,0 +1,61 @@ +.TH STEPDET 1 "6 March 2014" "WFDB 10.5.23" "WFDB Applications Guide" +.SH NAME +stepdet \- single-channel step change detector +.SH SYNOPSIS +\fBstepdet -r\fR \fIrecord\fR [ \fIoptions\fR ... ] +.SH DESCRIPTION +.PP +This program analyzes one signal of a PhysioBank-compatible \fIrecord\fR, +detecting and annotating rising and falling step changes. Typically this +can be useful for finding transitions in a recorded digital stimulus or +event marker signal, especially if the signal is noise-contaminated (as +may occur if it has been recorded via an analog-to-digital converter). +.PP +\fIOptions\fR include: +.TP +\fB-a\fR \fIannotator\fR +Write annotations to the specified \fIannotator\fR (default: 'steps') +.TP +\fB-f\fR \fItime\fR +Begin at the specified \fItime\fR in \fIrecord\fR (default: the beginning of +\fIrecord\fR). +.TP +\fB-h\fR +Print a brief usage summary. +.TP +\fB-H\fR +Read the signal files in high-resolution mode (default: standard mode). +These modes are identical for ordinary records. For multifrequency records, +the standard decimation of oversampled signals to the frame rate is suppressed +in high-resolution mode (rather, all other signals are resampled at the highest +sampling frequency). +.TP +\fB-m\fR \fItup tdown\fR +Specify thresholds for transitions from low to high (\fItup\fR, default: 550) +and from high to low (\fItdown\fR, default: 450). +.TP +\fB-s\fR \fIsignal\fR +Specify the \fIsignal\fR (number or name) to be used for step detection +(default: 0). +.TP +\fB-t\fR \fItime\fR +Process until the specified \fItime\fR in \fIrecord\fR (default: the end of the +\fIrecord\fR). +.PP +\fItup\fR is the threshold for detecting a rising step change (annotated +as 'R'), and \fItdown\fR is the threshold for detecting a falling ('F') step +change. This program requires that \fItup\fR > \fItdown\fR. Using its -m +option, set \fItup\fR to a value significantly greater than \fItdown\fR to +avoid false detections of transitions due to noise in the signal. Noise spikes +that still cause false detections can often be avoided by median-filtering the +signal (see mfilt(1)) before using it as input to this program. +.SH ENVIRONMENT +.PP +It may be necessary to set and export the shell variable \fBWFDB\fR (see +\fBsetwfdb\fR(1)). +.SH SEE ALSO +\fBmfilt\fR(1) +.SH AUTHORS +George B. Moody (george@mit.edu). +.SH SOURCE +http://www.physionet.org/physiotools/wfdb/app/stepdet.c diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/wag.ht0 wfdb-10.5.23/doc/wag-src/wag.ht0 --- wfdb-10.5.22/doc/wag-src/wag.ht0 2013-01-02 17:52:51.000000000 -0500 +++ wfdb-10.5.23/doc/wag-src/wag.ht0 2014-03-13 13:07:40.000000000 -0400 @@ -20,7 +20,7 @@ George B. Moody
Harvard-MIT Division of Health Sciences and Technology

-Copyright ©1980-2013 George B. Moody +Copyright ©1980-2014 George B. Moody

The most recent versions of the programs described in this guide may be freely downloaded from PhysioNet. For @@ -32,9 +32,9 @@ USA

-PostScript and PDF versions of this -guide are available. The latest version can be downloaded from -PhysioNet. +A PDF version of this guide is available. +The latest version can be downloaded from +PhysioNet.

Permission is granted to make and distribute verbatim copies of this guide provided that the copyright notice and this permission notice are diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/wag.tex wfdb-10.5.23/doc/wag-src/wag.tex --- wfdb-10.5.22/doc/wag-src/wag.tex 2013-01-02 17:52:46.000000000 -0500 +++ wfdb-10.5.23/doc/wag-src/wag.tex 2014-03-13 14:00:03.000000000 -0400 @@ -36,7 +36,7 @@ \pagestyle{empty} \vspace*{\fill} \noindent -Copyright \copyright 1980 -- 2013 George B. Moody +Copyright \copyright 1980 -- 2014 George B. Moody \vspace{1 in} \noindent @@ -130,10 +130,11 @@ out-of-date; rather they mean that the material described on that page remains current. -Under GNU/Linux or Unix, if the WFDB Software Package has been installed -on your system, you can also access the information contained in the -main sections of this guide using \textbf{man} and related programs. For -example, to see the manual page for \textbf{rdsamp}, run the command +Under GNU/Linux, Mac OS X, or Unix, if the WFDB Software Package has +been installed on your system, you can also access the information +contained in the main sections of this guide using \textbf{man} and +related programs. For example, to see the manual page for +\textbf{rdsamp}, run the command \begin{quote} \textbf{man rdsamp} \end{quote} @@ -153,7 +154,7 @@ input files. See \textbf{setwfdb}(1) in this guide for information about doing this; a more detailed discussion may be found in the first chapter of the \textit{WFDB Programmer's Guide}, in the section about the database -path. +path. Most users will not need to do this, however. Certain types of command-line arguments are used by many of the applications described in this guide. These include: @@ -221,21 +222,23 @@ \item{\textit{signal}} -Where this appears, substitute a signal number. Signal numbers are integers; -the first signal in each record is signal 0. In printed documentation for -the databases, signals always appear with signal 0 at the top, signal 1 -beneath, etc. +Where this appears, substitute a signal number or (in most cases) a signal name. +Signal numbers are integers; the first signal in each record is signal 0. +In printed documentation for the databases, signals always appear with signal 0 +at the top, signal 1 beneath, etc. Signal names are the strings printed by +\textbf{signame}(1). \item{\textit{signal-list}} Where this (or `\textit{signal ...}') appears, you may specify more than one -signal in any desired order; separate the signal numbers using spaces. -Unless otherwise noted, a signal may appear more than once, or not at all, -in a signal list. In most cases, the end of the signal list is unambiguous -(since signal numbers are never negative, an option argument beginning -with '-' is a reliable indicator). In unusual cases, you may need to arrange -options so that the signal list is at the end of the command, or so that it -is followed by an argument that cannot be interpreted as a signal number. +signal in any desired order; separate the signal numbers or names using spaces. +Unless otherwise noted, a signal may appear more than once, or not at all, in a +signal list. In most cases, the end of the signal list is unambiguous (since +signal numbers are never negative, and signal names rarely if ever begin with +'-', an option argument beginning with '-' is a reliable indicator). In unusual +cases, you may need to arrange options so that the signal list is at the end of +the command, or so that it is followed by an argument that cannot be +interpreted as a signal number. \end{description} \chapter*{Frequently Asked Questions\\(and Frequently Exclaimed Exclamations)} diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wag-src/wave.1 wfdb-10.5.23/doc/wag-src/wave.1 --- wfdb-10.5.22/doc/wag-src/wave.1 2009-10-28 11:05:03.000000000 -0400 +++ wfdb-10.5.23/doc/wag-src/wave.1 2014-03-12 16:00:45.000000000 -0400 @@ -1,4 +1,3 @@ -'\" t .TH WAVE 1 "28 October 2009" "WFDB 10.4.24" "WFDB Applications Guide" .SH NAME wave \- waveform analyzer, viewer, and editor @@ -696,11 +695,11 @@ incompatibility noted above) appeared with the release in 2009 of the X.org version 1.6.3 X server, which freezes when any application that uses the XView library (such as \fBwave\fR) 'grabs' the mouse pointer. By default, XView -applications do so in response to a left button click on any XView control. -'Grabs' can be disabled, and this behavior avoided, by using the \fB-Wfsdb\fR -option available in \fBwave\fR and in other XView applications. In \fBwave\fR -version 6.10 and later versions, the default behavior of XView has been -changed to disable 'grabs', and this problem does not occur. +applications do so in response to a left button click on any XView +control. 'Grabs' can be disabled, and this behavior avoided, by using +the \fB-Wfsdb\fR option available in \fBwave\fR and in other XView applications. +In \fBwave\fR version 6.10 and later versions, the default behavior of XView +has been changed to disable 'grabs', and this problem does not occur. .SH SEE ALSO \fBpschart\fR(1), \fBxview\fR(7) .br diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wpg-src/Makefile.tpl wfdb-10.5.23/doc/wpg-src/Makefile.tpl --- wfdb-10.5.22/doc/wpg-src/Makefile.tpl 2006-03-02 09:30:41.000000000 -0500 +++ wfdb-10.5.23/doc/wpg-src/Makefile.tpl 2014-03-13 14:34:51.000000000 -0400 @@ -1,5 +1,5 @@ -# file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 2 March 2006 +# file: Makefile.tpl G. Moody 24 May 2000 +# Last revised: 13 March 2014 # Change the settings below as appropriate for your setup. # D2PARGS is a list of options for dvips. Uncomment one of these to set the @@ -32,7 +32,7 @@ # them. If you have GNU 'makeinfo' and 'install-info' (preferred), # uncomment the next two lines. MAKEINFO = makeinfo --force --no-warn -INSTALLINFO = /sbin/install-info --info-dir=$(INFODIR) $(INFODIR)/wpg +INSTALLINFO = /usr/sbin/install-info --info-dir=$(INFODIR) $(INFODIR)/wpg # Otherwise, you can use GNU emacs to do the formatting, and standard utilities # to install the info files, by uncommenting the next two lines. @@ -62,9 +62,9 @@ .IGNORE: -all: wpg.html wpg.ps wpg.pdf +all: wpg.html wpg.pdf $(MAKE) INFODIR=../wpg/info INSTALLINFO=: wpg.info - cp -p wpg.ps wpg.pdf ../wpg + cp -p wpg.pdf ../wpg install: @echo Nothing to install in wpg-src. diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wpg-src/wpg0.tex wfdb-10.5.23/doc/wpg-src/wpg0.tex --- wfdb-10.5.22/doc/wpg-src/wpg0.tex 2013-11-18 19:43:02.000000000 -0500 +++ wfdb-10.5.23/doc/wpg-src/wpg0.tex 2014-03-13 14:10:44.000000000 -0400 @@ -13,15 +13,15 @@ @sp 5 @center @titlefont{WFDB Programmer's Guide} @sp 4 -@center Tenth Edition (revised and with additions for WFDB library version VERSION) +@center Tenth Edition +@center (revised and with additions for WFDB library version VERSION) @center LONGDATE @sp 5 @center George B. Moody -@sp 1 @center Harvard-MIT Division of Health Sciences and Technology @page @vskip 0pt plus 1filll -Copyright @copyright{} 1980 -- 2013 George B. Moody +Copyright @copyright{} 1980 -- 2014 George B. Moody @sp 2 The most recent versions of the software described in this guide may be downloaded from @uref{http://physionet.org/}. @@ -9051,6 +9051,14 @@ @unnumberedsec WFDB 10.5 +@unnumberedsubsec Changes in version 10.5.23 (13 March 2014) + +Changes in @file{configure}, @file{Makefile.tpl}, @file{conf/linux.def}, and +@file{conf/linux-slib.def} simplify installation of shared WFDB libraries and +the applications that use them on Linux platforms. + +(WFDB library version 10.5.22 was identical to version 10.5.21.) + @unnumberedsubsec Changes in version 10.5.21 (18 November 2013) In previous releases, WFDB library function @code{strtim()} did not always diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wpg-src/wpg.ht0 wfdb-10.5.23/doc/wpg-src/wpg.ht0 --- wfdb-10.5.22/doc/wpg-src/wpg.ht0 2013-01-02 17:51:46.000000000 -0500 +++ wfdb-10.5.23/doc/wpg-src/wpg.ht0 2014-03-13 14:12:11.000000000 -0400 @@ -19,7 +19,7 @@ George B. Moody
Harvard-MIT Division of Health Sciences and Technology

-Copyright ©1980-2013 George B. Moody. +Copyright ©1980-2014 George B. Moody.

The most recent versions of the software described in this guide are freely downloadable from PhysioNet. For @@ -31,9 +31,8 @@ USA

-PostScript and PDF versions of this -guide are available. The latest versions can be downloaded from -PhysioNet. +A PDF version of this guide is available. The latest +version can be downloaded from PhysioNet.

Permission is granted to make and distribute verbatim copies of this guide provided that the copyright notice and this permission notice are diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wug-src/Makefile.tpl wfdb-10.5.23/doc/wug-src/Makefile.tpl --- wfdb-10.5.22/doc/wug-src/Makefile.tpl 2006-02-27 12:47:40.000000000 -0500 +++ wfdb-10.5.23/doc/wug-src/Makefile.tpl 2014-03-13 14:30:37.000000000 -0400 @@ -1,5 +1,5 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 27 February 2006 +# Last revised: 13 March 2014 # Change the settings below as appropriate for your setup. # Set COLORS to 'color' if you have a color printer and would like to print @@ -43,8 +43,8 @@ .IGNORE: -all: wug.html wug.ps wug.pdf - cp -p wug.ps wug.pdf ../wug +all: wug.html wug.pdf + cp -p wug.pdf ../wug install: @echo Nothing to install in wug-src. diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/doc/wug-src/wug0.tex wfdb-10.5.23/doc/wug-src/wug0.tex --- wfdb-10.5.22/doc/wug-src/wug0.tex 2013-01-02 17:53:17.000000000 -0500 +++ wfdb-10.5.23/doc/wug-src/wug0.tex 2014-03-13 14:29:20.000000000 -0400 @@ -57,7 +57,7 @@ \pagestyle{empty} \vspace*{\fill} \noindent -Copyright \copyright 1992 -- 2013 George B. Moody +Copyright \copyright 1992 -- 2014 George B. Moody \vspace{1 in} \noindent @@ -80,9 +80,8 @@ \begin{htmlonly} \noindent -\htmladdnormallink{PostScript}{wug.ps} and -\htmladdnormallink{PDF}{wug.pdf} versions of this guide are available. -The latest versions can be downloaded from +A \htmladdnormallink{PDF}{wug.pdf} version of this guide is available. +The latest version can be downloaded from \htmladdnormallink{MIT}{http://physionet.org/}. \end{htmlonly} \begin{latexonly} diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/install-wave32 wfdb-10.5.23/install-wave32 --- wfdb-10.5.22/install-wave32 2013-11-18 19:25:56.000000000 -0500 +++ wfdb-10.5.23/install-wave32 2014-03-12 12:09:44.000000000 -0400 @@ -1,202 +1,54 @@ #! /bin/sh # file: install-wave32 G. Moody 7 October 2008 -# Last revised: 18 November 2013 -# Build and install WAVE on 64-bit Linux -# -# *** Debian and Ubuntu users, see the notes at the end! *** -# *** Fedora 12 users: if this script fails, see the notes near the end! *** -# -# WAVE must be compiled as a 32-bit application, because it depends on the -# XView toolkit, which does not support 64-bit mode (and most likely, never -# will), and 64-bit applications cannot use the 32-bit XView libraries. -# Furthermore, 32-bit applications cannot use 64-bit libraries, so it is -# necessary to install 32-bit versions of all of the libraries needed by WAVE, -# as well as the .h ("include") files associated with these libraries. - -# This script automates the process of building a 32-bit WAVE binary. On -# Fedora Linux, it should be able to install all of the prerequisites (beyond -# those already installed in order to compile the rest of the WFDB package in -# 64-bit mode) before compiling and installing WAVE. To do this, it should be -# sufficient to run this script with root (superuser) permissions from within -# the top-level WFDB source directory (the directory that also contains the -# 'configure' script). It is harmless to rerun this script if the -# prerequisites have been installed already. - -# On other versions of Linux, you will need to install the first two groups of -# prerequisites in some other way before running the final part of this script -# as root, using the -q option. See the notes below for hints. - -echo "This script builds and installs 'wave' as a 32-bit application on x86_64" -echo "(AMD/Intel 64-bit) Fedora GNU/Linux. It has been tested on Fedora 12," -echo "14, and 18; it will probably require modification on other platforms." +# Last revised: 12 March 2014 +# Build and install WAVE on 64-bit GNU/Linux platforms + +# ***************************************************************************** +# This script builds and installs 'wave' as a 32-bit application on x86_64 +# (AMD/Intel 64-bit) Fedora GNU/Linux. It has been tested on Fedora 12, 14, 16, +# and 18; it will probably require modification on other platforms. +# +# Part 1 of this script automates installation of prerequisite 32-bit libraries +# and *.h files on Fedora GNU/Linux and compatible platforms such as Red Hat, +# Scientific Linux, and Centos. It is harmless to rerun this script if some or +# all of these prerequisites have been installed already. +# +# If you use another GNU/Linux platform such as Debian, Ubuntu, or Mint: +# You will need to install these prerequisites in some other way before running +# part 2 of this script using the -q option; see +# http://physionet.org/physiotools/wave-installation.shtml +# for details. +# ***************************************************************************** + echo +echo "If this script fails, read it for troubleshooting hints." -if [ $UID != 0 ] -then +# Part 1: install prerequisites (unless skipped using -q option) +if [ "x$1" != "x-q" ]; then + if [ $UID != 0 ]; then echo "You do not have root (superuser) permissions, so this script may fail" echo "if it needs to install missing libraries. If this happens, rerun it" echo "as root (e.g., using su or sudo)." -fi + fi -echo -echo "If this script fails, read it for troubleshooting hints." - -# The prerequisites needed in order to compile WAVE on 64-bit Fedora fall into -# three groups: -# -# 1. 32-bit libraries and font packages available from Fedora repositories -# -# These include the standard C library, the X11 client libraries, the X11 -# pixmap libraries, the libcurl (HTTP client) libraries, and their -# respective developer's toolkits. The easiest way to install these on -# Fedora is using the yum commands below. These packages may have different -# names in other Linux distributions, and "yum" itself may not be available -# as a package manager in some distributions. These commands are safe to -# run even if any or all of these packages are already installed. -# -# If you use a Debian-based Linux, such as Ubuntu, it can be difficult to -# force apt-get to install 32-bit packages, especially if like-named 64-bit -# packages have been installed already. Google on "getlibs" for an -# alternative. - -if [ "x$1" != "x-q" ] -then - -# The next several lines attempt to determine if this script is being run -# under Fedora 12 or a later version, in order to determine the names of -# the 32-bit packages to be installed. This code has not been tested on -# other Linux distributions, but it will assume that the Fedora 12 package -# names are the correct ones unless it recognizes Fedora 7, 8, 9, 10, or -# 11. Until Fedora 12, 32-bit packages are named with 'i386' suffixes -# (although in some cases they have been compiled for i586 or i686 -# architectures). In Fedora 12, 32-bit packages are compiled and named as -# i686 packages. - - FV=`cat /etc/fedora-release | cut -d " " -f 3` - case $FV in - 7|8|9|10|11) X86=i386 ;; - *) X86=i686 ;; - esac - yum -y install libgcc.$X86 glibc-devel.$X86 libX11-devel.$X86 \ - libXpm-devel.$X86 libcurl-devel.$X86 - yum -y install xorg-x11-fonts-misc \ + # Install fonts and 32-bit libraries available from Fedora repositories + yum -y install libgcc.i686 glibc-devel.i686 libX11-devel.i686 \ + libXpm-devel.i686 libcurl-devel.i686 xorg-x11-fonts-misc \ xorg-x11-fonts-100dpi xorg-x11-fonts-ISO8859-1-100dpi \ xorg-x11-fonts-75dpi xorg-x11-fonts-ISO8859-1-75dpi -fi -# 2. XView libraries available from PhysioNet -# -# These are available as RPMs for Fedora and other RPM-based distributions, -# and in binary and source tarballs for other distributions. By far the -# easiest way to install them on Fedora is using the RPM command below. -# Again, this command is safe even if any or all of these are already -# installed. -# -# On Debian or Ubuntu, simply run -# apt-get install xviewg-dev -# instead of the rpm commands below (since 32-bit XView is in the Debian -# repositories). - -if [ "x$1" != "x-q" ] -then - rpm -Uvh http://physionet.org/physiotools/xview/i386-Fedora/xview-3.2p1.4-21.1.fc8.i386.rpm \ + # Install XView libraries and development toolkit available from PhysioNet + rpm -Uvh http://physionet.org/physiotools/xview/i386-Fedora/xview-3.2p1.4-21.1.fc8.i386.rpm \ http://physionet.org/physiotools/xview/i386-Fedora/xview-clients-3.2p1.4-21.1.fc8.i386.rpm \ http://physionet.org/physiotools/xview/i386-Fedora/xview-devel-3.2p1.4-21.1.fc8.i386.rpm fi -# 3. The 32-bit version of the WFDB library -# -# This is easily compiled and installed on any platform by the following -# commands: - +# Part 2: compile and install 32-bit WFDB library and WAVE make clean ./configure -m32 $* -cd lib -make install - -# Now all of the prerequisites are in place, and we can compile and install -# WAVE itself: - -cd ../wave -make install - -# Compile and install applications for remote control of WAVE. -cd ../waverc -make install - -# Clean up intermediate binaries and other temporary files. -cd .. -make clean - -# **************************************************************************** -# -# On Fedora 12, at the time this script was written, the most recent 32-bit -# version of libcurl-devel was older than the most recent 64-bit version. -# In this situation (and presumably in similar situations in which a 32-bit -# package is "stale") the first yum command above fails. This is a bug -# in yum that may be fixed in a future release; and it is also likely that -# libcurl-devel.i686 will be updated in the future, so you may not encounter -# this problem. If you do, here's the work-around: -# -# 1. Download the 32-bit rpm that yum refuses to install. -# -# 2. Attempt to install it using a command of the form -# rpm -ivh --force *.i686.rpm -# If this is successful, rerun this script. -# -# 3. If rpm complained that one or more dependencies were missing, use yum to -# install them, then repeat step 2. -# -# Iterate until successful. In the case of libcurl-devel, it was necessary -# to use these commands (the yum commands could have been combined into one): -# -# yum -y install libgcc.i686 libX11-devel.i686 install glibc-devel.i686 -# yum -y install libXpm-devel.i686 -# yum -y install cyrus-sasl-lib.i686 keyutils-libs.i686 krb5-libs.i686 -# yum -y install libidn.i686 libssh2.i686 ncurses-libs.i686 nspr.i686 -# yum -y install nss.i686 nss-softokn.i686 openldap.i686 -# rpm -ivh --force libcurl-7.19.7-2.fc12.i686.rpm -# rpm -ivh --force libcurl-devel-7.19.7-2.fc12.i686.rpm -# -# **************************************************************************** -# -# Notes for Debian Wheezy and Ubuntu 12.04, from Benjamin Moody: -# -# 1. Get the latest updates (needed on Wheezy, a good idea otherwise): -# apt-get update -# -# 2. Ensure that i386 is enabled in dpkg configuration. Apparently this -# is not needed on Ubuntu; on Debian: -# dpkg --add-architecture i386 -# -# 3. On Ubuntu 12.10 or earlier, or Debian Wheezy, you will need to -# upgrade the 'xbitmaps' package to version 1.1.1-2 manually (older -# versions aren't marked as multiarch-safe): -# wget http://archive.ubuntu.com/ubuntu/pool/main/x/xbitmaps/xbitmaps_1.1.1-2_all.deb -# dpkg -i xbitmaps_1.1.1-2_all.deb -# (Debian users can safely install the package from Ubuntu.) -# -# 4. On Ubuntu, ensure that 'universe' section is enabled: -# sed '/^deb/{/universe/!s/ main/ main universe/}' -i /etc/apt/sources.list -# apt-get update -# -# 5. Install the XView library and developer packages: -# apt-get install xviewg-dev:i386 -# On Debian Wheezy, this won't work if you skipped step 1! -# -# 6. Install the 32-bit libcurl library and developer packages: -# apt-get install libcurl4-gnutls-dev:i386 -# This can coexist with libcurl4-gnutls-dev:amd64 on Debian, not tested -# on Ubuntu (please send feedback if you try it). -# -# 7. Install the remaining 32-bit compatibility tools and libraries: -# apt-get install build-essential gcc-multilib -# -# 8. Compile and install the WFDB Software Package as usual, using configure's -# -m32 option. For example, to install in /usr/local, begin with: -# ./configure -m32 --prefix=/usr/local -# make install -# You will need to set and export LD_LIBRARY_PATH if you use --prefix as -# above: -# LD_LIBRARY_PATH=/usr/local/lib -# export LD_LIBRARY_PATH +cd lib; + make install +cd ../wave; + make install +cd ..; + make clean diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/lib/Makefile.tpl wfdb-10.5.23/lib/Makefile.tpl --- wfdb-10.5.22/lib/Makefile.tpl 2012-09-27 09:16:14.000000000 -0400 +++ wfdb-10.5.23/lib/Makefile.tpl 2014-03-01 13:52:06.000000000 -0500 @@ -1,5 +1,5 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 27 September 2012 +# Last revised: 1 March 2014 # This section of the Makefile should not need to be changed. INCLUDES = $(INCDIR)/wfdb/wfdb.h $(INCDIR)/wfdb/wfdblib.h \ @@ -71,4 +71,4 @@ calib.o: wfdb.h wfdblib.h calib.c wfdbio.o: wfdb.h wfdblib.h wfdbio.c $(CC) $(CFLAGS) -DVERSION='"$(VERSION)"' -DCFLAGS='"-I$(INCDIR)"' \ - -DLDFLAGS='"-L$(LIBDIR) -lwfdb"' -c wfdbio.c \ No newline at end of file + -DLDFLAGS='"-L$(LIBDIR) -lwfdb $(CWLOPT) $(LL)"' -c wfdbio.c \ No newline at end of file diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/lib/wfdb.h wfdb-10.5.23/lib/wfdb.h --- wfdb-10.5.22/lib/wfdb.h 2013-12-08 12:14:30.000000000 -0500 +++ wfdb-10.5.23/lib/wfdb.h 2014-03-13 15:39:40.000000000 -0400 @@ -32,7 +32,7 @@ /* WFDB library version. */ #define WFDB_MAJOR 10 #define WFDB_MINOR 5 -#define WFDB_RELEASE 22 +#define WFDB_RELEASE 23 #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.22/lib/wfdblib.h wfdb-10.5.23/lib/wfdblib.h --- wfdb-10.5.22/lib/wfdblib.h 2013-12-08 11:55:19.000000000 -0500 +++ wfdb-10.5.23/lib/wfdblib.h 2014-03-13 15:27:51.000000000 -0400 @@ -96,9 +96,9 @@ variable is not set. This value is edited by the configuration script (../configure), which also edits this block of comments to match. - If WFDB_NETFILES support is disabled, the string ". /usr/local/database" is + If WFDB_NETFILES support is disabled, the string ". /home/george/database" is usually sufficient for a default WFDB path, thus restricting the search for - WFDB files to the current directory ("."), followed by /usr/local/database). + WFDB files to the current directory ("."), followed by /home/george/database). If WFDB_NETFILES support is enabled, the first setting below adds the web-accessible PhysioBank databases to the default path; you may wish to @@ -107,9 +107,9 @@ */ #ifndef WFDB_NETFILES -# define DEFWFDB ". /usr/local/database" +# define DEFWFDB ". /home/george/database" #else -# define DEFWFDB ". /usr/local/database http://physionet.org/physiobank/database" +# define DEFWFDB ". /home/george/database http://physionet.org/physiobank/database" #endif /* Mac OS 9 and earlier, only: The value of DEFWFDB given below specifies diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/Makefile.tpl wfdb-10.5.23/Makefile.tpl --- wfdb-10.5.22/Makefile.tpl 2010-10-06 22:30:57.000000000 -0400 +++ wfdb-10.5.23/Makefile.tpl 2014-03-09 09:35:41.000000000 -0400 @@ -1,5 +1,5 @@ # file: Makefile.tpl G. Moody 24 May 2000 -# Last revised: 6 October 2010 +# Last revised: 9 March 2014 # This section of the Makefile should not need to be changed. # 'make' or 'make all': compile the WFDB applications without installing them @@ -8,16 +8,16 @@ # 'make install': compile and install the WFDB software package install: config.cache - cd lib; $(MAKE) install - cd app; $(MAKE) install - cd convert; $(MAKE) install - cd data; $(MAKE) install - cd fortran; $(MAKE) install - cd psd; $(MAKE) install - -( cd wave; $(MAKE) install ) - cd waverc; $(MAKE) install - -( cd xml; $(MAKE) install ) - test -d doc && ( cd doc; $(MAKE) install ) + cd lib; $(MAKE) clean install + cd app; $(MAKE) clean install + cd convert; $(MAKE) clean install + cd data; $(MAKE) clean install + cd fortran; $(MAKE) clean install + cd psd; $(MAKE) clean install + -( cd wave; $(MAKE) clean install ) + cd waverc; $(MAKE) clean install + -( cd xml; $(MAKE) clean install ) + test -d doc && ( cd doc; $(MAKE) clean install ) # 'make collect': collect the installed files into /tmp/wfdb/ collect: @@ -112,11 +112,13 @@ mkdir -p $(HOME)/wfdb-test/lib; \ $(SETDPERMISSIONS) $(HOME)/wfdb-test/lib -# 'make tarballs': clean up the source directories, then make a pair of gzipped -# tar source archives of the WFDB software package (with and without the -# documentation), and check that the MANIFEST (list of files in the package) -# is correct. +# 'make tarballs': clean up the source directories, run ./configure with +# default settings, then make a pair of gzipped tar source archives of the WFDB +# software package (with and without the documentation), and check that the +# MANIFEST (list of files in the package) is correct. tarballs: clean + ./configure + $(MAKE) clean rm -f ../$(PACKAGE)-MANIFEST ../$(PACKAGE).tar.gz \ ../$(PACKAGE)-no-docs.tar.gz cd lib; $(SETPERMISSIONS) *.h diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/MANIFEST wfdb-10.5.23/MANIFEST --- wfdb-10.5.22/MANIFEST 2013-11-19 13:09:04.000000000 -0500 +++ wfdb-10.5.23/MANIFEST 2014-03-08 18:03:25.000000000 -0500 @@ -46,6 +46,7 @@ app/sortann.c app/sqrs125.c app/sqrs.c +app/stepdet.c app/sumann.c app/sumstats.c app/tach.c @@ -109,6 +110,11 @@ checkpkg/expected/sumann.out checkpkg/expected/sumstats.out checkpkg/expected/tach.out +checkpkg/expected/test.dat +checkpkg/expected/test.des +checkpkg/expected/test.ecg +checkpkg/expected/test.hea +checkpkg/expected/test.key checkpkg/expected/udb checkpkg/expected/udb/100z.hea checkpkg/expected/wfd00001.dat @@ -129,6 +135,7 @@ checkpkg/input/100x.hea checkpkg/input/ecgeval checkpkg/input/sumstats +checkpkg/input/test.scp checkpkg/input/xform checkpkg/lcheck.c checkpkg/libcheck @@ -176,6 +183,7 @@ convert/md2a.c convert/mit2edf.c convert/mit2wav.c +convert/parsescp.c convert/rdedfann.c convert/readid.c convert/README @@ -318,6 +326,7 @@ doc/wag-src/mxm.1 doc/wag-src/nguess.1 doc/wag-src/nst.1 +doc/wag-src/parsescp.1 doc/wag-src/plot2d.1 doc/wag-src/plotstm.1 doc/wag-src/plt.1 @@ -342,6 +351,7 @@ doc/wag-src/snip.1 doc/wag-src/sortann.1 doc/wag-src/sqrs.1 +doc/wag-src/stepdet.1 doc/wag-src/sumann.1 doc/wag-src/sumstats.1 doc/wag-src/tach.1 diff -Naur --exclude Makefile --exclude info wfdb-10.5.22/NEWS wfdb-10.5.23/NEWS --- wfdb-10.5.22/NEWS 2013-12-08 13:52:31.128441279 -0500 +++ wfdb-10.5.23/NEWS 2014-03-13 16:09:49.539699712 -0400 @@ -1,3 +1,34 @@ +10.5.23 (13 March 2014): + + Changes in 'configure', 'Makefile.tpl', 'conf/linux.def', and + 'conf/linux-slib.def' simplify installation of shared WFDB libraries + and the applications that use them on Linux platforms. + + Two new applications are included in this release. 'stepdet' + (app/stepdet.c), detects and annotates transitions in (possibly noisy) + two-level signals. 'parsescp' (convert/parsescp.c) converts some types + of SCP-ECG files to PhysioNet-compatible format. + + The parser in 'wrsamp' has been rewritten in this release to accept a + wider range of non-standard CSV input formats. + + The application that converts PhysioBank-compatible records to EDF + files, 'convert/mit2edf', did not properly calculate the range of + signals if the ADC resolution was specified to be 0 in the input .hea + file. In this release, 'mit2edf' guesses the ADC resolution based on + the storage format if necessary, allowing it to produce correct EDF + output given this type of incorrect input in most cases. + + Another application, 'parsescp', has been added to the set of data + importers in 'convert/'. + + Luca Citi contributed a bug fix and a new feature to 'convert/rdedfann'. + Previous versions did not properly handle input in which the last + annotation in a frame ends with a \024 byte in an even position. The + new '-x' option causes the input EDF+ annotation text to be copied to + the 'aux' position in the output, rather than the 'anntyp' position + (the default). + 10.5.22 (8 December 2013): 'snip' has a new '-O' option to select the output format, and the '-l' option to specify the duration now works properly. @@ -78,29 +109,29 @@ 10.5.16 (27 September 2012): A bug in WFDB library versions 10.5.13 through 10.5.15 resulted in - an attempt to close an already-closed header file after invoking + an attempt to close an already-closed header file after invoking putinfo(). Thanks to Benjamin Moody for identifying the bug and contributing code to correct it (in lib/signal.c and lib/wfdbinit.c). 10.5.15 (25 September 2012): A new application (app/gqfuse.c) for combining two or more beat - annotation files is included in this release. + annotation files is included in this release. Changes to the internal function readheader() in WFDB library version 10.5.14 made the library unable to open EDF files. This bug has been - fixed. + fixed. 10.5.14 (13 August 2012): WFDB applications can now read shared and private PhysioNetWorks projects securely, just as they have been able to read PhysioBank data since version 10.0.1 (November 1999). A new shell script - (app/pnwlogin) simplifies access to PhysioNetWorks projects. Low-level - functions wfdb_open (in lib/wfdbio.c) and readheader (in lib/signal.c) + (app/pnwlogin) simplifies access to PhysioNetWorks projects. Low-level + functions wfdb_open (in lib/wfdbio.c) and readheader (in lib/signal.c) incorporate changes to implement this new capability, as well as another new feature that allows record names to be specified using absolute pathnames or URLs. As always, a final '.hea' is not - considered to be part of a record name, but it may be included or - omitted as desired. + considered to be part of a record name, but it may be included or + omitted as desired. WFDB library function wfdbquit() no longer frees resources allocated by libcurl or libwww, in order to avoid doing so more than once in @@ -123,10 +154,10 @@ to Justin Leo Cheang Loong for reporting this issue. 10.5.12 (24 April 2012): - When called with a NULL argument, getinfo() sometimes behaves - differently in WFDB library version 10.5.11 than it does in previous - versions. This release restores the previous behavior. Thanks to - Benjamin Moody for reporting this issue. + When called with a NULL argument, getinfo() sometimes behaves + differently in WFDB library version 10.5.11 than it does in previous + versions. This release restores the previous behavior. Thanks to + Benjamin Moody for reporting this issue. 10.5.11 (6 April 2012): This release of the WFDB library introduces support for '.info' files. @@ -178,12 +209,12 @@ Attempts to set the default size for HTTP range requests (using the environment variable WFDB_PAGESIZE) were ignored in previous versions - of the WFDB library when compiled with libcurl. Thanks again to David - for noting this limitation, which has been eliminated in this release + of the WFDB library when compiled with libcurl. Thanks again to David + for noting this limitation, which has been eliminated in this release by a change in www_init() (in lib/wfdbio.c). 'wfdbmap', which has been available in its own directory since release - 10.5.6, is a now a standard application (app/wfdbmap.c). 'wfdbmap' is + 10.5.6, is a now a standard application (app/wfdbmap.c). 'wfdbmap' is used by the PhysioBank ATM to produce compact synoptic maps of WFDB- compatible records. Previous versions of 'wfdbmap' were unable to map multi-segment records unless all segment headers were local; this @@ -194,18 +225,18 @@ is a stripped-down version of 'wfdbdesc'. 10.5.9 (10 September 2011): - When an application passes an array containing WFDB_INVALID_SAMPLE - values to putvec(), the function translates these into the - corresponding invalid-sample sentinel values used by the file format. - (This is the inverse of the transformation done by getvec() and - getframe(), so the effect is that at the application level, invalid - samples are always represented by the value WFDB_INVALID_SAMPLE.) - - Previous versions of the library did not perform this transformation - correctly for formats 80 and 160 (they used a value of zero rather than - -128 or -32768, respectively.) This bug was mainly an issue for - programs such as 'snip' and 'xform' that read and modify existing data - files. + When an application passes an array containing WFDB_INVALID_SAMPLE + values to putvec(), the function translates these into the + corresponding invalid-sample sentinel values used by the file format. + (This is the inverse of the transformation done by getvec() and + getframe(), so the effect is that at the application level, invalid + samples are always represented by the value WFDB_INVALID_SAMPLE.) + + Previous versions of the library did not perform this transformation + correctly for formats 80 and 160 (they used a value of zero rather than + -128 or -32768, respectively.) This bug was mainly an issue for + programs such as 'snip' and 'xform' that read and modify existing data + files. In multi-segment mode, 'snip' will attempt to copy the segment structure of the input record. In older versions, this was done by @@ -258,7 +289,7 @@ Thanks to Benjamin Moody for identifying the problem, providing test inputs, and contributions to the solution. - The -H option of app/sampfreq.c did not work in recent releases because + The -H option of app/sampfreq.c did not work in recent releases because of changes in the WFDB library; it works again in 10.5.7. 10.5.6 (29 November 2010): @@ -292,7 +323,7 @@ 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 + 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. @@ -324,7 +355,7 @@ omissions. An uninitialized flag may have made ahaecg2mit behave as if its -s - option was always given, and an informational message contained + option was always given, and an informational message contained a formatting error; both problems have been corrected, thanks to a report from Isaac Henry. @@ -354,10 +385,10 @@ 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 - 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 + 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"). - + 10.5.3 (22 June 2010): Function getgvmode() has been introduced in WFDB library version 10.5.3, to allow querying the current operating mode of getvec(). @@ -388,7 +419,7 @@ Certain malformed segment .hea files were able to cause null pointer errors in WFDB library function isigopen. This problem has been corrected. Thanks to Mauro Villarroel for reporting this problem - together with a test case. + together with a test case. The -s option of WFDB applications sqrs, sqrs125, wqrs, and wabp now works properly. A bug common to all four applications was introduced @@ -437,7 +468,7 @@ new functionality it provides. Memory allocation macros that have been defined previously in wfdblib.h - have been moved to wfdb.h so they are accessible to WFDB applications, + have been moved to wfdb.h so they are accessible to WFDB applications, including user-written applications. See the WFDB Programmer's Guide for information about using these macros (MEMERR, SFREE, SUALLOC, SALLOC, SREALLOC, and SSTRCPY). @@ -571,9 +602,9 @@ pschart and psfd now handle signal names in their command-line argument lists (following the '-s' option). - The version of wfdb2mat included in this release generates a .mat file - of the expected size even if the input ends prematurely. Thanks to - Weixiang Liu for a report that prompted this revision. + The version of wfdb2mat included in this release generates a .mat file + of the expected size even if the input ends prematurely. Thanks to + Weixiang Liu for a report that prompted this revision. 10.4.18 (15 March 2009): The WFDB library no longer reports spurious checksum errors when reading @@ -602,7 +633,7 @@ 10.4.15 (26 February 2009): WFDB library function mstimstr() now outputs time to the nearest millisecond, rather than truncating its calculation to the nearest - lower number of milliseconds. + lower number of milliseconds. A new WFDB library function, wfdbputprolog(), can write a prolog at the beginning of a signal file. WFDB applications ignore embedded prologs.