/* file: init.c G. Moody 1 May 1990 Last revised: 18 November 2013 Initialization functions for WAVE ------------------------------------------------------------------------------- WAVE: Waveform analyzer, viewer, and editor Copyright (C) 1990-2013 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 "wave.h" #include "xvwave.h" #include static WFDB_Siginfo *df; static int maxnsig; void memerr() { #ifdef NOTICE Xv_notice notice = xv_create((Frame)frame, NOTICE, XV_SHOW, TRUE, NOTICE_MESSAGE_STRINGS, "Insufficient memory", 0, NOTICE_BUTTON_YES, "Continue", NULL); xv_destroy_safe(notice); #else (void)notice_prompt((Frame)frame, (Event *)NULL, NOTICE_MESSAGE_STRINGS, "Insufficient memory", 0, NOTICE_BUTTON_YES, "Continue", NULL); #endif } void alloc_sigdata(ns) int ns; { int i; if ((df = realloc(df, ns * sizeof(WFDB_Siginfo))) == NULL || (signame = realloc(signame, ns * sizeof(char *))) == NULL || (sigunits = realloc(sigunits, ns * sizeof(char *))) == NULL || (calibrated = realloc(calibrated, ns * sizeof(char))) == NULL || (scope_v = realloc(scope_v, ns * sizeof(WFDB_Sample))) == NULL || (vref = realloc(vref, ns * sizeof(WFDB_Sample))) == NULL || (level_v = realloc(level_v, ns * sizeof(WFDB_Sample))) == NULL || (v = realloc(v, ns * sizeof(WFDB_Sample))) == NULL || (v0 = realloc(v0, ns * sizeof(WFDB_Sample))) == NULL || (vmax = realloc(vmax, ns * sizeof(WFDB_Sample))) == NULL || (vmin = realloc(vmin, ns * sizeof(WFDB_Sample))) == NULL || (vvalid = realloc(vvalid, ns * sizeof(int))) == NULL || (level_name_string = realloc(level_name_string, ns * sizeof(char **))) == NULL || (level_value_string = realloc(level_value_string, ns * sizeof(char **))) == NULL || (level_units_string = realloc(level_units_string, ns * sizeof(char **))) == NULL || (vscale = realloc(vscale, ns * sizeof(double))) == NULL || (vmag = realloc(vmag, ns * sizeof(double))) == NULL || (dc_coupled = realloc(dc_coupled, ns * sizeof(int))) == NULL || (sigbase = realloc(sigbase, ns * sizeof(int))) == NULL || (blabel = realloc(blabel, ns * sizeof(char *))) == NULL || (level_name = realloc(level_name, ns * sizeof(Panel_item))) == NULL || (level_value = realloc(level_value, ns * sizeof(Panel_item))) == NULL || (level_units = realloc(level_units, ns * sizeof(Panel_item))) == NULL) { memerr(); } for (i = maxnsig; i < ns; i++) { signame[i] = sigunits[i] = blabel[i] = NULL; level_name[i] = level_value[i] = level_units[i] = (Panel_item)NULL; dc_coupled[i] = scope_v[i] = vref[i] = level_v[i] = v[i] = v0[i] = vmax[i] = vmin[i] = 0; vscale[i] = vmag[i] = 1.0; if ((level_name_string[i] = calloc(1, 12)) == NULL || (level_value_string[i] = calloc(1, 12)) == NULL || (level_units_string[i] = calloc(1, 12)) == NULL) { memerr(); } } maxnsig = ns; } /* Open up a new ECG record. */ int record_init(s) char *s; { char ts[RNLMAX+30]; int i, rebuild_list, tl; /* Suppress error messages from the WFDB library. */ wfdbquiet(); /* Do nothing if the current annotation list has been edited and the changes can't be saved. */ if (post_changes() == 0) return (0); /* Check to see if the signal list must be rebuilt. Normally, this is done whenever the signal list is empty or if the record name has changed, but accept_remote_command (see xvwave.c) can force record_init not to rebuild the signal list by setting freeze_siglist. */ if (freeze_siglist) freeze_siglist = rebuild_list = 0; else rebuild_list = (siglistlen == 0) | strcmp(record, s); /* Save the name of the new record in local storage. */ strncpy(record, s, RNLMAX); /* Reset the frame title. */ set_frame_title(); /* Open as many signals as possible. */ nsig = isigopen(record, NULL, 0); if (nsig > maxnsig) alloc_sigdata(nsig); nsig = isigopen(record, df, nsig); /* Get time resolution for annotations in sample intervals. Except in WFDB_HIGHRES mode (selected using the -H option), the resolution is 1 sample interval. In WFDB_HIGHRES mode, when editing a multi-frequency record, the resolution for annotation times is 1 frame interval (i.e., getspf() sample intervals). */ atimeres = getspf(); /* By convention, a zero or negative sampling frequency is interpreted as if the value were WFDB_DEFFREQ (from wfdb.h); the units are samples per second per signal. */ if (nsig < 0 || (freq = sampfreq(NULL)) <= 0.) freq = WFDB_DEFFREQ; setifreq(freq); /* Inhibit the output of the 'time resolution' comment annotation unless we are operating in high-resolution mode. */ if ((getgvmode() & WFDB_HIGHRES) == 0) setafreq(0.); /* Quit if isigopen failed. */ if (nsig < 0) sprintf(ts, "Record %s is unavailable\n", record); if (nsig < 0) { #ifdef NOTICE Xv_notice notice = xv_create((Frame)frame, NOTICE, XV_SHOW, TRUE, #else (void)notice_prompt((Frame)frame, (Event *)NULL, #endif NOTICE_MESSAGE_STRINGS, ts, 0, NOTICE_BUTTON_YES, "Continue", 0); #ifdef NOTICE xv_destroy_safe(notice); #endif return (0); } /* If the record has a low sampling rate, use coarse time scale and grid mode. */ if (freq <= 10.0) { tsa_index = coarse_tsa_index; grid_mode = coarse_grid_mode; mode_undo(); set_modes(); } else { tsa_index = fine_tsa_index; grid_mode = fine_grid_mode; mode_undo(); set_modes(); } /* Set signal name pointers. Shorten the conventional "record x, signal n" to "signal n". */ sprintf(ts, "record %s, ", record); tl = strlen(ts); for (i = 0; i < nsig; i++) { if (strncmp(df[i].desc, ts, tl) == 0) signame[i] = df[i].desc + tl; else signame[i] = df[i].desc; if (df[i].units == NULL || *df[i].units == '\0') sigunits[i] = "mV"; else sigunits[i] = df[i].units; /* Replace any unspecified signal gains with the default gain from wfdb.h; the units of gain are ADC units per physical unit. */ if (df[i].gain == 0) { calibrated[i] = 0; df[i].gain = WFDB_DEFGAIN; } else calibrated[i] = 1; } /* Set range for signal selection on analyze panel. */ reset_maxsig(); /* Initialize the signal list unless the new record name matches the old one. */ if (rebuild_list) { if (nsig > maxsiglistlen) { siglist = realloc(siglist, nsig * sizeof(int)); base = realloc(base, nsig * sizeof(int)); level = realloc(level, nsig * sizeof(XSegment)); maxsiglistlen = nsig; } for (i = 0; i < nsig; i++) siglist[i] = i; siglistlen = nsig; reset_siglist(); } /* Calculate the base levels (in display units) for each signal, and for annotation display. */ set_baselines(); tmag = 1.0; vscale[0] = 0.; /* force clear_cache() -- see calibrate() */ calibrate(); /* Rebuild the level window (see edit.c) */ recreate_level_popup(); return (1); } /* Set_baselines() determines the ordinates for the signal baselines and for annotation display. Note that the signals are drawn centered about the calculated baselines (i.e., the baselines bear no fixed relationship to the sample values). */ void set_baselines() { int i; if (sig_mode == 0) for (i = 0; i < nsig; i++) base[i] = canvas_height*(2*i+1.)/(2.*nsig); else for (i = 0; i < siglistlen; i++) base[i] = canvas_height*(2*i+1.)/(2.*siglistlen); if (i > 1) abase = (base[i/2] + base[i/2-1])/2; else if (nsig > 0) abase = canvas_height*4/5; else abase = canvas_height/2; } /* Calibrate() sets scales for the display. Ordinate (amplitude) scaling is determined for each signal from the gain recorded in the header file, and from the display scales in the calibration specification file, but calibrate() scales the abscissa (time) for all signals based on the sampling frequency for signal 0. */ void calibrate() { int i; extern char *getenv(); struct WFDB_calinfo ci; /* vscale is a multiplicative scale factor that converts sample values to window ordinates. Since window ordinates are inverted, vscale includes a factor of -1. */ if (vscale[0] == 0.0) { clear_cache(); /* If specified, read the calibration file to get standard scales. */ if ((cfname == (char *)NULL) && (cfname = getenv("WFDBCAL"))) calopen(cfname); for (i = 0; i < nsig; i++) { vscale[i] = - vmag[i] * millivolts(1) / df[i].gain; dc_coupled[i] = 0; if (getcal(df[i].desc, df[i].units, &ci) == 0 && ci.scale != 0.0) { vscale[i] /= ci.scale; if (ci.caltype & 1) { dc_coupled[i] = 1; sigbase[i] = df[i].baseline; if (blabel[i] = (char *)malloc(strlen(ci.units) + strlen(df[i].desc) + 6)) sprintf(blabel[i], "0 %s (%s)", ci.units, df[i].desc); } } } } /* vscalea is used in the same way as vscale, but only when displaying the annotation 'num' fields as a signal. */ vscalea = - millivolts(1); if (af.name && getcal(af.name, "units", &ci) == 0 && ci.scale != 0) vscalea /= ci.scale; else if (getcal("ann", "units", &ci) == 0 && ci.scale != 0) vscalea /= ci.scale; else vscalea /= WFDB_DEFGAIN; /* tscale is a multiplicative scale factor that converts sample intervals to window abscissas. */ if (freq == 0.0) freq = WFDB_DEFFREQ; if (tmag <= 0.0) tmag = 1.0; nsamp = canvas_width_sec * freq / tmag; tscale = tmag * seconds(1) / freq; }