From 4449852215109c60eabfe952ce782a00021603df Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Tue, 3 Feb 2026 10:03:43 +0000 Subject: [PATCH 01/10] Add Tektronix AWG70000A enhancements - Add hold parameter for channel output condition during wait-for-trigger - Add force_jump parameter for sequence jumping - Add set_event_jump method for event-based sequence control --- .../instrument_drivers/tektronix/AWG70000A.py | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py index c321940bfdfc..542dca4e8e27 100644 --- a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py +++ b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py @@ -199,7 +199,21 @@ def __init__( vals=vals.Ints(0, 1), get_parser=int, ) - """Parameter state""" + """Channel State: (OFF: 0, ON: 1)""" + + self.hold: Parameter = self.add_parameter( + "hold", + label=f"Channel {channel} hold value", + get_cmd=f"OUTPut{channel}:WVALUE:ANALOG:STATE?", + set_cmd=f"OUTPut{channel}:WVALUE:ANALOG:STATE {{}}", + val_mapping={ + "FIRST": "FIRST", + "ZERO": "ZERO", + }, + ) + """ the output condition of a waveform of the specified + channel to hold while the instrument is in the waiting-for-trigger state. + ZERO = 0V, FIRST = first value of next sequence""" ################################################## # FGEN PARAMETERS @@ -587,6 +601,14 @@ def __init__( ) """Parameter all_output_off""" + self.force_jump: Parameter = self.add_parameter( + "force_jump", + label="Force Jump", + set_cmd="SOURCE1:JUMP:FORCE {}", + vals=vals.Ints(1, 16383), + ) + """Parameter force_jump""" + add_channel_list = self.num_channels > 2 # We deem 2 channels too few for a channel list if add_channel_list: @@ -619,6 +641,22 @@ def __init__( self.connect_message() + def set_event_jump( + self, sequence_name: str, current_step: int, next_step: int + ) -> None: + """ + Set event jump for a given step in the sequence + + Args: + sequence_name: The name of the sequence + current_step: The step number in the sequence (1-indexed) + next_step: The step number to jump to (1-indexed) + """ + + self.write( + f"SLISt:SEQuence:STEP{current_step}:EJUMp {sequence_name}, {next_step}" + ) + def force_triggerA(self) -> None: """ Force a trigger A event From b94464b5a2b8c72b8e413d19c9eb9e20a3d5b880 Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Tue, 3 Feb 2026 10:03:43 +0000 Subject: [PATCH 02/10] Add Tektronix DPO7200xx enhancements - Add TektronixDPOAcquisition module (mode, state, stop_after) - Add TektronixDPOCursor module (function, state, x1/x2/y1/y2 positions) - Add TektronixDPOMeasurementImmediate module - Enhance TektronixDPOTrigger (ready, state, level parameters) - Enhance TektronixDPOChannel (coupling parameter) - Add type annotations for submodules --- .../instrument_drivers/tektronix/DPO7200xx.py | 315 +++++++++++++++++- 1 file changed, 303 insertions(+), 12 deletions(-) diff --git a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py index f179ff5b71e5..5fdfac8c993f 100644 --- a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py +++ b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py @@ -95,7 +95,21 @@ def __init__( "delayed_trigger", TektronixDPOTrigger(self, "delayed_trigger", delayed_trigger=True), ) - """Instrument module delayed_trigger""" + """Instrument module acquisition""" + self.acquisition: TektronixDPOAcquisition = self.add_submodule( + "acquisition", TektronixDPOAcquisition(self, "acquisition") + ) + + """Instrument module cursor""" + self.cursor: TektronixDPOCursor = self.add_submodule( + "cursor", TektronixDPOCursor(self, "cursor") + ) + + """Instrument module measure immediate""" + self.measure_immediate: TektronixDPOMeasurementImmediate = self.add_submodule( + "measure_immediate", + TektronixDPOMeasurementImmediate(self, "measure_immediate"), + ) measurement_list = ChannelList(self, "measurement", TektronixDPOMeasurement) for measurement_number in range(1, self.number_of_measurements): @@ -458,6 +472,15 @@ def __init__( ) """Instrument module waveform""" + self.coupling: Parameter = self.add_parameter( + "coupling", + get_cmd=f"{self._identifier}:COUPling?", + set_cmd=f"{self._identifier}:COUPling {{}}", + vals=Enum("AC", "DC", "DCREJECT" "GND"), + get_parser=str, + ) + """Parameter coupling: 'AC', 'DC', 'DCREJECT', 'GND'""" + self.scale: Parameter = self.add_parameter( "scale", get_cmd=f"{self._identifier}:SCA?", @@ -465,7 +488,7 @@ def __init__( get_parser=float, unit="V/div", ) - """Parameter scale""" + """Parameter scale V/div""" self.offset: Parameter = self.add_parameter( "offset", @@ -474,16 +497,16 @@ def __init__( get_parser=float, unit="V", ) - """Parameter offset""" + """Parameter offset voltage""" self.position: Parameter = self.add_parameter( "position", get_cmd=f"{self._identifier}:POS?", set_cmd=f"{self._identifier}:POS {{}}", get_parser=float, - unit="V", + unit="div", ) - """Parameter position""" + """Parameter position [-8, 8] divisions""" self.termination: Parameter = self.add_parameter( "termination", @@ -677,6 +700,72 @@ def _set_scale(self, value: float) -> None: self.write(f"HORizontal:MODE:SCAle {value}") +class TektronixDPOAcquisition(InstrumentChannel): + """ + This submodule controls the acquisition mode of the + oscilloscope. It is used to set the acquisition mode + and the number of acquisitions. + """ + + def __init__( + self, + parent: Instrument, + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: + super().__init__(parent, name, **kwargs) + + self.mode: Parameter = self.add_parameter( + "mode", + get_cmd="ACQuire:MODe?", + set_cmd="ACQuire:MODe {}", + vals=Enum( + "sample", + "peakdetect", + "average", + "high_res", + "average", + "wfmdb", + "envelope", + ), + get_parser=str.lower, + ) + """Parameter mode""" + + self.state: Parameter = self.add_parameter( + "state", + get_cmd="ACQuire:STATE?", + set_cmd=f"ACQuire:STATE {{}}", + vals=Enum( + "ON", + "OFF", + "RUN", + "STOP", + ), + get_parser=str.lower, + ) + """This command starts or stops acquisitions. When state is set to ON or RUN, a + new acquisition will be started. If the last acquisition was a single acquisition + sequence, a new single sequence acquisition will be started. If the last acquisition + was continuous, a new continuous acquisition will be started. + + Args: + state: 'ON', 'OFF', 'RUN', or 'STOP' + """ + + self.stop_after: Parameter = self.add_parameter( + "stop_after", + get_cmd="ACQuire:STOPAfter?", + set_cmd=f"ACQuire:STOPAfter {{}}", + vals=Enum("SEQUENCE", "RUNSTOP"), + get_parser=str.lower, + ) + """This command sets or queries whether the instrument continually acquires + acquisitions or acquires a single sequence. Pressing SINGLE on the front + panel button is equivalent to sending these commands: ACQUIRE:STOPAFTER + SEQUENCE and ACQUIRE:STATE 1.""" + + class TektronixDPOTrigger(InstrumentChannel): """ Submodule for trigger setup. @@ -706,12 +795,48 @@ def __init__( super().__init__(parent, name, **kwargs) self._identifier = "B" if delayed_trigger else "A" - trigger_types = ["edge", "logic", "pulse"] + trigger_types = ["EDGE", "edge", "logic", "pulse"] if self._identifier == "A": trigger_types.extend( ["video", "i2c", "can", "spi", "communication", "serial", "rs232"] ) + self.ready: Parameter = self.add_parameter( + "ready", + get_cmd=f"TRIGger:{self._identifier}:READY?", + get_parser=str.lower, + ) + """Indicates whether the trigger system is ready to accept a trigger. + A value of 1 indicates that the trigger system is ready to accept a trigger. + A value of 0 indicates that the trigger system is not ready to accept a trigger. + """ + + self.state: Parameter = self.add_parameter( + "state", + get_cmd="TRIGger:STATe?", + get_parser=str.lower, + ) + """Gets the current Trigger state: + + ARMED indicates that the instrument is acquiring pretrigger information. + + AUTO indicates that the instrument is in the automatic mode and acquires data + even in the absence of a trigger. + + DPO indicates that the instrument is in DPO mode. + + PARTIAL indicates that the A trigger has occurred and the instrument is waiting + for the B trigger to occur. + + READY indicates that all pretrigger information is acquired and that the instrument + is ready to accept a trigger. + + SAVE indicates that the instrument is in save mode and is not acquiring data. + + TRIGGER indicates that the instrument triggered and is acquiring the post trigger + information. + """ + self.type: Parameter = self.add_parameter( "type", get_cmd=f"TRIGger:{self._identifier}:TYPE?", @@ -719,7 +844,7 @@ def __init__( vals=Enum(*trigger_types), get_parser=str.lower, ) - """Parameter type""" + """Trigger type""" edge_couplings = ["ac", "dc", "hfrej", "lfrej", "noiserej"] if self._identifier == "B": @@ -732,16 +857,16 @@ def __init__( vals=Enum(*edge_couplings), get_parser=str.lower, ) - """Parameter edge_coupling""" + """Trigger edge coupling: 'ac', 'dc', 'hfrej', 'lfrej', 'noiserej', 'atrigger'""" self.edge_slope: Parameter = self.add_parameter( "edge_slope", get_cmd=f"TRIGger:{self._identifier}:EDGE:SLOpe?", set_cmd=f"TRIGger:{self._identifier}:EDGE:SLOpe {{}}", - vals=Enum("rise", "fall", "either"), + vals=Enum("RISE", "rise", "FALL", "fall", "EITHER", "either"), get_parser=str.lower, ) - """Parameter edge_slope""" + """Trigger edge slope: 'rise', 'fall', or 'either'""" trigger_sources = [ f"CH{i}" for i in range(1, TektronixDPO7000xx.number_of_channels) @@ -752,16 +877,27 @@ def __init__( if self._identifier == "A": trigger_sources.append("line") + trigger_sources.append("AUX") + self.source: Parameter = self.add_parameter( "source", get_cmd=f"TRIGger:{self._identifier}:EDGE:SOUrce?", set_cmd=f"TRIGger:{self._identifier}:EDGE:SOUrce {{}}", vals=Enum(*trigger_sources), ) - """Parameter source""" + """Trigger source: 'CH1', 'CH2', ..., 'CH4', 'D0', 'D1', ..., 'D15', 'AUX', 'LINE'""" + + self.level: Parameter = self.add_parameter( + "level", + get_cmd=f"TRIGger:{self._identifier}:LEVel?", + set_cmd=f"TRIGger:{self._identifier}:LEVel {{}}", + get_parser=float, + unit="V", + ) + """Trigger level: The voltage level at which the trigger condition is met.""" def _trigger_type(self, value: str) -> None: - if value != "edge": + if value.lower() != "edge": raise NotImplementedError( "We currently only support the 'edge' trigger type" ) @@ -1002,3 +1138,158 @@ def __init__( def reset(self) -> None: self.write("MEASUrement:STATIstics:COUNt RESEt") + + +class TektronixDPOMeasurementImmediate(InstrumentChannel): + """ + The cursor submodule allows you to set and retrieve + information regarding the cursor type, state, and + positions. The cursor can be used to measure + voltage and time differences between two points on + the waveform display. + + Methods: + - function: Set or get the cursor type (e.g., horizontal bars, vertical bars, etc.) + - state: Set or get the cursor state (ON or OFF) + - x1: Set or get the x1 position of the cursor (in seconds) + - x2: Set or get the x2 position of the cursor (in seconds) + - y1: Set or get the y1 position of the cursor (in Volts) + - y2: Set or get the y2 position of the cursor (in Volts) + """ + + def __init__( + self, + parent: Instrument, + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: + super().__init__(parent, name, **kwargs) + + self.gating: Parameter = self.add_parameter( + "gating", + get_cmd="MEASUrement:GATing?", + set_cmd="MEASUrement:GATing {}", + vals=Enum("ON", "OFF", "ZOOM1", "ZOOM2", "ZOOM3", "ZOOM4", "CURSOR"), + ) + self.source1: Parameter = self.add_parameter( + "source1", + get_cmd="MEASUrement:IMMed:SOUrce1?", + set_cmd="MEASUrement:IMMed:SOUrce1 {}", + vals=Enum(*TektronixDPOWaveform.valid_identifiers), + ) + + self.source2: Parameter = self.add_parameter( + "source2", + get_cmd="MEASUrement:IMMed:SOUrce2?", + set_cmd="MEASUrement:IMMed:SOUrce2 {}", + vals=Enum(*TektronixDPOWaveform.valid_identifiers), + ) + + self.type: Parameter = self.add_parameter( + "type", + get_cmd="MEASUrement:IMMed:TYPE?", + set_cmd="MEASUrement:IMMed:TYPE {}", + vals=Enum( + "MEAN", + ), + get_parser=str.lower, + ) + """Cursor Type [OFF, HBARS, VBARS, SCREEN, WAVEFORM]""" + + self.units: Parameter = self.add_parameter( + "units", + get_cmd="MEASUrement:IMMed:UNITS?", + get_parser=strip_quotes, + ) + + self.value: Parameter = self.add_parameter( + "value", + get_cmd="MEASUrement:IMMed:VALue?", + get_parser=float, + ) + + +class TektronixDPOCursor(InstrumentChannel): + """ + The cursor submodule allows you to set and retrieve + information regarding the cursor type, state, and + positions. The cursor can be used to measure + voltage and time differences between two points on + the waveform display. + + Methods: + - function: Set or get the cursor type (e.g., horizontal bars, vertical bars, etc.) + - state: Set or get the cursor state (ON or OFF) + - x1: Set or get the x1 position of the cursor (in seconds) + - x2: Set or get the x2 position of the cursor (in seconds) + - y1: Set or get the y1 position of the cursor (in Volts) + - y2: Set or get the y2 position of the cursor (in Volts) + """ + + def __init__( + self, + parent: Instrument, + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: + super().__init__(parent, name, **kwargs) + + self.function: Parameter = self.add_parameter( + "function", + get_cmd="CURSOR:FUNCtion?", + set_cmd="CURSOR:FUNCtion {}", + vals=Enum( + "OFF", + "HBARS", + "VBARS", + "SCREEN", + "WAVEFORM", + ), + get_parser=str.lower, + ) + """Cursor Type [OFF, HBARS, VBARS, SCREEN, WAVEFORM]""" + + self.state: Parameter = self.add_parameter( + "state", + get_cmd="CURSOR:STATE?", + set_cmd="CURSOR:STATE {}", + vals=Enum("ON", "OFF"), + get_parser=str.lower, + ) + """Cursor state [ON, OFF]""" + + self.x1: Parameter = self.add_parameter( + "x1", + get_cmd="CURSOR:VBARS:POSITION1?", + set_cmd="CURSOR:VBARS:POSITION1 {}", + get_parser=float, + unit="s", + ) + """Cursor x1 position in seconds""" + + self.x2: Parameter = self.add_parameter( + "x2", + get_cmd="CURSOR:VBARS:POSITION2?", + set_cmd="CURSOR:VBARS:POSITION2 {}", + get_parser=float, + unit="s", + ) + """Cursor x2 position in seconds""" + + self.y1: Parameter = self.add_parameter( + "y1", + get_cmd="CURSOR:HBARS:POSITION1?", + set_cmd="CURSOR:HBARS:POSITION1 {}", + get_parser=float, + unit="V", + ) + """Cursor y1 position in Volts""" + + self.y2: Parameter = self.add_parameter( + "y2", + get_cmd="CURSOR:HBARS:POSITION2?", + set_cmd="CURSOR:HBARS:POSITION2 {}", + get_parser=float, + unit="V", + ) + """Cursor y2 position in Volts""" From f9e8963e1ea5ac3b744f54b4bc8f997570ae10ba Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Tue, 3 Feb 2026 10:24:15 +0000 Subject: [PATCH 03/10] Add newsfragment for PR #7838 --- docs/changes/newsfragments/7838.improved_driver | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/changes/newsfragments/7838.improved_driver diff --git a/docs/changes/newsfragments/7838.improved_driver b/docs/changes/newsfragments/7838.improved_driver new file mode 100644 index 000000000000..28e2e756c33c --- /dev/null +++ b/docs/changes/newsfragments/7838.improved_driver @@ -0,0 +1 @@ +Add TektronixDPOAcquisition, TektronixDPOCursor, and TektronixDPOMeasurementImmediate modules to the Tektronix DPO7200xx driver. Enhanced TektronixDPOTrigger with ready, state, and level parameters. Added coupling parameter to TektronixDPOChannel. From 3c634dd9597df5d72544a82aaf353c64bebd6808 Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Tue, 3 Feb 2026 10:25:36 +0000 Subject: [PATCH 04/10] Add newsfragment for PR #7837 --- docs/changes/newsfragments/7837.improved_driver | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/changes/newsfragments/7837.improved_driver diff --git a/docs/changes/newsfragments/7837.improved_driver b/docs/changes/newsfragments/7837.improved_driver new file mode 100644 index 000000000000..02fbe24d6a31 --- /dev/null +++ b/docs/changes/newsfragments/7837.improved_driver @@ -0,0 +1 @@ +Add hold parameter, force_jump parameter, and set_event_jump method to the Tektronix AWG70000A driver for improved sequence control. From 7e71f904996bd3f9ed6b3c4db7dbc1272c2892b0 Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Mon, 9 Feb 2026 10:57:30 +0000 Subject: [PATCH 05/10] Added new parameter Signature --- src/qcodes/instrument_drivers/tektronix/AWG70000A.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py index 542dca4e8e27..72e08e6523af 100644 --- a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py +++ b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py @@ -8,7 +8,7 @@ import xml.etree.ElementTree as ET import zipfile as zf from functools import partial -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Self import numpy as np import numpy.typing as npt @@ -206,10 +206,7 @@ def __init__( label=f"Channel {channel} hold value", get_cmd=f"OUTPut{channel}:WVALUE:ANALOG:STATE?", set_cmd=f"OUTPut{channel}:WVALUE:ANALOG:STATE {{}}", - val_mapping={ - "FIRST": "FIRST", - "ZERO": "ZERO", - }, + vals=vals.Enum("FIRST", "ZERO"), ) """ the output condition of a waveform of the specified channel to hold while the instrument is in the waiting-for-trigger state. @@ -601,7 +598,7 @@ def __init__( ) """Parameter all_output_off""" - self.force_jump: Parameter = self.add_parameter( + self.force_jump: Parameter[int, Self] = self.add_parameter( "force_jump", label="Force Jump", set_cmd="SOURCE1:JUMP:FORCE {}", @@ -651,6 +648,7 @@ def set_event_jump( sequence_name: The name of the sequence current_step: The step number in the sequence (1-indexed) next_step: The step number to jump to (1-indexed) + """ self.write( From 77b9ae6db654c77b43d465368a9abbd28666c415 Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Mon, 9 Feb 2026 11:31:14 +0000 Subject: [PATCH 06/10] PR fixes --- .../instrument_drivers/tektronix/DPO7200xx.py | 80 ++++++++++++------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py index 5fdfac8c993f..0972f94f34b8 100644 --- a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py +++ b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py @@ -7,7 +7,7 @@ import textwrap import time from functools import partial -from typing import TYPE_CHECKING, Any, ClassVar, Generic +from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, Self import numpy as np import numpy.typing as npt @@ -27,7 +27,7 @@ create_on_off_val_mapping, ) from qcodes.parameters.parameter_base import ParameterDataTypeVar -from qcodes.validators import Arrays, Enum +from qcodes.validators import Arrays, Enum, Numbers if TYPE_CHECKING: from collections.abc import Callable @@ -95,22 +95,20 @@ def __init__( "delayed_trigger", TektronixDPOTrigger(self, "delayed_trigger", delayed_trigger=True), ) - """Instrument module acquisition""" + """Instrument module delayed_trigger""" self.acquisition: TektronixDPOAcquisition = self.add_submodule( "acquisition", TektronixDPOAcquisition(self, "acquisition") ) - - """Instrument module cursor""" + """Instrument module acquisition""" self.cursor: TektronixDPOCursor = self.add_submodule( "cursor", TektronixDPOCursor(self, "cursor") ) - - """Instrument module measure immediate""" + """Instrument module cursor""" self.measure_immediate: TektronixDPOMeasurementImmediate = self.add_submodule( "measure_immediate", TektronixDPOMeasurementImmediate(self, "measure_immediate"), ) - + """Instrument module measure immediate""" measurement_list = ChannelList(self, "measurement", TektronixDPOMeasurement) for measurement_number in range(1, self.number_of_measurements): measurement_name = f"measurement{measurement_number}" @@ -472,12 +470,14 @@ def __init__( ) """Instrument module waveform""" - self.coupling: Parameter = self.add_parameter( - "coupling", - get_cmd=f"{self._identifier}:COUPling?", - set_cmd=f"{self._identifier}:COUPling {{}}", - vals=Enum("AC", "DC", "DCREJECT" "GND"), - get_parser=str, + self.coupling: Parameter[Literal["AC", "DC", "DCREJECT", "GND"], Self] = ( + self.add_parameter( + "coupling", + get_cmd=f"{self._identifier}:COUPling?", + set_cmd=f"{self._identifier}:COUPling {{}}", + vals=Enum("AC", "DC", "DCREJECT", "GND"), + get_parser=str, + ) ) """Parameter coupling: 'AC', 'DC', 'DCREJECT', 'GND'""" @@ -504,6 +504,7 @@ def __init__( get_cmd=f"{self._identifier}:POS?", set_cmd=f"{self._identifier}:POS {{}}", get_parser=float, + vals=Numbers(-8, 8), unit="div", ) """Parameter position [-8, 8] divisions""" @@ -715,7 +716,10 @@ def __init__( ) -> None: super().__init__(parent, name, **kwargs) - self.mode: Parameter = self.add_parameter( + self.mode: Parameter[ + Literal["sample", "peakdetect", "average", "high_res", "wfmdb", "envelope"], + Self, + ] = self.add_parameter( "mode", get_cmd="ACQuire:MODe?", set_cmd="ACQuire:MODe {}", @@ -724,33 +728,49 @@ def __init__( "peakdetect", "average", "high_res", - "average", "wfmdb", "envelope", ), get_parser=str.lower, ) - """Parameter mode""" + """Sample mode. This command sets or queries the acquisition mode. The acquisition mode + determines how the instrument acquires and processes data. The available acquisition modes are: + - Sample: The instrument acquires data at the specified sample rate and record length. + This is the default acquisition mode. + - Peak Detect: The instrument captures the maximum and minimum values for each sample interval. This mode is useful + for capturing narrow pulses or glitches that may be missed in sample mode. + - Average: The instrument acquires multiple waveforms and averages them together to reduce noise. + The number of waveforms to average can be set with the 'averages' parameter. + - High Res: The instrument acquires data at a higher resolution by using oversampling and digital filtering. + This mode is useful for capturing small signal details. + - WfmDB: Statistical database aquisition mode. The instrument acquires data and stores it in a statistical + database for later analysis. + - Envelope: The instrument captures the maximum and minimum values for each sample interval over multiple acquisitions + """ - self.state: Parameter = self.add_parameter( - "state", - get_cmd="ACQuire:STATE?", - set_cmd=f"ACQuire:STATE {{}}", - vals=Enum( - "ON", - "OFF", - "RUN", - "STOP", - ), - get_parser=str.lower, + self.state: Parameter[Literal["ON", "OFF", "RUN", "STOP"], Self] = ( + self.add_parameter( + "state", + get_cmd="ACQuire:STATE?", + set_cmd=f"ACQuire:STATE {{}}", + vals=Enum( + "ON", + "OFF", + "RUN", + "STOP", + ), + get_parser=str.upper, + ) ) - """This command starts or stops acquisitions. When state is set to ON or RUN, a + """ + This command starts or stops acquisitions. When state is set to ON or RUN, a new acquisition will be started. If the last acquisition was a single acquisition sequence, a new single sequence acquisition will be started. If the last acquisition was continuous, a new continuous acquisition will be started. Args: state: 'ON', 'OFF', 'RUN', or 'STOP' + """ self.stop_after: Parameter = self.add_parameter( @@ -758,7 +778,7 @@ def __init__( get_cmd="ACQuire:STOPAfter?", set_cmd=f"ACQuire:STOPAfter {{}}", vals=Enum("SEQUENCE", "RUNSTOP"), - get_parser=str.lower, + get_parser=str.upper, ) """This command sets or queries whether the instrument continually acquires acquisitions or acquires a single sequence. Pressing SINGLE on the front From ec754857e67cca5e9fd90b8d1985165985816ea2 Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Mon, 9 Feb 2026 14:27:27 +0000 Subject: [PATCH 07/10] Executed precommit checks --- src/qcodes/instrument_drivers/tektronix/DPO7200xx.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py index 0972f94f34b8..465e2d10fef9 100644 --- a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py +++ b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py @@ -752,7 +752,7 @@ def __init__( self.add_parameter( "state", get_cmd="ACQuire:STATE?", - set_cmd=f"ACQuire:STATE {{}}", + set_cmd="ACQuire:STATE {}", vals=Enum( "ON", "OFF", @@ -767,7 +767,7 @@ def __init__( new acquisition will be started. If the last acquisition was a single acquisition sequence, a new single sequence acquisition will be started. If the last acquisition was continuous, a new continuous acquisition will be started. - + Args: state: 'ON', 'OFF', 'RUN', or 'STOP' @@ -776,7 +776,7 @@ def __init__( self.stop_after: Parameter = self.add_parameter( "stop_after", get_cmd="ACQuire:STOPAfter?", - set_cmd=f"ACQuire:STOPAfter {{}}", + set_cmd="ACQuire:STOPAfter {}", vals=Enum("SEQUENCE", "RUNSTOP"), get_parser=str.upper, ) @@ -1175,6 +1175,7 @@ class TektronixDPOMeasurementImmediate(InstrumentChannel): - x2: Set or get the x2 position of the cursor (in seconds) - y1: Set or get the y1 position of the cursor (in Volts) - y2: Set or get the y2 position of the cursor (in Volts) + """ def __init__( @@ -1244,6 +1245,7 @@ class TektronixDPOCursor(InstrumentChannel): - x2: Set or get the x2 position of the cursor (in seconds) - y1: Set or get the y1 position of the cursor (in Volts) - y2: Set or get the y2 position of the cursor (in Volts) + """ def __init__( From eddff5bb782e072b26a2cc68bf41f7759bcb575c Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Wed, 11 Feb 2026 15:42:23 +0000 Subject: [PATCH 08/10] Addressed PR comments --- .../instrument_drivers/tektronix/DPO7200xx.py | 103 +++++++++++++++--- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py index 465e2d10fef9..fd4c162c3e89 100644 --- a/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py +++ b/src/qcodes/instrument_drivers/tektronix/DPO7200xx.py @@ -768,8 +768,7 @@ def __init__( sequence, a new single sequence acquisition will be started. If the last acquisition was continuous, a new continuous acquisition will be started. - Args: - state: 'ON', 'OFF', 'RUN', or 'STOP' + State can be 'ON', 'OFF', 'RUN', or 'STOP'. """ @@ -815,7 +814,7 @@ def __init__( super().__init__(parent, name, **kwargs) self._identifier = "B" if delayed_trigger else "A" - trigger_types = ["EDGE", "edge", "logic", "pulse"] + trigger_types = ["edge", "logic", "pulse"] if self._identifier == "A": trigger_types.extend( ["video", "i2c", "can", "spi", "communication", "serial", "rs232"] @@ -877,7 +876,10 @@ def __init__( vals=Enum(*edge_couplings), get_parser=str.lower, ) - """Trigger edge coupling: 'ac', 'dc', 'hfrej', 'lfrej', 'noiserej', 'atrigger'""" + """Trigger edge coupling for A and B triggers: + Trigger A: 'ac', 'dc', 'hfrej', 'lfrej', 'noiserej' + Trigger B: 'ac', 'dc', 'hfrej', 'lfrej', 'noiserej', 'atrigger' + """ self.edge_slope: Parameter = self.add_parameter( "edge_slope", @@ -1162,19 +1164,10 @@ def reset(self) -> None: class TektronixDPOMeasurementImmediate(InstrumentChannel): """ - The cursor submodule allows you to set and retrieve - information regarding the cursor type, state, and - positions. The cursor can be used to measure - voltage and time differences between two points on - the waveform display. - - Methods: - - function: Set or get the cursor type (e.g., horizontal bars, vertical bars, etc.) - - state: Set or get the cursor state (ON or OFF) - - x1: Set or get the x1 position of the cursor (in seconds) - - x2: Set or get the x2 position of the cursor (in seconds) - - y1: Set or get the y1 position of the cursor (in Volts) - - y2: Set or get the y2 position of the cursor (in Volts) + The measurement commands let you specify an additional measurement, IMMed. The immediate measurement + has no front panel equivalent. Immediate measurements are never displayed. + Because they are computed only when needed, immediate measurements slow the + waveform update rate less than displayed measurements. """ @@ -1192,12 +1185,22 @@ def __init__( set_cmd="MEASUrement:GATing {}", vals=Enum("ON", "OFF", "ZOOM1", "ZOOM2", "ZOOM3", "ZOOM4", "CURSOR"), ) + """Gating for the immediate measurement. Gating allows you to specify a subset of the waveform + to be measured. When gating is on, the measurement is performed only on the portion of the waveform + defined by the gate. The gate can be defined by zooming in on a portion of + the waveform and selecting one of the zoom gates (ZOOM1, ZOOM2, ZOOM3, ZOOM4), or by using the + cursor gate (CURSOR), which uses the horizontal positions of the cursors to define the gate. + """ + self.source1: Parameter = self.add_parameter( "source1", get_cmd="MEASUrement:IMMed:SOUrce1?", set_cmd="MEASUrement:IMMed:SOUrce1 {}", vals=Enum(*TektronixDPOWaveform.valid_identifiers), ) + """Source 1 for the immediate measurement: + CH1, CH2, CH3, CH4, MATH1, MATH2, MATH3, MATH4, + REF1, REF2, REF3, REF4, HISTogram""" self.source2: Parameter = self.add_parameter( "source2", @@ -1205,29 +1208,93 @@ def __init__( set_cmd="MEASUrement:IMMed:SOUrce2 {}", vals=Enum(*TektronixDPOWaveform.valid_identifiers), ) + """Source 2 for the immediate measurement. Source2 measurements only apply + to phase and delay measurement types, which require both a target (Source1) + and reference (Source2) source. + CH1, CH2, CH3, CH4, MATH1, MATH2, MATH3, MATH4, + REF1, REF2, REF3, REF4 + """ self.type: Parameter = self.add_parameter( "type", get_cmd="MEASUrement:IMMed:TYPE?", set_cmd="MEASUrement:IMMed:TYPE {}", vals=Enum( + "ACRMS", + "AMPlitude", + "AREa", + "BURst", + "CARea", + "CMEan", + "CRMs", + "DELay", + "DISTDUty", + "EXTINCTDB", + "EXTINCTPCT", + "EXTINCTRATIO", + "EYEHeight", + "EYEWIdth", + "FALL", + "FREQuency", + "HIGH", + "HITs", + "LOW", + "MAXimum", "MEAN", + "MEDian", + "MINImum", + "NCROss", + "NDUty", + "NOVershoot", + "NWIdth", + "PBASe", + "PCROss", + "PCTCROss", + "PDUty", + "PEAKHits", + "PERIod", + "PHAse", + "PK2Pk", + "PKPKJitter", + "PKPKNoise", + "POVershoot", + "PTOP", + "PWIdth", + "QFACtor", + "RISe", + "RMS", + "RMSJitter", + "RMSNoise", + "SIGMA1", + "SIGMA2", + "SIGMA3", + "SIXSigmajit", + "SNRatio", + "STDdev", + "UNDEFINED", + "WAVEFORMS", ), get_parser=str.lower, ) - """Cursor Type [OFF, HBARS, VBARS, SCREEN, WAVEFORM]""" + """ + Immediate measurement type + Please see page 2-547 of the programmers manual for a detailed description of these arguments. + https://download.tek.com/manual/MSO-DPO5000-B-DPO7000-C-DPO70000-B-C-D-DX-DSA70000-B-C-D-and-MSO70000-C-DX-_2.pdf + """ self.units: Parameter = self.add_parameter( "units", get_cmd="MEASUrement:IMMed:UNITS?", get_parser=strip_quotes, ) + """Units of the immediate measurement""" self.value: Parameter = self.add_parameter( "value", get_cmd="MEASUrement:IMMed:VALue?", get_parser=float, ) + """The value of the immediate measurement""" class TektronixDPOCursor(InstrumentChannel): From 93dc7ae6861b42edce35cc90e0857ed3fd08671b Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Wed, 11 Feb 2026 16:17:27 +0000 Subject: [PATCH 09/10] PR fixes --- src/qcodes/instrument_drivers/tektronix/AWG70000A.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py index 72e08e6523af..c52c2905ac1f 100644 --- a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py +++ b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py @@ -8,7 +8,7 @@ import xml.etree.ElementTree as ET import zipfile as zf from functools import partial -from typing import TYPE_CHECKING, Any, Self +from typing import TYPE_CHECKING, Any, Literal, Self import numpy as np import numpy.typing as npt @@ -201,7 +201,7 @@ def __init__( ) """Channel State: (OFF: 0, ON: 1)""" - self.hold: Parameter = self.add_parameter( + self.hold: Parameter[Literal["FIRST", "ZERO"], Self] = self.add_parameter( "hold", label=f"Channel {channel} hold value", get_cmd=f"OUTPut{channel}:WVALUE:ANALOG:STATE?", From e1caa502fba77bbfa4f3327f5c82c962fe05c5a2 Mon Sep 17 00:00:00 2001 From: Benn Thomsen Date: Wed, 11 Feb 2026 16:34:48 +0000 Subject: [PATCH 10/10] Added data type to the Parameters for those that I have added --- src/qcodes/instrument_drivers/tektronix/AWG70000A.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py index c52c2905ac1f..28bcff15c0d6 100644 --- a/src/qcodes/instrument_drivers/tektronix/AWG70000A.py +++ b/src/qcodes/instrument_drivers/tektronix/AWG70000A.py @@ -191,7 +191,7 @@ def __init__( if channel not in list(range(1, num_channels + 1)): raise ValueError("Illegal channel value.") - self.state: Parameter = self.add_parameter( + self.state: Parameter[int, Self] = self.add_parameter( "state", label=f"Channel {channel} state", get_cmd=f"OUTPut{channel}:STATe?",