1 /**********
2 * Copyright 1992 Regents of the University of California. All rights reserved.
3 * Author: 1992 David A. Gates, U. C. Berkeley CAD Group
4 *
5 * Author: 2008 Stefano Pedretti
6 **********/
7
8 /*
9 * gnuplot plots.
10 */
11
12 #include "ngspice/ngspice.h"
13 #include "ngspice/cpdefs.h"
14 #include "ngspice/ftedefs.h"
15 #include "ngspice/dvec.h"
16 #include "ngspice/fteparse.h"
17 #include "gnuplot.h"
18 #if defined(__MINGW32__) || defined(_MSC_VER)
19 #undef BOOLEAN
20 #include <windows.h>
21 #else
22 #include <unistd.h>
23 #endif
24 #include <locale.h>
25
26 #define GP_MAXVECTORS 64
27
28
29 static void
quote_gnuplot_string(FILE * stream,char * s)30 quote_gnuplot_string(FILE *stream, char *s)
31 {
32 fputc('"', stream);
33
34 for (; *s; s++)
35 /* NOTE: The FALLTHROUGH comment is used to suppress a GCC warning
36 * when flag -Wimplicit-fallthrough is present */
37 switch (*s) {
38 case '\n':
39 fputs("\\n", stream);
40 break;
41 case '"':
42 case '\\':
43 fputc('\\', stream);
44 /* FALLTHROUGH */
45 default:
46 fputc(*s, stream);
47 }
48
49 fputc('"', stream);
50 }
51
52
ft_gnuplot(double * xlims,double * ylims,double xdel,double ydel,const char * filename,const char * title,const char * xlabel,const char * ylabel,GRIDTYPE gridtype,PLOTTYPE plottype,struct dvec * vecs)53 void ft_gnuplot(double *xlims, double *ylims,
54 double xdel, double ydel,
55 const char *filename, const char *title,
56 const char *xlabel, const char *ylabel,
57 GRIDTYPE gridtype, PLOTTYPE plottype,
58 struct dvec *vecs)
59 {
60 FILE *file, *file_data;
61 struct dvec *v, *scale = NULL;
62 double xval, yval, prev_xval, extrange;
63 int i, dir, numVecs, linewidth, gridlinewidth, err, terminal_type;
64 bool xlog, ylog, nogrid, markers, nolegend;
65 char buf[BSIZE_SP], pointstyle[BSIZE_SP], *text, plotstyle[BSIZE_SP], terminal[BSIZE_SP];
66
67 char filename_data[128];
68 char filename_plt[128];
69
70 #ifdef SHARED_MODULE
71 char* llocale = setlocale(LC_NUMERIC, NULL);
72 setlocale(LC_NUMERIC, "C");
73 #endif
74
75 snprintf(filename_data, 128, "%s.data", filename);
76 snprintf(filename_plt, 128, "%s.plt", filename);
77
78 /* Sanity checking. */
79 for (v = vecs, numVecs = 0; v; v = v->v_link2)
80 numVecs++;
81
82 if (numVecs == 0) {
83 return;
84 } else if (numVecs > GP_MAXVECTORS) {
85 fprintf(cp_err, "Error: too many vectors for gnuplot.\n");
86 return;
87 }
88
89 if (ylims && (fabs((ylims[1]-ylims[0])/ylims[0]) < 1.0e-6)) {
90 fprintf(cp_err, "Error: range min ... max too small for using gnuplot.\n");
91 fprintf(cp_err, " Consider plotting with offset %g.\n", ylims[0]);
92 return;
93 }
94
95 extrange = 0.05 * (ylims[1] - ylims[0]);
96
97 if (!cp_getvar("gnuplot_terminal", CP_STRING,
98 terminal, sizeof(terminal))) {
99 terminal_type = 1;
100 }
101 else {
102 terminal_type = 1;
103 if (cieq(terminal,"png")) {
104 terminal_type = 2;
105 }
106 else if (cieq(terminal,"png/quit")) {
107 terminal_type = 3;
108 }
109 else if (cieq(terminal, "eps")) {
110 terminal_type = 4;
111 }
112 else if (cieq(terminal, "eps/quit")) {
113 terminal_type = 5;
114 }
115 else if (cieq(terminal, "xterm")) {
116 terminal_type = 6;
117 }
118 }
119
120 /* get linewidth for plotting the graph from .spiceinit */
121 if (!cp_getvar("xbrushwidth", CP_NUM, &linewidth, 0))
122 linewidth = 1;
123 if (linewidth < 1)
124 linewidth = 1;
125 /* get linewidth for grid from .spiceinit */
126 if (!cp_getvar("xgridwidth", CP_NUM, &gridlinewidth, 0))
127 gridlinewidth = 1;
128 if (gridlinewidth < 1)
129 gridlinewidth = 1;
130
131
132 if (!cp_getvar("pointstyle", CP_STRING, pointstyle, sizeof(pointstyle))) {
133 markers = FALSE;
134 } else {
135 if (cieq(pointstyle,"markers"))
136 markers = TRUE;
137 else
138 markers = FALSE;
139 }
140
141 if (!cp_getvar("nolegend", CP_BOOL, NULL, 0)) {
142 nolegend = FALSE;
143 }
144 else {
145 nolegend = TRUE;
146 }
147
148 /* Make sure the gridtype is supported. */
149 switch (gridtype) {
150 case GRID_LIN:
151 nogrid = xlog = ylog = FALSE;
152 break;
153 case GRID_XLOG:
154 xlog = TRUE;
155 nogrid = ylog = FALSE;
156 break;
157 case GRID_YLOG:
158 ylog = TRUE;
159 nogrid = xlog = FALSE;
160 break;
161 case GRID_LOGLOG:
162 xlog = ylog = TRUE;
163 nogrid = FALSE;
164 break;
165 case GRID_NONE:
166 nogrid = TRUE;
167 xlog = ylog = FALSE;
168 break;
169 default:
170 fprintf(cp_err, "Error: grid type unsupported by gnuplot.\n");
171 return;
172 }
173
174 /* Open the output gnuplot file. */
175 if ((file = fopen(filename_plt, "w")) == NULL) {
176 perror(filename);
177 return;
178 }
179
180 /* Set up the file header. */
181 #if !defined(__MINGW32__) && !defined(_MSC_VER) && !defined(__CYGWIN__)
182 fprintf(file, "set terminal X11 noenhanced\n");
183 #elif defined(__CYGWIN__)
184 #ifndef EXT_ASC
185 fprintf(file, "set encoding utf8\n");
186 #endif
187 #else
188 #ifndef EXT_ASC
189 fprintf(file, "set encoding utf8\n");
190 #endif
191 fprintf(file, "set termoption noenhanced\n");
192 #endif
193 if (title) {
194 text = cp_unquote(title);
195 fprintf(file, "set title ");
196 quote_gnuplot_string(file, text);
197 fprintf(file, "\n");
198 tfree(text);
199 }
200 if (xlabel) {
201 text = cp_unquote(xlabel);
202 fprintf(file, "set xlabel ");
203 quote_gnuplot_string(file, text);
204 fprintf(file, "\n");
205 tfree(text);
206 }
207 if (ylabel) {
208 text = cp_unquote(ylabel);
209 fprintf(file, "set ylabel ");
210 quote_gnuplot_string(file, text);
211 fprintf(file, "\n");
212 tfree(text);
213 }
214 if (!nogrid) {
215 if (gridlinewidth > 1)
216 fprintf(file, "set grid lw %d \n" , gridlinewidth);
217 else
218 fprintf(file, "set grid\n");
219 }
220 if (xlog) {
221 fprintf(file, "set logscale x\n");
222 if (xlims)
223 fprintf(file, "set xrange [%1.0e:%1.0e]\n",
224 pow(10, floor(log10(xlims[0]))), pow(10, ceil(log10(xlims[1]))));
225 fprintf(file, "set mxtics 10\n");
226 fprintf(file, "set grid mxtics\n");
227 } else {
228 fprintf(file, "unset logscale x \n");
229 if (xlims)
230 fprintf(file, "set xrange [%e:%e]\n", xlims[0], xlims[1]);
231 }
232 if (ylog) {
233 fprintf(file, "set logscale y \n");
234 if (ylims)
235 fprintf(file, "set yrange [%1.0e:%1.0e]\n",
236 pow(10, floor(log10(ylims[0]))), pow(10, ceil(log10(ylims[1]))));
237 fprintf(file, "set mytics 10\n");
238 fprintf(file, "set grid mytics\n");
239 } else {
240 fprintf(file, "unset logscale y \n");
241 if (ylims)
242 fprintf(file, "set yrange [%e:%e]\n", ylims[0] - extrange, ylims[1] + extrange);
243 }
244
245 if (xdel > 0.)
246 fprintf(file, "set xtics %e\n", xdel);
247 else
248 fprintf(file, "#set xtics 1\n");
249 fprintf(file, "#set x2tics 1\n");
250 if (ydel > 0.)
251 fprintf(file, "set ytics %e\n", ydel);
252 else
253 fprintf(file, "#set ytics 1\n");
254 fprintf(file, "#set y2tics 1\n");
255
256 if (gridlinewidth > 1)
257 fprintf(file, "set border lw %d\n", gridlinewidth);
258
259 if(nolegend)
260 fprintf(file, "set key off\n");
261
262 if (plottype == PLOT_COMB) {
263 strcpy(plotstyle, "boxes");
264 } else if (plottype == PLOT_POINT) {
265 if (markers) {
266 // fprintf(file, "Markers: True\n");
267 } else {
268 // fprintf(file, "LargePixels: True\n");
269 }
270 strcpy(plotstyle, "points");
271 } else {
272 strcpy(plotstyle, "lines");
273 }
274
275 /* Open the output gnuplot data file. */
276 if ((file_data = fopen(filename_data, "w")) == NULL) {
277 perror(filename);
278 return;
279 }
280 fprintf(file, "set format y \"%%g\"\n");
281 fprintf(file, "set format x \"%%g\"\n");
282
283 if ((terminal_type != 3) && (terminal_type != 5)) {
284 fprintf(file, "plot ");
285 i = 0;
286
287 /* Write out the gnuplot command */
288 for (v = vecs; v; v = v->v_link2) {
289 scale = v->v_scale;
290 if (v->v_name) {
291 i = i + 2;
292 if (i > 2) fprintf(file, ",\\\n");
293 fprintf(file, "\'%s\' using %d:%d with %s lw %d title ",
294 filename_data, i - 1, i, plotstyle, linewidth);
295 quote_gnuplot_string(file, v->v_name);
296 }
297 }
298 fprintf(file, "\n");
299 }
300
301 /* terminal_type
302 1: do not print an eps or png file
303 2: print png file, keep command window open
304 3: print png file, quit command window
305 4: print eps file, keep command window open
306 5: print eps file, quit command window
307 */
308 if ((terminal_type == 2) || (terminal_type == 4))
309 fprintf(file, "set terminal push\n");
310 if ((terminal_type == 4) || (terminal_type == 5)) {
311 fprintf(file, "set terminal postscript eps color noenhanced\n");
312 fprintf(file, "set out \'%s.eps\'\n", filename);
313 }
314 if ((terminal_type == 2) || (terminal_type == 3)) {
315 fprintf(file, "set terminal png noenhanced\n");
316 fprintf(file, "set out \'%s.png\'\n", filename);
317 }
318 if ((terminal_type == 2) || (terminal_type == 4)) {
319 fprintf(file, "replot\n");
320 fprintf(file, "set term pop\n");
321 fprintf(file, "replot\n");
322 }
323
324 if ((terminal_type == 3) || (terminal_type == 5)) {
325 fprintf(file, "plot ");
326 i = 0;
327
328 /* Write out the gnuplot command */
329 for (v = vecs; v; v = v->v_link2) {
330 scale = v->v_scale;
331 if (v->v_name) {
332 i = i + 2;
333 if (i > 2) fprintf(file, ",\\\n");
334 fprintf(file, "\'%s\' using %d:%d with %s lw %d title ",
335 filename_data, i - 1, i, plotstyle, linewidth);
336 quote_gnuplot_string(file, v->v_name);
337 }
338 }
339 fprintf(file, "\n");
340 fprintf(file, "exit\n");
341 }
342
343
344
345 (void) fclose(file);
346
347 /* Write out the data and setup arrays */
348 bool mono = (plottype != PLOT_RETLIN);
349 dir = 0;
350 prev_xval = NAN;
351 for (i = 0; i < scale->v_length; i++) {
352 for (v = vecs; v; v = v->v_link2) {
353 scale = v->v_scale;
354
355 xval = isreal(scale) ?
356 scale->v_realdata[i] : realpart(scale->v_compdata[i]);
357
358 yval = isreal(v) ?
359 v->v_realdata[i] : realpart(v->v_compdata[i]);
360
361 if (i > 0 && (mono || (scale->v_plot && scale->v_plot->pl_scale == scale))) {
362 if (dir * (xval - prev_xval) < 0) {
363 /* direction reversal, start a new graph */
364 fprintf(file_data, "\n");
365 dir = 0;
366 } else if (!dir && xval > prev_xval) {
367 dir = 1;
368 } else if (!dir && xval < prev_xval) {
369 dir = -1;
370 }
371 }
372
373 fprintf(file_data, "%e %e ", xval, yval);
374
375 prev_xval = xval;
376 }
377 fprintf(file_data, "\n");
378 }
379
380 (void) fclose(file_data);
381
382 #if defined(__MINGW32__) || defined(_MSC_VER)
383 /* for external fcn system() */
384 // (void) sprintf(buf, "start /B wgnuplot %s -" , filename_plt);
385 (void) sprintf(buf, "start /B wgnuplot -persist %s " , filename_plt);
386 _flushall();
387 #else
388 /* for external fcn system() from LINUX environment */
389 if (terminal_type == 3) {
390 fprintf(cp_out, "writing plot to file %s.png\n", filename);
391 (void) sprintf(buf, "gnuplot %s", filename_plt);
392 }
393 else if (terminal_type == 5) {
394 fprintf(cp_out, "writing plot to file %s.eps\n", filename);
395 (void) sprintf(buf, "gnuplot %s", filename_plt);
396 }
397 else if (terminal_type == 6) {
398 (void) sprintf(buf, "xterm -e gnuplot %s - &", filename_plt);
399 }
400 else {
401 (void) sprintf(buf, "gnuplot -persist %s &", filename_plt);
402 }
403 #endif
404 err = system(buf);
405
406 /* delete the plt and data files */
407 if ((terminal_type == 3) || (terminal_type == 5)) {
408 /* wait for gnuplot generating eps or png file */
409 #if defined(__MINGW32__) || defined(_MSC_VER)
410 Sleep(200);
411 #else
412 usleep(200000);
413 #endif
414 if (remove(filename_data)) {
415 fprintf(stderr, "Could not remove file %s\n", filename_data);
416 perror(NULL);
417 }
418 if (remove(filename_plt)) {
419 fprintf(stderr, "Could not remove file %s\n", filename_plt);
420 perror(NULL);
421 }
422 }
423 #ifdef SHARED_MODULE
424 /* go back to what it was before */
425 setlocale(LC_NUMERIC, llocale);
426 #endif
427 }
428
429
430 /* simple printout of data into a file, similar to data table in ft_gnuplot
431 command: wrdata file vecs, vectors of different length (from different plots)
432 may be printed. Data are written in pairs: scale vector, value vector. If
433 data are complex, a triple is printed (scale, real, imag).
434 Setting 'singlescale' as variable, the scale vector will be printed once only,
435 if scale vectors are of same length (there is little risk here!).
436 Width of numbers printed is set by option 'numdgt'.
437 */
ft_writesimple(double * xlims,double * ylims,const char * filename,const char * title,const char * xlabel,const char * ylabel,GRIDTYPE gridtype,PLOTTYPE plottype,struct dvec * vecs)438 void ft_writesimple(double *xlims, double *ylims,
439 const char *filename, const char *title,
440 const char *xlabel, const char *ylabel,
441 GRIDTYPE gridtype, PLOTTYPE plottype,
442 struct dvec *vecs)
443 {
444 FILE *file_data;
445 struct dvec *v;
446 int i, numVecs, maxlen, preci;
447 bool appendwrite, singlescale, vecnames;
448
449 NG_IGNORE(xlims);
450 NG_IGNORE(ylims);
451 NG_IGNORE(title);
452 NG_IGNORE(xlabel);
453 NG_IGNORE(ylabel);
454 NG_IGNORE(gridtype);
455 NG_IGNORE(plottype);
456
457 appendwrite = cp_getvar("appendwrite", CP_BOOL, NULL, 0);
458 singlescale = cp_getvar("wr_singlescale", CP_BOOL, NULL, 0);
459 vecnames = cp_getvar("wr_vecnames", CP_BOOL, NULL, 0);
460
461 /* Sanity checking. */
462 for (v = vecs, numVecs = 0; v; v = v->v_link2)
463 numVecs++;
464
465 if (numVecs == 0)
466 return;
467
468 /* print scale vector only once */
469 if (singlescale) {
470 /* check if all vectors have equal scale length */
471 maxlen = vecs->v_length; /* first length of vector read */
472 for (v = vecs; v; v = v->v_link2)
473 if (v->v_scale->v_length != maxlen) {
474 fprintf(stderr,
475 "Error: Option 'singlescale' not possible.\n"
476 " Vectors %s and %s have different lengths!\n"
477 " No data written to %s!\n\n",
478 vecs->v_name, v->v_name, filename);
479 return;
480 }
481 }
482 else {
483 /* find maximum scale length from all vectors */
484 maxlen = 0;
485 for (v = vecs; v; v = v->v_link2)
486 maxlen = MAX(v->v_scale->v_length, maxlen);
487 }
488
489 /* Open the output data file. */
490 if ((file_data = fopen(filename, appendwrite ? "a" : "w")) == NULL) {
491 perror(filename);
492 return;
493 }
494
495 /* If option numdgt is set, use it for printout precision. */
496 if (cp_numdgt > 0)
497 preci = cp_numdgt;
498 else
499 preci = 8;
500
501 /* Print names of vectors to first line */
502 if (vecnames) {
503 bool prscale = TRUE;
504 for (v = vecs; v; v = v->v_link2) {
505 struct dvec *scale = v->v_scale;
506 /* If wr_singlescale is set, print scale name only in first column */
507 if (prscale)
508 fprintf(file_data, " %-*s", preci + 7, scale->v_name);
509
510 if (isreal(v))
511 fprintf(file_data, " %-*s", preci + 7, v->v_name);
512 else
513 fprintf(file_data, " %-*s %-*s", preci + 7, v->v_name, preci + 7, v->v_name);
514 if (singlescale)
515 /* the following names are printed without scale vector names */
516 prscale = FALSE;
517 }
518 fprintf(file_data, "\n");
519 }
520
521 /* Write out the data as simple arrays */
522 for (i = 0; i < maxlen; i++) {
523 bool prscale = TRUE;
524 /* print scale from the first vector, then only if wr_singlescale is not set */
525 for (v = vecs; v; v = v->v_link2) {
526 struct dvec *scale = v->v_scale;
527 /* if no more scale and value data, just print spaces */
528 if (i >= scale->v_length) {
529 if (prscale)
530 fprintf(file_data, "%*s", preci + 8, "");
531
532 if (isreal(v))
533 fprintf(file_data, "%*s", preci + 8, "");
534 else
535 fprintf(file_data, "%*s", 2 * (preci + 8), "");
536 }
537 else {
538 if (prscale) {
539 double xval = isreal(scale)
540 ? scale->v_realdata[i]
541 : realpart(scale->v_compdata[i]);
542 fprintf(file_data, "% .*e ", preci, xval);
543 }
544
545 if (isreal(v))
546 fprintf(file_data, "% .*e ", preci, v->v_realdata[i]);
547 else
548 fprintf(file_data, "% .*e % .*e ", preci, realpart(v->v_compdata[i]), preci, imagpart(v->v_compdata[i]));
549 }
550 if (singlescale)
551 /* the following vectors are printed without scale vector */
552 prscale = FALSE;
553 }
554 fprintf(file_data, "\n");
555 }
556
557 (void) fclose(file_data);
558 }
559