VISA automation quick start¶
Labbench includes several features to organize and streamline VISA instrument automation through pyvisa
.
The labbench pyvisa backend¶
Generic VISA automation functions are encapsulated in a general-purpose object type: labbench.VISADevice
.
This brings several benefits compared to passing message strings directly into pyvisa
objects:
multi-threaded connection management
automatic coercion between python types and low-level/over-the-wire data types
support for automatic logging of instrument configuration parameters and metadata for
constraints on instrument parameters
When available, other more specialized classes (subclasses) can tailor VISADevice
to expose pythonic automation functions tailored to a specific instruments. However, guidance that follows for VISADevice
can also be applied to its subclasses.
Resource managers¶
Labbench supports the use of any pyvisa
resource manager backend.
The default is (`”@py”``), which is installed as a dependency
A special case driver for demonstration and testing is
@sim-labbench
. SpecializedVISADevice
objects for the simulated instruments are provided in
The following examples use some pre-defined simulated VISA instruments to illustrate workflow. These are exposed through the pyvisa
“@sim-labbench” resource manager.
Discover connected instruments¶
The labbench command line tool provides device discovery based on . The following
# remove the ! when running in a command prompt
!labbench visa-probe @sim-labbench
Resources discovered on resource manager '/Users/dkuester/Documents/src/labbench/src/labbench/testing/pyvisa_sim.yml@sim':
• Make: "Faketech"
Model: "Oscilloscope #1234"
Serial: "OMGBBQ58"
Device: lb.VISADevice('USB0::0x1111::0x2233::0x9876::0::INSTR')
• Make: "Faketech"
Model: "Spectrum Analyzer #1234"
Serial: "#AUJH99"
Device: lb.VISADevice('USB0::0x1111::0x2222::0x4445::0::INSTR')
• Make: "Faketech"
Model: "Signal Generator #1234"
Serial: "1837518935"
Device: lb.VISADevice('TCPIP0::localhost:10001::inst0::INSTR')
• Make: "Faketech"
Model: "Power Sensor #1234"
Serial: "63472"
Device: lb.VISADevice('USB0::0x1111::0x2222::0x1234::0::INSTR', write_termination='\r\n')
This probes instruments by attempting *IDN?
queries on the resource strings discovered by the resource manager. The resulting responses are used to determine valid connection parameters (read_termination
and write_termination
). When successful, identifying characteristics (make, model, serial number, and revision) are shown, together with explicit syntax to create a generic instrument control object.
For @py
backends, information about missing drivers is also shown when they limit the scope of the discovery.
Resource names¶
The VISADevice
resource argument specifies the information required to open a connection to the instrument. These can include:
The serial number of a connected instrument (for instruments discoverable through
labbench.visa_probe_devices()
). For subclasses that definemake
and/ormodel
, and these together match exactly one connected instrument, theresource
argument can also be omitted.
Connection with a VISADevice¶
At its simplest, a VISADevice
object exposes the communication capabilities of pyvisa resource. Starting from the power sensor in our VISA probe:
import labbench as lb
lb.visa_default_resource_manager('@sim-labbench')
inst = lb.VISADevice('USB0::0x1111::0x2222::0x1234::0::INSTR', write_termination='\r\n')
with inst:
print(inst.query('*IDN?'))
FakeTech,Power Sensor #1234,63472,rev f
The backend connection remains open for VISA communication for code executing in with
block. It is closed on exit, even in the event of an exception
Using specialized wrappers¶
Tailored instrument classes provide more convenient pythonic interaction. One example is provided for our
simulated instrument in labbench
. However, many more are available in external libraries like
ssmdevices.
# use a pyvisa-sim simulated VISA instrument for the demo
from labbench.testing.pyvisa_sim import PowerSensor
lb.visa_default_resource_manager('@sim-labbench')
# watch the low-level write and query actions
lb.show_messages('debug')
# No resource name required if it's the only connected match for its make and model
with PowerSensor() as sensor:
# apply the instrument preset state
sensor.preset()
# set acquisition parameters on the power sensor
sensor.frequency = 1e9
sensor.measurement_rate = "FAST"
sensor.trigger_count = 200
sensor.sweep_aperture = 20e-6
sensor.initiate_continuous = True
# retreive the 200 measurement samples
power = sensor.fetch()
DEBUG 2024-03-20 13:42:54,807.807 • PowerSensor(): probed resource by matching make 'FakeTech', model 'Power Sensor #1234'
DEBUG 2024-03-20 13:42:54,808.809 • PowerSensor(): 'USB0::0x1111::0x2222::0x1234::0::INSTR' → resource
DEBUG 2024-03-20 13:42:54,809.810 • PowerSensor(): opened
DEBUG 2024-03-20 13:42:54,810.810 • PowerSensor(): write('SYST:PRES')
DEBUG 2024-03-20 13:42:54,811.811 • PowerSensor(): write('SENS:FREQ 1000000000.0')
DEBUG 2024-03-20 13:42:54,812.813 • PowerSensor(): 1000000000.0 (Hz) → frequency
DEBUG 2024-03-20 13:42:54,812.813 • PowerSensor(): write('SENS:MRAT FAST')
DEBUG 2024-03-20 13:42:54,813.813 • PowerSensor(): 'FAST' → measurement_rate
DEBUG 2024-03-20 13:42:54,814.814 • PowerSensor(): write('TRIG:COUN 200')
DEBUG 2024-03-20 13:42:54,815.815 • PowerSensor(): 200 (samples) → trigger_count
DEBUG 2024-03-20 13:42:54,815.816 • PowerSensor(): write('SWE:APER 2e-05')
DEBUG 2024-03-20 13:42:54,816.816 • PowerSensor(): 2e-05 (s) → sweep_aperture
DEBUG 2024-03-20 13:42:54,816.817 • PowerSensor(): write('INIT:CONT ON')
DEBUG 2024-03-20 13:42:54,817.817 • PowerSensor(): True → initiate_continuous
DEBUG 2024-03-20 13:42:54,817.818 • PowerSensor(): query('FETC?'):
DEBUG 2024-03-20 13:42:54,820.820 • PowerSensor(): → (1572 bytes)
DEBUG 2024-03-20 13:42:54,820.821 • PowerSensor(): query('TRIG:COUN?'):
DEBUG 2024-03-20 13:42:54,821.821 • PowerSensor(): → '200'
DEBUG 2024-03-20 13:42:54,821.822 • PowerSensor(): trigger_count → 200 (samples)
DEBUG 2024-03-20 13:42:54,958.959 • PowerSensor(): closed
The use of debug messages serve as validation of the anticipated SCPI messages and responses. These are helpful when
familiarizing with labbench
after working directly with the command strings, or when developing wrappers of your own.