11import os
22from typing import TYPE_CHECKING
33
4+ import usb1
5+ from libusb1 import libusb_error
6+ from usb1 import USBContext , USBDevice , USBDeviceHandle , USBError
7+
48# QCoDeS imports
59from qcodes .instrument_drivers .Minicircuits .Base_SPDT import (
610 MiniCircuitsSPDTBase ,
1216
1317 from qcodes .instrument import InstrumentBaseKWArgs
1418
15- try :
16- import clr # pyright: ignore[reportMissingTypeStubs,reportMissingImports]
17- except ImportError :
18- raise ImportError (
19- """Module clr not found. Please obtain it by
20- installing QCoDeS with the
21- minicircuits_usb_spdt extra, e.g. by running
22- pip install qcodes[minicircuits_usb_spdt]"""
23- )
19+ MINICIRCUITS_VENDOR_ID = 0x20CE
20+ RF_SWITCH_PRODUCT_ID = 0x0022
21+
22+
23+ def open_switch_with_sn (serial_number : str | None ) -> USBDeviceHandle | None :
24+ usb_context = USBContext ()
25+ if serial_number is not None :
26+ device_iterator = usb_context .getDeviceIterator (
27+ skip_on_error = True ,
28+ )
29+ try :
30+ for device in device_iterator :
31+ if (
32+ device .getVendorID () == MINICIRCUITS_VENDOR_ID
33+ and device .getProductID () == RF_SWITCH_PRODUCT_ID
34+ ):
35+ handle = device .open ()
36+ if get_serial_number (handle ) == serial_number :
37+ return handle
38+ device .close () # Unsure what missing arguments are needed or what they do
39+ finally :
40+ device_iterator .close ()
41+ else : # If no SN is provided, we can use the built-in function to return the first Minicircuits switch
42+ return usb_context .openByVendorIDAndProductID (
43+ MINICIRCUITS_VENDOR_ID , RF_SWITCH_PRODUCT_ID
44+ )
45+ return None
46+
47+
48+ def get_serial_number (handle : USBDeviceHandle ) -> str :
49+ handle .resetDevice ()
50+ handle .claimInterface (0 )
51+ cmd = [
52+ 41 ,
53+ ]
54+ cmd_array = bytearray ([0 ] * 64 )
55+ cmd_array [0 : len (cmd )] = cmd
56+ handle .interruptWrite (endpoint = 1 , data = cmd_array , timeout = 50 )
57+ response = handle .interruptRead (endpoint = 1 , length = 64 , timeout = 1000 )
58+ resp_length = response .index (bytearray ([0 ]))
59+ trimmed_response = response [1 :resp_length ]
60+ return trimmed_response .decode ("ascii" )
2461
2562
2663class MiniCircuitsUsbSPDTSwitchChannel (
2764 MiniCircuitsSPDTSwitchChannelBase ["MiniCircuitsUsbSPDT" ]
2865):
2966 def _set_switch (self , switch : int ) -> None :
30- self .parent .switch . Set_Switch ( self .channel_letter , switch - 1 )
67+ self .parent ._query_scpi ( f"set { self .channel_letter } = { switch - 1 } " )
3168
3269 def _get_switch (self ) -> int :
33- status = self .parent .switch .GetSwitchesStatus (self ._parent .address )[1 ]
34- return int (f"{ status :04b} " [- 1 - self .channel_number ]) + 1
70+ all_ports_state = int (self .parent ._query_scpi ("SWPORT?" ))
71+ bitmask = 2 ** self .channel_number
72+ return int ((all_ports_state & bitmask ) >= 1 ) + 1
3573
3674
3775class MiniCircuitsUsbSPDT (MiniCircuitsSPDTBase ):
3876 CHANNEL_CLASS = MiniCircuitsUsbSPDTSwitchChannel
39- PATH_TO_DRIVER = r"mcl_RF_Switch_Controller64"
40- PATH_TO_DRIVER_45 = r"mcl_RF_Switch_Controller_NET45"
4177
4278 def __init__ (
4379 self ,
4480 name : str ,
45- driver_path : str | None = None ,
4681 serial_number : str | None = None ,
4782 ** kwargs : "Unpack[InstrumentBaseKWArgs]" ,
4883 ):
@@ -51,59 +86,42 @@ def __init__(
5186
5287 Args:
5388 name: the name of the instrument
54- driver_path: path to the dll
5589 serial_number: the serial number of the device
5690 (printed on the sticker on the back side, without s/n)
5791 kwargs: kwargs to be passed to Instrument class.
5892
5993 """
60- # import .net exception so we can catch it below
61- # we keep this import local so that the module can be imported
62- # without a working .net install
63- clr .AddReference ("System.IO" )
64- from System .IO import ( # pyright: ignore[reportMissingImports] # noqa: PLC0415
65- FileNotFoundException ,
66- )
67-
6894 super ().__init__ (name , ** kwargs )
69- if os .name != "nt" :
70- raise ImportError ("""This driver only works in Windows.""" )
71- try :
72- if driver_path is None :
73- try :
74- clr .AddReference (self .PATH_TO_DRIVER )
75- except FileNotFoundError :
76- clr .AddReference (self .PATH_TO_DRIVER_45 )
77- else :
78- clr .AddReference (driver_path )
79-
80- except (ImportError , FileNotFoundException ):
81- raise ImportError (
82- """Load of mcl_RF_Switch_Controller64.dll or mcl_RF_Switch_Controller_NET45.dll
83- not possible. Make sure the dll file is not blocked by Windows.
84- To unblock right-click the dll to open properties and check the 'unblock' checkmark
85- in the bottom. Check that your python installation is 64bit."""
86- )
87- try :
88- import mcl_RF_Switch_Controller64 as mw_driver # pyright: ignore[reportMissingImports]# noqa: PLC0415
89- except ImportError :
90- import mcl_RF_Switch_Controller_NET45 as mw_driver # pyright: ignore[reportMissingImports]# noqa: PLC0415
91-
92- self .switch = mw_driver .USB_RF_SwitchBox ()
95+ self ._handle : USBDeviceHandle | None = open_switch_with_sn (serial_number )
9396
94- if not self .switch .Connect (serial_number ):
95- raise RuntimeError ("Could not connect to device" )
96- self .address = self .switch .Get_Address ()
97- self .serial_number = self .switch .Read_SN ("" )[1 ]
98- self .connect_message ()
9997 self .add_channels ()
98+ self .connect_message ()
99+
100+ @property
101+ def handle (self ) -> USBDeviceHandle :
102+ if self ._handle is None :
103+ raise Exception # TODO Make better
104+ return self ._handle
105+
106+ def _query_scpi (self , command : str ) -> str :
107+ cmd_bytes = bytearray ([42 , 58 ]) # Interrupt code for Send SCPI Command
108+ cmd_bytes .extend (bytearray (command , "ascii" ))
109+ cmd_bytes .extend (bytearray (64 - len (cmd_bytes )))
110+
111+ self .handle .interruptWrite (endpoint = 1 , data = cmd_bytes , timeout = 50 )
112+
113+ response = self .handle .interruptRead (endpoint = 1 , length = 64 , timeout = 1000 )
114+ resp_length = response .index (bytearray ([0 ]))
115+ trimmed_response = response [1 :resp_length ]
116+
117+ return trimmed_response .decode ("ascii" )
100118
101119 def get_idn (self ) -> dict [str , str | None ]:
102120 # the arguments in those functions is the serial number or none if
103121 # there is only one switch.
104- fw = self .switch . GetFirmware ( )
105- MN = self .switch . Read_ModelName ( "" )[ 1 ]
106- SN = self .switch . Read_SN ( "" )[ 1 ]
122+ fw = self ._query_scpi ( "FIRMWARE?" )
123+ MN = self ._query_scpi ( "MN?" )
124+ SN = self ._query_scpi ( "SN?" )
107125
108126 id_dict = {"firmware" : fw , "model" : MN , "serial" : SN , "vendor" : "Mini-Circuits" }
109127 return id_dict
0 commit comments