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.
32 #ifdef HAVE_POSIX_REGEXP
34 #define REGEXP_T regex_t
35 #define regexp_test(c,s) (regexec((c), (s), 0, NULL, 0) == 0)
37 regexp_compile(char *str
)
43 creg
= g_new(regex_t
, 1);
44 err
= regcomp(creg
, str
, REG_NOSUB
|REG_EXTENDED
);
46 regerror(err
, creg
, ebuf
, sizeof(ebuf
));
47 fprintf(stderr
, "internal error (in regexp %s):\n", str
);
48 fprintf(stderr
, " %s\n", ebuf
);
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)
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
);
72 REGEXP_T
*creg
;/* compiled form of regexp */
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
)
107 unsigned int tried
= 0; /* bitmask of formats. */
109 g_assert(NFormats
<= 8*sizeof(tried
));
110 fp
= fopen64(name
, "r");
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
))
124 ss
= ss_open_internal(fp
, name
, format_tab
[i
].name
);
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) {
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,
142 for(i
= 0; i
< NFormats
; i
++) {
143 if((tried
& (1<<i
)) == 0) {
144 ss
= ss_open_internal(fp
, name
, format_tab
[i
].name
);
146 return wf_finish_read(ss
);
148 if(fseek(fp
, 0L, SEEK_SET
) < 0) {
154 ss_msg(ERR
, "wf_read", "%s: couldn't read with any format\n", name
);
156 } else { /* use specified format only */
157 ss
= ss_open_internal(fp
, name
, format
);
159 return wf_finish_read(ss
);
165 WaveFile
*wf_new(SpiceStream
*ss
)
168 wf
= g_new0(WaveFile
, 1);
170 wf
->tables
= g_ptr_array_new();
176 * read all of the data from a SpiceStream and store it in the WaveFile
179 WaveFile
*wf_finish_read(SpiceStream
*ss
)
189 dvals
= g_new(double, ss
->ncols
);
193 wt
= wf_read_table(ss
, wf
, &state
, &ival
, dvals
);
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
);
200 sprintf(tmp
, "tbl%d", wf
->wf_ntables
);
201 wt
->name
= g_strdup(tmp
);
204 ss_msg(DBG
, "wf_finish_read", "NULL table; state=%d", state
);
221 * read data for a single table (sweep or segment) from spicestream.
223 * state=0: no previous data; dvals is allocated but garbage
224 * state=1: first row of data is in *ivalp, and vals[].
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.
237 wf_read_table(SpiceStream
*ss
, WaveFile
*wf
,
238 int *statep
, double *ivalp
, double *dvals
)
247 if(ss
->nsweepparam
> 0) {
248 if(ss
->nsweepparam
== 1) {
249 if(ss_readsweep(ss
, &spar
) <= 0) {
254 ss_msg(ERR
, "wf_read_table", "nsweepparam=%d; multidimentional sweeps not supported\n", ss
->nsweepparam
);
259 wt
= wvtable_new(wf
);
260 if(ss
->nsweepparam
== 1) {
262 wt
->name
= g_strdup(ss
->spar
[0].name
);
268 wf_set_point(wt
->iv
->wds
, row
, *ivalp
);
269 for(i
= 0; i
< wt
->wt_ndv
; 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
]);
285 while((rc
= ss_readrow(ss
, ivalp
, dvals
)) > 0) {
286 if(row
> 0 && *ivalp
< last_ival
) {
288 ss_msg(ERR
, "wavefile_read", "independent variable is not nondecreasing at row %d; ival=%g last_ival=%g\n", row
, *ivalp
, last_ival
);
299 wf_set_point(wt
->iv
->wds
, row
, *ivalp
);
300 for(i
= 0; i
< wt
->wt_ndv
; 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
]);
324 * Free all memory used by a WaveFile
327 wf_free(WaveFile
*wf
)
331 for(i
= 0; i
< wf
->tables
->len
; i
++) {
332 wt
= wf_wtable(wf
, i
);
335 g_ptr_array_free(wf
->tables
, 0);
340 void wt_free(WvTable
*wt
)
344 for(i
= 0; i
< wt
->wt_ndv
; i
++) {
346 wf_free_dataset(dv
->wds
);
349 g_ptr_array_free(wt
->dvp
, 0);
350 wf_free_dataset(wt
->iv
->wds
);
358 * create a new, empty WvTable for a WaveFile
361 wvtable_new(WaveFile
*wf
)
364 SpiceStream
*ss
= wf
->ss
;
367 wt
= g_new0(WvTable
, 1);
369 wt
->iv
= g_new0(WaveVar
, 1);
370 wt
->iv
->sv
= ss
->ivar
;
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
++) {
379 dv
= g_new0(WaveVar
, 1);
380 g_ptr_array_add(wt
->dvp
, dv
);
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
]);
393 * initialize common elements of WDataSet structure
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
);
410 * initialize DataSet, all ready to hold N elements.
413 wf_init_dataset_size(WDataSet
*ds
, int nelem
)
417 ds
->min
= G_MAXDOUBLE
;
418 ds
->max
= -G_MAXDOUBLE
;
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.
434 wf_free_dataset(WDataSet
*ds
)
437 for(i
= 0; i
< ds
->bpused
; i
++)
445 * Iterate over all WaveVars in all sweeps/segments in the WaveFile,
446 * calling the function for each one.
449 wf_foreach_wavevar(WaveFile
*wf
, GFunc func
, gpointer
*p
)
455 for(i
= 0; i
< wf
->wf_ntables
; i
++) {
456 wt
= wf_wtable(wf
, i
);
457 for(j
= 0; j
< wf
->wf_ndv
; j
++) {
466 * expand dataset's storage to add one more block.
469 wf_expand_dset(WDataSet
*ds
)
471 if(ds
->bpused
>= ds
->bpsize
) {
473 ds
->bptr
= g_realloc(ds
->bptr
, ds
->bpsize
* sizeof(double*));
476 ds
->bptr
[ds
->bpused
++] = g_new(double, DS_DBLKSIZE
);
480 * set single value in dataset. Probably can be inlined.
483 wf_set_point(WDataSet
*ds
, int n
, double val
)
488 while(blk
>= ds
->bpused
)
491 ds
->bptr
[blk
][off
] = val
;
499 * get single point from dataset. Probably can be inlined.
502 wds_get_point(WDataSet
*ds
, int 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
;
534 b
= iv
->wv_nvalues
- 1;
538 cval
= wds_get_point(ds
, (a
+b
)/2);
539 /* printf(" a=%d b=%d ival=%g cval=%g\n", a,b,ival,cval); */
546 g_assert(n
++ < 32); /* > 2 ** 32 points? must be a bug! */
552 * return the value of the dependent variable dv at the point where
553 * its associated independent variable has the value ival.
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.)
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 */
573 li
= wf_find_point(iv
, ival
);
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! */
592 return ly
+ (ry
- ly
) * ((ival
- lx
)/(rx
- lx
));
596 * Find a named variable, return pointer to WaveVar
599 wf_find_variable(WaveFile
*wf
, char *varname
, int swpno
)
604 if(swpno
>= wf
->wf_ntables
)
607 for(i
= 0; i
< wf
->wf_ndv
; i
++) {
608 wt
= wf_wtable(wf
, swpno
);
610 if(0==strcmp(dv
->wv_name
, varname
))
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
,
635 for(swpno
= 0; swpno
< wf
->wf_ntables
; swpno
++) {
636 wv
= wf_find_variable(wf
, varname
, swpno
);
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);
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
);