/* file: stepdet.c G. Moody 28 February 2014 Last revised: 24 April 2020 ------------------------------------------------------------------------------- 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, 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/). _______________________________________________________________________________ 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; WFDB_Time 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 (gvmode & WFDB_HIGHRES) setafreq(sampfreq(NULL)); 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]); }