/* file: lcheck.c G. Moody 7 September 2001 Last revised: 18 May 2022 ------------------------------------------------------------------------------- wfdbcheck: test WFDB library Copyright (C) 2001-2010 George B. Moody This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . You may contact the author by e-mail (wfdb@physionet.org) or postal mail (MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software, please visit PhysioNet (http://www.physionet.org/). _______________________________________________________________________________ */ #include #include char *info, *pname, *prog_name(); int n, nsig, i, j, framelen, errors = 0, istat, vflag = 0; char headerversion[40]; char *libversion; char *p, *q, *defpath, *dbpath; WFDB_Anninfo aiarray[2]; WFDB_Annotation annot; WFDB_Calinfo cal; WFDB_Siginfo *si; WFDB_Sample *vector; void help(), list_untested(); void check_annotations(char *record); void check_signals(char *record, char *orec, int fmt, int split_info); main(argc, argv) int argc; char *argv[]; { pname = prog_name(argv[0]); if (argc > 1) { if (strcmp(argv[1], "-v") == 0) vflag = 1; else if (strcmp(argv[1], "-V") == 0) vflag = 2; else { help(); exit(1); } } if ((p = wfdberror()) == NULL) { printf("Error: wfdberror() did not return a version string\n"); p = "unknown version of the WFDB library"; errors++; } libversion = calloc(sizeof(char), strlen(p)+1); strcpy(libversion, p); /* Print the library version number and date. */ fprintf(stderr, "Testing %s", libversion); /* Check that the installed matches the library. */ sprintf(headerversion, "WFDB library version %d.%d.%d", WFDB_MAJOR, WFDB_MINOR, WFDB_RELEASE); if (strncmp(libversion, headerversion, strlen(headerversion))) { printf("Error: Library version does not match \n" " ( is from %s)\n", headerversion); errors++; } else if (vflag) printf("[OK]: Library version matches \n"); /* If in verbose mode, print library defaults. */ if (vflag) { static int format[WFDB_NFMTS] = WFDB_FMT_LIST; printf("[OK]: WFDB %s NETFILES\n", WFDB_NETFILES ? "supports" : "does not support"); printf("[OK]: WFDB_MAXANN = %d\n", WFDB_MAXANN); printf("[OK]: WFDB_MAXSIG = %d\n", WFDB_MAXSIG); printf("[OK]: WFDB_MAXSPF = %d\n", WFDB_MAXSPF); printf("[OK]: WFDB_MAXRNL = %d\n", WFDB_MAXRNL); printf("[OK]: WFDB_MAXUSL = %d\n", WFDB_MAXUSL); printf("[OK]: WFDB_MAXDSL = %d\n", WFDB_MAXDSL); printf("[OK]: Signal formats = {"); for (i = 0; i < WFDB_NFMTS; i++) printf("%d%s", format[i], (i < WFDB_NFMTS-1) ? ", " : "}\n"); printf("[OK]: WFDB_DEFFREQ = %g\n", WFDB_DEFFREQ); printf("[OK]: WFDB_DEFGAIN = %g\n", WFDB_DEFGAIN); printf("[OK]: WFDB_DEFRES = %d\n", WFDB_DEFRES); } /* Test the WFDB library functions. */ aiarray[0].name = "atr"; aiarray[0].stat = WFDB_READ; aiarray[1].name = "chk"; aiarray[1].stat = WFDB_WRITE; /* *** getwfdb *** */ if ((p = getwfdb()) == NULL) { printf("Error: No default WFDB path defined\n"); defpath = ""; errors++; } else { defpath = calloc(sizeof(char), strlen(p)+1); strcpy(defpath, p); if (vflag) printf("[OK]: Default WFDB path = %s\n", defpath); } /* *** setwfdb *** */ dbpath = calloc(sizeof(char), strlen(defpath)+10); sprintf(dbpath, "data %s\n", defpath); setwfdb(dbpath); if ((p = getwfdb()) == NULL || strcmp(p, dbpath)) { printf("Error: Could not set WFDB path\n"); errors++; } else if (vflag) printf("[OK]: WFDB path modified successfully\n"); /* *** calopen *** */ i = calopen(NULL); if (i != 0) { printf("Error: Could not open calibration list, calopen returned %d\n", i); errors++; } else if (vflag) printf("[OK]: WFDB calibration list opened successfully\n"); /* *** getcal *** */ i = getcal("NBPfoo", "mmHg", &cal); if (i != 0 || cal.low != 0.0 || cal.high != 100.0 || cal.scale != 100.0 || strcmp(cal.sigtype, "NBP") != 0 || strcmp(cal.units, "mmHg") != 0 || cal.caltype != (WFDB_DC_COUPLED | WFDB_CAL_SQUARE)) { printf("Error: getcal returned %d, cal = { %g %g %g %s %s %d }\n", i, cal.low, cal.high, cal.scale, cal.sigtype, cal.units, cal.caltype); errors++; } else if (vflag) printf("[OK]: getcal was successful\n"); /* *** putcal *** */ cal.sigtype = "foobar"; i = putcal(&cal); if (i != 0) { printf("Error: putcal returned %d\n", i); errors++; } else if (vflag) printf("[OK]: putcal returned 0\n"); i = getcal("foobar", "mmHg", &cal); if (i != 0 || cal.low != 0.0 || cal.high != 100.0 || cal.scale != 100.0 || strcmp(cal.sigtype, "foobar") != 0 || strcmp(cal.units, "mmHg") != 0 || cal.caltype != (WFDB_DC_COUPLED | WFDB_CAL_SQUARE)) { printf("Error: putcal failed, cal = { %g %g %g %s %s %d }\n", cal.low, cal.high, cal.scale, cal.sigtype, cal.units, cal.caltype); errors++; } else if (vflag) printf("[OK]: putcal was successful\n"); /* *** newcal *** */ i = newcal("lcheck_cal"); if (i != 0) { printf("Error: newcal returned %d\n", i); errors++; } else if (vflag) printf("[OK]: newcal was successful\n"); /* Test I/O using the local record first, converting signals to another format and back. */ check_annotations("100s"); #ifdef WFDB_FLAC_SUPPORT check_signals("100s", "100y", 516, 1); #else check_signals("100s", "100y", 16, 1); #endif check_signals("100y", "100z", 212, 0); /* Test I/O again using the remote record. */ if (WFDB_NETFILES) { if (vflag) printf("[OK]: Repeating tests using NETFILES"); if (strstr(defpath, "://") == NULL) { fprintf(stderr, "\nWarning: default WFDB path does not include an http:// component\n"); setwfdb(". http://www.physionet.org/physiobank/database"); printf("WFDB path has been set to: %s\n", getwfdb()); } else if (vflag) { printf(" (reverting to default WFDB path)\n"); setwfdb(defpath); } check_annotations("udb/100s"); check_signals("udb/100s", "udb/100z", -1, 0); } /* If there were any errors detected by the WFDB library but not by this program, this test will pick them up. */ if (errors == 0) { if ((p = wfdberror()) == NULL) { printf("Error: wfdberror() did not return a version string\n"); p = "unknown version of the WFDB library"; errors++; } if (strcmp(libversion, p)) { printf("Error: WFDB library error(s) not detected by %s\n", pname); printf(" Last library error was: '%s'\n", p); errors++; } else if (vflag) printf("[OK]: no WFDB library errors\n"); } /* In this section, test functions that can only be checked by looking for library errors. */ /* *** flushcal *** */ flushcal(); i = getcal("foobar", "mmHg", &cal); if (i == 0) { printf("Error: flushcal did not empty the calibration list\n"); errors++; } else if (vflag) printf("[OK]: flushcal was successful\n"); /* Summarize the results and exit. */ if (errors) printf("%d error%s: test failed\n", errors, errors > 1 ? "s" :""); else if (vflag) printf("no errors: test succeeded\n"); if (vflag == 2) list_untested(); exit(errors); } void check_annotations(char *record) { WFDB_Date d; WFDB_Frequency f; WFDB_Time t; /* *** sampfreq *** */ if ((f = sampfreq(NULL)) != 0.0) { printf("Error: sampfreq(NULL) returned %g (should have been 0)\n", f); errors++; } else if (vflag) printf("[OK]: sampfreq(NULL) returned %g\n", f); /* *** setsampfreq *** */ if (istat = setsampfreq(100.0)) { printf("Error: setsampfreq returned %d (should have been 0)\n", istat); errors++; } if (sampfreq(NULL) != 100.0) { printf("Error: failed to set sampling frequency using setsampfreq\n"); errors++; } else if (vflag) printf("[OK]: setsampfreq changed sampling frequency successfully\n"); /* *** sampfreq, again *** */ if ((f = sampfreq(record)) != 360.0) { printf("Error: sampfreq(%s) returned %g (should have been 360)\n", record, f); errors++; } else if (vflag) printf("[OK]: sampfreq(%s) returned %g\n", record, f); /* *** annopen *** */ istat = annopen(record, aiarray, 1); if (istat) { fprintf(stderr, "Error: annopen of 1 file returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: annopen of 1 file succeeded\n"); istat = annopen(record, aiarray, 2); if (istat) { fprintf(stderr, "Error: annopen of 2 files returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: annopen of 2 files succeeded\n"); /* *** strecg, ecgstr *** */ i = strecg("N"); if (i != 1) { printf("Error: strecg returned %d (should have been 1)\n", i); errors++; } else if (vflag) printf("[OK]: strecg returned %d\n", i); p = ecgstr(i); if (strcmp(p, "N")) { printf("Error: ecgstr returned '%s' (should have been 'N')\n", p); errors++; } else if (vflag) printf("[OK]: ecgstr returned '%s'\n", p); /* *** strann, annstr, anndesc *** */ i = strann("N"); if (i != 1) { printf("Error: strann returned %d (should have been 1)\n", i); errors++; } else if (vflag) printf("[OK]: strann returned %d\n", i); p = annstr(i); if (strcmp(p, "N")) { printf("Error: annstr returned '%s' (should have been 'N')\n", p); errors++; } else if (vflag) printf("[OK]: annstr returned '%s'\n", p); p = anndesc(i); if (strcmp(p, "Normal beat")) { printf("Error: anndesc returned '%s' (should have been 'Normal beat')\n", p); errors++; } else if (vflag) printf("[OK]: anndesc returned '%s'\n", p); /* *** setecgstr, setannstr, setanndesc *** */ i = setecgstr(1, "X"); p = ecgstr(1); if (i != 0 || strcmp(p, "X")) { printf("Error: setecgstr failed\n"); errors++; } else if (vflag) printf("[OK]: setecgstr succeeded\n"); i = setannstr(-1, "Y"); p = annstr(1); if (i != 0 || strcmp(p, "Y")) { printf("Error: setannstr failed\n"); errors++; } else if (vflag) printf("[OK]: setannstr succeeded\n"); i = setanndesc(-1, "ZZ zz"); p = anndesc(1); if (i != 0 || strcmp(p, "ZZ zz")) { printf("Error: setanndesc failed\n"); errors++; } else if (vflag) printf("[OK]: setanndesc succeeded\n"); /* *** strtim, timstr *** */ t = strtim("0:5"); if (t != (WFDB_Time)(5.0 * f)) { printf("Error: strtim returned %"WFDB_Pd_TIME " (should have been %"WFDB_Pd_TIME")\n", t, (WFDB_Time)(5.0 * f)); errors++; } else if (vflag) printf("[OK]: strtim returned %"WFDB_Pd_TIME"\n", t); p = timstr(t); q = " 0:05"; if (strcmp(p, q)) { printf("Error: timstr returned '%s' (should have been '%s')\n", p, q); errors++; } else if (vflag) printf("[OK]: timstr returned '%s'\n", p); /* *** strdat, datstr *** */ q = " 31/12/1999"; d = strdat(q); if (d != 2451544L) { printf("Error: strdat returned %ld (should have been 2451544)\n", d); errors++; } else if (vflag) printf("[OK]: strdat returned %ld\n", d); p = datstr(d); if (strcmp(p, q)) { printf("Error: datstr returned '%s' (should have been '%s')\n", p, q); errors++; } else if (vflag) printf("[OK]: datstr returned '%s'\n", p); /* *** iannsettime *** */ istat = iannsettime(t); if (istat) { printf("Error: iannsettime returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: iannsettime skipping forward to %s\n", timstr(t)); /* *** getann, stimstr *** */ for (i = 0; i < 5; i++) { istat = getann(0, &annot); if (istat != 0 && istat != -1) { printf("Error: getann returned %d (should have been 0 or -1)\n", istat); errors++; } else if (vflag) printf("[OK]: getann read: {%s %d %d %d} at %s (%"WFDB_Pd_TIME")\n", annstr(annot.anntyp), annot.subtyp, annot.chan, annot.num, mstimstr(annot.time), annot.time); } /* *** iannsettime, again *** */ istat = iannsettime(0L); if (istat) { printf("Error: iannsettime returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: iannsettime skipping backward to %s\n", timstr(0L)); /* *** getann, putann *** */ i = j = istat = 0; while (istat == 0) { istat = getann(0, &annot); if (istat != 0 && istat != -1) { printf("Error: getann returned %d (should have been 0 or -1)\n", istat); errors++; } else if (istat == 0) { i++; istat = putann(0, &annot); if (istat != 0) { printf("Error: putann returned %d (should have been 0)\n", istat); errors++; } else j++; } } if (vflag) printf("[OK]: %d annotations read, %d written\n", i, j); } void check_signals(char *record, char *orec, int ofmt, int split_info) { WFDB_Frequency f; WFDB_Time t, tt; double x; /* *** isigopen *** */ /* Get the number of signals without opening any signal files. */ n = isigopen(record, NULL, 0); if (n != 2) { fprintf(stderr, "Error: isigopen(%s, NULL, 0) returned %d (should have been 2)\n", record, n); errors++; } else if (vflag) printf("[OK]: isigopen(%s, NULL, 0) succeeded\n", record); /* Allocate WFDB_Siginfo structures before calling isigopen again. */ si = calloc(n, sizeof(WFDB_Siginfo)); nsig = isigopen(record, si, n); /* Note that nsig equals n only if all signals were readable. */ /* Get the number of samples per frame. */ for (i = framelen = 0; i < nsig; i++) framelen += si[i].spf; /* Allocate WFDB_Samples before calling getframe. */ vector = calloc(framelen, sizeof(WFDB_Sample)); for (t = 0L; t < 5L; t++) { if ((n = getframe(vector)) != nsig) { printf("Error: getframe returned %d (should have been %d)\n", n, nsig); errors++; break; } else if (vflag) { printf("[OK]: (at %s) getframe returned {", mstimstr(t)); for (i = 0; i < framelen; i++) printf("%5d%s", vector[i], i < framelen-1 ? ", " : "}\n"); } } /* *** aduphys, physadu *** */ x = aduphys((WFDB_Signal)0, (WFDB_Sample)1000); if (x > -0.119999999 || x < -0.120000001) { printf("Error: aduphys returned %g (should have been -0.12)\n", x); errors++; } else if (vflag) printf("[OK]: aduphys returned %g\n", x); i = physadu((WFDB_Signal)0, x); if (i != (WFDB_Sample)1000) { printf("Error: physadu returned %d (should have been 1000)\n", i); errors++; } else if (vflag) printf("[OK]: physadu returned %d\n", i); /* *** adumuv, muvadu *** */ i = adumuv((WFDB_Signal)0, (WFDB_Sample)1000); if (i != 5000) { printf("Error: adumuv returned %d (should have been 5000)\n", i); errors++; } else if (vflag) printf("[OK]: adumuv returned %d\n", i); i = muvadu((WFDB_Signal)0, i); if (i != (WFDB_Sample)1000) { printf("Error: muvadu returned %d (should have been 1000)\n", i); errors++; } else if (vflag) printf("[OK]: muvadu returned %d\n", i); /* *** sampfreq *** */ f = sampfreq(NULL); if (f != 360.0) { printf("Error: sampfreq returned %g (should have been 360)\n", f); errors++; } else if (vflag) printf("[OK]: sampfreq returned %g\n", f); /* *** strtim *** */ t = strtim("0:20"); if (t != (WFDB_Time)(20.0 * f)) { printf("Error: strtim returned %"WFDB_Pd_TIME " (should have been %"WFDB_Pd_TIME")\n", t, (WFDB_Time)(20.0 * f)); errors++; } else if (vflag) printf("[OK]: strtim returned %"WFDB_Pd_TIME"\n", t); p = timstr(t); q = " 0:20"; if (strcmp(p, q)) { printf("Error: timstr returned '%s' (should have been '%s')\n", p, q); errors++; } else if (vflag) printf("[OK]: timstr returned '%s'\n", p); /* *** isigsettime *** */ istat = isigsettime(t); if (istat) { printf("Error: isigsettime returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: isigsettime skipping forward to %s\n", timstr(t)); /* *** getvec *** */ for (tt = t; t < tt+5L; t++) { if ((n = getvec(vector)) != nsig) { printf("Error: getvec returned %d (should have been %d)\n", n, nsig); errors++; break; } else if (vflag) { printf("[OK]: (at %s) getvec returned {", mstimstr(t)); for (i = 0; i < nsig; i++) printf("%5d%s", vector[i], i < nsig-1 ? ", " : "} ["); for (i = 0; i < nsig; i++) printf("%5.3lf%s", aduphys(i, vector[i]), i < nsig-1?", ":"]\n"); } } /* *** isigsettime *** */ t = tt-1; /* try a backward skip, to one sample before the previous set */ istat = isigsettime(t); if (istat) { printf("Error: isigsettime returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: isigsettime skipping backward to %s\n", mstimstr(t)); /* *** getframe, again *** */ for ( ; t < tt+5L; t++) { if ((n = getframe(vector)) != nsig) { printf("Error: getframe returned %d (should have been %d)\n", n, nsig); errors++; break; } else if (vflag) { printf("[OK]: (at %s) getframe returned {", mstimstr(t)); for (i = 0; i < framelen; i++) printf("%5d%s", vector[i], i < framelen-1 ? ", " : "}\n"); } } /* Now return to the beginning of the record and copy it. */ istat = isigsettime(t = 0L); if (istat) { printf("Error: isigsettime returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: isigsettime skipping backward to %s\n", mstimstr(t)); /* *** osigfopen *** */ for (i = 0; i < nsig; i++) { si[i].fname = realloc(si[i].fname, strlen(orec) + 5); sprintf(si[i].fname, "%s.dat", orec); if (ofmt >= 0) si[i].fmt = ofmt; } istat = osigfopen(si, nsig); if (istat != nsig) { printf("Error: osigfopen returned %d (should have been %d)\n", istat, nsig); errors++; } else if (vflag) printf("[OK]: osigfopen returned %d\n", istat); /* *** getframe (again), putvec *** */ while ((n = getframe(vector)) == nsig) { t++; if ((istat = putvec(vector)) != nsig) { printf("Error: putvec returned %d (should have been %d)\n", istat, nsig); errors++; break; } } if (n != -1) { /* some error occurred while reading samples */ printf("Error: getframe returned %d (should have been %d) at %s\n", n, nsig, mstimstr(t)); errors++; } else if (vflag) /* getframe reached EOF, checksums OK */ printf("[OK]: getframe read %"WFDB_Pd_TIME" samples\n", t); if (istat != nsig) { /* some error occurred while writing samples */ printf("Error: putvec returned %d (should have been %d) at %s\n", istat, nsig, mstimstr(t)); errors++; } else if (vflag) /* putvec wrote all samples without apparent error */ printf("[OK]: putvec wrote %"WFDB_Pd_TIME" samples\n", t); /* *** newheader *** */ istat = newheader(orec); if (istat) { /* some error occurred while writing the header */ printf("Error: newheader returned %d (should have been 0)\n", istat); errors++; } else if (vflag) /* putvec wrote all samples without apparent error */ printf("[OK]: newheader created header for output record %s\n", orec); /* *** getinfo, setinfo, putinfo *** */ n = 0; if (info = getinfo(record)) { if (split_info) { istat = setinfo(orec); if (istat) { printf("Error: setinfo returned %d (should have been 0)\n", istat); errors++; } else if (vflag) printf("[OK]: setinfo created info for output record %s\n", orec); } do { istat = putinfo(info); if (istat) { printf("Error: putinfo returned %d (should have been 0)\n", istat); errors++; } else n++; } while (info = getinfo(NULL)); if (vflag) printf("[OK]: %d info strings copied to record %s header\n", n, orec); } wfdbquit(); setecgstr(1, "N"); setannstr(-1, "N"); setanndesc(-1, "Normal beat"); } 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 [OPTIONS ...]\n", " -v verbose mode", " -V verbose mode, also list untested functions", 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]); } void list_untested() { printf( "This program does not (yet) test the following WFDB library functions:\n"); printf("osigopen\n"); printf("wfdbinit\n"); printf("getspf\n"); printf("setgvmode\n"); printf("ungetann\n"); printf("isgsettime\n"); printf("iannclose\n"); printf("oannclose\n"); printf("setheader\n"); printf("setmsheader\n"); printf("wfdbgetskew\n"); printf("wfdbsetskew\n"); printf("wfdbgetstart\n"); printf("wfdbsetstart\n"); printf("getcfreq\n"); printf("setcfreq\n"); printf("getbasecount\n"); printf("setbasecount\n"); printf("setbasetime\n"); printf("wfdbquiet\n"); printf("wfdbverbose\n"); printf("setibsize\n"); printf("setobsize\n"); printf("wfdbfile\n"); printf("wfdbflush\n"); printf("setifreq\n"); printf("getifreq\n"); }