This document serves as a supplemental guide to the more advanced functionality of the ALICE Desktop software interface written for use with the ADALM1000 and ADALM2000 active learning kit hardware. It is assumed that the reader has more than a passing familiarity with the Python programing language.
ALICE 1.1 and 2.0 Desktop is written in Python and includes the Numpy extension for numerical analysis. Numpy is the fundamental Python package for scientific computing. It contains among other things, a powerful array object along with a large library of high-level mathematical functions to operate on these arrays. Such as histograms, the Fourier transform, polynomial curve fitting and random number capabilities. ALICE provides the user access to these functions through a number of the interfaces such as mathematical operations on captured waveforms, including digital filters, arbitrary waveform generation, FFTs and windowing functions.
There are a number of variables that the user can use to customize the appearance of the user interface. These variables are located near the top of the Python program file. Alternatively, especially for users of the Windows executable version of ALICE Desktop, a file named alice_init.ini can be created. It should be placed in the same directory with the alice-desktop-1.1.exe executable file or the directory where the program is started. The alice_init.ini file is read, if found, when ALICE Desktop starts and before any of the windows are created. If no init file is found the internal default settings are used.
To improve the appearance of the desktop screens across operating systems ALICE supports Themed widgets. The theme used can be selected in the alice_init.ini file by adding or changing the following line:
global Style_String; Style_String = 'alt'
Where string 'alt' contains name of the desired theme. In Windows 7 and up the builtin themes are: 'winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative'. For Linux the following are likely options: 'clam', 'alt', 'default', 'classic'. Mac OS X may contain others like 'aqua'. The widgets are generally configured to look the best using the 'alt' style which should be common across operating systems.
How mouse focus is controlled can be either of two modes. The default is that the focus follows the mouse such that you don't need to click inside a window to switch the mouse focus. The other mode is the opposite where the mouse focus does not follow the mouse location that is the mouse focus stays tied to selected window.
The mouse focus modes can be st as follows:
global MouseFocus; MouseFocus = 1 # focus follows mouse around from window to window
global MouseFocus; MouseFocus = 0 # mouse focus stays tied to selected window
Many of the buttons and controls have balloon help that appears if the mouse hovers over a control for more than a few seconds. This feature can be turned on and off by the following:
global ShowBallonHelp; ShowBallonHelp = 0 # balloon help off
global ShowBallonHelp; ShowBallonHelp = 1 # balloon help on
There are pairs of variables for each display window that set the size of the graphics drawing area in screen pixels. The default values are sized to optimally fill a screen with 1024×600 resolution. The menu buttons surrounding the graphics area need this much space to be properly displayed on most screens so using sizes smaller than the default may result in mangled menus.
GRW = 720 # Width of the Time grid
GRH = 390 # Height of the Time grid
GRWN = 720 # Width of the spectrum grid 720 default
GRHN = 390 # Height of the spectrum grid 390 default
GRWBP = 720 # Width of the Bode Plot grid 720 default
GRHBP = 390 # Height of the Bode Plot grid 390 default
GRWXY = 420 # Width of the XY grid 420 default
GRHXY = 390 # Height of the XY grid 390 default
GRWIA = 400 # Width of the Impedance grid 400 default
GRHIA = 400 # Height of the Impedance grid 400 default
The colors that are used to draw the various parts of the screen can be modified.
Color = “#rrggbb” rr=red gg=green bb=blue, Hexadecimal values 00 - ff
COLORframes = “#000080” # 50% blue
COLORcanvas = “#000000” # 100% black used for background color
COLORgrid = “#808080” # 50% Gray used for grid lines
COLORzeroline = “#0000ff” # 100% blue used for vertical and horizontal center grid lines
COLORtrace1 = “#00ff00” # 100% green CH A voltage trace
COLORtrace2 = “#ff8000” # 100% orange CH B voltage trace
COLORtrace3 = “#00ffff” # 100% cyan CH A current trace
COLORtrace4 = “#ffff00” # 100% yellow CH B current trace
COLORtrace5 = “#ff00ff” # 100% magenta Math trace
COLORtrace6 = “#ff0000” # 100% red
COLORtrace7 = “#8080ff” # 100% purple
COLORtraceR1 = “#008000” # 50% green CH A voltage snapshot trace
COLORtraceR2 = “#804000” # 50% orange CH B voltage snapshot trace
COLORtraceR3 = “#008080” # 50% cyan CH A current snapshot trace
COLORtraceR4 = “#808000” # 50% yellow CH B current snapshot trace
COLORtraceR5 = “#800080” # 50% magenta Math snapshot trace
COLORtraceR6 = “#800000” # 50% red
COLORtraceR7 = “#4040a0” # 70% purple
COLORtext = “#ffffff” # 100% white used for Text display
COLORtrigger = “#ff0000” # 100% red used for trigger point
COLORsignalband = “#ff0000” # 100% red
Variable for width of grid lines in pixels
GridWidth = 1
Value for on board resistors and external AD584 reference used in the self calibration procedure
OnBoardRes = 50.83
AD584act = 2.5
Default math equations, These initialize the entry spaces the first time each matching dialog pop-up window opens.
MathString = “(VBuffA[t] + VBuffB[t] - CHAOffset)“
MathXString = ”(VBuffA[t] - CHAOffset)“
MathYString = ”(VBuffB[t] - CHBOffset)“
UserAString = “MaxV1-VATop”
UserALabel = “OverShoot”
UserBString = “MinV2-VBBase”
UserBLabel = “UnderShoot”
MathAxis = “V-A” # can be one of the following “V-A”, “V-B”, I-A”, “I-B”
MathXAxis = “V-A”
MathYAxis = “V-B”
AWGAMathString = ”(VBuffA + VBuffB)/2“
AWGBMathString = ”(VBuffA + VBuffB)/2“
FFTUserWindowString = “numpy.kaiser(SMPfft, 14) * 3”
DigFilterAString = “numpy.sinc(numpy.linspace(-1, 1, 91))“
DigFilterBString = “numpy.sinc(numpy.linspace(-1, 1, 91))”
Inside the alice_init.ini any of these variables can be set using the example format shown here:
global GRW; GRW = 720
global GRH; GRH = 390
global GRWN; GRWN = 720
global GRHN; GRHN = 390
global GRWXY; GRWXY = 420
global GRHXY; GRHXY = 390
global GRWIA; GRWIA = 400
global GRHIA; GRHIA = 400
The variables need to be declared as global when the init file is read at the beginning of the start-up process.
The optional software interfaces can be enabled or disabled by setting the following variables to either 1 or 0 in the alice_init.ini file.
ALICE uses a large number of variables and arrays to hold both the captured data as well as the results of calculations performed on the data. Below is a list of the variable and array names with explanations of their use and how they are calculated. Endsample = Last sample in Buffer
hldn = number of samples from start of buffer to ignore based on Hold Off time setting.
Waveform calculated Vertical measurement constants:
Channel A Average voltage, DCV1 = numpy.mean(VBuffA[hldn:Endsample])
Channel A Minimum voltage, MinV1 = numpy.amin(VBuffA[hldn:Endsample])
Channel A Maximum voltage, MaxV1 = numpy.amax(VBuffA[hldn:Endsample])
Channel A Top voltage, VATop is the voltage of the most positive peak in the histogram
Channel A Base voltage, VABase is the voltage of the least positive peak in the histogram
Channel A RMS voltage, SV1 = numpy.sqrt(numpy.mean(numpy.square(VBuffA[hldn:Endsample])))
Channel B Average voltage, DCV2 = numpy.mean(VBuffB[hldn:Endsample])
Channel B Minimum voltage, MinV2 = numpy.amin(VBuffB[hldn:Endsample])
Channel B Maximum voltage, MaxV2 = numpy.amax(VBuffB[hldn:Endsample])
Channel B Top voltage, VBTop is the voltage of the most positive peak in the histogram
Channel B Base voltage, VBBase is the voltage of the least positive peak in the histogram
Channel B RMS voltage, SV2 = numpy.sqrt(numpy.mean(numpy.square(VBuffB[hldn:Endsample])))
Channel A Average current in mA, DCI1 = numpy.mean(IBuffA[hldn:Endsample])*1000
Channel A Minimum current in mA, MinI1 = numpy.amin(IBuffA[hldn:Endsample]) ])*1000
Channel A Maximum current in mA, MaxI1 = numpy.amax(IBuffA[hldn:Endsample]) ])*1000
Channel A RMS current in mA, SI1 = numpy.sqrt(numpy.mean(numpy.square(IBuffA[hldn:Endsample])))*1000
Channel B Average current in mA, DCI2 = numpy.mean(IBuffB[hldn:Endsample])*1000
Channel B Minimum current in mA, MinI2 = numpy.amin(IBuffB[hldn:Endsample])
Channel B Maximum current in mA, MaxI2 = numpy.amax(IBuffB[hldn:Endsample])
Channel B RMS current in mA, SI2 = numpy.sqrt(numpy.mean(numpy.square(IBuffB[hldn:Endsample])))*1000
Waveform calculated Horizontal measurement constants:
CHAHW is the channel A High Pulse Width
CHALW is the channel A Low Pulse Width
CHADCy is the channel A Duty Cycle
CHAperiod is the channel A Period
CHAfreq is the channel A Frequency
CHABphase is the channel A to channel B relative phase angle
CHBHW is the channel B High Pulse Width
CHBLW is the channel B Low Pulse Width
CHBDCy is the channel B Duty Cycle
CHBperiod is the channel B Period
CHBfreq is the channel B Frequency
Captured Data Waveform Buffers:
VBuffA is the Channel A voltage sample array ( in volts )
VBuffB is the Channel B voltage sample array ( in volts )
IBuffA is the Channel A current sample array ( in amps, multiply by 1000 for mA )
IBuffB is the Channel B current sample array ( in amps, multiply by 1000 for mA )
VmemoryA is the Channel A voltage memory array used for Trace Averaging
VmemoryB is the Channel B voltage memory array used for Trace Averaging
ImemoryA is the Channel A current memory array used for Trace Averaging
ImemoryB is the Channel B current memory array used for Trace Averaging
HBuffA contains the histogram of the channel A voltage waveform
HBuffB contains the histogram of the channel B voltage waveform
VBuffMA is the Mux Mode A channel voltage sample array ( in volts )
VBuffMB is the Mux Mode B channel voltage sample array ( in volts )
VBuffMC is the Mux Mode C channel voltage sample array ( in volts )
VBuffMD is the Mux Mode D channel voltage sample array ( in volts )
t is the time index ( 10 uSec per point )
SAMPLErate is the sampling rate, 100000 samples per Sec, or 10 uSec per sample
Vertical Position variables:
CHAOffset is the value in the channel A voltage position entry window
CHBOffset is the value in the channel B voltage position entry window
CHAIOffset is the value in the channel A current position entry window
CHBIOffset is the value in the channel B current position entry window
AWG waveform arrays:
AWGAwaveform is the Channel A AWG waveform memory array (used for non-built in waveforms)
AWGBwaveform is the Channel B AWG waveform memory array (used for non-built in waveforms)
The following example Python syntax allows setting the start and stop points to be used in an array:
AWGAwaveform[ start : stop ] where start and stop are integers.
Digital Filter coefficients:
DFiltACoef and DFiltBCoef
Use Example: VBuffA = numpy.convolve(VBuffA, DFiltACoef)
Frequency domain buffers:
FFTresultA contains the Channel A voltage magnitude FFT frequency bin results. To get the results in dB the following formula is used:
dbA = (10 * math.log10(float(FFTresultA[n])) + 17) # gives amplitude in dBVolts where 0 dB = 1 Vrms
FFTresultB, Same for Channel B
FFTmemoryA is the Channel A FFT memory array used for Trace Averaging and Peak Hold modes
FFTmemoryB, Same for Channel B
SMPfft is the number of samples used when the FFT is calculated. It will always be a power of 2. And it will be the length of the FFT window function array.
Bode Plotter arrays:
FSweepAdB contains the Channel A voltage magnitude Bode plot frequency sweep results. To get the results in dB the following formula is used:
dbA = (10 * math.log10(float(FSweepAdB[n])) + 17)
FSweepBdB, Same for Channel B
FSweepAPh, Channel A Phase in degrees
FSweepBPh, Same for Channel B
One of the AWG waveforms that can be constructed is the Fourier series of cosines for a square wave.
The routine starts by first making the cosine wave at the fundamental frequency:
AWGAwaveform = numpy.cos(numpy.linspace(0, 2*numpy.pi, SAMPLErate/AWGAFreqvalue))
It then loops over k ( only odd numbers ), the number of requested terms, calculating the harmonic and adding it to the waveform:
Harmonic = (math.sin(k*numpy.pi/2)/k)*(numpy.cos(numpy.linspace(0, k*2*numpy.pi, SAMPLErate/AWGAFreqvalue)))
AWGAwaveform = AWGAwaveform + Harmonic
After all the harmonic terms have been added the waveform is scaled and offset based on the entered Min and Max values.
ALICE supplies two ways to save the contents of any array to a file using the Command Line interface. These are in addition to the ( behind the scenes ) ways provided under the File drop down and other menus. The first is built into numpy. For example, to save the VBuffA array ( channel A voltage waveform buffer ) to a .csv file you would type in the Command Line interface:
numpy.savetxt('my_data.csv', VBuffA, delimiter=',', fmt='%2.4f')
Where “my_data.csv” is the name of the destination file, VBuffA is of course the data array to save, delimiter=”,” tells the function to use a , to separate the columns ( there won’t be multiple columns since most ALICE arrays are one dimensional ) and fmt=‘%2.4f’ sets the format to 4 decimal places.
The second is a wrapper function around the Python wave package. For example, to save the VBuffA array ( channel A voltage waveform buffer ) to a mono .wav file ( at 100 KSPS ) you would type in the Command Line interface:
Write_WAV(VBuffA, 2, “my_data.wav”)
Where “my_data.wav” is the name of the destination file, VBuffA is of course the data array to save, and the 2 tells the program to save two copies of the array data to the file for example. This is handy to make longer versions of the relatively short buffer lengths, used in ALICE, that can be listened to by playing back the .wav file.
ALICE includes the Numpy numerical library of array creation and manipulation functions. The reader is directed to the numpy documentation for complete details on these functions. Here we will point out some of the more useful functions for creating and manipulating waveform sample arrays. Numpy contains many more than can be covered here. However, be sure to only use functions that return 1 dimensional arrays.
In these example we use AWGAwaveform as the array variable but any of the ALICE internal waveform arrays can be of course used.
numpy.ones(length) Return a new array of given length filled with ones.
numpy.zeros(length) Return a new array of given length filled with zeros.
numpy.full(length, fill_value) Return a new array of given length, filled with fill_value.
numpy.linspace(start_value, stop_value, num=length) Return a new array of given length of evenly spaced numbers between start_value and stop_value.
numpy.logspace(start_value, stop_value, num=length, base=log_base) Return a new array of given length of numbers spaced evenly on a log scale. The base of the log can be optionally specified such as 10 or 2 etc.
numpy.square(x) Return the element-wise square of the input.
numpy.sqrt(x) Return the positive square-root of an array, element-wise.
numpy.exp(x) Calculate the exponential of all elements in the input array.
numpy.log(x) Return the Natural logarithm, element-wise.
numpy.log10(x) Return the base 10 logarithm of the input array, element-wise.
numpy.sin(x) Trigonometric Sine, element-wise.
numpy.cos(x) Cosine element-wise.
To create one cycle of a sine wave 400 samples long you will first create an array of values from 0 to 2*pi and then send it to the sine function like this.
numpy.sin(numpy.linspace(0, 2*numpy.pi, 400))
The waveform values will be from -1 to 1 so additionally you will need to scale and or offset the values to be between 0 than 5 for the AWG. In this example we create the sine wave centered on 2.5 V with a P-P of 4 V.
(numpy.sin(numpy.linspace(0, 2*numpy.pi, 400)) * 2) + 2.5
numpy.sinc(x) Return the sinc function.
Much like the trig functions the input to the sinc function is a linear spaced array of points.
numpy.sinc(numpy.linspace(-4, 4, 400)) will product 4 “cycles” 400 samples long.
The values will be between -1 to 1 so additionally you will need to scale and or offset the values to be between 0 than 5 for the AWG. In this example we create the sinc pulse centered on 2.5 V with a peak value of 4.5 V.
(numpy.sinc(numpy.linspace(-4, 4, 400)) * 2)+2.5
numpy.roll(AWGAwaveform, shift) Roll array elements by shift points. This will in effect change the relative timing delay or phase of the waveform.
numpy.concatenate( (AWGAwaveform, AWGBwaveform, … ) ) Join a sequence of arrays.
numpy.repeat(AWGAwaveform, repeats) Repeat elements of an array. This will effectively lower the sample rate of the waveform. If repeat is 2 the frequency of the new waveform will be ½ what the original was.
The pad function adds samples to the beginning and end of the array.
numpy.pad(AWGAwaveform, (100, 100), ‘edge’)
numpy.pad(AWGAwaveform, (100,100), ‘maximum’)
The first argument is the array variable, next is a list of the number of points to add. In the case of our one dimensional waveforms this is just two values for the beginning and end of the array. The third argument tells the function what values to use to extend the array. How the array is extended can be one of the following:
‘constant’ - Pads with a constant value.
‘edge’ - Pads with the edge values of array.
‘linear_ramp’ - Pads with the linear ramp between end_value and the array edge value.
‘maximum’ - Pads with the maximum value of all or part of the vector along each axis.
‘mean’ - Pads with the mean value of all or part of the vector along the axis.
‘median’ - Pads with the median value of all or part of the vector along the axis.
‘minimum’ - Pads with the minimum value of all or part of the vector along the axis.
‘reflect’ - Pads with the reflection of the vector mirrored on the first and last values of the vector along the axis.
‘symmetric’ - Pads with the reflection of the vector mirrored along the edge of the array.
‘wrap’ - Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning.
numpy.bartlett(length) Return the Bartlett window.
numpy.blackman(length) Return the Blackman window.
numpy.hamming(length) Return the Hamming window.
numpy.hanning(length) Return the Hanning window.
numpy.kaiser(length, beta) Return the Kaiser window
numpy.convolve(a, v) Returns the discrete, linear convolution of two one-dimensional sequences.
This is used primarily for digital filtering of waveform data arrays.
numpy.polyfit(x, y, deg) Fit a polynomial p(x) = p * x^deg + … + p[deg] of degree deg to points (x, y). Returns a vector of coefficients p that minimizes the squared error.
numpy.poly1d(p) A convenience class, used to encapsulate “natural” operations on polynomials so that said operations may take on their customary form in code.
The following example shows how to use polyfit and poly1d to fit a 5th order polynomial to the voltage characteristics of diode and plot the polynomial over the plot of the measured data points.
First construct the simple resistor and diode circuit shown in the figure.
Figure, Diode test circuit
Set Channel A AWG Min value to 0V and Max value to 5V. Set the Mode to SVMI and the Shape to triangle. Set the Freq to 100 Hz. Set Channel B mode to Hi-Z to measure the voltage across the diode.
Set the Horz Time scale to 0.5mSec/Div. Hit Run, wait for a few seconds to capture some data then hit Stop. This should display the rising half of the triangle wave on Channel A from 0 to 5 V ( green trace ). The width of the grid will be 500 sample points ( 5 mSec at 10 uSec/sample). Channel B should display the voltage across the diode going from 0 to about 0.8 V ( orange trace ). You may want to change the vertical scale to 0.1 V/div and position to 0.5 for CH-B to display the waveform from 0 to 1 V. You should now have something like this:
Plot of Diode Voltage
Open the Command Line interface ( with the program stopped ). We want to fit a polynomial to the first 500 samples where CH-A ramps from 0 to 5 V. Type the following line into the entry space and hit return.
global Zpoly; Zpoly = numpy.polyfit(VBuffA[0:499], VBuffB[0:499], 5)
To check the terms of the polynomial type the following line into the entry space and hit return.
In the ALICE desktop console window you should see something like this.
ALICE console showing polynomial terms
We can use poly1d to make an object that makes this easy to plot on the screen. Type the following line into the entry space and hit return.
global ZBuff; ZBuff = numpy.poly1d(Zpoly)
Again to check the results type the following line into the entry space and hit return.
In the ALICE desktop console window you should now see something like this.
ALICE console showing polynomial equation
To plot the polynomial on the screen we will use the Math waveform feature. From the Math drop down menu select Math Axis and set it to V-B to use the same axis as the diode voltage plot. From the Math drop down menu select Enter Formula and enter the following:
This plots the value of the polynomial evaluated at each point in VBuffA as the time index t goes from 0 to 499 ( 5 mSec ). Be sure to note that () are used for ZBuff and not  because it is a function and not an array like VBuffA. You should now see something like this on the display. The magenta Math plot is the polynomial.
Plot of measured data and polynomial
Many of the random number functions return arrays of random numbers. Here are a few examples:
numpy.random.standard_normal(8000)+2.5 will return a 8,000 sample array of random numbers with a normal distribution, standard deviation = 1, centered on 2.5.
numpy.random.uniform(1,4,10000) will return a 10,000 sample array of random numbers with a uniform distribution between 1 and 4.
numpy.random.triangular(1, 2.5, 4, 10000) will return a 10,000 sample array of random numbers with a triangular distribution between 1 and 4, centered on 2.5.
For completeness, here are a few of the more obscure arrays used in ALICE:
These trace “lines” are 2d X-Y arrays in screen pixels.
T1Vline =  # Voltage Trace line channel A
T2Vline =  # Voltage Trace line channel B
T1Iline =  # Current Trace line channel A
T2Iline =  # Current Trace line channel B
TMAVline =  # Voltage Trace line MUX channel A
TMBVline =  # Voltage Trace line MUX channel B
TMCVline =  # Voltage Trace line MUX channel C
TMDVline =  # Voltage Trace line MUX channel D
TMBRline =  # V reference Trace line MUX channel B
TMCRline =  # V reference line MUX channel C
TXYline =  # XY Trace line
TXYRline =  # XY reference trace line
Tmathline =  # Math trace line
T1VRline =  # V reference Trace line channel A
T2VRline =  # V reference Trace line channel B
T1IRline =  # I reference Trace line channel A
T2IRline =  # I reference Trace line channel B
TMRline =  # Math reference Trace line
T1Fline =  # Frequency Trace line channel A
T2Fline =  # Frequency Trace line channel B
T1Pline =  # Phase angle Trace line channel A - B
T2Pline =  # Phase angle Trace line channel B - A
T1FRline =  # F reference Trace line channel A
T2FRline =  # F reference Trace line channel B
T1PRline =  # Phase reference Trace line channel A - B
T2PRline =  # Phase reference Trace line channel B - A
TFMline =  # Frequency Math Trace
TFRMline =  # Frequency reference Math Trace
TAFline =  # Bode Freq Trace line channel A
TBFline =  # Bode Freq Trace line channel B
TAPline =  # Bode Phase angle Trace line channel A - B
TBPline =  # Bode Phase angle Trace line channel B - A
TAFRline =  # Bode F reference Trace line channel A
TBFRline =  # Bode F reference Trace line channel B
TAPRline =  # Bode Phase reference Trace line channel A - B
TBPRline =  # Bode Phase reference Trace line channel B - A
TBPMline =  # Bode Frequency Math Trace
TBPRMline =  # Bode Frequency reference Math Trace
For Further Reading: