PyVISA
PyVISADocs

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

CategorySeriesExamples
OscilloscopesTDS/TBSTDS2000, TBS1000, TBS2000
OscilloscopesMSO/DPOMSO4000, DPO4000, MSO5000, DPO5000
OscilloscopesMDOMDO3000, MDO4000
Signal generatorsAFGAFG3000, AFG31000
Signal generatorsAWGAWG5000, AWG7000
Spectrum analyzersRSARSA5000, 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

IssueDetailFix
Long timeoutsTektronix scopes often need 10+ seconds for waveform transfersSet instrument.timeout = 10000 or higher
WFMP? preambleComma-separated, field positions vary by firmware versionPrint the raw preamble once and verify indices 9, 13, 14 for your model
SYST:ERR? unsupportedOlder TDS models don't implement the SCPI error queueWrap error checks in try/except
VERB OFFTerse mode removes headers from query responses, needed for binary parsingAlways send VERB OFF before CURV? transfers
Acquisition state pollingACQ:STATE? returns "1" while running, "0" when donePoll with a timeout loop, don't rely on *OPC? alone for single-shot
USB vendor IDTektronix USB instruments use vendor ID 0x0699Use this to filter rm.list_resources() results