To build Post, 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 gnuplot() which you will need to install first.
Run post with "post". You'll get a ":" prompt in your terminal window. There should be command line editing available. I think emacs is the default, I switch it to "vi" mode by having a .inputrc file in my home directory which contains:
# this is the gnu-readline configuration file set editing-mode vi set keymap vi
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".
Numbers can be real or complex. All numbers are cast to double precision. 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
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
Finally, each graph can have its yaxis range set with "yl
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
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
and the addition of a,b yields
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:
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.
In this summary, names like "p, p1, p2,..." refer to piecewise linear
waveforms. Names like "d, d1, d2..." refer to simple scalars. Individual
PWL datapoints are referred to as "p(k)". Since both scalars and PWL's can
be complex, the real and imaginary parts are referred to as p(k).re, or d.re
and p(k).im or d.im. In most cases, math between a PWL and a scalar is
handled as if the scalar was a constant valued PWL defined from plus to
minus infinity. When an algorithm is handled point by point, it is described
below as pn(k) = f(p1(k), p2(k), ...) where k is understood to be evaluated
at the union of timepoints defined by the set of all independent PWLs. Finally,
the output of any operation is truncated to the range of times that are spanned by all the input waveforms.
: gr a,b ; d,e xl .1,1.8
: gr a,b yl -1,1; d,e yl -2,2 xl .1,1.8
: c=a+b
: a = {0,0; 1,1}
: b = {0.5,1; 1.5,2}
: pr a+b
{
0.5,1
1,1.5
}
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
/* 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)
Operators
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
DECIBAL
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 )
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(
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
pr
PLOT
gr
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,p2) ; use one PWL to phase modulate another
; p3(k+p1(k).re) = p1(k)
p3=warp(p1,d) ; timeshift a signal by delay d
; p3(k+d) = p1(k)
p3=delay(p1,d) ; delay is a synonym for warp...
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 setl
; 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()".