1 /*
2  * ss_nsout.c: routines for SpiceStream that handle the ".out" file format
3  * 	from Synopsis' nanosim.
4  *
5  * Copyright (C) 2004  Stephen G. Tell
6  *
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.
11  *
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.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22 
23 #include "ssintern.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <float.h>
30 
31 #include <config.h>
32 #include <glib.h>
33 #include "spicestream.h"
34 
35 static int sf_readrow_nsout(SpiceStream *sf, double *ivar, double *dvars);
36 static char *msgid = "nsout";
37 
38 struct nsvar {
39 	char *name;
40 	int index;
41 	VarType type;
42 };
43 
44 /* convert variable type string from out-file to
45  * our type numbers
46  */
47 static VarType
sf_str2type_nsout(char * s)48 sf_str2type_nsout(char *s)
49 {
50 	if(strcasecmp(s, "v") == 0)
51 		return VOLTAGE;
52 	else if(strcasecmp(s, "i") == 0)
53 		return CURRENT;
54 	else return UNKNOWN;
55 }
56 
57 
58 /* Read spice-type file header - nanosim "out" format */
59 SpiceStream *
sf_rdhdr_nsout(char * name,FILE * fp)60 sf_rdhdr_nsout(char *name, FILE *fp)
61 {
62 	SpiceStream *sf = NULL;
63 	char *line = NULL;
64 	int lineno = 0;
65 	int linesize = 1024;
66 	char *key, *val;
67 	int got_ivline = 0;
68 	int ndvars;
69 	double voltage_resolution = 1.0;
70 	double current_resolution = 1.0;
71 	double time_resolution = 1.0;
72 	GList *vlist = NULL;
73 	struct nsvar *nsv;
74 	int i;
75 	int maxindex = 0;
76 
77 	while(fread_line(fp, &line, &linesize) != EOF) {
78 		lineno++;
79 		if(lineno == 1 && strncmp(line, ";! output_format", 16)) {
80 			/* not an out file; bail out */
81 			ss_msg(DBG, msgid, "%s:%d: Doesn't look like an ns-out file; \"output_format\" expected\n", name, lineno);
82 
83 			return NULL;
84 		}
85 		if(line[0] == ';')
86 			continue;
87 
88 		if(line[0] == '.') {
89 			key = strtok(&line[1], " \t");
90 			if(!key) {
91 				ss_msg(ERR, msgid, "%s:%d: syntax error, expected \"keyword:\"", name, lineno);
92 				g_free(line);
93 				return NULL;
94 			}
95 			if(strcmp(key, "time_resolution") == 0) {
96 				val = strtok(NULL, " \t\n");
97 				if(!val) {
98 					ss_msg(ERR, msgid, "%s:%d: syntax error, expected number", name, lineno);
99 					g_free(line);
100 					return NULL;
101 				}
102 				time_resolution = atof(val);
103 			}
104 			if(strcmp(key, "current_resolution") == 0) {
105 				val = strtok(NULL, " \t\n");
106 				if(!val) {
107 					ss_msg(ERR, msgid, "%s:%d: syntax error, expected number", name, lineno);
108 					g_free(line);
109 					return NULL;
110 				}
111 				current_resolution = atof(val);
112 			}
113 			if(strcmp(key, "voltage_resolution") == 0) {
114 				val = strtok(NULL, " \t\n");
115 				if(!val) {
116 					ss_msg(ERR, msgid, "%s:%d: syntax error, expected number", name, lineno);
117 					g_free(line);
118 					return NULL;
119 				}
120 				voltage_resolution = atof(val);
121 			}
122 			if(strcmp(key, "index") == 0) {
123 				nsv = g_new0(struct nsvar, 1);
124 
125 				val = strtok(NULL, " \t\n");
126 				if(!val) {
127 					ss_msg(ERR, msgid, "%s:%d: syntax error, expected varname", name, lineno);
128 					goto err;
129 				}
130 				nsv->name = g_strdup(val);
131 
132 				val = strtok(NULL, " \t\n");
133 				if(!val) {
134 					ss_msg(ERR, msgid, "%s:%d: syntax error, expected var-index", name, lineno);
135 					goto err;
136 				}
137 				nsv->index = atoi(val);
138 				if(nsv->index > maxindex)
139 					maxindex = nsv->index;
140 
141 				val = strtok(NULL, " \t\n");
142 				if(!val) {
143 					ss_msg(ERR, msgid, "%s:%d: syntax error, expected variable type", name, lineno);
144 					goto err;
145 				}
146 				nsv->type = sf_str2type_nsout(val);
147 				vlist = g_list_append(vlist, nsv);
148 			}
149 		}
150 
151 		if(isdigit(line[0])) {
152 			got_ivline = 1;
153 			break;
154 		}
155 	}
156 	if(!vlist) {
157 		ss_msg(ERR, msgid, "%s:%d: no variable indices found in header", name, lineno);
158 	}
159 	if(!got_ivline) {
160 		ss_msg(ERR, msgid, "%s:%d: EOF without data-line in header", name, lineno);
161 		goto err;
162 	}
163 	ndvars = g_list_length(vlist);
164 
165 	sf = ss_new(fp, name, ndvars, 0);
166 	sf->time_resolution = time_resolution;
167 	sf->current_resolution = current_resolution;
168 	sf->voltage_resolution = voltage_resolution;
169 	sf->maxindex = maxindex;
170 	sf->datrow = g_new0(double, maxindex+1);
171 	sf->nsindexes = g_new0(int, ndvars);
172 	sf->ncols = 1;
173 	sf->ntables = 1;
174 	sf->ivar->name = g_strdup("TIME");
175 	sf->ivar->type = TIME;
176 	sf->ivar->col = 0;
177 
178 	for(i = 0; i < ndvars; i++) {
179 		nsv = g_list_nth_data(vlist, i);
180 
181 		sf->dvar[i].name = g_strdup(nsv->name);
182 		sf->dvar[i].type = nsv->type;
183 		sf->nsindexes[i] = nsv->index;
184 		sf->dvar[i].ncols = 1;
185 		sf->dvar[i].col = sf->ncols;
186 		sf->ncols += sf->dvar[i].ncols;
187 
188 		ss_msg(DBG, msgid, "dv[%d] \"%s\" nsindex=%d",
189 		       i, sf->dvar[i].name, sf->nsindexes[i]);
190 	}
191 
192 	sf->readrow = sf_readrow_nsout;
193 	sf->read_rows = 0;
194 
195 	sf->lineno = lineno;
196 	sf->linebuf = line;
197 	sf->lbufsize = linesize;
198 	ss_msg(DBG, msgid, "Done with header at offset 0x%lx", (long) ftello64(sf->fp));
199 
200 	return sf;
201 err:
202 	if(line)
203 		g_free(line);
204 	if(sf) {
205 		sf->fp = NULL;
206 		/* prevent ss_delete from cleaning up FILE*; ss_open callers
207 		   may rewind and try another format on failure. */
208 		ss_delete(sf);
209 	}
210 	return NULL;
211 }
212 
213 /*
214  * Read row of values from an out-format file
215  * upon call, line buffer should always contain the
216  * independent-variable line that starts this set of values.
217  */
218 static int
sf_readrow_nsout(SpiceStream * sf,double * ivar,double * dvars)219 sf_readrow_nsout(SpiceStream *sf, double *ivar, double *dvars)
220 {
221 	int i;
222 	int idx;
223 	char *sidx;
224 	char *sval;
225 	double v;
226 	double scale;
227 	SpiceVar *dvp;
228 
229 	if(feof(sf->fp)) {
230 		return 0;
231 	}
232 
233 	// process iv line
234 	v = atof(sf->linebuf) * sf->time_resolution * 1e-9; /* ns */
235 	*ivar = v;
236 
237 	// read and process dv lines until we see another iv line
238 	while(fread_line(sf->fp, &sf->linebuf, &sf->lbufsize) != EOF) {
239 		sf->lineno++;
240 		if(sf->linebuf[0] == ';')
241 			continue;
242 
243 		sidx = strtok(sf->linebuf, " \t");
244 		if(!sidx) {
245 			ss_msg(ERR, msgid, "%s:%d: expected value",
246 			       sf->filename, sf->lineno);
247 			return -1;
248 		}
249 
250 		sval = strtok(NULL, " \t");
251 		if(!sval)
252 			/* no value token: this is the ivar line for the
253 			    next row */
254 			break;
255 
256 		idx = atoi(sidx);
257 		if(idx <= sf->maxindex) {
258 			sf->datrow[idx] = atof(sval);
259 		}
260 	}
261 
262 	for(i = 0; i < sf->ndv; i++) {
263 		dvp = &sf->dvar[i];
264 		scale = 1.0;
265 		switch(dvp->type) {
266 		case VOLTAGE:
267 			scale = sf->voltage_resolution;
268 			break;
269 		case CURRENT:
270 			scale = sf->current_resolution;
271 			break;
272 		}
273 		dvars[i] = sf->datrow[ sf->nsindexes[i] ] * scale;
274 	}
275 
276 	return 1;
277 }
278 
279