Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!husc6!rutgers!super.upenn.edu!dsl.cis.upenn.edu!bradley From: bradley@dsl.cis.upenn.edu (John Bradley) Newsgroups: comp.windows.x Subject: xsplot - a SPICE plotting program for X V10.4 (part 3 of 4) Message-ID: <1843@super.upenn.edu> Date: Mon, 24-Aug-87 19:08:40 EDT Article-I.D.: super.1843 Posted: Mon Aug 24 19:08:40 1987 Date-Received: Tue, 25-Aug-87 06:50:11 EDT References: <1841@super.upenn.edu> Sender: news@super.upenn.edu Reply-To: bradley@dsl.cis.upenn.edu.UUCP (John Bradley) Distribution: na Organization: University of Pennsylvania Lines: 1757 xsplot is an X program (10.4) for viewing, plotting, and analyzing SPICE output. It will drive an HP-GL plotter for hardcopy. It can be used to look at non-SPICE data, also. Why, it could be the Neatest Thing Ever. Probably not, though. Runs just dandy on Berkeley 4.2 Unix variants (on the IBM RT under 4.2 and 4.3, and on random Vaxen under Ultrix 1.2 and 2.0) --John Bradley - University of Pennsylvania - bradley@cis.upenn.edu Part 3 of 4 ----------------------cut here------------------- #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -d ./xsplot` then mkdir ./xsplot echo "mkdir ./xsplot" fi if `test ! -s ./xsplot/xsplot.c` then echo "writing ./xsplot/xsplot.c" cat > ./xsplot/xsplot.c << '\Rogue\Monster\' /* * xsplot.c - Spice Plotting program for the X Window system * * Author: John Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) * * Concept: Filip Fuma, University of Pennsylvania * (fuma@cis.upenn.edu) * * Copyright Notice: Oh, let's see... The typical stuff, I suppose. * don't be removing our names and claiming you wrote it. Don't * sell this program, as if people'd actually pay money for it. * There are, of course, no warranties implied, under the "you should * be happy it does anything at all" school of thought. In short, * don't be a WEASEL. Other than that, feel free to redistribute the * source, and of course, send us lots of letters telling us how much * you like it, and what great guys we are for writing it. * * Copyright 1986,1987, University of Pennsylvania * */ #define MAIN #define PREFILE "~/.splotpre" #include "xsplot.h" #include #include #include #ifndef sigmask #define sigmask(m) (1 << ((m)-1)) #endif static int LastClicked = -1; static int EditButton = -1; static int MadeStuff = 0; /***********/ crash(disp,err) Display *disp; XErrorEvent *err; /***********/ { /* a routine that crashes and dumps core. Useful for tracking X errors*/ char *st, *tmp="foobar"; if (err->error_code == BadFont) return; st = NULL; fprintf(stderr,"Crashing...\n"); fprintf(stderr,"err=%s\n",XErrDescrip(err->error_code)); strcpy(st,tmp); } /*******************************************/ main(argc, argv) int argc; char *argv[]; /*******************************************/ { int i, status,dpcs; char *strind; char *fc, *bc; char *geom = NULL; char *display = NULL; int rvflag = 0; /* don't use reverse video as a default */ int invkey = -1; Color cdef; XEvent event; char *def; pid = getpid(); /*********************Defaults*********************/ fc = XGetDefault(argv[0], "Foreground"); bc = XGetDefault(argv[0], "Background"); /*********************Options*********************/ basfname[0]='\0'; for (i = 1; i < argc; i++) { if (argv[i][0] == '=') { geom = argv[i]; continue; } strind = index(argv[i], ':'); if(strind != NULL) { display = argv[i]; continue; } if (argv[i][0] != '-') { /* the file name */ strcpy(basfname,argv[i]); continue; } Syntax(argv[0]); } if (basfname[0] == '\0') Syntax(argv[0]); /* didn't get a file name */ /*****************************************************/ /* Open up the display. */ if (XOpenDisplay(display) == NULL) { fprintf(stderr, "%s: Can't open display '%s'\n", argv[0], XDisplayName(display)); exit(1); } /* load fonts */ if ((butfinfo = XOpenFont("6x10"))==NULL) FatalError("couldn't open '6x10' font\n"); butfont=butfinfo->id; if ((dlogfinfo = XOpenFont("8x13"))==NULL) FatalError("couldn't open '8x13' font\n"); dlogfont=dlogfinfo->id; /* Set up colors and pixmaps. */ /* Set normal default colors */ if (!rvflag) { ForeColor=BlackPixel; BackColor=WhitePixel; } else { ForeColor=WhitePixel; BackColor=BlackPixel; } dpcs=DisplayCells(); mono = 1; /* default is monochrome */ if (dpcs>2) { /* if it's a color display, however... */ int pixels[1],planes,b; Color defs[8]; status=XGetColorCells(1,1,3,&planes,pixels); if (planes != 7) { fprintf(stderr,"XGetColorCells: planes=%d!\n",planes); status=0; } if (status) { b=pixels[0]; for (i=0; i<8; i++) defs[i].pixel = b+i; /* set up default colors */ XParseColor("green", defs); /* first 4 cols are trace cols */ XParseColor("red", defs+1); XParseColor("slate blue",defs+2); XParseColor("yellow", defs+3); XParseColor("black", defs+4); XParseColor("white", defs+5); XParseColor("firebrick", defs+6); XParseColor("medium forest green", defs+7); XStoreColors(8,defs); /* do it */ /* set up color numbers */ gridFcol=b+5; gridBcol=b+4; /* NOTE: gridBcol MUST be b+4 */ panFcol =b+5; panBcol =b+4; butFcol =b+5; butBcol =b+6; mainFcol=b+5; mainBcol=b+2; dlgFcol =b+3; dlgBcol =b+7; errFcol =b+5; errBcol =b+4; for (i=0; i<4; i++) trcol[i]=b+i; mono=0; } } if (dpcs>2&&(fc !=NULL)&&XParseColor(fc ,&cdef)&&XGetHardwareColor(&cdef)) ForeColor=cdef.pixel; if (dpcs>2&&(bc !=NULL)&&XParseColor(bc ,&cdef)&&XGetHardwareColor(&cdef)) BackColor=cdef.pixel; if (mono) { gridFcol = panFcol = butFcol = mainFcol = dlgFcol = errFcol=ForeColor; gridBcol = panBcol = butBcol = mainBcol = dlgBcol = errBcol=BackColor; for (i=0; i<4; i++) trcol[i]=ForeColor; } /**************** parse/load spice file *****************/ InitVars(); OpenFiles(); /**************** Create/Open X Resources ***************/ CreateStuff(); CreateMainWindow(argv[0],geom); CreateSubwindows(); CreateButtons(); XSelectInput(mainW, ExposeWindow|ButtonReleased|ButtonPressed|KeyPressed); /* map all the windows, except for the command window, and the dlogs. Map the subwindows except for the ones in offsetW and measureW, as ChangedAnal does that (to avoid redraw). Note that this prevents me from simply doing XMapSubwindows on mainW */ XMapWindow (mainW); XMapWindow (titleW); XMapWindow (gridW); XMapWindow (presetW); XMapWindow (traceW); XMapWindow (scaleW); XMapWindow (measureW); XMapWindow (offsetW); XMapWindow (modifyW); XMapWindow (miscW); XMapWindow (errW); XMapSubwindows(presetW); XMapSubwindows(traceW); XMapSubwindows(scaleW); XMapSubwindows(commandW); XMapSubwindows(modifyW); XMapSubwindows(miscW); /* map x range/offset buttons, as these are NOT mapped in ChangedAnal */ XMapWindow(butts[2].win); XMapWindow(butts[3].win); XSetIconWindow(mainW,iconW); XDefineCursor (mainW,arrow); /********************************************/ ChangedAnal(); XErrorHandler(crash); MadeStuff=1; /* redirect ErrBox output to errW */ InitDivers(); /**************** Main loop *****************/ while (1) { XNextEvent(&event); HandleEvent(&event); } } /****************/ HandleEvent(event) XEvent *event; /****************/ { int i; Window wind; switch (event->type) { case ExposeWindow: { XExposeWindowEvent *exp_event = (XExposeWindowEvent *) event; wind = exp_event->window; /* figure out what redraw routine to call */ if (wind==mainW) DrawWindow(); else if (wind==iconW) RedrawIcon(); else if (wind==titleW) DrawTitleBar(); else if (wind==gridW) DrawGwin(); else if (wind==errW) RedrawErrBox(); else if (wind==dlogW) RedrawDlog(); else if (wind==diversW) RedrawDivers(); else if (wind==AboutDlog) RedrawAbout(); else if (wind==presetW) RedrawPanel(wind,PREW,"User Presets"); else if (wind==traceW) RedrawPanel(wind,TRAW,"Trace Location"); else if (wind==scaleW) RedrawPanel(wind,SCAW,"Grid Type"); else if (wind==measureW) RedrawPanel(wind,MSRW,"Measurement"); else if (wind==modifyW) RedrawPanel(wind,MODW,"Modify"); else if (wind==miscW) RedrawPanel(wind,MISW,"Misc."); else if (wind==commandW) RedrawPanel(wind,OFFW,"Alternate Commands"); else if (wind==offsetW) RedrawOffPan(); else { if (dlogW) { for (i=0; i<3; i++) if (dbutts[i].win==wind) {RedrawBut(dbutts+i); break;} if (dlogW == CurvDlog) { for (i=0; i<8; i++) if (curvbutts[i].win==wind) {RedrawBut(curvbutts+i); break;} } } for (i=0; iwindow; /* if dlog window up, mouse events outside dlog are tossed away */ if (dlogW) { /* only left button clicks do anything */ if ((but_event->detail & 0xff) == LeftButton) { if (wind==dlogW) { ClickDlog(but_event->x,but_event->y); } } } else { /***************/ /* LEFT BUTTON */ /***************/ if ((but_event->detail & 0xff) == LeftButton) { if (wind == titleW) { InvTitleBar(); Credits(); } if (wind == gridW) { TrackMeasure(but_event->x,but_event->y); break; } for (i=0; idetail & 0xff) == MiddleButton) { if (wind == gridW) TrackSBox(but_event->x,but_event->y); else BigPicture(); /* BigPicture command */ } /****************/ /* RIGHT BUTTON */ /****************/ else if ((but_event->detail & 0xff) == RightButton) { if (wind == titleW) OpenDivers(); else if (wind == gridW) Track159Box(but_event->x,but_event->y); else { PlotData(); SetUndo(); } /* redraw command */ } } } break; case ButtonReleased: { XButtonEvent *but_event = (XButtonEvent *) event; if ((but_event->detail & 0xff) == LeftButton) Click(); } break; case KeyPressed: { XKeyEvent *key_event = (XKeyEvent *) event; char *st; int cnt; st = XLookupMapping(key_event,&cnt); if (dlogW) DlogKey(st,cnt); } break; default: printf("event type=%ld\n",event->type); FatalError("Unexpected X_Event"); } /* end of switch */ } /***********************************/ Syntax(call) char *call; { printf ("Usage: %s filename [display] [=geometry]\n",call); exit(0); } /***********************************/ FatalError (identifier) char *identifier; { fprintf(stderr, "xsplot: %s\n", identifier); exit(1); } /***********************************/ InitVars() { tbInvert=0; selCB=0; comUp=0; NOPARSE =0; dlogW = 0; ylock=0; L10Offset = L10Range = OctOffset = OctRange = 0; } /***********************************/ ChangedAnal() { /* loaded a different anal. redraw the "file: analysis" string, do a bestfit, redraw x control if necessary */ int tmp; DrawWindow(); /* choose xscal based on anal.type */ if (theAnal->type==AC) xscal=LOG10; else xscal=LINEAR; BestFit(); ChangedCurves(); } /***********************************/ ChangedCurves() { /* changed curves. since numy could now be different, map/unmap numy-dependent stuff, copy strings to buttons */ int i; /* map/unmap offset/range/color buttons */ for (i=0; i<4; i++) { if (i=numy) { /* map the selected CB to a valid CB (if nec.) */ i=selCB; selCB=0; RedrawCB(i); RedrawCB(0); } ChangedSettings(); SetUndo(); SetUndo(); /* clear the undo buffer */ } /***********************************/ ChangedSettings() { /* changed the range/offset settings through zooming, digital, presets, etc. Reset the button strings */ int i; /* set range/offset button strings */ for (i=0; i<=numy; i++) { R2Str(i); butts[3+i*2].st = ranges[i].str; O2Str(i); butts[2+i*2].st = offsets[i].str; } SetXButs(); } /***********************************/ RedrawSettings() { int i; Pixmap tmpPix; if (comUp) { /* Alt Commands window up. close it */ Com2Ctrl(); } else { /* redraw range/offset buttons */ tmpPix = XPixmapSave(offsetW,0,0,OFFW,OFFH); /* save the bits */ if (tmpPix) { ComZoom(offsetW); /* only do the effect if bits saved */ XClipDrawThrough(offsetW); XPixmapPut(offsetW,0,0,0,0,OFFW,OFFH,tmpPix,GXcopy,AllPlanes); XClipClipped(offsetW); XFreePixmap(tmpPix); } for (i=2; i<12; i++) RedrawBut(butts+i); } RedrawBut(&butts[0]); } /***********************************/ DrawWindow() { char str[256]; XClear(mainW); sprintf(str,"%s: %s",FixName(basfname),theAnal->title); XText(mainW,215-XStringWidth(str,butfinfo,0,0)/2,30, str,strlen(str),butfont,mainFcol,mainBcol); } static int errInv=0; /***********************************/ RedrawErrBox() { if (errInv) XText(errW,6,1,errstr,strlen(errstr),butfont,errBcol,errFcol); else XText(errW,6,1,errstr,strlen(errstr),butfont,errFcol,errBcol); } /***********************************/ ErrBox(str,delay) char *str; short delay; { /* positive delay does 'effect', waits 'delay' seconds */ /* 0 delay redraws ErrBox with different string */ /* negative delay does effect, but no delay */ int i,col; strcpy(errstr,str); if (errInv) col=errBcol; else col=errFcol; if (MadeStuff) { /* only draw stuff if window has been opened up */ int xm,ym,w; if (delay) { if (errInv) XChangeBackground(errW,errBpix); else XChangeBackground(errW,errFpix); errInv = !errInv; xm = (WIDTH-20) / 2; ym=6; w=WIDTH-20; for (i=0; i<7; i++) { /* size=(WIDTH-20)x13 */ XPixSet(errW,xm-(i*w/12),ym-i,i*w/6,i*2+1,col); XFlush(); Timer(50000L); } } else { XClear(errW); } RedrawErrBox(); XFlush(); if (delay>0) Timer((long) (delay * 1000000L)); /* wait 'delay' secs */ } else { /* windows not up, just printf */ fprintf(stderr,"%s",errstr); if (delay) fprintf(stderr,"\n"); else fprintf(stderr,"\r"); } } /***********************************/ Click() /* called when a pushbutton is released. handles inverting of buttons, and if it's a command button, calls the appropriate routine. */ { if (LastClicked == -1) return; /* not sure if this ever happens */ if (LastClicked<12) { /* clicked an edit button */ if (EditButton>=0) InvertBut(&butts[EditButton]); /* turn off prev but */ EditButton = LastClicked; LastClicked = -1; } else { /* clicked some command button */ switch (LastClicked) { case 12: /* Modification controls */ case 13: case 14: case 15: case 16: case 17: case 18: if (EditButton>=0) ModifyBut(EditButton,LastClicked); InvertBut(&butts[LastClicked]); break; case 19: /* Redraw button */ PlotData(); SetUndo(); InvertBut(&butts[LastClicked]); break; case 20: /* Undo button */ InvertBut(&butts[LastClicked]); GetUndo(); ChangedSettings(); PlotData(); RedrawSettings(); SetUndo(); break; case 21: /* Y-Lock button */ ylock = !ylock; break; case 22: /* Commands button */ if (comUp) { Com2Ctrl(); } else { ComZoom(offsetW); XMapWindow(commandW); XUnmapWindow(offsetW); comUp=1; } break; case 23: /* Big Picture button */ InvertBut(&butts[LastClicked]); BigPicture(); Com2Ctrl(); break; case 24: /* Switch Analysis */ OpenDlog(SwitchDlog); break; case 25: /* Load Data File */ OpenDlog(LoadDlog); break; case 26: /* Choose Curves */ OpenDlog(CurvDlog); break; case 27: /* Digital Presets */ InvertBut(&butts[LastClicked]); DigitalPresets(); ChangedSettings(); PlotData(); RedrawSettings(); SetUndo(); break; case 28: /* user preset #1 */ case 29: /* ' ' #2 */ case 30: /* ' ' #3 */ case 31: /* ' ' #4 */ case 32: /* ' ' #5 */ case 33: /* ' ' #6 */ case 34: /* ' ' #7 */ case 35: /* ' ' #8 */ InvertBut(&butts[LastClicked]); Preset(LastClicked-28); ChangedSettings(); PlotData(); RedrawSettings(); SetUndo(); break; case 36: /* Set user preset */ SetPreLoop(); break; case 38: /* Plot to 'where' */ OpenDlog(PlotDlog); break; case 39: /* Help */ OpenDlog(HelpDlog); break; case 37: /* Quit button */ InvertBut(&butts[LastClicked]); Quit(); break; } LastClicked = -1; } } /***********************/ SetPreLoop() /***********************/ { /* loop that's called when 'Set' button is pressed. handles events until the next mouse event if mouse event is a LeftClick in one of the preset butts (28-35) then call SetPreset else turn 'set' button off, handle mouse event, and leave */ int i,j; XEvent event; Window wind; while (1) { XNextEvent(&event); if (event.type != ButtonPressed) HandleEvent(&event); else { XButtonEvent *but_event = (XButtonEvent *) &event; wind = but_event->window; if ((but_event->detail & 0xff) != LeftButton) { InvertBut(&butts[36]); HandleEvent(&event); } else { /* well, it's a leftclick... */ for (i=28; i<37 && (wind!=butts[i].win); i++); if (i==36) { /* clicked the 'Set' button again */ InvertBut(&butts[36]); } else if (i==37) { /* wasn't a preset button */ InvertBut(&butts[36]); HandleEvent(&event); } else { /* WAS one of the preset buttons */ InvertBut(&butts[i]); XFlush(); for (j=0; j<7; j++) { /* flash the 'set' and '#' buts */ InvertBut(&butts[i]); InvertBut(&butts[36]); XFlush(); Timer(100000L); } SetPreset(i-28); } } break; /* break out of the infinite loop on ANY ButtonPress */ } } } /***********************/ ComZoom(win) Window win; { /* gratuitous zoom effect for changing between AltCommands and RangeOffset windows */ /* note: the '11' is so that only the lower part of the window gets zoomed. zooming doesn't overwrite the titlebar part of the window (which, coincidentally, is 11 pixels high) */ short i,x,y,w,h; XClipDrawThrough(win); for (i=1; i<=15; i++) { w = OFFW*i/15; h = (OFFH-11)*i/15; x = OFFW/2 - w/2; y = 11 + (OFFH-11)/2 - h/2; XTileFill(win,x+1,y+1,w,h,panBpix,0,GXcopy,AllPlanes); Frame(win,x,y,x+w,y+h,1,panFcol,GXcopy); XFlush(); Timer(30000L); } XClipClipped(win); } /******************/ Com2Ctrl() { /* closes commands window, brings up range window, in amusing fashion */ ComZoom(commandW); XMapWindow(offsetW); XUnmapWindow(commandW); comUp=0; if (butts[22].invert) InvertBut(&butts[22]); /* turn command button off */ } /******************/ TrackMeasure(x,y) int x,y; { /* tracks the measuring tool, and show measurments when done */ float zx,zy,zx1,zy1,zdx,zdy,CalcX(),CalcY(); char vals[100],vals1[16],vals2[16],vals3[16],vals4[16],vals5[16]; short x1,y1,state; int i,xn,yn; Window subw; Pixmap tmpPix; XDefineCursor(gridW,cursx); /* while dragging the tool, show a different cursor */ x1=x; y1=y; InvertCross(x,y,x1,y1); while ( XQueryMouseButtons(gridW,&xn,&yn,&subw,&state), state&LeftMask) { if (xn!=x1 || yn!=y1) { /* moved */ InvertCross(x,y,x1,y1); x1=xn; y1=yn; InvertCross(x,y,x1,y1); } } InvertCross(x,y,x1,y1); XUndefineCursor(gridW); /* if ended up outside the gridrect, don't do anything */ if (x1GRight || y1GBottom) sprintf(vals,""); else { zx =CalcX(x); zx1=CalcX(x1); zdx=zx1-zx; zy = CalcY(y,selCB); zy1= CalcY(y1,selCB); zdy=zy1-zy; FpToEng(vals1,zx1); FpToEng(vals2,zy1); FpToEng(vals3,zdx); FpToEng(vals4,zdy); if (zdx!=0.0) FpToEng(vals5,zdy/zdx); else sprintf(vals5," infinity"); if (zdx==0.0 && zdy==0.0) sprintf(vals,"X:%-11.11s Y:%-11.11s",vals1,vals2); else sprintf(vals,"X:%-11.11s Y:%-11.11s dX:%-11.11s dY:%-11.11s dY/dX:%-11.11s",vals1,vals2,vals3,vals4,vals5); } ErrBox(vals,-1); } /******************/ InvertCross(x,y,x1,y1) int x,y,x1,y1; { /* draws an L-shaped thing from x,y to x1,y1. Only checks position of x1,y1, as x,y is known to be inside the grid. */ if (x1GRight) return; if (y1GBottom) return; SetVertex(curve+0,x,y,0); SetVertex(curve+1,x,y1,0); SetVertex(curve+2,x1,y1,0); if (mono) XDraw(gridW,curve,3,1,1,ForeColor,GXinvert,1); else XDraw(gridW,curve,3,1,1,ForeColor,GXinvert,4+selCB); } /***********************/ Track159Box(x,y) short x,y; { /* tracks the 10-50-90% box */ short x1,y1,state; int i,xn,yn; Window subw; if (Vis159) Invert159Box(R159[0],R159[1],R159[2],R159[3]); XDefineCursor(gridW,cursx); /* while dragging the box, show a different cursor */ x1=x; y1=y; InvertBox(x,y,x1,y1); while ( XQueryMouseButtons(gridW,&xn,&yn,&subw,&state), state&RightMask) { if (xn!=x1 || yn!=y1) { /* moved */ InvertBox(x,y,x1,y1); x1=xn; y1=yn; InvertBox(x,y,x1,y1); } } XUndefineCursor(gridW); InvertBox(x,y,x1,y1); /* if ended up outside the gridrect */ if (x1GRight || y1GBottom) Vis159=0; else { Vis159=1; R159[0]=x; R159[1]=y; R159[2]=x1; R159[3]=y1; Invert159Box(x,y,x1,y1); } } InvertBox(x,y,x1,y1) short x,y,x1,y1; { /* inverts a 1-pix-thick rectangle. Note that it only checks the range of x1,y1, as x,y is known to be inside the grid */ if (x1GRight) return; if (y1GBottom) return; if (x==x1 || y==y1) return; MakeRect(x,y,x1,y1); Frame(gridW,x,y,x1,y1,1,ForeColor,GXinvert); } Invert159Box(x,y,x1,y1) short x,y,x1,y1; { /* inverts a 1-pix-rectangle and 10, 50, and 90% lines */ short i,dy; int p; if (x==x1 || y==y1) return; InvertBox(x,y,x1,y1); if (x1GRight || y1GBottom) return; if (CalcSRect(x,y,x1,y1)) { ChangedSettings(); PlotData(); RedrawSettings(); SetUndo(); } } InvertSBox(x,y,x1,y1) short x,y,x1,y1; { /* inverts a 2-pix-thick rectangle. Note that it only checks the range of x1,y1, as x,y is known to be inside the grid */ if (x1GRight) return; if (y1GBottom) return; if (x==x1 || y==y1) { return; } Frame(gridW,x,y,x1,y1,2,ForeColor,GXinvert); } /***************************************************/ static int timerdone; /*******/ onalarm() /*******/ { timerdone=1; } /*******/ Timer(val) /* waits for VAL microseconds */ long val; /*******/ { struct itimerval it; if (val==0) return; bzero(&it, sizeof(it)); if (val>=1000000L) { /* more than 1 second */ it.it_value.tv_sec = val / 1000000L; val %= 1000000L; } it.it_value.tv_usec = val; timerdone=0; signal(SIGALRM,onalarm); setitimer(ITIMER_REAL, &it, (struct itimerval *)0); while (1) { sigblock(sigmask(SIGALRM)); /* note: have to block, so that ALRM */ if (timerdone) break; /* doesn't occur between 'if (timerdone)' */ else sigpause(0); /* and calling sigpause(0) */ } sigblock(0); /* turn ALRM blocking off */ signal(SIGALRM,SIG_DFL); } /*******/ mmove(num,src,des) int num; char *src,*des; /*******/ { /* copies num bytes from src to des. Probably won't work on some systems, but I couldn't find a handy move routine... */ int i; for (i=0; iid; XFlush(); /* scroll window contents down */ for (i = -10; i<=GSIZE; i+=10) { XMoveArea(gridW,0,i,0,i+10,GSIZE+10,GSIZE-i-10); if (mono) XPixSet(gridW,0,i,GSIZE+10,10,gridFcol); else XPixSet(gridW,0,i,GSIZE+10,10,gridBcol); XFlush(); Timer(50000L); } /* draw window */ if (mono) XChangeBackground(AboutDlog,gridFpix); else XChangeBackground(AboutDlog,gridBpix); XMapWindow(AboutDlog); i=j=0; /* handle events until the next mouse event */ while (1) { if (XPending()) { XNextEvent(&event); if (event.type != ButtonPressed) HandleEvent(&event); else break; } /* amusing (?) animation */ if (mono) { int col; i+=5; if (i>GSIZE) { i=j%5; j++; j=j%10; } if (j&1) col=gridFcol; else col=gridBcol; XLine(AboutDlog, i,0,GSIZE,i, 1,1,col,GXcopy,AllPlanes); XLine(AboutDlog, GSIZE,i,GSIZE-i,GSIZE, 1,1,col,GXcopy,AllPlanes); XLine(AboutDlog, GSIZE-i,GSIZE,0,GSIZE-i,1,1,col,GXcopy,AllPlanes); XLine(AboutDlog, 0,GSIZE-i,i,0, 1,1,col,GXcopy,AllPlanes); } else { i+=5; if (i>=GSIZE) { i=0; j+=3; j&=3; } XLine(AboutDlog, i,0,GSIZE,i, 1,1,trcol[j],GXcopy,AllPlanes); XLine(AboutDlog, GSIZE,i,GSIZE-i,GSIZE, 1,1,trcol[(j+1)&3],GXcopy,AllPlanes); XLine(AboutDlog, GSIZE-i,GSIZE,0,GSIZE-i,1,1,trcol[(j+2)&3],GXcopy,AllPlanes); XLine(AboutDlog, 0,GSIZE-i,i,0, 1,1,trcol[(j+3)&3],GXcopy,AllPlanes); } Timer(20000L); XFlush(); } /* scroll window contents up, filling new area with mainBpix */ for (i=GSIZE-10; i>=0; i-=10) { if (i) XMoveArea(AboutDlog,0,10, 0,0, GSIZE,i); XPixSet(AboutDlog,0,i, GSIZE,10, gridBcol); XFlush(); Timer(50000L); } /* invert title bar back to normal */ InvTitleBar(); XUnmapWindow(AboutDlog); /* jettison bigfont if one was loaded */ if (bigfont != dlogfont && bigfont != 0) XFreeFont(bigfont); } /***********************/ RedrawAbout() /***********************/ { int bcol,c1,c2,c3; if (mono) { bcol=gridFcol; c1=c2=c3=gridBcol; } else { bcol=gridBcol; c1=trcol[2]; c2=trcol[3]; c3=trcol[0]; } if (bigfont) XText(AboutDlog, 200-(XStringWidth("xSplot",bigfinfo,0,0))/2, 200-(bigfinfo->height/2), "xSplot",6, bigfont, c1,bcol); XText(AboutDlog, 200-(XStringWidth("by John Bradley and Filip Fuma",butfinfo,0,0))/2, 200+(bigfinfo->height/2)+10, "by John Bradley and Filip Fuma",30, butfont, c2,bcol); XText(AboutDlog, 200-(XStringWidth(REVDATE,butfinfo,0,0))/2, 200+(bigfinfo->height/2)+10+butfinfo->height+10, REVDATE,strlen(REVDATE), butfont, c3,bcol); } \Rogue\Monster\ else echo "will not over write ./xsplot/xsplot.c" fi if `test ! -s ./xsplot/xsplot.l` then echo "writing ./xsplot/xsplot.l" cat > ./xsplot/xsplot.l << '\Rogue\Monster\' .TH XSPLOT l "30 July 1987" "X Version 10.4" .SH NAME xsplot \- An interactive SPICE plotting program .SH SYNOPSIS .B xsplot filename [host:display] [=geometry] .SH DESCRIPTION .I xsplot is an interactive X program that graphically displays the output of SPICE runs in an oscilloscope-ular fashion. It will allow you to zoom in on interesting sections, measure distances and slopes, and draw 10% cutoffs. It will, with the help of an auxilliary program, generate hardcopy to an HP-GL plotter. Rumor has it that there's even an amusing diversion buried in the code. Why, it may well be the Neatest Program Ever. Probably not, though. .SH OPTIONS .PP .TP 14 .B \=geometry The width and height shouldn't be set by the user, as the default size (640x480) is also the only size. Anything smaller will be ignored, and anything larger won't be used. You can feel free to set the position, though. .PP .TP 14 .B \fIhost\fP:\fIdisplay\fP Normally, .I xsplot gets the host and display number from the environment variable "DISPLAY". However, one can specify them explicitly. The .I host specifies which machine to create the .I xsplot window on, and the .I display argument specifies the display number. .SH OPERATION .PP Start up .I xsplot on the "diff.out" SPICE file by typing "xsplot diff.out". With any luck at all, the program should parse the data and open up the main window. .PP Note: Unless specified otherwise, 'click' means 'clicking the left button'. .PP .I The Title Bar: At the very top of the screen is the title bar. Clicking on it with the left button will display the credits. Click the left button again to make them go away. .PP .I The Grid: The large grid that takes up most of the main window is where all the data is actually displayed. Note that the name of the file and the analysis type are displayed above the grid. .PP .I The Offset/Range Box: This specifies the values used in graphing the data. The first pair of values are used for the (common) x-axis. Subsequent pairs are used for the y-axes. In the example, (diff.out: DC Analysis), the common x-axis is labeled "VIN". The offset of "0.000 V" means that the center of the screen corresponds to VIN=0.0V. The range (50mV) is the amount the x-value changes per division. The other two pairs of numbers work the same way for the two y-axes. (Note that in the example they are the same, meaning the two curves are plotted in the same scale. This doesn't have to be the case, however.) .PP .I The Modify Box: This houses the controls used to modify the offset/range settings and the Scale settings. To modify one of these values, click on the value to be modified, and then click the modification controls. Click on the Redraw button to see the results. .PP .I The User Presets Box: You have the ability to save eight complete sets of control settings. This allows you to return to a certain "view" of your data without having to play around with the controls. To save a preset, click on the .B Set button, and then click on one of the numbered buttons. To return to a preset, click on one of the numbered buttons. .PP .I The Measurement Box: Up to four buttons will be shown, one for each dataset you have chosen to graph (2 in the example). If you are on a color display, the colors of the boxes will match the colors of the traces. On a monochrome display, the buttons will have a line drawn in them with the same stipple pattern as the related trace. The box that has the thick frame around it is the 'Active Dataset'. This is the dataset that can be measured at the moment. (See .B "Cursor Madness!", below.) Note that if all the traces have the same offset/range settings, then it doesn't matter which one is the active dataset. .PP .I The Trace Location Box: This shows in what directions the active dataset goes off the screen, if any. .PP .I The Grid Type Box: This determines the type of grid that will be used for the x and y axes. Currently, the y axis is always 'linear'. The x axis, however, can be either 'linear', 'log 10', or 'log 2'. You can change the x axis type by selecting the x control and using the modification controls. Note that as you change the grid type, the offset/range information for the x-axis changes, also. Log 10 and log 2 grids are quite similar. In both cases, the offset, an integer power (of 10 or 2, respectively), refers to the left edge of the grid. The range is either 1, 3, or 10 divisions. An example of data that should be viewed on a log scale is the AC Analysis found in the diff.out file. (See .B "Switch Analysis", below.) .PP .I The Miscellaneous Box: This houses four command buttons. The first, .B Commands, opens and closes the "Alternate Commands" box. The second, .B Y-Lock, toggles locking on and off. When locking is on, any change you make to one of the y-axis offset/range controls is carried out on all of them. This can have the effect of moving all the traces up or down simultaneously. The .B Undo control returns all the settings to their previous values. The .B Redraw button redraws the data, using the current settings. .PP .TP 8 .I Alternate Commands Box: .B Load Data File: Clicking this will bring up a dialog box in the middle of the grid. You'll be prompted for the name of a SPICE file to load. Enter the filename and hit return, or hit return immediately to cancel the load. .B Switch Analysis: Clicking this button will bring up a dialog box listing all the analyses found in the current SPICE file. Double-click on the one you'd like to examine. .B Curves: While .I xsplot is only capable of displaying four traces at once, a SPICE analysis can have up to eight traces in it. This button brings up a dialog box listing all the curves found in the current analysis. Select the ones you want and click .B Ok , or click .B Cancel if you don't want to change curves. .B Big Picture: This will bring up a zoomed-all-the-way-out, scaled-to-fit graph of the data. .B Plot: This will allow you to generate hard-copy, assuming you have an HP-GL plotter and a working copy of .I splotter. Choosing .B File will prompt you for a file name to dump the plot data to, for later use. Choosing .B Plotter will dump the plot file to a temporary file, and attempt to exec "/usr/local/splotter". It will complain if it is unable to do so. .B Help: This may eventually do something useful. At the moment, it doesn't. Click the mouse inside the window to make it go away. .B Digital: Viewing Transient Analyses of digital circuits can be unpleasant, as the traces constantly overlap at 0V and 5V. This button will adjust the settings so that the traces appear stacked on the grid, rather than overlapping. Provided that the traces range between 0V and +5V, that is. A good example of the usefulness of this feature can be seen by loading the data file "sreg.out". .B Quit: Exits the program. Pretty obvious. .SH MEASURING .PP One of the major advantages .I xsplot has over the traditional ".PLOT" that SPICE generates is the ability to measure the output. Clicking the left button anywhere inside the Grid (or the Big Picture) will give you the x and y values associated with that particular screen coordinate. Clicking and dragging the left button inside the Grid/Big Picture will drag an L-shaped pair of lines from the original point to the cursor. Releasing the left button will give you the x and y values at the cursor point and the deltas between the two points (dX, dY). It will also give you the slope of the line connecting the two points (dY/dX), which can be useful for measuring amplification. Or something. .PP Clicking and dragging the RIGHT button inside the Grid/Big Picture will drag around a rectangle from the original point to the cursor. When you let go of the right button, three lines will be drawn inside the box at 10%, 50%, and 90% of it's vertical range. This may be useful for measuring rise and fall times of exponential outputs. Or maybe not. .SH ZOOMING .I xsplot allows you to directly zoom in on interesting parts of the data without fiddling around the the offset/range controls. To do this, locate a point of interest (on either the Grid or the Big Picture), and draw a rectangle around it by clicking the MIDDLE mouse button at one corner, and dragging it to the opposite corner. When you let go of the button, the program will zoom in as well as it's able to on the data, preserving the nice, round-number-osity of the range/offset controls. Also of interest: simply clicking the middle button inside the grid area (without dragging it anywhere) will zoom to the initial 'best-fit' settings. .SH CURSOR MADNESS! (a summary of the button usage) .TP 8 .I Inside the Grid Area: The LEFT button is used for measuring data. The MIDDLE button is used for zooming in. The RIGHT button is used for drawing 10%, 50%, and 90% cutoffs. .TP 8 .I Outside the Grid Area: The LEFT button is used to operate the controls. The MIDDLE button is a shortcut for the Big Picture command. The RIGHT button is a shortcut for the Redraw command. .SH MISCELLANEOUS .PP If less than 30 data points are visible on the grid, the actual points will be drawn, in addition to the lines connecting them. .PP .I xsplot attempts to acquire 8 r/w color cells for it's purposes. If it succeeds, you'll get to use the program in color, the way it was meant to be. If not, it'll only use two colors ("xsplot.Foreground" and "xsplot.Background" in your ".Xdefaults" file). Then again, since it's only likely to fail on a monochrome display, the two colors probably aren't very meaningful. Either way, if it goes into monochrome, traces will be drawn with different stipple patterns. Decidedly less fun. .SH LIMITATIONS .TP 8 * Only the first 1000 data points of an analysis are used. .TP 8 * Analyses must not have more than 8 dependent (output) variables. .TP 8 * There is an upper limit of 20 analyses per file, which should be more than ANYONE needs. .TP 8 * There should only be one SPICE run per output file. If your SPICE input file has multiple jobs in it, you should split them up first. .TP 8 * xsplot is fairly hard-wired to the format of SPICE output. In particular, things like the number of lines between a title and the start of data are expected to remain the same. It works with the output of SPICE 2g.6. It MAY work with other versions of SPICE, but then again, it may not. I can't guarantee anything. .TP 8 * xsplot gets it's data from the results of '.PRINT' statements. You should probably strip out any '.PLOT' statements you might have in your input file, as they slow the run down, and are Completely Ignored by .I xsplot. They won't interfere with it's ability to parse the file, though. .TP 8 * xsplot only supports the DC, AC, and TRANSIENT analyses generated by SPICE. It will not display NOISE or FOURIER analyses, for the simple reason that I've never seen one. If some of you analog types out would like it to deal with these analyses, kindly send me some data, and I'll see what I can do about it. .SH USING XSPLOT TO VIEW NON-SPICE DATA On the off chance that you'd like to view some non-SPICE data with .I xsplot, it can be done, and it's only slightly painful. Generate, or edit up a data file that looks like the one below. Note that case is important. The '.PRINT' line says that the data should be plotted on a linear grid (as the default), (by virtue of being a DC analysis), specifies the number of traces, and their names. It is important that the 'DC ANALYSIS' line start in column 0, and be typed EXACTLY as shown here. The 8 ignored lines can be blank. After that comes the actual data points. The first number on each line is the common x-value, and the other numbers are the y-values (1-8, depending on the number of labels specified in the .PRINT line). The end of data coincides with the end of the file. Note: if the data would look more appropriate on a logarithmic grid, change the two 'DC's to 'AC'. Also, note that you can't specify the x-axis label. The x-axis will always be labeled 'VIN' for a DC analysis, and 'FREQ' for an AC analysis. Nothing can be done about this, as far as .I xsplot is concerned. For hard-copy, however, you can edit the SPIF file before sending it to the plotter. See "The SPIF Spec", below. .SH THE SPIF SPEC When you tell .I xsplot to plot, it will generate a file in this format, and (hopefully) 'splotter' will be able to drive the actual plotter. .PP SPIF files are plain text files, and can be easily created/edited with any text editor. .TP 25 The First Line: The name of the SPICE file. .TP 25 The Second Line: The name of the analysis. .TP 25 The Third Line: The grid type (an integer) (0=Linear) (1=Log 10, 1 division) (2=Log 10, 3 divisions) (3=Log 10, 10 divisions) (4=Log 2, 1 division) (5=Log 2, 3 divisions) (6=Log 2, 10 divisions) .TP 25 The Fourth Line: The number of curves in the plot. (an integer, from 1 to 4). .TP 25 The Fifth Line: The names of the axes (2-5 words, separated by spaces). The first word is the name of the (common) x-axis. The second (and following) words are the names of the curves. .TP 25 The Sixth Line: The x-axis offset and range info. If the grid type equals 0, this line will be in the form "x.xxxExx x.xxxExx", where the first number is the offset, and the second, the range. If the grid type equals 1,2, or 3, the line will be in the form "10^x y", where x is an integer in the range +/-19, and 10^x is the value at the left edge. y will be an integer, either 1,3, or 10, specifying the number of divisions. If the grid type 4,5, or 6, this line will be in the form "2^x y", where x is an integer in the range +/-19, and 2^x is the value at the left edge. y will be an integer, either 1,3 or 10, specifying the number of divisions. .TP 25 The Seventh Line: The y-axis offset and range info. There will be from 1 to 4 lines (depending on the number of curves). Each line will be in the form "x.xxxxExx x.xxxxExx" where the first number is the offset, and the second number is the range. .TP 25 The Rest of the File: This will be an indeterminant number of lines, each with an x-coordinate and 1-4 y-coordinates separated by spaces. The coordinates will be integers in the range +/- 32K. The plotting program should clip these coordinates to the rante 0-400 on both axes (0,0 = bottom-left). The end of the data will coincide with the end of the SPIF file. .SH BUGS If you are foolish enough to try to plot negative values on a log scale, the program will treat them as '0'. The resulting graph probably won't look the way you'd expect, but you should be doing such a thing anyhow... .SH AUTHOR John Bradley, University of Pennsylvania (bradley@cis.upenn.edu) .SH DESIGNER Filip Fuma, University of Pennsylvania (fuma@cis.upenn.edu) \Rogue\Monster\ else echo "will not over write ./xsplot/xsplot.l" fi echo "Finished archive 3 of 4" exit