Skip to content

Transport Utilities

Instrumation v0.6.0 introduces a set of transport utility functions that help with common instrument communication tasks. These functions are available in the instrumation.transport module.

Overview

Function Description
detect_line_termination() Auto-detect the correct line termination character for an instrument
find_minimum_timeout() Find the smallest safe timeout value for an instrument
poll_for_mav() Poll the Status Byte Register for the MAV (Message Available) bit
poll_opc_with_backoff() Poll for operation-complete with exponential backoff

detect_line_termination()

Different instruments use different line-termination characters (LF, CR, or CRLF). When connecting to a new instrument, guessing the wrong terminator causes silent failures. This function automates detection.

import os
os.environ["INSTRUMATION_MODE"] = "SIM"
from instrumation.factory import get_instrument
from instrumation.transport import detect_line_termination

dmm = get_instrument("DUMMY", "DMM")
dmm.connect()
terminator = detect_line_termination(dmm)
print(f"Instrument uses: {repr(terminator)}")
dmm.disconnect()

Returns: One of "\n", "\r", or "\r\n".

Raises: RuntimeError if no terminator produces a valid response.

find_minimum_timeout()

Instruments vary wildly in response time. Setting a timeout too low causes spurious failures; setting it too high makes error detection sluggish. This function finds the smallest timeout that actually works.

import os
os.environ["INSTRUMATION_MODE"] = "SIM"
from instrumation.factory import get_instrument
from instrumation.transport import find_minimum_timeout

dmm = get_instrument("DUMMY", "DMM")
dmm.connect()
min_timeout = find_minimum_timeout(dmm)
print(f"Minimum safe timeout: {min_timeout}ms")
dmm.disconnect()

Returns: An integer representing milliseconds.

Raises: RuntimeError if no candidate timeout works.

Default candidates: [100, 250, 500, 1000, 2500, 5000]

poll_for_mav()

Many drivers read immediately after a query, but some instruments aren't ready yet. The MAV (Message Available) bit in the Status Byte Register tells you definitively when data is ready.

import os
os.environ["INSTRUMATION_MODE"] = "SIM"
from instrumation.factory import get_instrument
from instrumation.transport import poll_for_mav

dmm = get_instrument("DUMMY", "DMM")
dmm.connect()
poll_for_mav(dmm, timeout=5.0, poll_interval=0.1)
# Now safe to read
dmm.disconnect()

Parameters: - timeout: Maximum seconds to wait for MAV (default: 10.0) - poll_interval: Seconds between polls (default: 0.1)

Raises: InstrumentTimeout if MAV is not set within the timeout.

poll_opc_with_backoff()

Long instrument operations (VNA presets, full calibrations) can take seconds. Polling *OPC? at a fixed interval wastes bus traffic. Exponential backoff starts fast (catches quick completions) then backs off to reduce bus load.

import os
os.environ["INSTRUMATION_MODE"] = "SIM"
from instrumation.factory import get_instrument
from instrumation.transport import poll_opc_with_backoff

vna = get_instrument("DUMMY_VNA", "NA")
vna.connect()
vna.preset()
poll_opc_with_backoff(vna, timeout=30.0, initial_delay=0.1, max_delay=2.0)
print("VNA preset complete")
vna.disconnect()

Parameters: - timeout: Maximum seconds to wait (default: 30.0) - initial_delay: Starting poll delay in seconds (default: 0.1) - max_delay: Maximum delay between polls (default: 1.0)

Raises: InstrumentTimeout if *OPC? does not return "1" within the timeout.

Scanner Utilities

find_duplicate_addresses()

On shared buses (GPIB, RS-485), two instruments configured with the same address corrupt each other's responses. This function flags addresses that show up multiple times in a scan result. Devices are keyed on (type, desc), so a conflict is reported even when two genuinely different devices share an identical description string (e.g. two serial ports both labelled "USB Serial Port").

from instrumation.scanner import scan, find_duplicate_addresses

devices = scan()
conflicts = find_duplicate_addresses(devices)
for c in conflicts:
    print(f"CONFLICT on {c['address']}: {c['identities']}")

Returns: A list of dicts, each with keys: - "address": The duplicated address string - "identities": List of distinct descriptions seen - "count": How many times it appeared

Returns empty list if no conflicts found.