APPLICATION NOTE
Instrument Control and
Automation with Python
Easily control instruments and create test sequences with
Python in a hybrid learning environment with Digital
Learning Suite
Introduction
Test automation is heavily used in the R&D and production environment in the test and measurement
industry. In education, educators and students use test automation to characterize electronic designs,
completing group assignments or research projects. Without the support from an easy-to-use
development environment, scripting test automation is challenging especially when it involves the
integration of multiple remote instruments and DUT controls, interactive user interfaces, advanced data
processing and charting. Often, students also need access to lab equipment out of lab hours to continue
with their lab work. It is crucial to have a test automation environment that can be accessed anywhere
and anytime, equipped with a remote lab management and scheduler.
Keysight enables innovators to push the boundaries of engineering by quickly solving design, emulation,
and test challenges to create the best product experiences. Keysight is committed to ensure that students
in the universities are exposed to the latest tools used in the industry. PathWave Test Automation
(powered by OpenTAP) is the core automation engine powering many Keysight solutions in automating
measurements in fields such as 4G, 5G, Automotive, Cybersecurity, etc. PathWave Test Automation
OpenTAP engine is now built into the new web-based Digital Learning Suite (DLS) to ease the use of test
automation for students and educators in a hybrid learning environment.
Python is a common programming language in the engineering education for device controls and data
analysis. It is widely used in instrumentation and controls, data analysis, control systems, signal
processing, statistical analysis, etc. With OpenTAP and DLS, professors can continue leveraging existing
knowledge and code written in Python and have the frontend test automation handled entirely by DLS.
The DLS can be accessed from a web browser which offers the greatest flexibility and mobility to the
educators and students. The OpenTAP Python plugin makes it possible to use Python to program plugins
for OpenTAP that can eventually be accessed from a browser with DLS. This application note will explain
how you can easily construct a test automation with Python, controlling an instrument and performing
data analysis with OpenTAP and DLS.
2
Setting Up the Environment
We will first set up the environment by installing a few tools and plugins. Then, we will go through two
different ways of using Python in DLS. The first one will see a creation of a test sequence consisting of
an instrument driver and control test step. The second method uses Python to do post data analysis on
results coming from built-in instrument control test steps in DLS.
1. Download Python from [Link]
2. Install Keysight IO Libraries Suite, if not already installed from
[Link]
3. Visit OpenTAP Public Package Repository. [Link]
a. Search for "OpenTAP", select Windows for OS and version 9.20.4+c2191172 or above.
Download the package. Once downloaded, use any archiver to extract the package (it is a
zipped file) to your local folder. You can place the folder anywhere and the location of this folder
is your OpenTAP installation directory. Launch a command prompt window in elevated mode
and change directory to where OpenTAP package is extracted to.
b. Use a command prompt and run the following command lines inside the OpenTAP installation directory:
i. tap package install OpenTAP
ii. tap package install Python
iii. tap package install REST-API - Requires KS8400EDU license
iv. tap package install "Keysight Licensing"
v. tap package install "KeyLab Test Sequencing and Control" - Requires KS8400EDU license
c. Now you are ready to create an OpenTAP plugin with Python
Next, you can start developing Python plugin projects in two different ways. You will find more information
about Python plugin creation here [Link]
1. Develop the plugin in an isolated 'project' folder. This is recommended for bigger projects.
2. Develop the plugin inside the 'Packages' folder. This way it's easy to get started and make
something quick, but maintaining the project becomes harder in the long run
In this app note, we will show you the second method by developing the code in a folder within
%TAP_PATH%\Packages. %TAP_PATH% is your OpenTAP installation directory. To get started quickly,
you can also copy all files in [Link]
examples/[Link] and paste them in your
%TAP_PATH%/Packages/PythonExampleForDLS. This effectively creates a Python plugin called
“PythonExampleForDLS”. The plugin contains many examples of controlling Keysight and non-Keysight
devices. The Python codes presented in the next sections can be found in this plugin folder.
To get started, we will use Keysight InfiniiVision Oscilloscope as an example. We will create an
instrument driver with Python that contains the controls such as “Identify Instrument”, “Auto Scale”,
“Measure Frequency”, “Screenshot” and “Get Trace Data”. We will also show you how you can easily
publish results and transfer data between test steps using the custom “DLSStep” class. Lastly, we will
show you can quickly connect your plugin to DLS, running the test steps, plotting the data.
3
Oscilloscope Instrument Driver
[Link]
from System import Double, String, Byte, Int32
from opentap import *
Import these modules to access the
import OpenTap OpenTAP API that contains .NET types
from OpenTap import Log and OpenTAP classes. Numpy library
import numpy as np can be used to ease data manipulation
@attribute([Link](Name="Oscilloscope", Groups= ["DLS Python Plugin"]))
class OscilloscopeSCPIInstrument([Link]):
def __init__(self): Create an oscilloscope instrument class
super(OscilloscopeSCPIInstrument,self).__init__() that users can add as an instrument.
[Link] = "Oscilloscope" SCPI commands are used to control the
[Link] = 10000 oscilloscope. Attributes (Name, Groups,
etc) can be added to aid users when
def GetIdnString(self):
return [Link][String]("*IDN?") selecting the instrument
def AutoScale(self):
return [Link][String](":AUTOSCALE;*OPC?")
def MeasureFrequency(self,channel):
return [Link][String]("MEASURE:FREQUENCY? CHANNEL" + str(channel))
def GetScreenshot(self):
Now, let us define a few oscilloscope
data = [Link](":DISPlay:DATA? BMP,COLOR") functions that can be used to identify
print(":DISPlay:DATA? BMP,COLOR") the instrument headers, auto scale,
return data measure frequency. Get a screenshot
and trace data. ScpiCommand is used
def GetTraceData(self,channel): to send a SCPI command without a
[Link]("WAVEFORM:SOURCE CHANNEL" + str(channel))
returned value. To get a returned value,
[Link]("WAVEFORM:POINTS:MODE MAXIMUM")
[Link](":WAVeform:FORMat BYTE") we will use ScpiQuery[String] for string
preamble = [Link][String](":WAVEFORM:PREAMBLE?").split(",") value while ScpiQueryBlock for binary
t = int(preamble[1]) value. In “GetTraceData” function,
fPoints = int(preamble[2]) numpy is used to construct arrays that
fCount = int(preamble[3]) contain the returned waveform point
fXincrement = float(preamble[4]) values.
fXorigin = float(preamble[5])
fXreference = float(preamble[6])
fYincrement = float(preamble[7])
fYorigin = float(preamble[8])
fYreference = float(preamble[9])
rawdata = [Link](":WAVeform:DATA?")
x = [Link](len(rawdata),dtype=[Link])
y = [Link](len(rawdata),dtype=[Link])
for idx, d in enumerate(rawdata):
x[idx] = (fXorigin + ( ( float(idx) - fXreference) * fXincrement))
y[idx] = ((d - fYreference) * fYincrement) + fYorigin
return [x,y]
4
Oscilloscope Test Step
[Link]
import sys
import opentap Now, we will use the instrument driver
from opentap import *
import OpenTap “OscilloscopeSCPIInstrument” (from
import math [Link]) created
from OpenTap import Log, EnabledIfAttribute
import System above. In DLS environment, you can
from System import Array, Double, Byte, Int32, String, Boolean
from [Link] import BrowsableAttribute
also use the DLSStep (from
import [Link] [Link]) class to simplify
from [Link] import XmlIgnoreAttribute
from .OscilloscopeInstrument import * data publishing and transfer.
from .DLSOutputInput import *
@attribute([Link](Name="Identify", Description="", Groups= ["DLS Python Plugin", "Oscilloscope"]))
class OscilloscopeIdentify(TestStep): Next, we will create a test step named
ScpiInst = property(OscilloscopeSCPIInstrument, None).add_attribute([Link]( "Oscilloscope"))
“Identify”. A property named
def __init__(self):
super(OscilloscopeIdentify,self).__init__() “Oscilloscope” is created for user to
select the instrument. This test step will
def Run(self):
idn = [Link]() run the “GetIdnString()” function from
[Link]("IDN: "+str(idn))
the instrument driver to obtain the
@attribute([Link](Name="Autoscale", Description="", Groups= ["DLS Python Plugin", "Oscilloscope"])) instrument header and print on the log
class OscilloscopeAutoScale(TestStep):
ScpiInst = property(OscilloscopeSCPIInstrument, None).add_attribute([Link]( "Oscilloscope")) panel. Follow the same process to
create a function named “Autoscale” to
def __init__(self):
super(OscilloscopeAutoScale,self).__init__() run “AutoScale()” function from the
def Run(self):
driver.
[Link]()
@attribute([Link](Name="Measure Frequency", Description="", Groups= ["DLS Python Plugin", "Oscilloscope"])) You can also add more properties for
class OscilloscopeMeasureFrequency(TestStep):
ScpiInst = property(OscilloscopeSCPIInstrument, None).add_attribute([Link]( "Oscilloscope")) users to select. In this case, to measure
Channel = property(Int32, 1).add_attribute([Link]( "Channel")) frequency, user can select the channel
def __init__(self): to measure. Create a property named
super(OscilloscopeMeasureFrequency,self).__init__()
“Channel”. The returned data can be
def Run(self): further processed if needed and be
freq = [Link]([Link])
[Link]("Frequency: "+str(freq)) published to the OpenTAP result
[Link]("Measure Frequency", ["Frequency"], [freq]) listener. The result can later be exported
@attribute([Link](Name="Screenshot", Description="", Groups= ["DLS Python Plugin", "Oscilloscope"])) or plotted in DLS
class OscilloscopeGetScreenshot(DLSStep):
ScpiInst = property(OscilloscopeSCPIInstrument, None).add_attribute([Link]( "Oscilloscope"))
def __init__(self): For more complicated returned data
super(OscilloscopeGetScreenshot,self).__init__()
type such as binary and array, we will
def Run(self):
y = [Link]()
use the “DLSStep” instead. Inherit
super().OutputToDLS("Screenshot",["Screenshot"] ,[y], True) “DLSStep” to use the “OutputToDLS”
@attribute([Link](Name="Trace Data", Description="", Groups= ["DLS Python Plugin", "Oscilloscope"])) function. Data can also be further
class OscilloscopeGetTraceData(DLSStep): manipulated before outputting to DLS if
ScpiInst = property(OscilloscopeSCPIInstrument, None).add_attribute([Link]( "Oscilloscope"))
Channel = property(Int32, 1).add_attribute([Link]( "Channel")) needed. The “OutputToDLS” function
def __init__(self): will format the data into a recognizable
super(OscilloscopeGetTraceData,self).__init__() format in DLS where you can view the
def Run(self): screenshot image or the data array in a
x,y = [Link]([Link])
super().OutputToDLS("TraceData1",["X","Y"] ,[x,y])
tabular form that can be easily plotted
on the DLS charts.
5
Connecting Python to DLS
Now, we have the plugin ready to connect to DLS. Open a command prompt and change the directory to
your OpenTAP installation directory. Type “tap remote sessionmanager” to start the local session for DLS.
In DLS, navigate to Local Test Automation to start the session.
Figure 1. Launching DLS Test Automation
On the DLS Test Automation user interface, add a new “Oscilloscope” instrument under the “DLS Python
Plugin” category and enter the instrument address. Go to Steps and expand the “DLS Python Plugin” to
find all the test steps created in previous sections. Add and run all of them on the test plan.
Figure 2. Creating a test plan and test steps
After completing the run, you can get the measured frequency value, screenshot image and trace data on
the Result page. You can also plot the Trace Data on the Chart page by editing the X and Y text boxes.
Figure 3. Viewing results
6
Data Transfer and Analysis
In DLS test automation, you can also obtain results from other test step to do post data analysis. DLS has
hundreds of built-in test steps that you can readily use to control Keysight and non-Keysight instruments.
With Python, you can easily get results from the built-in steps and perform further data analysis. Now, you
can spend time focusing on creating data analysis algorithms and DLS will manage the control and data
extraction from instruments.
Each test step in DLS has three additional properties: “ResultNamesList”, “ColumnNamesList” and
“DataList”. These properties are used to store the returned data after each successful test step run. A test
step can store one or more ResultNames with each ResultName containing one or more columns of data
array stored in “DataList” where each array corresponds to a ColumnName. These properties can be
renamed by users. “DLSStep” comes with default properties for users to choose the data array based on
the chosen InputTestStep, ResultName and ColumnName. The example below shows how you can
obtain results from other built-in DLS test steps using the above properties, apply mathematical operation
using NumPy (A powerful Python library used for numerical computing and scientific computing) and plot
the data on DLS charts.
Data Transfer and Analysis with Numpy
[Link]
from [Link] import List
from opentap import *
import OpenTap
from OpenTap import TestStep
import numpy as np
from scipy import stats
from .DLSOutputInput import *
First, import from “[Link]”
@attribute([Link](Name="Numpy Difference", Description="", Groups= ["DLS Python Plugin", "Numpy
Step"])) Inherit DLSStep. The DLSStep class
class NumpyDifference(DLSStep): contains a default InputTestStep selection
ResultName1 = property(String, 'Get Measurement').add_attribute([Link]( 'Result Name 1', Or-
der=10))
by default for users to choose. Now, you
ColumnName1 = property(String, 'Data').add_attribute([Link]( 'Column Name 1', Order=11)) can create a few more properties for
ResultName2 = property(String, 'Get Measurement (1)').add_attribute([Link]( 'Result Name 2', users to select ResultName and
Order=12))
ColumnName. In this case, we are going
ColumnName2 = property(String, 'Data').add_attribute([Link]( 'Column Name 2', Order=13))
def __init__(self): to get the results from two test steps.
super().__init__()
def Run(self): Now, we will use “GetStepData1” to get
[SelectedData1,SelectedData2] = super().GetStepData1(self.ResultName1,self.ColumnName1,[Link]- the data from the two test steps by
sultName2,self.ColumnName2)
Data1 = [Link](SelectedData1,dtype=[Link]) selecting the result name and column
Data2 = [Link](SelectedData2,dtype=[Link]) name. If only data from one test step is
super().OutputToDLS('NumpyDifference',['Data2-Data1'],[Data2-Data1]) required, you can use “GetStepData”
instead. We will do a simple subtraction
here. You can further process the data if
needed.
7
In this section, we will look at the use of [Link] described previously by extracting trace data
from two different DLS built-in test steps. Create a new test plan and add two “Get Waveform Data” steps
(from built-in Oscilloscope – InfiniiVision category) with each of them getting data from Channel 1 and
Channel 2 respectively. Alternatively, you can also choose to use your Python-written “Trace Data” steps.
Both test steps must be included as children of “Sequence With Outputs” step obtained from the Flow
Control. Add “Numpy Difference” step created earlier at the bottom.
These are the built-in
steps in DLS.
This is “Numpy
Difference” step created
with Python to process
the data from the above
built-in steps
Figure 4. Creating another test plan and test steps
At the “Numpy Difference” step, change the “Result Name” and “Column Name” to the respective “Get
Waveform Data” steps’ step name and choose “Data” as the column name. Next, run the test plan and
plot Data2-Data1. This effectively subtracts “Get Waveform Data (1)” containing the Channel 2 waveform
data by “Get Waveform Data” containing the Channel 1 waveform data. If needed, you can also apply
additional data manipulation using the built-in mathematical operations inside the X and Y axis text boxes
as shown in Figure 5.
Input Test Step: Select
the test step / sequence of
test steps that contains
the data
Choose ResultName and
ColumnName that
contains the data from the
selected “Input Test Step”
Edit the X and Y
boxes and select the
respective data to
plot. You can also
add mathematical
operations here if
needed.
Figure 5. Viewing results
8
Conclusion
We have shown how Python scripts can be easily connected to DLS to create a powerful test automation
on a web browser. This enables users to access instrument and measurement data anywhere, anywhere,
with maximum flexibility and mobility. The DLS contains an industry-grade test sequencer based on
OpenTAP test automation coupled with a versatile charting tool for users to visualize, compare, analyze,
and share your test results in different format. Apart remote instrument controls and test automation, DLS
also offers an easy access to thousands of on-demand industry-relevant Keysight learning resources.
Also, the DLS enables multi-user remote access with built-in scheduler, optimized for hybrid collaborative
learning. With built-in IMS LTI connection and SSO authentication, you can easily integrate DLS with your
favorite Learning Management System or identity providers
For more information
Visit [Link] to
learn more about DLS
For more insights on how to easily create test automation without programming, visit
[Link]
[Link]
You can also take a quick tour of this software here
[Link]
Keysight enables innovators to push the boundaries of engineering by quickly solving
design, emulation, and test challenges to create the best product experiences. Start your
innovation journey at [Link].
This information is subject to change without notice. © Keysight Technologies, 202 3,
Published in USA, June 6, 2023, [Link]