ngspice/src/spectrum

166 lines
5.1 KiB
Plaintext

* Fourier Series Function for SPICE
* This script is offered here for learning purposes, even if it is outdated
* and superseeded by the spec function and especially by the much faster fft function.
* You may use this script in conjunction with e.g. a ringoscillator output (see
* the ngspice manual, chapter 17).
.control
begin
* Variable argc delivers the number of command line parameters given by the user
* after the 'spectrum' command
if ($argc lt 4)
echo Error: Too few arguments.
echo ' 'Spectrum produces a plot containing a fourier series transformation of
echo ' 'the specified vectors
echo usage: spectrum startfreq stop step vec [[vec] ...]
goto bottom
end
* Check if vectors 'time' and any input vector(s) are available
* argv[n] delivers the command line entries after the 'spectrum' command,
* starting with argv[1]. $argv[4-len] delivers the value of all tokens,
* starting with postion 4 till the end of the command line
if ( time eq time )
foreach vec $argv[4-len]
if ( $vec eq $vec )
else
goto bottom
end
end
else
echo ' 'Spectrum can not work without a time vector from a transient analysis.
goto bottom
end
* generate a new plot entitled 'scratch', which will hold intermediate
* results and will be discarded after their evaluation.
set dt=$curplot
set title=$curplottitle
set curplot=new
set scratch=$curplot
* A vector 'span' is created in the 'scratch' plot to hold the time difference
* of the transient simulation. {$dt}.time allows to access the 'time' vector
* from the dt plot (which is normally named 'tranx' with x a consecutoive
* integer number, depending on the amount of transient simulations already run
* in the present job.
let span={$dt}.time[length({$dt}.time)-1]-{$dt}.time[0]
* Calculate the number of steps in all of the spectra to be evaluated below
if ($argv[3] gt 0.999/span)
let fpoints= ( $argv[2] - $argv[1] ) / $argv[3] +1
if (fpoints < 2)
echo frequency start stop or step not correctly specified
goto reset
end
else
echo Error: time span is not long enough for a step frequency of $argv[3] Hz
goto reset
end
let lent = length({$dt}.time)
set lent = "$&lent"
let nyquist = {$lent}/2/span
if ($argv[2] gt nyquist)
echo Error: The nyquist limit is exceeded, try a frequency less than "$&nyquist" Hz
goto reset
end
set fpoints="$&fpoints"
* generate a new plot to hold the spectra
set curplot=new
set spec=$curplot
set curplottitle=$title
set curplotname='Spectrum Analysis'
* argv[3] is the third agrgument from the input line
* spectrum 1 1000MEG 10MEG v(out25)
* that is the delta frequency
* The fcn vector(n) creates a vector of length n, its elements have
* the values 0, 1, 2, 3, ..., n-2, n-1. Each element then is multiplied
* with the frequency step value.
let frequency=vector( $fpoints )*$argv[3]
* Add an frequency offset to each element of vector 'frequency'
* to suppress the (typically) large dc component.
dowhile frequency[1] < ( $argv[1] + 1e-9 )
let frequency = frequency + $argv[3]
end
* For each input vector given on the command line,
* create a new vector for complex numbers
foreach vec $argv[4-len]
let $vec = vector( $fpoints ) + j(vector( $fpoints ))
reshape $vec [{$fpoints}]
end
* $scratch is a plot for intermediate results, will be destroyed during cleanup
* $dt is the plot with the original data
* $spec is a plot for storing the spectrum
set curplot=$scratch
* some test
let npers=1
let test = span-2/$argv[3] + 1e-9
while test > 0
let npers = npers + 1
let test = test-1/$argv[3]
end
* Do the spectrum calculations
let ircle = 2*pi*max(-1,({$dt}.time-{$dt}.time[{$lent}-1])*{$argv[3]}/npers)
let win = 1 - cos(ircle)
let ircle = npers*ircle
let circle = ircle * ({$spec}.frequency[0]/$argv[3] - 1)
let k=vector( $fpoints )
foreach k $&k
let circle = circle + ircle
foreach vec $argv[4-len]
let tmp = win*{$dt}.{$vec}
let {$spec}.{$vec}[{$k}] = 2*(mean(cos(circle)*tmp),mean(sin(circle)*tmp))
end
end
* plot (and write) the generated spectrum
set curplot = $spec
settype frequency frequency
foreach vec $argv[4-len]
let spectrum = mag({$vec})
plot spectrum
write specout.out spectrum
end
* If you have an oscillator, fimd its frequency
* as maximum of vector spectrum or goto end (uncomment next line)
* goto cleanup
set curplot=$scratch
let counter = 0
let contents = 0
let freqmax = 0
let spectrum = {$spec}.spectrum
foreach spectrum $&spectrum
if counter > 4
if ( contents < $spectrum )
let contents = $spectrum
set count = "$&counter"
let freqmax = {$spec}.frequency[{$count}]
end
end
let counter = counter + 1
end
echo
echo Osc. frequency at "$&freqmax" Hz
echo
goto cleanup
label reset
set curplot=$dt
label cleanup
destroy $scratch
unset fpoints dt scratch spec vec k title lent
label bottom
end