Tektronix Instruments
How to control Tektronix oscilloscopes, signal generators, and spectrum analyzers with PyVISA. Connection snippets, SCPI commands, and quirks.
Control Tektronix oscilloscopes, AFGs, and spectrum analyzers with PyVISA. One connection pattern, model-specific SCPI.
Supported Models
| Category | Series | Examples |
|---|---|---|
| Oscilloscopes | TDS/TBS | TDS2000, TBS1000, TBS2000 |
| Oscilloscopes | MSO/DPO | MSO4000, DPO4000, MSO5000, DPO5000 |
| Oscilloscopes | MDO | MDO3000, MDO4000 |
| Signal generators | AFG | AFG3000, AFG31000 |
| Signal generators | AWG | AWG5000, AWG7000 |
| Spectrum analyzers | RSA | RSA5000, RSA6000 |
Connect to a Tektronix Instrument
import pyvisa
rm = pyvisa.ResourceManager()
# USB
scope = rm.open_resource("USB0::0x0699::0x0363::C102912::INSTR")
# Ethernet
# scope = rm.open_resource("TCPIP0::192.168.1.100::inst0::INSTR")
# GPIB
# scope = rm.open_resource("GPIB0::1::INSTR")
scope.timeout = 10000 # 10 s (Tektronix instruments often need longer)
print(scope.query("*IDN?").strip())
scope.write("*RST")
scope.write("*CLS")Log measurements automatically
TofuPilot records test results from your PyVISA scripts, tracks waveform captures and pass/fail rates across production runs. Free to start.
Oscilloscope: Acquire a Waveform
Single-shot acquisition with binary transfer on a TDS/TBS/MSO/DPO scope.
import pyvisa
import numpy as np
import time
rm = pyvisa.ResourceManager()
scope = rm.open_resource("TCPIP0::192.168.1.100::inst0::INSTR")
scope.timeout = 10000
# Binary encoding for fast transfer
scope.write("DAT:ENC RIB")
scope.write("DAT:WID 2") # 16-bit samples
scope.write("DAT:SOU CH1")
# Horizontal and vertical
scope.write("HOR:SCA 1e-6") # 1 us/div
scope.write("CH1:SCA 0.5") # 500 mV/div
# Trigger
scope.write("TRIG:MAI:TYP EDGE")
scope.write("TRIG:MAI:EDGE:SOU CH1")
scope.write("TRIG:MAI:LEV 0.1")
scope.write("TRIG:MAI:EDGE:SLO RISE")
# Single acquisition
scope.write("ACQ:MODE SAM")
scope.write("ACQ:STOPA SEQ")
scope.write("ACQ:STATE RUN")
# Wait for trigger
start = time.time()
while time.time() - start < 30:
if scope.query("ACQ:STATE?").strip() == "0":
break
time.sleep(0.1)
# Read preamble for scaling
preamble = scope.query("WFMP?").split(",")
y_scale = float(preamble[13])
y_offset = float(preamble[14])
x_scale = float(preamble[9])
# Binary transfer
raw = scope.query_binary_values("CURV?", datatype="h")
voltages = (np.array(raw) - y_offset) * y_scale
time_axis = np.arange(len(raw)) * x_scale
print(f"Captured {len(raw)} points")
scope.close()
rm.close()Built-in Measurements
You can read automatic measurements without transferring the full waveform.
# After acquisition completes
scope.write("MEASU:MEAS1:TYP FREQ")
scope.write("MEASU:MEAS1:SOU CH1")
freq = float(scope.query("MEASU:MEAS1:VAL?"))
scope.write("MEASU:MEAS2:TYP AMP")
scope.write("MEASU:MEAS2:SOU CH1")
amp = float(scope.query("MEASU:MEAS2:VAL?"))
scope.write("MEASU:MEAS3:TYP RMS")
scope.write("MEASU:MEAS3:SOU CH1")
rms = float(scope.query("MEASU:MEAS3:VAL?"))
print(f"Frequency: {freq:.1f} Hz, Amplitude: {amp:.3f} V, RMS: {rms:.3f} V")Signal Generator: Output a Waveform
Generate a sine wave on an AFG3000/AFG31000.
import pyvisa
rm = pyvisa.ResourceManager()
afg = rm.open_resource("USB0::0x0699::0x0346::C012345::INSTR")
afg.timeout = 5000
afg.write("*RST")
# 1 kHz sine, 2 Vpp, 0.5 V offset
afg.write("SOUR1:FUNC:SHAP SIN")
afg.write("SOUR1:FREQ 1000")
afg.write("SOUR1:VOLT:AMPL 2.0")
afg.write("SOUR1:VOLT:OFFS 0.5")
afg.write("OUTP1:STAT ON")
print("Output enabled: 1 kHz sine, 2 Vpp")
# Burst mode: 10 cycles per trigger
afg.write("SOUR1:BURS:STAT ON")
afg.write("SOUR1:BURS:MODE TRIG")
afg.write("SOUR1:BURS:NCYC 10")
# Frequency sweep: 100 Hz to 10 kHz over 5 s
afg.write("SOUR1:BURS:STAT OFF")
afg.write("SOUR1:SWE:STAT ON")
afg.write("SOUR1:FREQ:START 100")
afg.write("SOUR1:FREQ:STOP 10000")
afg.write("SOUR1:SWE:TIME 5.0")
afg.close()
rm.close()Spectrum Analyzer: Capture a Trace
Single sweep on an RSA5000/RSA6000.
import pyvisa
import numpy as np
rm = pyvisa.ResourceManager()
rsa = rm.open_resource("TCPIP0::192.168.1.100::inst0::INSTR")
rsa.timeout = 30000
rsa.write("*RST")
rsa.write("INIT:CONT OFF") # Single sweep
# 1 GHz center, 100 MHz span, 1 MHz RBW
rsa.write("FREQ:CENT 1e9")
rsa.write("FREQ:SPAN 100e6")
rsa.write("BAND:RES 1e6")
rsa.write("DET:TRAC:FUNC AVER")
# Sweep
rsa.write("INIT:IMM")
rsa.query("*OPC?")
# Read trace
rsa.write("TRAC:DATA? TRACE1")
spectrum = rsa.read_binary_values("f")
center = float(rsa.query("FREQ:CENT?"))
span = float(rsa.query("FREQ:SPAN?"))
freqs = np.linspace(center - span / 2, center + span / 2, len(spectrum))
peak_idx = np.argmax(spectrum)
print(f"Peak: {freqs[peak_idx]/1e6:.1f} MHz at {spectrum[peak_idx]:.1f} dBm")
rsa.close()
rm.close()Tektronix Quirks
| Issue | Detail | Fix |
|---|---|---|
| Long timeouts | Tektronix scopes often need 10+ seconds for waveform transfers | Set instrument.timeout = 10000 or higher |
WFMP? preamble | Comma-separated, field positions vary by firmware version | Print the raw preamble once and verify indices 9, 13, 14 for your model |
SYST:ERR? unsupported | Older TDS models don't implement the SCPI error queue | Wrap error checks in try/except |
VERB OFF | Terse mode removes headers from query responses, needed for binary parsing | Always send VERB OFF before CURV? transfers |
| Acquisition state polling | ACQ:STATE? returns "1" while running, "0" when done | Poll with a timeout loop, don't rely on *OPC? alone for single-shot |
| USB vendor ID | Tektronix USB instruments use vendor ID 0x0699 | Use this to filter rm.list_resources() results |