Skip to content

Commit

Permalink
Merge pull request #6160 from jenshnielsen/minicircuits_refactor
Browse files Browse the repository at this point in the history
Refactor drivers Minicircuits - R&S
  • Loading branch information
jenshnielsen authored Jun 7, 2024
2 parents 1afaee8 + 64579e8 commit e06393b
Show file tree
Hide file tree
Showing 34 changed files with 1,425 additions and 798 deletions.
6 changes: 6 additions & 0 deletions docs/changes/newsfragments/6160.improved_driver
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
The Minicircuits, Oxford, QDev, QuantumDesign, Rigol and Rohde & Schwarz drivers shipping with QCoDeS
have been updated to ensure all Parameters are set as static
attributes that are documented and can be type checked. The docs for the same drivers have been
updated to not document inherited members. This makes the documentation significantly more readable
as it focuses on specific members for a given instrument. The documentation now also links superclasses.
Please consult these for inherited members.
1 change: 1 addition & 0 deletions docs/drivers_api/Minicircuits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ MiniCircuits Drivers

.. automodule:: qcodes.instrument_drivers.Minicircuits
:autosummary:
:no-inherited-members:
1 change: 1 addition & 0 deletions docs/drivers_api/Oxford.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Oxford Instruments Drivers

.. automodule:: qcodes.instrument_drivers.oxford
:autosummary:
:no-inherited-members:
1 change: 1 addition & 0 deletions docs/drivers_api/QDev.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ QDev Drivers

.. automodule:: qcodes.instrument_drivers.QDev
:autosummary:
:no-inherited-members:
1 change: 1 addition & 0 deletions docs/drivers_api/QuantumDesign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Quantum Design Drivers

.. automodule:: qcodes.instrument_drivers.QuantumDesign
:autosummary:
:no-inherited-members:
1 change: 1 addition & 0 deletions docs/drivers_api/Rigol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Rigol Drivers

.. automodule:: qcodes.instrument_drivers.rigol
:autosummary:
:no-inherited-members:
1 change: 1 addition & 0 deletions docs/drivers_api/RohdeSchwarz.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Rohde & Schwarz Drivers

.. automodule:: qcodes.instrument_drivers.rohde_schwarz
:autosummary:
:no-inherited-members:
36 changes: 32 additions & 4 deletions src/qcodes/instrument_drivers/Minicircuits/Base_SPDT.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@
import warnings
from typing import TYPE_CHECKING, Any

from typing_extensions import deprecated

from qcodes.instrument import (
ChannelList,
Instrument,
InstrumentBaseKWArgs,
InstrumentChannel,
)
from qcodes.utils import QCoDeSDeprecationWarning
from qcodes.validators import Ints

if TYPE_CHECKING:
from typing_extensions import Unpack

from qcodes.parameters import Parameter

log = logging.getLogger(__name__)


