Skip to content

Commit 0d55b35

Browse files
authored
Merge pull request #7910 from jenshnielsen/jenshnielsen/strict_rigol
Rigol + R&S drivers avoid internal dependency on dynamic attributes
2 parents a7bd327 + f4df9e3 commit 0d55b35

4 files changed

Lines changed: 64 additions & 41 deletions

File tree

src/qcodes/instrument_drivers/rigol/Rigol_DG4000.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,13 +738,15 @@ def __init__(
738738
# Trace
739739
self.add_function("upload_data", call_cmd=self._upload_data, args=[Anything()])
740740

741-
self.add_function("reset", call_cmd="*RST")
742-
743741
if reset:
744742
self.reset()
745743

746744
self.connect_message()
747745

746+
def reset(self) -> None:
747+
"""Reset the instrument to default settings."""
748+
self.write("*RST")
749+
748750
def _upload_data(self, data: "Sequence[float] | npt.NDArray") -> None:
749751
"""
750752
Upload data to the AWG memory.

src/qcodes/instrument_drivers/rigol/Rigol_DS1074Z.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from qcodes.parameters import Parameter
2020

2121

22-
class RigolDS1074ZChannel(InstrumentChannel):
22+
class RigolDS1074ZChannel(InstrumentChannel["RigolDS1074Z"]):
2323
"""
2424
Contains methods and attributes specific to the Rigol
2525
oscilloscope channels.
@@ -58,23 +58,23 @@ def __init__(
5858
"""Parameter trace"""
5959

6060
def _get_full_trace(self) -> npt.NDArray:
61-
y_ori = self.root_instrument.waveform_yorigin()
62-
y_increm = self.root_instrument.waveform_yincrem()
63-
y_ref = self.root_instrument.waveform_yref()
61+
y_ori = self.parent.waveform_yorigin()
62+
y_increm = self.parent.waveform_yincrem()
63+
y_ref = self.parent.waveform_yref()
6464
y_raw = self._get_raw_trace()
6565
y_raw_shifted = y_raw - y_ori - y_ref
6666
full_data = np.multiply(y_raw_shifted, y_increm)
6767
return full_data
6868

6969
def _get_raw_trace(self) -> npt.NDArray:
7070
# set the out type from oscilloscope channels to WORD
71-
self.root_instrument.write(":WAVeform:FORMat WORD")
71+
self.parent.write(":WAVeform:FORMat WORD")
7272

7373
# set the channel from where data will be obtained
74-
self.root_instrument.data_source(f"ch{self.channel}")
74+
self.parent.data_source(f"ch{self.channel}")
7575

7676
# Obtain the trace
77-
raw_trace_val = self.root_instrument.visa_handle.query_binary_values(
77+
raw_trace_val = self.parent.visa_handle.query_binary_values(
7878
"WAV:DATA?", datatype="h", is_big_endian=False, expect_termination=False
7979
)
8080
return np.array(raw_trace_val)
@@ -231,10 +231,8 @@ def _get_time_axis(self) -> npt.NDArray:
231231
return xdata
232232

233233
def _get_trigger_level(self) -> str:
234-
trigger_level = self.root_instrument.ask(
235-
f":TRIGger:{self.trigger_mode()}:LEVel?"
236-
)
234+
trigger_level = self.ask(f":TRIGger:{self.trigger_mode()}:LEVel?")
237235
return trigger_level
238236

239237
def _set_trigger_level(self, value: str) -> None:
240-
self.root_instrument.write(f":TRIGger:{self.trigger_mode()}:LEVel {value}")
238+
self.write(f":TRIGger:{self.trigger_mode()}:LEVel {value}")

src/qcodes/instrument_drivers/rohde_schwarz/RTO1000.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# All manual references are to R&S RTO Digital Oscilloscope User Manual
22
# for firmware 3.65, 2017
3-
43
import logging
54
import time
65
import warnings
@@ -12,6 +11,8 @@
1211

1312
import qcodes.validators as vals
1413
from qcodes.instrument import (
14+
ChannelList,
15+
ChannelTuple,
1516
Instrument,
1617
InstrumentBaseKWArgs,
1718
InstrumentChannel,
@@ -26,9 +27,13 @@
2627
log = logging.getLogger(__name__)
2728

2829

29-
class ScopeTrace(ArrayParameter):
30+
class ScopeTrace(ArrayParameter[npt.NDArray, "RohdeSchwarzRTO1000ScopeChannel"]):
3031
def __init__(
31-
self, name: str, instrument: InstrumentChannel, channum: int, **kwargs: Any
32+
self,
33+
name: str,
34+
instrument: "RohdeSchwarzRTO1000ScopeChannel",
35+
channum: int,
36+
**kwargs: Any,
3237
) -> None:
3338
"""
3439
The ScopeTrace parameter is attached to a channel of the oscilloscope.
@@ -53,6 +58,12 @@ def __init__(
5358
self.channum = channum
5459
self._trace_ready = False
5560

61+
@property
62+
def root_instrument(self) -> "RohdeSchwarzRTO1000":
63+
root_instrument = super().root_instrument
64+
assert isinstance(root_instrument, RohdeSchwarzRTO1000)
65+
return root_instrument
66+
5667
def prepare_trace(self) -> None:
5768
"""
5869
Prepare the scope for returning data, calculate the setpoints
@@ -452,7 +463,7 @@ def __init__(
452463
ScopeMeasurement = RohdeSchwarzRTO1000ScopeMeasurement
453464

454465

455-
class RohdeSchwarzRTO1000ScopeChannel(InstrumentChannel):
466+
class RohdeSchwarzRTO1000ScopeChannel(InstrumentChannel["RohdeSchwarzRTO1000"]):
456467
"""
457468
Class to hold an input channel of the scope.
458469
@@ -462,7 +473,7 @@ class RohdeSchwarzRTO1000ScopeChannel(InstrumentChannel):
462473

463474
def __init__(
464475
self,
465-
parent: Instrument,
476+
parent: "RohdeSchwarzRTO1000",
466477
name: str,
467478
channum: int,
468479
**kwargs: "Unpack[InstrumentBaseKWArgs]",
@@ -631,12 +642,12 @@ def __init__(
631642
def _set_range(self, value: float) -> None:
632643
self.scale.cache.set(value / 10)
633644

634-
self._parent.write(f"CHANnel{self.channum}:RANGe {value}")
645+
self.parent.write(f"CHANnel{self.channum}:RANGe {value}")
635646

636647
def _set_scale(self, value: float) -> None:
637648
self.range.cache.set(value * 10)
638649

639-
self._parent.write(f"CHANnel{self.channum}:SCALe {value}")
650+
self.parent.write(f"CHANnel{self.channum}:SCALe {value}")
640651

641652

642653
ScopeChannel = RohdeSchwarzRTO1000ScopeChannel
@@ -966,15 +977,31 @@ def __init__(
966977
"""Parameter error_next"""
967978

968979
# Add the channels to the instrument
980+
scope_channels = ChannelList(
981+
self, "scope_channels", RohdeSchwarzRTO1000ScopeChannel
982+
)
983+
"""ChannelTuple holding the scope channels.
984+
"""
969985
for ch in range(1, self.num_chans + 1):
970986
chan = RohdeSchwarzRTO1000ScopeChannel(self, f"channel{ch}", ch)
987+
scope_channels.append(chan)
971988
self.add_submodule(f"ch{ch}", chan)
972-
989+
self.scope_channels: ChannelTuple[RohdeSchwarzRTO1000ScopeChannel] = (
990+
self.add_submodule("scope_channels", scope_channels.to_channel_tuple())
991+
)
992+
measurements = ChannelList(
993+
self, "measurements", RohdeSchwarzRTO1000ScopeMeasurement
994+
)
973995
for measId in range(1, self.num_meas + 1):
974996
measCh = RohdeSchwarzRTO1000ScopeMeasurement(
975997
self, f"measurement{measId}", measId
976998
)
999+
measurements.append(measCh)
9771000
self.add_submodule(f"meas{measId}", measCh)
1001+
self.measurements: ChannelTuple[RohdeSchwarzRTO1000ScopeMeasurement] = (
1002+
self.add_submodule("measurements", measurements.to_channel_tuple())
1003+
)
1004+
"""ChannelTuple holding the scope measurements."""
9781005

9791006
self.add_function("stop", call_cmd="STOP")
9801007
self.add_function("reset", call_cmd="*RST")
@@ -1055,10 +1082,8 @@ def _make_traces_not_ready(self) -> None:
10551082
"""
10561083
Make the scope traces be not ready.
10571084
"""
1058-
self.ch1.trace._trace_ready = False
1059-
self.ch2.trace._trace_ready = False
1060-
self.ch3.trace._trace_ready = False
1061-
self.ch4.trace._trace_ready = False
1085+
for chan in self.scope_channels:
1086+
chan.trace._trace_ready = False
10621087

10631088
def _set_trigger_level(self, value: float) -> None:
10641089
"""
@@ -1071,7 +1096,7 @@ def _set_trigger_level(self, value: float) -> None:
10711096
source = trans[self.trigger_source.get()]
10721097
if source != 5:
10731098
submodule = self.submodules[f"ch{source}"]
1074-
assert isinstance(submodule, InstrumentChannel)
1099+
assert isinstance(submodule, RohdeSchwarzRTO1000ScopeChannel)
10751100
v_range = submodule.range()
10761101
offset = submodule.offset()
10771102

src/qcodes/instrument_drivers/rohde_schwarz/ZNB.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ def get_raw(self) -> npt.NDArray[np.floating]:
397397
return self.instrument._get_sweep_data()
398398

399399

400-
class RohdeSchwarzZNBChannel(InstrumentChannel):
400+
class RohdeSchwarzZNBChannel(InstrumentChannel["RohdeSchwarzZNBBase"]):
401401
def __init__(
402402
self,
403403
parent: "RohdeSchwarzZNBBase",
@@ -432,7 +432,7 @@ def __init__(
432432
if existing_trace_to_bind_to is None:
433433
self._tracename = f"Trc{channel}"
434434
else:
435-
traces = self._parent.ask("CONFigure:TRACe:CATalog?")
435+
traces = self.parent.ask("CONFigure:TRACe:CATalog?")
436436
if existing_trace_to_bind_to not in traces:
437437
raise RuntimeError(
438438
f"Trying to bind to"
@@ -759,12 +759,10 @@ def __init__(
759759

760760
def set_electrical_delay_auto(self) -> None:
761761
n = self._instrument_channel
762-
self.root_instrument.write(f"SENS{n}:CORR:EDEL:AUTO ONCE")
762+
self.parent.write(f"SENS{n}:CORR:EDEL:AUTO ONCE")
763763

764764
def autoscale(self) -> None:
765-
self.root_instrument.write(
766-
f"DISPlay:TRACe1:Y:SCALe:AUTO ONCE, {self._tracename}"
767-
)
765+
self.parent.write(f"DISPlay:TRACe1:Y:SCALe:AUTO ONCE, {self._tracename}")
768766

769767
def _get_format(self, tracename: str) -> str:
770768
n = self._instrument_channel
@@ -925,7 +923,7 @@ def _get_sweep_data(self, force_polar: bool = False) -> npt.NDArray:
925923

926924
# preserve original state of the znb
927925
with self.status.set_to(1):
928-
self.root_instrument.cont_meas_off()
926+
self.parent.cont_meas_off()
929927
try:
930928
# if force polar is set, the SDAT data format will be used.
931929
# Here the data will be transferred as a complex number
@@ -935,7 +933,7 @@ def _get_sweep_data(self, force_polar: bool = False) -> npt.NDArray:
935933
else:
936934
data_format_command = "FDAT"
937935

938-
with self.root_instrument.timeout.set_to(self._get_timeout()):
936+
with self.parent.timeout.set_to(self._get_timeout()):
939937
# instrument averages over its last 'avg' number of sweeps
940938
# need to ensure averaged result is returned
941939
for _ in range(self.avg()):
@@ -950,7 +948,7 @@ def _get_sweep_data(self, force_polar: bool = False) -> npt.NDArray:
950948
if self.format() in ["Polar", "Complex", "Smith", "Inverse Smith"]:
951949
data = data[0::2] + 1j * data[1::2]
952950
finally:
953-
self.root_instrument.cont_meas_on()
951+
self.parent.cont_meas_on()
954952
return data
955953

956954
def setup_cw_sweep(self) -> None:
@@ -977,15 +975,15 @@ def setup_cw_sweep(self) -> None:
977975
self.auto_sweep_time_enabled(True)
978976
# Set cont measurement off here so we don't have to send that command
979977
# while measuring later.
980-
self.root_instrument.cont_meas_off()
978+
self.parent.cont_meas_off()
981979

982980
def setup_lin_sweep(self) -> None:
983981
"""
984982
Setup the instrument into linear sweep mode.
985983
"""
986984
self.sweep_type("Linear")
987985
self.averaging_enabled(True)
988-
self.root_instrument.cont_meas_on()
986+
self.parent.cont_meas_on()
989987

990988
def _check_cw_sweep(self) -> None:
991989
"""
@@ -998,7 +996,7 @@ def _check_cw_sweep(self) -> None:
998996
f"mode, instead it is: {self.sweep_type()}"
999997
)
1000998

1001-
if not self.root_instrument.rf_power():
999+
if not self.parent.rf_power():
10021000
log.warning("RF output is off when getting sweep data")
10031001

10041002
# It is possible that the instrument and QCoDeS disagree about
@@ -1016,7 +1014,7 @@ def _check_cw_sweep(self) -> None:
10161014
# Set the format to complex.
10171015
self.format("Complex")
10181016
# Set cont measurement off.
1019-
self.root_instrument.cont_meas_off()
1017+
self.parent.cont_meas_off()
10201018
# Cache the sweep time so it is up to date when setting timeouts
10211019
self.sweep_time()
10221020

@@ -1027,7 +1025,7 @@ def _get_cw_data(self) -> tuple[npt.NDArray, npt.NDArray]:
10271025
self._check_cw_sweep()
10281026

10291027
with self.status.set_to(1):
1030-
with self.root_instrument.timeout.set_to(self._get_timeout()):
1028+
with self.parent.timeout.set_to(self._get_timeout()):
10311029
self.write(f"INIT{self._instrument_channel}:IMM; *WAI")
10321030
data_str = self.ask(f"CALC{self._instrument_channel}:DATA? SDAT")
10331031
data = np.array(data_str.rstrip().split(",")).astype("float64")
@@ -1037,7 +1035,7 @@ def _get_cw_data(self) -> tuple[npt.NDArray, npt.NDArray]:
10371035
return i, q
10381036

10391037
def _get_timeout(self) -> float:
1040-
timeout = self.root_instrument.timeout() or float("+inf")
1038+
timeout = self.parent.timeout() or float("+inf")
10411039
timeout = max(self.sweep_time.cache.get() * 1.5, timeout)
10421040
return timeout
10431041

0 commit comments

Comments
 (0)