From 4b918035e253dbaaf419a2ad512c7adb4f0a46fc Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Wed, 8 Jan 2020 13:18:02 +0100 Subject: [PATCH 1/7] Added driver for 'Standa Filter Wheel 10MWA168' from SvenBo90/Qcodes:driver/Standa_10MWA168 --- .../Qcodes example with Standa 10MWA168.ipynb | 125 +++++++++++ .../drivers/Standa/Standa_10MWA168.py | 209 ++++++++++++++++++ .../drivers/Standa/__init__.py | 0 3 files changed, 334 insertions(+) create mode 100644 docs/examples/Qcodes example with Standa 10MWA168.ipynb create mode 100644 qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py create mode 100644 qcodes_contrib_drivers/drivers/Standa/__init__.py diff --git a/docs/examples/Qcodes example with Standa 10MWA168.ipynb b/docs/examples/Qcodes example with Standa 10MWA168.ipynb new file mode 100644 index 000000000..83414b1fe --- /dev/null +++ b/docs/examples/Qcodes example with Standa 10MWA168.ipynb @@ -0,0 +1,125 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Qcodes example with Standa 10MWA168" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-03-05T12:33:46.552440Z", + "start_time": "2019-03-05T12:33:46.522440Z" + } + }, + "outputs": [], + "source": [ + "from qcodes.instrument_drivers.Standa.Standa_10MWA168 import Standa_10MWA168" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialize instrument" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-03-05T12:34:08.039225Z", + "start_time": "2019-03-05T12:34:07.969226Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: None Standa_10MWA168 (serial:None, firmware:None) in 0.06s\n" + ] + } + ], + "source": [ + "standa = Standa_10MWA168(\"Standa_10MWA168\", '37866131')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py new file mode 100644 index 000000000..853e23229 --- /dev/null +++ b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py @@ -0,0 +1,209 @@ +from qcodes import Instrument +import ctypes +import numpy as np +import os +import time + + +class DeviceEnumeration(ctypes.BigEndianStructure): + pass + + +class DeviceInformation(ctypes.Structure): + _fields_ = [ + ("Manufacturer", ctypes.c_char * 5), + ("ManufacturerId", ctypes.c_char * 3), + ("ProductDescription", ctypes.c_char * 9), + ("Major", ctypes.c_uint), + ("Minor", ctypes.c_uint), + ("Release", ctypes.c_uint), + ] + + +class GetPosition(ctypes.Structure): + _fields_ = [ + ("Position", ctypes.c_int), + ("uPosition", ctypes.c_int), + ("EncPosition", ctypes.c_longlong), + ] + + +class Status(ctypes.Structure): + _fields_ = [ + ("MoveSts", ctypes.c_uint), + ("MvCmdSts", ctypes.c_uint), + ("PWRSts", ctypes.c_uint), + ("EncSts", ctypes.c_uint), + ("WindSts", ctypes.c_uint), + ("CurPosition", ctypes.c_int), + ("uCurPosition", ctypes.c_int), + ("EncPosition", ctypes.c_longlong), + ("CurSpeed", ctypes.c_int), + ("uCurSpeed", ctypes.c_int), + ("Ipwr", ctypes.c_int), + ("Upwr", ctypes.c_int), + ("Iusb", ctypes.c_int), + ("Uusb", ctypes.c_int), + ("CurT", ctypes.c_int), + ("Flags", ctypes.c_uint), + ("GPIOFlags", ctypes.c_uint), + ("CmdBufFreeSpace", ctypes.c_uint), + ] + + +class libximc: + + # TODO: use error check, implement wait for stop function from dll + + # default dll path + _dll_path = 'C:\\Program Files\\XILab\\libximc.dll' + + # success and error codes + _success_codes = {0: 'Ok'} + _error_codes = {-1: 'Error', -2: 'NotImplemented', -3: 'ValueError', -4: 'NoDevice'} + + def __init__(self, dll_path=None, verbose=False): + + # save attributes + self.verbose = verbose + + # connect to the dll + current_path = os.getcwd() + os.chdir(os.path.dirname(self._dll_path)) + self.dll = ctypes.windll.LoadLibrary(dll_path or self._dll_path) + os.chdir(current_path) + + # set resource type + self.dll.enumerate_devices.restype = ctypes.POINTER(DeviceInformation) + + def error_check(self, code, function_name=''): + if code in self._success_codes.keys(): + if self.verbose: + print("libximc: [%s]: %s" % (function_name, self._success_codes[code])) + elif code in self._error_codes.keys(): + print("libximc: [%s]: %s" % (function_name, self._error_codes[code])) + raise Exception(self._error_codes[code]) + else: + print("libximc: [%s]: Unknown code: %s" % (function_name, code)) + raise Exception() + + def command_move(self, device_id, position, u_position): + self.dll.command_move(device_id, position, u_position) + + def enumerate_devices(self, probe_flags): + enumeration = self.dll.enumerate_devices(probe_flags, b"") + return enumeration + + def get_device_name(self, device_enumeration, device_index): + device_name = self.dll.get_device_name(device_enumeration, device_index) + return device_name + + def get_position(self, device_id, get_position): + self.dll.get_position(device_id, get_position) + + def get_status(self, device_id, status): + self.dll.get_status(device_id, status) + + def open_device(self, device_name): + device_id = self.dll.open_device(device_name) + return device_id + + +class Standa_10MWA168(Instrument): + + def __init__(self, name, serial_number, dll_path=None, **kwargs): + + super().__init__(name, **kwargs) + + # link to dll + self.libximc = libximc(dll_path=dll_path) + + # instrument constants + self.filter_wheel_1 = [-1, 0, 1, -1, 1, 2, -1, 1, 2, 3, 3, 3, 1, 2, 4, 4, 1, 2, 5, 5, 4, 1, 2, 6, 6, 6, 2, 5, 6, 6, 6] + self.filter_wheel_2 = [-1, 0, -1, 1, 1, 1, 2, 2, 2, -1, 1, 2, 3, 3, -1, 2, 4, 4, -1, 1, 3, 5, 5, -1, 1, 2, 6, 4, 4, 5, 6] + self.offset_wheel_1 = 0. + self.offset_wheel_2 = 62. + self.revolution = 200. + self.distance = self.revolution / 8. + + # initialization + self.serial_number = serial_number + device_enumeration = self.libximc.enumerate_devices(0) + enumeration_name = self.libximc.get_device_name(device_enumeration, 0) + self.device_id = self.libximc.open_device(enumeration_name) + + # add parameters + self.add_parameter('transmittance', + set_cmd=self.set_transmittance, + label='Transmittance', + val_mapping={ + 1: 0, 0: 1, 9.0e-1: 2, 8.0e-1: 3, 7.2e-1: 4, 4.0e-1: 5, 3.0e-1: 6, 2.7e-1: 7, + 1.5e-1: 8, 1.0e-1: 9, 8.0e-2: 10, 3.0e-2: 11, 2.7e-2: 12, 1.5e-2: 13, 1.0e-2: 14, + 3.0e-3: 15, 2.7e-3: 16, 1.5e-3: 17, 1.0e-3: 18, 8.0e-4: 19, 3.0e-4: 20, 2.7e-4: 21, + 1.5e-4: 22, 1.0e-4: 23, 8.0e-5: 24, 3.0e-5: 25, 1.5e-5: 26, 3.0e-6: 27, 3.0e-7: 28, + 3.0e-8: 29, 3.0e-9: 30}) + + self.add_parameter('position', + set_cmd=self.set_position, + get_cmd=self.get_position, + get_parser=float, + label='Position') + + self.add_parameter('status', + get_cmd=self.get_status, + get_parser=int, + label='status') + + self.connect_message() + + # get methods + def get_position(self): + position = GetPosition() + self.libximc.get_position(self.device_id, ctypes.byref(position)) + return position.Position + + def get_status(self): + status = Status() + self.libximc.get_status(self.device_id, ctypes.byref(status)) + return status.MoveSts + + # set methods + def set_position(self, position): + self.libximc.command_move(self.device_id, int(position), 0) + + def set_transmittance(self, transmittance_id): + + # get filter to set + filter_wheel_1 = self.filter_wheel_1[transmittance_id] + filter_wheel_2 = self.filter_wheel_2[transmittance_id] + + # get current position + current_position = self.position.get() + + # determine new positions + position_wheel_2 = self.offset_wheel_2 + self.distance * filter_wheel_2 + \ + np.ceil(current_position / self.revolution + 2) * self.revolution + position_wheel_1 = self.offset_wheel_1 + self.distance * filter_wheel_1 + \ + np.ceil(current_position / self.revolution + 2) * self.revolution + print(position_wheel_1, position_wheel_2) + if position_wheel_1 > position_wheel_2: + print('new', position_wheel_1, position_wheel_2) + position_wheel_1 -= self.revolution + + # set position of the second wheel + self.position.set(int(position_wheel_2)) + time.sleep(1) + for i in range(100): + if self.status.get() == 0: + break + + # wait another time + time.sleep(10) + + # set position of the first wheel + self.position.set(int(position_wheel_1)) + time.sleep(1) + for i in range(100): + if self.status.get() == 0: + break + time.sleep(0.1) diff --git a/qcodes_contrib_drivers/drivers/Standa/__init__.py b/qcodes_contrib_drivers/drivers/Standa/__init__.py new file mode 100644 index 000000000..e69de29bb From 78e1f66d8e10d055b471052c81e272410bec998c Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Wed, 8 Jan 2020 13:34:20 +0100 Subject: [PATCH 2/7] Code format, minor changes --- ...mple with Standa 10MWA168.ipynb => Standa_10MWA168.ipynb} | 0 qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py | 5 +---- 2 files changed, 1 insertion(+), 4 deletions(-) rename docs/examples/{Qcodes example with Standa 10MWA168.ipynb => Standa_10MWA168.ipynb} (100%) diff --git a/docs/examples/Qcodes example with Standa 10MWA168.ipynb b/docs/examples/Standa_10MWA168.ipynb similarity index 100% rename from docs/examples/Qcodes example with Standa 10MWA168.ipynb rename to docs/examples/Standa_10MWA168.ipynb diff --git a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py index 853e23229..e9f586c46 100644 --- a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py +++ b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py @@ -52,7 +52,6 @@ class Status(ctypes.Structure): class libximc: - # TODO: use error check, implement wait for stop function from dll # default dll path @@ -63,7 +62,6 @@ class libximc: _error_codes = {-1: 'Error', -2: 'NotImplemented', -3: 'ValueError', -4: 'NoDevice'} def __init__(self, dll_path=None, verbose=False): - # save attributes self.verbose = verbose @@ -112,7 +110,6 @@ def open_device(self, device_name): class Standa_10MWA168(Instrument): def __init__(self, name, serial_number, dll_path=None, **kwargs): - super().__init__(name, **kwargs) # link to dll @@ -185,7 +182,7 @@ def set_transmittance(self, transmittance_id): np.ceil(current_position / self.revolution + 2) * self.revolution position_wheel_1 = self.offset_wheel_1 + self.distance * filter_wheel_1 + \ np.ceil(current_position / self.revolution + 2) * self.revolution - print(position_wheel_1, position_wheel_2) + if position_wheel_1 > position_wheel_2: print('new', position_wheel_1, position_wheel_2) position_wheel_1 -= self.revolution From a066664040a8f9d29cd1910c15f8a406c0a7c168 Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Tue, 14 Jan 2020 08:28:26 +0100 Subject: [PATCH 3/7] Error handling and code style --- docs/examples/Standa_10MWA168.ipynb | 2 +- .../drivers/Standa/Standa_10MWA168.py | 41 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/docs/examples/Standa_10MWA168.ipynb b/docs/examples/Standa_10MWA168.ipynb index 83414b1fe..44e6f24f9 100644 --- a/docs/examples/Standa_10MWA168.ipynb +++ b/docs/examples/Standa_10MWA168.ipynb @@ -18,7 +18,7 @@ }, "outputs": [], "source": [ - "from qcodes.instrument_drivers.Standa.Standa_10MWA168 import Standa_10MWA168" + "from qcodes_contrib_drivers.drivers.Standa.Standa_10MWA168 import Standa_10MWA168" ] }, { diff --git a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py index e9f586c46..c544768c0 100644 --- a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py +++ b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py @@ -67,9 +67,11 @@ def __init__(self, dll_path=None, verbose=False): # connect to the dll current_path = os.getcwd() - os.chdir(os.path.dirname(self._dll_path)) - self.dll = ctypes.windll.LoadLibrary(dll_path or self._dll_path) - os.chdir(current_path) + try: + os.chdir(os.path.dirname(self._dll_path)) + self.dll = ctypes.windll.LoadLibrary(dll_path or self._dll_path) + finally: + os.chdir(current_path) # set resource type self.dll.enumerate_devices.restype = ctypes.POINTER(DeviceInformation) @@ -129,9 +131,12 @@ def __init__(self, name, serial_number, dll_path=None, **kwargs): enumeration_name = self.libximc.get_device_name(device_enumeration, 0) self.device_id = self.libximc.open_device(enumeration_name) + # Time to wait (in seconds) between setting up wheel 1 and 2 + self.set_transmittance_sleep_time = 10.0 + # add parameters self.add_parameter('transmittance', - set_cmd=self.set_transmittance, + set_cmd=self._set_transmittance, label='Transmittance', val_mapping={ 1: 0, 0: 1, 9.0e-1: 2, 8.0e-1: 3, 7.2e-1: 4, 4.0e-1: 5, 3.0e-1: 6, 2.7e-1: 7, @@ -141,35 +146,34 @@ def __init__(self, name, serial_number, dll_path=None, **kwargs): 3.0e-8: 29, 3.0e-9: 30}) self.add_parameter('position', - set_cmd=self.set_position, - get_cmd=self.get_position, + set_cmd=self._set_position, + get_cmd=self._get_position, get_parser=float, label='Position') self.add_parameter('status', - get_cmd=self.get_status, + get_cmd=self._get_status, get_parser=int, label='status') self.connect_message() # get methods - def get_position(self): + def _get_position(self): position = GetPosition() self.libximc.get_position(self.device_id, ctypes.byref(position)) return position.Position - def get_status(self): + def _get_status(self): status = Status() self.libximc.get_status(self.device_id, ctypes.byref(status)) return status.MoveSts # set methods - def set_position(self, position): + def _set_position(self, position): self.libximc.command_move(self.device_id, int(position), 0) - def set_transmittance(self, transmittance_id): - + def _set_transmittance(self, transmittance_id): # get filter to set filter_wheel_1 = self.filter_wheel_1[transmittance_id] filter_wheel_2 = self.filter_wheel_2[transmittance_id] @@ -188,19 +192,20 @@ def set_transmittance(self, transmittance_id): position_wheel_1 -= self.revolution # set position of the second wheel - self.position.set(int(position_wheel_2)) - time.sleep(1) + self.position.set(np.floor(position_wheel_2)) + time.sleep(self.sleep_time / 10.0) # default: 1 s for i in range(100): if self.status.get() == 0: break # wait another time - time.sleep(10) + time.sleep(self.sleep_time) # default: 10 s # set position of the first wheel - self.position.set(int(position_wheel_1)) - time.sleep(1) + self.position.set(np.floor(position_wheel_1)) + time.sleep(self.sleep_time / 10.0) # default: 1 s for i in range(100): if self.status.get() == 0: break - time.sleep(0.1) + + time.sleep(self.sleep_time / 100.0) # default: 0.1 s From a559d49d2ed7d76e81bd8e8bba7e512650e84d79 Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Wed, 15 Jan 2020 08:20:45 +0100 Subject: [PATCH 4/7] Add never-execute-flag to ipynb --- docs/examples/Standa_10MWA168.ipynb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/examples/Standa_10MWA168.ipynb b/docs/examples/Standa_10MWA168.ipynb index 44e6f24f9..ae32ba7e2 100644 --- a/docs/examples/Standa_10MWA168.ipynb +++ b/docs/examples/Standa_10MWA168.ipynb @@ -121,5 +121,8 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 2, + "nbsphinx": { + "execute": "never" + } } From 398da6fae1360119022cec634634be5619e0f603 Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Wed, 15 Jan 2020 08:33:45 +0100 Subject: [PATCH 5/7] Add never-execute-flag to ipynb --- docs/examples/Standa_10MWA168.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/examples/Standa_10MWA168.ipynb b/docs/examples/Standa_10MWA168.ipynb index ae32ba7e2..f4481949c 100644 --- a/docs/examples/Standa_10MWA168.ipynb +++ b/docs/examples/Standa_10MWA168.ipynb @@ -118,11 +118,11 @@ "_Feature" ], "window_display": false + }, + "nbsphinx": { + "execute": "never" } }, "nbformat": 4, - "nbformat_minor": 2, - "nbsphinx": { - "execute": "never" - } + "nbformat_minor": 2 } From 8f78cc74c9c5a1eb876db278264b890f50e8f1d9 Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Mon, 20 Jan 2020 15:43:53 +0100 Subject: [PATCH 6/7] Removed unused class DeviceEnumeration --- qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py index c544768c0..1217d81c2 100644 --- a/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py +++ b/qcodes_contrib_drivers/drivers/Standa/Standa_10MWA168.py @@ -5,10 +5,6 @@ import time -class DeviceEnumeration(ctypes.BigEndianStructure): - pass - - class DeviceInformation(ctypes.Structure): _fields_ = [ ("Manufacturer", ctypes.c_char * 5), From 08178acf6b0ca69a48d22b7ebc63fe031a02bca3 Mon Sep 17 00:00:00 2001 From: "Lukas.Lankes" Date: Mon, 20 Jan 2020 15:55:49 +0100 Subject: [PATCH 7/7] Added "_ctypes.Structure" to nitpick_ignore --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 5db0c87c3..7709112d6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -139,5 +139,6 @@ ('py:class', 'SPI_rack'), ('py:class', 'unittest.case.TestCase'), ('py:class', 'builtins.AssertionError'), + ('py:class', '_ctypes.Structure'), ('py:exc', 'visa.VisaIOError') ]