#include #include #define SIGTYPES "signal-types" struct psig_t { char *name; int found; char *units; WFDB_Gain gain; int fmt; int spf; int adcres; int adczero; int baseline; }; struct segd_t { char *recname; char basetime[32]; long nsamp; WFDB_Time start; WFDB_Time end; WFDB_Time useful_start; /* Sample number of first nonzero sample */ WFDB_Time useful_end; /* Sample number of last nonzero sample */ WFDB_Time final_start; /* First sample number to be used */ WFDB_Time final_end; /* Last sample number to be used */ WFDB_Time shift; }; int check_overlaps=0; int shift_overlaps=0; void memerr() { fprintf(stderr, "insufficient memory -- exiting\n"); exit(3); } char *copystr(char *oldstring) { char *newstring; if (oldstring == NULL || *oldstring == '\0') { if ((newstring = calloc(1, 1)) == NULL) memerr(); } else { if ((newstring = calloc(1, strlen(oldstring) + 1)) == NULL) memerr(); strcpy(newstring, oldstring); } return (newstring); } /* Return number of bytes per sample for given format */ int fmtfact(int fmt) { switch(fmt) { case 0: return 0; case 8: case 80: return 1; case 16: case 61: case 160: return 2; default: fprintf(stderr,"cannot use byte offsets with format %d\n", fmt); return 0; } } /* Find the start and end times for the given segment */ void get_lengths(int nsig, struct segd_t *seg) { WFDB_Sample *samples; WFDB_Time t, current; int i, found=0; seg->start = 0; seg->end = seg->nsamp; if (!check_overlaps) { seg->useful_start = seg->start; seg->useful_end = seg->end; return; } samples = malloc(nsig * sizeof(WFDB_Sample)); if (!samples) memerr(); t = 0; /* Find the earliest nonzero sample in any signal */ while (!found && getvec(samples) > 0) { for (i=0; i 5) seg->useful_start = t - 2; else seg->useful_start = 0; /* Search backwards from the end to find the latest nonzero sample */ current = seg->nsamp; do { current -= 1024; if (current < 0) current = 0; isigsettime(current); seg->useful_end = -1; for (t=current; tnsamp && t<(current+1024); t++) { if (0 > getvec(samples)) { exit(2); } for (i=0; iuseful_end = t + 1; } } while (seg->useful_end == -1 && current > 0); if (seg->useful_end == -1) seg->useful_end = 0; if (seg->useful_end > seg->nsamp - 5) seg->useful_end = seg->nsamp; free(samples); } char *usage = "usage: %s [-cs] [-o output-record] input-record ...\n" " -c: check for empty space at start and end of segments, and remove\n" " it if necessary\n" " -s: when adjacent segments overlap, shift the \"later\" segment\n" " forward in time (default is to drop the overlapping portion)\n" " Note: input records must be in chronological order!\n"; int main(int argc, char **argv) { static char buf[256], *p, *q, *sigtypes, *recordname, *filename; double *dv, *g, vf; FILE *sfile; int i, j, nsig, nosig, nseg, s, *smap, ss, t, tsig = 0; struct psig_t *psig = NULL; struct segd_t *seg; unsigned long seglen; WFDB_Sample *vin, *vout; WFDB_Siginfo *si, *so; WFDB_Time tt; char *outrecname = "out"; if ((seg = calloc(sizeof(struct segd_t), argc)) == NULL) memerr(); nseg = 0; for (i=1; i= tsig) { psig = realloc(psig, sizeof(struct psig_t) * ++tsig); g = realloc(g, sizeof(double) * tsig); if (psig == NULL || g == NULL) memerr(); psig[t].name = copystr(si[s].desc); } if (psig[t].found != 1) { psig[t].found = 1; psig[t].units = copystr(si[s].units); psig[t].gain = g[t] = si[s].gain; psig[t].fmt = si[s].fmt; psig[t].spf = si[s].spf; psig[t].adcres = si[s].adcres; psig[t].adczero = si[s].adczero; psig[t].baseline = si[s].baseline; } else { if (strcmp(psig[t].units, si[s].units)) fprintf(stderr, "warning: units of %s change\n", si[s].desc); if (psig[t].gain != si[s].gain) { if (si[s].gain > psig[t].gain) { psig[t].gain = si[s].gain; psig[t].baseline = si[s].baseline; } if (si[s].gain < g[t]) g[t] = si[s].gain; fprintf(stderr, "warning: gain of %s changes\n", si[s].desc); } if (psig[t].fmt != si[s].fmt) fprintf(stderr, "warning: format of %s changes\n", si[s].desc); if (psig[t].spf != si[s].spf) fprintf(stderr, "warning: spf of %s changes\n", si[s].desc); if (psig[t].adcres != si[s].adcres) fprintf(stderr, "warning: adcres of %s changes\n", si[s].desc); if (psig[t].adczero != si[s].adczero) fprintf(stderr, "warning: adczero of %s changes\n", si[s].desc); if (psig[t].baseline != si[s].baseline) fprintf(stderr, "warning: baseline of %s changes\n", si[s].desc); } } wfdbquit(); } /* Determine the final starting and ending points for the segments */ if ((si = calloc(sizeof(WFDB_Siginfo), tsig)) == NULL) memerr(); if ((nsig = isigopen(seg[0].recname, si, -tsig)) < 1) exit(2); for (i = 1; i < nseg; i++) { tt = strtim(seg[i].basetime); if (tt < 0) tt = -tt; seg[i].start += tt; seg[i].end += tt; seg[i].useful_start += tt; seg[i].useful_end += tt; if (seg[i].useful_start < seg[i-1].useful_end) { if (shift_overlaps) { fprintf(stderr, "%s: cannot resolve overlap between %s and %s, shifting forward by %s\n", argv[0], seg[i-1].recname, seg[i].recname, mstimstr(seg[i-1].useful_end - seg[i].useful_start)); seg[i].shift = seg[i-1].useful_end - seg[i].useful_start; seg[i].start += seg[i].shift; seg[i].end += seg[i].shift; seg[i].useful_start += seg[i].shift; seg[i].useful_end += seg[i].shift; seg[i].final_start = seg[i].useful_start; seg[i-1].final_end = seg[i-1].useful_end; } else { fprintf(stderr, "%s: cannot resolve overlap between %s and %s, discarding %s of earlier record\n", argv[0], seg[i-1].recname, seg[i].recname, mstimstr(seg[i-1].useful_end - seg[i].useful_start)); seg[i].final_start = seg[i-1].final_end = seg[i].useful_start; } } else if (seg[i].start < seg[i-1].useful_end) seg[i].final_start = seg[i-1].final_end = seg[i-1].useful_end; else if (seg[i].start <= seg[i-1].end) seg[i].final_start = seg[i-1].final_end = seg[i].start; else { // insert empty segment nseg++; seg = realloc(seg, nseg*sizeof(struct segd_t)); if (!seg) memerr(); for (j=(nseg-1); j>i; j--) memcpy(&(seg[j]), &(seg[j-1]), sizeof(struct segd_t)); seg[i].recname = "~"; seg[i].final_start = seg[i].useful_start = seg[i].start = seg[i-1].end; seg[i].final_end = seg[i].useful_end = seg[i].end = seg[i+1].start; seg[i+1].final_start = seg[i+1].start; seg[i-1].final_end = seg[i-1].end; i++; } } seg[0].final_start = 0; seg[nseg-1].final_end = seg[nseg-1].end; for (i=0; i 1.0) { so[t].adcres++; dg /= 2.0; } if (so[t].adcres > 16) { /* The highest precision supported by the current version of the WFDB library is 16 bits. If we are here, the gain of signal t has varied so much that we cannot represent the smallest quantization steps in 16 bits. */ fprintf(stderr, "warning: there may be loss of precision" " in the %s signal\n", so[t].desc); while (--so[t].adcres > 16) so[t].gain /= 2.0; } } so[t].adczero = psig[s].adczero; so[t].baseline = psig[s].baseline; so[t].nsamp = 0; so[t].cksum = 0; so[t].initval = so[t].adczero - (1 << (so[t].adcres-1)); t++; } nosig = t; snprintf(buf, sizeof(buf), "%s_layout", outrecname); if (0>setheader(buf, so, nosig)) exit(4); FILE *heafile, *subheafile; char newbasetime[256]; char *info, **infostrs; int ninfostrs; int *groupcount; snprintf(buf, sizeof(buf), "%s.hea", outrecname); heafile = fopen(buf, "wb"); if (!heafile) { perror(buf); exit(4); } strcpy(newbasetime, mstimstr(0)); newbasetime[strlen(newbasetime)-1] = 0; fprintf(heafile,"%s/%d %d %lg %ld %s\r\n", outrecname, nseg+1, nosig, (double) sampfreq(NULL), (long) seg[nseg-1].final_end, newbasetime+1); fprintf(heafile,"%s_layout 0\r\n",outrecname); wfdbquit(); for (i=0; i 0) { strcpy(newbasetime, mstimstr(seg[i].start - seg[i].shift - seg[i].final_start)); } else { strcpy(newbasetime, seg[i].basetime); } newbasetime[strlen(newbasetime)-1] = 0; infostrs = NULL; ninfostrs = 0; if (info = getinfo(seg[i].recname)) { infostrs = malloc(sizeof(char*)); if (!infostrs) memerr(); ninfostrs = 1; infostrs[0] = copystr(info); while (info = getinfo(NULL)) { infostrs = realloc(infostrs, (++ninfostrs)*sizeof(char*)); if (!infostrs) memerr(); infostrs[ninfostrs-1] = copystr(info); } } groupcount = calloc(nsig, sizeof(int)); if (!groupcount) memerr(); for (j=0; j 1) fprintf(subheafile, "x%d", si[j].spf); if (seg[i].final_start > seg[i].start) fprintf(subheafile, "+%d", fmtfact(si[j].fmt) * (seg[i].final_start - seg[i].start) * groupcount[si[j].group]); fprintf(subheafile, " %lg", (double) si[j].gain); if (si[j].baseline != si[j].adczero) fprintf(subheafile, "(%d)", si[j].baseline); fprintf(subheafile, "/%s %d %d %d %d %d %s\r\n", si[j].units, si[j].adcres, si[j].adczero, si[j].initval, si[j].cksum, si[j].bsize, si[j].desc); } if (seg[i].shift) fprintf(subheafile, "# Shifted by %d\r\n", seg[i].shift); if (seg[i].final_start > seg[i].start) fprintf(subheafile, "# %d samples removed from start\r\n", seg[i].final_start - seg[i].start); if (seg[i].end > seg[i].final_end) fprintf(subheafile, "# %d samples removed from end\r\n", seg[i].end - seg[i].final_end); seg[i].basetime[strlen(seg[i].basetime)-1] = 0; fprintf(subheafile, "# %s %d %lg %ld %s\r\n# -----\r\n", seg[i].recname, nsig, (double) sampfreq(NULL), (long) seg[i].end - seg[i].start, seg[i].basetime+1); for (j=0; j