-
Notifications
You must be signed in to change notification settings - Fork 352
Expand file tree
/
Copy path_minicircuits_usb_spdt.py
More file actions
140 lines (113 loc) · 4.81 KB
/
_minicircuits_usb_spdt.py
File metadata and controls
140 lines (113 loc) · 4.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import os
from typing import TYPE_CHECKING
import usb1
from libusb1 import libusb_error
from usb1 import USBContext, USBDevice, USBDeviceHandle, USBError
# QCoDeS imports
from qcodes.instrument_drivers.Minicircuits.Base_SPDT import (
MiniCircuitsSPDTBase,
MiniCircuitsSPDTSwitchChannelBase,
)
if TYPE_CHECKING:
from typing_extensions import Unpack
from qcodes.instrument import InstrumentBaseKWArgs
MINICIRCUITS_VENDOR_ID = 0x20CE
RF_SWITCH_PRODUCT_ID = 0x0022
def open_switch_with_sn(serial_number: str | None) -> USBDeviceHandle | None:
usb_context = USBContext()
if serial_number is not None:
device_iterator = usb_context.getDeviceIterator(
skip_on_error=True,
)
try:
for device in device_iterator:
if (
device.getVendorID() == MINICIRCUITS_VENDOR_ID
and device.getProductID() == RF_SWITCH_PRODUCT_ID
):
handle = device.open()
if get_serial_number(handle) == serial_number:
return handle
device.close() # Unsure what missing arguments are needed or what they do
finally:
device_iterator.close()
else: # If no SN is provided, we can use the built-in function to return the first Minicircuits switch
return usb_context.openByVendorIDAndProductID(
MINICIRCUITS_VENDOR_ID, RF_SWITCH_PRODUCT_ID
)
return None
def get_serial_number(handle: USBDeviceHandle) -> str:
handle.resetDevice()
handle.claimInterface(0)
cmd = [
41,
]
cmd_array = bytearray([0] * 64)
cmd_array[0 : len(cmd)] = cmd
handle.interruptWrite(endpoint=1, data=cmd_array, timeout=50)
response = handle.interruptRead(endpoint=1, length=64, timeout=1000)
resp_length = response.index(bytearray([0]))
trimmed_response = response[1:resp_length]
return trimmed_response.decode("ascii")
class MiniCircuitsUsbSPDTSwitchChannel(
MiniCircuitsSPDTSwitchChannelBase["MiniCircuitsUsbSPDT"]
):
def _set_switch(self, switch: int) -> None:
self.parent._query_scpi(f"set{self.channel_letter}={switch - 1}")
def _get_switch(self) -> int:
all_ports_state = int(self.parent._query_scpi("SWPORT?"))
bitmask = 2**self.channel_number
return int((all_ports_state & bitmask) >= 1) + 1
class MiniCircuitsUsbSPDT(MiniCircuitsSPDTBase):
CHANNEL_CLASS = MiniCircuitsUsbSPDTSwitchChannel
def __init__(
self,
name: str,
serial_number: str | None = None,
**kwargs: "Unpack[InstrumentBaseKWArgs]",
):
"""
Mini-Circuits SPDT RF switch
Args:
name: the name of the instrument
serial_number: the serial number of the device
(printed on the sticker on the back side, without s/n)
kwargs: kwargs to be passed to Instrument class.
"""
super().__init__(name, **kwargs)
self._handle: USBDeviceHandle | None = open_switch_with_sn(serial_number)
self.add_channels()
self.connect_message()
@property
def handle(self) -> USBDeviceHandle:
if self._handle is None:
raise Exception # TODO Make better
return self._handle
def _query_scpi(self, command: str) -> str:
cmd_bytes = bytearray([42, 58]) # Interrupt code for Send SCPI Command
cmd_bytes.extend(bytearray(command, "ascii"))
cmd_bytes.extend(bytearray(64 - len(cmd_bytes)))
self.handle.interruptWrite(endpoint=1, data=cmd_bytes, timeout=50)
response = self.handle.interruptRead(endpoint=1, length=64, timeout=1000)
resp_length = response.index(bytearray([0]))
trimmed_response = response[1:resp_length]
return trimmed_response.decode("ascii")
def get_idn(self) -> dict[str, str | None]:
# the arguments in those functions is the serial number or none if
# there is only one switch.
fw = self._query_scpi("FIRMWARE?")
MN = self._query_scpi("MN?")
SN = self._query_scpi("SN?")
id_dict = {"firmware": fw, "model": MN, "serial": SN, "vendor": "Mini-Circuits"}
return id_dict
# Notes:
# https://www.minicircuits.com/softwaredownload/Prog_Manual-2-Switch.pdf
# Section 3 of the Minicircuits Programming Manual for RF switches includes additional
# SCPI commands which we may find useful. These are not currently implemented
# For example: `SETP=[states]` allows multiple switch states to be set with a single command
#
# We may also eventually be able to unify the Ethernet interface used by the RC-SPDT and RC-SP4T drivers
# with the USB interface and have both rely on the SCPI commands
#
# Finally, the commands for SP4T and SPDT are not so different that we couldn't have a generic driver
# That works for all versions