/* * read all of the data from a SpiceStream and store it in the WaveFile * structure. */ WaveFile *wf_finish_read(SpiceStream *ss) { WaveFile *wf; int rc; double ival; double *dvals; WvTable *wt; int state; double *spar = NULL; wf = g_new0(WaveFile, 1); wf->ss = ss; wf->tables = g_ptr_array_new(); dvals = g_new(double, ss->ncols); state = 0; do { wt = wf_read_table(ss, wf, &state, &ival, dvals); if(wt) { ss_msg(DBG, "wf_finish_read", "table with %d rows; state=%d", wt->nvalues, state); wt->swindex = wf->wf_ntables; g_ptr_array_add(wf->tables, wt); if(!wt->name) { char tmp[128]; sprintf(tmp, "tbl%d", wf->wf_ntables); wt->name = g_strdup(tmp); } } else { ss_msg(DBG, "wf_finish_read", "NULL table; state=%d", state); } } while(state > 0); g_free(dvals); g_free(spar); ss_close(ss); if(state < 0) { wf_free(wf); return NULL; } else { return wf; } }
/* * Process a header line from an ascii or cazm format file. * Returns a filled-in SpiceStream* with variable information. */ static SpiceStream *ascii_process_header(char *line, VarType ivtype, char *fname, int lineno) { SpiceStream *sf; char *signam; int dvsize = 64; signam = strtok(line, " \t\n"); if(!signam) { ss_msg(ERR, "ascii_process_header", "%s:%d: syntax error in header", fname, lineno); return NULL; } /* a bit of a hack: get ss_new to allocate additional * dvars, then only use the entries we need or allocate more */ sf = ss_new(NULL, fname, dvsize, 0); if(ivtype == UNKNOWN) { if(strcasecmp(signam, "time") == 0) sf->ivar->type = TIME; } else { sf->ivar->type = ivtype; } sf->ivar->name = g_strdup(signam); sf->ivar->col = 0; sf->ivar->ncols = 1; sf->ndv = 0; sf->ncols = 1; sf->ntables = 1; while((signam = strtok(NULL, " \t\n")) != NULL) { if(sf->ndv >= dvsize) { dvsize *= 2; sf->dvar = g_realloc(sf->dvar, dvsize * sizeof(SpiceVar)); } sf->dvar[sf->ndv].name = g_strdup(signam); sf->dvar[sf->ndv].type = UNKNOWN; sf->dvar[sf->ndv].col = sf->ncols; sf->dvar[sf->ndv].ncols = 1; sf->ndv++; sf->ncols++; } sf->readrow = sf_readrow_ascii; return sf; }
/* Read row of values from ascii- or cazm- format file. * Possibly reusable for other future formats with lines of * whitespace-seperated values. * Returns: * 1 on success. also fills in *ivar scalar and *dvars vector * 0 on EOF * -1 on error (may change some ivar/dvar values) */ static int sf_readrow_ascii(SpiceStream *sf, double *ivar, double *dvars) { int i = 0; char *tok; if(fread_line(sf->fp, &sf->linebuf, &sf->lbufsize) == EOF) { return 0; } sf->lineno++; tok = strtok(sf->linebuf, " \t\n"); if(!tok) { return 0; /* blank line can indicate end of data */ } /* check to see if it is numeric: ascii format is so loosly defined * that we might read a load of garbage otherwise. */ if(strspn(tok, "0123456789eE+-.") != strlen(tok)) { ss_msg(ERR, "sf_readrow_ascii", "%s:%d: expected number; maybe this isn't an ascii data file at all?", sf->filename, sf->lineno, i); return -1; } *ivar = atof(tok); for(i = 0; i < sf->ncols-1; i++) { tok = strtok(NULL, " \t\n"); if(!tok) { ss_msg(ERR, "sf_readrow_ascii", "%s:%d: data field %d missing", sf->filename, sf->lineno, i); return -1; } dvars[i] = atof(tok); } return 1; }
static int sf_getval_s3bin(SpiceStream *sf, double *dval) { off64_t pos; double val; int i; if(sf->read_vals >= sf->expected_vals) { pos = ftello64(sf->fp); ss_msg(DBG, "sf_getval_s3bin", "past last expected value offset 0x%lx", (long) pos); return 0; } if(fread(&val, sizeof(double), 1, sf->fp) != 1) { pos = ftello64(sf->fp); ss_msg(ERR, "sf_getval_s3bin", "unexepected EOF in data at offset 0x%lx", (long) pos); return -1; } sf->read_vals++; *dval = val; return 1; }
/* * Read row of values from a binay spice3 raw file */ static int sf_readrow_s3bin(SpiceStream *sf, double *ivar, double *dvars) { int i, rc; double v; double dummy; if((sf->flags & SSF_PUSHBACK) == 0) { rc = sf_getval_s3bin(sf, &v); if(rc == 0) /* file EOF */ return 0; if(rc < 0) return -1; if(sf->ivar->ncols == 2) { rc = sf_getval_s3bin(sf, &dummy); if(rc == 0) /* file EOF */ return 0; if(rc < 0) return -1; } if(v < sf->ivval) { /* independent-variable value decreased, this must * be the start of another sweep. hold the value and * return flag to caller. */ sf->ivval = v; sf->flags |= SSF_PUSHBACK; return -2; } else { sf->ivval = v; *ivar = v; } } else { /* iv value for start of new sweep was read last time */ sf->flags &= ~SSF_PUSHBACK; *ivar = sf->ivval; } for(i = 0; i < sf->ncols-1; i++) { if(sf_getval_s3bin(sf, &dvars[i]) != 1) { ss_msg(WARN, "sf_readrow_s3bin", "%s: EOF or error reading data field %d in row %d; file is incomplete.", sf->filename, i, sf->read_rows); return 0; } } sf->read_rows++; return 1; }
/* Read spice-type file header - Berkeley Spice3 "raw" format */ SpiceStream * sf_rdhdr_s3raw(char *name, FILE *fp) { SpiceStream *sf = NULL; char *line = NULL; char *signam; int lineno = 0; int linesize = 1024; int dvsize = 128; char *key, *val; int nvars, npoints; int got_nvars = 0; int got_values = 0; int dtype_complex = 0; int binary = 0; char *vnum, *vname, *vtypestr; int i; while(fread_line(fp, &line, &linesize) != EOF) { lineno++; if(lineno == 1 && strncmp(line, "Title: ", 7)) { /* not a spice3raw file; bail out */ ss_msg(DBG, msgid, "%s:%d: Doesn't look like a spice3raw file; \"Title:\" expected\n", name, lineno); return NULL; } key = strtok(line, ":"); if(!key) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected \"keyword:\"", name, lineno); g_free(line); return NULL; } if(strcmp(key, "Flags") == 0) { while(val = strtok(NULL, " ,\t\n")) { if(strcmp(val, "real") == 0) { dtype_complex = 0; } if(strcmp(val, "complex") == 0) { dtype_complex = 1; } } } else if(strcmp(key, "No. Variables") == 0) { val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected integer", name, lineno); g_free(line); return NULL; } nvars = atoi(val); got_nvars = 1; } else if(strcmp(key, "No. Points") == 0) { val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected integer", name, lineno); g_free(line); return NULL; } npoints = atoi(val); } else if(strcmp(key, "Variables") == 0) { if(!got_nvars) { ss_msg(ERR, msgid, "%s:%d: \"Variables:\" before \"No. Variables:\"", name, lineno, i); goto err; } sf = ss_new(fp, name, nvars-1, 0); sf->ncols = 1; sf->ntables = 1; /* first variable may be described on the same line * as "Variables:" keyword */ vnum = strtok(NULL, " \t\n"); for(i = 0; i < nvars; i++) { if(i || !vnum) { if(fread_line(fp, &line, &linesize) == EOF) { ss_msg(ERR, msgid, "%s:%d: Unexpected EOF in \"Variables:\" at var %d", name, lineno, i); goto err; } lineno++; vnum = strtok(line, " \t\n"); } vname = strtok(NULL, " \t\n"); vtypestr = strtok(NULL, " \t\n"); if(!vnum || !vname || !vtypestr) { ss_msg(ERR, msgid, "%s:%d: expected number name type", name, lineno); goto err; } if(i == 0) { /* assume Ind.Var. first */ sf->ivar->name = g_strdup(vname); sf->ivar->type = sf_str2type_s3raw(vtypestr); sf->ivar->col = 0; /* ivar can't really be two-column, this is a flag that says to discard 2nd point */ if(dtype_complex) sf->ivar->ncols = 2; else sf->ivar->ncols = 1; } else { sf->dvar[i-1].name = g_strdup(vname); sf->dvar[i-1].type = sf_str2type_s3raw(vtypestr); sf->dvar[i-1].col = sf->ncols; if(dtype_complex) sf->dvar[i-1].ncols = 2; else sf->dvar[i-1].ncols = 1; sf->ncols += sf->dvar[i-1].ncols; } } } else if(strcmp(key, "Values") == 0) { got_values = 1; break; } else if(strcmp(key, "Binary") == 0) { binary = 1; got_values = 1; break; } if(got_values) break; } if(!sf) { ss_msg(ERR, msgid, "%s:%d: no \"Variables:\" section in header", name, lineno); goto err; } if(!got_values) { ss_msg(ERR, msgid, "%s:%d: EOF without \"Values:\" in header", name, lineno); goto err; } if(binary) { sf->readrow = sf_readrow_s3bin; } else { sf->readrow = sf_readrow_s3raw; } sf->read_rows = 0; sf->expected_vals = npoints * (sf->ncols + (dtype_complex ? 1 : 0)); ss_msg(DBG, msgid, "expecting %d values\n", sf->expected_vals); sf->lineno = lineno; sf->linebuf = line; sf->lbufsize = linesize; ss_msg(DBG, msgid, "Done with header at offset 0x%lx\n", (long) ftello64(sf->fp)); return sf; err: if(line) g_free(line); if(sf) { sf->fp = NULL; /* prevent ss_delete from cleaning up FILE*; ss_open callers may rewind and try another format on failure. */ ss_delete(sf); } return NULL; }
/* * Read row of values from an ascii spice3 raw file */ static int sf_readrow_s3raw(SpiceStream *sf, double *ivar, double *dvars) { int i; int frownum; char *tok; double v; if((sf->flags & SSF_PUSHBACK) == 0) { tok = sf_nexttoken(sf); if(!tok) { return 0; // ss_msg(ERR, msgid, "%s:%d: expected row number", // sf->filename, sf->lineno); // return -1; } if(!isdigit(*tok)) { ss_msg(WARN, msgid, "%s:%d: expected row number, got \"%s\". Note: only one dataset per file is supported, extra garbage ignored", sf->filename, sf->lineno, tok); return 0; } frownum = atoi(tok); /* todo: check for expected and maximum row number */ tok = sf_nexttoken(sf); if(!tok) { ss_msg(WARN, msgid, "%s:%d: expected ivar value", sf->filename, sf->lineno); return -1; } v = atof(tok); if(v < sf->ivval) { /* independent-variable value decreased, this must * be the start of another sweep. hold the value and * return flag to caller. */ sf->ivval = v; sf->flags |= SSF_PUSHBACK; return -2; } else { sf->ivval = v; *ivar = v; } } else { /* iv value for start of new sweep was read last time */ sf->flags &= ~SSF_PUSHBACK; *ivar = sf->ivval; } for(i = 0; i < sf->ndv; i++) { SpiceVar *dv; dv = &sf->dvar[i]; tok = sf_nexttoken(sf); if(!tok) { ss_msg(ERR, msgid, "%s:%d: expected value", sf->filename, sf->lineno); return -1; } dvars[dv->col-1] = atof(tok); if(dv->ncols > 1) { tok = strchr(tok, ','); if(!tok || !*(tok+1)) { ss_msg(ERR, msgid, "%s:%d: expected second value", sf->filename, sf->lineno); return -1; } tok++; dvars[dv->col] = atof(tok); } } sf->read_rows++; return 1; }
/* * read data for a single table (sweep or segment) from spicestream. * on entry: * state=0: no previous data; dvals is allocated but garbage * state=1: first row of data is in *ivalp, and vals[]. * on exit: * return NULL: fatal error, *statep=-1 * return non-NULL: valid wvtable* * * state=-1 fatal error * state=0: successful completion of reading whole file * state=1: finished table but more tables remain, * none of the next table has yet been read * state=2: finished table but more tables remain and * *ivalp,dvals[] contain first row of next table. */ WvTable * wf_read_table(SpiceStream *ss, WaveFile *wf, int *statep, double *ivalp, double *dvals) { WvTable *wt; int row; WaveVar *dv; double last_ival; double spar; int rc, i, j; if(ss->nsweepparam > 0) { if(ss->nsweepparam == 1) { if(ss_readsweep(ss, &spar) <= 0) { *statep = -1; return NULL; } } else { ss_msg(ERR, "wf_read_table", "nsweepparam=%d; multidimentional sweeps not supported\n", ss->nsweepparam); *statep = -1; return NULL; } } wt = wvtable_new(wf); if(ss->nsweepparam == 1) { wt->swval = spar; wt->name = g_strdup(ss->spar[0].name); } else { wt->swval = 0; } if(*statep == 2) { wf_set_point(wt->iv->wds, row, *ivalp); for(i = 0; i < wt->wt_ndv; i++) { dv = &wt->dv[i]; for(j = 0; j < dv->wv_ncols; j++) wf_set_point(&dv->wds[j], row, dvals[dv->sv->col - 1 + j ]); } row = 1; wt->nvalues = 1; last_ival = *ivalp; } else { row = 0; wt->nvalues = 0; last_ival = -1.0e29; } while((rc = ss_readrow(ss, ivalp, dvals)) > 0) { if(row > 0 && *ivalp < last_ival) { if(row == 1) { ss_msg(ERR, "wavefile_read", "independent variable is not nondecreasing at row %d; ival=%g last_ival=%g\n", row, *ivalp, last_ival); wt_free(wt); *statep = -1; return NULL; } else { *statep = 2; return wt; } } last_ival = *ivalp; wf_set_point(wt->iv->wds, row, *ivalp); for(i = 0; i < wt->wt_ndv; i++) { dv = &wt->dv[i]; for(j = 0; j < dv->wv_ncols; j++) wf_set_point(&dv->wds[j], row, dvals[dv->sv->col - 1 + j ]); } row++; wt->nvalues++; } if(rc == -2) *statep = 1; else if(rc < 0) { wt_free(wt); *statep = -1; return NULL; } else { *statep = 0; } return wt; }
/* * Read a waveform data file. * If the format name is non-NULL, only tries reading in specified format. * If format not specified, tries to guess based on filename, and if * that fails, tries all of the readers until one sucedes. * Returns NULL on failure after printing an error message. * * TODO: use some kind of callback or exception so that client * can put the error messages in a GUI or somthing. */ WaveFile *wf_read(char *name, char *format) { FILE *fp; SpiceStream *ss; int i; unsigned int tried = 0; /* bitmask of formats. */ g_assert(NFormats <= 8*sizeof(tried)); fp = fopen64(name, "r"); if(fp == NULL) { perror(name); return NULL; } if(format == NULL) { for(i = 0; i < NFormats; i++) { if(!format_tab[i].creg) { format_tab[i].creg = regexp_compile(format_tab[i].fnrexp); } if(regexp_test(format_tab[i].creg, name)) { tried |= 1<<i; ss = ss_open_internal(fp, name, format_tab[i].name); if(ss) { ss_msg(INFO, "wf_read", "%s: read with format \"%s\"", name, format_tab[i].name); return wf_finish_read(ss); } if(fseek(fp, 0L, SEEK_SET) < 0) { perror(name); return NULL; } } } if(tried == 0) ss_msg(INFO, "wf_read", "%s: couldn't guess a format from filename suffix.", name); /* no success with formats whose regexp matched filename, * try the others. */ for(i = 0; i < NFormats; i++) { if((tried & (1<<i)) == 0) { ss = ss_open_internal(fp, name, format_tab[i].name); if(ss) return wf_finish_read(ss); tried |= 1<<i; if(fseek(fp, 0L, SEEK_SET) < 0) { perror(name); return NULL; } } } ss_msg(ERR, "wf_read", "%s: couldn't read with any format\n", name); return NULL; } else { /* use specified format only */ ss = ss_open_internal(fp, name, format); if(ss) return wf_finish_read(ss); else return NULL; } }
/* Read spice-type file header - nanosim "out" format */ SpiceStream * sf_rdhdr_nsout(char *name, FILE *fp) { SpiceStream *sf = NULL; char *line = NULL; int lineno = 0; int linesize = 1024; char *key, *val; int got_ivline = 0; int ndvars; double voltage_resolution = 1.0; double current_resolution = 1.0; double time_resolution = 1.0; GList *vlist = NULL; struct nsvar *nsv; int i; int maxindex = 0; while(fread_line(fp, &line, &linesize) != EOF) { lineno++; if(lineno == 1 && strncmp(line, ";! output_format", 16)) { /* not an out file; bail out */ ss_msg(DBG, msgid, "%s:%d: Doesn't look like an ns-out file; \"output_format\" expected\n", name, lineno); return NULL; } if(line[0] == ';') continue; if(line[0] == '.') { key = strtok(&line[1], " \t"); if(!key) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected \"keyword:\"", name, lineno); g_free(line); return NULL; } if(strcmp(key, "time_resolution") == 0) { val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected number", name, lineno); g_free(line); return NULL; } time_resolution = atof(val); } if(strcmp(key, "current_resolution") == 0) { val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected number", name, lineno); g_free(line); return NULL; } current_resolution = atof(val); } if(strcmp(key, "voltage_resolution") == 0) { val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected number", name, lineno); g_free(line); return NULL; } voltage_resolution = atof(val); } if(strcmp(key, "index") == 0) { nsv = g_new0(struct nsvar, 1); val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected varname", name, lineno); goto err; } nsv->name = g_strdup(val); val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected var-index", name, lineno); goto err; } nsv->index = atoi(val); if(nsv->index > maxindex) maxindex = nsv->index; val = strtok(NULL, " \t\n"); if(!val) { ss_msg(ERR, msgid, "%s:%d: syntax error, expected variable type", name, lineno); goto err; } nsv->type = sf_str2type_nsout(val); vlist = g_list_append(vlist, nsv); } }