1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Mon Feb  2 17:03:26 2009
19 ****************************************************************************/
20 /*
21     Postscript generator for tkgate.
22 */
23 
24 #ifdef __cplusplus
25 #include <cstdlib>
26 #include <cstdio>
27 #include <cstring>
28 #include <cassert>
29 #include <cmath>
30 #else
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <assert.h>
35 #include <math.h>
36 #endif
37 
38 #include <unistd.h>
39 #include <pwd.h>
40 #include <sys/time.h>
41 
42 #include "tkgate.h"
43 #include "print.h"
44 
45 /***********************************************************************/
46 
47 
choose_trace_interval(simtime_t * e_per_l,simtime_t * e_inter)48 static void choose_trace_interval(simtime_t *e_per_l,simtime_t *e_inter)
49 {
50   double n,m;
51   int l,h;
52 
53   /*
54    * Power of ten >= to e_per_l
55    */
56   n = pow(10.0,ceil(log((double)*e_per_l)/log(10.0)));
57 
58   m = (n-*e_per_l)/n;
59 
60   if (m >= 0.8)
61     m = 0.02;
62   else if (m >= 0.6)
63     m = 0.05;
64   else
65     m = 0.1;
66 
67   n = m*n;
68 
69   *e_inter = (int)n;
70 
71   l = (*e_per_l / *e_inter)* *e_inter;
72   h = l + *e_inter;
73 
74   if (*e_per_l-l < h-*e_per_l)
75     *e_per_l = l;
76   else
77     *e_per_l = h;
78 }
79 
80 
GPrint_setupTracePages(GPrint * P)81 void GPrint_setupTracePages(GPrint *P)
82 {
83   extern GScope *Scope;		/* The scope trace */
84   int hspace;			/* Horizontal space for traces on page */
85   int vspace;			/* Vertical space for traces on page */
86   int tsheight;			/* Height of a full trace set and scale */
87   int pg;			/* Page # */
88   int e_start;			/* Starting epoch # on a page */
89 
90   pg = 0;
91 
92   /*
93    * If no traces, output an empty page.
94    */
95   if (!Scope || Scope->NumTraces <= 0 || P->p_trace.ti_start >= P->p_trace.ti_end) {
96     P->p_numPages = 1;
97     P->p_pages = (GPage**) ob_malloc(sizeof(GPage*),"GPage*");
98     assert(P->p_pages);
99     P->p_pages[0] = new_GPage(PT_TRACE,1);
100     return;
101   }
102 
103   vspace = P->p_uHeight-PTRC_TMARGIN-PTRC_BMARGIN;		/* Max usable vert. space*/
104   hspace = P->p_uWidth-PTRC_LMARGIN-PTRC_RMARGIN;		/* Max usable horz. space*/
105   tsheight = Scope->NumTraces*PTRC_TRHEIGHT+PTRC_SCHEIGHT;	/* Ver. space per trace set */
106 
107   P->p_trace.ti_simEnd = Scope->s_time;
108 
109   /*
110    * Choose an interval and adjust epochs per line to be a multiple of the interval
111    */
112   choose_trace_interval(&P->p_trace.ti_scaleLength,&P->p_trace.ti_interval);
113 
114   /*
115    * Compute translation from epochs to points.
116    */
117   P->p_trace.ti_pointsPerEpoch = (double)hspace/(double)P->p_trace.ti_scaleLength;
118 
119   /*
120    * Round start down and end up to a multiple of the interval
121    */
122   P->p_trace.ti_istart = (P->p_trace.ti_start/P->p_trace.ti_interval)*P->p_trace.ti_interval;
123   P->p_trace.ti_iend = ((P->p_trace.ti_end+P->p_trace.ti_interval-1)/P->p_trace.ti_interval)*P->p_trace.ti_interval;
124 
125   if (tsheight <= vspace) {					/* 1 or more trace sets on a page */
126 
127     /*
128      * Compute the number of trace sets on a page, the number of epochs on a page and the
129      * number of pages in the document.  The number of pages per trace set is one.
130      */
131     P->p_trace.ti_tracesPerPage = (vspace/tsheight);
132     P->p_trace.ti_epochsPerPage = P->p_trace.ti_scaleLength*P->p_trace.ti_tracesPerPage;
133     P->p_numPages = (P->p_trace.ti_iend-P->p_trace.ti_istart+P->p_trace.ti_epochsPerPage-1)/P->p_trace.ti_epochsPerPage;
134     P->p_trace.ti_pagesPerTrace = 1;
135 
136     P->p_pages = (GPage**) ob_malloc(P->p_numPages*sizeof(GPage*),"GPage*[]");
137     assert(P->p_pages);
138 
139     for (e_start = P->p_trace.ti_istart;e_start < P->p_trace.ti_iend;e_start += P->p_trace.ti_epochsPerPage) {
140       GPage *G;
141 
142       assert(pg < P->p_numPages);
143 
144       G = new_GPage(PT_TRACE,pg+1);
145       P->p_pages[pg++] = G;
146 
147       G->pg_traceNum = 0;
148       G->pg_traceStart = e_start;
149       G->pg_traceEnd = e_start + P->p_trace.ti_epochsPerPage - 1;
150       if (G->pg_traceEnd > P->p_trace.ti_iend)
151 	G->pg_traceEnd = P->p_trace.ti_iend;
152     }
153   } else {						/* multiple pages per trace set */
154     int maxtr = (vspace-PTRC_SCHEIGHT)/PTRC_TRHEIGHT;	/*   Maximum number of traces per page */
155     int j;
156 
157     P->p_trace.ti_pagesPerTrace = (Scope->NumTraces+maxtr-1)/maxtr;
158     P->p_trace.ti_epochsPerPage = P->p_trace.ti_scaleLength;
159     P->p_numPages = ((P->p_trace.ti_iend-P->p_trace.ti_istart+P->p_trace.ti_epochsPerPage-1)/P->p_trace.ti_epochsPerPage);
160     P->p_numPages *= P->p_trace.ti_pagesPerTrace;
161     P->p_trace.ti_tracesPerPage = 1;
162 
163     P->p_pages = (GPage**) ob_malloc(P->p_numPages*sizeof(GPage*),"GPage*[]");
164     assert(P->p_pages);
165 
166     pg = 0;
167     for (e_start = P->p_trace.ti_istart;e_start < P->p_trace.ti_iend;e_start += P->p_trace.ti_epochsPerPage) {
168       for (j = 0;j < P->p_trace.ti_pagesPerTrace;j++) {
169 	GPage *G;
170 
171 	assert(pg < P->p_numPages);
172 
173 	G = new_GPage(PT_TRACE,pg);
174 	P->p_pages[pg++] = G;
175 
176 	G->pg_traceNum = j*maxtr;
177 	G->pg_traceStart = e_start;
178 	G->pg_traceEnd = e_start + P->p_trace.ti_epochsPerPage - 1;
179 	if (G->pg_traceEnd > P->p_trace.ti_iend)
180 	  G->pg_traceEnd = P->p_trace.ti_iend;
181 
182       }
183     }
184   }
185 
186 #if 0
187   printf("actual pages (p=%d (%gx%g) s=%d r=[%d..%d] ---> %d\n",(P->p_size-paperSizes),P->p_uWidth,P->p_uHeight,P->p_trace.scale,P->p_trace.ti_start,P->p_trace.ti_end,P->p_numPages);
188 #endif
189 }
190 
191 /*****************************************************************************
192  *
193  * Format a time value for printing
194  *
195  *****************************************************************************/
GPrint_formatTime(GPrint * P,char * buf,simtime_t t)196 char *GPrint_formatTime(GPrint *P,char *buf,simtime_t t)
197 {
198   SimInterface *si = &TkGate.circuit->simulator;
199   double n = t*si->si_tsmult/(double)si->si_precision;
200   double interval = P->p_trace.ti_interval*si->si_tsmult/(double)si->si_precision;
201   int units = si->si_units;
202 
203 
204   while (interval > 1000) {
205     interval /= 1000;
206     n /= 1000;
207     units--;
208   }
209 
210   switch (si->si_precision) {
211   case 1 :
212     sprintf(buf,"%0.0f%s",n,SimInterface_unitsToStr(units));
213     break;
214   case 10 :
215     sprintf(buf,"%0.1f%s",n,SimInterface_unitsToStr(units));
216     break;
217   case 100 :
218     sprintf(buf,"%0.2f%s",n,SimInterface_unitsToStr(units));
219     break;
220   default :
221     sprintf(buf,"%0.3f%s",n,SimInterface_unitsToStr(units));
222     break;
223   }
224 
225 
226   return buf;
227 }
228 
229 /*
230  * Print a scope trace document
231  */
GPrintOpt_tracePrint(GPrintOpt * PO)232 void GPrintOpt_tracePrint(GPrintOpt *PO)
233 {
234   GPrint *P = new_GPrint(PO);
235 
236   if (!P) return;
237 
238   GPrint_setupTracePages(P);
239   GPrint_outputPreamble(P,0);
240   GPrint_outputPages(P);
241   GPrint_outputTrailer(P);
242 
243   delete_GPrint(P);
244 }
245 
246 
247 /*****************************************************************************
248  *
249  * Returns an estimate of the number of lines of trace groups that can
250  * fit on a page.  If more than one page is needed, a value less than one
251  * will be returned.
252  *
253  * Parameters
254  *   orient			Page orientation
255  *   paper			Paper type
256  *
257  *****************************************************************************/
traceLinesPerPage(const char * orient,const char * paper)258 double traceLinesPerPage(const char *orient,const char *paper)
259 {
260   extern GScope *Scope;		/* The scope trace */
261   PaperSize *size = 0;
262   /** @TODO to remove */
263   double uHeight/*, uWidth*/;
264   double vspace,tsheight;
265   int i;
266 
267   /*
268    * If no active scope, return 1 page.
269    */
270   if (!Scope || Scope->NumTraces <= 0)
271     return 1.0;
272 
273   /*
274    * Search for the paper size
275    */
276   for (i = 0;paperSizes[i].ps_size;i++) {
277     if (strcmp(paperSizes[i].ps_size,paper) == 0) {
278       size = &paperSizes[i];
279       break;
280     }
281   }
282   if (!size) size = &paperSizes[0];
283 
284   /*
285    * Figure out height and width from paper size and orientation.
286    */
287   if (strcmp(orient,"landscape") == 0) {
288     /** @TODO uWidth = size->ps_height - PAGE_LMARGIN - PAGE_RMARGIN; */
289     uHeight = size->ps_width - PAGE_TMARGIN - PAGE_BMARGIN - PAGE_LBLOCK;;
290   } else {
291     /** @TODO uWidth = size->ps_width - PAGE_LMARGIN - PAGE_RMARGIN; */
292     uHeight = size->ps_height- PAGE_TMARGIN - PAGE_BMARGIN - PAGE_LBLOCK;
293   }
294 
295   vspace = uHeight-PTRC_TMARGIN-PTRC_BMARGIN;			/* Max usable vert. space*/
296   tsheight = Scope->NumTraces*PTRC_TRHEIGHT+PTRC_SCHEIGHT;	/* Ver. space per trace set */
297 
298   if (tsheight <= vspace) {
299     return floor(vspace/tsheight);		/* One or more trace groups fit on a page */
300   } else {
301     return 1.0/ceil(tsheight/vspace);		/* More than one page needed for a trace group */
302   }
303 }
304 
305 /*
306  * Output a single line of trace data for a single signal.
307  *
308  * Parameters:
309  *   P		Page on which to print
310  *   T		Trace data to print
311  *   y		y position of page of trace
312  *   pg_tstart	time value at left edge of page.
313  */
GPrint_outputTrace(GPrint * P,GTrace * T,int y,int pg_tstart)314 static void GPrint_outputTrace(GPrint *P,GTrace *T,int y,int pg_tstart)
315 {
316   simtime_t tend = imin(pg_tstart + P->p_trace.ti_scaleLength,P->p_trace.ti_end);
317   simtime_t tstart = imax(pg_tstart,P->p_trace.ti_start);
318   char buf[STRMAX];
319   GateValue *pV,*V;
320   const char *name;
321 
322   name = strchr(T->t_name,'.');
323   if (name)
324     name++;
325   else
326     name = T->t_name;
327 
328   fprintf(P->p_f,"%d %d %f %d gline\n",PTRC_TXTMARGIN,y,P->p_uWidth-PTRC_RMARGIN,y);
329   fprintf(P->p_f,"%d bfont\n",PTRC_FONTSIZE);
330   fprintf(P->p_f,"(%s) %d %d prshow\n",filterParen(buf,name),
331 	  PTRC_TXTMARGIN,y+PTRC_TRHEIGHT/2-PTRC_FONTSIZE/2);
332   fprintf(P->p_f,"%d rfont\n",PTRC_FONTSIZE);
333 
334   /*
335    * Scan for first value
336    */
337   pV = 0;
338   for (V = T->t_first;V->v_next && V->v_next->v_time < tstart;pV = V, V = V->v_next);
339 
340   fprintf(P->p_f,"%d %d %d %d BT\n",
341 	  PTRC_LMARGIN-1,y,(int)P->p_uWidth-PTRC_LMARGIN-PTRC_RMARGIN+2,PTRC_TRHEIGHT);
342 
343   for (;V && V->v_time <= tend;pV = V, V = V->v_next) {
344     simtime_t seg_start,seg_end;
345     simtime_t subseg_start, subseg_end;
346     double x1,x2;
347 
348     seg_start = V->v_time;
349     seg_end = V->v_next ? V->v_next->v_time : imin(tend,P->p_trace.ti_simEnd);
350     subseg_start = imax(imax(seg_start,pg_tstart),tstart);
351     subseg_end = imin(seg_end,tend);
352 
353     x1 = PTRC_LMARGIN+(subseg_start-pg_tstart)*P->p_trace.ti_pointsPerEpoch;
354     x2 = PTRC_LMARGIN+(subseg_end-pg_tstart)*P->p_trace.ti_pointsPerEpoch;
355 
356     if (V->v_hexValue) {
357       if (pV && (seg_start == subseg_start || pV->v_time > pg_tstart))
358 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x1,y+PTRC_TRLOW);
359       fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRLOW,x2,y+PTRC_TRLOW);
360       fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x2,y+PTRC_TRHIGH);
361 
362       fprintf(P->p_f,"(%s) %g %d %g trshow\n",V->v_hexValue,x1+PTRC_HEXPOS,y+PTRC_TRLOW+3,x2-x1-PTRC_HEXPOS);
363     } else {
364       if (pV && (seg_start == subseg_start || pV->v_time > pg_tstart)) {
365 	unsigned tt = (T->t_nBits == 1) ? transition_type(pV->v_code,V->v_code) : 0x3;
366 	switch (tt) {
367 	case 0x1 :
368 	  fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRLOW,x1,y+PTRC_TRMID);
369 	  break;
370 	case 0x2 :
371 	  fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x1,y+PTRC_TRMID);
372 	  break;
373 	case 0x3 :
374 	  fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x1,y+PTRC_TRLOW);
375 	  break;
376 	}
377       }
378 
379       switch (V->v_code) {
380       case VC_ZERO :
381 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRLOW,x2,y+PTRC_TRLOW);
382 	break;
383       case VC_ONE :
384 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x2,y+PTRC_TRHIGH);
385 	break;
386       case VC_UNKNOWN :
387 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRLOW,x2,y+PTRC_TRLOW);
388 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x2,y+PTRC_TRHIGH);
389 	break;
390       case VC_FLOAT :
391 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRMID,x2,y+PTRC_TRMID);
392 	break;
393       case VC_HIGH :
394 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRHIGH,x2,y+PTRC_TRHIGH);
395 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRMID,x2,y+PTRC_TRMID);
396 	break;
397       case VC_LOW :
398 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRLOW,x2,y+PTRC_TRLOW);
399 	fprintf(P->p_f,"%g %d %g %d line\n",x1,y+PTRC_TRMID,x2,y+PTRC_TRMID);
400 	break;
401       }
402     }
403   }
404 
405   fprintf(P->p_f,"ET\n");
406 }
407 
GPrint_outputTraceScale(GPrint * P,int y,simtime_t tstart)408 static void GPrint_outputTraceScale(GPrint *P,int y,simtime_t tstart)
409 {
410   simtime_t tend = tstart + P->p_trace.ti_scaleLength;
411   simtime_t t;
412   char buf[STRMAX];
413 
414   fprintf(P->p_f,"%d bfont\n",PTRC_FONTSIZE);
415   for (t = tstart;t <= tend;t += P->p_trace.ti_interval) {
416     double x =  PTRC_LMARGIN + (t-tstart)*P->p_trace.ti_pointsPerEpoch;
417     fprintf(P->p_f,"%g %d %g %d line\n",x,y,x,y-PTRC_SCTICK);
418 
419     if (t + P->p_trace.ti_interval <= tend)
420       fprintf(P->p_f,"(%s) %g %d rCT\n",GPrint_formatTime(P,buf,t),x,y-PTRC_SCLABPOS);
421   }
422 }
423 
424 
GPrint_printTracePage(GPrint * P,GPage * PG)425 void GPrint_printTracePage(GPrint *P,GPage *PG)
426 {
427   extern GScope *Scope;
428   int t,ts,top_y,y;
429   char buf1[STRMAX],buf2[STRMAX];
430 
431   fprintf(P->p_f,"(%d of %d) (%s-%s) BP_T\n",
432 	  PG->pg_num,P->p_numPages,
433 	  GPrint_formatTime(P,buf1,imax(PG->pg_traceStart,P->p_trace.ti_start)),
434 	  GPrint_formatTime(P,buf2,imin(PG->pg_traceEnd,P->p_trace.ti_end)));
435 
436   y = P->p_uHeight - PTRC_TMARGIN;
437 
438   for (ts = 0;ts < P->p_trace.ti_tracesPerPage;ts++) {
439     simtime_t l_start = PG->pg_traceStart+ts*P->p_trace.ti_scaleLength;
440 
441     if (l_start >= PG->pg_traceEnd) break;
442 
443     top_y = y;
444     for (t = PG->pg_traceNum;t < Scope->NumTraces;t++) {
445       GTrace *T = Scope->Traces[t];				/* Trace to display */
446       y -= PTRC_TRHEIGHT;					/* Position of trace baseline */
447       if (y - PTRC_SCHEIGHT < 0) break;				/* Page is full */
448 
449       GPrint_outputTrace(P,T,y,l_start);
450     }
451 
452     fprintf(P->p_f,"%d %d %d %d bold_box\n",0,y-PTRC_SCBXHEIGHT,(int)P->p_uWidth,top_y-y+PTRC_SCBXHEIGHT);
453 
454     GPrint_outputTraceScale(P,y,l_start);
455 
456     y -= PTRC_SCHEIGHT;
457   }
458 
459   fprintf(P->p_f,"EP\n");
460 }
461