Pure Python implementation of a VISA library for instrument communication.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Support for Prologix GPIB-USB and GPIB-Ethernet adapters that provide GPIB functionality through USB and TCP/IP interfaces. PyVISA-py includes specialized session classes for Prologix adapters with thread-safe operation, command escaping, and proper bus management.
Handles communication with Prologix Ethernet-to-GPIB adapters, providing GPIB bus access over TCP/IP networks.
class PrologixTCPIPIntfcSession:
"""Session for Prologix Ethernet-to-GPIB adapter interface."""
def __init__(self, resource_manager_session, resource_name, parsed, open_timeout):
"""
Initialize Prologix TCP/IP interface session.
Args:
resource_manager_session (VISARMSession): Parent RM session
resource_name (str): Prologix TCP/IP resource name
parsed (rname.ResourceName): Parsed resource name
open_timeout (int): Connection timeout in milliseconds
"""
def read(self, count):
"""
Read data from GPIB device via Prologix adapter.
Args:
count (int): Maximum number of bytes to read
Returns:
Tuple[bytes, StatusCode]: Data read and operation status
"""
def write(self, data):
"""
Write data to GPIB device via Prologix adapter.
Args:
data (bytes): Data to transmit
Returns:
Tuple[int, StatusCode]: Number of bytes written and status
"""
def clear(self):
"""
Clear GPIB device using Prologix adapter.
Returns:
StatusCode: Operation result
"""
def close(self):
"""
Close Prologix TCP/IP connection.
Returns:
StatusCode: Operation result
"""Handles communication with Prologix USB-to-GPIB adapters through virtual serial ports.
class PrologixASRLIntfcSession:
"""Session for Prologix USB-to-GPIB adapter interface."""
def __init__(self, resource_manager_session, resource_name, parsed, open_timeout):
"""
Initialize Prologix serial interface session.
Args:
resource_manager_session (VISARMSession): Parent RM session
resource_name (str): Prologix serial resource name
parsed (rname.ResourceName): Parsed resource name
open_timeout (int): Connection timeout in milliseconds
"""
def read(self, count):
"""
Read data from GPIB device via Prologix USB adapter.
Args:
count (int): Maximum number of bytes to read
Returns:
Tuple[bytes, StatusCode]: Data read and operation status
"""
def write(self, data):
"""
Write data to GPIB device via Prologix USB adapter.
Args:
data (bytes): Data to transmit
Returns:
Tuple[int, StatusCode]: Number of bytes written and status
"""
def clear(self):
"""
Clear GPIB device using Prologix USB adapter.
Returns:
StatusCode: Operation result
"""
def close(self):
"""
Close Prologix serial connection.
Returns:
StatusCode: Operation result
"""Provides high-level instrument communication through Prologix adapters with automatic address management and command escaping.
class PrologixInstrSession:
"""Session for GPIB instruments accessed via Prologix adapters."""
def __init__(self, resource_manager_session, resource_name, parsed, open_timeout):
"""
Initialize Prologix instrument session.
Args:
resource_manager_session (VISARMSession): Parent RM session
resource_name (str): Instrument resource name via Prologix
parsed (rname.ResourceName): Parsed resource name
open_timeout (int): Connection timeout in milliseconds
"""
def read(self, count):
"""
Read data from GPIB instrument.
Args:
count (int): Maximum number of bytes to read
Returns:
Tuple[bytes, StatusCode]: Data read and operation status
"""
def write(self, data):
"""
Write data to GPIB instrument.
Args:
data (bytes): Data to transmit
Returns:
Tuple[int, StatusCode]: Number of bytes written and status
"""
def assert_trigger(self, protocol):
"""
Assert trigger on GPIB instrument.
Args:
protocol (constants.TriggerProtocol): Trigger protocol
Returns:
StatusCode: Operation result
"""Configure Prologix adapter settings for proper GPIB bus operation and device communication.
# Prologix adapter configuration commands (sent as strings)
PROLOGIX_COMMANDS = {
'auto': '++auto', # Auto read after write mode
'addr': '++addr', # Set GPIB address
'mode': '++mode', # Set controller/device mode
'eoi': '++eoi', # End-of-string assertion
'eos': '++eos', # End-of-string character
'read': '++read', # Read from instrument
'clr': '++clr', # Clear device
'trg': '++trg', # Trigger device
'ver': '++ver', # Get adapter version
'help': '++help', # Get help information
}import pyvisa
# Use PyVISA-py backend
rm = pyvisa.ResourceManager('@py')
# Resource format: PRLGX-TCPIP::hostname::port::INTFC
# Default port is typically 1234 for Prologix Ethernet adapters
try:
# Open Prologix Ethernet-to-GPIB adapter
adapter = rm.open_resource("PRLGX-TCPIP::192.168.1.200::1234::INTFC")
# Configure adapter timeout
adapter.timeout = 5000 # 5 seconds
# Get adapter version
adapter.write("++ver")
version = adapter.read()
print(f"Prologix version: {version.strip()}")
# Configure for controller mode
adapter.write("++mode 1") # Controller mode
adapter.write("++auto 0") # Manual read mode
adapter.write("++eoi 1") # Assert EOI
adapter.write("++eos 3") # Both CR and LF
# Address GPIB device at address 10
adapter.write("++addr 10")
# Send SCPI command to instrument
adapter.write("*IDN?")
adapter.write("++read eoi") # Read response
idn = adapter.read()
print(f"Instrument ID: {idn.strip()}")
# Send measurement command
adapter.write("READ?")
adapter.write("++read")
measurement = adapter.read()
print(f"Measurement: {measurement.strip()}")
except pyvisa.VisaIOError as e:
print(f"Prologix Ethernet error: {e}")
finally:
adapter.close()
rm.close()# Resource format: PRLGX-ASRL::port::INTFC
# Port is the serial port where USB adapter appears
try:
# Open Prologix USB-to-GPIB adapter
# Windows: "PRLGX-ASRL::COM3::INTFC"
# Linux: "PRLGX-ASRL::/dev/ttyUSB0::INTFC"
adapter = rm.open_resource("PRLGX-ASRL::COM3::INTFC")
# USB adapters typically use 115200 baud
adapter.baud_rate = 115200
adapter.timeout = 3000
# Initialize adapter
adapter.write("++ver")
version = adapter.read()
print(f"USB Adapter version: {version.strip()}")
# Configure adapter settings
adapter.write("++mode 1") # Controller mode
adapter.write("++auto 1") # Auto read after write
adapter.write("++eoi 1") # Assert EOI with last byte
adapter.write("++eos 2") # LF terminator only
# Communicate with multiple GPIB devices
devices = [3, 5, 10, 15] # GPIB addresses
for address in devices:
try:
# Switch to device address
adapter.write(f"++addr {address}")
# Query device identification
adapter.write("*IDN?")
idn = adapter.read()
print(f"Device {address}: {idn.strip()}")
except pyvisa.VisaIOError:
print(f"Device {address}: No response")
except pyvisa.VisaIOError as e:
print(f"Prologix USB error: {e}")
finally:
adapter.close()# Using Prologix adapter transparently for instrument access
# PyVISA-py can automatically handle Prologix protocol
try:
# Open instrument through Prologix adapter
# The adapter addressing is handled automatically
inst = rm.open_resource("PRLGX-TCPIP::192.168.1.200::10::INSTR")
# Configure instrument session
inst.timeout = 5000
inst.read_termination = '\\n'
inst.write_termination = '\\n'
# Standard instrument communication
idn = inst.query("*IDN?")
print(f"Instrument: {idn.strip()}")
# Reset and configure instrument
inst.write("*RST")
inst.write("*CLS")
# Make measurement
voltage = float(inst.query("MEAS:VOLT:DC?"))
print(f"Voltage: {voltage} V")
# Trigger instrument
inst.assert_trigger()
# Check for completion
opc = inst.query("*OPC?")
print(f"Operation complete: {opc.strip()}")
except pyvisa.VisaIOError as e:
print(f"Instrument communication error: {e}")
finally:
inst.close()# Transfer large amounts of data through Prologix adapter
adapter = rm.open_resource("PRLGX-TCPIP::192.168.1.200::1234::INTFC")
try:
# Configure for high-speed transfer
adapter.write("++mode 1")
adapter.write("++auto 0") # Manual control for bulk transfer
adapter.write("++eoi 1") # Use EOI for efficiency
# Address waveform generator
adapter.write("++addr 12")
# Send arbitrary waveform data (large data block)
waveform_data = [0.5 * np.sin(2 * np.pi * i / 1000) for i in range(10000)]
# Format for SCPI DATA command
data_string = ",".join(f"{x:.6f}" for x in waveform_data)
# Send in chunks to avoid buffer overflow
chunk_size = 1000
adapter.write(f"DATA:ARB MYWAVE,{len(waveform_data)}")
for i in range(0, len(data_string), chunk_size):
chunk = data_string[i:i+chunk_size]
adapter.write(chunk)
time.sleep(0.01) # Small delay for adapter
# Verify upload
adapter.write("DATA:ARB:SIZE? MYWAVE")
adapter.write("++read")
size = int(adapter.read())
print(f"Uploaded waveform size: {size} points")
except Exception as e:
print(f"Bulk transfer error: {e}")
finally:
adapter.close()def diagnose_prologix_adapter(resource_name):
"""Diagnose Prologix adapter connectivity and configuration."""
try:
adapter = rm.open_resource(resource_name)
adapter.timeout = 2000
print(f"Testing {resource_name}...")
# Test basic communication
try:
adapter.write("++ver")
version = adapter.read()
print(f"✓ Adapter responds: {version.strip()}")
except:
print("✗ Adapter not responding")
return False
# Test configuration commands
commands = [
("++mode 1", "Set controller mode"),
("++auto 0", "Disable auto mode"),
("++eoi 1", "Enable EOI"),
]
for cmd, desc in commands:
try:
adapter.write(cmd)
print(f"✓ {desc}")
except Exception as e:
print(f"✗ {desc}: {e}")
# Test GPIB bus scan
print("Scanning GPIB bus...")
found_devices = []
for addr in range(1, 31):
try:
adapter.write(f"++addr {addr}")
adapter.write("*IDN?")
adapter.write("++read eoi")
response = adapter.read()
if response.strip():
found_devices.append((addr, response.strip()))
print(f" Address {addr}: {response.strip()}")
except:
continue # No device at this address
if not found_devices:
print(" No GPIB devices found")
adapter.close()
return True
except Exception as e:
print(f"✗ Adapter test failed: {e}")
return False
# Test both adapter types
diagnose_prologix_adapter("PRLGX-TCPIP::192.168.1.200::1234::INTFC")
diagnose_prologix_adapter("PRLGX-ASRL::COM3::INTFC")Prologix adapter resource strings use custom formats:
PRLGX-TCPIP::[board]::hostname::port::INTFC
PRLGX-TCPIP::[board]::hostname::gpib_address::INSTRPRLGX-ASRL::port::INTFC
PRLGX-ASRL::port::gpib_address::INSTRExamples:
PRLGX-TCPIP::192.168.1.200::1234::INTFC - Ethernet adapter interfacePRLGX-TCPIP::192.168.1.200::10::INSTR - Instrument at GPIB address 10 via EthernetPRLGX-ASRL::COM3::INTFC - USB adapter interfacePRLGX-ASRL::/dev/ttyUSB0::15::INSTR - Instrument at GPIB address 15 via USBPrologix adapters use ++ prefix for adapter commands to distinguish from instrument commands:
# Adapter configuration commands
adapter.write("++mode 1") # Configure adapter
adapter.write("++addr 10") # Set GPIB address
# Instrument commands (no ++ prefix)
adapter.write("*IDN?") # Send to instrument
adapter.write("++read") # Read instrument responsePrologix sessions include thread-safe address switching to prevent conflicts when multiple threads access different GPIB devices:
import threading
import time
def instrument_worker(adapter_resource, gpib_address, measurements):
"""Worker function for multi-threaded instrument access."""
# Each thread gets its own session
local_rm = pyvisa.ResourceManager('@py')
try:
inst = local_rm.open_resource(f"{adapter_resource}::{gpib_address}::INSTR")
for i in range(measurements):
# Thread-safe addressing is handled automatically
value = float(inst.query("READ?"))
print(f"Address {gpib_address}, measurement {i}: {value}")
time.sleep(0.5)
except Exception as e:
print(f"Thread {gpib_address} error: {e}")
finally:
inst.close()
local_rm.close()
# Start multiple threads accessing different GPIB devices
adapter = "PRLGX-TCPIP::192.168.1.200::1234"
threads = []
for address in [3, 5, 10]:
thread = threading.Thread(
target=instrument_worker,
args=(adapter, address, 5)
)
threads.append(thread)
thread.start()
# Wait for all threads to complete
for thread in threads:
thread.join()Prologix adapter support has the same dependencies as the underlying communication method:
# Install serial support for USB adapters
pip install pyvisa-py[serial]Install with Tessl CLI
npx tessl i tessl/pypi-pyvisa-py