Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dd0ba89
Make use of generic parameters in ZNB driver
jenshnielsen Dec 16, 2025
18b93b2
Make KeysightB1517A use generic parameter args
jenshnielsen Dec 18, 2025
f599668
Improve type checking of message_builder
jenshnielsen Dec 18, 2025
cf18cba
Make use of generic parameters in KeysightN9030B driver
jenshnielsen Dec 18, 2025
98b12ee
Make use of generic parameters in DPO7200xx driver
jenshnielsen Dec 18, 2025
909428f
Make use of generic parameters in Yokogawa driver
jenshnielsen Dec 18, 2025
985c011
Better types in dynacool driver
jenshnielsen Dec 18, 2025
2a1f8fd
Make use of generic parameters in Alazar driver
jenshnielsen Dec 18, 2025
dd70d8e
Add Generic arguments to Keysight Keysight344xx driver
jenshnielsen Dec 18, 2025
d9ef3f0
Fix B1500 root instrument generic issues
jenshnielsen Dec 18, 2025
133636c
Fix Generic issues in B1520 driver
jenshnielsen Dec 18, 2025
74c6ae1
Fix Generic issues in B1520 sampling meas
jenshnielsen Dec 18, 2025
3236fdc
Messagebuilder fix Liskov
jenshnielsen Dec 18, 2025
5f103f8
Use generic in KtM960x
jenshnielsen Dec 19, 2025
3219fd3
Fix type checking of tests with improved driver types
jenshnielsen Dec 20, 2025
b0b0e64
Address feedback in yokogawa
jenshnielsen Feb 2, 2026
e0eac0c
Address feedback in test
jenshnielsen Feb 2, 2026
38f5650
Fix call to none existing parameter
jenshnielsen Feb 2, 2026
8e074d8
B1500 fix gettting instrument from by_channel
jenshnielsen Feb 2, 2026
3d5dc0e
Fix timeout handling
jenshnielsen Feb 2, 2026
836f8c1
Adapt M5xxx driver to avoid asseert and cast
jenshnielsen Feb 2, 2026
19d634f
Address code review
jenshnielsen Feb 2, 2026
4aba9fd
Add changes for 7822
jenshnielsen Feb 2, 2026
7710775
Correct instrument in driver
jenshnielsen Feb 2, 2026
0cdc012
Cleanup some imports and usage in B1500 driver
jenshnielsen Feb 2, 2026
aab9649
Replace root_instrument with parent when possible for more type safety
jenshnielsen Feb 2, 2026
008b251
Apply suggestions from code review
jenshnielsen Feb 2, 2026
50f8ff2
Update changelog
jenshnielsen Feb 2, 2026
a985ec0
Add missing imports
jenshnielsen Feb 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/changes/newsfragments/7822.improved_driver
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Several instrument drivers now make better use of the newly added generic datatype and instrument arguments to ParameterBase and subclasses.

- AlazarTech ATS: ``TraceParameter`` now uses generic type parameters.
- CopperMountain M5xxx: ``FrequencySweepMagPhase``, ``PointMagPhase``, and ``PointIQ`` now use generic type parameters. Removed unnecessary ``assert isinstance`` calls. Fixed timeout handling when timeout is ``None``.
- Keysight 344xxA: ``Keysight344xxATrigger``, ``Keysight344xxASample``, ``Keysight344xxADisplay`` and ``TimeTrace`` now use generic type parameters. Refactored ``_acquire_time_trace`` to use explicit context managers instead of a list of parameter settings. .. gitleaks:allow
- Keysight B1500: Added ``MeasurementModeDict`` TypedDict. ``IVSweepMeasurement`` corrected instrument type and now uses generic type parameters. ``KeysightB1500Module`` now uses generic type parameters.
- Keysight B1517A: ``KeysightB1500IVSweeper``, ``_ParameterWithStatus``, ``_SpotMeasurementVoltageParameter``, and ``_SpotMeasurementCurrentParameter`` now use generic type parameters. Removed unnecessary ``cast`` calls. .. gitleaks:allow
- Keysight B1520A: ``KeysightB1500CVSweeper`` and ``KeysightB1500CVSweepMeasurement`` now use generic type parameters. Added ``root_instrument`` property to ``KeysightB1500CVSweepMeasurement``. .. gitleaks:allow
- Keysight B1500 sampling measurement: ``SamplingMeasurement`` now uses generic type parameters and has a ``root_instrument`` property. Fixed timeout handling when timeout is ``None``.
- Keysight B1500 message builder: Improved type annotations using ``ParamSpec`` and ``TypeVar``. Made ``CommandList`` generic.
- Keysight KtM960x: ``Measure`` parameter now uses generic type parameters.
- Keysight N9030B: ``FrequencyAxis``, ``Trace``, ``KeysightN9030BSpectrumAnalyzerMode``, and ``KeysightN9030BPhaseNoiseMode`` now use generic type parameters. Replaced ``assert`` statements with proper error handling.
- QuantumDesign DynaCool: Improved type annotation for ``_pick_one`` method. Removed unnecessary ``cast`` call.
- Rohde & Schwarz ZNB: ``FixedFrequencyTraceIQ``, ``FixedFrequencyPointIQ``, ``FixedFrequencyPointMagPhase``, ``FrequencySweepMagPhase``, ``FrequencySweepDBPhase``, and ``FrequencySweep`` now use generic type parameters. Removed unnecessary ``assert isinstance`` calls.
- Tektronix DPO7200xx: ``TektronixDPOMeasurementParameter`` now uses generic type parameters. Removed unnecessary ``cast`` call.
- Yokogawa GS200: ``source_mode`` and ``current_limit`` parameters now use generic type parameters. Replaced if-else statements with match statements for source mode handling.
6 changes: 4 additions & 2 deletions src/qcodes/instrument/visa.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging
import warnings
from importlib.resources import as_file, files
from typing import TYPE_CHECKING, Any, Literal, TypedDict
from typing import TYPE_CHECKING, Any, Literal, Self, TypedDict
from weakref import finalize

