2 * sp2sp - test program for spicestream library and
3 * rudimentary spicefile format converter.
5 * Copyright (C) 1998,1999 Stephen G. Tell
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include "spicestream.h"
33 #define SWEEP_PREPEND 1
36 static const char NPY_MAGIC
[] = "\x93NUMPY";
37 static const int NPY_MAJOR
= 1;
38 static const int NPY_MINOR
= 0;
39 static const int NPY_MAX_HDR_LEN
= 256 * 256;
40 static const int NPY_PREAMBLE_LEN
= 6 + 1 + 1 + 2;
42 #if __BYTE_ORDER == __LITTLE_ENDIAN
43 static const char NPY_ENDIAN_CHAR
= '<';
45 static const char NPY_ENDIAN_CHAR
= '>';
50 int sweep_mode
= SWEEP_PREPEND
;
51 char *progname
= "sp2sp";
53 static void ascii_header_output(SpiceStream
*sf
, int *enab
, int nidx
, FILE *of
);
54 static void ascii_data_output(SpiceStream
*sf
, int *enab
, int nidx
,
55 double begin_val
, double end_val
, int ndigits
, FILE *of
);
56 static void numpy_output(SpiceStream
*sf
, int *enab
, int nidx
,
57 double begin_val
, double end_val
, int ndigits
, FILE *of
);
58 static int numpy_header_output(int ndims
, int* shape
, int len
, FILE *of
);
59 static int numpy_footer_output(SpiceStream
*sf
, int *indices
, int nidx
,
60 int *sweeprows
, FILE *of
);
61 static int parse_field_numbers(int **index
, int *idxsize
, int *nsel
,
62 char *list
, int nfields
);
63 static int parse_field_names(int **index
, int *idxsize
, int *nsel
,
64 char *list
, SpiceStream
*sf
);
65 static VarType
get_vartype_code(char *vartype
);
72 fprintf(stderr
, "usage: %s [options] file\n", progname
);
73 fprintf(stderr
, " options:\n");
74 fprintf(stderr
, " -b V begin output after independent-variable value V is reached\n");
75 fprintf(stderr
, " instead of start of input\n");
76 fprintf(stderr
, " -c T Convert output to type T\n");
77 fprintf(stderr
, " -d N use N significant digits in output\n");
78 fprintf(stderr
, " -e V stop after independent-variable value V is reached\n");
79 fprintf(stderr
, " instead of end of input.\n");
81 fprintf(stderr
, " -f f1,f2,... Output only fields named f1, f2, etc.\n");
82 fprintf(stderr
, " -n n1,n2,... Output only fields n1, n2, etc;\n");
83 fprintf(stderr
, " independent variable is field number 0\n");
84 fprintf(stderr
, " -o file Output to named file instead of default '-' stdout.\n");
85 fprintf(stderr
, " -u U Output only variables with units of type; U\n");
86 fprintf(stderr
, " U = volts, amps, etc.\n");
87 fprintf(stderr
, " -s S Handle sweep parameters as S:\n");
88 fprintf(stderr
, " -s head add header-like comment line\n");
89 fprintf(stderr
, " -s prepend prepend columns to all output lines\n");
90 fprintf(stderr
, " -s none ignore sweep info\n");
91 fprintf(stderr
, " -t T Assume that input is of type T\n");
92 fprintf(stderr
, " -v Verbose - print detailed signal information\n");
93 fprintf(stderr
, " output format types:\n");
94 fprintf(stderr
, " none - no data output\n");
95 fprintf(stderr
, " ascii - lines of space-seperated numbers, with header\n");
96 fprintf(stderr
, " nohead - lines of space-seperated numbers, no headers\n");
97 fprintf(stderr
, " cazm - CAzM format\n");
98 fprintf(stderr
, " numpy - .npy data followed by python str(dict) and footer len uint16\n");
99 fprintf(stderr
, " input format types:\n");
102 while(s
= ss_filetype_name(i
++)) {
103 fprintf(stderr
, " %s\n", s
);
108 main(int argc
, char **argv
)
119 char *infiletype
= "hspice";
120 char *outfiletype
= "ascii";
121 char *outfilename
= "-";
122 char *fieldnamelist
= NULL
;
123 char *fieldnumlist
= NULL
;
124 int *out_indices
= NULL
;
127 VarType vartype
= UNKNOWN
;
130 double begin_val
= -DBL_MAX
;
131 double end_val
= DBL_MAX
;
133 while ((c
= getopt (argc
, argv
, "b:c:d:e:f:n:s:t:u:o:vx")) != EOF
) {
136 spicestream_msg_level
= DBG
;
140 begin_val
= atof(optarg
);
143 outfiletype
= optarg
;
146 ndigits
= atoi(optarg
);
151 end_val
= atof(optarg
);
154 fieldnamelist
= optarg
;
157 fieldnumlist
= optarg
;
160 if(strcmp(optarg
, "none") == 0)
161 sweep_mode
= SWEEP_NONE
;
162 else if(strcmp(optarg
, "prepend") == 0)
163 sweep_mode
= SWEEP_PREPEND
;
164 else if(strcmp(optarg
, "head") == 0)
165 sweep_mode
= SWEEP_HEAD
;
167 fprintf(stderr
, "unknown sweep-data style %s\n", optarg
);
175 vartype
= get_vartype_code(optarg
);
178 spicestream_msg_level
= DBG
;
182 outfilename
= optarg
;
190 if(errflg
|| optind
>= argc
) {
195 sf
= ss_open(argv
[optind
], infiletype
);
198 perror(argv
[optind
]);
199 fprintf(stderr
, "unable to read file\n");
203 printf("filename: \"%s\"\n", sf
->filename
);
204 printf(" columns: %d\n", sf
->ncols
);
205 printf(" tables: %d\n", sf
->ntables
);
206 printf("independent variable:\n");
207 printf(" name: \"%s\"\n", sf
->ivar
->name
);
208 printf(" type: %s\n", vartype_name_str(sf
->ivar
->type
));
209 printf(" col: %d\n", sf
->ivar
->col
);
210 printf(" ncols: %d\n", sf
->ivar
->ncols
);
211 printf("sweep parameters: %d\n", sf
->nsweepparam
);
212 for(i
= 0; i
< sf
->nsweepparam
; i
++) {
213 printf(" name: \"%s\"\n", sf
->spar
[i
].name
);
214 printf(" type: %s\n", vartype_name_str(sf
->spar
[i
].type
));
216 printf("dependent variables: %d\n", sf
->ndv
);
217 for(i
= 0; i
< sf
->ndv
; i
++) {
219 dvar
= ss_dvar(sf
, i
);
220 printf(" dv[%d] \"%s\" ", i
, dvar
->name
);
221 printf(" (type=%s col=%d ncols=%d)\n",
222 vartype_name_str(dvar
->type
),
228 if(strcmp(outfilename
, "-") == 0) {
229 if(strcmp(outfiletype
, "numpy") == 0) {
230 fprintf(stderr
, "cannot output to stdout for 'numpy' format, use -o\n");
235 of
= (FILE *)fopen64(outfilename
, "w"); /* DJW: why is the cast needed? */
239 fprintf(stderr
, "unable to open output file\n");
244 if(fieldnamelist
== NULL
&& fieldnumlist
== NULL
) {
245 out_indices
= g_new0(int, sf
->ndv
+1);
248 for(i
= 0; i
< sf
->ndv
+1; i
++) {
250 dvar
= ss_dvar(sf
, i
-1);
254 || dvar
->type
== vartype
)) {
255 out_indices
[idx
++] = i
;
261 if(parse_field_numbers(&out_indices
, &outi_size
, &nsel
,
262 fieldnumlist
, sf
->ndv
+1) < 0)
265 if(parse_field_names(&out_indices
, &outi_size
, &nsel
,
266 fieldnamelist
, sf
) < 0)
269 fprintf(stderr
, "No fields selected for output\n");
273 if(strcmp(outfiletype
, "cazm") == 0) {
274 fprintf(of
, "* CAZM-format output converted with sp2sp\n");
276 fprintf(of
, "TRANSIENT ANALYSIS\n");
277 ascii_header_output(sf
, out_indices
, nsel
, of
);
278 ascii_data_output(sf
, out_indices
, nsel
, begin_val
, end_val
, ndigits
, of
);
279 } else if(strcmp(outfiletype
, "ascii") == 0) {
280 ascii_header_output(sf
, out_indices
, nsel
, of
);
281 ascii_data_output(sf
, out_indices
, nsel
, begin_val
, end_val
, ndigits
, of
);
282 } else if(strcmp(outfiletype
, "nohead") == 0) {
283 ascii_data_output(sf
, out_indices
, nsel
, begin_val
, end_val
, ndigits
, of
);
284 } else if(strcmp(outfiletype
, "numpy") == 0) {
285 numpy_output(sf
, out_indices
, nsel
, begin_val
, end_val
, ndigits
, of
);
286 } else if(strcmp(outfiletype
, "none") == 0) {
289 fprintf(stderr
, "%s: invalid output type name: %s\n",
290 progname
, outfiletype
);
300 * print all column headers.
301 * For multicolumn variables, ss_var_name will generate a column name
302 * consisting of the variable name plus a suffix.
305 ascii_header_output(SpiceStream
*sf
, int *indices
, int nidx
, FILE *of
)
310 if((sf
->nsweepparam
> 0) && (sweep_mode
== SWEEP_PREPEND
)) {
311 for(i
= 0; i
< sf
->nsweepparam
; i
++) {
312 fprintf(of
, "%s ", sf
->spar
[i
].name
);
315 for(i
= 0; i
< nidx
; i
++) {
318 if(indices
[i
] == 0) {
319 ss_var_name(sf
->ivar
, 0, buf
, 1024);
320 fprintf(of
, "%s", buf
);
322 int varno
= indices
[i
]-1;
324 dvar
= ss_dvar(sf
, varno
);
325 for(j
= 0; j
< dvar
->ncols
; j
++) {
328 ss_var_name(dvar
, j
, buf
, 1024);
329 fprintf(of
, "%s", buf
);
337 * print data as space-seperated columns.
340 ascii_data_output(SpiceStream
*sf
, int *indices
, int nidx
,
341 double begin_val
, double end_val
, int ndigits
, FILE *of
)
350 dvals
= g_new(double, sf
->ncols
);
351 if(sf
->nsweepparam
> 0)
352 spar
= g_new(double, sf
->nsweepparam
);
357 if(sf
->nsweepparam
> 0) {
358 if(ss_readsweep(sf
, spar
) <= 0)
361 if(tab
> 0 && sweep_mode
== SWEEP_HEAD
) {
362 fprintf(of
, "# sweep %d;", tab
);
363 for(i
= 0; i
< sf
->nsweepparam
; i
++) {
364 fprintf(of
, " %s=%g", sf
->spar
[i
].name
, spar
[i
]);
368 while((rc
= ss_readrow(sf
, &ival
, dvals
)) > 0) {
372 /* past end_val, but can only stop reading
373 early if if there is only one sweep-table
381 if((sf
->nsweepparam
> 0) && (sweep_mode
== SWEEP_PREPEND
)) {
382 for(i
= 0; i
< sf
->nsweepparam
; i
++) {
383 fprintf(of
, "%.*g ", ndigits
, spar
[i
]);
386 for(i
= 0; i
< nidx
; i
++) {
390 fprintf(of
, "%.*g", ndigits
, ival
);
392 int varno
= indices
[i
]-1;
393 SpiceVar
*dvar
= ss_dvar(sf
, varno
);
394 int dcolno
= dvar
->col
- 1;
395 for(j
= 0; j
< dvar
->ncols
; j
++) {
398 fprintf(of
, "%.*g", ndigits
,
405 if(rc
== -2) { /* end of sweep, more follow */
406 if(sf
->nsweepparam
== 0)
407 sweep_mode
= SWEEP_HEAD
;
409 } else { /* EOF or error */
419 numpy_header_output(int ndims
, int *shape
, int len
, FILE *of
)
421 /* More documentation about the npy format at numpy/lib/format.py */
423 char header
[NPY_MAX_HDR_LEN
];
429 strcpy(header
, "{'descr':'");
431 descr
[0] = NPY_ENDIAN_CHAR
;
433 sprintf(descr
+2, "%d", (int) sizeof(float));
435 strcat(header
, descr
);
436 strcat(header
, "', 'fortran_order':False, 'shape': (");
437 for(i
=0; i
< ndims
; i
++) {
438 hlen
= strlen(header
);
439 sprintf(header
+hlen
, "%d", shape
[i
]);
442 strcat(header
, ", ");
445 strcat(header
, ") }");
446 hlen
= strlen(header
);
449 /* bogus header values or just write the length needed */
450 nspaces
= 16 - ((NPY_PREAMBLE_LEN
+ hlen
+ 1) % 16);
452 } else if(len
< hlen
) {
453 fprintf(stderr
, "numpy_header_output: requested header len is too small\n");
455 } else if(((len
+ NPY_PREAMBLE_LEN
) % 16) != 0) {
456 fprintf(stderr
, "requested header len does not align to mult of 16\n");
459 /* pad to the (longer) header length */
460 nspaces
= len
- hlen
- 1;
463 if(hlen
+ nspaces
+ 1 > NPY_MAX_HDR_LEN
) {
464 fprintf(stderr
, "npy header too long (%d)\n", hlen
+nspaces
+1);
468 for(i
=0; i
< nspaces
; i
++) {
471 strcat(header
, "\n");
472 hlen
= strlen(header
);
475 fprintf(of
, NPY_MAGIC
);
476 fwrite(&NPY_MAJOR
, sizeof(char), 1, of
);
477 fwrite(&NPY_MINOR
, sizeof(char), 1, of
);
478 fputc((0xff & hlen
), of
);
479 fputc((0xff & (hlen
>> 8)), of
);
480 fwrite(header
, sizeof(char), hlen
, of
);
487 numpy_footer_output(SpiceStream
*sf
, int *indices
, int nidx
, int *sweeprows
, FILE *of
)
495 foot_start
= ftell(of
);
502 /* sweep variables, these values prepend the data columns for each row */
503 fprintf(of
, "'sweepvars':(");
504 /*for(i=0; i < sf->ntables; i++) {*/
505 if(sf
->ntables
> 1) {
506 for(j
= 0; j
< sf
->nsweepparam
; j
++) {
507 fprintf(of
, "'%s'", sf
->spar
[j
].name
);
513 fprintf(of
, "'sweeprows':(");
514 if(sf
->ntables
> 1) {
515 for(i
=0; i
< sf
->ntables
; i
++) {
516 fprintf(of
, "(%d,%d),", sweeprows
[2*i
], sweeprows
[2*i
+1]);
521 fprintf(of
, "'cols':(");
522 for(i
= 0; i
< nidx
; i
++) {
523 if(indices
[i
] == 0) {
524 ss_var_name(sf
->ivar
, 0, buf
, 1024);
525 fprintf(of
, "'%s', ", buf
);
527 int varno
= indices
[i
]-1;
528 SpiceVar
*dvar
= ss_dvar(sf
, varno
);
529 int dcolno
= dvar
->col
- 1;
530 for(j
= 0; j
< dvar
->ncols
; j
++) {
531 ss_var_name(dvar
, j
, buf
, 1024);
532 fprintf(of
, "'%s', ", buf
);
538 /* space-pad to end on a 16 byte boundary */
539 while(((ftell(of
)+3) % 16) != 0) {
543 foot_len
= ftell(of
) - foot_start
+ 2;
544 fputc((0xFF & foot_len
), of
);
545 fputc((0xFF & (foot_len
>> 8)), of
);
553 * print data as a .npy format array
554 * See numpy/lib/format.py for details of the .npy file format.
557 numpy_output(SpiceStream
*sf
, int *indices
, int nidx
,
558 double begin_val
, double end_val
, int ndigits
, FILE *of
)
565 int ndims
= 0, ncols
= 0, nrows
= 0;
569 char npy_descr
[5], npy_preamble
[NPY_PREAMBLE_LEN
], npy_header
[NPY_MAX_HDR_LEN
];
578 * write sham npy preamble + header to reserve space, don't know nrows yet
583 npy_hlen
= numpy_header_output(ndims
, shape
, -1, of
);
586 dvals
= g_new(double, sf
->ncols
);
587 sweeprows
= g_new(int, 2 * sf
->ntables
);
588 if(sf
->nsweepparam
> 0)
589 spar
= g_new(double, sf
->nsweepparam
);
594 sweeprows
[2*tab
] = nrows
;
596 if(sf
->nsweepparam
> 0) {
597 if(ss_readsweep(sf
, spar
) <= 0)
601 while((rc
= ss_readrow(sf
, &ival
, dvals
)) > 0) {
605 /* past end_val, but can only stop reading
606 early if if there is only one sweep-table
615 if(sf
->nsweepparam
> 0) {
616 for(i
= 0; i
< sf
->nsweepparam
; i
++) {
617 val
= (float)spar
[i
];
618 fwrite(&val
, sizeof(float), 1, of
);
621 for(i
= 0; i
< nidx
; i
++) {
622 if(indices
[i
] == 0) {
624 fwrite(&val
, sizeof(float), 1, of
);
626 int varno
= indices
[i
]-1;
627 SpiceVar
*dvar
= ss_dvar(sf
, varno
);
628 int dcolno
= dvar
->col
- 1;
629 for(j
= 0; j
< dvar
->ncols
; j
++) {
630 val
= (float)dvals
[dcolno
+j
];
631 fwrite(&val
, sizeof(float), 1, of
);
637 if(rc
== -2) { /* end of sweep, more follow */
638 if(sf
->nsweepparam
== 0)
639 sweep_mode
= SWEEP_HEAD
;
641 sweeprows
[2*tab
+1] = nrows
-1;
643 sweeprows
[2*tab
] = nrows
;
644 } else { /* EOF or error */
646 sweeprows
[2*tab
+1] = nrows
-1;
653 /* write trailing string representation of a python dict describing the data */
654 foot_len
= numpy_footer_output(sf
, indices
, nidx
, sweeprows
, of
);
658 * go back and overwrite npy header with correct values
660 fseek(of
, 0, SEEK_SET
);
662 /* calc num cols of data, second dimension */
663 ncols
= sf
->nsweepparam
;
664 for(i
= 0; i
< nidx
; i
++) {
665 if(indices
[i
] == 0) { /* independent var */
668 int varno
= indices
[i
]-1;
669 SpiceVar
*dvar
= ss_dvar(sf
, varno
);
670 ncols
+= dvar
->ncols
;
675 int npy_hlen2
= numpy_header_output(ndims
, shape
, npy_hlen
, of
);
677 if(npy_hlen2
!= npy_hlen
) {
678 fprintf(stderr
, "numpy OOPS, inconsistent header lengths\n");
693 static int parse_field_numbers(int **indices
, int *idxsize
, int *nidx
,
694 char *list
, int nfields
)
700 if(!*indices
|| idxsize
== 0) {
701 *idxsize
= nfields
*2;
702 idx
= g_new0(int, *idxsize
);
707 fnum
= strtok(list
, ", \t");
710 if(*nidx
>= *idxsize
) {
712 idx
= g_realloc(idx
, (*idxsize
) * sizeof(int));
716 if(n
< 0 || n
>= nfields
) {
717 fprintf(stderr
, "bad field number in -n option: %s\n", fnum
);
723 fnum
= strtok(NULL
, ", \t");
730 * Try looking for named dependent variable. Try twice,
731 * first as-is, then with "v(" prepended the way hspice mangles things.
733 static int find_dv_by_name(char *name
, SpiceStream
*sf
)
738 for(i
= 0; i
< sf
->ndv
; i
++) {
739 dvar
= ss_dvar(sf
, i
);
740 if(strcasecmp(name
, dvar
->name
) == 0)
743 for(i
= 0; i
< sf
->ndv
; i
++) {
744 dvar
= ss_dvar(sf
, i
);
745 if(strncasecmp("v(", dvar
->name
, 2) == 0
746 && strcasecmp(name
, &dvar
->name
[2]) == 0)
753 * parse comma-seperated list of field names. Turn on the output-enables
754 * for the listed fields.
756 static int parse_field_names(int **indices
, int *idxsize
, int *nidx
,
757 char *list
, SpiceStream
*sf
)
765 if(!*indices
|| idxsize
== 0) {
766 *idxsize
= (sf
->ndv
+1)*2;
767 idx
= g_new0(int, *idxsize
);
772 fld
= strtok(list
, ", \t");
775 if(*nidx
>= *idxsize
) {
777 idx
= g_realloc(idx
, (*idxsize
) * sizeof(int));
780 if(strcasecmp(fld
, sf
->ivar
->name
)==0) {
783 } else if((n
= find_dv_by_name(fld
, sf
)) >= 0) {
787 fprintf(stderr
, "field name in -f option not found in file: %s\n", fld
);
790 fld
= strtok(NULL
, ", \t");
800 static struct vtlistel vtlist
[] = {
804 {VOLTAGE
, "voltage"},
805 {CURRENT
, "current"},
808 {FREQUENCY
, "frequency"},
809 {FREQUENCY
, "hertz"},
814 * Given a variable type name, return a numeric VarType.
815 * Returns 0 (UNKNOWN) if no match.
817 static VarType
get_vartype_code(char *vartype
)
820 for(i
= 0; vtlist
[i
].s
; i
++) {
821 if(strcasecmp(vartype
, vtlist
[i
].s
) == 0)
827 * vim:tabstop=4 noexpandtab