class SwitchChannelBase(InstrumentChannel):
class MiniCircuitsSPDTSwitchChannelBase(InstrumentChannel):
def __init__(
self,
parent: Instrument,
Expand All @@ -28,6 +33,9 @@ def __init__(
**kwargs: Unpack[InstrumentBaseKWArgs],
):
"""
Base class for MiniCircuits SPDT Switch channels.
Should not be instantiated directly.
Args:
parent: The instrument the channel is a part of
name: the name of the channel
Expand All @@ -40,12 +48,13 @@ def __init__(
_chanlist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
self.channel_number = _chanlist.index(channel_letter)

self.add_parameter(
self.switch: Parameter = self.add_parameter(
'switch',
label=f'switch {self.channel_letter}',
set_cmd=self._set_switch,
get_cmd=self._get_switch,
vals=Ints(1, 2))
"""Parameter switch"""

def __call__(self, *args: int) -> int | None:
if len(args) == 1:
Expand All @@ -63,10 +72,21 @@ def _set_switch(self, switch: int) -> None:
def _get_switch(self) -> int:
raise NotImplementedError()

@deprecated(
"Deprecated alias, use MiniCircuitsSPDTSwitchChannelBase.",
category=QCoDeSDeprecationWarning,
)
class SwitchChannelBase(MiniCircuitsSPDTSwitchChannelBase):
pass


class SPDT_Base(Instrument):
class MiniCircuitsSPDTBase(Instrument):
"""
Base class for MiniCircuits SPDT Switch instruments.
Should not be instantiated directly.
"""

CHANNEL_CLASS: type[SwitchChannelBase]
CHANNEL_CLASS: type[MiniCircuitsSPDTSwitchChannelBase]

def add_channels(self) -> None:
channels = ChannelList(
Expand Down Expand Up @@ -136,3 +156,11 @@ def get_number_of_channels(self) -> int:
f" the model '{model}', it might not be supported"
)
return int(channels)


@deprecated(
"Deprecated alias, use MiniCircuitsSPDTBase.",
category=QCoDeSDeprecationWarning,
)
class SPDT_Base(MiniCircuitsSPDTBase):
pass
124 changes: 122 additions & 2 deletions src/qcodes/instrument_drivers/Minicircuits/USBHIDMixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import time
from typing import TYPE_CHECKING, Optional

from typing_extensions import deprecated

from qcodes.utils import QCoDeSDeprecationWarning

try:
from pywinusb import hid # pyright: ignore[reportMissingModuleSource]

Expand All @@ -23,6 +27,10 @@
from qcodes.instrument import InstrumentBaseKWArgs


@deprecated(
"USBHIDMixin is deprecated. This is unused in QCoDeS",
category=QCoDeSDeprecationWarning,
)
class USBHIDMixin(Instrument):

# The following class attributes should be set by subclasses
Expand Down Expand Up @@ -164,7 +172,22 @@ def enumerate_devices(cls) -> list[str]:
return [dev.instance_id for dev in devs]


class MiniCircuitsHIDMixin(USBHIDMixin):
class MiniCircuitsHIDMixin(Instrument):
# The following class attributes should be set by subclasses
vendor_id = 0x0000
product_id = 0x0000

@staticmethod
def _check_hid_import() -> None:
if os.name != "nt":
raise ImportError("This driver only works on Windows.")

if imported_hid is False:
raise ImportError(
"pywinusb is not installed. Please install it by typing "
"'pip install pywinusb' in a qcodes environment terminal"
)

def __init__(
self,
name: str,
Expand All @@ -188,13 +211,110 @@ def __init__(
timeout: Specify a timeout for this instrument in seconds
**kwargs: Forwarded to base class.
"""
self._check_hid_import()

# USB interrupt code for sending SCPI commands
self._sending_scpi_cmds_code = 1
self._usb_endpoint = 0
self._end_of_message = b"\x00"
self.packet_size = 64

super().__init__(name, instance_id, timeout, **kwargs)
devs = hid.HidDeviceFilter( # pyright: ignore[reportPossiblyUnboundVariable]
product_id=self.product_id,
vendor_id=self.vendor_id,
instance_id=instance_id,
).get_devices()

if len(devs) == 0:
raise RuntimeError("No instruments found!")
elif len(devs) > 1:
raise RuntimeError(
"Multiple HID devices detected! Please supply a instance id"
)

self._device = devs[0]
self._device.open()

self._data_buffer: Optional[bytes] = None
self._device.set_raw_data_handler(self._handler)

self._timeout = timeout
self._tries_per_second = 5

super().__init__(name, **kwargs)

def _handler(self, data: bytes) -> None:
self._data_buffer = data

def _get_data_buffer(self) -> Optional[bytes]:
data = self._data_buffer
self._data_buffer = None
return data

def write_raw(self, cmd: str) -> None:
"""
Send a string command to the human interface device
The given command is processed by `_pack_string` method to return a
byte sequence that is going to be actually sent to the device.
Subclasses must implement `_pack_string` method.
Args:
cmd: a command to send in a form of a string
"""
data = self._pack_string(cmd)

result = self._device.send_output_report(data)
if not result:
raise RuntimeError(f"Communication with device failed for command {cmd}")

def ask_raw(self, cmd: str) -> str:
"""
Send a string command to the human interface device and wait for a reply
The given command is processed by `_pack_string` method to return a
byte sequence that is going to be actually sent to the device.
Subclasses must implement `_pack_string` method.
The byte sequence of the reply is processed by `_unpack_string`
method, and the resulting string is returned. Subclasses must
implement `_unpack_string` method.
Args:
cmd: a command to send in a form of a string
"""
self.write_raw(cmd)

number_of_tries = int(self._tries_per_second * self._timeout)

response = None
for _ in range(number_of_tries):
time.sleep(1 / self._tries_per_second)
response = self._get_data_buffer()
if response is not None:
break

if response is None:
raise TimeoutError(f"Timed out for command {cmd}")

return self._unpack_string(response)

def close(self) -> None:
self._device.close()

@classmethod
def enumerate_devices(cls) -> list[str]:
"""
This method returns the 'instance_id's of all connected devices for
with the given product and vendor IDs.
"""
cls._check_hid_import()

devs = hid.HidDeviceFilter( # pyright: ignore[reportPossiblyUnboundVariable]
porduct_id=cls.product_id, vendor_id=cls.vendor_id
).get_devices()

return [dev.instance_id for dev in devs]

def _pack_string(self, cmd: str) -> bytes:
"""
Expand Down
11 changes: 10 additions & 1 deletion src/qcodes/instrument_drivers/Minicircuits/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
from ._minicircuits_rc_sp4t import MiniCircuitsRCSP4T, MiniCircuitsRCSP4TChannel
from ._minicircuits_rc_spdt import MiniCircuitsRCSPDT, MiniCircuitsRCSPDTChannel
from ._minicircuits_rudat_13g_90 import MiniCircuitsRudat13G90Usb
from ._minicircuits_rudat_13g_90 import (
MiniCircuitsRudat13G90Base,
MiniCircuitsRudat13G90Usb,
)
from ._minicircuits_usb_spdt import (
MiniCircuitsUsbSPDT,
MiniCircuitsUsbSPDTSwitchChannel,
)
from .Base_SPDT import MiniCircuitsSPDTBase, MiniCircuitsSPDTSwitchChannelBase
from .USBHIDMixin import MiniCircuitsHIDMixin

__all__ = [
"MiniCircuitsRCSP4T",
"MiniCircuitsRCSP4TChannel",
"MiniCircuitsRCSPDT",
"MiniCircuitsRCSPDTChannel",
"MiniCircuitsRudat13G90Usb",
"MiniCircuitsRudat13G90Base",
"MiniCircuitsHIDMixin",
"MiniCircuitsUsbSPDT",
"MiniCircuitsSPDTBase",
"MiniCircuitsUsbSPDTSwitchChannel",
"MiniCircuitsSPDTSwitchChannelBase",
]
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
if TYPE_CHECKING:
from typing_extensions import Unpack

from qcodes.parameters import Parameter


class MiniCircuitsRCSP4TChannel(InstrumentChannel):
def __init__(
Expand All @@ -34,13 +36,14 @@ def __init__(
chanlist = ["a", "b"]
self.channel_number = chanlist.index(channel_letter)

self.add_parameter(
self.switch: Parameter = self.add_parameter(
"switch",
label="switch",
set_cmd=self._set_switch,
get_cmd=self._get_switch,
vals=vals.Ints(0, 4),
)
"""Parameter switch"""

def _set_switch(self, switch: int) -> None:
if len(self._parent.channels) > 1:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
if TYPE_CHECKING:
from typing_extensions import Unpack

from qcodes.parameters import Parameter


class MiniCircuitsRCSPDTChannel(InstrumentChannel):
def __init__(
Expand All @@ -33,13 +35,14 @@ def __init__(
_chanlist = ["a", "b", "c", "d", "e", "f", "g", "h"]
self.channel_number = _chanlist.index(channel_letter)

self.add_parameter(
self.switch: Parameter = self.add_parameter(
"switch",
label="switch",
set_cmd=self._set_switch,
get_cmd=self._get_switch,
vals=vals.Ints(1, 2),
)
"""Parameter switch"""

def _set_switch(self, switch: int) -> None:
self.write(f"SET{self.channel_letter}={switch-1}")
Expand Down
Loading

0 comments on commit e06393b

Please sign in to comment.