Ignore all generated/compiled files
[gwave-svn.git] / spicefile / wavefile.c
blob06c7be1c115945a2442bfbb3652226f55e3d7b7d
1 /*
2 * wavefile.c - stuff for working with entire datasets of waveform data.
4 * Copyright 1999, Stephen G. Tell.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "ssintern.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <config.h>
28 #include <glib.h>
29 #include "wavefile.h"
32 #ifdef HAVE_POSIX_REGEXP
33 #include <regex.h>
34 #define REGEXP_T regex_t
35 #define regexp_test(c,s) (regexec((c), (s), 0, NULL, 0) == 0)
36 static regex_t *
37 regexp_compile(char *str)
39 int err;
40 char ebuf[128];
41 regex_t *creg;
43 creg = g_new(regex_t, 1);
44 err = regcomp(creg, str, REG_NOSUB|REG_EXTENDED);
45 if(err) {
46 regerror(err, creg, ebuf, sizeof(ebuf));
47 fprintf(stderr, "internal error (in regexp %s):\n", str);
48 fprintf(stderr, " %s\n", ebuf);
49 exit(1);
51 return creg;
54 #else
55 #include "regexp.h" /* Henry Spencer's V8 regexp */
56 #define REGEXP_T regexp
57 #define regexp_test(c,s) regexec((c), (s))
58 #define regexp_compile(s) regcomp(s)
59 #endif
61 WaveFile *wf_finish_read(SpiceStream *ss);
62 WvTable *wf_read_table(SpiceStream *ss, WaveFile *wf, int *statep, double *ivalp, double *dvals);
63 void wf_init_dataset(WDataSet *ds);
64 extern inline void wf_set_point(WDataSet *ds, int n, double val);
65 void wf_free_dataset(WDataSet *ds);
66 WvTable *wvtable_new(WaveFile *wf);
67 void wt_free(WvTable *wt);
69 typedef struct {
70 char *name;
71 char *fnrexp;
72 REGEXP_T *creg;/* compiled form of regexp */
73 } DFormat;
75 /* table associating file typenames with filename regexps.
76 * Typenames should be those supported by spicefile.c.
78 * Filename patterns are full egrep-style
79 * regular expressions, NOT shell-style globs.
81 static DFormat format_tab[] = {
82 {"hspice", "\\.(tr|sw|ac)[0-9]$" },
83 {"cazm", "\\.[BNW]$" },
84 {"spice3raw", "\\.raw$" },
85 {"spice2raw", "\\.rawspice$" },
86 {"nsout", "\\.out$" },
87 {"ascii", "\\.(asc|acs|ascii)$" }, /* ascii / ACS format */
89 static const int NFormats = sizeof(format_tab)/sizeof(DFormat);
92 * Read a waveform data file.
93 * If the format name is non-NULL, only tries reading in specified format.
94 * If format not specified, tries to guess based on filename, and if
95 * that fails, tries all of the readers until one sucedes.
96 * Returns NULL on failure after printing an error message.
98 * TODO: use some kind of callback or exception so that client
99 * can put the error messages in a GUI or somthing.
101 WaveFile *wf_read(char *name, char *format)
103 FILE *fp;
104 SpiceStream *ss;
105 int i;
107 unsigned int tried = 0; /* bitmask of formats. */
109 g_assert(NFormats <= 8*sizeof(tried));
110 fp = fopen64(name, "r");
111 if(fp == NULL) {
112 perror(name);
113 return NULL;
116 if(format == NULL) {
117 for(i = 0; i < NFormats; i++) {
118 if(!format_tab[i].creg) {
119 format_tab[i].creg = regexp_compile(format_tab[i].fnrexp);
121 if(regexp_test(format_tab[i].creg, name))
123 tried |= 1<<i;
124 ss = ss_open_internal(fp, name, format_tab[i].name);
125 if(ss) {
126 ss_msg(INFO, "wf_read", "%s: read with format \"%s\"", name, format_tab[i].name);
127 return wf_finish_read(ss);
130 if(fseek(fp, 0L, SEEK_SET) < 0) {
131 perror(name);
132 return NULL;
137 if(tried == 0)
138 ss_msg(INFO, "wf_read", "%s: couldn't guess a format from filename suffix.", name);
139 /* no success with formats whose regexp matched filename,
140 * try the others.
142 for(i = 0; i < NFormats; i++) {
143 if((tried & (1<<i)) == 0) {
144 ss = ss_open_internal(fp, name, format_tab[i].name);
145 if(ss)
146 return wf_finish_read(ss);
147 tried |= 1<<i;
148 if(fseek(fp, 0L, SEEK_SET) < 0) {
149 perror(name);
150 return NULL;
154 ss_msg(ERR, "wf_read", "%s: couldn't read with any format\n", name);
155 return NULL;
156 } else { /* use specified format only */
157 ss = ss_open_internal(fp, name, format);
158 if(ss)
159 return wf_finish_read(ss);
160 else
161 return NULL;
165 WaveFile *wf_new(SpiceStream *ss)
167 WaveFile *wf;
168 wf = g_new0(WaveFile, 1);
169 wf->ss = ss;
170 wf->tables = g_ptr_array_new();
171 return wf;
176 * read all of the data from a SpiceStream and store it in the WaveFile
177 * structure.
179 WaveFile *wf_finish_read(SpiceStream *ss)
181 WaveFile *wf;
182 double ival;
183 double *dvals;
184 WvTable *wt;
185 int state;
186 double *spar = NULL;
188 wf = wf_new(ss);
189 dvals = g_new(double, ss->ncols);
191 state = 0;
192 do {
193 wt = wf_read_table(ss, wf, &state, &ival, dvals);
194 if(wt) {
195 ss_msg(DBG, "wf_finish_read", "table with %d rows; state=%d", wt->nvalues, state);
196 wt->swindex = wf->wf_ntables;
197 g_ptr_array_add(wf->tables, wt);
198 if(!wt->name) {
199 char tmp[128];
200 sprintf(tmp, "tbl%d", wf->wf_ntables);
201 wt->name = g_strdup(tmp);
203 } else {
204 ss_msg(DBG, "wf_finish_read", "NULL table; state=%d", state);
206 } while(state > 0);
208 g_free(dvals);
209 g_free(spar);
210 ss_close(ss);
212 if(state < 0) {
213 wf_free(wf);
214 return NULL;
215 } else {
216 return wf;
221 * read data for a single table (sweep or segment) from spicestream.
222 * on entry:
223 * state=0: no previous data; dvals is allocated but garbage
224 * state=1: first row of data is in *ivalp, and vals[].
225 * on exit:
226 * return NULL: fatal error, *statep=-1
227 * return non-NULL: valid wvtable*
229 * state=-1 fatal error
230 * state=0: successful completion of reading whole file
231 * state=1: finished table but more tables remain,
232 * none of the next table has yet been read
233 * state=2: finished table but more tables remain and
234 * *ivalp,dvals[] contain first row of next table.
236 WvTable *
237 wf_read_table(SpiceStream *ss, WaveFile *wf,
238 int *statep, double *ivalp, double *dvals)
240 WvTable *wt;
241 int row;
242 WaveVar *dv;
243 double last_ival;
244 double spar;
245 int rc, i, j;
247 if(ss->nsweepparam > 0) {
248 if(ss->nsweepparam == 1) {
249 if(ss_readsweep(ss, &spar) <= 0) {
250 *statep = -1;
251 return NULL;
253 } else {
254 ss_msg(ERR, "wf_read_table", "nsweepparam=%d; multidimentional sweeps not supported\n", ss->nsweepparam);
255 *statep = -1;
256 return NULL;
259 wt = wvtable_new(wf);
260 if(ss->nsweepparam == 1) {
261 wt->swval = spar;
262 wt->name = g_strdup(ss->spar[0].name);
263 } else {
264 wt->swval = 0;
267 if(*statep == 2) {
268 wf_set_point(wt->iv->wds, row, *ivalp);
269 for(i = 0; i < wt->wt_ndv; i++) {
270 WaveVar *dv;
271 dv = wt_dv(wt, i);
272 for(j = 0; j < dv->wv_ncols; j++)
273 wf_set_point(&dv->wds[j], row,
274 dvals[dv->sv->col - 1 + j ]);
276 row = 1;
277 wt->nvalues = 1;
278 last_ival = *ivalp;
279 } else {
280 row = 0;
281 wt->nvalues = 0;
282 last_ival = -1.0e29;
285 while((rc = ss_readrow(ss, ivalp, dvals)) > 0) {
286 if(row > 0 && *ivalp < last_ival) {
287 if(row == 1) {
288 ss_msg(ERR, "wavefile_read", "independent variable is not nondecreasing at row %d; ival=%g last_ival=%g\n", row, *ivalp, last_ival);
289 wt_free(wt);
290 *statep = -1;
291 return NULL;
293 } else {
294 *statep = 2;
295 return wt;
298 last_ival = *ivalp;
299 wf_set_point(wt->iv->wds, row, *ivalp);
300 for(i = 0; i < wt->wt_ndv; i++) {
301 WaveVar *dv;
302 dv = wt_dv(wt, i);
303 for(j = 0; j < dv->wv_ncols; j++)
304 wf_set_point(&dv->wds[j], row,
305 dvals[dv->sv->col - 1 + j ]);
307 row++;
308 wt->nvalues++;
310 if(rc == -2)
311 *statep = 1;
312 else if(rc < 0) {
313 wt_free(wt);
314 *statep = -1;
315 return NULL;
316 } else {
317 *statep = 0;
319 return wt;
324 * Free all memory used by a WaveFile
326 void
327 wf_free(WaveFile *wf)
329 int i;
330 WvTable *wt;
331 for(i = 0; i < wf->tables->len; i++) {
332 wt = wf_wtable(wf, i);
333 wt_free(wt);
335 g_ptr_array_free(wf->tables, 0);
336 ss_delete(wf->ss);
337 g_free(wf);
340 void wt_free(WvTable *wt)
342 int i;
343 WaveVar *dv;
344 for(i = 0; i < wt->wt_ndv; i++) {
345 dv = wt_dv(wt, i);
346 wf_free_dataset(dv->wds);
347 g_free(dv);
349 g_ptr_array_free(wt->dvp, 0);
350 wf_free_dataset(wt->iv->wds);
351 g_free(wt->iv);
352 if(wt->name)
353 g_free(wt->name);
354 g_free(wt);
358 * create a new, empty WvTable for a WaveFile
360 WvTable *
361 wvtable_new(WaveFile *wf)
363 WvTable *wt;
364 SpiceStream *ss = wf->ss;
365 int i, j;
367 wt = g_new0(WvTable, 1);
368 wt->wf = wf;
369 wt->iv = g_new0(WaveVar, 1);
370 wt->iv->sv = ss->ivar;
371 wt->iv->wtable = wt;
372 wt->iv->wds = g_new0(WDataSet, 1);
373 wf_init_dataset(wt->iv->wds);
375 //wt->dv = g_new0(WaveVar, wf->ss->ndv);
376 wt->dvp = g_ptr_array_sized_new(wf->ss->ndv);
377 for(i = 0; i < wf->wf_ndv; i++) {
378 WaveVar *dv;
379 dv = g_new0(WaveVar, 1);
380 g_ptr_array_add(wt->dvp, dv);
382 dv->wtable = wt;
383 dv->sv = ss_dvar(ss, i);
384 dv->wds = g_new0(WDataSet, dv->sv->ncols);
385 for(j = 0; j < dv->sv->ncols; j++)
386 wf_init_dataset(&dv->wds[j]);
388 return wt;
393 * initialize common elements of WDataSet structure
395 void
396 wf_init_dataset(WDataSet *ds)
398 ds->min = G_MAXDOUBLE;
399 ds->max = -G_MAXDOUBLE;
401 ds->bpsize = DS_INBLKS;
402 ds->bptr = g_new0(double *, ds->bpsize);
403 ds->bptr[0] = g_new(double, DS_DBLKSIZE);
404 ds->bpused = 1;
405 ds->nreallocs = 0;
410 * initialize DataSet, all ready to hold N elements.
412 void
413 wf_init_dataset_size(WDataSet *ds, int nelem)
415 int nblocks, i;
417 ds->min = G_MAXDOUBLE;
418 ds->max = -G_MAXDOUBLE;
419 ds->nreallocs = 0;
421 nblocks = (nelem - 1) / DS_DBLKSIZE + 1;
423 ds->bpused = ds->bpsize = nblocks;
424 ds->bptr = g_new0(double *, ds->bpsize);
425 for(i = 0; i < nblocks; i++) {
426 ds->bptr[i] = g_new(double, DS_DBLKSIZE);
431 * free up memory pointed to by a DataSet, but not the dataset itself.
433 void
434 wf_free_dataset(WDataSet *ds)
436 int i;
437 for(i = 0; i < ds->bpused; i++)
438 if(ds->bptr[i])
439 g_free(ds->bptr[i]);
440 g_free(ds->bptr);
441 g_free(ds);
445 * Iterate over all WaveVars in all sweeps/segments in the WaveFile,
446 * calling the function for each one.
448 void
449 wf_foreach_wavevar(WaveFile *wf, GFunc func, gpointer *p)
451 WvTable *wt;
452 WaveVar *wv;
453 int i, j;
455 for(i = 0; i < wf->wf_ntables; i++) {
456 wt = wf_wtable(wf, i);
457 for(j = 0; j < wf->wf_ndv; j++) {
458 WaveVar *wv;
459 wv = wt_dv(wt, j);
460 (func)(wv, p);
466 * expand dataset's storage to add one more block.
468 void
469 wf_expand_dset(WDataSet *ds)
471 if(ds->bpused >= ds->bpsize) {
472 ds->bpsize *= 2;
473 ds->bptr = g_realloc(ds->bptr, ds->bpsize * sizeof(double*));
474 ds->nreallocs++;
476 ds->bptr[ds->bpused++] = g_new(double, DS_DBLKSIZE);
480 * set single value in dataset. Probably can be inlined.
482 void
483 wf_set_point(WDataSet *ds, int n, double val)
485 int blk, off;
486 blk = ds_blockno(n);
487 off = ds_offset(n);
488 while(blk >= ds->bpused)
489 wf_expand_dset(ds);
491 ds->bptr[blk][off] = val;
492 if(val < ds->min)
493 ds->min = val;
494 if(val > ds->max)
495 ds->max = val;
499 * get single point from dataset. Probably can be inlined.
501 double
502 wds_get_point(WDataSet *ds, int n)
504 int blk, off;
505 blk = ds_blockno(n);
506 off = ds_offset(n);
507 g_assert(blk <= ds->bpused);
508 g_assert(off < DS_DBLKSIZE);
510 return ds->bptr[blk][off];
514 * Use a binary search to return the index of the point
515 * whose value is the largest not greater than ival.
516 * if ival is equal or greater than the max value of the
517 * independent variable, return the index of the last point.
519 * Only works on independent-variables, which we require to
520 * be nondecreasing and have only a single column.
522 * Further, if there are duplicate values, returns the highest index
523 * that has the same value.
526 wf_find_point(WaveVar *iv, double ival)
528 WDataSet *ds = iv->wds;
529 double cval;
530 int a, b;
531 int n = 0;
533 a = 0;
534 b = iv->wv_nvalues - 1;
535 if(ival >= ds->max)
536 return b;
537 while(a+1 < b) {
538 cval = wds_get_point(ds, (a+b)/2);
539 /* printf(" a=%d b=%d ival=%g cval=%g\n", a,b,ival,cval); */
540 if(ival < cval)
541 b = (a+b)/2;
542 else
543 a = (a+b)/2;
546 g_assert(n++ < 32); /* > 2 ** 32 points? must be a bug! */
548 return a;
552 * return the value of the dependent variable dv at the point where
553 * its associated independent variable has the value ival.
555 * FIXME:tell
556 * make this fill in an array of dependent values,
557 * one for each column in the specified dependent variable.
558 * This will be better than making the client call us once for each column,
559 * because we'll only have to search for the independent value once.
560 * (quick hack until we need support for complex and other multicolumn vars:
561 * just return first column's value.)
563 double
564 wv_interp_value(WaveVar *dv, double ival)
566 int li, ri; /* index of points to left and right of desired value */
567 double lx, rx; /* independent variable's value at li and ri */
568 double ly, ry; /* dependent variable's value at li and ri */
569 WaveVar *iv;
571 iv = dv->wv_iv;
573 li = wf_find_point(iv, ival);
574 ri = li + 1;
575 if(ri >= dv->wv_nvalues)
576 return wds_get_point(dv->wds, dv->wv_nvalues-1);
578 lx = wds_get_point(&iv->wds[0], li);
579 rx = wds_get_point(&iv->wds[0], ri);
580 /* g_assert(lx <= ival); */
581 if(li > 0 && lx > ival) {
582 fprintf(stderr, "wv_interp_value: assertion failed: lx <= ival for %s: ival=%g li=%d lx=%g\n", dv->wv_name, ival, li, lx);
585 ly = wds_get_point(&dv->wds[0], li);
586 ry = wds_get_point(&dv->wds[0], ri);
588 if(ival > rx) { /* no extrapolation allowed! */
589 return ry;
592 return ly + (ry - ly) * ((ival - lx)/(rx - lx));
596 * Find a named variable, return pointer to WaveVar
598 WaveVar *
599 wf_find_variable(WaveFile *wf, char *varname, int swpno)
601 int i;
602 WvTable *wt;
603 WaveVar *dv;
604 if(swpno >= wf->wf_ntables)
605 return NULL;
607 for(i = 0; i < wf->wf_ndv; i++) {
608 wt = wf_wtable(wf, swpno);
609 dv = wt_dv(wt, i);
610 if(0==strcmp(dv->wv_name, varname))
611 return dv;
613 return NULL;
617 * add a new variable to all sweeps in a WaveFile object,
618 * initialiazing the data to all 0's.
621 int wf_add_var(WaveFile *wf, char *varname, int ncols, VarType type,
622 void *udata)
624 int swpno;
625 WvTable *wt;
626 WaveVar *wv;
627 SpiceVar *sv;
628 WDataSet *wds;
629 int col0;
630 int dvno;
631 int nblocks;
632 int i;
635 for(swpno = 0; swpno < wf->wf_ntables; swpno++) {
636 wv = wf_find_variable(wf, varname, swpno);
637 if(wv)
638 return -1;
641 col0 = wf->wf_ncols;
642 dvno = wf->wf_ndv;
643 wf->wf_ndv++;
644 wf->wf_ncols += ncols;
646 sv = ss_spicevar_new(varname, type, col0, ncols);
647 g_ptr_array_add(wf->ss->dvarp, sv);
649 for(swpno = 0; swpno < wf->wf_ntables; swpno++) {
650 wt = wf_wtable(wf, swpno);
652 wv = g_new0(WaveVar, 1);
653 wv->wtable = wt;
654 wv->sv = sv;
655 wv->udata = udata;
656 wv->wds = g_new0(WDataSet, ncols);
657 g_ptr_array_add(wt->dvp, wv);
659 for(i = 0; i < ncols; i++) {
660 wf_init_dataset_size(&wv->wds[i], wt->nvalues);
661 wds->min = 0.0;
662 wds->max = 0.0;
665 return 0;