import pyvisa
Expand All @@ -26,6 +26,8 @@

from typing_extensions import Unpack

from qcodes.parameters.parameter import Parameter

VISA_LOGGER = ".".join((InstrumentBase.__module__, "com", "visa"))

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -153,7 +155,7 @@ def __init__(
super().__init__(name, **kwargs)
self.visa_log = get_instrument_logger(self, VISA_LOGGER)

self.add_parameter(
self.timeout: Parameter[float | None, Self] = self.add_parameter(
"timeout",
get_cmd=self._get_visa_timeout,
set_cmd=self._set_visa_timeout,
Expand Down
13 changes: 8 additions & 5 deletions src/qcodes/instrument_drivers/AlazarTech/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
:mod:`.AlazarTech.helpers` module).
"""

from typing import TYPE_CHECKING, Any, cast
from typing import TYPE_CHECKING, Any, Generic

from qcodes.parameters import Parameter, ParamRawDataType
from qcodes.parameters.parameter_base import ParameterDataTypeVar

if TYPE_CHECKING:
from .ATS import AlazarTechATS
# ruff does not detect that this is used as a generic parameter below
from .ATS import AlazarTechATS # noqa: F401


class TraceParameter(Parameter):
class TraceParameter(
Parameter[ParameterDataTypeVar, "AlazarTechATS"], Generic[ParameterDataTypeVar]
):
"""
A parameter that keeps track of if its value has been synced to
the ``Instrument``. To achieve that, this parameter sets
Expand All @@ -38,6 +42,5 @@ def synced_to_card(self) -> bool:
return self._synced_to_card

def set_raw(self, value: ParamRawDataType) -> None:
instrument = cast("AlazarTechATS", self.instrument)
instrument._parameters_synced = False
self.instrument._parameters_synced = False
self._synced_to_card = False
42 changes: 26 additions & 16 deletions src/qcodes/instrument_drivers/CopperMountain/_M5xxx.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import TYPE_CHECKING, Any, Literal

import numpy as np
from numpy.typing import NDArray

from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs
from qcodes.parameters import (
Expand All @@ -13,7 +14,6 @@
from qcodes.validators import Bool, Enum, Ints, Numbers

if TYPE_CHECKING:
from numpy.typing import NDArray
from typing_extensions import Unpack


Expand Down Expand Up @@ -544,8 +544,10 @@ def get_s_parameters(
s22 magnitude [dB], s22 phase [rad]

"""
timeout = self.timeout()
current_timeout = timeout if timeout is not None else float("inf")

with self.timeout.set_to(max(self.timeout(), expected_measurement_duration)):
with self.timeout.set_to(max(current_timeout, expected_measurement_duration)):
self.write("CALC1:PAR:COUN 4") # 4 trace
self.write("CALC1:PAR1:DEF S11") # Choose S11 for trace 1
self.write("CALC1:PAR2:DEF S12") # Choose S12 for trace 2
Expand Down Expand Up @@ -622,7 +624,9 @@ def _db(data: "NDArray") -> "NDArray":
return 20.0 * np.log10(np.abs(data))


class FrequencySweepMagPhase(MultiParameter):
class FrequencySweepMagPhase(
MultiParameter[tuple[NDArray, NDArray], CopperMountainM5xxx]
):
"""
Sweep that returns magnitude and phase.
"""
Expand Down Expand Up @@ -696,17 +700,19 @@ def set_sweep(self, start: float, stop: float, number_of_points: int) -> None:
self.setpoints = ((f,), (f,))
self.shapes = ((number_of_points,), (number_of_points,))

def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
def get_raw(self) -> tuple[NDArray, NDArray]:
"""Gets data from instrument

Returns:
Tuple[ParamRawDataType, ...]: magnitude, phase

"""
assert isinstance(self.instrument, CopperMountainM5xxx)

timeout = self.instrument.timeout()
current_timeout = timeout if timeout is not None else float("inf")

with self.instrument.timeout.set_to(
max(self.instrument.timeout(), self.expected_measurement_duration)
max(current_timeout, self.expected_measurement_duration)
):
self.instrument.write("CALC1:PAR:COUN 1") # 1 trace
self.instrument.write(f"CALC1:PAR1:DEF {self.name}")
Expand All @@ -728,7 +734,9 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
return self.instrument._db(sxx), np.angle(sxx)


class PointMagPhase(MultiParameter):
class PointMagPhase(
MultiParameter[tuple[np.floating, np.floating], CopperMountainM5xxx]
):
"""
Returns the average Sxx of a frequency sweep.
Work around for a CW mode where only one point is read.
Expand All @@ -738,7 +746,7 @@ class PointMagPhase(MultiParameter):
def __init__(
self,
name: str,
instrument: VisaInstrument,
instrument: CopperMountainM5xxx,
expected_measurement_duration: float = 600,
**kwargs: Any,
) -> None:
Expand Down Expand Up @@ -785,8 +793,6 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
Tuple[ParamRawDataType, ...]: magnitude, phase

"""

assert isinstance(self.instrument, CopperMountainM5xxx)
# check that number_of_points, start and stop fullfill requirements if point_check_sweep_first is True.
if self.instrument.point_check_sweep_first():
if self.instrument.number_of_points() != 2:
Expand All @@ -799,8 +805,11 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
"Please adjust start or stop."
)

timeout = self.instrument.timeout()
current_timeout = timeout if timeout is not None else float("inf")

with self.instrument.timeout.set_to(
max(self.instrument.timeout(), self.expected_measurement_duration)
max(current_timeout, self.expected_measurement_duration)
):
self.instrument.write("CALC1:PAR:COUN 1") # 1 trace
self.instrument.write(f"CALC1:PAR1:DEF {self.name[-3:]}")
Expand All @@ -825,7 +834,7 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
return 20 * np.log10(abs(sxx_mean)), (np.angle(sxx_mean))


class PointIQ(MultiParameter):
class PointIQ(MultiParameter[tuple[np.floating, np.floating], CopperMountainM5xxx]):
"""
Returns the average Sxx of a frequency sweep, in terms of I and Q.
Work around for a CW mode where only one point is read.
Expand All @@ -835,7 +844,7 @@ class PointIQ(MultiParameter):
def __init__(
self,
name: str,
instrument: VisaInstrument,
instrument: CopperMountainM5xxx,
expected_measurement_duration: float = 600,
**kwargs: Any,
) -> None:
Expand Down Expand Up @@ -882,8 +891,6 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
Tuple[ParamRawDataType, ...]: I, Q

"""

assert isinstance(self.instrument, CopperMountainM5xxx)
# check that number_of_points, start and stop fullfill requirements if point_check_sweep_first is True.
if self.instrument.point_check_sweep_first():
if self.instrument.number_of_points() != 2:
Expand All @@ -895,8 +902,11 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
f"Stop-start is not 1 Hz but {self.instrument.stop() - self.instrument.start()} Hz. Please adjust start or stop."
)

timeout = self.instrument.timeout()
current_timeout = timeout if timeout is not None else float("inf")

with self.instrument.timeout.set_to(
max(self.instrument.timeout(), self.expected_measurement_duration)
max(current_timeout, self.expected_measurement_duration)
):
self.instrument.write("CALC1:PAR:COUN 1") # 1 trace
self.instrument.write(f"CALC1:PAR1:DEF {self.name[-3:]}")
Expand Down
Loading
Loading