Instrument Integration #
Litmus does NOT provide instrument drivers. You bring your own:
- PyMeasure (100+ drivers): https://pymeasure.readthedocs.io/
- PyVISA for raw SCPI: https://pyvisa.readthedocs.io/
- Vendor libraries (NI-DAQmx, etc.)
Litmus provides utilities for discovery, identification, mocking, and traceability.
Quick Start with PyVISA #
Install PyVISA with the pure-Python backend (no NI-VISA or Keysight IO Libraries required):
pip install pyvisa pyvisa-pyDirect PyVISA Usage #
import pyvisa
# Connect to instrument
rm = pyvisa.ResourceManager('@py') # Use pyvisa-py backend
dmm = rm.open_resource("TCPIP::192.168.1.100::INSTR")
# Query identity
print(dmm.query("*IDN?"))
# Measure voltage
voltage = float(dmm.query("MEAS:VOLT:DC?"))
print(f"Voltage: {voltage} V")
dmm.close()With Station Config #
In your station YAML, reference the driver class:
# stations/bench_1.yaml
id: bench_1
name: "Test Bench 1"
instruments:
dmm:
type: dmm
driver: pyvisa.resources.MessageBasedResource
resource: "TCPIP::192.168.1.100::INSTR"The pytest plugin will instantiate the driver and make it available as a fixture:
def test_voltage(dmm, logger):
# dmm is a pyvisa MessageBasedResource
voltage = float(dmm.query("MEAS:VOLT:DC?"))
logger.measure("voltage", voltage)Using PyMeasure Drivers #
PyMeasure provides high-level drivers for 100+ instruments:
pip install pymeasure# stations/bench_1.yaml
id: bench_1
name: "Test Bench 1"
instruments:
dmm:
type: dmm
driver: pymeasure.instruments.keysight.Keysight34461A
resource: "TCPIP::192.168.1.100::INSTR"
psu:
type: psu
driver: pymeasure.instruments.keysight.KeysightE36312A
resource: "TCPIP::192.168.1.101::INSTR"def test_output_voltage(psu, dmm, logger):
# PyMeasure provides high-level methods
psu.voltage = 5.0
psu.output_enabled = True
logger.measure("output_voltage", dmm.voltage_dc)
psu.output_enabled = FalseMock Instruments #
For testing without hardware, Litmus provides a Mock factory that works with any class:
from pymeasure.instruments.keithley import Keithley2400
from litmus.instruments.mocks import Mock
# Create mock that passes isinstance checks
smu = Mock(Keithley2400, voltage=5.0, current=1.5e-6)
assert isinstance(smu, Keithley2400)
assert smu.voltage == 5.0Mock Configuration #
Mock supports three value types:
from litmus.instruments.mocks import Mock
# Simple values - always returned
dmm = Mock(object, measure_voltage=3.31)
dmm.measure_voltage() # Returns 3.31
# Dict lookup - first argument is key
inst = Mock(object, query={
"MEAS:VOLT:DC?": "3.31",
"MEAS:CURR:DC?": "0.1",
"*IDN?": "Keysight,34461A,SN123,1.0",
})
inst.query("MEAS:VOLT:DC?") # Returns "3.31"
# Callable - full control
inst = Mock(object, query=lambda cmd: "3.31" if "VOLT" in cmd else "0.0")Station Mock Config #
Configure mocks in station YAML:
# stations/dev_station.yaml
id: dev_station
name: "Development Station"
instruments:
dmm:
type: dmm
catalog_ref: generic_dmm
mock: true # Use mock mode
mock_config:
measure_dc_voltage: 3.31
measure_dc_current: 0.1
psu:
type: psu
catalog_ref: generic_psu
mock: true
mock_config:
measure_voltage: 5.0
measure_current: 0.25Run with mocks:
pytest tests/ --station=dev_station --mock-instruments --dut-serial=TEST001Discovery #
Scan for available VISA instruments:
from litmus.instruments.discovery import discover_visa, get_info_visa
# Find all instruments
resources = discover_visa()
# ["TCPIP::192.168.1.100::INSTR", "USB0::0x1234::0x5678::SN123::INSTR"]
# Get identity info
info = get_info_visa("TCPIP::192.168.1.100::INSTR")
# InstrumentInfo(manufacturer="Keysight", model="34461A", serial="SN123")Or use the CLI:
litmus discoverIntegration Patterns #
pytest (Recommended) #
Station roles become fixtures automatically:
# Station config has dmm and psu → fixtures auto-registered
def test_output_voltage(context, psu, dmm, logger):
psu.voltage = context.get_param("vin", 5.0)
psu.output_enabled = True
logger.measure("output_voltage", dmm.voltage_dc)Custom Fixture Override #
Override auto-registered fixtures for custom setup/teardown:
# tests/conftest.py
import pytest
@pytest.fixture(scope="session")
def psu(instruments):
"""Custom PSU with safety defaults."""
inst = instruments["psu"]
inst.current_limit = 0.5 # Safety limit
yield inst
inst.output_enabled = False # Always disable on teardownStandalone Script #
#!/usr/bin/env python3
import pyvisa
from litmus.instruments.mocks import Mock
def measure_voltage(resource: str, mock: bool = False) -> float:
if mock:
dmm = Mock(object, query={"MEAS:VOLT:DC?": "3.31"})
else:
rm = pyvisa.ResourceManager('@py')
dmm = rm.open_resource(resource)
try:
voltage = float(dmm.query("MEAS:VOLT:DC?"))
return voltage
finally:
if not mock:
dmm.close()
if __name__ == "__main__":
import sys
mock = "--mock" in sys.argv
v = measure_voltage("TCPIP::192.168.1.100::INSTR", mock=mock)
print(f"Voltage: {v} V")Traceability #
Every measurement records which instrument took it:
Per-step instrument identity is stored as parallel arrays (one entry
per instrument touched by the step) under the step_instruments_*
columns:
# Each step row carries parallel arrays:
# - step_instruments_name : ["dmm", "psu", ...]
# - step_instruments_serial : ["SN123456", "SN789", ...]
# - step_instruments_model : ["34461A", "E36312A", ...]
# - step_instruments_firmware : ["1.0.2", "2.1.0", ...]
# - step_instruments_resource : ["TCPIP::...", "GPIB::...", ...]
# (See `INSTRUMENT_ARRAY_KEYS` in litmus.data.backends._row_helpers.)Configure calibration info on the instrument asset (instruments/<id>.yaml) and reference it from the station:
# instruments/keysight_dmm_001.yaml
id: keysight_dmm_001
catalog_ref: catalog/keysight/34461a.yaml
serial: MY12345678
calibration:
due_date: 2024-06-15
last_cal: 2023-06-15
certificate: "CAL-2023-1234"
lab: "Acme Calibration"# stations/bench_1.yaml
instruments:
dmm: keysight_dmm_001 # role → instrument id
resources:
keysight_dmm_001: "TCPIP::192.168.1.100::INSTR" # id → VISA addressA station's instruments: block does not carry a calibration: field — that lives on the instrument asset YAML, which the loader joins to the station at session start.
Next Steps #
- Custom drivers — Build a non-VISA driver
- Mock Mode — Testing without hardware
- Stations — Station architecture