-
Notifications
You must be signed in to change notification settings - Fork 0
Post: interactive graphic shell for doing math on SPICE piece-wise-linear waveforms
License
GPL-2.0, Unknown licenses found
Licenses found
GPL-2.0
COPYING
Unknown
license.c
omnister/post
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
NAME PDpost - an interactive language for post-processing and plotting of SPICE files Synopsis Richard Walker released under GPL v2.0 Post can do math on piece-wise-linear SPICE waveforms. Plot gives you an interactive shell where you can type expressions like: gr db((v1-v2)/v3) ; graph log of (v1-v2)/v3 gr versus(v1,mod(time,100p)) ; graph a 10Gb/s eye diagram gr integral(v2) ; graph the integral of v2 gr v1, delay(v2,1n) ; graph v1 and a delayed v2 on same graph gr mag(v1), im(v2) ; magnitude of v1, imaginary part of v2 Functions: avg() db() cos() dt() exp() greater() lpf() im() integral() pha() less() ln() log10() log re() mag() mod() min() max() pause() pow sin() sqrt() warp() versus() delay() ui() xcross() xcrossn() xcrossp() Commands: gr gs ci di ls pr print quit exit bye This version defaults to use the pdplot(1) program for plotting. gr v1 logx ; logx gr db(v1) logf ; logf gr db(v1) yl 0,10 ; ylimit gr db(v1) xl 0,10 ; xlimit Gnuplot(1) can be used instead by specifying the -g option. However, no further development with gnuplot is anticipated. It doesn't behave nicely on my system (grabbing the focus and relocating the window after each plot), so pdplot(1) is preferred. Eventually, I plan to integrate the plotting engine into the same source as post to avoid the slow transmission of large data sets between separate programs using pipes. Some preliminary experiments show that 10x speedups are possible by plotting only visible pixels and not sending the complete dataset to the plotter. In either case, you will need to install your graphing program of choice. -- Rick Walker [email protected] QUICK START ---------- For a quick start, type "make depend" and then "make". If you like what you see, then run "make install" with sufficient permission to write in "/usr/local/bin". Post does it's graphing with either pdplot() or gnuplot() which you will need to install first. Pdplot is preferred because it was developed together with post and is optimized for the application. Gnuplot() is used when the "-g" option is specified. Run post with "post [-g] [-r <rawfile>] [scriptfilenames...]". You might try the included "run" script. If given the basename of a spice .cki file, it will check to see if a .raw file already exists that is newer than the .cki file. If not, it will build it. Then it will invoke post(1) on the .raw file. Post(1) will start up interactively if given no arguments. You'll get a ":" prompt in your terminal window. If you want Post to simply run a script and exit, you can invoke it with the name of the file. If you'd like Post to load up a particular rawfile before giving you a prompt, you can use the "-r <rawfile>" command. Post(1) has command line editing in interactive mode. Emacs is the default, but I switch it to "vi" mode by having a .inputrc file in my home directory which looks like: -------------- cut here --------------- # this is the gnu-readline configuration file set editing-mode vi set keymap vi -------------- cut here --------------- If you have written some nice waveform definitions for post you can put them in file and load them at start up time with "post mydefs -". Post will load all listed files into memory. The final "-" drops you into interactive mode after loading everything. Of course, the most important feature is loading spice raw files. You can get a list of loadable spice raw files with "ls". You can load a rawfile with : ci "file" The filename must be in quotes. You can list the loaded variables with "di". In addition to dependant variables from the SPICE deck, there will be a synthetic time variable that can be used for making calculations as a function of the time axis. Post ignores anything from a "#" character to the end of the line. C-style comments "/* .... */" are also recognized and can be used in scripts to comment out multiple lines. NUMBERS ------- Numbers can be real or complex. All numbers are cast to double precision. Reference to an undefined name automatically creates a zero-valued variable. Engineering notation is supported. Starting with A,a for "atto" or 10^-18, permitted suffixes are: F,f (10^-15), P,p (10-^12), N,n (10^-9), U,u (10^e-6), "%" (10e-2), k,K (10^3), MEG,meg (10^6), G,g (10^9) and T,t (10^12). Some examples of valid numbers are: .1 1.0 -1.3e-7 1f 10P 8meg The variable name "i" and "I" are both equal to the imaginary number sqrt(-1). You can create a pure imaginary number with "3i", or "3*i". Mixed numbers are written as "1+i" or 1+3*i" or "1+3i". Examples: :i i :3i 3i :3n*i 3e-09i :1p+i 1e-12+i VARIABLES --------- Variables names must start with any alphabetic character or the "_" character. The remainder of the variable name must be composed of any alphanumeric character, or the characters "_<>". Piecewise linear waveforms can be defined by :a = {0,0; 1,4; 3,4; 4,3} a list of independant value, dependant value pairs. It is required that the independant value be in strict monotonic rising order. For many engineering applications the independant variable will be either time or frequency, however it can also represent space, or any other parameter such as a resistor values in circuit. As implemented, a PWL can be thought of as both a data-type and a function. if a is defined as above then a(1) will return 4. a(2) also returns 4 by interpolation. a(sqrt(4)) will return 4.0. This functional notation replaces the yvalue() function of HP's post. Expressions are fairly obvious, read below for details. You can graph any number of expressions on the same graph with "gr". : gr a,b,c You can plot two groups of variables on separate axes with : gr a,b,c ; d, e, f You can limit the xaxis range with "xl <start>,<stop>". This will apply to all axes. If xl is used twice on a command line, the last one will take precedence: : gr a,b ; d,e xl .1,1.8 Finally, each graph can have its yaxis range set with "yl <min>,<max>" : gr a,b yl -1,1; d,e yl -2,2 xl .1,1.8 The rule is that each expression or PWL name must be separated from any other PWL or expression by either a "," or a ";". A semicolon creates second plot. Piecewise linear (PWL) variables can be treated like normal scalars. Generally post will do the obvious, most useful thing. Adding two PWLs with : c=a+b will add them point by point, cross-interpolating where necessary. The output PWL will always include a value at every independant variable point defined in either of the input PWLs. The output PWL is defined only at the intersection of the of the span of each PWL's independant variable. For instance, you can define two PWL's with : a = {0,0; 1,1} : b = {0.5,1; 1.5,2} and the addition of a,b yields : pr a+b { 0.5,1; 1,1.5; } Which is only defined over the overlap of a,b. There is currently only one fundamental data type in post(). This is the DATUM which is currently defined as a doubly linked list: typedef struct datum { double iv; /* independant variable, usually time or freq */ double re; /* real part */ double im; /* imaginary part */ struct datum *next; struct datum *prev; } DATUM Simple scalars like "0.0", "1+2i", "-4i" are just DATUMS with *next pointing to NULL. A piece-wise linear (PWL) such as "a = {0,0; 1,1; 2,i}" is implemented as a doubly-linked list of datums defined in such a way that appending new items is fast. (this is done by keeping the *prev link of the first element pointing at the last element so we don't have to walk the list to add a new element to the end). In the summaries below, a single scalar is notated as "d" and a PWL list is notated as "p". In general, when a math operation is performed on two PWLs, the operation is done point by point with cross interpolation whenever the two PWLs differ in their independant variables. A math operation between a PWL and a scalar DATUM generally takes the DATUM value as applying over all time or frequency. Operations between two scalar DATUMs is just the ordinary math that one would expect. In cases where complex definitions are awkward, the real value is used. An example is the max(p1,p2) function. In most cases, it is expected that it is the maximum real value that is compared. Although only the real value is compared, the entire complex value of the maximum segment is copied to the output. This allows the max/min functions to be used as a multiplexer. /* given two real signals siga, sigb, and select signal */ /* "mux" that is greater than 0 when we want to select siga, */ /* a multiplexor can be implemented as: */ a=re(siga)*i+re(mux) b=re(sigb)*i+0.0 output = i*max(a,b) # AVERAGE p3=avg(p1,p2) ;p3(k) = (p1(k)+p2(k))/2 d3=avg(d1,d2) ;d3 = (d1+d2)/2 p3=avg(d1,p1) ;p3(k) = (p1(k)+d1)/2 p3=avg(p1,d1) ;p3(k) = (p1(k)+d1)/2 d=avg(p) ;compute average value of a single PWL # DECIBEL p=db(p) ;return 20*log10(mag(p)) d=db(d) ;return 20*log10(mag(d)) # DERIVATIVE p2=dt(p1) ;uses global variable DT, default = 1u ;p2 = (warp(p1,-DT/2)-warp(p1,DT/2))/DT # EXPONENTIAL p2=exp(p1) ;p2(k) = e^p1(k) # INTEGRAL p2=integral(p1) ;return the running integral of p1*dt ;definite integral from a to b = p2(b)-p2(a) # LOGARITHM p2=ln(p1) ;p2(k) = ln(p1(k)) p2=log10(p1) ;p2(k) = ln(p1(k))/ln(10) p2=log(p1) ;p2(k) = ln(p1(k))/ln(10) # MAGNITUDE p2=mag(p1) ;p2(k) = sqrt( (p1(k).re)^2 + (p1(k).im)^2 ) # MODULUS p2=mod(p1,p2) ;p3.re = fmod(p1.re, p2.re) ;p3.im = 0.0; # MAXIMUM/MINIMUM p3=max(p1,p2) ;p3 = (p1(k).re > p2(k).re)?p1(k):p2(k) d=max(p1) ;find k where p1(k).re is maximum, return p1(k) ;note: decision is made on real value, but ;complex value is returned. min(p1,p2) ;p3 = (p1(k).re < p2(k).re)?p1(k):p2(k) d=min(p1) ;find k where p1(k).re is minimum, return p1(k) ;note: decision is made on real value, but ;complex value is returned. # PAUSE pause(<expression>) ;post will sleep(2) for expression.re seconds. ;If an interrupt (usually ^C) is received, the ;pause will be aborted, returning to normal ;command flow. Pause is handy inside a script ;for putting between graph commands so the user ;can page through multiple graph results with ^C. ;pause() returns the number of seconds remaining ;to wait. If you don't want this number printed ;then assign it to a scratch variable eg: ;"tmp=pause(10000)" # PHASE p2=pha(p) ;p2(k) = atan2(p(k).im, p(k).re) d2=pha(d) ;d2 = atan2(d.im, d.re) # POWER p3=pow(p1,p2) ;p3(k) = p1(k)^p2(k) p3=pow(p1,d2) ;p3(k) = p1(k)^d d=pow(d1,d2) ;d = d1^d2 p3=p1^p2 ;alternative ways of computing a^b d3=d1^d2 d3=p1^d1 # PRINT print [<exp> | <string> | ","]* Print any combination of expressions and strings. Combining terms without a comma "," results in concatenation of the fields. Using a comma will pad the fields with a space. pr <exp> ;print the value of expression print <exp> ;print the value of expression Examples: > a=5 > print a 5 > print "a=" a a=5 > print "a =", a a = 5 # PLOT gr <exp> ;graph an expression on a new graph gs <exp> ;graph on the same graph (currently not implemented) gn <exp> ;graph on next y-graph (currently not implemented) ;for example you can graph a,b on top ;plot and d,c on bottom plot with ; "gr a; gs b; gn c; gs d" # REAL/IMAGINARY PART p2=re(p1) ;p2(k) = p1(k).re d2=re(d) ;d2 = d.re p2=im(p1) ;p2(k) = imaginary part of p1(k) d2=im(d) ;d2 = d.im # SPICE RAW FILES ls ; list all raw files in the current directory ci "file.raw" ; load the raw file "file". Quotes are required di ; display the names of all loaded variables # SQUARE ROOT p2=sqrt(p1) ; same as pow(p,0.5) # TIME DELAY p3=warp(p1,d) ; timeshift a signal by delay d ; p3(k+d) = p1(k) p3=warp(p1,p2) ; use one PWL to phase modulate another ; the delay amount is read from p2(k) ; p3(k+p2(k).re) = p1(k) p3=delay(p1,d) ; delay is a synonym for warp... # VERSUS (making eye diagrams) gr versus(p1,p2) ; return a new PWL with the re/im part determined by ; p1, and the independant variable (time/frequency) ; set by p2.re. gr versus(p1,mod(time, 160p)) ; plots the p1 waveform with modular time axis ; (an eye diagram)... gr versus(p1,mod(time+82p, 160p)) ; shifts the center of the eye diagram by 82p ; since versus() returns a PWL with a non-monotonic ; independant variable, it will not usually be useful ; to do anything other than plot the result. # ZERO CROSSING MEASUREMENT p3=xcross(p1,p2) p3=xcrossp(p1,p2) p3=xcrossn(p1,p2) ; let n=(int)p2.re, find nth zero crossing (xcross), ; nth negative-going zero crossing (xcrossn), or ; nth positive going zero crossing (xcrossp) of ; p1(xc).re. set p3.re=xc. If n==0, return all ; zero crossings as a PWL with iv set to n, .re ; set to crossing time. Can then extract nth ; crossing with p3(n). If n is negative, will return ; nth crossing counted from the end of the waveform p2=ui(p1) ; compute the value of the unit interval as a function of time: ; for every pair of positive zero crossings in p1, create ; a data point in p2 with iv set to the iv of the second zero ; crossing and the dv set to the time between the two crossings. ; eg: for a VCO, frequency can be approximated by 1/ui(vout) # resampling / filtering p2 = lpf(p1, tau) ; filters a (possibly unevenly sampled) PWL p1 ; with RC time constant tau. returns a new ; filtered PWL that is evenly sampled in time ; with spacing = tau/16.0. A high-pass coupling ; can be created with "1-lpf()".
About
Post: interactive graphic shell for doing math on SPICE piece-wise-linear waveforms
Resources
License
GPL-2.0, Unknown licenses found
Licenses found
GPL-2.0
COPYING
Unknown
license.c
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published