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. Specialized VISADevice 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:

  • Any pyvisa resource name

  • The serial number of a connected instrument (for instruments discoverable through labbench.visa_probe_devices()). For subclasses that define make and/or model, and these together match exactly one connected instrument, the resource 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.807PowerSensor(): probed resource by matching make 'FakeTech', model 'Power Sensor #1234'
 DEBUG  2024-03-20 13:42:54,808.809PowerSensor(): 'USB0::0x1111::0x2222::0x1234::0::INSTR'  → resource
 DEBUG  2024-03-20 13:42:54,809.810PowerSensor(): opened
 DEBUG  2024-03-20 13:42:54,810.810PowerSensor(): write('SYST:PRES')
 DEBUG  2024-03-20 13:42:54,811.811PowerSensor(): write('SENS:FREQ 1000000000.0')
 DEBUG  2024-03-20 13:42:54,812.813PowerSensor(): 1000000000.0 (Hz) → frequency
 DEBUG  2024-03-20 13:42:54,812.813PowerSensor(): write('SENS:MRAT FAST')
 DEBUG  2024-03-20 13:42:54,813.813PowerSensor(): 'FAST'  → measurement_rate
 DEBUG  2024-03-20 13:42:54,814.814PowerSensor(): write('TRIG:COUN 200')
 DEBUG  2024-03-20 13:42:54,815.815PowerSensor(): 200 (samples) → trigger_count
 DEBUG  2024-03-20 13:42:54,815.816PowerSensor(): write('SWE:APER 2e-05')
 DEBUG  2024-03-20 13:42:54,816.816PowerSensor(): 2e-05 (s) → sweep_aperture
 DEBUG  2024-03-20 13:42:54,816.817PowerSensor(): write('INIT:CONT ON')
 DEBUG  2024-03-20 13:42:54,817.817PowerSensor(): True  → initiate_continuous
 DEBUG  2024-03-20 13:42:54,817.818PowerSensor(): query('FETC?'):
 DEBUG  2024-03-20 13:42:54,820.820PowerSensor():     → (1572 bytes)
 DEBUG  2024-03-20 13:42:54,820.821PowerSensor(): query('TRIG:COUN?'):
 DEBUG  2024-03-20 13:42:54,821.821PowerSensor():     → '200'
 DEBUG  2024-03-20 13:42:54,821.822PowerSensor(): trigger_count → 200  (samples)
 DEBUG  2024-03-20 13:42:54,958.959PowerSensor(): 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.