From 6fcabb54a5a41d89a66e217b1d41a954bdd53656 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 19 Apr 2017 09:37:46 -0400 Subject: [PATCH 001/108] Freeze pylint to version 1.6.5 --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 7418bc754..c42f8689e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ mock nose -pylint +pylint==1.6.5 From 065d3d23d78ae2a91ed452d4610fb7607a5471fb Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Thu, 20 Apr 2017 23:24:15 +1000 Subject: [PATCH 002/108] Extended YAML (#164) * Added test:// URI schema. * Config: Py3 fix and support for setting attrs. * Added sub/indexing to attrs config. * Added to docstring. * Added support for physical quantities to config. * pylint fixes * Tests for new setattr_expression * Added tests for new config, YAML. * pylint fix for new tests * Very minor pylint fix * Added ruamel.yaml as testing dep. * Reworked awkward sentence. * Updated docs for ruamel.yaml dep. * Remove pyyaml, only use ruamel.yaml * Disabled info category messages from mylint. * Added version dump to travis config. * Trying ot use python -m to work around venv issues. * Revert "Trying ot use python -m to work around venv issues." This reverts commit f73a2439116ba89b867893dbcfe00f1982e03694. * ruamel.yaml vs ruamel_yaml and fixing pylint false +ve. * Explicitly use unsafe loader as suggested by ruamel.yaml warnings. * Marked test as explicitly unsafe as well. --- .travis.yml | 15 +++- doc/source/intro.rst | 6 +- .../abstract_instruments/instrument.py | 6 +- instruments/config.py | 76 ++++++++++++++++--- instruments/tests/test_config.py | 64 ++++++++++++++++ instruments/tests/test_util_fns.py | 45 ++++++++++- instruments/util_fns.py | 34 ++++++++- requirements.txt | 2 +- setup.py | 2 +- 9 files changed, 230 insertions(+), 20 deletions(-) create mode 100644 instruments/tests/test_config.py diff --git a/.travis.yml b/.travis.yml index 7cbb7d02b..ed6d2479f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,21 @@ install: - "pip install -r dev-requirements.txt" - pip install python-coveralls - pip install coverage +before_script: + # We use before_script to report version and path information in a way + # that can be easily hidden by Travis' log folding. Moreover, a nonzero + # exit code from this block kills the entire job, meaning that if we can't + # even sensibly get version information, we correctly abort. + - which python + - python --version + - which nosetests + - nosetests --version + - which pylint + - pylint --version script: - nosetests --with-coverage -w instruments - - pylint --py3k instruments/ - - pylint instruments/ + - pylint --py3k instruments + - pylint --disable=I instruments after_success: - coveralls deploy: diff --git a/doc/source/intro.rst b/doc/source/intro.rst index bc8c18ac8..733a33c44 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -33,9 +33,9 @@ $ pip install -r requirements.txt - `enum34`_ - `future`_ - `python-vxi11`_ -- `PyUSB`_ +- `PyUSB`_ (version 1.0a or higher, required for raw USB support) - `python-usbtmc`_ -- `PyYAML`_ +- `ruamel.yaml`_ (required for configuration file support) Optional Dependencies ~~~~~~~~~~~~~~~~~~~~~ @@ -46,7 +46,7 @@ Optional Dependencies .. _quantities: http://pythonhosted.org/quantities/ .. _enum34: https://pypi.python.org/pypi/enum34 .. _future: https://pypi.python.org/pypi/future -.. _PyYAML: https://bitbucket.org/xi/pyyaml +.. _ruamel.yaml: http://yaml.readthedocs.io .. _PyUSB: http://sourceforge.net/apps/trac/pyusb/ .. _PyVISA: http://pyvisa.sourceforge.net/ .. _python-usbtmc: https://pypi.python.org/pypi/python-usbtmc diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 0e791142f..7163f40f0 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -310,7 +310,8 @@ def binblockread(self, data_width, fmt=None): # CLASS METHODS # URI_SCHEMES = ["serial", "tcpip", "gpib+usb", - "gpib+serial", "visa", "file", "usbtmc", "vxi11"] + "gpib+serial", "visa", "file", "usbtmc", "vxi11", + "test"] @classmethod def open_from_uri(cls, uri): @@ -330,6 +331,7 @@ def open_from_uri(cls, uri): gpib+serial:///dev/ttyACM0/15 # Currently non-functional. visa://USB::0x0699::0x0401::C0000001::0::INSTR usbtmc://USB::0x0699::0x0401::C0000001::0::INSTR + test:// For the ``serial`` URI scheme, baud rates may be explicitly specified using the query parameter ``baud=``, as in the example @@ -415,6 +417,8 @@ def open_from_uri(cls, uri): # vxi11://192.168.1.104 # vxi11://TCPIP::192.168.1.105::gpib,5::INSTR return cls.open_vxi11(parsed_uri.netloc, **kwargs) + elif parsed_uri.scheme == "test": + return cls.open_test(**kwargs) else: raise NotImplementedError("Invalid scheme or not yet " "implemented.") diff --git a/instruments/config.py b/instruments/config.py index 74d09499b..107daa157 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -12,9 +12,22 @@ import warnings try: - import yaml + import ruamel.yaml as yaml except ImportError: - yaml = None + # Some versions of ruamel.yaml are named ruamel_yaml, so try that + # too. + # + # In either case, we've observed issues with pylint where it will raise + # a false positive from its import-error checker, so we locally disable + # it here. Once the cause for the false positive has been identified, + # the import-error check should be re-enabled. + import ruamel_yaml as yaml # pylint: disable=import-error + +import quantities as pq + +from future.builtins import str + +from instruments.util_fns import setattr_expression, split_unit_str # FUNCTIONS ################################################################### @@ -37,8 +50,9 @@ def walk_dict(d, path): # Treat as a base case that the path is empty. if not path: return d - if isinstance(path, str): + if not isinstance(path, list): path = path.split("/") + if not path[0]: # If the first part of the path is empty, do nothing. return walk_dict(d, path[1:]) @@ -46,6 +60,18 @@ def walk_dict(d, path): # Otherwise, resolve that segment and recurse. return walk_dict(d[path[0]], path[1:]) +def quantity_constructor(loader, node): + """ + Constructs a `pq.Quantity` instance from a PyYAML + node tagged as ``!Q``. + """ + # Follows the example of http://stackoverflow.com/a/43081967/267841. + value = loader.construct_scalar(node) + return pq.Quantity(*split_unit_str(value)) + +# We avoid having to register !Q every time by doing as soon as the +# relevant constructor is defined. +yaml.add_constructor(u'!Q', quantity_constructor) def load_instruments(conf_file_name, conf_path="/"): """ @@ -63,6 +89,28 @@ def load_instruments(conf_file_name, conf_path="/"): the form ``{'ddg': instruments.srs.SRSDG645.open_from_uri('gpib+usb://COM7/15')}``. + Each instrument configuration section can also specify one or more attributes + to set. These attributes are specified using a ``attrs`` section as well as the + required ``class`` and ``uri`` sections. For instance, the following + dictionary creates a ThorLabs APT motor controller instrument with a single motor + model configured:: + + rot_stage: + class: !!python/name:instruments.thorabsapt.APTMotorController + uri: serial:///dev/ttyUSB0?baud=115200 + attrs: + channel[0].motor_model: PRM1-Z8 + + Unitful attributes can be specified by using the ``!Q`` tag to quickly create + instances of `pq.Quantity`. In the example above, for instance, we can set a motion + timeout as a unitful quantity:: + + attrs: + motion_timeout: !Q 1 minute + + When using the ``!Q`` tag, any text before a space is taken to be the magnitude + of the quantity, and text following is taken to be the unit specification. + By specifying a path within the configuration file, one can load only a part of the given file. For instance, consider the configuration:: @@ -78,7 +126,7 @@ def load_instruments(conf_file_name, conf_path="/"): all other keys in the YAML file. :param str conf_file_name: Name of the configuration file to load - instruments from. + instruments from. Alternatively, a file-like object may be provided. :param str conf_path: ``"/"`` separated path to the section in the configuration file to load. @@ -95,23 +143,33 @@ def load_instruments(conf_file_name, conf_path="/"): """ if yaml is None: - raise ImportError("Could not import PyYAML, which is required " + raise ImportError("Could not import ruamel.yaml, which is required " "for this function.") - with open(conf_file_name, 'r') as f: - conf_dict = yaml.load(f) + if isinstance(conf_file_name, str): + with open(conf_file_name, 'r') as f: + conf_dict = yaml.load(f, Loader=yaml.Loader) + else: + conf_dict = yaml.load(conf_file_name, Loader=yaml.Loader) conf_dict = walk_dict(conf_dict, conf_path) inst_dict = {} - for name, value in conf_dict.iteritems(): + for name, value in conf_dict.items(): try: inst_dict[name] = value["class"].open_from_uri(value["uri"]) + + if 'attrs' in value: + # We have some attrs we can set on the newly created instrument. + for attr_name, attr_value in value['attrs'].items(): + setattr_expression(inst_dict[name], attr_name, attr_value) + except IOError as ex: # FIXME: need to subclass Warning so that repeated warnings # aren't ignored. - warnings.warn("Exception occured loading device URI " + warnings.warn("Exception occured loading device with URI " "{}:\n\t{}.".format(value["uri"], ex), RuntimeWarning) inst_dict[name] = None + return inst_dict diff --git a/instruments/tests/test_config.py b/instruments/tests/test_config.py new file mode 100644 index 000000000..6a2119fa5 --- /dev/null +++ b/instruments/tests/test_config.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for util_fns.py +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import, unicode_literals + +from io import StringIO + +import quantities as pq + +from instruments import Instrument +from instruments.config import ( + load_instruments, yaml +) + +# TEST CASES ################################################################# + +# pylint: disable=protected-access,missing-docstring + +def test_load_test_instrument(): + config_data = StringIO(u""" +test: + class: !!python/name:instruments.Instrument + uri: test:// +""") + insts = load_instruments(config_data) + assert isinstance(insts['test'], Instrument) + +def test_load_test_instrument_subtree(): + config_data = StringIO(u""" +instruments: + test: + class: !!python/name:instruments.Instrument + uri: test:// +""") + insts = load_instruments(config_data, conf_path="/instruments") + assert isinstance(insts['test'], Instrument) + +def test_yaml_quantity_tag(): + yaml_data = StringIO(u""" +a: + b: !Q 37 tesla + c: !Q 41.2 inches + d: !Q 98 +""") + data = yaml.load(yaml_data, Loader=yaml.Loader) + assert data['a']['b'] == pq.Quantity(37, 'tesla') + assert data['a']['c'] == pq.Quantity(41.2, 'inches') + assert data['a']['d'] == 98 + +def test_load_test_instrument_setattr(): + config_data = StringIO(u""" +test: + class: !!python/name:instruments.Instrument + uri: test:// + attrs: + foo: !Q 111 GHz +""") + insts = load_instruments(config_data) + assert insts['test'].foo == pq.Quantity(111, 'GHz') diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index 914372404..ecf8e6ec5 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -16,7 +16,8 @@ from instruments.util_fns import ( ProxyList, - assume_units, convert_temperature + assume_units, convert_temperature, + setattr_expression ) # TEST CASES ################################################################# @@ -169,3 +170,45 @@ def test_temperater_conversion_failure(): @raises(ValueError) def test_assume_units_failures(): assume_units(1, 'm').rescale('s') + +def test_setattr_expression_simple(): + class A(object): + x = 'x' + y = 'y' + z = 'z' + + a = A() + setattr_expression(a, 'x', 'foo') + assert a.x == 'foo' + +def test_setattr_expression_index(): + class A(object): + x = ['x', 'y', 'z'] + + a = A() + setattr_expression(a, 'x[1]', 'foo') + assert a.x[1] == 'foo' + +def test_setattr_expression_nested(): + class B(object): + x = 'x' + class A(object): + b = None + def __init__(self): + self.b = B() + + a = A() + setattr_expression(a, 'b.x', 'foo') + assert a.b.x == 'foo' + +def test_setattr_expression_both(): + class B(object): + x = 'x' + class A(object): + b = None + def __init__(self): + self.b = [B()] + + a = A() + setattr_expression(a, 'b[0].x', 'foo') + assert a.b[0].x == 'foo' diff --git a/instruments/util_fns.py b/instruments/util_fns.py index 316343fc9..977bfdcf3 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -14,11 +14,13 @@ from enum import Enum, IntEnum import quantities as pq -# FUNCTIONS ################################################################### +# CONSTANTS ################################################################### -# pylint: disable=too-many-arguments +_IDX_REGEX = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*)\[(-?[0-9]*)\]') +# FUNCTIONS ################################################################### +# pylint: disable=too-many-arguments def assume_units(value, units): """ If units are not provided for ``value`` (that is, if it is a raw @@ -37,6 +39,34 @@ def assume_units(value, units): value = pq.Quantity(value, units) return value +def setattr_expression(target, name_expr, value): + """ + Recursively calls getattr/setattr for attribute + names that are miniature expressions with subscripting. + For instance, of the form ``a[0].b``. + """ + # Allow "." in attribute names so that we can set attributes + # recursively. + if '.' in name_expr: + # Recursion: We have to strip off a level of getattr. + head, name_expr = name_expr.split('.', 1) + match = _IDX_REGEX.match(head) + if match: + head_name, head_idx = match.groups() + target = getattr(target, head_name)[int(head_idx)] + else: + target = getattr(target, head) + + setattr_expression(target, name_expr, value) + else: + # Base case: We're in the last part of a dot-expression. + match = _IDX_REGEX.match(name_expr) + if match: + name, idx = match.groups() + getattr(target, name)[int(idx)] = value + else: + setattr(target, name_expr, value) + def convert_temperature(temperature, base): """ diff --git a/requirements.txt b/requirements.txt index 113c7a6a3..622bb14d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,4 @@ enum34 python-vxi11>=0.8 pyusb python-usbtmc -pyyaml +ruamel.yaml diff --git a/setup.py b/setup.py index c928c9608..b422efd4b 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ "python-vxi11", "python-usbtmc", "pyusb", - "pyyaml" + "ruamel.yaml" ] EXTRAS_REQUIRE = { 'VISA': ["pyvisa"] From 6c6dbb498ad38abd0310f7e4a894d329f6e60280 Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Wed, 7 Jun 2017 07:34:05 +1000 Subject: [PATCH 003/108] Fixes for pylint version 1.7 (#166) * Started fixing new pylint 1.7 checks. * More pylint17 fixes. * Removed flags that are now disabled by default. * Still more misc pylint17 fixes. * Updated pylint freeze to 1.7. * Guess what? Still more misc fixes. * More fixes. * pylint17 fixes nearly done * Should be the last fixes for 1.7. * pylint 1.7 fix * Fix parameter names * Update pylint 1.7->1.7.1 * Cleanup thorlabs_utils.py --- dev-requirements.txt | 2 +- .../comm/file_communicator.py | 4 +-- .../comm/gi_gpib_communicator.py | 7 ++--- .../comm/usbtmc_communicator.py | 4 +-- .../function_generator.py | 5 ++-- .../abstract_instruments/oscilloscope.py | 6 ++-- instruments/agilent/agilent33220a.py | 3 -- instruments/agilent/agilent34410a.py | 3 -- instruments/config.py | 6 ++-- .../generic_scpi/scpi_function_generator.py | 3 -- instruments/generic_scpi/scpi_instrument.py | 3 -- instruments/generic_scpi/scpi_multimeter.py | 5 +--- instruments/hp/hp6632b.py | 3 -- instruments/hp/hp6652a.py | 3 -- instruments/keithley/keithley195.py | 10 +++---- instruments/keithley/keithley2182.py | 3 -- instruments/keithley/keithley580.py | 8 ++--- instruments/ondax/lm.py | 4 +-- instruments/qubitekk/cc1.py | 4 +-- instruments/rigol/rigolds1000.py | 3 -- instruments/srs/srs830.py | 4 +-- instruments/srs/srsctc100.py | 2 +- instruments/tektronix/tekdpo4104.py | 10 ++++--- instruments/tektronix/tekdpo70000.py | 3 -- instruments/tektronix/tektds224.py | 4 +-- instruments/tektronix/tektds5xx.py | 11 +++---- instruments/tests/test_comm/test_file.py | 2 +- .../tests/test_comm/test_gi_gpibusb.py | 2 +- instruments/tests/test_comm/test_loopback.py | 2 +- instruments/tests/test_comm/test_socket.py | 2 +- instruments/tests/test_comm/test_usbtmc.py | 2 +- .../tests/test_qubitekk/test_qubitekk_cc1.py | 4 +-- instruments/tests/test_srs/test_srs830.py | 2 +- instruments/thorlabs/_packets.py | 10 +++---- instruments/thorlabs/tc200.py | 4 +-- instruments/thorlabs/thorlabs_utils.py | 30 ++++--------------- instruments/toptica/topmode.py | 6 ++-- instruments/toptica/toptica_utils.py | 4 +-- instruments/util_fns.py | 17 ++++++----- instruments/yokogawa/yokogawa7651.py | 3 -- 40 files changed, 80 insertions(+), 133 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c42f8689e..419748e1c 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ mock nose -pylint==1.6.5 +pylint==1.7.1 diff --git a/instruments/abstract_instruments/comm/file_communicator.py b/instruments/abstract_instruments/comm/file_communicator.py index b6022e7ce..46245660e 100644 --- a/instruments/abstract_instruments/comm/file_communicator.py +++ b/instruments/abstract_instruments/comm/file_communicator.py @@ -60,8 +60,8 @@ def address(self): """ if hasattr(self._filelike, 'name'): return self._filelike.name - else: - return None + + return None @address.setter def address(self, newval): diff --git a/instruments/abstract_instruments/comm/gi_gpib_communicator.py b/instruments/abstract_instruments/comm/gi_gpib_communicator.py index ab686bb15..3123059d7 100644 --- a/instruments/abstract_instruments/comm/gi_gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gi_gpib_communicator.py @@ -48,7 +48,7 @@ def __init__(self, filelike, gpib_address): if self._version <= 4: self._eos = 10 else: - self._eos = "\n" # pylint: disable=redefined-variable-type + self._eos = "\n" # PROPERTIES # @@ -117,8 +117,8 @@ def terminator(self): """ if not self._eoi: return self._terminator - else: - return 'eoi' + + return 'eoi' @terminator.setter def terminator(self, newval): @@ -203,7 +203,6 @@ def eos(self): @eos.setter def eos(self, newval): - # pylint: disable=redefined-variable-type if self._version <= 4: if isinstance(newval, (str, bytes)): newval = ord(newval) diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 2f96194f6..f465abba7 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -43,8 +43,8 @@ def __init__(self, *args, **kwargs): def address(self): if hasattr(self._filelike, "name"): return id(self._filelike) # TODO: replace with something more useful. - else: - return None + + return None @property def terminator(self): diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index ffe73f3aa..e9baa64be 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -153,8 +153,8 @@ def amplitude(self): if units == self.VoltageMode.dBm: return pq.Quantity(mag, u.dBm) - else: - return pq.Quantity(mag, pq.V), units + + return pq.Quantity(mag, pq.V), units @amplitude.setter def amplitude(self, newval): @@ -168,7 +168,6 @@ def amplitude(self, newval): # OK, we have volts. Now, do we have a tuple? If not, assume Vpp. if not isinstance(newval, tuple): mag = newval - # pylint: disable=redefined-variable-type units = self.VoltageMode.peak_to_peak else: mag, units = newval diff --git a/instruments/abstract_instruments/oscilloscope.py b/instruments/abstract_instruments/oscilloscope.py index 960f28b28..16fa662ee 100644 --- a/instruments/abstract_instruments/oscilloscope.py +++ b/instruments/abstract_instruments/oscilloscope.py @@ -49,8 +49,10 @@ def __exit__(self, type, value, traceback): def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented - else: - return other.name == self.name + + return other.name == self.name + + __hash__ = None # PROPERTIES # diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 7a2c56b7c..5a24ea9ad 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -43,9 +43,6 @@ class Agilent33220a(SCPIFunctionGenerator): """ - def __init__(self, filelike): - super(Agilent33220a, self).__init__(filelike) - # ENUMS # class Function(Enum): diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 329ad8abf..8c444f552 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -35,9 +35,6 @@ class Agilent34410a(SCPIMultimeter): # pylint: disable=abstract-method .. _Keysight website: http://www.keysight.com/ """ - def __init__(self, filelike): - super(Agilent34410a, self).__init__(filelike) - # PROPERTIES # @property diff --git a/instruments/config.py b/instruments/config.py index 107daa157..2e6d7b6eb 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -56,9 +56,9 @@ def walk_dict(d, path): if not path[0]: # If the first part of the path is empty, do nothing. return walk_dict(d, path[1:]) - else: - # Otherwise, resolve that segment and recurse. - return walk_dict(d[path[0]], path[1:]) + + # Otherwise, resolve that segment and recurse. + return walk_dict(d[path[0]], path[1:]) def quantity_constructor(loader, node): """ diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index fcad54c04..66630006a 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -32,9 +32,6 @@ class SCPIFunctionGenerator(FunctionGenerator, SCPIInstrument): >>> inst.frequency = 1 * pq.kHz """ - def __init__(self, filelike): - super(SCPIFunctionGenerator, self).__init__(filelike) - # CONSTANTS # _UNIT_MNEMONICS = { diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index 30d16f670..5f4f0d93b 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -37,9 +37,6 @@ class SCPIInstrument(Instrument): >>> print(inst.name) """ - def __init__(self, filelike): - super(SCPIInstrument, self).__init__(filelike) - # PROPERTIES # @property diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index 213d9b3d7..b6cc062fd 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -45,9 +45,6 @@ class SCPIMultimeter(SCPIInstrument, Multimeter): >>> print(inst.measure(inst.Mode.resistance)) """ - def __init__(self, filelike): - super(SCPIMultimeter, self).__init__(filelike) - # ENUMS ## class Mode(Enum): @@ -142,7 +139,7 @@ class SampleSource(Enum): # PROPERTIES ## - # pylint: disable=unnecessary-lambda + # pylint: disable=unnecessary-lambda,undefined-variable mode = enum_property( name="CONF", enum=Mode, diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index 635ca53cd..b07407293 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -77,9 +77,6 @@ class HP6632b(SCPIInstrument, HP6652a): array(10.0) * V """ - def __init__(self, filelike): - super(HP6632b, self).__init__(filelike) - # ENUMS ## class ALCBandwidth(IntEnum): diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index 38e6bac40..f5d88ce03 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -55,9 +55,6 @@ class HP6652a(PowerSupply, PowerSupplyChannel): >>> psu.display_textmode=False """ - def __init__(self, filelike): - super(HP6652a, self).__init__(filelike) - # ENUMS ## # I don't know of any possible enumerations supported diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index 5bf00b06f..620be342b 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -195,11 +195,11 @@ def input_range(self): index = self.parse_status_word(self.get_status_word())['range'] if index == 0: return 'auto' - else: - mode = self.mode - value = Keithley195.ValidRange[mode.name].value[index - 1] - units = UNITS2[mode] - return value * units + + mode = self.mode + value = Keithley195.ValidRange[mode.name].value[index - 1] + units = UNITS2[mode] + return value * units @input_range.setter def input_range(self, newval): diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index 6a4873b06..8e3e245b1 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -35,9 +35,6 @@ class Keithley2182(SCPIMultimeter): """ - def __init__(self, filelike): - super(Keithley2182, self).__init__(filelike) - # INNER CLASSES # class Channel(Multimeter): diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index d8336ebbd..5ed6bbd26 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -463,8 +463,8 @@ def parse_measurement(measurement): # COMMUNICATOR METHODS # - def sendcmd(self, msg): - super(Keithley580, self).sendcmd(msg + ':') + def sendcmd(self, cmd): + super(Keithley580, self).sendcmd(cmd + ':') - def query(self, msg, size=-1): - return super(Keithley580, self).query(msg + ':', size)[:-1] + def query(self, cmd, size=-1): + return super(Keithley580, self).query(cmd + ':', size)[:-1] diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index 11070253a..0f4dfbfa3 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -411,8 +411,8 @@ def enabled(self, newval): def _ack_expected(self, msg=""): if msg.find("?") > 0: return None - else: - return "OK" + + return "OK" @property def firmware(self): diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index b6074755a..e2c785b31 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -275,8 +275,8 @@ def dwell_time(self): dwell_time = pq.Quantity(*split_unit_str(self.query("DWEL?"), "s")) if self.firmware[0] <= 2 and self.firmware[1] <= 1: return dwell_time/1000.0 - else: - return dwell_time + + return dwell_time @dwell_time.setter def dwell_time(self, new_val): diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 4f23ae5e5..91acc4777 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -60,9 +60,6 @@ class DataSource(OscilloscopeDataSource): is designed to be initialized by the `RigolDS1000Series` class. """ - def __init__(self, parent, name): - super(RigolDS1000Series.DataSource, self).__init__(parent, name) - @property def name(self): return self._name diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index f533d0b9e..93f195c43 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -61,9 +61,9 @@ def __init__(self, filelike, outx_mode=None): # pragma: no cover will be sent depending on what type of communicator self._file is. """ super(SRS830, self).__init__(filelike) - if outx_mode is 1: + if outx_mode == 1: self.sendcmd("OUTX 1") - elif outx_mode is 2: + elif outx_mode == 2: self.sendcmd("OUTX 2") else: if isinstance(self._file, GPIBCommunicator): diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 827b3ee90..3042ebc5b 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -168,7 +168,7 @@ def stats_enabled(self): :type: `bool` """ - return True if self._get('stats') is 'On' else False + return True if self._get('stats') == 'On' else False @stats_enabled.setter def stats_enabled(self, newval): diff --git a/instruments/tektronix/tekdpo4104.py b/instruments/tektronix/tekdpo4104.py index e770e70b6..0baea2240 100644 --- a/instruments/tektronix/tekdpo4104.py +++ b/instruments/tektronix/tekdpo4104.py @@ -83,8 +83,10 @@ def __exit__(self, type, value, traceback): def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented - else: - return other.name == self.name + + return other.name == self.name + + __hash__ = None def read_waveform(self, bin_format=True): """ @@ -259,8 +261,8 @@ def data_source(self): name = self.query("DAT:SOU?") if name.startswith("CH"): return _TekDPO4104Channel(self, int(name[2:]) - 1) - else: - return _TekDPO4104DataSource(self, name) + + return _TekDPO4104DataSource(self, name) @data_source.setter def data_source(self, newval): diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index 3c4c7664a..a70767b7c 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -172,9 +172,6 @@ class DataSource(OscilloscopeDataSource): is designed to be initialized by the `TekDPO70000` class. """ - def __init__(self, parent, name): - super(TekDPO70000.DataSource, self).__init__(parent, name) - @property def name(self): return self._name diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index ba7238d50..e5574da80 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -227,8 +227,8 @@ def data_source(self): name = self.query("DAT:SOU?") if name.startswith("CH"): return _TekTDS224Channel(self, int(name[2:]) - 1) - else: - return _TekTDS224DataSource(self, name) + + return _TekTDS224DataSource(self, name) @data_source.setter def data_source(self, newval): diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index d41c930c3..91bf28cfa 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -83,8 +83,8 @@ def read(self): resp = self._tek.query('MEASU:MEAS{}:VAL?'.format(self._id)) self._data['value'] = float(resp) return self._data - else: - return self._data + + return self._data class _TekTDS5xxDataSource(OscilloscopeDataSource): @@ -97,9 +97,6 @@ class _TekTDS5xxDataSource(OscilloscopeDataSource): designed to be initialized by the `TekTDS5xx` class. """ - def __init__(self, parent, name): - super(_TekTDS5xxDataSource, self).__init__(parent, name) - @property def name(self): """ @@ -434,8 +431,8 @@ def data_source(self): name = self.query("DAT:SOU?") if name.startswith("CH"): return _TekTDS5xxChannel(self, int(name[2:]) - 1) - else: - return _TekTDS5xxDataSource(self, name) + + return _TekTDS5xxDataSource(self, name) @data_source.setter def data_source(self, newval): diff --git a/instruments/tests/test_comm/test_file.py b/instruments/tests/test_comm/test_file.py index fcd242077..fde974caf 100644 --- a/instruments/tests/test_comm/test_file.py +++ b/instruments/tests/test_comm/test_file.py @@ -60,7 +60,7 @@ def test_filecomm_terminator(): comm.terminator = "*" eq_(comm._terminator, "*") - comm.terminator = b"*" # pylint: disable=redefined-variable-type + comm.terminator = b"*" eq_(comm._terminator, "*") diff --git a/instruments/tests/test_comm/test_gi_gpibusb.py b/instruments/tests/test_comm/test_gi_gpibusb.py index bee107953..263844e3e 100644 --- a/instruments/tests/test_comm/test_gi_gpibusb.py +++ b/instruments/tests/test_comm/test_gi_gpibusb.py @@ -66,7 +66,7 @@ def test_gpibusbcomm_address(): eq_(comm._gpib_address, 5) # Able to set address with a list - comm.address = [6, "/dev/foobar"] # pylint: disable=redefined-variable-type + comm.address = [6, "/dev/foobar"] eq_(comm._gpib_address, 6) port_name.assert_called_with("/dev/foobar") diff --git a/instruments/tests/test_comm/test_loopback.py b/instruments/tests/test_comm/test_loopback.py index 66e5aae5d..eaa9e796c 100644 --- a/instruments/tests/test_comm/test_loopback.py +++ b/instruments/tests/test_comm/test_loopback.py @@ -48,7 +48,7 @@ def test_loopbackcomm_terminator(): eq_(comm.terminator, "*") eq_(comm._terminator, "*") - comm.terminator = u"\r" # pylint: disable=redefined-variable-type + comm.terminator = u"\r" eq_(comm.terminator, u"\r") eq_(comm._terminator, u"\r") diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index a08c39660..05307f946 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -59,7 +59,7 @@ def test_socketcomm_terminator(): eq_(comm.terminator, "*") eq_(comm._terminator, "*") - comm.terminator = u"\r" # pylint: disable=redefined-variable-type + comm.terminator = u"\r" eq_(comm.terminator, u"\r") eq_(comm._terminator, u"\r") diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index 826ccecec..18e2ebcf7 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -58,7 +58,7 @@ def test_usbtmccomm_terminator_setter(mock_usbtmc): eq_(comm._terminator, "*") term_char.assert_called_with(42) - comm.terminator = b"*" # pylint: disable=redefined-variable-type + comm.terminator = b"*" eq_(comm._terminator, "*") term_char.assert_called_with(42) diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index e5d366fbc..cdbe9b499 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -364,7 +364,7 @@ def test_cc1_subtract_error(): cc.subtract = "blo" -def test_cc1_trigger_mode(): # pylint: disable=redefined-variable-type +def test_cc1_trigger_mode(): with expected_protocol( ik.qubitekk.CC1, [ @@ -386,7 +386,7 @@ def test_cc1_trigger_mode(): # pylint: disable=redefined-variable-type cc.trigger_mode = cc.TriggerMode.start_stop -def test_cc1_trigger_mode_old_firmware(): # pylint: disable=redefined-variable-type +def test_cc1_trigger_mode_old_firmware(): with expected_protocol( ik.qubitekk.CC1, [ diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index d94128726..57a612c08 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -125,7 +125,7 @@ def test_sample_rate(): # sends index of VALID_SAMPLE_RATES assert inst.sample_rate == 16 * pq.Hz assert inst.sample_rate == "trigger" inst.sample_rate = 2 - inst.sample_rate = "trigger" # pylint: disable=redefined-variable-type + inst.sample_rate = "trigger" @raises(ValueError) diff --git a/instruments/thorlabs/_packets.py b/instruments/thorlabs/_packets.py index 83c6decb9..7a9ef8ded 100644 --- a/instruments/thorlabs/_packets.py +++ b/instruments/thorlabs/_packets.py @@ -138,11 +138,11 @@ def pack(self): self._message_id, len( self._data), 0x80 | self._dest, self._source ) + self._data - else: - return message_header_nopacket.pack( - self._message_id, self._param1, self._param2, self._dest, - self._source - ) + + return message_header_nopacket.pack( + self._message_id, self._param1, self._param2, self._dest, + self._source + ) @classmethod def unpack(cls, bytes): diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index 0589de9ee..4478c80d8 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -292,8 +292,8 @@ def degrees(self): return pq.degC elif (response >> 5) % 2: return pq.degK - else: - return pq.degF + + return pq.degF @degrees.setter def degrees(self, newval): diff --git a/instruments/thorlabs/thorlabs_utils.py b/instruments/thorlabs/thorlabs_utils.py index d5514d0a6..5969afa1d 100644 --- a/instruments/thorlabs/thorlabs_utils.py +++ b/instruments/thorlabs/thorlabs_utils.py @@ -1,26 +1,5 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# -# thorlabs_utils.py: Utility functions for Thorlabs-brand instruments. -# -# © 2016 Steven Casagrande (scasagrande@galvant.ca). -# -# This file is a part of the InstrumentKit project. -# Licensed under the AGPL version 3. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# """ Contains common utility functions for Thorlabs-brand instruments """ @@ -35,7 +14,8 @@ def check_cmd(response): :return: 1 if not found, 0 otherwise :rtype: int """ - if response != "CMD_NOT_DEFINED" and response != "CMD_ARG_INVALID": - return 1 - else: - return 0 + return ( + 1 + if response != "CMD_NOT_DEFINED" and response != "CMD_ARG_INVALID" + else 0 + ) diff --git a/instruments/toptica/topmode.py b/instruments/toptica/topmode.py index e50607b84..b12978a40 100644 --- a/instruments/toptica/topmode.py +++ b/instruments/toptica/topmode.py @@ -48,8 +48,8 @@ def _ack_expected(self, msg=""): return [msg, "reboot process started."] elif "start-correction" in msg: return [msg, "()"] - else: - return msg + + return msg # ENUMS # @@ -326,7 +326,7 @@ def set(self, param, value): if isinstance(value, str): self.query("(param-set! '{} \"{}\")".format(param, value)) - elif isinstance(value, tuple) or isinstance(value, list): + elif isinstance(value, (tuple, list)): self.query("(param-set! '{} '({}))".format(param, " ".join(value))) elif isinstance(value, bool): value = "t" if value else "f" diff --git a/instruments/toptica/toptica_utils.py b/instruments/toptica/toptica_utils.py index ead664049..0ffb424e5 100644 --- a/instruments/toptica/toptica_utils.py +++ b/instruments/toptica/toptica_utils.py @@ -37,5 +37,5 @@ def convert_toptica_datetime(response): """ if response.find('""') >= 0: return None - else: - return datetime.strptime(response, "%Y-%m-%d %H:%M:%S") + + return datetime.strptime(response, "%Y-%m-%d %H:%M:%S") diff --git a/instruments/util_fns.py b/instruments/util_fns.py index 977bfdcf3..fad59455e 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -151,8 +151,9 @@ def split_unit_str(s, default_units=pq.dimensionless, lookup=None): if units is None: return float(val), default_units - else: - return float(val), lookup(units) + + return float(val), lookup(units) + else: try: return float(s), default_units @@ -179,8 +180,8 @@ def rproperty(fget=None, fset=None, doc=None, readonly=False, writeonly=False): return property(fget=fget, fset=None, doc=doc) elif writeonly: return property(fget=None, fset=fset, doc=doc) - else: - return property(fget=fget, fset=fset, doc=doc) + + return property(fget=fget, fset=fset, doc=doc) def bool_property(name, inst_true, inst_false, doc=None, readonly=False, @@ -480,14 +481,14 @@ def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", def _min_getter(self): if valid_range[0] == "query": return pq.Quantity(*split_unit_str(self.query(min_fmt_str.format(name)), units)) - else: - return assume_units(valid_range[0], units).rescale(units) + + return assume_units(valid_range[0], units).rescale(units) def _max_getter(self): if valid_range[1] == "query": return pq.Quantity(*split_unit_str(self.query(max_fmt_str.format(name)), units)) - else: - return assume_units(valid_range[1], units).rescale(units) + + return assume_units(valid_range[1], units).rescale(units) new_range = ( None if valid_range[0] is None else _min_getter, diff --git a/instruments/yokogawa/yokogawa7651.py b/instruments/yokogawa/yokogawa7651.py index c5779ab06..3d7d03e08 100644 --- a/instruments/yokogawa/yokogawa7651.py +++ b/instruments/yokogawa/yokogawa7651.py @@ -36,9 +36,6 @@ class Yokogawa7651(PowerSupply, Instrument): >>> inst.voltage = 10 * pq.V """ - def __init__(self, filelike): - super(Yokogawa7651, self).__init__(filelike) - # INNER CLASSES # class Channel(PowerSupplyChannel): From 3316327723a58f29ad9c21d1524358df3e49d29a Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 6 Jun 2017 23:16:44 -0400 Subject: [PATCH 004/108] Update supported Python versions (#170) * Add Py36 and drop Py33 * Update readme * Revert adding version constraint to numpy --- .travis.yml | 2 +- README.rst | 6 +++--- setup.py | 2 +- tox.ini | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed6d2479f..a076dfaaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,9 @@ sudo: false language: python python: - "2.7" - - "3.3" - "3.4" - "3.5" + - "3.6" install: - "pip install -r requirements.txt" - "pip install -r dev-requirements.txt" diff --git a/README.rst b/README.rst index 67bbd6c3c..d9e318113 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ InstrumentKit ============= -.. image:: https://img.shields.io/travis/Galvant/InstrumentKit.svg?maxAge=2592000 +.. image:: https://img.shields.io/travis/Galvant/InstrumentKit/master.svg?maxAge=2592000 :target: https://travis-ci.org/Galvant/InstrumentKit :alt: Travis-CI build status @@ -111,7 +111,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 2.7, 3.3, 3.4, and 3.5 are supported. Should you encounter +At this time, Python 2.7, 3.4, 3.5, and 3.6 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. @@ -134,7 +134,7 @@ To run the tests against all supported version of Python, you will need to have the binary for each installed, as well as any requirements needed to install ``numpy`` under each Python version. On Debian/Ubuntu systems this means you will need to install the ``python-dev`` package for each version of Python -supported (``python2.7-dev``, ``python3.3-dev``, etc). +supported (``python2.7-dev``, ``python3.4-dev``, etc). With the required system packages installed, all tests can be run with ``tox``: diff --git a/setup.py b/setup.py index b422efd4b..e658c45b8 100644 --- a/setup.py +++ b/setup.py @@ -23,9 +23,9 @@ "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Operating System :: OS Independent", "License :: OSI Approved :: GNU Affero General Public License v3", "Intended Audience :: Science/Research", diff --git a/tox.ini b/tox.ini index ae996789c..a6bf9ea2e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py33,py34,py35 +envlist = py27,py34,py35,py36 [testenv] deps = -rdev-requirements.txt commands = nosetests From cf711163fa11b620fa5d65b910079dc0362724b3 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Fri, 9 Jun 2017 18:46:44 -0400 Subject: [PATCH 005/108] Pin ruamel version (#171) * Use ruamel.yaml 0.14 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 622bb14d0..30a1a88d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,4 @@ enum34 python-vxi11>=0.8 pyusb python-usbtmc -ruamel.yaml +ruamel.yaml~=0.14.12 From 363de47d7941cd83953728b3c4fd4abd6511f81d Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Fri, 9 Jun 2017 19:31:53 -0400 Subject: [PATCH 006/108] Add set_cmd to property factories (#172) --- instruments/agilent/agilent33220a.py | 12 +- .../generic_scpi/scpi_function_generator.py | 6 +- instruments/generic_scpi/scpi_multimeter.py | 10 +- instruments/lakeshore/lakeshore475.py | 2 +- instruments/picowatt/picowattavs47.py | 10 +- instruments/qubitekk/mc1.py | 10 +- instruments/srs/srs345.py | 8 +- .../test_bool_property.py | 26 ++- .../test_bounded_unitful_property.py | 4 +- .../test_enum_property.py | 15 ++ .../test_int_property.py | 12 ++ .../test_string_property.py | 12 ++ .../test_unitful_property.py | 15 ++ .../test_unitless_property.py | 12 ++ instruments/thorlabs/lcc25.py | 12 +- instruments/thorlabs/sc10.py | 12 +- instruments/util_fns.py | 150 +++++++++++++----- 17 files changed, 241 insertions(+), 87 deletions(-) diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 5a24ea9ad..8afb868cf 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -87,7 +87,7 @@ def frequency(self, newval): super(Agilent33220a, self).frequency = newval function = enum_property( - name="FUNC", + command="FUNC", enum=Function, doc=""" Gets/sets the output function of the function generator @@ -98,7 +98,7 @@ def frequency(self, newval): ) duty_cycle = int_property( - name="FUNC:SQU:DCYC", + command="FUNC:SQU:DCYC", doc=""" Gets/sets the duty cycle of a square wave. @@ -111,7 +111,7 @@ def frequency(self, newval): ) ramp_symmetry = int_property( - name="FUNC:RAMP:SYMM", + command="FUNC:RAMP:SYMM", doc=""" Gets/sets the ramp symmetry for ramp waves. @@ -124,7 +124,7 @@ def frequency(self, newval): ) output = bool_property( - name="OUTP", + command="OUTP", inst_true="ON", inst_false="OFF", doc=""" @@ -138,7 +138,7 @@ def frequency(self, newval): ) output_sync = bool_property( - name="OUTP:SYNC", + command="OUTP:SYNC", inst_true="ON", inst_false="OFF", doc=""" @@ -149,7 +149,7 @@ def frequency(self, newval): ) output_polarity = enum_property( - name="OUTP:POL", + command="OUTP:POL", enum=OutputPolarity, doc=""" Gets/sets the polarity of the waveform relative to the offset voltage. diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 66630006a..4877168ae 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -75,7 +75,7 @@ def _set_amplitude_(self, magnitude, units): # PROPERTIES # frequency = unitful_property( - name="FREQ", + command="FREQ", units=pq.Hz, doc=""" Gets/sets the output frequency. @@ -86,7 +86,7 @@ def _set_amplitude_(self, magnitude, units): ) function = enum_property( - name="FUNC", + command="FUNC", enum=lambda: Function, # pylint: disable=undefined-variable doc=""" Gets/sets the output function of the function generator @@ -96,7 +96,7 @@ def _set_amplitude_(self, magnitude, units): ) offset = unitful_property( - name="VOLT:OFFS", + command="VOLT:OFFS", units=pq.volt, doc=""" Gets/sets the offset voltage of the function generator. diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index b6cc062fd..1a273f7de 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -141,7 +141,7 @@ class SampleSource(Enum): # pylint: disable=unnecessary-lambda,undefined-variable mode = enum_property( - name="CONF", + command="CONF", enum=Mode, doc=""" Gets/sets the current measurement mode for the multimeter. @@ -157,7 +157,7 @@ class SampleSource(Enum): ) trigger_mode = enum_property( - name="TRIG:SOUR", + command="TRIG:SOUR", enum=TriggerMode, doc=""" Gets/sets the SCPI Multimeter trigger mode. @@ -317,7 +317,7 @@ def sample_count(self, newval): self.sendcmd("SAMP:COUN {}".format(newval)) trigger_delay = unitful_property( - name="TRIG:DEL", + command="TRIG:DEL", units=pq.second, doc=""" Gets/sets the time delay which the multimeter will use following @@ -329,7 +329,7 @@ def sample_count(self, newval): ) sample_source = enum_property( - name="SAMP:SOUR", + command="SAMP:SOUR", enum=SampleSource, doc=""" Gets/sets the multimeter sample source. This determines whether the @@ -344,7 +344,7 @@ def sample_count(self, newval): ) sample_timer = unitful_property( - name="SAMP:TIM", + command="SAMP:TIM", units=pq.second, doc=""" Gets/sets the sample interval when the sample counter is greater than diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 86d02fb59..0db502339 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -269,7 +269,7 @@ def control_slope_limit(self, newval): self.field_control_params = tuple(values) control_mode = bool_property( - name="CMODE", + command="CMODE", inst_true="1", inst_false="0", doc=""" diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index c255b8d28..acf1d8d0f 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -97,7 +97,7 @@ def sensor(self): return ProxyList(self, PicowattAVS47.Sensor, range(8)) remote = bool_property( - name="REM", + command="REM", inst_true="1", inst_false="0", doc=""" @@ -111,7 +111,7 @@ def sensor(self): ) input_source = enum_property( - name="INP", + command="INP", enum=InputSource, input_decoration=int, doc=""" @@ -122,7 +122,7 @@ def sensor(self): ) mux_channel = int_property( - name="MUX", + command="MUX", doc=""" Gets/sets the multiplexer sensor number. It is recommended that you ground the input before switching the @@ -136,7 +136,7 @@ def sensor(self): ) excitation = int_property( - name="EXC", + command="EXC", doc=""" Gets/sets the excitation sensor number. @@ -148,7 +148,7 @@ def sensor(self): ) display = int_property( - name="DIS", + command="DIS", doc=""" Gets/sets the sensor that is displayed on the front panel. diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index 98b353962..facc54eab 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -91,7 +91,7 @@ def upper_limit(self, newval): self._upper_limit = assume_units(newval, pq.ms).rescale(pq.ms) direction = unitful_property( - name="DIRE", + command="DIRE", doc=""" Get the internal direction variable, which is a function of how far the motor needs to go. @@ -104,7 +104,7 @@ def upper_limit(self, newval): ) inertia = unitful_property( - name="INER", + command="INER", doc=""" Gets/Sets the amount of force required to overcome static inertia. Must be between 0 and 100 milliseconds. @@ -133,7 +133,7 @@ def internal_position(self): return response metric_position = unitful_property( - name="METR", + command="METR", doc=""" Get the estimated motor position, in millimeters. @@ -145,7 +145,7 @@ def internal_position(self): ) setting = int_property( - name="OUTP", + command="OUTP", doc=""" Gets/sets the output port of the optical switch. 0 means input 1 is directed to output 1, and input 2 is directed to output 2. 1 means that @@ -158,7 +158,7 @@ def internal_position(self): ) step_size = unitful_property( - name="STEP", + command="STEP", doc=""" Gets/Sets the number of milliseconds per step. Must be between 1 and 100 milliseconds. diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index a344a4b5d..c86de3c63 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -78,7 +78,7 @@ class Function(IntEnum): # PROPERTIES ## frequency = unitful_property( - name="FREQ", + command="FREQ", units=pq.Hz, doc=""" Gets/sets the output frequency. @@ -89,7 +89,7 @@ class Function(IntEnum): ) function = enum_property( - name="FUNC", + command="FUNC", enum=Function, input_decoration=int, doc=""" @@ -100,7 +100,7 @@ class Function(IntEnum): ) offset = unitful_property( - name="OFFS", + command="OFFS", units=pq.volt, doc=""" Gets/sets the offset voltage for the output waveform. @@ -111,7 +111,7 @@ class Function(IntEnum): ) phase = unitful_property( - name="PHSE", + command="PHSE", units=pq.degree, doc=""" Gets/sets the phase for the output waveform. diff --git a/instruments/tests/test_property_factories/test_bool_property.py b/instruments/tests/test_property_factories/test_bool_property.py index 9fc36fc19..01995485e 100644 --- a/instruments/tests/test_property_factories/test_bool_property.py +++ b/instruments/tests/test_property_factories/test_bool_property.py @@ -20,8 +20,8 @@ def test_bool_property_basics(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF') - mock2 = bool_property('MOCK2', 'YES', 'NO') + mock1 = bool_property('MOCK1') + mock2 = bool_property('MOCK2', inst_true='YES', inst_false='NO') mock_inst = BoolMock({'MOCK1?': 'OFF', 'MOCK2?': 'YES'}) @@ -36,7 +36,7 @@ class BoolMock(MockInstrument): def test_bool_property_set_fmt(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', set_fmt="{}={}") + mock1 = bool_property('MOCK1', set_fmt="{}={}") mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -48,7 +48,7 @@ class BoolMock(MockInstrument): @raises(AttributeError) def test_bool_property_readonly_writing_fails(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', readonly=True) + mock1 = bool_property('MOCK1', readonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -57,7 +57,7 @@ class BoolMock(MockInstrument): def test_bool_property_readonly_reading_passes(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', readonly=True) + mock1 = bool_property('MOCK1', readonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -67,7 +67,7 @@ class BoolMock(MockInstrument): @raises(AttributeError) def test_bool_property_writeonly_reading_fails(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', writeonly=True) + mock1 = bool_property('MOCK1', writeonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) @@ -76,8 +76,20 @@ class BoolMock(MockInstrument): def test_bool_property_writeonly_writing_passes(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', 'ON', 'OFF', writeonly=True) + mock1 = bool_property('MOCK1', writeonly=True) mock_instrument = BoolMock({'MOCK1?': 'OFF'}) mock_instrument.mock1 = False + + +def test_bool_property_set_cmd(): + class BoolMock(MockInstrument): + mock1 = bool_property('MOCK1', set_cmd='FOOBAR') + + mock_inst = BoolMock({'MOCK1?': 'OFF'}) + + eq_(mock_inst.mock1, False) + mock_inst.mock1 = True + + eq_(mock_inst.value, 'MOCK1?\nFOOBAR ON\n') diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index 543320c5c..d982dc989 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -124,7 +124,7 @@ class BoundedUnitfulMock(MockInstrument): @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): bounded_unitful_property( - name='MOCK', + command='MOCK', units=pq.Hz, derp="foobar" ) @@ -139,7 +139,7 @@ def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_valid_range_none(mock_unitful_property): bounded_unitful_property( - name='MOCK', + command='MOCK', units=pq.Hz, valid_range=(None, None) ) diff --git a/instruments/tests/test_property_factories/test_enum_property.py b/instruments/tests/test_property_factories/test_enum_property.py index 6384e7305..862a55df2 100644 --- a/instruments/tests/test_property_factories/test_enum_property.py +++ b/instruments/tests/test_property_factories/test_enum_property.py @@ -196,3 +196,18 @@ class EnumMock(MockInstrument): eq_(mock_instrument.a, SillyEnum.a) eq_(mock_instrument.value, 'MOCK:A?\n') + + +def test_enum_property_set_cmd(): + class SillyEnum(Enum): + a = 'aa' + + class EnumMock(MockInstrument): + a = enum_property('MOCK:A', SillyEnum, set_cmd='FOOBAR:A') + + mock_inst = EnumMock({'MOCK:A?': 'aa'}) + + eq_(mock_inst.a, SillyEnum.a) + mock_inst.a = SillyEnum.a + + eq_(mock_inst.value, 'MOCK:A?\nFOOBAR:A aa\n') diff --git a/instruments/tests/test_property_factories/test_int_property.py b/instruments/tests/test_property_factories/test_int_property.py index b0bec2064..1a9ab2340 100644 --- a/instruments/tests/test_property_factories/test_int_property.py +++ b/instruments/tests/test_property_factories/test_int_property.py @@ -97,3 +97,15 @@ class IntMock(MockInstrument): mock_inst.int_property = 1 eq_(mock_inst.value, 'MOCK {:e}\n'.format(1)) + + +def test_int_property_set_cmd(): + class IntMock(MockInstrument): + int_property = int_property('MOCK', set_cmd='FOOBAR') + + mock_inst = IntMock({'MOCK?': '1'}) + + eq_(mock_inst.int_property, 1) + mock_inst.int_property = 1 + + eq_(mock_inst.value, 'MOCK?\nFOOBAR 1\n') diff --git a/instruments/tests/test_property_factories/test_string_property.py b/instruments/tests/test_property_factories/test_string_property.py index d1c94554d..e30e5998f 100644 --- a/instruments/tests/test_property_factories/test_string_property.py +++ b/instruments/tests/test_property_factories/test_string_property.py @@ -52,3 +52,15 @@ class StringMock(MockInstrument): mock_inst.mock_property = 'foo' eq_(mock_inst.value, 'MOCK?\nMOCK foo\n') + + +def test_string_property_set_cmd(): + class StringMock(MockInstrument): + mock_property = string_property('MOCK', set_cmd='FOOBAR') + + mock_inst = StringMock({'MOCK?': '"derp"'}) + + eq_(mock_inst.mock_property, 'derp') + + mock_inst.mock_property = 'qwerty' + eq_(mock_inst.value, 'MOCK?\nFOOBAR "qwerty"\n') diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index 551d28d42..9d146236b 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -242,3 +242,18 @@ class UnitfulMock(MockInstrument): value = mock_inst.unitful_property assert value.magnitude == 1000 assert value.units == pq.hertz + + +def test_unitful_property_name_read_not_none(): + class UnitfulMock(MockInstrument): + a = unitful_property( + 'MOCK', + units=pq.hertz, + set_cmd='FOOBAR' + ) + + mock_inst = UnitfulMock({'MOCK?': '1000'}) + eq_(mock_inst.a, 1000 * pq.hertz) + mock_inst.a = 1000 * pq.hertz + + eq_(mock_inst.value, 'MOCK?\nFOOBAR {:e}\n'.format(1000)) diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index 16654d3b5..7698f2aaf 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -88,3 +88,15 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock({'MOCK?': '1'}) eq_(mock_inst.mock_property, 1) + + +def test_unitless_property_set_cmd(): + class UnitlessMock(MockInstrument): + mock_property = unitless_property('MOCK', set_cmd='FOOBAR') + + mock_inst = UnitlessMock({'MOCK?': '1'}) + + eq_(mock_inst.mock_property, 1) + mock_inst.mock_property = 1 + + eq_(mock_inst.value, 'MOCK?\nFOOBAR {:e}\n'.format(1)) diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index a44211507..2989283d9 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -95,8 +95,8 @@ def name(self): enable = bool_property( "enable", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets the output enable status. @@ -109,8 +109,8 @@ def name(self): extern = bool_property( "extern", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets the use of the external TTL modulation. @@ -124,8 +124,8 @@ def name(self): remote = bool_property( "remote", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets front panel lockout status for remote instrument operation. diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index 135d65b3c..cfce252b4 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -67,8 +67,8 @@ def name(self): enable = bool_property( "ens", - "1", - "0", + inst_true="1", + inst_false="0", set_fmt="{}={}", doc=""" Gets/sets the shutter enable status, False for disabled, True if @@ -182,8 +182,8 @@ def baud_rate(self, newval): closed = bool_property( "closed", - "1", - "0", + inst_true="1", + inst_false="0", readonly=True, doc=""" Gets the shutter closed status. @@ -197,8 +197,8 @@ def baud_rate(self, newval): interlock = bool_property( "interlock", - "1", - "0", + inst_true="1", + inst_false="0", readonly=True, doc=""" Gets the interlock tripped status. diff --git a/instruments/util_fns.py b/instruments/util_fns.py index fad59455e..d0430170a 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -20,6 +20,7 @@ # FUNCTIONS ################################################################### + # pylint: disable=too-many-arguments def assume_units(value, units): """ @@ -39,6 +40,7 @@ def assume_units(value, units): value = pq.Quantity(value, units) return value + def setattr_expression(target, name_expr, value): """ Recursively calls getattr/setattr for attribute @@ -184,16 +186,29 @@ def rproperty(fget=None, fset=None, doc=None, readonly=False, writeonly=False): return property(fget=fget, fset=fset, doc=doc) -def bool_property(name, inst_true, inst_false, doc=None, readonly=False, - writeonly=False, set_fmt="{} {}"): +def bool_property(command, set_cmd=None, inst_true="ON", inst_false="OFF", + doc=None, readonly=False, writeonly=False, set_fmt="{} {}"): """ Called inside of SCPI classes to instantiate boolean properties of the device cleanly. For example: - >>> my_property = bool_property("BEST:PROPERTY", "ON", "OFF") # doctest: +SKIP - - :param str name: Name of the SCPI command corresponding to this property. + >>> my_property = bool_property( + ... "BEST:PROPERTY", + ... inst_true="ON", + ... inst_false="OFF" + ... ) # doctest: +SKIP + + This will result in "BEST:PROPERTY ON" or "BEST:PROPERTY OFF" being sent + when setting, and "BEST:PROPERTY?" being sent when getting. + + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str inst_true: String returned and accepted by the instrument for `True` values. :param str inst_false: String returned and accepted by the instrument for @@ -210,19 +225,22 @@ def bool_property(name, inst_true, inst_false, doc=None, readonly=False, """ def _getter(self): - return self.query(name + "?").strip() == inst_true + return self.query(command + "?").strip() == inst_true def _setter(self, newval): if not isinstance(newval, bool): raise TypeError("Bool properties must be specified with a " "boolean value") - self.sendcmd(set_fmt.format(name, inst_true if newval else inst_false)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + inst_true if newval else inst_false + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def enum_property(name, enum, doc=None, input_decoration=None, +def enum_property(command, enum, set_cmd=None, doc=None, input_decoration=None, output_decoration=None, readonly=False, writeonly=False, set_fmt="{} {}"): """ @@ -234,7 +252,13 @@ def enum_property(name, enum, doc=None, input_decoration=None, Example: my_property = bool_property("BEST:PROPERTY", enum_class) - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param type enum: Class derived from `Enum` representing valid values. :param callable input_decoration: Function called on responses from the instrument before passing to user code. @@ -249,6 +273,10 @@ def enum_property(name, enum, doc=None, input_decoration=None, non-query to the instrument. The default is "{} {}" which places a space between the SCPI command the associated parameter. By switching to "{}={}" an equals sign would instead be used as the separator. + :param str get_cmd: If not `None`, this parameter sets the command string + to be used when reading/querying from the instrument. If used, the name + parameter is still used to set the command for pure-write commands to + the instrument. """ def _in_decor_fcn(val): if input_decoration is None: @@ -265,7 +293,7 @@ def _out_decor_fcn(val): return output_decoration(val) def _getter(self): - return enum(_in_decor_fcn(self.query("{}?".format(name)).strip())) + return enum(_in_decor_fcn(self.query("{}?".format(command)).strip())) def _setter(self, newval): try: # First assume newval is Enum.value @@ -275,19 +303,28 @@ def _setter(self, newval): newval = enum(newval) except ValueError: raise ValueError("Enum property new value not in enum.") - self.sendcmd(set_fmt.format(name, _out_decor_fcn(enum(newval).value))) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + _out_decor_fcn(enum(newval).value) + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def unitless_property(name, format_code='{:e}', doc=None, readonly=False, - writeonly=False, set_fmt="{} {}"): +def unitless_property(command, set_cmd=None, format_code='{:e}', doc=None, + readonly=False, writeonly=False, set_fmt="{} {}"): """ Called inside of SCPI classes to instantiate properties with unitless numeric values. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str format_code: Argument to `str.format` used in sending values to the instrument. :param str doc: Docstring to be associated with the new property. @@ -302,7 +339,7 @@ def unitless_property(name, format_code='{:e}', doc=None, readonly=False, """ def _getter(self): - raw = self.query("{}?".format(name)) + raw = self.query("{}?".format(command)) return float(raw) def _setter(self, newval): @@ -312,19 +349,29 @@ def _setter(self, newval): else: raise ValueError strval = format_code.format(newval) - self.sendcmd(set_fmt.format(name, strval)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + strval + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def int_property(name, format_code='{:d}', doc=None, readonly=False, - writeonly=False, valid_set=None, set_fmt="{} {}"): +def int_property(command, set_cmd=None, format_code='{:d}', doc=None, + readonly=False, writeonly=False, valid_set=None, + set_fmt="{} {}"): """ Called inside of SCPI classes to instantiate properties with unitless numeric values. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str format_code: Argument to `str.format` used in sending values to the instrument. :param str doc: Docstring to be associated with the new property. @@ -341,12 +388,15 @@ def int_property(name, format_code='{:d}', doc=None, readonly=False, """ def _getter(self): - raw = self.query("{}?".format(name)) + raw = self.query("{}?".format(command)) return int(raw) if valid_set is None: def _setter(self, newval): strval = format_code.format(newval) - self.sendcmd(set_fmt.format(name, strval)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + strval + )) else: def _setter(self, newval): if newval not in valid_set: @@ -355,13 +405,16 @@ def _setter(self, newval): "must be one of {}.".format(newval, valid_set) ) strval = format_code.format(newval) - self.sendcmd(set_fmt.format(name, strval)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + strval + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def unitful_property(name, units, format_code='{:e}', doc=None, +def unitful_property(command, units, set_cmd=None, format_code='{:e}', doc=None, input_decoration=None, output_decoration=None, readonly=False, writeonly=False, set_fmt="{} {}", valid_range=(None, None)): @@ -373,7 +426,13 @@ def unitful_property(name, units, format_code='{:e}', doc=None, for instruments where the units can change dynamically due to front-panel interaction or due to remote commands. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param units: Units to assume in sending and receiving magnitudes to and from the instrument. :param str format_code: Argument to `str.format` used in sending the @@ -413,7 +472,7 @@ def _out_decor_fcn(val): return output_decoration(val) def _getter(self): - raw = _in_decor_fcn(self.query("{}?".format(name))) + raw = _in_decor_fcn(self.query("{}?".format(command))) return pq.Quantity(*split_unit_str(raw, units)).rescale(units) def _setter(self, newval): @@ -434,13 +493,16 @@ def _setter(self, newval): # catch bad units. strval = format_code.format( assume_units(newval, units).rescale(units).item()) - self.sendcmd(set_fmt.format(name, _out_decor_fcn(strval))) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + _out_decor_fcn(strval) + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) -def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", +def bounded_unitful_property(command, units, min_fmt_str="{}:MIN?", max_fmt_str="{}:MAX?", valid_range=("query", "query"), **kwargs): """ @@ -454,7 +516,13 @@ def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", the one created by `unitful_property`, one for the minimum value, and one for the maximum value. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param units: Units to assume in sending and receiving magnitudes to and from the instrument. :param str min_fmt_str: Specify the string format to use when sending a @@ -480,13 +548,13 @@ def bounded_unitful_property(name, units, min_fmt_str="{}:MIN?", def _min_getter(self): if valid_range[0] == "query": - return pq.Quantity(*split_unit_str(self.query(min_fmt_str.format(name)), units)) + return pq.Quantity(*split_unit_str(self.query(min_fmt_str.format(command)), units)) return assume_units(valid_range[0], units).rescale(units) def _max_getter(self): if valid_range[1] == "query": - return pq.Quantity(*split_unit_str(self.query(max_fmt_str.format(name)), units)) + return pq.Quantity(*split_unit_str(self.query(max_fmt_str.format(command)), units)) return assume_units(valid_range[1], units).rescale(units) @@ -496,18 +564,24 @@ def _max_getter(self): ) return ( - unitful_property(name, units, valid_range=new_range, **kwargs), + unitful_property(command, units, valid_range=new_range, **kwargs), property(_min_getter) if valid_range[0] is not None else None, property(_max_getter) if valid_range[1] is not None else None ) -def string_property(name, bookmark_symbol='"', doc=None, readonly=False, - writeonly=False, set_fmt="{} {}{}{}"): +def string_property(command, set_cmd=None, bookmark_symbol='"', doc=None, + readonly=False, writeonly=False, set_fmt="{} {}{}{}"): """ Called inside of SCPI classes to instantiate properties with a string value. - :param str name: Name of the SCPI command corresponding to this property. + :param str command: Name of the SCPI command corresponding to this property. + If parameter set_cmd is not specified, then this parameter is also used + for both getting and setting. + :param str set_cmd: If not `None`, this parameter sets the command string + to be used when sending commands with no return values to the + instrument. This allows for non-symmetric properties that have different + strings for getting vs setting a property. :param str doc: Docstring to be associated with the new property. :param bool readonly: If `True`, the returned property does not have a setter. @@ -523,14 +597,16 @@ def string_property(name, bookmark_symbol='"', doc=None, readonly=False, bookmark_length = len(bookmark_symbol) def _getter(self): - string = self.query("{}?".format(name)) + string = self.query("{}?".format(command)) string = string[ bookmark_length:-bookmark_length] if bookmark_length > 0 else string return string def _setter(self, newval): - self.sendcmd( - set_fmt.format(name, bookmark_symbol, newval, bookmark_symbol)) + self.sendcmd(set_fmt.format( + command if set_cmd is None else set_cmd, + bookmark_symbol, newval, bookmark_symbol + )) return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly) From 215a6d0adb13eaadcb330e5b14d37db604506274 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 10 Jun 2017 20:29:24 -0400 Subject: [PATCH 007/108] Fix timeout and terminator for USBTMC (#173) --- .../abstract_instruments/comm/usbtmc_communicator.py | 4 ++-- instruments/tests/test_comm/test_usbtmc.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index f465abba7..88855d4e5 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -54,7 +54,7 @@ def terminator(self): :type: `str` """ - return self._filelike.term_char + return chr(self._filelike.term_char) @terminator.setter def terminator(self, newval): @@ -78,7 +78,7 @@ def timeout(self): @timeout.setter def timeout(self, newval): - newval = assume_units(newval, pq.second).rescale(pq.ms).magnitude + newval = assume_units(newval, pq.second).rescale(pq.s).magnitude self._filelike.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index 18e2ebcf7..da56749af 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -40,7 +40,7 @@ def test_usbtmccomm_init_missing_module(): def test_usbtmccomm_terminator_getter(mock_usbtmc): comm = USBTMCCommunicator() - term_char = mock.PropertyMock(return_value="\n") + term_char = mock.PropertyMock(return_value=10) type(comm._filelike).term_char = term_char eq_(comm.terminator, "\n") @@ -74,10 +74,10 @@ def test_usbtmccomm_timeout(mock_usbtmc): timeout.assert_called_with() comm.timeout = 10 - timeout.assert_called_with(array(10000.0)) + timeout.assert_called_with(array(10.0)) comm.timeout = 1000 * pq.millisecond - timeout.assert_called_with(array(1000.0)) + timeout.assert_called_with(array(1.0)) @mock.patch(patch_path) From 12e9a157ac17e2a2d536dd567c70330e8f824dcf Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Sat, 15 Jul 2017 08:00:00 +1000 Subject: [PATCH 008/108] Feature: named structures (#169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial version of named struct class. * Added tests for new class. * docs and a pylint fix. * Py3k fix (filter → list(filter)) * Improvements to string handling. * Ignored additional carp. * Provide fixes for build and start using hypothesis * Locked requirements.txt; see #174. * Fixed py3k compat issue. * Fixed negative length checks. * Fixed bug in StringField.__get__ * Added NamedStruct.__eq__ test. * Fixing pylint warnings. * Moved pylint warning comment. * Changed numpy version in reqs as suggested by @scasagrande --- .gitignore | 9 + dev-requirements.txt | 1 + doc/source/devguide/util_fns.rst | 12 + instruments/named_struct.py | 318 +++++++++++++++++++++++++ instruments/tests/test_named_struct.py | 70 ++++++ requirements.txt | 2 +- 6 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 instruments/named_struct.py create mode 100644 instruments/tests/test_named_struct.py diff --git a/.gitignore b/.gitignore index 3f7a79c94..3d527fd94 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,12 @@ nosetests.xml #pycharm generated .idea + +# VS Code IDE internals +.vscode/ + +# nosetests metadata +.noseids + +# Hypothesis files +.hypothesis/ diff --git a/dev-requirements.txt b/dev-requirements.txt index 419748e1c..825550120 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,4 @@ mock nose +hypothesis pylint==1.7.1 diff --git a/doc/source/devguide/util_fns.rst b/doc/source/devguide/util_fns.rst index 2d2719a91..9fa06769b 100644 --- a/doc/source/devguide/util_fns.rst +++ b/doc/source/devguide/util_fns.rst @@ -183,3 +183,15 @@ String Property .. autofunction:: string_property +Named Structures +================ + +The :class:`~instruments.named_struct.NamedStruct` class can be used to represent +C-style structures for serializing and deserializing data. + +.. autoclass:: instruments.named_struct.NamedStruct + +.. autoclass:: instruments.named_struct.Field + +.. autoclass:: instruments.named_struct.Padding + diff --git a/instruments/named_struct.py b/instruments/named_struct.py new file mode 100644 index 000000000..ef8522ab7 --- /dev/null +++ b/instruments/named_struct.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Class for quickly defining C-like structures with named fields. +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division + +import struct +from collections import OrderedDict + +from future.utils import with_metaclass + +# DESIGN NOTES ################################################################ + +# This class uses the Django-like strategy described at +# http://stackoverflow.com/a/3288988/267841 +# to assign a "birthday" to each Field as it's instantiated. We can thus sort +# each Field in a NamedStruct by its birthday. + +# Notably, this hack is not at all required on Python 3.6: +# https://www.python.org/dev/peps/pep-0520/ + +# TODO: arrays other than string arrays do not currently work. + +# PYLINT CONFIGURATION ######################################################## + +# All of the classes in this module need to interact with each other rather +# deeply, so we disable the protected-access check within this module. + +# pylint:disable=protected-access + +# CLASSES ##################################################################### + + +class Field(object): + """ + A named field within a C-style structure. + + :param str fmt: Format for the field, corresponding to the + documentation of the :mod:`struct` standard library package. + """ + + __n_fields_created = 0 + _field_birthday = None + + _fmt = '' + _name = None + _owner_type = object + + def __init__(self, fmt, strip_null=False): + super(Field, self).__init__() + + # Record our birthday so that we can sort fields later. + self._field_birthday = Field.__n_fields_created + Field.__n_fields_created += 1 + + self._fmt = fmt.strip() + self._strip_null = strip_null + + # If we're given a length, check that it + # makes sense. + if self._fmt[:-1] and int(self._fmt[:-1]) < 0: + raise TypeError("Field is specified with negative length.") + + + def is_significant(self): + return not self._fmt.endswith('x') + + @property + def fmt_char(self): + return self._fmt[-1] + + def __len__(self): + if self._fmt[:-1]: + # Although we know that length > 0, this abs ensures that static + # code checks are happy with __len__ always returning a positive number + return abs(int(self._fmt[:-1])) + + raise TypeError("Field is scalar and has no len().") + + def __repr__(self): + if self._owner_type: + return "".format( + self._name, self._owner_type, self._fmt + ) + + return "".format( + self._fmt + ) + + def __str__(self): + n, fmt_char = len(self), self.fmt_char + c_type = { + 'x': 'char', + 'c': 'char', + 'b': 'char', + 'B': 'unsigned char', + '?': 'bool', + 'h': 'short', + 'H': 'unsigned short', + 'i': 'int', + 'I': 'unsigned int', + 'l': 'long', + 'L': 'unsigned long', + 'q': 'long long', + 'Q': 'unsigned long long', + 'f': 'float', + 'd': 'double', + # NB: no [], since that will be implied by n. + 's': 'char', + 'p': 'char', + 'P': 'void *' + }[fmt_char] + + if n: + c_type = "{}[{}]".format(c_type, n) + return ( + "{c_type} {self._name}".format(c_type=c_type, self=self) + if self.is_significant() + else c_type + ) + + # DESCRIPTOR PROTOCOL # + + def __get__(self, obj, type=None): + return obj._values[self._name] + + def __set__(self, obj, value): + obj._values[self._name] = value + +class StringField(Field): + """ + Represents a field that is interpreted as a Python string. + + :param int length: Maximum allowed length of the field, as + measured in the number of bytes used by its encoding. + Note that if a shorter string is provided, it will + be padded by null bytes. + :param str encoding: Name of an encoding to use in serialization + and deserialization to Python strings. + :param bool strip_null: If `True`, null bytes (``'\x00'``) will + be removed from the right upon deserialization. + """ + + _strip_null = False + _encoding = 'ascii' + + def __init__(self, length, encoding='ascii', strip_null=False): + super(StringField, self).__init__('{}s'.format(length)) + self._strip_null = strip_null + self._encoding = encoding + + def __set__(self, obj, value): + if isinstance(value, bytes): + value = value.decode(self._encoding) + if self._strip_null: + value = value.rstrip('\x00') + value = value.encode(self._encoding) + + super(StringField, self).__set__(obj, value) + + def __get__(self, obj, type=None): + return super(StringField, self).__get__(obj, type=type).decode(self._encoding) + + +class Padding(Field): + """ + Represents a field whose value is insignificant, and will not + be kept in serialization and deserialization. + + :param int n_bytes: Number of padding bytes occupied by this field. + """ + + def __init__(self, n_bytes=1): + super(Padding, self).__init__('{}x'.format(n_bytes)) + +class HasFields(type): + def __new__(mcs, name, bases, attrs): + # Since this is a metaclass, the __new__ method observes + # creation of new *classes* and not new instances. + # We call the superclass of HasFields, which is another + # metaclass, to do most of the heavy lifting of creating + # the new class. + cls = super(HasFields, mcs).__new__(mcs, name, bases, attrs) + + # We now sort the fields by their birthdays and store them in an + # ordered dict for easier look up later. + cls._fields = OrderedDict([ + (field_name, field) + for field_name, field in sorted( + [ + (field_name, field) + for field_name, field in attrs.items() + if isinstance(field, Field) + ], + key=lambda item: item[1]._field_birthday + ) + ]) + + # Assign names and owner types to each field so that they can follow + # the descriptor protocol. + for field_name, field in cls._fields.items(): + field._name = field_name + field._owner_type = cls + + # Associate a struct.Struct instance with the new class + # that defines how to pack/unpack the new type. + cls._struct = struct.Struct( + # TODO: support alignment char at start. + " ".join([ + field._fmt for field in cls._fields.values() + ]) + ) + + return cls + + +class NamedStruct(with_metaclass(HasFields, object)): + """ + Represents a C-style struct with one or more named fields, + useful for packing and unpacking serialized data documented + in terms of C examples. For instance, consider a struct of the + form:: + + typedef struct { + unsigned long a = 0x1234; + char[12] dummy; + unsigned char b = 0xab; + } Foo; + + This struct can be represented as the following NamedStruct:: + + class Foo(NamedStruct): + a = Field('L') + dummy = Padding(12) + b = Field('B') + + foo = Foo(a=0x1234, b=0xab) + """ + + # Provide reasonable defaults for the lowercase-f-fields + # created by HasFields. This will prevent a few edge cases, + # allow type inference and will prevent pylint false positives. + _fields = {} + _struct = None + + def __init__(self, **kwargs): + super(NamedStruct, self).__init__() + self._values = OrderedDict([ + ( + field._name, None + ) + for field in filter(Field.is_significant, self._fields.values()) + ]) + + for field_name, value in kwargs.items(): + setattr(self, field_name, value) + + def _to_seq(self): + return tuple(self._values.values()) + + @classmethod + def _from_seq(cls, new_values): + return cls(**{ + field._name: new_value + for field, new_value in + zip(list(filter(Field.is_significant, cls._fields.values())), new_values) + }) + + def pack(self): + """ + Packs this instance into bytes, suitable for transmitting over + a network or recording to disc. See :func:`struct.pack` for details. + + :return bytes packed_data: A serialized representation of this + instance. + """ + return self._struct.pack(*self._to_seq()) + + @classmethod + def unpack(cls, buffer): + """ + Given a buffer, unpacks it into an instance of this NamedStruct. + See :func:`struct.unpack` for details. + + :param bytes buffer: Data to use in creating a new instance. + :return: The new instance represented by `buffer`. + """ + return cls._from_seq(cls._struct.unpack(buffer)) + + def __eq__(self, other): + if not isinstance(other, NamedStruct): + return False + + return self._values == other._values + + def __hash__(self): + return hash(self._values) + + def __str__(self): + return "{name} {{\n{fields}\n}}".format( + name=type(self).__name__, + fields="\n".join([ + " {field}{value};".format( + field=field, + value=( + " = {}".format(repr(self._values[field._name])) + if field.is_significant() + else "" + ) + ) + for field in self._fields.values() + ]) + ) diff --git a/instruments/tests/test_named_struct.py b/instruments/tests/test_named_struct.py new file mode 100644 index 000000000..adeef46da --- /dev/null +++ b/instruments/tests/test_named_struct.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for named structures. +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import, unicode_literals + +from unittest import TestCase + +from hypothesis import given +import hypothesis.strategies as st + +from instruments.named_struct import ( + Field, StringField, Padding, NamedStruct +) + +# TESTS ###################################################################### + +# We disable pylint warnings that are not as applicable for unit tests. +# pylint: disable=no-member,protected-access,blacklisted-name,missing-docstring,no-self-use + +class TestNamedStruct(TestCase): + @given(st.integers(min_value=0, max_value=0x7FFF*2+1), st.integers(min_value=0, max_value=0xFF)) + def test_roundtrip(self, var1, var2): + class Foo(NamedStruct): + a = Field('H') + padding = Padding(12) + b = Field('B') + + foo = Foo(a=var1, b=var2) + assert Foo.unpack(foo.pack()) == foo + + + def test_str(self): + class Foo(NamedStruct): + a = StringField(8, strip_null=False) + b = StringField(9, strip_null=True) + c = StringField(2, encoding='utf-8') + + foo = Foo(a="0123456\x00", b='abc', c=u'α') + assert Foo.unpack(foo.pack()) == foo + + # Also check that we can get fields out directly. + self.assertEqual(foo.a, '0123456\x00') + self.assertEqual(foo.b, 'abc') + self.assertEqual(foo.c, u'α') + + + def test_negative_len(self): + """ + Checks whether negative field lengths correctly raise. + """ + with self.assertRaises(TypeError): + class Foo(NamedStruct): # pylint: disable=unused-variable + a = StringField(-1) + + def test_equality(self): + class Foo(NamedStruct): + a = Field('H') + b = Field('B') + c = StringField(5, encoding='utf8', strip_null=True) + + foo1 = Foo(a=0x1234, b=0x56, c=u'ω') + foo2 = Foo(a=0xabcd, b=0xef, c=u'α') + + assert foo1 == foo1 + assert foo1 != foo2 diff --git a/requirements.txt b/requirements.txt index 30a1a88d6..89e22a811 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy +numpy<1.13.0 pyserial quantities future>=0.15 From 5a8e546d056f11d7c8034159accc021aa648c49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Troels=20Kr=C3=B8gh?= Date: Thu, 17 Aug 2017 00:24:09 +0200 Subject: [PATCH 009/108] Change write to write_raw when communicating using visa (#177) --- instruments/abstract_instruments/comm/visa_communicator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 391487757..7f407298c 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -149,7 +149,7 @@ def write_raw(self, msg): :param bytes msg: Bytes to be sent to the instrument over the VISA connection. """ - self._conn.write(msg) + self._conn.write_raw(msg) def seek(self, offset): # pylint: disable=unused-argument,no-self-use return NotImplemented From 87aa8d69dce39154cc3b341c5259953ff2ff55a3 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 30 Aug 2017 09:39:53 -0400 Subject: [PATCH 010/108] Bump version 0.3.1 -> 0.4.0 --- instruments/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index 562ae3bae..3ae8c8d05 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -36,7 +36,7 @@ # In keeping with PEP-396, we define a version number of the form # {major}.{minor}[.{postrelease}]{prerelease-tag} -__version__ = "0.3.1" +__version__ = "0.4.0" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" From 97f5dd580800a5cc25194160af0ac151c13851c4 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 31 Aug 2017 18:21:43 -0400 Subject: [PATCH 011/108] Fix README badges --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d9e318113..9545f52bc 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,8 @@ InstrumentKit :target: https://travis-ci.org/Galvant/InstrumentKit :alt: Travis-CI build status -.. image:: https://img.shields.io/coveralls/Galvant/InstrumentKit/dev.svg?maxAge=2592000 - :target: https://coveralls.io/r/Galvant/InstrumentKit?branch=dev +.. image:: https://img.shields.io/coveralls/Galvant/InstrumentKit/master.svg?maxAge=2592000 + :target: https://coveralls.io/github/Galvant/InstrumentKit?branch=master :alt: Coveralls code coverage .. image:: https://readthedocs.org/projects/instrumentkit/badge/?version=latest From 5ef13965cb854a0b54d8f48b1f45d6c80d46a3e7 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 31 Aug 2017 18:26:20 -0400 Subject: [PATCH 012/108] Use new version of quantities, unpin numpy --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 89e22a811..1d83117d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -numpy<1.13.0 +numpy pyserial -quantities +quantities>=0.12.1 future>=0.15 enum34 python-vxi11>=0.8 From 8425341a8ba60ee447016ec87867ce7f0c19aa55 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 31 Aug 2017 18:50:03 -0400 Subject: [PATCH 013/108] Bump version 0.4.0 -> 0.4.1 --- instruments/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index 3ae8c8d05..20aeb3844 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -36,7 +36,7 @@ # In keeping with PEP-396, we define a version number of the form # {major}.{minor}[.{postrelease}]{prerelease-tag} -__version__ = "0.4.0" +__version__ = "0.4.1" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" From bb4ab2152bcf3a5881100af06fd9afb75618cfc9 Mon Sep 17 00:00:00 2001 From: Christoph Buchner Date: Sat, 24 Mar 2018 15:52:24 +0100 Subject: [PATCH 014/108] Nose to pytest transition (#183) * Use mock from the standard lib if available. * Migrate nose raises to pytest.raises. This touches a lot of code because pytest.raises is used in a context manager, so indentation has to be changed. Replacement happened with regex from @raises\((.*)\) to @pytest.mark.xfail(raises=$1) then from @pytest.mark.xfail\(raises=(.*)\)\ndef(.*) to def$2\n with pytest.raises($1): then manual correction of indentation. * Replace nose eq_ by assert, remove nottest. * Move CI from nosetests to pytest, adapt docs. Pin astroid version, too, to avoid wrong errors on CI. * Fix some introduced errors flagged by pylint. * Specify minimum pyserial requirement (see #58). --- .gitignore | 1 + .travis.yml | 7 +- dev-requirements.txt | 3 +- doc/source/devguide/index.rst | 4 +- doc/source/devguide/testing.rst | 10 +- instruments/tests/__init__.py | 9 +- instruments/tests/test_base_instrument.py | 168 +++---- instruments/tests/test_comm/test_file.py | 38 +- .../tests/test_comm/test_gi_gpibusb.py | 96 ++-- instruments/tests/test_comm/test_loopback.py | 42 +- instruments/tests/test_comm/test_serial.py | 46 +- instruments/tests/test_comm/test_socket.py | 58 +-- instruments/tests/test_comm/test_usbtmc.py | 30 +- instruments/tests/test_comm/test_vxi11.py | 36 +- .../test_holzworth/test_holzworth_hs9000.py | 2 +- instruments/tests/test_hp/test_hp3456a.py | 182 +++---- instruments/tests/test_hp/test_hp6624a.py | 68 +-- .../tests/test_keithley/test_keithley2182.py | 62 +-- .../tests/test_keithley/test_keithley6514.py | 30 +- instruments/tests/test_ondax/test_lm.py | 82 ++-- .../test_bool_property.py | 36 +- .../test_bounded_unitful_property.py | 66 +-- .../test_enum_property.py | 72 +-- .../test_int_property.py | 48 +- .../test_property_factories/test_rproperty.py | 50 +- .../test_string_property.py | 18 +- .../test_unitful_property.py | 86 ++-- .../test_unitless_property.py | 46 +- .../tests/test_qubitekk/test_qubitekk_cc1.py | 272 +++++----- .../tests/test_qubitekk/test_qubitekk_mc1.py | 18 +- instruments/tests/test_split_str.py | 62 +-- instruments/tests/test_srs/test_srs830.py | 240 ++++----- .../test_thorlabs/test_thorlabs_lcc25.py | 222 ++++----- .../tests/test_thorlabs/test_thorlabs_sc10.py | 66 +-- .../test_thorlabs/test_thorlabs_tc200.py | 463 +++++++++--------- .../test_toptica/test_toptica_topmode.py | 200 ++++---- .../tests/test_toptica/test_toptica_utils.py | 6 +- instruments/tests/test_util_fns.py | 76 +-- setup.py | 2 +- tox.ini | 2 +- 40 files changed, 1513 insertions(+), 1512 deletions(-) diff --git a/.gitignore b/.gitignore index 3d527fd94..47f4499c9 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ pip-log.txt .coverage .tox nosetests.xml +.pytest_cache #Translations *.mo diff --git a/.travis.yml b/.travis.yml index a076dfaaf..033893a0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ install: - "pip install -r dev-requirements.txt" - pip install python-coveralls - pip install coverage + - pip install pytest-cov before_script: # We use before_script to report version and path information in a way # that can be easily hidden by Travis' log folding. Moreover, a nonzero @@ -17,12 +18,12 @@ before_script: # even sensibly get version information, we correctly abort. - which python - python --version - - which nosetests - - nosetests --version + - which pytest + - pytest --version - which pylint - pylint --version script: - - nosetests --with-coverage -w instruments + - pytest --cov=instruments - pylint --py3k instruments - pylint --disable=I instruments after_success: diff --git a/dev-requirements.txt b/dev-requirements.txt index 825550120..4fe922cad 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,5 @@ mock -nose +pytest hypothesis pylint==1.7.1 +astroid==1.5.3 diff --git a/doc/source/devguide/index.rst b/doc/source/devguide/index.rst index d0ef44b4c..f730eaf3c 100644 --- a/doc/source/devguide/index.rst +++ b/doc/source/devguide/index.rst @@ -9,7 +9,7 @@ InstrumentKit Development Guide code_style testing util_fns - + Introduction ============ @@ -34,7 +34,7 @@ provided ``dev-requirements.txt``:: $ pip install -r dev-requirements.txt - mock -- nose +- pytest - pylint Optional Development Dependencies diff --git a/doc/source/devguide/testing.rst b/doc/source/devguide/testing.rst index e2ba4d9bc..6d249c5d6 100644 --- a/doc/source/devguide/testing.rst +++ b/doc/source/devguide/testing.rst @@ -20,13 +20,13 @@ of InstrumentKit will not, in general, have access to each instrument that is supported--- we rely on automated testing to ensure that future changes do not cause invalid or undesired operation. -For InstrumentKit, we rely heavily on `nose`_, a mature and flexible +For InstrumentKit, we rely heavily on `pytest`_, a mature and flexible unit-testing framework for Python. When run from the command line via -``nosetests``, or when run by Travis CI, nose will automatically execute +``pytest``, or when run by Travis CI, pytest will automatically execute functions and methods whose names start with ``test`` in packages, modules and classes whose names start with ``test`` or ``Test``, depending. (Please -see the `nose`_ documentation for full details, as this is not intended -to be a guide to nose so much as a guide to how we use it in IK.) +see the `pytest`_ documentation for full details, as this is not intended +to be a guide to pytest so much as a guide to how we use it in IK.) Because of this, we keep all test cases in the ``instruments.tests`` package, under a subpackage named for the particular manufacturer, such as ``instruments.tests.test_srs``. The tests for each instrument should @@ -88,4 +88,4 @@ Protocol Assertion Functions .. autofunction:: expected_protocol -.. _nose: https://nose.readthedocs.org/en/latest/ \ No newline at end of file +.. _pytest: https://docs.pytest.org/en/latest/ diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index 1237d2591..ea901d886 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -17,7 +17,10 @@ from builtins import bytes, str -from nose.tools import nottest, eq_ +try: + from unittest import mock # from Python 3.3 onward, this is in the stdlib +except ImportError: + import mock # FUNCTIONS ################################################################## @@ -90,7 +93,6 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n"): # """Only read {} bytes out of {}""".format(current, end) -@nottest def unit_eq(a, b, msg=None, thresh=1e-5): """ Asserts that two unitful quantites ``a`` and ``b`` @@ -103,7 +105,6 @@ def unit_eq(a, b, msg=None, thresh=1e-5): assert a.units == b.units, "{} and {} have different units".format(a, b) -@nottest def make_name_test(ins_class, name_cmd="*IDN?"): """ Given an instrument class, produces a test which asserts that the instrument @@ -111,5 +112,5 @@ def make_name_test(ins_class, name_cmd="*IDN?"): """ def test(): with expected_protocol(ins_class, name_cmd + "\n", "NAME\n") as ins: - eq_(ins.name, "NAME") + assert ins.name == "NAME" return test diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 46b1d323a..2c121e1c6 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -15,9 +15,7 @@ import serial from serial.tools.list_ports_common import ListPortInfo -from nose.tools import raises -import mock - +import pytest import numpy as np import instruments as ik @@ -30,6 +28,8 @@ ) from instruments.errors import AcknowledgementError, PromptError +from . import mock + # TESTS ###################################################################### # pylint: disable=no-member,protected-access @@ -63,23 +63,23 @@ def test_instrument_binblockread_two_reads(): np.testing.assert_array_equal(calls_expected, calls_actual) -@raises(IOError) def test_instrument_binblockread_too_many_reads(): - inst = ik.Instrument.open_test() - data = bytes.fromhex("00000001000200030004") - inst._file.read_raw = mock.MagicMock( - side_effect=[b"#", b"2", b"10", data[:6], b"", b"", b""] - ) + with pytest.raises(IOError): + inst = ik.Instrument.open_test() + data = bytes.fromhex("00000001000200030004") + inst._file.read_raw = mock.MagicMock( + side_effect=[b"#", b"2", b"10", data[:6], b"", b"", b""] + ) - _ = inst.binblockread(2) + _ = inst.binblockread(2) -@raises(IOError) def test_instrument_binblockread_bad_block_start(): - inst = ik.Instrument.open_test() - inst._file.read_raw = mock.MagicMock(return_value=b"@") + with pytest.raises(IOError): + inst = ik.Instrument.open_test() + inst._file.read_raw = mock.MagicMock(return_value=b"@") - _ = inst.binblockread(2) + _ = inst.binblockread(2) # OPEN CONNECTION TESTS @@ -180,73 +180,73 @@ def test_instrument_open_serial_by_usb_ids_and_serial_number(mock_serial_manager ) -@raises(serial.SerialException) @mock.patch("instruments.abstract_instruments.instrument.comports") @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_multiple_matches(_, mock_comports): - fake_device = ListPortInfo() - fake_device.vid = 0 - fake_device.pid = 1000 - fake_device.serial_number = 'a1' - fake_device.device = 'COM1' + with pytest.raises(serial.SerialException): + fake_device = ListPortInfo() + fake_device.vid = 0 + fake_device.pid = 1000 + fake_device.serial_number = 'a1' + fake_device.device = 'COM1' - fake_device2 = ListPortInfo() - fake_device2.vid = 0 - fake_device2.pid = 1000 - fake_device2.serial_number = 'b2' - fake_device2.device = 'COM2' + fake_device2 = ListPortInfo() + fake_device2.vid = 0 + fake_device2.pid = 1000 + fake_device2.serial_number = 'b2' + fake_device2.device = 'COM2' - mock_comports.return_value = [fake_device, fake_device2] + mock_comports.return_value = [fake_device, fake_device2] - _ = ik.Instrument.open_serial(baud=1234, vid=0, pid=1000) + _ = ik.Instrument.open_serial(baud=1234, vid=0, pid=1000) -@raises(ValueError) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_incorrect_serial_num(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator - _ = ik.Instrument.open_serial(baud=1234, vid=0, pid=1000, serial_number="xyz") + with pytest.raises(ValueError): + mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + _ = ik.Instrument.open_serial(baud=1234, vid=0, pid=1000, serial_number="xyz") -@raises(ValueError) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_cant_find(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator - _ = ik.Instrument.open_serial(baud=1234, vid=1234, pid=1000) + with pytest.raises(ValueError): + mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + _ = ik.Instrument.open_serial(baud=1234, vid=1234, pid=1000) -@raises(ValueError) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_no_port(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator - _ = ik.Instrument.open_serial(baud=1234) + with pytest.raises(ValueError): + mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + _ = ik.Instrument.open_serial(baud=1234) -@raises(ValueError) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_and_port(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator - _ = ik.Instrument.open_serial(port="COM1", baud=1234, vid=1234, pid=1000) + with pytest.raises(ValueError): + mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + _ = ik.Instrument.open_serial(port="COM1", baud=1234, vid=1234, pid=1000) -@raises(ValueError) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_vid_no_pid(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator - _ = ik.Instrument.open_serial(baud=1234, vid=1234) + with pytest.raises(ValueError): + mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + _ = ik.Instrument.open_serial(baud=1234, vid=1234) -@raises(ValueError) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_pid_no_vid(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator - _ = ik.Instrument.open_serial(baud=1234, pid=1234) + with pytest.raises(ValueError): + mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + _ = ik.Instrument.open_serial(baud=1234, pid=1234) # TEST OPEN_GPIBUSB ########################################################## @@ -274,10 +274,10 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): ) -@raises(ImportError) @mock.patch("instruments.abstract_instruments.instrument.visa", new=None) def test_instrument_open_visa_import_error(): - _ = ik.Instrument.open_visa("abc123") + with pytest.raises(ImportError): + _ = ik.Instrument.open_visa("abc123") @mock.patch("instruments.abstract_instruments.instrument.VisaCommunicator") @@ -419,16 +419,16 @@ def test_instrument_open_from_uri_vxi11(mock_open_conn): mock_open_conn.assert_called_with("TCPIP::192.168.1.105::gpib,5::INSTR") -@raises(NotImplementedError) def test_instrument_open_from_uri_invalid_scheme(): - _ = ik.Instrument.open_from_uri("foo://bar") + with pytest.raises(NotImplementedError): + _ = ik.Instrument.open_from_uri("foo://bar") # INIT TESTS -@raises(TypeError) def test_instrument_init_bad_filelike(): - _ = ik.Instrument(mock.MagicMock()) + with pytest.raises(TypeError): + _ = ik.Instrument(mock.MagicMock()) def test_instrument_init(): @@ -503,19 +503,19 @@ def new_ack(msg): inst._file.sendcmd.assert_called_with("foobar") -@raises(AcknowledgementError) def test_instrument_sendcmd_bad_ack(): - mock_filelike = mock.MagicMock() - mock_filelike.__class__ = AbstractCommunicator - inst = ik.Instrument(mock_filelike) + with pytest.raises(AcknowledgementError): + mock_filelike = mock.MagicMock() + mock_filelike.__class__ = AbstractCommunicator + inst = ik.Instrument(mock_filelike) - def new_ack(msg): - return msg + def new_ack(msg): + return msg - inst._ack_expected = new_ack - inst.read = mock.MagicMock(return_value="derp") + inst._ack_expected = new_ack + inst.read = mock.MagicMock(return_value="derp") - inst.sendcmd("foobar") + inst.sendcmd("foobar") def test_instrument_sendcmd_noack(): @@ -531,16 +531,16 @@ def test_instrument_sendcmd_noack(): inst._file.sendcmd.assert_called_with("foobar") -@raises(PromptError) def test_instrument_sendcmd_noack_bad_prompt(): - mock_filelike = mock.MagicMock() - mock_filelike.__class__ = AbstractCommunicator - inst = ik.Instrument(mock_filelike) + with pytest.raises(PromptError): + mock_filelike = mock.MagicMock() + mock_filelike.__class__ = AbstractCommunicator + inst = ik.Instrument(mock_filelike) - inst.prompt = "> " - inst.read = mock.MagicMock(return_value="* ") + inst.prompt = "> " + inst.read = mock.MagicMock(return_value="* ") - inst.sendcmd("foobar") + inst.sendcmd("foobar") def test_instrument_sendcmd(): @@ -622,19 +622,19 @@ def new_ack(msg): inst.read.assert_called_with(-1) -@raises(AcknowledgementError) def test_instrument_query_bad_ack(): - mock_filelike = mock.MagicMock() - mock_filelike.__class__ = AbstractCommunicator - inst = ik.Instrument(mock_filelike) - inst.read = mock.MagicMock(return_value="derp") + with pytest.raises(AcknowledgementError): + mock_filelike = mock.MagicMock() + mock_filelike.__class__ = AbstractCommunicator + inst = ik.Instrument(mock_filelike) + inst.read = mock.MagicMock(return_value="derp") - def new_ack(msg): - return msg + def new_ack(msg): + return msg - inst._ack_expected = new_ack + inst._ack_expected = new_ack - _ = inst.query("foobar?") + _ = inst.query("foobar?") def test_instrument_query_noack(): @@ -659,17 +659,17 @@ def test_instrument_query_noack(): inst.read.assert_called_with(2) -@raises(PromptError) def test_instrument_query_noack_bad_prompt(): - mock_filelike = mock.MagicMock() - mock_filelike.__class__ = AbstractCommunicator - inst = ik.Instrument(mock_filelike) - inst._file.query.return_value = "datas" + with pytest.raises(PromptError): + mock_filelike = mock.MagicMock() + mock_filelike.__class__ = AbstractCommunicator + inst = ik.Instrument(mock_filelike) + inst._file.query.return_value = "datas" - inst.prompt = "> " - inst.read = mock.MagicMock(return_value="* ") + inst.prompt = "> " + inst.read = mock.MagicMock(return_value="* ") - _ = inst.query("foobar?") + _ = inst.query("foobar?") def test_instrument_query(): diff --git a/instruments/tests/test_comm/test_file.py b/instruments/tests/test_comm/test_file.py index fde974caf..173924087 100644 --- a/instruments/tests/test_comm/test_file.py +++ b/instruments/tests/test_comm/test_file.py @@ -8,10 +8,10 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest from instruments.abstract_instruments.comm import FileCommunicator +from .. import mock # TEST CASES ################################################################# @@ -33,7 +33,7 @@ def test_filecomm_address_getter(): mock_name = mock.PropertyMock(return_value="/home/user/file") type(comm._filelike).name = mock_name - eq_(comm.address, "/home/user/file") + assert comm.address == "/home/user/file" mock_name.assert_called_with() @@ -43,37 +43,37 @@ def test_filecomm_address_getter_no_name(): del comm._filelike.name - eq_(comm.address, None) + assert comm.address is None -@raises(NotImplementedError) def test_filecomm_address_setter(): - comm = FileCommunicator(mock.MagicMock()) - comm.address = "abc123" + with pytest.raises(NotImplementedError): + comm = FileCommunicator(mock.MagicMock()) + comm.address = "abc123" def test_filecomm_terminator(): comm = FileCommunicator(mock.MagicMock()) - eq_(comm.terminator, "\n") + assert comm.terminator == "\n" comm.terminator = "*" - eq_(comm._terminator, "*") + assert comm._terminator == "*" comm.terminator = b"*" - eq_(comm._terminator, "*") + assert comm._terminator == "*" -@raises(NotImplementedError) def test_filecomm_timeout_getter(): - comm = FileCommunicator(mock.MagicMock()) - _ = comm.timeout + with pytest.raises(NotImplementedError): + comm = FileCommunicator(mock.MagicMock()) + _ = comm.timeout -@raises(NotImplementedError) def test_filecomm_timeout_setter(): - comm = FileCommunicator(mock.MagicMock()) - comm.timeout = 1 + with pytest.raises(NotImplementedError): + comm = FileCommunicator(mock.MagicMock()) + comm.timeout = 1 def test_filecomm_close(): @@ -87,7 +87,7 @@ def test_filecomm_read_raw(): comm = FileCommunicator(mock.MagicMock()) comm._filelike.read = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\n"]) - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" comm._filelike.read.assert_has_calls([mock.call(1)] * 4) assert comm._filelike.read.call_count == 4 @@ -115,7 +115,7 @@ def test_filecomm_query(): comm._testing = True # to disable the delay in the _query function comm._filelike.read = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\n"]) - eq_(comm._query("mock"), "abc") + assert comm._query("mock") == "abc" def test_filecomm_seek(): @@ -128,7 +128,7 @@ def test_filecomm_tell(): comm = FileCommunicator(mock.MagicMock()) comm._filelike.tell.return_value = 5 - eq_(comm.tell(), 5) + assert comm.tell() == 5 comm._filelike.tell.assert_called_with() diff --git a/instruments/tests/test_comm/test_gi_gpibusb.py b/instruments/tests/test_comm/test_gi_gpibusb.py index 263844e3e..b6b75f428 100644 --- a/instruments/tests/test_comm/test_gi_gpibusb.py +++ b/instruments/tests/test_comm/test_gi_gpibusb.py @@ -8,13 +8,13 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest import serial import quantities as pq from instruments.abstract_instruments.comm import GPIBCommunicator, SerialCommunicator from instruments.tests import unit_eq +from .. import mock # TEST CASES ################################################################# @@ -34,10 +34,10 @@ def test_gpibusbcomm_init_correct_values_new_firmware(): mock_gpib.query.return_value = "5" comm = GPIBCommunicator(mock_gpib, 1) - eq_(comm._terminator, "\n") - eq_(comm._version, 5) - eq_(comm._eos, "\n") - eq_(comm._eoi, True) + assert comm._terminator == "\n" + assert comm._version == 5 + assert comm._eos == "\n" + assert comm._eoi is True unit_eq(comm._timeout, 1000 * pq.millisecond) @@ -47,7 +47,7 @@ def test_gpibusbcomm_init_correct_values_old_firmware(): mock_gpib.query.return_value = "4" comm = GPIBCommunicator(mock_gpib, 1) - eq_(comm._eos, 10) + assert comm._eos == 10 def test_gpibusbcomm_address(): @@ -58,30 +58,30 @@ def test_gpibusbcomm_address(): type(comm._file).address = port_name # Check that our address function is working - eq_(comm.address, (1, "/dev/address")) + assert comm.address == (1, "/dev/address") port_name.assert_called_with() # Able to set GPIB address comm.address = 5 - eq_(comm._gpib_address, 5) + assert comm._gpib_address == 5 # Able to set address with a list comm.address = [6, "/dev/foobar"] - eq_(comm._gpib_address, 6) + assert comm._gpib_address == 6 port_name.assert_called_with("/dev/foobar") -@raises(ValueError) def test_gpibusbcomm_address_out_of_range(): - comm = GPIBCommunicator(mock.MagicMock(), 1) + with pytest.raises(ValueError): + comm = GPIBCommunicator(mock.MagicMock(), 1) - comm.address = 31 + comm.address = 31 -@raises(TypeError) def test_gpibusbcomm_address_wrong_type(): - comm = GPIBCommunicator(mock.MagicMock(), 1) - comm.address = "derp" + with pytest.raises(TypeError): + comm = GPIBCommunicator(mock.MagicMock(), 1) + comm.address = "derp" def test_gpibusbcomm_eoi(): @@ -90,14 +90,14 @@ def test_gpibusbcomm_eoi(): comm._file.sendcmd = mock.MagicMock() comm.eoi = True - eq_(comm.eoi, True) - eq_(comm._eoi, True) + assert comm.eoi is True + assert comm._eoi is True comm._file.sendcmd.assert_called_with("++eoi 1") comm._file.sendcmd = mock.MagicMock() comm.eoi = False - eq_(comm.eoi, False) - eq_(comm._eoi, False) + assert comm.eoi is False + assert comm._eoi is False comm._file.sendcmd.assert_called_with("++eoi 0") @@ -107,22 +107,22 @@ def test_gpibusbcomm_eoi_old_firmware(): comm._file.sendcmd = mock.MagicMock() comm.eoi = True - eq_(comm.eoi, True) - eq_(comm._eoi, True) + assert comm.eoi is True + assert comm._eoi is True comm._file.sendcmd.assert_called_with("+eoi:1") comm._file.sendcmd = mock.MagicMock() comm.eoi = False - eq_(comm.eoi, False) - eq_(comm._eoi, False) + assert comm.eoi is False + assert comm._eoi is False comm._file.sendcmd.assert_called_with("+eoi:0") -@raises(TypeError) def test_gpibusbcomm_eoi_bad_type(): - comm = GPIBCommunicator(mock.MagicMock(), 1) - comm._version = 5 - comm.eoi = "abc" + with pytest.raises(TypeError): + comm = GPIBCommunicator(mock.MagicMock(), 1) + comm._version = 5 + comm.eoi = "abc" def test_gpibusbcomm_eos_rn(): @@ -131,8 +131,8 @@ def test_gpibusbcomm_eos_rn(): comm._file.sendcmd = mock.MagicMock() comm.eos = "\r\n" - eq_(comm.eos, "\r\n") - eq_(comm._eos, "\r\n") + assert comm.eos == "\r\n" + assert comm._eos == "\r\n" comm._file.sendcmd.assert_called_with("++eos 0") @@ -142,8 +142,8 @@ def test_gpibusbcomm_eos_r(): comm._file.sendcmd = mock.MagicMock() comm.eos = "\r" - eq_(comm.eos, "\r") - eq_(comm._eos, "\r") + assert comm.eos == "\r" + assert comm._eos == "\r" comm._file.sendcmd.assert_called_with("++eos 1") @@ -153,16 +153,16 @@ def test_gpibusbcomm_eos_n(): comm._file.sendcmd = mock.MagicMock() comm.eos = "\n" - eq_(comm.eos, "\n") - eq_(comm._eos, "\n") + assert comm.eos == "\n" + assert comm._eos == "\n" comm._file.sendcmd.assert_called_with("++eos 2") -@raises(ValueError) def test_gpibusbcomm_eos_invalid(): - comm = GPIBCommunicator(mock.MagicMock(), 1) - comm._version = 5 - comm.eos = "*" + with pytest.raises(ValueError): + comm = GPIBCommunicator(mock.MagicMock(), 1) + comm._version = 5 + comm.eos = "*" def test_gpibusbcomm_eos_old_firmware(): @@ -171,7 +171,7 @@ def test_gpibusbcomm_eos_old_firmware(): comm._file.sendcmd = mock.MagicMock() comm.eos = "\n" - eq_(comm._eos, 10) + assert comm._eos == 10 comm._file.sendcmd.assert_called_with("+eos:10") @@ -180,16 +180,16 @@ def test_gpibusbcomm_terminator(): comm._version = 5 # Default terminator should be eoi - eq_(comm.terminator, "eoi") - eq_(comm._eoi, True) + assert comm.terminator == "eoi" + assert comm._eoi is True comm.terminator = "\n" - eq_(comm.terminator, "\n") - eq_(comm._eoi, False) + assert comm.terminator == "\n" + assert comm._eoi is False comm.terminator = "eoi" - eq_(comm.terminator, "eoi") - eq_(comm._eoi, True) + assert comm.terminator == "eoi" + assert comm._eoi is True def test_gpibusbcomm_timeout(): @@ -215,7 +215,7 @@ def test_gpibusbcomm_read_raw(): comm._version = 5 comm._file.read_raw = mock.MagicMock(return_value=b"abc") - eq_(comm.read_raw(3), b"abc") + assert comm.read_raw(3) == b"abc" comm._file.read_raw.assert_called_with(3) @@ -257,7 +257,7 @@ def test_gpibusbcomm_query(): comm._file.read = mock.MagicMock(return_value="answer") comm.sendcmd = mock.MagicMock() - eq_(comm._query("mock?"), "answer") + assert comm._query("mock?") == "answer" comm.sendcmd.assert_called_with("mock?") comm._file.read.assert_called_with(-1) @@ -273,7 +273,7 @@ def test_gpibusbcomm_query_no_question_mark(): comm._file.read = mock.MagicMock(return_value="answer") comm.sendcmd = mock.MagicMock() - eq_(comm._query("mock"), "answer") + assert comm._query("mock") == "answer" comm.sendcmd.assert_called_with("mock") comm._file.read.assert_called_with(-1) comm._file.sendcmd.assert_has_calls([mock.call("+read")]) diff --git a/instruments/tests/test_comm/test_loopback.py b/instruments/tests/test_comm/test_loopback.py index eaa9e796c..c02c36024 100644 --- a/instruments/tests/test_comm/test_loopback.py +++ b/instruments/tests/test_comm/test_loopback.py @@ -8,10 +8,10 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest from instruments.abstract_instruments.comm import LoopbackCommunicator +from .. import mock # TEST CASES ################################################################# @@ -34,7 +34,7 @@ def test_loopbackcomm_address(mock_sys): comm._conn = mock.MagicMock() # Check that our address function is working - eq_(comm.address, "address") + assert comm.address == "address" mock_name.assert_called_with() @@ -42,28 +42,28 @@ def test_loopbackcomm_terminator(): comm = LoopbackCommunicator() # Default terminator should be \n - eq_(comm.terminator, "\n") + assert comm.terminator == "\n" comm.terminator = b"*" - eq_(comm.terminator, "*") - eq_(comm._terminator, "*") + assert comm.terminator == "*" + assert comm._terminator == "*" comm.terminator = u"\r" - eq_(comm.terminator, u"\r") - eq_(comm._terminator, u"\r") + assert comm.terminator == u"\r" + assert comm._terminator == u"\r" comm.terminator = "\r\n" - eq_(comm.terminator, "\r\n") - eq_(comm._terminator, "\r\n") + assert comm.terminator == "\r\n" + assert comm._terminator == "\r\n" def test_loopbackcomm_timeout(): comm = LoopbackCommunicator() - eq_(comm.timeout, 0) + assert comm.timeout == 0 comm.timeout = 10 - eq_(comm.timeout, 0) # setting should be ignored + assert comm.timeout == 0 # setting should be ignored def test_loopbackcomm_close(): @@ -79,7 +79,7 @@ def test_loopbackcomm_read_raw(): mock_stdin.read.side_effect = [b"a", b"b", b"c", b"\n"] comm = LoopbackCommunicator(stdin=mock_stdin) - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" mock_stdin.read.assert_has_calls([mock.call(1)]*4) assert mock_stdin.read.call_count == 4 @@ -94,7 +94,7 @@ def test_loopbackcomm_read_raw_2char_terminator(): comm = LoopbackCommunicator(stdin=mock_stdin) comm._terminator = "\r\n" - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" mock_stdin.read.assert_has_calls([mock.call(1)]*5) assert mock_stdin.read.call_count == 5 @@ -124,7 +124,7 @@ def test_loopbackcomm_query(): comm.read = mock.MagicMock(return_value="answer") comm.sendcmd = mock.MagicMock() - eq_(comm._query("mock"), "answer") + assert comm._query("mock") == "answer" comm.sendcmd.assert_called_with("mock") comm.read.assert_called_with(-1) @@ -132,16 +132,16 @@ def test_loopbackcomm_query(): comm.read.assert_called_with(10) -@raises(NotImplementedError) def test_loopbackcomm_seek(): - comm = LoopbackCommunicator() - comm.seek(1) + with pytest.raises(NotImplementedError): + comm = LoopbackCommunicator() + comm.seek(1) -@raises(NotImplementedError) def test_loopbackcomm_tell(): - comm = LoopbackCommunicator() - comm.tell() + with pytest.raises(NotImplementedError): + comm = LoopbackCommunicator() + comm.tell() def test_loopbackcomm_flush_input(): diff --git a/instruments/tests/test_comm/test_serial.py b/instruments/tests/test_comm/test_serial.py index d12c2f150..870238f47 100644 --- a/instruments/tests/test_comm/test_serial.py +++ b/instruments/tests/test_comm/test_serial.py @@ -8,13 +8,13 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest import serial import quantities as pq from instruments.abstract_instruments.comm import SerialCommunicator from instruments.tests import unit_eq +from .. import mock # TEST CASES ################################################################# @@ -26,9 +26,9 @@ def test_serialcomm_init(): assert isinstance(comm._conn, serial.Serial) is True -@raises(TypeError) def test_serialcomm_init_wrong_filelike(): - _ = SerialCommunicator("derp") + with pytest.raises(TypeError): + _ = SerialCommunicator("derp") def test_serialcomm_address(): @@ -40,7 +40,7 @@ def test_serialcomm_address(): type(comm._conn).port = port_name # Check that our address function is working - eq_(comm.address, "/dev/address") + assert comm.address == "/dev/address" port_name.assert_called_with() @@ -48,14 +48,14 @@ def test_serialcomm_terminator(): comm = SerialCommunicator(serial.Serial()) # Default terminator should be \n - eq_(comm.terminator, "\n") + assert comm.terminator == "\n" comm.terminator = "*" - eq_(comm.terminator, "*") + assert comm.terminator == "*" comm.terminator = "\r\n" - eq_(comm.terminator, "\r\n") - eq_(comm._terminator, "\r\n") + assert comm.terminator == "\r\n" + assert comm._terminator == "\r\n" def test_serialcomm_timeout(): @@ -89,7 +89,7 @@ def test_serialcomm_read_raw(): comm._conn = mock.MagicMock() comm._conn.read = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\n"]) - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" comm._conn.read.assert_has_calls([mock.call(1)]*4) assert comm._conn.read.call_count == 4 @@ -104,18 +104,18 @@ def test_loopbackcomm_read_raw_2char_terminator(): comm._conn.read = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\r", b"\n"]) comm._terminator = "\r\n" - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" comm._conn.read.assert_has_calls([mock.call(1)] * 5) assert comm._conn.read.call_count == 5 -@raises(IOError) def test_serialcomm_read_raw_timeout(): - comm = SerialCommunicator(serial.Serial()) - comm._conn = mock.MagicMock() - comm._conn.read = mock.MagicMock(side_effect=[b"a", b"b", b""]) + with pytest.raises(IOError): + comm = SerialCommunicator(serial.Serial()) + comm._conn = mock.MagicMock() + comm._conn.read = mock.MagicMock(side_effect=[b"a", b"b", b""]) - _ = comm.read_raw(-1) + _ = comm.read_raw(-1) def test_serialcomm_write_raw(): @@ -140,7 +140,7 @@ def test_serialcomm_query(): comm.read = mock.MagicMock(return_value="answer") comm.sendcmd = mock.MagicMock() - eq_(comm._query("mock"), "answer") + assert comm._query("mock") == "answer" comm.sendcmd.assert_called_with("mock") comm.read.assert_called_with(-1) @@ -148,16 +148,16 @@ def test_serialcomm_query(): comm.read.assert_called_with(10) -@raises(NotImplementedError) def test_serialcomm_seek(): - comm = SerialCommunicator(serial.Serial()) - comm.seek(1) + with pytest.raises(NotImplementedError): + comm = SerialCommunicator(serial.Serial()) + comm.seek(1) -@raises(NotImplementedError) def test_serialcomm_tell(): - comm = SerialCommunicator(serial.Serial()) - comm.tell() + with pytest.raises(NotImplementedError): + comm = SerialCommunicator(serial.Serial()) + comm.tell() def test_serialcomm_flush_input(): diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index 05307f946..59b943d4a 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -10,12 +10,12 @@ import socket -from nose.tools import raises, eq_ -import mock +import pytest import quantities as pq from instruments.abstract_instruments.comm import SocketCommunicator from instruments.tests import unit_eq +from .. import mock # TEST CASES ################################################################# @@ -29,9 +29,9 @@ def test_socketcomm_init(): assert comm._conn == socket_object -@raises(TypeError) def test_socketcomm_init_wrong_filelike(): - _ = SocketCommunicator("derp") + with pytest.raises(TypeError): + _ = SocketCommunicator("derp") def test_socketcomm_address(): @@ -39,33 +39,33 @@ def test_socketcomm_address(): comm._conn = mock.MagicMock() comm._conn.getpeername.return_value = "127.0.0.1", 1234 - eq_(comm.address, ("127.0.0.1", 1234)) + assert comm.address == ("127.0.0.1", 1234) comm._conn.getpeername.assert_called_with() -@raises(NotImplementedError) def test_socketcomm_address_setting(): - comm = SocketCommunicator(socket.socket()) - comm.address = "foobar" + with pytest.raises(NotImplementedError): + comm = SocketCommunicator(socket.socket()) + comm.address = "foobar" def test_socketcomm_terminator(): comm = SocketCommunicator(socket.socket()) # Default terminator should be \n - eq_(comm.terminator, "\n") + assert comm.terminator == "\n" comm.terminator = b"*" - eq_(comm.terminator, "*") - eq_(comm._terminator, "*") + assert comm.terminator == "*" + assert comm._terminator == "*" comm.terminator = u"\r" - eq_(comm.terminator, u"\r") - eq_(comm._terminator, u"\r") + assert comm.terminator == u"\r" + assert comm._terminator == u"\r" comm.terminator = "\r\n" - eq_(comm.terminator, "\r\n") - eq_(comm._terminator, "\r\n") + assert comm.terminator == "\r\n" + assert comm._terminator == "\r\n" def test_socketcomm_timeout(): @@ -97,7 +97,7 @@ def test_socketcomm_read_raw(): comm._conn = mock.MagicMock() comm._conn.recv = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\n"]) - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" comm._conn.recv.assert_has_calls([mock.call(1)]*4) assert comm._conn.recv.call_count == 4 @@ -112,18 +112,18 @@ def test_loopbackcomm_read_raw_2char_terminator(): comm._conn.recv = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\r", b"\n"]) comm._terminator = "\r\n" - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" comm._conn.recv.assert_has_calls([mock.call(1)] * 5) assert comm._conn.recv.call_count == 5 -@raises(IOError) def test_serialcomm_read_raw_timeout(): - comm = SocketCommunicator(socket.socket()) - comm._conn = mock.MagicMock() - comm._conn.recv = mock.MagicMock(side_effect=[b"a", b"b", b""]) + with pytest.raises(IOError): + comm = SocketCommunicator(socket.socket()) + comm._conn = mock.MagicMock() + comm._conn.recv = mock.MagicMock(side_effect=[b"a", b"b", b""]) - _ = comm.read_raw(-1) + _ = comm.read_raw(-1) def test_socketcomm_write_raw(): @@ -148,7 +148,7 @@ def test_socketcomm_query(): comm.read = mock.MagicMock(return_value="answer") comm.sendcmd = mock.MagicMock() - eq_(comm._query("mock"), "answer") + assert comm._query("mock") == "answer" comm.sendcmd.assert_called_with("mock") comm.read.assert_called_with(-1) @@ -156,16 +156,16 @@ def test_socketcomm_query(): comm.read.assert_called_with(10) -@raises(NotImplementedError) def test_socketcomm_seek(): - comm = SocketCommunicator(socket.socket()) - comm.seek(1) + with pytest.raises(NotImplementedError): + comm = SocketCommunicator(socket.socket()) + comm.seek(1) -@raises(NotImplementedError) def test_socketcomm_tell(): - comm = SocketCommunicator(socket.socket()) - comm.tell() + with pytest.raises(NotImplementedError): + comm = SocketCommunicator(socket.socket()) + comm.tell() def test_socketcomm_flush_input(): diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index da56749af..988b928d3 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -8,14 +8,14 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest import quantities as pq from numpy import array from instruments.abstract_instruments.comm import USBTMCCommunicator from instruments.tests import unit_eq +from .. import mock # TEST CASES ################################################################# @@ -30,10 +30,10 @@ def test_usbtmccomm_init(mock_usbtmc): mock_usbtmc.Instrument.assert_called_with("foobar", var1=123) -@raises(ImportError) @mock.patch(patch_path, new=None) def test_usbtmccomm_init_missing_module(): - _ = USBTMCCommunicator() + with pytest.raises(ImportError): + _ = USBTMCCommunicator() @mock.patch(patch_path) @@ -43,7 +43,7 @@ def test_usbtmccomm_terminator_getter(mock_usbtmc): term_char = mock.PropertyMock(return_value=10) type(comm._filelike).term_char = term_char - eq_(comm.terminator, "\n") + assert comm.terminator == "\n" term_char.assert_called_with() @@ -55,11 +55,11 @@ def test_usbtmccomm_terminator_setter(mock_usbtmc): type(comm._filelike).term_char = term_char comm.terminator = "*" - eq_(comm._terminator, "*") + assert comm._terminator == "*" term_char.assert_called_with(42) comm.terminator = b"*" - eq_(comm._terminator, "*") + assert comm._terminator == "*" term_char.assert_called_with(42) @@ -93,7 +93,7 @@ def test_usbtmccomm_read_raw(mock_usbtmc): comm = USBTMCCommunicator() comm._filelike.read_raw = mock.MagicMock(return_value=b"abc") - eq_(comm.read_raw(), b"abc") + assert comm.read_raw() == b"abc" comm._filelike.read_raw.assert_called_with(num=-1) assert comm._filelike.read_raw.call_count == 1 @@ -124,25 +124,25 @@ def test_usbtmccomm_query(mock_usbtmc): comm = USBTMCCommunicator() comm._filelike.ask = mock.MagicMock(return_value="answer") - eq_(comm._query("mock"), "answer") + assert comm._query("mock") == "answer" comm._filelike.ask.assert_called_with("mock", num=-1, encoding="utf-8") comm._query("mock", size=10) comm._filelike.ask.assert_called_with("mock", num=10, encoding="utf-8") -@raises(NotImplementedError) @mock.patch(patch_path) def test_usbtmccomm_seek(mock_usbtmc): - comm = USBTMCCommunicator() - comm.seek(1) + with pytest.raises(NotImplementedError): + comm = USBTMCCommunicator() + comm.seek(1) -@raises(NotImplementedError) @mock.patch(patch_path) def test_usbtmccomm_tell(mock_usbtmc): - comm = USBTMCCommunicator() - comm.tell() + with pytest.raises(NotImplementedError): + comm = USBTMCCommunicator() + comm.tell() @mock.patch(patch_path) diff --git a/instruments/tests/test_comm/test_vxi11.py b/instruments/tests/test_comm/test_vxi11.py index 60dd4a899..f7925c459 100644 --- a/instruments/tests/test_comm/test_vxi11.py +++ b/instruments/tests/test_comm/test_vxi11.py @@ -8,10 +8,10 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest from instruments.abstract_instruments.comm import VXI11Communicator +from .. import mock # TEST CASES ################################################################# @@ -26,10 +26,10 @@ def test_vxi11comm_init(mock_vxi11): mock_vxi11.Instrument.assert_called_with("host") -@raises(ImportError) @mock.patch(import_base, new=None) def test_vxi11comm_init_no_vxi11(): - _ = VXI11Communicator("host") + with pytest.raises(ImportError): + _ = VXI11Communicator("host") @mock.patch(import_base) @@ -45,7 +45,7 @@ def test_vxi11comm_address(mock_vxi11): type(comm._inst).name = name # Check that our address function is working - eq_(comm.address, ["host", "name"]) + assert comm.address == ["host", "name"] host.assert_called_with() name.assert_called_with() @@ -57,7 +57,7 @@ def test_vxi11comm_terminator(mock_vxi11): term_char = mock.PropertyMock(return_value="\n") type(comm._inst).term_char = term_char - eq_(comm.terminator, "\n") + assert comm.terminator == "\n" term_char.assert_called_with() comm.terminator = "*" @@ -71,7 +71,7 @@ def test_vxi11comm_timeout(mock_vxi11): timeout = mock.PropertyMock(return_value=30) type(comm._inst).timeout = timeout - eq_(comm.timeout, 30) + assert comm.timeout == 30 timeout.assert_called_with() comm.timeout = 10 @@ -100,7 +100,7 @@ def test_vxi11comm_read(mock_vxi11): comm = VXI11Communicator() comm._inst.read_raw.return_value = b"mock" - eq_(comm.read_raw(), b"mock") + assert comm.read_raw() == b"mock" comm._inst.read_raw.assert_called_with(num=-1) comm.read(10) @@ -128,29 +128,29 @@ def test_vxi11comm_query(mock_vxi11): comm = VXI11Communicator() comm._inst.ask.return_value = "answer" - eq_(comm._query("mock"), "answer") + assert comm._query("mock") == "answer" comm._inst.ask.assert_called_with("mock", num=-1) comm._query("mock", size=10) comm._inst.ask.assert_called_with("mock", num=10) -@raises(NotImplementedError) @mock.patch(import_base) def test_vxi11comm_seek(mock_vxi11): - comm = VXI11Communicator() - comm.seek(1) + with pytest.raises(NotImplementedError): + comm = VXI11Communicator() + comm.seek(1) -@raises(NotImplementedError) @mock.patch(import_base) def test_vxi11comm_tell(mock_vxi11): - comm = VXI11Communicator() - comm.tell() + with pytest.raises(NotImplementedError): + comm = VXI11Communicator() + comm.tell() -@raises(NotImplementedError) @mock.patch(import_base) def test_vxi11comm_flush(mock_vxi11): - comm = VXI11Communicator() - comm.flush_input() + with pytest.raises(NotImplementedError): + comm = VXI11Communicator() + comm.flush_input() diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index 8e531f586..4245b92d2 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -9,11 +9,11 @@ from __future__ import absolute_import import quantities as pq -import mock import instruments as ik from instruments.tests import expected_protocol from instruments.units import dBm +from .. import mock # TEST CLASSES ################################################################ diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index eb97a2b85..555a8c459 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -10,7 +10,7 @@ import quantities as pq import numpy as np -from nose.tools import raises +import pytest import instruments as ik from instruments.tests import expected_protocol @@ -34,20 +34,20 @@ def test_hp3456a_trigger_mode(): dmm.trigger_mode = dmm.TriggerMode.hold -@raises(ValueError) def test_hp3456a_number_of_digits(): - with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W6STG", - "REG" - ], [ - "+06.00000E+0" - ], - sep="\r" - ) as dmm: - dmm.number_of_digits = 7 + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP3456a, + [ + "HO0T4SO1", + "W6STG", + "REG" + ], [ + "+06.00000E+0" + ], + sep="\r" + ) as dmm: + dmm.number_of_digits = 7 def test_hp3456a_number_of_digits_invalid(): @@ -112,20 +112,20 @@ def test_hp3456a_nplc(): assert dmm.nplc == 1 -@raises(ValueError) def test_hp3456a_nplc_invalid(): - with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W1STI", - "REI" - ], [ - "+1.00000E+0" - ], - sep="\r" - ) as dmm: - dmm.nplc = 0 + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP3456a, + [ + "HO0T4SO1", + "W1STI", + "REI" + ], [ + "+1.00000E+0" + ], + sep="\r" + ) as dmm: + dmm.nplc = 0 def test_hp3456a_mode(): @@ -350,48 +350,48 @@ def test_hp3456a_input_range(): dmm.input_range = 1e3 * pq.ohm -@raises(ValueError) def test_hp3456a_input_range_invalid_str(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm.input_range = "derp" + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm.input_range = "derp" -@raises(ValueError) def test_hp3456a_input_range_invalid_range(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm.input_range = 1 * pq.ohm + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm.input_range = 1 * pq.ohm -@raises(TypeError) def test_hp3456a_input_range_bad_type(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm.input_range = True + with pytest.raises(TypeError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm.input_range = True -@raises(ValueError) def test_hp3456a_input_range_bad_units(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm.input_range = 1 * pq.amp + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm.input_range = 1 * pq.amp def test_hp3456a_relative(): @@ -411,15 +411,15 @@ def test_hp3456a_relative(): assert dmm.relative is True -@raises(TypeError) def test_hp3456a_relative_bad_type(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm.relative = "derp" + with pytest.raises(TypeError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm.relative = "derp" def test_hp3456a_auto_zero(): @@ -454,34 +454,34 @@ def test_hp3456a_filter(): dmm.filter = True -@raises(TypeError) def test_hp3456a_register_read_bad_name(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm._register_read("foobar") + with pytest.raises(TypeError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm._register_read("foobar") -@raises(TypeError) def test_hp3456a_register_write_bad_name(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm._register_write("foobar", 1) + with pytest.raises(TypeError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm._register_write("foobar", 1) -@raises(ValueError) def test_hp3456a_register_write_bad_register(): - with expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" - ) as dmm: - dmm._register_write(dmm.Register.mean, 1) + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP3456a, + [], + [], + sep="\r" + ) as dmm: + dmm._register_write(dmm.Register.mean, 1) diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 93208b728..215874865 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -9,11 +9,11 @@ from __future__ import absolute_import import quantities as pq -import mock -from nose.tools import raises +import pytest import instruments as ik from instruments.tests import expected_protocol +from .. import mock # TESTS ####################################################################### @@ -206,15 +206,15 @@ def test_all_voltage(): hp.voltage = (1 * pq.V, 2 * pq.V, 3 * pq.V, 4 * pq.V) -@raises(ValueError) def test_all_voltage_wrong_length(): - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: - hp.voltage = (1 * pq.volt, 2 * pq.volt) + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP6624a, + [], + [], + sep="\n" + ) as hp: + hp.voltage = (1 * pq.volt, 2 * pq.volt) def test_all_current(): @@ -249,15 +249,15 @@ def test_all_current(): hp.current = (1 * pq.A, 2 * pq.A, 3 * pq.A, 4 * pq.A) -@raises(ValueError) def test_all_current_wrong_length(): - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: - hp.current = (1 * pq.amp, 2 * pq.amp) + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP6624a, + [], + [], + sep="\n" + ) as hp: + hp.current = (1 * pq.amp, 2 * pq.amp) def test_all_voltage_sense(): @@ -323,23 +323,23 @@ def test_channel_count(): hp.channel_count = 3 -@raises(TypeError) def test_channel_count_wrong_type(): - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: - hp.channel_count = "foobar" + with pytest.raises(TypeError): + with expected_protocol( + ik.hp.HP6624a, + [], + [], + sep="\n" + ) as hp: + hp.channel_count = "foobar" -@raises(ValueError) def test_channel_count_too_small(): - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: - hp.channel_count = 0 + with pytest.raises(ValueError): + with expected_protocol( + ik.hp.HP6624a, + [], + [], + sep="\n" + ) as hp: + hp.channel_count = 0 diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 02303b0da..8d7878948 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -10,7 +10,7 @@ import quantities as pq import numpy as np -from nose.tools import raises +import pytest import instruments as ik from instruments.tests import expected_protocol @@ -73,23 +73,23 @@ def test_channel_measure_temperature(): assert channel.measure() == 1.234 * pq.celsius -@raises(ValueError) def test_channel_measure_unknown_temperature_units(): - with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:CHAN 1", - "SENS:DATA:FRES?", - "SENS:FUNC?", - "UNIT:TEMP?" - ], - [ - "1.234", - "TEMP", - "Z" - ] - ) as inst: - inst.channel[0].measure() + with pytest.raises(ValueError): + with expected_protocol( + ik.keithley.Keithley2182, + [ + "SENS:CHAN 1", + "SENS:DATA:FRES?", + "SENS:FUNC?", + "UNIT:TEMP?" + ], + [ + "1.234", + "TEMP", + "Z" + ] + ) as inst: + inst.channel[0].measure() def test_units(): @@ -166,14 +166,14 @@ def test_measure(): assert inst.measure() == 1.234 * pq.volt -@raises(TypeError) def test_measure_invalid_mode(): - with expected_protocol( - ik.keithley.Keithley2182, - [], - [] - ) as inst: - inst.measure("derp") + with pytest.raises(TypeError): + with expected_protocol( + ik.keithley.Keithley2182, + [], + [] + ) as inst: + inst.measure("derp") def test_relative_get(): @@ -227,11 +227,11 @@ def test_relative_set_start_disabled(): inst.relative = True -@raises(TypeError) def test_relative_set_wrong_type(): - with expected_protocol( - ik.keithley.Keithley2182, - [], - [] - ) as inst: - inst.relative = "derp" + with pytest.raises(TypeError): + with expected_protocol( + ik.keithley.Keithley2182, + [], + [] + ) as inst: + inst.relative = "derp" diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index 167e29ff7..9864e5bda 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import quantities as pq -from nose.tools import raises +import pytest import instruments as ik from instruments.tests import expected_protocol @@ -28,10 +28,10 @@ def test_valid_range(): assert inst._valid_range(inst.Mode.charge) == inst.ValidRange.charge -@raises(ValueError) def test_valid_range_invalid(): - inst = ik.keithley.Keithley6514.open_test() - inst._valid_range(inst.TriggerMode.immediate) + with pytest.raises(ValueError): + inst = ik.keithley.Keithley6514.open_test() + inst._valid_range(inst.TriggerMode.immediate) def test_parse_measurement(): @@ -176,18 +176,18 @@ def test_input_range(): inst.input_range = 20 * pq.volt -@raises(ValueError) def test_input_range_invalid(): - with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?" - ], - [ - '"VOLT:DC"' - ] - ) as inst: - inst.input_range = 10 * pq.volt + with pytest.raises(ValueError): + with expected_protocol( + ik.keithley.Keithley6514, + [ + "FUNCTION?" + ], + [ + '"VOLT:DC"' + ] + ) as inst: + inst.input_range = 10 * pq.volt def test_auto_config(): diff --git a/instruments/tests/test_ondax/test_lm.py b/instruments/tests/test_ondax/test_lm.py index 93c89de29..8d7477cdc 100644 --- a/instruments/tests/test_ondax/test_lm.py +++ b/instruments/tests/test_ondax/test_lm.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises +import pytest import quantities @@ -62,15 +62,15 @@ def test_acc_disable(): assert not lm.acc.enabled -@raises(TypeError) def test_acc_enable_not_boolean(): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: - lm.acc.enabled = "foobar" + with pytest.raises(TypeError): + with expected_protocol( + ondax.LM, + [], + [], + sep="\r" + ) as lm: + lm.acc.enabled = "foobar" def test_acc_on(): @@ -145,15 +145,15 @@ def test_apc_disable(): assert not lm.apc.enabled -@raises(TypeError) def test_apc_enable_not_boolean(): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: - lm.apc.enabled = "foobar" + with pytest.raises(TypeError): + with expected_protocol( + ondax.LM, + [], + [], + sep="\r" + ) as lm: + lm.apc.enabled = "foobar" @@ -249,15 +249,15 @@ def test_modulation_disabled(): assert not lm.modulation.enabled -@raises(TypeError) def test_modulation_enable_not_boolean(): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: - lm.modulation.enabled = "foobar" + with pytest.raises(TypeError): + with expected_protocol( + ondax.LM, + [], + [], + sep="\r" + ) as lm: + lm.modulation.enabled = "foobar" def test_tec_current(): @@ -318,15 +318,15 @@ def test_tec_disable(): assert not lm.tec.enabled -@raises(TypeError) def test_tec_enable_not_boolean(): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: - lm.tec.enabled = "foobar" + with pytest.raises(TypeError): + with expected_protocol( + ondax.LM, + [], + [], + sep="\r" + ) as lm: + lm.tec.enabled = "foobar" def test_firmware(): @@ -469,15 +469,15 @@ def test_disable(): assert not lm.enabled -@raises(TypeError) def test_enable_not_boolean(): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: - lm.enabled = "foobar" + with pytest.raises(TypeError): + with expected_protocol( + ondax.LM, + [], + [], + sep="\r" + ) as lm: + lm.enabled = "foobar" def test_save(): diff --git a/instruments/tests/test_property_factories/test_bool_property.py b/instruments/tests/test_property_factories/test_bool_property.py index 01995485e..cb3ab923c 100644 --- a/instruments/tests/test_property_factories/test_bool_property.py +++ b/instruments/tests/test_property_factories/test_bool_property.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ +import pytest from instruments.util_fns import bool_property from . import MockInstrument @@ -25,13 +25,13 @@ class BoolMock(MockInstrument): mock_inst = BoolMock({'MOCK1?': 'OFF', 'MOCK2?': 'YES'}) - eq_(mock_inst.mock1, False) - eq_(mock_inst.mock2, True) + assert mock_inst.mock1 is False + assert mock_inst.mock2 is True mock_inst.mock1 = True mock_inst.mock2 = False - eq_(mock_inst.value, 'MOCK1?\nMOCK2?\nMOCK1 ON\nMOCK2 NO\n') + assert mock_inst.value == 'MOCK1?\nMOCK2?\nMOCK1 ON\nMOCK2 NO\n' def test_bool_property_set_fmt(): @@ -42,17 +42,17 @@ class BoolMock(MockInstrument): mock_instrument.mock1 = True - eq_(mock_instrument.value, 'MOCK1=ON\n') + assert mock_instrument.value == 'MOCK1=ON\n' -@raises(AttributeError) def test_bool_property_readonly_writing_fails(): - class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', readonly=True) + with pytest.raises(AttributeError): + class BoolMock(MockInstrument): + mock1 = bool_property('MOCK1', readonly=True) - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({'MOCK1?': 'OFF'}) - mock_instrument.mock1 = True + mock_instrument.mock1 = True def test_bool_property_readonly_reading_passes(): @@ -61,17 +61,17 @@ class BoolMock(MockInstrument): mock_instrument = BoolMock({'MOCK1?': 'OFF'}) - eq_(mock_instrument.mock1, False) + assert mock_instrument.mock1 is False -@raises(AttributeError) def test_bool_property_writeonly_reading_fails(): - class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', writeonly=True) + with pytest.raises(AttributeError): + class BoolMock(MockInstrument): + mock1 = bool_property('MOCK1', writeonly=True) - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({'MOCK1?': 'OFF'}) - _ = mock_instrument.mock1 + _ = mock_instrument.mock1 def test_bool_property_writeonly_writing_passes(): @@ -89,7 +89,7 @@ class BoolMock(MockInstrument): mock_inst = BoolMock({'MOCK1?': 'OFF'}) - eq_(mock_inst.mock1, False) + assert mock_inst.mock1 is False mock_inst.mock1 = True - eq_(mock_inst.value, 'MOCK1?\nFOOBAR ON\n') + assert mock_inst.value == 'MOCK1?\nFOOBAR ON\n' diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index d982dc989..8826f0c8e 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -8,12 +8,12 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ -import mock +import pytest import quantities as pq from instruments.util_fns import bounded_unitful_property from . import MockInstrument +from .. import mock # TEST CASES ################################################################# @@ -30,39 +30,39 @@ class BoundedUnitfulMock(MockInstrument): mock_inst = BoundedUnitfulMock( {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) - eq_(mock_inst.property, 1000 * pq.hertz) - eq_(mock_inst.property_min, 10 * pq.hertz) - eq_(mock_inst.property_max, 9999 * pq.hertz) + assert mock_inst.property == 1000 * pq.hertz + assert mock_inst.property_min == 10 * pq.hertz + assert mock_inst.property_max == 9999 * pq.hertz mock_inst.property = 1000 * pq.hertz -@raises(ValueError) def test_bounded_unitful_property_set_outside_max(): - class BoundedUnitfulMock(MockInstrument): - property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=pq.hertz - ) + with pytest.raises(ValueError): + class BoundedUnitfulMock(MockInstrument): + property, property_min, property_max = bounded_unitful_property( + 'MOCK', + units=pq.hertz + ) - mock_inst = BoundedUnitfulMock( - {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) + mock_inst = BoundedUnitfulMock( + {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) - mock_inst.property = 10000 * pq.hertz # Should raise ValueError + mock_inst.property = 10000 * pq.hertz # Should raise ValueError -@raises(ValueError) def test_bounded_unitful_property_set_outside_min(): - class BoundedUnitfulMock(MockInstrument): - property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=pq.hertz - ) + with pytest.raises(ValueError): + class BoundedUnitfulMock(MockInstrument): + property, property_min, property_max = bounded_unitful_property( + 'MOCK', + units=pq.hertz + ) - mock_inst = BoundedUnitfulMock( - {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) + mock_inst = BoundedUnitfulMock( + {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) - mock_inst.property = 1 * pq.hertz # Should raise ValueError + mock_inst.property = 1 * pq.hertz # Should raise ValueError def test_bounded_unitful_property_min_fmt_str(): @@ -75,8 +75,8 @@ class BoundedUnitfulMock(MockInstrument): mock_inst = BoundedUnitfulMock({'MOCK MIN?': '10'}) - eq_(mock_inst.property_min, 10 * pq.Hz) - eq_(mock_inst.value, 'MOCK MIN?\n') + assert mock_inst.property_min == 10 * pq.Hz + assert mock_inst.value == 'MOCK MIN?\n' def test_bounded_unitful_property_max_fmt_str(): @@ -89,8 +89,8 @@ class BoundedUnitfulMock(MockInstrument): mock_inst = BoundedUnitfulMock({'MOCK MAX?': '9999'}) - eq_(mock_inst.property_max, 9999 * pq.Hz) - eq_(mock_inst.value, 'MOCK MAX?\n') + assert mock_inst.property_max == 9999 * pq.Hz + assert mock_inst.value == 'MOCK MAX?\n' def test_bounded_unitful_property_static_range(): @@ -103,8 +103,8 @@ class BoundedUnitfulMock(MockInstrument): mock_inst = BoundedUnitfulMock() - eq_(mock_inst.property_min, 10 * pq.Hz) - eq_(mock_inst.property_max, 9999 * pq.Hz) + assert mock_inst.property_min == 10 * pq.Hz + assert mock_inst.property_max == 9999 * pq.Hz def test_bounded_unitful_property_static_range_with_units(): @@ -117,8 +117,8 @@ class BoundedUnitfulMock(MockInstrument): mock_inst = BoundedUnitfulMock() - eq_(mock_inst.property_min, 10 * 1000 * pq.Hz) - eq_(mock_inst.property_max, 9999 * 1000 * pq.Hz) + assert mock_inst.property_min == 10 * 1000 * pq.Hz + assert mock_inst.property_max == 9999 * 1000 * pq.Hz @mock.patch("instruments.util_fns.unitful_property") @@ -160,5 +160,5 @@ class BoundedUnitfulMock(MockInstrument): mock_inst = BoundedUnitfulMock() - eq_(mock_inst.property_min, None) - eq_(mock_inst.property_max, None) + assert mock_inst.property_min is None + assert mock_inst.property_max is None diff --git a/instruments/tests/test_property_factories/test_enum_property.py b/instruments/tests/test_property_factories/test_enum_property.py index 862a55df2..55be52610 100644 --- a/instruments/tests/test_property_factories/test_enum_property.py +++ b/instruments/tests/test_property_factories/test_enum_property.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from enum import Enum, IntEnum -from nose.tools import raises, eq_ +import pytest from instruments.util_fns import enum_property from . import MockInstrument @@ -30,29 +30,29 @@ class EnumMock(MockInstrument): mock_inst = EnumMock({'MOCK:A?': 'aa', 'MOCK:B?': 'bb'}) - eq_(mock_inst.a, SillyEnum.a) - eq_(mock_inst.b, SillyEnum.b) + assert mock_inst.a == SillyEnum.a + assert mock_inst.b == SillyEnum.b # Test EnumValues, string values and string names. mock_inst.a = SillyEnum.b mock_inst.b = 'a' mock_inst.b = 'bb' - eq_(mock_inst.value, 'MOCK:A?\nMOCK:B?\nMOCK:A bb\nMOCK:B aa\nMOCK:B bb\n') + assert mock_inst.value == 'MOCK:A?\nMOCK:B?\nMOCK:A bb\nMOCK:B aa\nMOCK:B bb\n' -@raises(ValueError) def test_enum_property_invalid(): - class SillyEnum(Enum): - a = 'aa' - b = 'bb' + with pytest.raises(ValueError): + class SillyEnum(Enum): + a = 'aa' + b = 'bb' - class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum) + class EnumMock(MockInstrument): + a = enum_property('MOCK:A', SillyEnum) - mock_inst = EnumMock({'MOCK:A?': 'aa', 'MOCK:B?': 'bb'}) + mock_inst = EnumMock({'MOCK:A?': 'aa', 'MOCK:B?': 'bb'}) - mock_inst.a = 'c' + mock_inst.a = 'c' def test_enum_property_set_fmt(): @@ -65,7 +65,7 @@ class EnumMock(MockInstrument): mock_instrument = EnumMock() mock_instrument.a = 'aa' - eq_(mock_instrument.value, 'MOCK:A=aa\n') + assert mock_instrument.value == 'MOCK:A=aa\n' def test_enum_property_input_decoration(): @@ -85,7 +85,7 @@ def _input_decorator(_): mock_instrument = EnumMock({'MOCK:A?': 'garbage'}) - eq_(mock_instrument.a, SillyEnum.a) + assert mock_instrument.a == SillyEnum.a def test_enum_property_input_decoration_not_a_function(): @@ -102,7 +102,7 @@ class EnumMock(MockInstrument): mock_instrument = EnumMock({'MOCK:A?': '1'}) - eq_(mock_instrument.a, SillyEnum.a) + assert mock_instrument.a == SillyEnum.a def test_enum_property_output_decoration(): @@ -124,7 +124,7 @@ def _output_decorator(_): mock_instrument.a = SillyEnum.a - eq_(mock_instrument.value, 'MOCK:A foobar\n') + assert mock_instrument.value == 'MOCK:A foobar\n' def test_enum_property_output_decoration_not_a_function(): @@ -143,20 +143,20 @@ class EnumMock(MockInstrument): mock_instrument.a = SillyEnum.a - eq_(mock_instrument.value, 'MOCK:A 0.23\n') + assert mock_instrument.value == 'MOCK:A 0.23\n' -@raises(AttributeError) def test_enum_property_writeonly_reading_fails(): - class SillyEnum(Enum): - a = 'aa' + with pytest.raises(AttributeError): + class SillyEnum(Enum): + a = 'aa' - class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, writeonly=True) + class EnumMock(MockInstrument): + a = enum_property('MOCK:A', SillyEnum, writeonly=True) - mock_instrument = EnumMock() + mock_instrument = EnumMock() - _ = mock_instrument.a + _ = mock_instrument.a def test_enum_property_writeonly_writing_passes(): @@ -169,20 +169,20 @@ class EnumMock(MockInstrument): mock_instrument = EnumMock() mock_instrument.a = SillyEnum.a - eq_(mock_instrument.value, 'MOCK:A aa\n') + assert mock_instrument.value == 'MOCK:A aa\n' -@raises(AttributeError) def test_enum_property_readonly_writing_fails(): - class SillyEnum(Enum): - a = 'aa' + with pytest.raises(AttributeError): + class SillyEnum(Enum): + a = 'aa' - class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, readonly=True) + class EnumMock(MockInstrument): + a = enum_property('MOCK:A', SillyEnum, readonly=True) - mock_instrument = EnumMock({'MOCK:A?': 'aa'}) + mock_instrument = EnumMock({'MOCK:A?': 'aa'}) - mock_instrument.a = SillyEnum.a + mock_instrument.a = SillyEnum.a def test_enum_property_readonly_reading_passes(): @@ -194,8 +194,8 @@ class EnumMock(MockInstrument): mock_instrument = EnumMock({'MOCK:A?': 'aa'}) - eq_(mock_instrument.a, SillyEnum.a) - eq_(mock_instrument.value, 'MOCK:A?\n') + assert mock_instrument.a == SillyEnum.a + assert mock_instrument.value == 'MOCK:A?\n' def test_enum_property_set_cmd(): @@ -207,7 +207,7 @@ class EnumMock(MockInstrument): mock_inst = EnumMock({'MOCK:A?': 'aa'}) - eq_(mock_inst.a, SillyEnum.a) + assert mock_inst.a == SillyEnum.a mock_inst.a = SillyEnum.a - eq_(mock_inst.value, 'MOCK:A?\nFOOBAR:A aa\n') + assert mock_inst.value == 'MOCK:A?\nFOOBAR:A aa\n' diff --git a/instruments/tests/test_property_factories/test_int_property.py b/instruments/tests/test_property_factories/test_int_property.py index 1a9ab2340..d92249211 100644 --- a/instruments/tests/test_property_factories/test_int_property.py +++ b/instruments/tests/test_property_factories/test_int_property.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ +import pytest from instruments.util_fns import int_property from . import MockInstrument @@ -18,13 +18,13 @@ # pylint: disable=missing-docstring -@raises(ValueError) def test_int_property_outside_valid_set(): - class IntMock(MockInstrument): - mock_property = int_property('MOCK', valid_set=set([1, 2])) + with pytest.raises(ValueError): + class IntMock(MockInstrument): + mock_property = int_property('MOCK', valid_set=set([1, 2])) - mock_inst = IntMock() - mock_inst.mock_property = 3 + mock_inst = IntMock() + mock_inst.mock_property = 3 def test_int_property_valid_set(): @@ -33,10 +33,10 @@ class IntMock(MockInstrument): mock_inst = IntMock({'MOCK?': '1'}) - eq_(mock_inst.int_property, 1) + assert mock_inst.int_property == 1 mock_inst.int_property = 2 - eq_(mock_inst.value, 'MOCK?\nMOCK 2\n') + assert mock_inst.value == 'MOCK?\nMOCK 2\n' def test_int_property_no_set(): @@ -47,17 +47,17 @@ class IntMock(MockInstrument): mock_inst.int_property = 1 - eq_(mock_inst.value, 'MOCK 1\n') + assert mock_inst.value == 'MOCK 1\n' -@raises(AttributeError) def test_int_property_writeonly_reading_fails(): - class IntMock(MockInstrument): - int_property = int_property('MOCK', writeonly=True) + with pytest.raises(AttributeError): + class IntMock(MockInstrument): + int_property = int_property('MOCK', writeonly=True) - mock_inst = IntMock() + mock_inst = IntMock() - _ = mock_inst.int_property + _ = mock_inst.int_property def test_int_property_writeonly_writing_passes(): @@ -67,17 +67,17 @@ class IntMock(MockInstrument): mock_inst = IntMock() mock_inst.int_property = 1 - eq_(mock_inst.value, 'MOCK {:d}\n'.format(1)) + assert mock_inst.value == 'MOCK {:d}\n'.format(1) -@raises(AttributeError) def test_int_property_readonly_writing_fails(): - class IntMock(MockInstrument): - int_property = int_property('MOCK', readonly=True) + with pytest.raises(AttributeError): + class IntMock(MockInstrument): + int_property = int_property('MOCK', readonly=True) - mock_inst = IntMock({'MOCK?': '1'}) + mock_inst = IntMock({'MOCK?': '1'}) - mock_inst.int_property = 1 + mock_inst.int_property = 1 def test_int_property_readonly_reading_passes(): @@ -86,7 +86,7 @@ class IntMock(MockInstrument): mock_inst = IntMock({'MOCK?': '1'}) - eq_(mock_inst.int_property, 1) + assert mock_inst.int_property == 1 def test_int_property_format_code(): @@ -96,7 +96,7 @@ class IntMock(MockInstrument): mock_inst = IntMock() mock_inst.int_property = 1 - eq_(mock_inst.value, 'MOCK {:e}\n'.format(1)) + assert mock_inst.value == 'MOCK {:e}\n'.format(1) def test_int_property_set_cmd(): @@ -105,7 +105,7 @@ class IntMock(MockInstrument): mock_inst = IntMock({'MOCK?': '1'}) - eq_(mock_inst.int_property, 1) + assert mock_inst.int_property == 1 mock_inst.int_property = 1 - eq_(mock_inst.value, 'MOCK?\nFOOBAR 1\n') + assert mock_inst.value == 'MOCK?\nFOOBAR 1\n' diff --git a/instruments/tests/test_property_factories/test_rproperty.py b/instruments/tests/test_property_factories/test_rproperty.py index 9e64235a8..7ba644598 100644 --- a/instruments/tests/test_property_factories/test_rproperty.py +++ b/instruments/tests/test_property_factories/test_rproperty.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ +import pytest from instruments.util_fns import rproperty from . import MockInstrument @@ -35,23 +35,23 @@ def mockset(self, newval): mock_inst = Mock() mock_inst.mockproperty = 1 - eq_(mock_inst.mockproperty, 1) + assert mock_inst.mockproperty == 1 -@raises(AttributeError) def test_rproperty_readonly_writing_fails(): - class Mock(MockInstrument): + with pytest.raises(AttributeError): + class Mock(MockInstrument): - def __init__(self): - super(Mock, self).__init__() - self._value = 0 + def __init__(self): + super(Mock, self).__init__() + self._value = 0 - def mockset(self, newval): # pragma: no cover - self._value = newval - mockproperty = rproperty(fget=None, fset=mockset, readonly=True) + def mockset(self, newval): # pragma: no cover + self._value = newval + mockproperty = rproperty(fget=None, fset=mockset, readonly=True) - mock_inst = Mock() - mock_inst.mockproperty = 1 + mock_inst = Mock() + mock_inst.mockproperty = 1 def test_rproperty_readonly_reading_passes(): @@ -66,23 +66,23 @@ def mockget(self): mockproperty = rproperty(fget=mockget, fset=None, readonly=True) mock_inst = Mock() - eq_(mock_inst.mockproperty, 0) + assert mock_inst.mockproperty == 0 -@raises(AttributeError) def test_rproperty_writeonly_reading_fails(): - class Mock(MockInstrument): + with pytest.raises(AttributeError): + class Mock(MockInstrument): - def __init__(self): - super(Mock, self).__init__() - self._value = 0 + def __init__(self): + super(Mock, self).__init__() + self._value = 0 - def mockget(self): # pragma: no cover - return self._value - mockproperty = rproperty(fget=mockget, fset=None, writeonly=True) + def mockget(self): # pragma: no cover + return self._value + mockproperty = rproperty(fget=mockget, fset=None, writeonly=True) - mock_inst = Mock() - eq_(mock_inst.mockproperty, 0) + mock_inst = Mock() + assert mock_inst.mockproperty == 0 def test_rproperty_writeonly_writing_passes(): @@ -100,6 +100,6 @@ def mockset(self, newval): mock_inst.mockproperty = 1 -@raises(ValueError) def test_rproperty_readonly_and_writeonly(): - _ = rproperty(readonly=True, writeonly=True) + with pytest.raises(ValueError): + _ = rproperty(readonly=True, writeonly=True) diff --git a/instruments/tests/test_property_factories/test_string_property.py b/instruments/tests/test_property_factories/test_string_property.py index e30e5998f..02cba688e 100644 --- a/instruments/tests/test_property_factories/test_string_property.py +++ b/instruments/tests/test_property_factories/test_string_property.py @@ -8,8 +8,6 @@ from __future__ import absolute_import -from nose.tools import eq_ - from instruments.util_fns import string_property from . import MockInstrument @@ -24,10 +22,10 @@ class StringMock(MockInstrument): mock_inst = StringMock({'MOCK?': '"foobar"'}) - eq_(mock_inst.mock_property, 'foobar') + assert mock_inst.mock_property == 'foobar' mock_inst.mock_property = 'foo' - eq_(mock_inst.value, 'MOCK?\nMOCK "foo"\n') + assert mock_inst.value == 'MOCK?\nMOCK "foo"\n' def test_string_property_different_bookmark_symbol(): @@ -36,10 +34,10 @@ class StringMock(MockInstrument): mock_inst = StringMock({'MOCK?': '%^foobar%^'}) - eq_(mock_inst.mock_property, 'foobar') + assert mock_inst.mock_property == 'foobar' mock_inst.mock_property = 'foo' - eq_(mock_inst.value, 'MOCK?\nMOCK %^foo%^\n') + assert mock_inst.value == 'MOCK?\nMOCK %^foo%^\n' def test_string_property_no_bookmark_symbol(): @@ -48,10 +46,10 @@ class StringMock(MockInstrument): mock_inst = StringMock({'MOCK?': 'foobar'}) - eq_(mock_inst.mock_property, 'foobar') + assert mock_inst.mock_property == 'foobar' mock_inst.mock_property = 'foo' - eq_(mock_inst.value, 'MOCK?\nMOCK foo\n') + assert mock_inst.value == 'MOCK?\nMOCK foo\n' def test_string_property_set_cmd(): @@ -60,7 +58,7 @@ class StringMock(MockInstrument): mock_inst = StringMock({'MOCK?': '"derp"'}) - eq_(mock_inst.mock_property, 'derp') + assert mock_inst.mock_property == 'derp' mock_inst.mock_property = 'qwerty' - eq_(mock_inst.value, 'MOCK?\nFOOBAR "qwerty"\n') + assert mock_inst.value == 'MOCK?\nFOOBAR "qwerty"\n' diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index 9d146236b..ebdc97bf6 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ +import pytest import quantities as pq from instruments.util_fns import unitful_property @@ -25,10 +25,10 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock({'MOCK?': '1000'}) - eq_(mock_inst.unitful_property, 1000 * pq.hertz) + assert mock_inst.unitful_property == 1000 * pq.hertz mock_inst.unitful_property = 1000 * pq.hertz - eq_(mock_inst.value, 'MOCK?\nMOCK {:e}\n'.format(1000)) + assert mock_inst.value == 'MOCK?\nMOCK {:e}\n'.format(1000) def test_unitful_property_format_code(): @@ -39,7 +39,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1000 * pq.hertz - eq_(mock_inst.value, 'MOCK {:f}\n'.format(1000)) + assert mock_inst.value == 'MOCK {:f}\n'.format(1000) def test_unitful_property_rescale_units(): @@ -49,7 +49,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1 * pq.kilohertz - eq_(mock_inst.value, 'MOCK {:e}\n'.format(1000)) + assert mock_inst.value == 'MOCK {:e}\n'.format(1000) def test_unitful_property_no_units_on_set(): @@ -59,27 +59,27 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1000 - eq_(mock_inst.value, 'MOCK {:e}\n'.format(1000)) + assert mock_inst.value == 'MOCK {:e}\n'.format(1000) -@raises(ValueError) def test_unitful_property_wrong_units(): - class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz) + with pytest.raises(ValueError): + class UnitfulMock(MockInstrument): + unitful_property = unitful_property('MOCK', pq.hertz) - mock_inst = UnitfulMock() + mock_inst = UnitfulMock() - mock_inst.unitful_property = 1 * pq.volt + mock_inst.unitful_property = 1 * pq.volt -@raises(AttributeError) def test_unitful_property_writeonly_reading_fails(): - class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz, writeonly=True) + with pytest.raises(AttributeError): + class UnitfulMock(MockInstrument): + unitful_property = unitful_property('MOCK', pq.hertz, writeonly=True) - mock_inst = UnitfulMock() + mock_inst = UnitfulMock() - _ = mock_inst.unitful_property + _ = mock_inst.unitful_property def test_unitful_property_writeonly_writing_passes(): @@ -89,17 +89,17 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1 * pq.hertz - eq_(mock_inst.value, 'MOCK {:e}\n'.format(1)) + assert mock_inst.value == 'MOCK {:e}\n'.format(1) -@raises(AttributeError) def test_unitful_property_readonly_writing_fails(): - class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz, readonly=True) + with pytest.raises(AttributeError): + class UnitfulMock(MockInstrument): + unitful_property = unitful_property('MOCK', pq.hertz, readonly=True) - mock_inst = UnitfulMock({'MOCK?': '1'}) + mock_inst = UnitfulMock({'MOCK?': '1'}) - mock_inst.unitful_property = 1 * pq.hertz + mock_inst.unitful_property = 1 * pq.hertz def test_unitful_property_readonly_reading_passes(): @@ -108,7 +108,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock({'MOCK?': '1'}) - eq_(mock_inst.unitful_property, 1 * pq.hertz) + assert mock_inst.unitful_property == 1 * pq.hertz def test_unitful_property_valid_range(): @@ -121,7 +121,7 @@ class UnitfulMock(MockInstrument): mock_inst.unitful_property = 0 mock_inst.unitful_property = 10 - eq_(mock_inst.value, 'MOCK {:e}\nMOCK {:e}\n'.format(0, 10)) + assert mock_inst.value == 'MOCK {:e}\nMOCK {:e}\n'.format(0, 10) def test_unitful_property_valid_range_functions(): @@ -141,29 +141,29 @@ def max_value(self): mock_inst.unitful_property = 0 mock_inst.unitful_property = 10 - eq_(mock_inst.value, 'MOCK {:e}\nMOCK {:e}\n'.format(0, 10)) + assert mock_inst.value == 'MOCK {:e}\nMOCK {:e}\n'.format(0, 10) -@raises(ValueError) def test_unitful_property_minimum_value(): - class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(0, 10)) + with pytest.raises(ValueError): + class UnitfulMock(MockInstrument): + unitful_property = unitful_property( + 'MOCK', pq.hertz, valid_range=(0, 10)) - mock_inst = UnitfulMock() + mock_inst = UnitfulMock() - mock_inst.unitful_property = -1 + mock_inst.unitful_property = -1 -@raises(ValueError) def test_unitful_property_maximum_value(): - class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(0, 10)) + with pytest.raises(ValueError): + class UnitfulMock(MockInstrument): + unitful_property = unitful_property( + 'MOCK', pq.hertz, valid_range=(0, 10)) - mock_inst = UnitfulMock() + mock_inst = UnitfulMock() - mock_inst.unitful_property = 11 + mock_inst.unitful_property = 11 def test_unitful_property_input_decoration(): @@ -180,7 +180,7 @@ def _input_decorator(_): mock_instrument = UnitfulMock({'MOCK:A?': 'garbage'}) - eq_(mock_instrument.a, 1 * pq.Hz) + assert mock_instrument.a == 1 * pq.Hz def test_unitful_property_input_decoration_not_a_function(): @@ -194,7 +194,7 @@ class UnitfulMock(MockInstrument): mock_instrument = UnitfulMock({'MOCK:A?': '.123'}) - eq_(mock_instrument.a, 0.123 * pq.Hz) + assert mock_instrument.a == 0.123 * pq.Hz def test_unitful_property_output_decoration(): @@ -213,7 +213,7 @@ def _output_decorator(_): mock_instrument.a = 345 * pq.hertz - eq_(mock_instrument.value, 'MOCK:A 1\n') + assert mock_instrument.value == 'MOCK:A 1\n' def test_unitful_property_output_decoration_not_a_function(): @@ -229,7 +229,7 @@ class UnitfulMock(MockInstrument): mock_instrument.a = 1 * pq.hertz - eq_(mock_instrument.value, 'MOCK:A True\n') + assert mock_instrument.value == 'MOCK:A True\n' def test_unitful_property_split_str(): @@ -253,7 +253,7 @@ class UnitfulMock(MockInstrument): ) mock_inst = UnitfulMock({'MOCK?': '1000'}) - eq_(mock_inst.a, 1000 * pq.hertz) + assert mock_inst.a == 1000 * pq.hertz mock_inst.a = 1000 * pq.hertz - eq_(mock_inst.value, 'MOCK?\nFOOBAR {:e}\n'.format(1000)) + assert mock_inst.value == 'MOCK?\nFOOBAR {:e}\n'.format(1000) diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index 7698f2aaf..5a4616763 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises, eq_ +import pytest import quantities as pq from instruments.util_fns import unitless_property @@ -25,20 +25,20 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock({'MOCK?': '1'}) - eq_(mock_inst.mock_property, 1) + assert mock_inst.mock_property == 1 mock_inst.mock_property = 1 - eq_(mock_inst.value, 'MOCK?\nMOCK {:e}\n'.format(1)) + assert mock_inst.value == 'MOCK?\nMOCK {:e}\n'.format(1) -@raises(ValueError) def test_unitless_property_units(): - class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK') + with pytest.raises(ValueError): + class UnitlessMock(MockInstrument): + mock_property = unitless_property('MOCK') - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({'MOCK?': '1'}) - mock_inst.mock_property = 1 * pq.volt + mock_inst.mock_property = 1 * pq.volt def test_unitless_property_format_code(): @@ -48,17 +48,17 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock() mock_inst.mock_property = 1 - eq_(mock_inst.value, 'MOCK {:f}\n'.format(1)) + assert mock_inst.value == 'MOCK {:f}\n'.format(1) -@raises(AttributeError) def test_unitless_property_writeonly_reading_fails(): - class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', writeonly=True) + with pytest.raises(AttributeError): + class UnitlessMock(MockInstrument): + mock_property = unitless_property('MOCK', writeonly=True) - mock_inst = UnitlessMock() + mock_inst = UnitlessMock() - _ = mock_inst.mock_property + _ = mock_inst.mock_property def test_unitless_property_writeonly_writing_passes(): @@ -68,17 +68,17 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock() mock_inst.mock_property = 1 - eq_(mock_inst.value, 'MOCK {:e}\n'.format(1)) + assert mock_inst.value == 'MOCK {:e}\n'.format(1) -@raises(AttributeError) def test_unitless_property_readonly_writing_fails(): - class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', readonly=True) + with pytest.raises(AttributeError): + class UnitlessMock(MockInstrument): + mock_property = unitless_property('MOCK', readonly=True) - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({'MOCK?': '1'}) - mock_inst.mock_property = 1 + mock_inst.mock_property = 1 def test_unitless_property_readonly_reading_passes(): @@ -87,7 +87,7 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock({'MOCK?': '1'}) - eq_(mock_inst.mock_property, 1) + assert mock_inst.mock_property == 1 def test_unitless_property_set_cmd(): @@ -96,7 +96,7 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock({'MOCK?': '1'}) - eq_(mock_inst.mock_property, 1) + assert mock_inst.mock_property == 1 mock_inst.mock_property = 1 - eq_(mock_inst.value, 'MOCK?\nFOOBAR {:e}\n'.format(1)) + assert mock_inst.value == 'MOCK?\nFOOBAR {:e}\n'.format(1) diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index cdbe9b499..d3d968ef3 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises +import pytest import quantities as pq import instruments as ik @@ -56,22 +56,22 @@ def test_cc1_window(): cc.window = 7 -@raises(ValueError) def test_cc1_window_error(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":WIND 10" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.window = 10 + with pytest.raises(ValueError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":WIND 10" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.window = 10 def test_cc1_delay(): @@ -95,40 +95,40 @@ def test_cc1_delay(): cc.delay = 2 -@raises(ValueError) def test_cc1_delay_error1(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":DELA -1" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.delay = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":DELA -1" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.delay = -1 -@raises(ValueError) def test_cc1_delay_error2(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":DELA 1" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.delay = 1 + with pytest.raises(ValueError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":DELA 1" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.delay = 1 def test_cc1_dwell_old_firmware(): @@ -172,22 +172,22 @@ def test_cc1_dwell_new_firmware(): cc.dwell_time = 2 -@raises(ValueError) def test_cc1_dwell_time_error(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":DWEL -1" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.dwell_time = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":DWEL -1" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.dwell_time = -1 def test_cc1_firmware(): @@ -303,22 +303,22 @@ def test_cc1_gate_old_firmware(): cc.gate = False -@raises(TypeError) def test_cc1_gate_error(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":GATE blo" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.gate = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":GATE blo" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.gate = "blo" def test_cc1_subtract_new_firmware(): @@ -345,23 +345,23 @@ def test_cc1_subtract_new_firmware(): cc.subtract = False -@raises(TypeError) def test_cc1_subtract_error(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":SUBT blo" - - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.subtract = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":SUBT blo" + + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.subtract = "blo" def test_cc1_trigger_mode(): @@ -410,21 +410,21 @@ def test_cc1_trigger_mode_old_firmware(): cc.trigger_mode = cc.TriggerMode.start_stop -@raises(ValueError) def test_cc1_trigger_mode_error(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" - ) as cc: - cc.trigger_mode = "blo" + with pytest.raises(ValueError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" + ) as cc: + cc.trigger_mode = "blo" def test_cc1_clear(): @@ -473,37 +473,37 @@ def test_acknowledge(): cc.clear_counts() -@raises(NotImplementedError) def test_acknowledge_notimplementederror(): - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2.001" - - ], - sep="\n" - ) as cc: - cc.acknowledge = True + with pytest.raises(NotImplementedError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?" + ], + [ + "Unknown Command", + "Firmware v2.001" + + ], + sep="\n" + ) as cc: + cc.acknowledge = True -@raises(NotImplementedError) def test_acknowledge_not_implemented_error(): # pylint: disable=protected-access - with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2.001" - - ], - sep="\n" - ) as cc: - cc.acknowledge = True + with pytest.raises(NotImplementedError): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?" + ], + [ + "Unknown Command", + "Firmware v2.001" + + ], + sep="\n" + ) as cc: + cc.acknowledge = True diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index eff278cf6..e6cfef952 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises +import pytest import quantities as pq import instruments as ik @@ -201,12 +201,12 @@ def test_mc1_move(): mc.move(0) -@raises(ValueError) def test_mc1_move_value_error(): - with expected_protocol( - ik.qubitekk.MC1, - [":MOVE -1000"], - [""], - sep="\r" - ) as mc: - mc.move(-1000) + with pytest.raises(ValueError): + with expected_protocol( + ik.qubitekk.MC1, + [":MOVE -1000"], + [""], + sep="\r" + ) as mc: + mc.move(-1000) diff --git a/instruments/tests/test_split_str.py b/instruments/tests/test_split_str.py index 3154e2620..cbb5c4102 100644 --- a/instruments/tests/test_split_str.py +++ b/instruments/tests/test_split_str.py @@ -10,7 +10,7 @@ import quantities as pq -from nose.tools import raises, eq_ +import pytest from instruments.util_fns import ( split_unit_str @@ -27,8 +27,8 @@ def test_split_unit_str_magnitude_and_units(): This checks that "[val] [units]" works where val is a non-scientific number """ mag, units = split_unit_str("42 foobars") - eq_(mag, 42) - eq_(units, "foobars") + assert mag == 42 + assert units == "foobars" def test_split_unit_str_magnitude_and_default_units(): @@ -40,8 +40,8 @@ def test_split_unit_str_magnitude_and_default_units(): default_units as the units. """ mag, units = split_unit_str("42", default_units="foobars") - eq_(mag, 42) - eq_(units, "foobars") + assert mag == 42 + assert units == "foobars" def test_split_unit_str_ignore_default_units(): @@ -53,8 +53,8 @@ def test_split_unit_str_ignore_default_units(): are ignored. """ mag, units = split_unit_str("42 snafus", default_units="foobars") - eq_(mag, 42) - eq_(units, "snafus") + assert mag == 42 + assert units == "snafus" def test_split_unit_str_lookups(): @@ -70,8 +70,8 @@ def test_split_unit_str_lookups(): "SNA": "snafus" } mag, units = split_unit_str("42 FOO", lookup=unit_dict.__getitem__) - eq_(mag, 42) - eq_(units, "foobars") + assert mag == 42 + assert units == "foobars" def test_split_unit_str_scientific_notation(): @@ -84,46 +84,46 @@ def test_split_unit_str_scientific_notation(): """ # No signs, no units mag, units = split_unit_str("123E1") - eq_(mag, 1230) - eq_(units, pq.dimensionless) + assert mag == 1230 + assert units == pq.dimensionless # Negative exponential, no units mag, units = split_unit_str("123E-1") - eq_(mag, 12.3) - eq_(units, pq.dimensionless) + assert mag == 12.3 + assert units == pq.dimensionless # Negative magnitude, no units mag, units = split_unit_str("-123E1") - eq_(mag, -1230) - eq_(units, pq.dimensionless) + assert mag == -1230 + assert units == pq.dimensionless # No signs, with units mag, units = split_unit_str("123E1 foobars") - eq_(mag, 1230) - eq_(units, "foobars") + assert mag == 1230 + assert units == "foobars" # Signs everywhere, with units mag, units = split_unit_str("-123E-1 foobars") - eq_(mag, -12.3) - eq_(units, "foobars") + assert mag == -12.3 + assert units == "foobars" # Lower case e mag, units = split_unit_str("123e1") - eq_(mag, 1230) - eq_(units, pq.dimensionless) + assert mag == 1230 + assert units == pq.dimensionless -@raises(ValueError) def test_split_unit_str_empty_string(): """ split_unit_str: Given an empty string, I expect the function to raise a ValueError. """ - _ = split_unit_str("") + with pytest.raises(ValueError): + _ = split_unit_str("") -@raises(ValueError) def test_split_unit_str_only_exponential(): """ split_unit_str: Given a string with only an exponential, I expect the function to raise a ValueError. """ - _ = split_unit_str("E3") + with pytest.raises(ValueError): + _ = split_unit_str("E3") def test_split_unit_str_magnitude_with_decimal(): @@ -133,18 +133,18 @@ def test_split_unit_str_magnitude_with_decimal(): """ # Decimal and units mag, units = split_unit_str("123.4 foobars") - eq_(mag, 123.4) - eq_(units, "foobars") + assert mag == 123.4 + assert units == "foobars" # Decimal, units, and exponential mag, units = split_unit_str("123.4E1 foobars") - eq_(mag, 1234) - eq_(units, "foobars") + assert mag == 1234 + assert units == "foobars" -@raises(ValueError) def test_split_unit_str_only_units(): """ split_unit_str: Given a bad string containing only units (ie, no numbers), I expect the function to raise a ValueError. """ - _ = split_unit_str("foobars") + with pytest.raises(ValueError): + _ = split_unit_str("foobars") diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index 57a612c08..a339623ec 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -10,7 +10,7 @@ import quantities as pq import numpy as np -from nose.tools import raises +import pytest import instruments as ik from instruments.tests import expected_protocol @@ -128,14 +128,14 @@ def test_sample_rate(): # sends index of VALID_SAMPLE_RATES inst.sample_rate = "trigger" -@raises(ValueError) def test_sample_rate_invalid(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.sample_rate = "foobar" + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.sample_rate = "foobar" def test_buffer_mode(): @@ -194,16 +194,16 @@ def test_auto_offset(): inst.auto_offset("x") -@raises(ValueError) def test_auto_offset_invalid(): - with expected_protocol( - ik.srs.SRS830, - [ - "AOFF 1", - ], - [] - ) as inst: - inst.auto_offset(inst.Mode.theta) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [ + "AOFF 1", + ], + [] + ) as inst: + inst.auto_offset(inst.Mode.theta) def test_auto_phase(): @@ -270,14 +270,14 @@ def test_take_measurement(): np.testing.assert_array_equal(resp, [[1.234, 5.678], [0.456, 5.321]]) -@raises(ValueError) def test_take_measurement_invalid_num_samples(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - _ = inst.take_measurement(sample_rate=1, num_samples=16384) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + _ = inst.take_measurement(sample_rate=1, num_samples=16384) def test_set_offset_expand(): @@ -302,54 +302,54 @@ def test_set_offset_expand_mode_as_str(): inst.set_offset_expand(mode="x", offset=0, expand=1) -@raises(ValueError) def test_set_offset_expand_invalid_mode(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.theta, offset=0, expand=1) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_offset_expand(mode=inst.Mode.theta, offset=0, expand=1) -@raises(ValueError) def test_set_offset_expand_invalid_offset(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset=106, expand=1) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset=106, expand=1) -@raises(ValueError) def test_set_offset_expand_invalid_expand(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=5) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=5) -@raises(TypeError) def test_set_offset_expand_invalid_type_offset(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset="derp", expand=1) + with pytest.raises(TypeError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset="derp", expand=1) -@raises(TypeError) def test_set_offset_expand_invalid_type_expand(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand="derp") + with pytest.raises(TypeError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand="derp") def test_start_scan(): @@ -404,34 +404,34 @@ def test_data_snap_mode_as_str(): np.testing.assert_array_equal(data, expected) -@raises(ValueError) def test_data_snap_invalid_snap_mode1(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - _ = inst.data_snap(mode1=inst.Mode.xnoise, mode2=inst.Mode.y) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + _ = inst.data_snap(mode1=inst.Mode.xnoise, mode2=inst.Mode.y) -@raises(ValueError) def test_data_snap_invalid_snap_mode2(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.ynoise) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.ynoise) -@raises(ValueError) def test_data_snap_identical_modes(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.x) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.x) def test_read_data_buffer(): @@ -468,14 +468,14 @@ def test_read_data_buffer_mode_as_str(): np.testing.assert_array_equal(data, expected) -@raises(ValueError) def test_read_data_buffer_invalid_mode(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - _ = inst.read_data_buffer(channel=inst.Mode.x) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + _ = inst.read_data_buffer(channel=inst.Mode.x) def test_clear_data_buffer(): @@ -519,43 +519,43 @@ def test_set_channel_display_params_as_str(): ) -@raises(ValueError) def test_set_channel_display_invalid_channel(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_channel_display( - channel=inst.Mode.x, - display=inst.Mode.x, - ratio=inst.Mode.none - ) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_channel_display( + channel=inst.Mode.x, + display=inst.Mode.x, + ratio=inst.Mode.none + ) -@raises(ValueError) def test_set_channel_display_invalid_display(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_channel_display( - channel=inst.Mode.ch1, - display=inst.Mode.y, # y is only valid for ch2, not ch1! - ratio=inst.Mode.none - ) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_channel_display( + channel=inst.Mode.ch1, + display=inst.Mode.y, # y is only valid for ch2, not ch1! + ratio=inst.Mode.none + ) -@raises(ValueError) def test_set_channel_display_invalid_ratio(): - with expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: - inst.set_channel_display( - channel=inst.Mode.ch1, - display=inst.Mode.x, - ratio=inst.Mode.xnoise - ) + with pytest.raises(ValueError): + with expected_protocol( + ik.srs.SRS830, + [], + [] + ) as inst: + inst.set_channel_display( + channel=inst.Mode.ch1, + display=inst.Mode.x, + ratio=inst.Mode.xnoise + ) diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index e7f4d0944..1865b5f51 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises +import pytest import quantities as pq import instruments as ik @@ -53,36 +53,36 @@ def test_lcc25_frequency(): lcc.frequency = 10.0 -@raises(ValueError) def test_lcc25_frequency_lowlimit(): - with expected_protocol( - ik.thorlabs.LCC25, - [ - "freq=0.0" - ], - [ - "freq=0.0", - ">" - ], - sep="\r" - ) as lcc: - lcc.frequency = 0.0 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [ + "freq=0.0" + ], + [ + "freq=0.0", + ">" + ], + sep="\r" + ) as lcc: + lcc.frequency = 0.0 -@raises(ValueError) def test_lcc25_frequency_highlimit(): - with expected_protocol( - ik.thorlabs.LCC25, - [ - "freq=160.0" - ], - [ - "freq=160.0", - ">" - ], - sep="\r" - ) as lcc: - lcc.frequency = 160.0 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [ + "freq=160.0" + ], + [ + "freq=160.0", + ">" + ], + sep="\r" + ) as lcc: + lcc.frequency = 160.0 def test_lcc25_mode(): @@ -104,14 +104,14 @@ def test_lcc25_mode(): lcc.mode = ik.thorlabs.LCC25.Mode.voltage1 -@raises(ValueError) def test_lcc25_mode_invalid(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: - lcc.mode = "blo" + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [] + ) as lcc: + lcc.mode = "blo" def test_lcc25_enable(): @@ -133,14 +133,14 @@ def test_lcc25_enable(): lcc.enable = True -@raises(TypeError) def test_lcc25_enable_invalid_type(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: - lcc.enable = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [] + ) as lcc: + lcc.enable = "blo" def test_lcc25_extern(): @@ -162,14 +162,14 @@ def test_lcc25_extern(): lcc.extern = True -@raises(TypeError) def test_tc200_extern_invalid_type(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as tc: - tc.extern = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [] + ) as tc: + tc.extern = "blo" def test_lcc25_remote(): @@ -191,14 +191,14 @@ def test_lcc25_remote(): lcc.remote = True -@raises(TypeError) def test_tc200_remote_invalid_type(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as tc: - tc.remote = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [] + ) as tc: + tc.remote = "blo" def test_lcc25_voltage1(): @@ -302,20 +302,20 @@ def test_lcc25_dwell(): lcc.dwell = 10 -@raises(ValueError) def test_lcc25_dwell_positive(): - with expected_protocol( - ik.thorlabs.LCC25, - [ - "dwell=-10" - ], - [ - "dwell=-10", - ">" - ], - sep="\r" - ) as lcc: - lcc.dwell = -10 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [ + "dwell=-10" + ], + [ + "dwell=-10", + ">" + ], + sep="\r" + ) as lcc: + lcc.dwell = -10 def test_lcc25_increment(): @@ -337,20 +337,20 @@ def test_lcc25_increment(): lcc.increment = 10.0 -@raises(ValueError) def test_lcc25_increment_positive(): - with expected_protocol( - ik.thorlabs.LCC25, - [ - "increment=-10" - ], - [ - "increment=-10", - ">" - ], - sep="\r" - ) as lcc: - lcc.increment = -10 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [ + "increment=-10" + ], + [ + "increment=-10", + ">" + ], + sep="\r" + ) as lcc: + lcc.increment = -10 def test_lcc25_default(): @@ -401,15 +401,15 @@ def test_lcc25_set_settings(): lcc.set_settings(2) -@raises(ValueError) def test_lcc25_set_settings_invalid(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [], - sep="\r" - ) as lcc: - lcc.set_settings(5) + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [], + sep="\r" + ) as lcc: + lcc.set_settings(5) def test_lcc25_get_settings(): @@ -428,15 +428,15 @@ def test_lcc25_get_settings(): lcc.get_settings(2) -@raises(ValueError) def test_lcc25_get_settings_invalid(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [], - sep="\r" - ) as lcc: - lcc.get_settings(5) + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [], + sep="\r" + ) as lcc: + lcc.get_settings(5) def test_lcc25_test_mode(): @@ -455,21 +455,21 @@ def test_lcc25_test_mode(): lcc.test_mode() -@raises(TypeError) def test_lcc25_remote_invalid_type(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: - lcc.remote = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [] + ) as lcc: + lcc.remote = "blo" -@raises(TypeError) def test_lcc25_extern_invalid_type(): - with expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: - lcc.extern = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.LCC25, + [], + [] + ) as lcc: + lcc.extern = "blo" diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 942c2bbef..4f1c16003 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -from nose.tools import raises +import pytest import quantities as pq import instruments as ik @@ -52,15 +52,15 @@ def test_sc10_enable(): sc.enable = True -@raises(TypeError) def test_sc10_enable_invalid(): - with expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" - ) as sc: - sc.enable = 10 + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.SC10, + [], + [], + sep="\r" + ) as sc: + sc.enable = 10 def test_sc10_repeat(): @@ -82,15 +82,15 @@ def test_sc10_repeat(): sc.repeat = 10 -@raises(ValueError) def test_sc10_repeat_invalid(): - with expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" - ) as sc: - sc.repeat = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.SC10, + [], + [], + sep="\r" + ) as sc: + sc.repeat = -1 def test_sc10_mode(): @@ -112,15 +112,15 @@ def test_sc10_mode(): sc.mode = ik.thorlabs.SC10.Mode.auto -@raises(ValueError) def test_sc10_mode_invalid(): - with expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" - ) as sc: - sc.mode = "blo" + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.SC10, + [], + [], + sep="\r" + ) as sc: + sc.mode = "blo" def test_sc10_trigger(): @@ -218,15 +218,15 @@ def test_sc10_baud_rate(): sc.baud_rate = 115200 -@raises(ValueError) def test_sc10_baud_rate_error(): - with expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" - ) as sc: - sc.baud_rate = 115201 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.SC10, + [], + [], + sep="\r" + ) as sc: + sc.baud_rate = 115201 def test_sc10_closed(): diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index 350d8ed5e..ac3766d97 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from enum import IntEnum -from nose.tools import raises +import pytest import quantities as pq import instruments as ik @@ -71,29 +71,29 @@ def test_tc200_mode_2(): tc.mode = ik.thorlabs.TC200.Mode.normal -@raises(TypeError) def test_tc200_mode_error(): - with expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" - ) as tc: - tc.mode = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [], + sep="\r" + ) as tc: + tc.mode = "blo" -@raises(TypeError) def test_tc200_mode_error2(): - with expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" - ) as tc: - class TestEnum(IntEnum): - blo = 1 - beep = 2 - tc.mode = TestEnum.blo + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [], + sep="\r" + ) as tc: + class TestEnum(IntEnum): + blo = 1 + beep = 2 + tc.mode = TestEnum.blo def test_tc200_enable(): @@ -121,15 +121,15 @@ def test_tc200_enable(): tc.enable = False -@raises(TypeError) def test_tc200_enable_type(): - with expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" - ) as tc: - tc.enable = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [], + sep="\r" + ) as tc: + tc.enable = "blo" def test_tc200_temperature(): @@ -170,21 +170,21 @@ def test_tc200_temperature_set(): tc.temperature_set = 40 * pq.degC -@raises(ValueError) def test_tc200_temperature_range(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "tmax?" - ], - [ - "tmax?", - "40", - "> " - ], - sep="\r" - ) as tc: - tc.temperature_set = 50 * pq.degC + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "tmax?" + ], + [ + "tmax?", + "40", + "> " + ], + sep="\r" + ) as tc: + tc.temperature_set = 50 * pq.degC def test_tc200_pid(): @@ -261,111 +261,111 @@ def test_tc200_pid(): tc.pid = (2, 0, 220) -@raises(TypeError) def test_tc200_pid_invalid_type(): - with expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" - ) as tc: - tc.pid = "foo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [], + sep="\r" + ) as tc: + tc.pid = "foo" -@raises(ValueError) def test_tc200_pmin(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "pgain=-1" - ], - [ - "pgain=-1", - "> " - ], - sep="\r" - ) as tc: - tc.p = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "pgain=-1" + ], + [ + "pgain=-1", + "> " + ], + sep="\r" + ) as tc: + tc.p = -1 -@raises(ValueError) def test_tc200_pmax(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "pgain=260" - ], - [ - "pgain=260", - "> " - ], - sep="\r" - ) as tc: - tc.p = 260 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "pgain=260" + ], + [ + "pgain=260", + "> " + ], + sep="\r" + ) as tc: + tc.p = 260 -@raises(ValueError) def test_tc200_imin(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "igain=-1" - ], - [ - "igain=-1", - "> " - ], - sep="\r" - ) as tc: - tc.i = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "igain=-1" + ], + [ + "igain=-1", + "> " + ], + sep="\r" + ) as tc: + tc.i = -1 -@raises(ValueError) def test_tc200_imax(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "igain=260" - ], - [ - "igain=260", - "> " - ], - sep="\r" - ) as tc: - tc.i = 260 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "igain=260" + ], + [ + "igain=260", + "> " + ], + sep="\r" + ) as tc: + tc.i = 260 -@raises(ValueError) def test_tc200_dmin(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "dgain=-1" - ], - [ - "dgain=-1", - "> " - ], - sep="\r" - ) as tc: - tc.d = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "dgain=-1" + ], + [ + "dgain=-1", + "> " + ], + sep="\r" + ) as tc: + tc.d = -1 -@raises(ValueError) def test_tc200_dmax(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "dgain=260" - ], - [ - "dgain=260", - "> " - ], - sep="\r" - ) as tc: - tc.d = 260 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "dgain=260" + ], + [ + "dgain=260", + "> " + ], + sep="\r" + ) as tc: + tc.d = 260 def test_tc200_degrees(): @@ -399,16 +399,15 @@ def test_tc200_degrees(): tc.degrees = pq.degK -@raises(TypeError) def test_tc200_degrees_invalid(): - - with expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" - ) as tc: - tc.degrees = "blo" + with pytest.raises(TypeError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [], + sep="\r" + ) as tc: + tc.degrees = "blo" def test_tc200_sensor(): @@ -430,27 +429,27 @@ def test_tc200_sensor(): tc.sensor = tc.Sensor.ptc100 -@raises(ValueError) def test_tc200_sensor_error(): - with expected_protocol( - ik.thorlabs.TC200, - [], - [] - ) as tc: - tc.sensor = "blo" + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [] + ) as tc: + tc.sensor = "blo" -@raises(ValueError) def test_tc200_sensor_error2(): - with expected_protocol( - ik.thorlabs.TC200, - [], - [] - ) as tc: - class TestEnum(IntEnum): - blo = 1 - beep = 2 - tc.sensor = TestEnum.blo + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [], + [] + ) as tc: + class TestEnum(IntEnum): + blo = 1 + beep = 2 + tc.sensor = TestEnum.blo def test_tc200_beta(): @@ -472,36 +471,36 @@ def test_tc200_beta(): tc.beta = 2000 -@raises(ValueError) def test_tc200_beta_min(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "beta=200" - ], - [ - "beta=200", - "> " - ], - sep="\r" - ) as tc: - tc.beta = 200 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "beta=200" + ], + [ + "beta=200", + "> " + ], + sep="\r" + ) as tc: + tc.beta = 200 -@raises(ValueError) def test_tc200_beta_max(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "beta=20000" - ], - [ - "beta=20000", - "> " - ], - sep="\r" - ) as tc: - tc.beta = 20000 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "beta=20000" + ], + [ + "beta=20000", + "> " + ], + sep="\r" + ) as tc: + tc.beta = 20000 def test_tc200_max_power(): @@ -523,36 +522,36 @@ def test_tc200_max_power(): tc.max_power = 12 * pq.W -@raises(ValueError) def test_tc200_power_min(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "PMAX=-2" - ], - [ - "PMAX=-2", - "> " - ], - sep="\r" - ) as tc: - tc.max_power = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "PMAX=-2" + ], + [ + "PMAX=-2", + "> " + ], + sep="\r" + ) as tc: + tc.max_power = -1 -@raises(ValueError) def test_tc200_power_max(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "PMAX=20000" - ], - [ - "PMAX=20000", - "> " - ], - sep="\r" - ) as tc: - tc.max_power = 20000 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "PMAX=20000" + ], + [ + "PMAX=20000", + "> " + ], + sep="\r" + ) as tc: + tc.max_power = 20000 def test_tc200_max_temperature(): @@ -574,33 +573,33 @@ def test_tc200_max_temperature(): tc.max_temperature = 180 * pq.degC -@raises(ValueError) def test_tc200_temp_min(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "TMAX=-2" - ], - [ - "TMAX=-2", - ">" - ], - sep="\r" - ) as tc: - tc.max_temperature = -1 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "TMAX=-2" + ], + [ + "TMAX=-2", + ">" + ], + sep="\r" + ) as tc: + tc.max_temperature = -1 -@raises(ValueError) def test_tc200_temp_max(): - with expected_protocol( - ik.thorlabs.TC200, - [ - "TMAX=20000" - ], - [ - "TMAX=20000", - ">" - ], - sep="\r" - ) as tc: - tc.max_temperature = 20000 + with pytest.raises(ValueError): + with expected_protocol( + ik.thorlabs.TC200, + [ + "TMAX=20000" + ], + [ + "TMAX=20000", + ">" + ], + sep="\r" + ) as tc: + tc.max_temperature = 20000 diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index 1833b4a66..2b0ebe639 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -8,7 +8,7 @@ from __future__ import absolute_import from datetime import datetime -from nose.tools import raises +import pytest import quantities as pq @@ -101,44 +101,44 @@ def test_laser_enable(): tm.laser[0].enable = True -@raises(RuntimeError) def test_laser_enable_no_laser(): - with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:serial-number)", - "(param-set! 'laser1:enable-emission #t)" - ], - [ - "(param-ref 'laser1:serial-number)", - "unknown", - "> (param-set! 'laser1:enable-emission #t)", - "0", - "> " - ], - sep="\r\n" - ) as tm: - tm.laser[0].enable = True + with pytest.raises(RuntimeError): + with expected_protocol( + ik.toptica.TopMode, + [ + "(param-ref 'laser1:serial-number)", + "(param-set! 'laser1:enable-emission #t)" + ], + [ + "(param-ref 'laser1:serial-number)", + "unknown", + "> (param-set! 'laser1:enable-emission #t)", + "0", + "> " + ], + sep="\r\n" + ) as tm: + tm.laser[0].enable = True -@raises(TypeError) def test_laser_enable_error(): - with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:serial-number)", - "(param-set! 'laser1:enable-emission #t)" - ], - [ - "(param-ref 'laser1:serial-number)", - "bloop1", - "> (param-set! 'laser1:enable-emission #t)", - "0", - "> " - ], - sep="\n" - ) as tm: - tm.laser[0].enable = 'True' + with pytest.raises(TypeError): + with expected_protocol( + ik.toptica.TopMode, + [ + "(param-ref 'laser1:serial-number)", + "(param-set! 'laser1:enable-emission #t)" + ], + [ + "(param-ref 'laser1:serial-number)", + "bloop1", + "> (param-set! 'laser1:enable-emission #t)", + "0", + "> " + ], + sep="\n" + ) as tm: + tm.laser[0].enable = 'True' def test_laser_tec_status(): @@ -208,45 +208,45 @@ def test_laser_lock_start(): _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].lock_start == _date -@raises(RuntimeError) def test_laser_lock_start_runtime_error(): - with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)", - "(param-ref 'laser1:charm:reg:started)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", - "0", - "> (param-ref 'laser1:charm:reg:started)", - "\"\"", - "> " - ], - sep="\r\n" - ) as tm: - _date = datetime(2012, 12, 1, 1, 2, 1) - assert tm.laser[0].lock_start == _date + with pytest.raises(RuntimeError): + with expected_protocol( + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)", + "(param-ref 'laser1:charm:reg:started)" + ], + [ + "(param-ref 'laser1:charm:correction-status)", + "0", + "> (param-ref 'laser1:charm:reg:started)", + "\"\"", + "> " + ], + sep="\r\n" + ) as tm: + _date = datetime(2012, 12, 1, 1, 2, 1) + assert tm.laser[0].lock_start == _date -@raises(RuntimeError) def test_laser_first_mode_hop_time_runtime_error(): - with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:first-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#f", - "> (param-ref 'laser1:charm:reg:first-mh)", - "\"\"", - "> " - ], - sep="\r\n" - ) as tm: - assert tm.laser[0].first_mode_hop_time is None + with pytest.raises(RuntimeError): + with expected_protocol( + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:first-mh)" + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#f", + "> (param-ref 'laser1:charm:reg:first-mh)", + "\"\"", + "> " + ], + sep="\r\n" + ) as tm: + assert tm.laser[0].first_mode_hop_time is None def test_laser_first_mode_hop_time(): @@ -269,24 +269,24 @@ def test_laser_first_mode_hop_time(): assert tm.laser[0].first_mode_hop_time == _date -@raises(RuntimeError) def test_laser_latest_mode_hop_time_none(): - with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:latest-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#f", - "> (param-ref 'laser1:charm:reg:latest-mh)", - "\"\"", - "> " - ], - sep="\r\n" - ) as tm: - assert tm.laser[0].latest_mode_hop_time is None + with pytest.raises(RuntimeError): + with expected_protocol( + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:latest-mh)" + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#f", + "> (param-ref 'laser1:charm:reg:latest-mh)", + "\"\"", + "> " + ], + sep="\r\n" + ) as tm: + assert tm.laser[0].latest_mode_hop_time is None def test_laser_latest_mode_hop_time(): @@ -559,20 +559,20 @@ def test_serial_number(): assert tm.serial_number == '010101' -@raises(TypeError) def test_enable_error(): - with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'enable-emission #f)" - ], - [ - "(param-set! 'enable-emission #f)", - ">" - ], - sep="\r\n" - ) as tm: - tm.enable = "False" + with pytest.raises(TypeError): + with expected_protocol( + ik.toptica.TopMode, + [ + "(param-set! 'enable-emission #f)" + ], + [ + "(param-set! 'enable-emission #f)", + ">" + ], + sep="\r\n" + ) as tm: + tm.enable = "False" def test_front_key(): diff --git a/instruments/tests/test_toptica/test_toptica_utils.py b/instruments/tests/test_toptica/test_toptica_utils.py index 2e6414531..790197036 100644 --- a/instruments/tests/test_toptica/test_toptica_utils.py +++ b/instruments/tests/test_toptica/test_toptica_utils.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import datetime -from nose.tools import raises +import pytest from instruments.toptica import toptica_utils @@ -22,9 +22,9 @@ def test_convert_boolean(): assert toptica_utils.convert_toptica_boolean("Error: -3") is None -@raises(ValueError) def test_convert_boolean_value(): - toptica_utils.convert_toptica_boolean("blo") + with pytest.raises(ValueError): + toptica_utils.convert_toptica_boolean("blo") def test_convert_toptica_datetime(): diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index ecf8e6ec5..a968c8e1e 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -12,7 +12,7 @@ from enum import Enum import quantities as pq -from nose.tools import raises, eq_ +import pytest from instruments.util_fns import ( ProxyList, @@ -71,7 +71,7 @@ def __init__(self, parent, name): proxy_list = ProxyList(parent, ProxyChild, range(10)) - eq_(len(proxy_list), 10) + assert len(proxy_list) == 10 def test_ProxyList_iterator(): @@ -87,89 +87,89 @@ def __init__(self, parent, name): i = 0 for item in proxy_list: - eq_(item._name, i) + assert item._name == i i = i + 1 -@raises(IndexError) def test_ProxyList_invalid_idx_enum(): - class ProxyChild(object): + with pytest.raises(IndexError): + class ProxyChild(object): - def __init__(self, parent, name): - self._parent = parent - self._name = name + def __init__(self, parent, name): + self._parent = parent + self._name = name - class MockEnum(Enum): - a = "aa" - b = "bb" + class MockEnum(Enum): + a = "aa" + b = "bb" - parent = object() + parent = object() - proxy_list = ProxyList(parent, ProxyChild, MockEnum) + proxy_list = ProxyList(parent, ProxyChild, MockEnum) - _ = proxy_list['c'] # Should raise IndexError + _ = proxy_list['c'] # Should raise IndexError -@raises(IndexError) def test_ProxyList_invalid_idx(): - class ProxyChild(object): + with pytest.raises(IndexError): + class ProxyChild(object): - def __init__(self, parent, name): - self._parent = parent - self._name = name + def __init__(self, parent, name): + self._parent = parent + self._name = name - parent = object() + parent = object() - proxy_list = ProxyList(parent, ProxyChild, range(5)) + proxy_list = ProxyList(parent, ProxyChild, range(5)) - _ = proxy_list[10] # Should raise IndexError + _ = proxy_list[10] # Should raise IndexError def test_assume_units_correct(): m = pq.Quantity(1, 'm') # Check that unitful quantities are kept unitful. - eq_(assume_units(m, 'mm').rescale('mm').magnitude, 1000) + assert assume_units(m, 'mm').rescale('mm').magnitude == 1000 # Check that raw scalars are made unitful. - eq_(assume_units(1, 'm').rescale('mm').magnitude, 1000) + assert assume_units(1, 'm').rescale('mm').magnitude == 1000 def test_temperature_conversion(): blo = 70.0 * pq.degF out = convert_temperature(blo, pq.degC) - eq_(out.magnitude, 21.11111111111111) + assert out.magnitude == 21.11111111111111 out = convert_temperature(blo, pq.degK) - eq_(out.magnitude, 294.2055555555555) + assert out.magnitude == 294.2055555555555 out = convert_temperature(blo, pq.degF) - eq_(out.magnitude, 70.0) + assert out.magnitude == 70.0 blo = 20.0 * pq.degC out = convert_temperature(blo, pq.degF) - eq_(out.magnitude, 68) + assert out.magnitude == 68 out = convert_temperature(blo, pq.degC) - eq_(out.magnitude, 20.0) + assert out.magnitude == 20.0 out = convert_temperature(blo, pq.degK) - eq_(out.magnitude, 293.15) + assert out.magnitude == 293.15 blo = 270 * pq.degK out = convert_temperature(blo, pq.degC) - eq_(out.magnitude, -3.1499999999999773) + assert out.magnitude == -3.1499999999999773 out = convert_temperature(blo, pq.degF) - eq_(out.magnitude, 141.94736842105263) + assert out.magnitude == 141.94736842105263 out = convert_temperature(blo, pq.K) - eq_(out.magnitude, 270) + assert out.magnitude == 270 -@raises(ValueError) def test_temperater_conversion_failure(): - blo = 70.0 * pq.degF - convert_temperature(blo, pq.V) + with pytest.raises(ValueError): + blo = 70.0 * pq.degF + convert_temperature(blo, pq.V) -@raises(ValueError) def test_assume_units_failures(): - assume_units(1, 'm').rescale('s') + with pytest.raises(ValueError): + assume_units(1, 'm').rescale('s') def test_setattr_expression_simple(): class A(object): diff --git a/setup.py b/setup.py index e658c45b8..85af41b41 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ ] INSTALL_REQUIRES = [ "numpy", - "pyserial", + "pyserial>=3.3", "quantities", "enum34", "future", diff --git a/tox.ini b/tox.ini index a6bf9ea2e..6808db30b 100644 --- a/tox.ini +++ b/tox.ini @@ -2,4 +2,4 @@ envlist = py27,py34,py35,py36 [testenv] deps = -rdev-requirements.txt -commands = nosetests +commands = pytest From b5fa44b2375b431ff0ec37646df12169a6d285ee Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 6 Feb 2019 19:00:31 -0500 Subject: [PATCH 015/108] Fix socket.shutdown function call (issue #189) (#191) --- instruments/abstract_instruments/comm/socket_communicator.py | 2 +- instruments/tests/test_comm/test_socket.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index 2d68beab0..aefdd5878 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -91,7 +91,7 @@ def close(self): Shutdown and close the `socket.socket` connection. """ try: - self._conn.shutdown() + self._conn.shutdown(socket.SHUT_RDWR) finally: self._conn.close() diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index 59b943d4a..aee17e5c5 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -88,7 +88,7 @@ def test_socketcomm_close(): comm._conn = mock.MagicMock() comm.close() - comm._conn.shutdown.assert_called_with() + comm._conn.shutdown.assert_called_with(socket.SHUT_RDWR) comm._conn.close.assert_called_with() From 3d9175df412f0323a32a2e0b0b2cfaa481020c61 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 6 Feb 2019 20:17:52 -0500 Subject: [PATCH 016/108] Fix visa readbytes (#192) * Replace deprecated ask method for query in visa session. * Update gitignore and add test requirements * Replace visa read with with read_bytes * Add encode parameter to instrument.read() - Introduce unpacking of IEEE-754/64 double real numbers. * Add newest pyvisa package to requirements.txt - Removed extras_require from setup.py * Update read mock call with utf-8 * Replace exception NotImplemented with NotImplementedError * Change read method signature on pm100usb * Revert change to _extras_require_ * Add pyvisa 1.9.x install requirement * Fix pyvisa requirement * Replace NotImplementedError with ValueError on abstract_comm * Loosen pyvisa versioning requirements * Use codecs module to determine valid builtin encode types --- .gitignore | 5 +++ .../comm/abstract_comm.py | 11 +++++- .../comm/visa_communicator.py | 9 ++--- .../abstract_instruments/instrument.py | 4 +-- instruments/tests/test_base_instrument.py | 4 +-- instruments/thorlabs/pm100usb.py | 2 +- requirements.txt | 1 + setup.py | 36 ++++++++++--------- 8 files changed, 44 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 47f4499c9..c02194b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,10 @@ Thumbs.db ## Build directories ## doc/_build +## Venv +.venv/ + + ## setup.py generated files ## MANIFEST @@ -20,6 +24,7 @@ MANIFEST *.so # Packages +.egg/ *.egg *.egg-info dist diff --git a/instruments/abstract_instruments/comm/abstract_comm.py b/instruments/abstract_instruments/comm/abstract_comm.py index e15547760..b29b5e97d 100644 --- a/instruments/abstract_instruments/comm/abstract_comm.py +++ b/instruments/abstract_instruments/comm/abstract_comm.py @@ -11,7 +11,9 @@ from __future__ import unicode_literals import abc +import codecs import logging +import struct from future.utils import with_metaclass @@ -202,7 +204,14 @@ def read(self, size=-1, encoding="utf-8"): :return: The read string from the connection :rtype: `str` """ - return self.read_raw(size).decode(encoding) + try: + codecs.lookup(encoding) + return self.read_raw(size).decode(encoding) + except LookupError: + if encoding == 'IEEE-754/64': + return struct.unpack('>d', self.read_raw(size))[0] + else: + raise ValueError("Encoding {} is not currently supported.".format(encoding)) def sendcmd(self, msg): """ diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 7f407298c..0b16ef2a5 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -124,14 +124,11 @@ def read_raw(self, size=-1): :rtype: `bytes` """ if size >= 0: - while len(self._buf) < size: - data = self._conn.read() - if data == "": - break - self._buf += data + self._buf += self._conn.read_bytes(size) msg = self._buf[:size] # Remove the front of the buffer. del self._buf[:size] + elif size == -1: # Read the whole contents, appending the buffer we've already read. msg = self._buf + self._conn.read() @@ -193,4 +190,4 @@ def _query(self, msg, size=-1): :rtype: `str` """ msg += self._terminator - return self._conn.ask(msg) + return self._conn.query(msg) diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 7163f40f0..2b294e96c 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -151,7 +151,7 @@ def query(self, cmd, size=-1): ) return value - def read(self, size=-1): + def read(self, size=-1, encoding="utf-8"): """ Read the last line. @@ -161,7 +161,7 @@ def read(self, size=-1): connected instrument. :rtype: `str` """ - return self._file.read(size) + return self._file.read(size, encoding) # PROPERTIES # diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 2c121e1c6..f369f3d95 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -710,13 +710,13 @@ def test_instrument_read(): inst._file.read.return_value = "foobar" assert inst.read() == "foobar" - inst._file.read.assert_called_with(-1) + inst._file.read.assert_called_with(-1, 'utf-8') inst._file = mock.MagicMock() inst._file.read.return_value = "foobar" assert inst.read(6) == "foobar" - inst._file.read.assert_called_with(6) + inst._file.read.assert_called_with(6, 'utf-8') def test_instrument_write(): diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index 8b06dceff..e9ad6e99b 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -222,7 +222,7 @@ def averaging_count(self, newval): }) - def read(self, size=-1): + def read(self, size=-1, encoding='utf-8'): """ Reads a measurement from this instrument, according to its current configuration mode. diff --git a/requirements.txt b/requirements.txt index 1d83117d2..5c1eb06f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ numpy pyserial +pyvisa>=1.9 quantities>=0.12.1 future>=0.15 enum34 diff --git a/setup.py b/setup.py index 85af41b41..4275a38d3 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ INSTALL_REQUIRES = [ "numpy", "pyserial>=3.3", + "pyvisa>=1.9", "quantities", "enum34", "future", @@ -45,9 +46,7 @@ "pyusb", "ruamel.yaml" ] -EXTRAS_REQUIRE = { - 'VISA': ["pyvisa"] -} + # HELPER FUNCTONS ############################################################ @@ -62,6 +61,7 @@ def read(*parts): with codecs.open(os.path.join(HERE, *parts), "rb", "utf-8") as f: return f.read() + META_FILE = read(META_PATH) @@ -77,18 +77,22 @@ def find_meta(meta): return meta_match.group(1) raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta)) + # MAIN ####################################################################### -if __name__ == "__main__": - setup( - name=find_meta("title"), - version=find_meta("version"), - url=find_meta("uri"), - author=find_meta("author"), - author_email=find_meta("email"), - packages=PACKAGES, - install_requires=INSTALL_REQUIRES, - extras_require=EXTRAS_REQUIRE, - description=find_meta("description"), - classifiers=CLASSIFIERS - ) + +setup( + name=find_meta("title"), + version=find_meta("version"), + url=find_meta("uri"), + author=find_meta("author"), + author_email=find_meta("email"), + packages=PACKAGES, + install_requires=INSTALL_REQUIRES, + tests_require=[ + 'pytest >= 2.9.1', + 'hypothesis' + ], + description=find_meta("description"), + classifiers=CLASSIFIERS +) From ea2e8a5bac3d8ed282dfaecd304cf8705e5eb211 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 6 Feb 2019 21:02:32 -0500 Subject: [PATCH 017/108] Doc updates (#193) * Move pyvisa to required in the docs * Update authorship years (-> 2019) --- doc/source/conf.py | 2 +- doc/source/intro.rst | 3 +-- instruments/__init__.py | 2 +- license/AUTHOR.TXT | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 49f94c4b0..7ad9faa69 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,7 +42,7 @@ # General information about the project. project = u'InstrumentKit Library' -copyright = u'2013-2016, Steven Casagrande' +copyright = u'2013-2019, Steven Casagrande' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 733a33c44..87d723ae8 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -36,12 +36,11 @@ $ pip install -r requirements.txt - `PyUSB`_ (version 1.0a or higher, required for raw USB support) - `python-usbtmc`_ - `ruamel.yaml`_ (required for configuration file support) +- `PyVISA`_ (required for accessing instruments via VISA library) Optional Dependencies ~~~~~~~~~~~~~~~~~~~~~ -- `PyVISA`_ (required for accessing instruments via VISA library) - .. _PySerial: http://pyserial.sourceforge.net/ .. _quantities: http://pythonhosted.org/quantities/ .. _enum34: https://pypi.python.org/pypi/enum34 diff --git a/instruments/__init__.py b/instruments/__init__.py index 20aeb3844..586b06c67 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -46,4 +46,4 @@ __email__ = "scasagrande@galvant.ca" __license__ = "AGPLv3" -__copyright__ = "Copyright (c) 2012-2016 Steven Casagrande" +__copyright__ = "Copyright (c) 2012-2019 Steven Casagrande" diff --git a/license/AUTHOR.TXT b/license/AUTHOR.TXT index aa0485eef..1c4b0f138 100644 --- a/license/AUTHOR.TXT +++ b/license/AUTHOR.TXT @@ -7,4 +7,4 @@ Steven Casagrande scasagrande@galvant.ca twitter.com/stevecasagrande -2012-2016 +2012-2019 From 7195a67721fb2225c838c9ae859808b7192ae6f2 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 6 Feb 2019 21:13:44 -0500 Subject: [PATCH 018/108] Bump version v0.4.1 -> v0.4.2 (#194) --- instruments/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index 586b06c67..ca26f525b 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -36,7 +36,7 @@ # In keeping with PEP-396, we define a version number of the form # {major}.{minor}[.{postrelease}]{prerelease-tag} -__version__ = "0.4.1" +__version__ = "0.4.2" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" From cd571bc8303fbf2872f4e3f1d92407ccd5ee8a64 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 7 Feb 2019 11:48:04 -0500 Subject: [PATCH 019/108] Python 3.7 support (#195) * Add support for Python 3.7 * Fix PyPI description rendering * Upgrade travis environment to Ubuntu 16.04 Xenial * Update ruamel.yaml to 0.15.x * Match setup.py version reqs to requirements.txt * Disable Pylint on Py37 Pylint 2.0+ is needed for Py37, which in turn doesn't support Py27. * Fix bash IF statement * More pylint travis modifications --- .travis.yml | 7 +++++-- README.rst | 2 +- requirements.txt | 2 +- setup.py | 13 +++++++++---- tox.ini | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 033893a0e..b46392035 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: xenial sudo: false language: python python: @@ -5,6 +6,7 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" install: - "pip install -r requirements.txt" - "pip install -r dev-requirements.txt" @@ -24,8 +26,8 @@ before_script: - pylint --version script: - pytest --cov=instruments - - pylint --py3k instruments - - pylint --disable=I instruments + - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --py3k instruments; fi + - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --disable=I instruments; fi after_success: - coveralls deploy: @@ -35,3 +37,4 @@ deploy: distributions: "sdist bdist_wheel" on: tags: true + condition: "$TRAVIS_PYTHON_VERSION == 3.7" diff --git a/README.rst b/README.rst index 9545f52bc..bd4e5d44c 100644 --- a/README.rst +++ b/README.rst @@ -111,7 +111,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 2.7, 3.4, 3.5, and 3.6 are supported. Should you encounter +At this time, Python 2.7, 3.4, 3.5, 3.6, and 3.7 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. diff --git a/requirements.txt b/requirements.txt index 5c1eb06f8..c6153e9af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,4 @@ enum34 python-vxi11>=0.8 pyusb python-usbtmc -ruamel.yaml~=0.14.12 +ruamel.yaml~=0.15.37 diff --git a/setup.py b/setup.py index 4275a38d3..7e5e6b1c4 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Operating System :: OS Independent", "License :: OSI Approved :: GNU Affero General Public License v3", "Intended Audience :: Science/Research", @@ -38,13 +39,13 @@ "numpy", "pyserial>=3.3", "pyvisa>=1.9", - "quantities", + "quantities>=0.12.1", "enum34", - "future", - "python-vxi11", + "future>=0.15", + "python-vxi11>=0.8", "python-usbtmc", "pyusb", - "ruamel.yaml" + "ruamel.yaml~=0.15.37" ] @@ -80,6 +81,8 @@ def find_meta(meta): # MAIN ####################################################################### +with open("README.rst", "r") as fh: + long_description = fh.read() setup( name=find_meta("title"), @@ -94,5 +97,7 @@ def find_meta(meta): 'hypothesis' ], description=find_meta("description"), + long_description=long_description, + long_description_content_type="text/restructedtext", classifiers=CLASSIFIERS ) diff --git a/tox.ini b/tox.ini index 6808db30b..508210fb1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py34,py35,py36 +envlist = py27,py34,py35,py36,py37 [testenv] deps = -rdev-requirements.txt commands = pytest From 73d921f52d29a49643bd60b54085bbcf19ba5bc6 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 12 Feb 2019 19:05:26 -0500 Subject: [PATCH 020/108] Bump version v0.4.2 -> v0.4.3 (#204) --- instruments/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index ca26f525b..d3220a24a 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -36,7 +36,7 @@ # In keeping with PEP-396, we define a version number of the form # {major}.{minor}[.{postrelease}]{prerelease-tag} -__version__ = "0.4.2" +__version__ = "0.4.3" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" From 497cc7cedfbc21ca15896c408819b3048a0105a1 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 13 Feb 2019 09:40:29 -0500 Subject: [PATCH 021/108] Fix setup.py for publishing (#205) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7e5e6b1c4..eec71c990 100644 --- a/setup.py +++ b/setup.py @@ -98,6 +98,6 @@ def find_meta(meta): ], description=find_meta("description"), long_description=long_description, - long_description_content_type="text/restructedtext", + long_description_content_type="text/x-rst", classifiers=CLASSIFIERS ) From 016c957d9dc958941e87c76f43c4f4050c5312d4 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 19 Feb 2019 13:50:11 -0500 Subject: [PATCH 022/108] Develop -> master for v0.5.0 (#209) * Drop support for Python 3.4 (#200) * Fix issue #197 (#201) Where checking if value is in enum will raise value error in Py38 * Improvements to APT (#167) * Added driver/motor tables for T/KDC001 APT devs. * Moved TODO comment to avoid pylint error. * Misc Py3k changes for ThorLabs APT * motion_timeout for APT motor cmds, fix scale factor * ThorLabsAPT: Example of new config support. * More pylint fixes * Fix for line continuation convention. * Rearranged imports into standard order. * Added an APT test. Not working yet. * Fix linting issues * New handling in loopback for empty terminator. * struct.Struct for contents of hw_info packets * Support for specifying expected apt pkt sizes * Fixes to APT and APT tests * Missed a conflict marker. * Fixed bug due to `if size` falling through on size == 0. * Removed trailing whitespace. * Locked requirements.txt; see #174. * Remove numpy version pinning in requirements.txt * Add tests to cover additional loopback comm behaviour * Make pylint happy * Revert changes to size=0 behaviour in loopback comm * Fix bug with SCPIFunctionGenerator.function, add tests (#202) * Fix bug with SCPIFunctionGenerator.function, add tests * Fix linting * Fix Agilent 33220a, add tests (#203) * Function Generator single/multi-channel consistency (#206) * Function Generator single/multi-channel consistency * Fix Py27 linting issue * Fix linting issue * Add tests for FunctionGenerator abstract instrument * Adding support for the minghe mhs5200 (#150) * added mhs5200 * added minghe function generator * added absolute_import * fixed scaling on frequency * switched to abstract instrument class * fixed a few docstrings * after testing with device * Minghe MHS5200 - Add instrument to docs * isolating changes from cc1 test station: * Revert "isolating changes from cc1 test station:" This reverts commit 87b8dec40d927460bb9cb0edf6d671c3535f27c2. * reverting changes and fixing duty cycle * Update for new FunctionGenerator multichannel consistency update * Docstring modifications (#207) --- .travis.yml | 1 - README.rst | 4 +- dev-requirements.txt | 1 + doc/examples/minghe/ex_minghe_mhs5200.py | 28 ++ doc/source/apiref/minghe.rst | 15 + instruments/__init__.py | 1 + .../comm/loopback_communicator.py | 22 +- .../comm/serial_communicator.py | 16 +- .../function_generator.py | 291 +++++++++++++----- instruments/agilent/agilent33220a.py | 14 +- .../generic_scpi/scpi_function_generator.py | 4 +- instruments/hp/hp6632b.py | 5 +- instruments/minghe/__init__.py | 7 + instruments/minghe/mhs5200a.py | 235 ++++++++++++++ instruments/tests/__init__.py | 17 +- .../test_function_generator.py | 171 ++++++++++ .../tests/test_agilent/test_agilent_33220a.py | 169 ++++++++++ instruments/tests/test_comm/test_loopback.py | 19 ++ .../test_scpi_function_generator.py | 97 ++++++ .../tests/test_minghe/test_minghe_mhs5200a.py | 210 +++++++++++++ .../tests/test_thorlabs/test_thorlabs_apt.py | 82 +++++ instruments/thorlabs/_abstract.py | 43 ++- instruments/thorlabs/_packets.py | 14 +- instruments/thorlabs/thorlabsapt.py | 92 +++++- setup.py | 1 - tox.ini | 2 +- 26 files changed, 1433 insertions(+), 128 deletions(-) create mode 100644 doc/examples/minghe/ex_minghe_mhs5200.py create mode 100644 doc/source/apiref/minghe.rst create mode 100644 instruments/minghe/__init__.py create mode 100644 instruments/minghe/mhs5200a.py create mode 100644 instruments/tests/test_abstract_inst/test_function_generator.py create mode 100644 instruments/tests/test_agilent/test_agilent_33220a.py create mode 100644 instruments/tests/test_generic_scpi/test_scpi_function_generator.py create mode 100644 instruments/tests/test_minghe/test_minghe_mhs5200a.py create mode 100644 instruments/tests/test_thorlabs/test_thorlabs_apt.py diff --git a/.travis.yml b/.travis.yml index b46392035..1d34c5b7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false language: python python: - "2.7" - - "3.4" - "3.5" - "3.6" - "3.7" diff --git a/README.rst b/README.rst index bd4e5d44c..d37d1674a 100644 --- a/README.rst +++ b/README.rst @@ -111,7 +111,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 2.7, 3.4, 3.5, 3.6, and 3.7 are supported. Should you encounter +At this time, Python 2.7, 3.5, 3.6, and 3.7 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. @@ -134,7 +134,7 @@ To run the tests against all supported version of Python, you will need to have the binary for each installed, as well as any requirements needed to install ``numpy`` under each Python version. On Debian/Ubuntu systems this means you will need to install the ``python-dev`` package for each version of Python -supported (``python2.7-dev``, ``python3.4-dev``, etc). +supported (``python2.7-dev``, ``python3.7-dev``, etc). With the required system packages installed, all tests can be run with ``tox``: diff --git a/dev-requirements.txt b/dev-requirements.txt index 4fe922cad..b4082436b 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,6 @@ mock pytest +pytest-mock hypothesis pylint==1.7.1 astroid==1.5.3 diff --git a/doc/examples/minghe/ex_minghe_mhs5200.py b/doc/examples/minghe/ex_minghe_mhs5200.py new file mode 100644 index 000000000..60b69bedb --- /dev/null +++ b/doc/examples/minghe/ex_minghe_mhs5200.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +from instruments.minghe import MHS5200 +import quantities as pq + +mhs = MHS5200.open_serial(vid=6790, pid=29987, baud=57600) +print(mhs.serial_number) +mhs.channel[0].frequency = 3000000*pq.Hz +print(mhs.channel[0].frequency) +mhs.channel[0].function = MHS5200.Function.sawtooth_down +print(mhs.channel[0].function) +mhs.channel[0].amplitude = 9.0*pq.V +print(mhs.channel[0].amplitude) +mhs.channel[0].offset = -0.5 +print(mhs.channel[0].offset) +mhs.channel[0].phase = 90 +print(mhs.channel[0].phase) + +mhs.channel[1].frequency = 2000000*pq.Hz +print(mhs.channel[1].frequency) +mhs.channel[1].function = MHS5200.Function.square +print(mhs.channel[1].function) +mhs.channel[1].amplitude = 2.0*pq.V +print(mhs.channel[1].amplitude) +mhs.channel[1].offset = 0.0 +print(mhs.channel[1].offset) +mhs.channel[1].phase = 15 +print(mhs.channel[1].phase) + diff --git a/doc/source/apiref/minghe.rst b/doc/source/apiref/minghe.rst new file mode 100644 index 000000000..6a1763039 --- /dev/null +++ b/doc/source/apiref/minghe.rst @@ -0,0 +1,15 @@ +.. + TODO: put documentation license header here. + +.. currentmodule:: instruments.minghe + +====== +Minghe +====== + +:class:`MHS5200` Function Generator +=================================== + +.. autoclass:: MHS5200 + :members: + :undoc-members: diff --git a/instruments/__init__.py b/instruments/__init__.py index d3220a24a..3e096bee5 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -17,6 +17,7 @@ from . import hp from . import keithley from . import lakeshore +from . import minghe from . import newport from . import oxford from . import phasematrix diff --git a/instruments/abstract_instruments/comm/loopback_communicator.py b/instruments/abstract_instruments/comm/loopback_communicator.py index 85020ec3c..029c9d34f 100644 --- a/instruments/abstract_instruments/comm/loopback_communicator.py +++ b/instruments/abstract_instruments/comm/loopback_communicator.py @@ -108,17 +108,21 @@ def read_raw(self, size=-1): :rtype: `bytes` """ if self._stdin is not None: - if size >= 0: + if size == -1 or size is None: + result = bytes() + if self._terminator: + while result.endswith(self._terminator.encode("utf-8")) is False: + c = self._stdin.read(1) + if c == b'': + break + result += c + return result[:-len(self._terminator)] + return self._stdin.read(-1) + + elif size >= 0: input_var = self._stdin.read(size) return bytes(input_var) - elif size == -1: - result = bytes() - while result.endswith(self._terminator.encode("utf-8")) is False: - c = self._stdin.read(1) - if c == b'': - break - result += c - return result[:-len(self._terminator)] + else: raise ValueError("Must read a positive value of characters.") else: diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index 02b88f23c..40bc5b30d 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -118,13 +118,23 @@ def read_raw(self, size=-1): return resp elif size == -1: result = bytes() - while result.endswith(self._terminator.encode("utf-8")) is False: + # If the terminator is empty, we can't use endswith, but must + # read as many bytes as are available. + # On the other hand, if terminator is nonempty, we can check + # that the tail end of the buffer matches it. + c = None + term = self._terminator.encode('utf-8') if self._terminator else None + while not ( + result.endswith(term) + if term is not None else + c == b'' + ): c = self._conn.read(1) - if c == b'': + if c == b'' and term is not None: raise IOError("Serial connection timed out before reading " "a termination character.") result += c - return result[:-len(self._terminator)] + return result[:-len(term)] if term is not None else result else: raise ValueError("Must read a positive value of characters.") diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index e9baa64be..107d6591f 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -12,13 +12,13 @@ import abc from enum import Enum +from builtins import range from future.utils import with_metaclass import quantities as pq - from instruments.abstract_instruments import Instrument import instruments.units as u -from instruments.util_fns import assume_units +from instruments.util_fns import assume_units, ProxyList # CLASSES ##################################################################### @@ -32,6 +32,175 @@ class FunctionGenerator(with_metaclass(abc.ABCMeta, Instrument)): provide a consistent interface to the user. """ + def __init__(self, filelike): + super(FunctionGenerator, self).__init__(filelike) + self._channel_count = 1 + + # pylint:disable=protected-access + class Channel(with_metaclass(abc.ABCMeta, object)): + """ + Abstract base class for physical channels on a function generator. + + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. + + Function generators that only have a single channel do not need to + define their own concrete implementation of this class. Ones with + multiple channels need their own definition of this class, where + this class contains the concrete implementations of the below + abstract methods. Instruments with 1 channel have their concrete + implementations at the parent instrument level. + """ + def __init__(self, parent, name): + self._parent = parent + self._name = name + + # ABSTRACT PROPERTIES # + + @property + def frequency(self): + """ + Gets/sets the the output frequency of the function generator. This is + an abstract property. + + :type: `~quantities.Quantity` + """ + if self._parent._channel_count == 1: + return self._parent.frequency + else: + raise NotImplementedError() + + @frequency.setter + def frequency(self, newval): + if self._parent._channel_count == 1: + self._parent.frequency = newval + else: + raise NotImplementedError() + + @property + def function(self): + """ + Gets/sets the output function mode of the function generator. This is + an abstract property. + + :type: `~enum.Enum` + """ + if self._parent._channel_count == 1: + return self._parent.function + else: + raise NotImplementedError() + + @function.setter + def function(self, newval): + if self._parent._channel_count == 1: + self._parent.function = newval + else: + raise NotImplementedError() + + @property + def offset(self): + """ + Gets/sets the output offset voltage of the function generator. This is + an abstract property. + + :type: `~quantities.Quantity` + """ + if self._parent._channel_count == 1: + return self._parent.offset + else: + raise NotImplementedError() + + @offset.setter + def offset(self, newval): + if self._parent._channel_count == 1: + self._parent.offset = newval + else: + raise NotImplementedError() + + @property + def phase(self): + """ + Gets/sets the output phase of the function generator. This is an + abstract property. + + :type: `~quantities.Quantity` + """ + if self._parent._channel_count == 1: + return self._parent.phase + else: + raise NotImplementedError() + + @phase.setter + def phase(self, newval): + if self._parent._channel_count == 1: + self._parent.phase = newval + else: + raise NotImplementedError() + + def _get_amplitude_(self): + if self._parent._channel_count == 1: + return self._parent._get_amplitude_() + else: + raise NotImplementedError() + + def _set_amplitude_(self, magnitude, units): + if self._parent._channel_count == 1: + self._parent._set_amplitude_(magnitude=magnitude, units=units) + else: + raise NotImplementedError() + + @property + def amplitude(self): + """ + Gets/sets the output amplitude of the function generator. + + If set with units of :math:`\\text{dBm}`, then no voltage mode can + be passed. + + If set with units of :math:`\\text{V}` as a `~quantities.Quantity` or a + `float` without a voltage mode, then the voltage mode is assumed to be + peak-to-peak. + + :units: As specified, or assumed to be :math:`\\text{V}` if not + specified. + :type: Either a `tuple` of a `~quantities.Quantity` and a + `FunctionGenerator.VoltageMode`, or a `~quantities.Quantity` + if no voltage mode applies. + """ + mag, units = self._get_amplitude_() + + if units == self._parent.VoltageMode.dBm: + return pq.Quantity(mag, u.dBm) + + return pq.Quantity(mag, pq.V), units + + @amplitude.setter + def amplitude(self, newval): + # Try and rescale to dBm... if it succeeds, set the magnitude + # and units accordingly, otherwise handle as a voltage. + try: + newval_dbm = newval.rescale(u.dBm) + mag = float(newval_dbm.magnitude) + units = self._parent.VoltageMode.dBm + except (AttributeError, ValueError): + # OK, we have volts. Now, do we have a tuple? If not, assume Vpp. + if not isinstance(newval, tuple): + mag = newval + units = self._parent.VoltageMode.peak_to_peak + else: + mag, units = newval + + # Finally, convert the magnitude out to a float. + mag = float(assume_units(mag, pq.V).rescale(pq.V).magnitude) + + self._set_amplitude_(mag, units) + + def sendcmd(self, cmd): + self._parent.sendcmd(cmd) + + def query(self, cmd, size=-1): + return self._parent.query(cmd, size) + # ENUMS # class VoltageMode(Enum): @@ -53,20 +222,27 @@ class Function(Enum): noise = 'NOIS' arbitrary = 'ARB' - # ABSTRACT METHODS # + @property + def channel(self): + return ProxyList(self, self.Channel, range(self._channel_count)) + + # PASSTHROUGH PROPERTIES # + + @property + def amplitude(self): + return self.channel[0].amplitude + + @amplitude.setter + def amplitude(self, newval): + self.channel[0].amplitude = newval - @abc.abstractmethod def _get_amplitude_(self): - pass + raise NotImplementedError() - @abc.abstractmethod def _set_amplitude_(self, magnitude, units): - pass - - # ABSTRACT PROPERTIES # + raise NotImplementedError() @property - @abc.abstractmethod def frequency(self): """ Gets/sets the the output frequency of the function generator. This is @@ -74,15 +250,19 @@ def frequency(self): :type: `~quantities.Quantity` """ - pass + if self._channel_count > 1: + return self.channel[0].frequency + else: + raise NotImplementedError() @frequency.setter - @abc.abstractmethod def frequency(self, newval): - pass + if self._channel_count > 1: + self.channel[0].frequency = newval + else: + raise NotImplementedError() @property - @abc.abstractmethod def function(self): """ Gets/sets the output function mode of the function generator. This is @@ -90,15 +270,19 @@ def function(self): :type: `~enum.Enum` """ - pass + if self._channel_count > 1: + return self.channel[0].function + else: + raise NotImplementedError() @function.setter - @abc.abstractmethod def function(self, newval): - pass + if self._channel_count > 1: + self.channel[0].function = newval + else: + raise NotImplementedError() @property - @abc.abstractmethod def offset(self): """ Gets/sets the output offset voltage of the function generator. This is @@ -106,15 +290,19 @@ def offset(self): :type: `~quantities.Quantity` """ - pass + if self._channel_count > 1: + return self.channel[0].offset + else: + raise NotImplementedError() @offset.setter - @abc.abstractmethod def offset(self, newval): - pass + if self._channel_count > 1: + self.channel[0].offset = newval + else: + raise NotImplementedError() @property - @abc.abstractmethod def phase(self): """ Gets/sets the output phase of the function generator. This is an @@ -122,57 +310,14 @@ def phase(self): :type: `~quantities.Quantity` """ - pass + if self._channel_count > 1: + return self.channel[0].phase + else: + raise NotImplementedError() @phase.setter - @abc.abstractmethod def phase(self, newval): - pass - - # CONCRETE PROPERTIES # - - @property - def amplitude(self): - """ - Gets/sets the output amplitude of the function generator. - - If set with units of :math:`\\text{dBm}`, then no voltage mode can - be passed. - - If set with units of :math:`\\text{V}` as a `~quantities.Quantity` or a - `float` without a voltage mode, then the voltage mode is assumed to be - peak-to-peak. - - :units: As specified, or assumed to be :math:`\\text{V}` if not - specified. - :type: Either a `tuple` of a `~quantities.Quantity` and a - `FunctionGenerator.VoltageMode`, or a `~quantities.Quantity` - if no voltage mode applies. - """ - mag, units = self._get_amplitude_() - - if units == self.VoltageMode.dBm: - return pq.Quantity(mag, u.dBm) - - return pq.Quantity(mag, pq.V), units - - @amplitude.setter - def amplitude(self, newval): - # Try and rescale to dBm... if it succeeds, set the magnitude - # and units accordingly, otherwise handle as a voltage. - try: - newval_dbm = newval.rescale(u.dBm) - mag = float(newval_dbm.magnitude) - units = self.VoltageMode.dBm - except (AttributeError, ValueError): - # OK, we have volts. Now, do we have a tuple? If not, assume Vpp. - if not isinstance(newval, tuple): - mag = newval - units = self.VoltageMode.peak_to_peak - else: - mag, units = newval - - # Finally, convert the magnitude out to a float. - mag = float(assume_units(mag, pq.V).rescale(pq.V).magnitude) - - self._set_amplitude_(mag, units) + if self._channel_count > 1: + self.channel[0].phase = newval + else: + raise NotImplementedError() diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 8afb868cf..b5282414d 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -78,14 +78,6 @@ class OutputPolarity(Enum): # PROPERTIES # - @property - def frequency(self): - return super(Agilent33220a, self).frequency - - @frequency.setter - def frequency(self, newval): - super(Agilent33220a, self).frequency = newval - function = enum_property( command="FUNC", enum=Function, @@ -182,13 +174,11 @@ def load_resistance(self): def load_resistance(self, newval): if isinstance(newval, self.LoadResistance): newval = newval.value - elif isinstance(newval, int): + else: + newval = assume_units(newval, pq.ohm).rescale(pq.ohm).magnitude if (newval < 0) or (newval > 10000): raise ValueError( "Load resistance must be between 0 and 10,000") - newval = assume_units(newval, pq.ohm).rescale(pq.ohm).magnitude - else: - raise TypeError("Not a valid load resistance type.") self.sendcmd("OUTP:LOAD {}".format(newval)) @property diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 4877168ae..122d29894 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -87,7 +87,7 @@ def _set_amplitude_(self, magnitude, units): function = enum_property( command="FUNC", - enum=lambda: Function, # pylint: disable=undefined-variable + enum=FunctionGenerator.Function, doc=""" Gets/sets the output function of the function generator @@ -103,7 +103,7 @@ def _set_amplitude_(self, magnitude, units): Set value should be within correct bounds of instrument. - :units: As specified (if a `~quntities.quantity.Quantity`) or assumed + :units: As specified (if a `~quantities.quantity.Quantity`) or assumed to be of units volts. :type: `~quantities.quantity.Quantity` with units volts. """ diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index b07407293..06e352558 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -468,6 +468,9 @@ def check_error_queue(self): done = True else: result.append( - self.ErrorCodes(err) if err in self.ErrorCodes else err) + self.ErrorCodes(err) + if any(err == item.value for item in self.ErrorCodes) + else err + ) return result diff --git a/instruments/minghe/__init__.py b/instruments/minghe/__init__.py new file mode 100644 index 000000000..997950f6c --- /dev/null +++ b/instruments/minghe/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing MingHe instruments +""" +from __future__ import absolute_import +from .mhs5200a import MHS5200 diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py new file mode 100644 index 000000000..cfe04b2ab --- /dev/null +++ b/instruments/minghe/mhs5200a.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Provides the support for the MingHe low-cost function generator. + +Class originally contributed by Catherine Holloway. +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division + +from builtins import range +from enum import Enum + +import quantities as pq +from instruments.abstract_instruments import FunctionGenerator +from instruments.util_fns import ProxyList, assume_units + +# CLASSES ##################################################################### + + +class MHS5200(FunctionGenerator): + """ + The MHS5200 is a low-cost, 2 channel function generator. + + There is no user manual, but Al Williams has reverse-engineered the + communications protocol: + https://github.com/wd5gnr/mhs5200a/blob/master/MHS5200AProtocol.pdf + """ + def __init__(self, filelike): + super(MHS5200, self).__init__(filelike) + self._channel_count = 2 + self.terminator = "\r\n" + + def _ack_expected(self, msg=""): + if msg.find(":r") == 0: + return None + # most commands res + return "ok" + + # INNER CLASSES # + + class Channel(FunctionGenerator.Channel): + """ + Class representing a channel on the MHS52000. + """ + # pylint: disable=protected-access + + __CHANNEL_NAMES = { + 1: '1', + 2: '2' + } + + def __init__(self, mhs, idx): + self._mhs = mhs + super(MHS5200.Channel, self).__init__(parent=mhs, name=idx) + # Use zero-based indexing for the external API, but one-based + # for talking to the instrument. + self._idx = idx + 1 + self._chan = self.__CHANNEL_NAMES[self._idx] + self._count = 0 + + def _get_amplitude_(self): + query = ":r{0}a".format(self._chan) + response = self._mhs.query(query) + return float(response.replace(query, ""))/100.0, self._mhs.VoltageMode.rms + + def _set_amplitude_(self, magnitude, units): + if units == self._mhs.VoltageMode.peak_to_peak or \ + units == self._mhs.VoltageMode.rms: + magnitude = assume_units(magnitude, "V").rescale(pq.V).magnitude + elif units == self._mhs.VoltageMode.dBm: + raise NotImplementedError("Decibel units are not supported.") + magnitude *= 100 + query = ":s{0}a{1}".format(self._chan, int(magnitude)) + self._mhs.sendcmd(query) + + @property + def duty_cycle(self): + """ + Gets/Sets the duty cycle of this channel. + + :units: A fraction + :type: `float` + """ + query = ":r{0}d".format(self._chan) + response = self._mhs.query(query) + duty = float(response.replace(query, ""))/10.0 + return duty + + @duty_cycle.setter + def duty_cycle(self, new_val): + query = ":s{0}d{1}".format(self._chan, int(100.0*new_val)) + self._mhs.sendcmd(query) + + @property + def enable(self): + """ + Gets/Sets the enable state of this channel. + + :type: `bool` + """ + query = ":r{0}b".format(self._chan) + return int(self._mhs.query(query).replace(query, ""). + replace("\r", "")) + + @enable.setter + def enable(self, newval): + query = ":s{0}b{1}".format(self._chan, int(newval)) + self._mhs.sendcmd(query) + + @property + def frequency(self): + """ + Gets/Sets the frequency of this channel. + + :units: As specified (if a `~quantities.Quantity`) or assumed to be + of units hertz. + :type: `~quantities.Quantity` + """ + query = ":r{0}f".format(self._chan) + response = self._mhs.query(query) + freq = float(response.replace(query, ""))*pq.Hz + return freq/100.0 + + @frequency.setter + def frequency(self, new_val): + new_val = assume_units(new_val, pq.Hz).rescale(pq.Hz).\ + magnitude*100.0 + query = ":s{0}f{1}".format(self._chan, int(new_val)) + self._mhs.sendcmd(query) + + @property + def offset(self): + """ + Gets/Sets the offset of this channel. + + The fraction of the duty cycle to offset the function by. + + :type: `float` + """ + # need to convert + query = ":r{0}o".format(self._chan) + response = self._mhs.query(query) + return int(response.replace(query, ""))/100.0-1.20 + + @offset.setter + def offset(self, new_val): + new_val = int(new_val*100)+120 + query = ":s{0}o{1}".format(self._chan, new_val) + self._mhs.sendcmd(query) + + @property + def phase(self): + """ + Gets/Sets the phase of this channel. + + :units: As specified (if a `~quantities.Quantity`) or assumed to be + of degrees. + :type: `~quantities.Quantity` + """ + # need to convert + query = ":r{0}p".format(self._chan) + response = self._mhs.query(query) + return int(response.replace(query, ""))*pq.deg + + @phase.setter + def phase(self, new_val): + new_val = assume_units(new_val, pq.deg).rescale("deg").magnitude + query = ":s{0}p{1}".format(self._chan, int(new_val)) + self._mhs.sendcmd(query) + + @property + def function(self): + """ + Gets/Sets the wave type of this channel. + + :type: `MHS5200.Function` + """ + query = ":r{0}w".format(self._chan) + response = self._mhs.query(query).replace(query, "") + return self._mhs.Function(int(response)) + + @function.setter + def function(self, new_val): + query = ":s{0}w{1}".format(self._chan, + self._mhs.Function(new_val).value) + self._mhs.sendcmd(query) + + class Function(Enum): + """ + Enum containing valid wave modes for + """ + sine = 0 + square = 1 + triangular = 2 + sawtooth_up = 3 + sawtooth_down = 4 + + @property + def channel(self): + """ + Gets a specific channel object. The desired channel is specified like + one would access a list. + + For instance, this would print the counts of the first channel:: + + >>> import instruments as ik + >>> mhs = ik.minghe.MHS5200.open_serial(vid=1027, pid=24577, + baud=19200, timeout=1) + >>> print(mhs.channel[0].frequency) + + :rtype: `list`[`MHS5200.Channel`] + """ + return ProxyList(self, MHS5200.Channel, range(self._channel_count)) + + @property + def serial_number(self): + """ + Get the serial number, as an int + + :rtype: int + """ + query = ":r0c" + response = self.query(query) + response = response.replace(query, "").replace("\r", "") + return response + + def _get_amplitude_(self): + raise NotImplementedError() + + def _set_amplitude_(self, magnitude, units): + raise NotImplementedError() diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index ea901d886..b54a355ad 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -26,7 +26,7 @@ @contextlib.contextmanager -def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n"): +def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n", repeat=1): """ Given an instrument class, expected output from the host and expected input from the instrument, asserts that the protocol in a context block proceeds @@ -35,7 +35,8 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n"): For an example of how to write tests using this context manager, see the ``make_name_test`` function below. - :param type ins_class: Instrument class to use for the protocol assertion. + :param ins_class: Instrument class to use for the protocol assertion. + :type ins_class: `~instruments.Instrument` :param host_to_ins: Data to be sent by the host to the instrument; this is checked against the actual data sent by the instrument class during the execution of this context manager. @@ -46,9 +47,17 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n"): be used to assert correct behaviour within the context. :type ins_to_host: ``str`` or ``list``; if ``list``, each line is concatenated with the separator given by ``sep``. + :param str sep: Character to be inserted after each string in both + host_to_ins and ins_to_host parameters. This is typically the + termination character you would like to have inserted. + :param int repeat: The number of times the host_to_ins and + ins_to_host data sets should be duplicated. Typically the default + value of 1 is sufficient, but increasing this is useful when + testing multiple calls in the same test that should have the same + command transactions. """ if isinstance(sep, bytes): - sep = sep.encode("utf-8") + sep = sep.decode("utf-8") # Normalize assertion and playback strings. if isinstance(ins_to_host, list): @@ -60,6 +69,7 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n"): (sep.encode("utf-8") if ins_to_host else b"") elif isinstance(ins_to_host, str): ins_to_host = ins_to_host.encode("utf-8") + ins_to_host *= repeat if isinstance(host_to_ins, list): host_to_ins = [ @@ -70,6 +80,7 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n"): (sep.encode("utf-8") if host_to_ins else b"") elif isinstance(host_to_ins, str): host_to_ins = host_to_ins.encode("utf-8") + host_to_ins *= repeat stdin = BytesIO(ins_to_host) stdout = BytesIO() diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py new file mode 100644 index 000000000..00198669b --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract function generator class +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import + +import pytest +import quantities as pq + +import instruments as ik + + +# TESTS ###################################################################### + +@pytest.fixture +def fg(): + return ik.abstract_instruments.FunctionGenerator.open_test() + + +def test_func_gen_default_channel_count(fg): + assert fg._channel_count == 1 + + +def test_func_gen_raises_not_implemented_error_one_channel_getting(fg): + fg._channel_count = 1 + with pytest.raises(NotImplementedError): + _ = fg.amplitude + with pytest.raises(NotImplementedError): + _ = fg.frequency + with pytest.raises(NotImplementedError): + _ = fg.function + with pytest.raises(NotImplementedError): + _ = fg.offset + with pytest.raises(NotImplementedError): + _ = fg.phase + + +def test_func_gen_raises_not_implemented_error_one_channel_setting(fg): + fg._channel_count = 1 + with pytest.raises(NotImplementedError): + fg.amplitude = 1 + with pytest.raises(NotImplementedError): + fg.frequency = 1 + with pytest.raises(NotImplementedError): + fg.function = 1 + with pytest.raises(NotImplementedError): + fg.offset = 1 + with pytest.raises(NotImplementedError): + fg.phase = 1 + + +def test_func_gen_raises_not_implemented_error_two_channel_getting(fg): + fg._channel_count = 2 + with pytest.raises(NotImplementedError): + _ = fg.channel[0].amplitude + with pytest.raises(NotImplementedError): + _ = fg.channel[0].frequency + with pytest.raises(NotImplementedError): + _ = fg.channel[0].function + with pytest.raises(NotImplementedError): + _ = fg.channel[0].offset + with pytest.raises(NotImplementedError): + _ = fg.channel[0].phase + + +def test_func_gen_raises_not_implemented_error_two_channel_setting(fg): + fg._channel_count = 2 + with pytest.raises(NotImplementedError): + fg.channel[0].amplitude = 1 + with pytest.raises(NotImplementedError): + fg.channel[0].frequency = 1 + with pytest.raises(NotImplementedError): + fg.channel[0].function = 1 + with pytest.raises(NotImplementedError): + fg.channel[0].offset = 1 + with pytest.raises(NotImplementedError): + fg.channel[0].phase = 1 + + +def test_func_gen_two_channel_passes_thru_call_getter(fg, mocker): + mock_channel = mocker.MagicMock() + mock_properties = [mocker.PropertyMock(return_value=1) for _ in range(5)] + + mocker.patch("instruments.abstract_instruments.FunctionGenerator.Channel", new=mock_channel) + type(mock_channel()).amplitude = mock_properties[0] + type(mock_channel()).frequency = mock_properties[1] + type(mock_channel()).function = mock_properties[2] + type(mock_channel()).offset = mock_properties[3] + type(mock_channel()).phase = mock_properties[4] + + fg._channel_count = 2 + _ = fg.amplitude + _ = fg.frequency + _ = fg.function + _ = fg.offset + _ = fg.phase + + for mock_property in mock_properties: + mock_property.assert_called_once_with() + + +def test_func_gen_one_channel_passes_thru_call_getter(fg, mocker): + mock_properties = [mocker.PropertyMock(return_value=1) for _ in range(4)] + mock_method = mocker.MagicMock(return_value=(1, pq.V)) + + mocker.patch("instruments.abstract_instruments.FunctionGenerator.frequency", new=mock_properties[0]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator.function", new=mock_properties[1]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator.offset", new=mock_properties[2]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator.phase", new=mock_properties[3]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator._get_amplitude_", new=mock_method) + + fg._channel_count = 1 + _ = fg.channel[0].amplitude + _ = fg.channel[0].frequency + _ = fg.channel[0].function + _ = fg.channel[0].offset + _ = fg.channel[0].phase + + for mock_property in mock_properties: + mock_property.assert_called_once_with() + + mock_method.assert_called_once_with() + + +def test_func_gen_two_channel_passes_thru_call_setter(fg, mocker): + mock_channel = mocker.MagicMock() + mock_properties = [mocker.PropertyMock() for _ in range(5)] + + mocker.patch("instruments.abstract_instruments.FunctionGenerator.Channel", new=mock_channel) + type(mock_channel()).amplitude = mock_properties[0] + type(mock_channel()).frequency = mock_properties[1] + type(mock_channel()).function = mock_properties[2] + type(mock_channel()).offset = mock_properties[3] + type(mock_channel()).phase = mock_properties[4] + + fg._channel_count = 2 + fg.amplitude = 1 + fg.frequency = 1 + fg.function = 1 + fg.offset = 1 + fg.phase = 1 + + for mock_property in mock_properties: + mock_property.assert_called_once_with(1) + + +def test_func_gen_one_channel_passes_thru_call_setter(fg, mocker): + mock_properties = [mocker.PropertyMock() for _ in range(4)] + mock_method = mocker.MagicMock() + + mocker.patch("instruments.abstract_instruments.FunctionGenerator.frequency", new=mock_properties[0]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator.function", new=mock_properties[1]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator.offset", new=mock_properties[2]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator.phase", new=mock_properties[3]) + mocker.patch("instruments.abstract_instruments.FunctionGenerator._set_amplitude_", new=mock_method) + + fg._channel_count = 1 + fg.channel[0].amplitude = 1 + fg.channel[0].frequency = 1 + fg.channel[0].function = 1 + fg.channel[0].offset = 1 + fg.channel[0].phase = 1 + + for mock_property in mock_properties: + mock_property.assert_called_once_with(1) + + mock_method.assert_called_once_with(magnitude=1, units=fg.VoltageMode.peak_to_peak) diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py new file mode 100644 index 000000000..425e03a19 --- /dev/null +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for generic SCPI function generator instruments +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import + +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol, make_name_test + +# TESTS ###################################################################### + +test_scpi_func_gen_name = make_name_test(ik.agilent.Agilent33220a) + + +def test_agilent33220a_amplitude(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "VOLT:UNIT?", + "VOLT?", + "VOLT:UNIT VPP", + "VOLT 2.0", + "VOLT:UNIT DBM", + "VOLT 1.5" + ], [ + "VPP", + "+1.000000E+00" + ] + ) as fg: + assert fg.amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) + fg.amplitude = 2 * pq.V + fg.amplitude = (1.5 * pq.V, fg.VoltageMode.dBm) + + +def test_agilent33220a_frequency(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "FREQ?", + "FREQ 1.005000e+02" + ], [ + "+1.234000E+03" + ] + ) as fg: + assert fg.frequency == 1234 * pq.Hz + fg.frequency = 100.5 * pq.Hz + + +def test_agilent33220a_function(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "FUNC?", + "FUNC:SQU" + ], [ + "SIN" + ] + ) as fg: + assert fg.function == fg.Function.sinusoid + fg.function = fg.Function.square + + +def test_agilent33220a_offset(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "VOLT:OFFS?", + "VOLT:OFFS 4.321000e-01" + ], [ + "+1.234000E+01", + ] + ) as fg: + assert fg.offset == 12.34 * pq.V + fg.offset = 0.4321 * pq.V + + +def test_agilent33220a_duty_cycle(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "FUNC:SQU:DCYC?", + "FUNC:SQU:DCYC 75" + ], [ + "53", + ] + ) as fg: + assert fg.duty_cycle == 53 + fg.duty_cycle = 75 + + +def test_agilent33220a_ramp_symmetry(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "FUNC:RAMP:SYMM?", + "FUNC:RAMP:SYMM 75" + ], [ + "53", + ] + ) as fg: + assert fg.ramp_symmetry == 53 + fg.ramp_symmetry = 75 + + +def test_agilent33220a_output(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "OUTP?", + "OUTP OFF" + ], [ + "ON", + ] + ) as fg: + assert fg.output is True + fg.output = False + + +def test_agilent33220a_output_sync(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "OUTP:SYNC?", + "OUTP:SYNC OFF" + ], [ + "ON", + ] + ) as fg: + assert fg.output_sync is True + fg.output_sync = False + + +def test_agilent33220a_output_polarity(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "OUTP:POL?", + "OUTP:POL NORM" + ], [ + "INV", + ] + ) as fg: + assert fg.output_polarity == fg.OutputPolarity.inverted + fg.output_polarity = fg.OutputPolarity.normal + + +def test_agilent33220a_load_resistance(): + with expected_protocol( + ik.agilent.Agilent33220a, + [ + "OUTP:LOAD?", + "OUTP:LOAD?", + "OUTP:LOAD 100.0", + "OUTP:LOAD MAX" + ], [ + "50", + "INF" + ] + ) as fg: + assert fg.load_resistance == 50 * pq.Ohm + assert fg.load_resistance == fg.LoadResistance.high_impedance + fg.load_resistance = 100 * pq.Ohm + fg.load_resistance = fg.LoadResistance.maximum diff --git a/instruments/tests/test_comm/test_loopback.py b/instruments/tests/test_comm/test_loopback.py index c02c36024..943fd6a0e 100644 --- a/instruments/tests/test_comm/test_loopback.py +++ b/instruments/tests/test_comm/test_loopback.py @@ -99,6 +99,25 @@ def test_loopbackcomm_read_raw_2char_terminator(): assert mock_stdin.read.call_count == 5 +def test_loopbackcomm_read_raw_terminator_is_empty_string(): + mock_stdin = mock.MagicMock() + mock_stdin.read.side_effect = [b"abc"] + comm = LoopbackCommunicator(stdin=mock_stdin) + comm._terminator = "" + + assert comm.read_raw() == b"abc" + mock_stdin.read.assert_has_calls([mock.call(-1)]) + assert mock_stdin.read.call_count == 1 + + +def test_loopbackcomm_read_raw_size_invalid(): + with pytest.raises(ValueError): + mock_stdin = mock.MagicMock() + mock_stdin.read.side_effect = [b"abc"] + comm = LoopbackCommunicator(stdin=mock_stdin) + comm.read_raw(size=-2) + + def test_loopbackcomm_write_raw(): mock_stdout = mock.MagicMock() comm = LoopbackCommunicator(stdout=mock_stdout) diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py new file mode 100644 index 000000000..d32456728 --- /dev/null +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for generic SCPI function generator instruments +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import + +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol, make_name_test + +# TESTS ###################################################################### + +test_scpi_func_gen_name = make_name_test(ik.generic_scpi.SCPIFunctionGenerator) + + +def test_scpi_func_gen_amplitude(): + with expected_protocol( + ik.generic_scpi.SCPIFunctionGenerator, + [ + "VOLT:UNIT?", + "VOLT?", + "VOLT:UNIT VPP", + "VOLT 2.0", + "VOLT:UNIT DBM", + "VOLT 1.5" + ], [ + "VPP", + "+1.000000E+00" + ], + repeat=2 + ) as fg: + assert fg.amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) + fg.amplitude = 2 * pq.V + fg.amplitude = (1.5 * pq.V, fg.VoltageMode.dBm) + + assert fg.channel[0].amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) + fg.channel[0].amplitude = 2 * pq.V + fg.channel[0].amplitude = (1.5 * pq.V, fg.VoltageMode.dBm) + + +def test_scpi_func_gen_frequency(): + with expected_protocol( + ik.generic_scpi.SCPIFunctionGenerator, + [ + "FREQ?", + "FREQ 1.005000e+02" + ], [ + "+1.234000E+03" + ], + repeat=2 + ) as fg: + assert fg.frequency == 1234 * pq.Hz + fg.frequency = 100.5 * pq.Hz + + assert fg.channel[0].frequency == 1234 * pq.Hz + fg.channel[0].frequency = 100.5 * pq.Hz + + +def test_scpi_func_gen_function(): + with expected_protocol( + ik.generic_scpi.SCPIFunctionGenerator, + [ + "FUNC?", + "FUNC SQU" + ], [ + "SIN" + ], + repeat=2 + ) as fg: + assert fg.function == fg.Function.sinusoid + fg.function = fg.Function.square + + assert fg.channel[0].function == fg.Function.sinusoid + fg.channel[0].function = fg.Function.square + + +def test_scpi_func_gen_offset(): + with expected_protocol( + ik.generic_scpi.SCPIFunctionGenerator, + [ + "VOLT:OFFS?", + "VOLT:OFFS 4.321000e-01" + ], [ + "+1.234000E+01", + ], + repeat=2 + ) as fg: + assert fg.offset == 12.34 * pq.V + fg.offset = 0.4321 * pq.V + + assert fg.channel[0].offset == 12.34 * pq.V + fg.channel[0].offset = 0.4321 * pq.V diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py new file mode 100644 index 000000000..1f70afdc8 --- /dev/null +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the MingHe MHS52000a +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import + +import pytest +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +def test_mhs_amplitude(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1a", + ":r2a", + ":s1a660", + ":s2a800" + ], + [ + ":r1a330", + ":r2a500", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].amplitude[0] == 3.3*pq.V + assert mhs.channel[1].amplitude[0] == 5.0*pq.V + mhs.channel[0].amplitude = 6.6*pq.V + mhs.channel[1].amplitude = 8.0*pq.V + + +def test_mhs_amplitude_dbm_notimplemented(): + with expected_protocol( + ik.minghe.MHS5200, + [], + [], + sep="\r\n" + ) as mhs: + with pytest.raises(NotImplementedError): + mhs.channel[0].amplitude = 6.6*ik.units.dBm + + +def test_mhs_duty_cycle(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1d", + ":r2d", + ":s1d6", + ":s2d80" + + ], + [ + ":r1d010", + ":r2d100", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].duty_cycle == 1.0 + assert mhs.channel[1].duty_cycle == 10.0 + mhs.channel[0].duty_cycle = 0.06 + mhs.channel[1].duty_cycle = 0.8 + + +def test_mhs_enable(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1b", + ":r2b", + ":s1b0", + ":s2b1" + ], + [ + ":r1b1", + ":r2b0", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].enable + assert not mhs.channel[1].enable + mhs.channel[0].enable = False + mhs.channel[1].enable = True + + +def test_mhs_frequency(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1f", + ":r2f", + ":s1f600000", + ":s2f800000" + + ], + [ + ":r1f3300000", + ":r2f50000000", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].frequency == 33.0*pq.kHz + assert mhs.channel[1].frequency == 500.0*pq.kHz + mhs.channel[0].frequency = 6*pq.kHz + mhs.channel[1].frequency = 8*pq.kHz + + +def test_mhs_offset(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1o", + ":r2o", + ":s1o60", + ":s2o180" + + ], + [ + ":r1o120", + ":r2o0", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].offset == 0 + assert mhs.channel[1].offset == -1.2 + mhs.channel[0].offset = -0.6 + mhs.channel[1].offset = 0.6 + + +def test_mhs_phase(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1p", + ":r2p", + ":s1p60", + ":s2p180" + + ], + [ + ":r1p120", + ":r2p0", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].phase == 120 + assert mhs.channel[1].phase == 0 + mhs.channel[0].phase = 60 + mhs.channel[1].phase = 180 + + +def test_mhs_wave_type(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r1w", + ":r2w", + ":s1w2", + ":s2w3" + + ], + [ + ":r1w0", + ":r2w1", + "ok", + "ok" + ], + sep="\r\n" + ) as mhs: + assert mhs.channel[0].function == mhs.Function.sine + assert mhs.channel[1].function == mhs.Function.square + mhs.channel[0].function = mhs.Function.triangular + mhs.channel[1].function = mhs.Function.sawtooth_up + + +def test_mhs_serial_number(): + with expected_protocol( + ik.minghe.MHS5200, + [ + ":r0c" + + ], + [ + ":r0c5225A1", + ], + sep="\r\n" + ) as mhs: + assert mhs.serial_number == "5225A1" diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py new file mode 100644 index 000000000..286d585f0 --- /dev/null +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Thorlabs TC200 +""" + +# IMPORTS #################################################################### + +# pylint: disable=unused-import + +from __future__ import absolute_import + +import struct + +import pytest +import quantities as pq + +import instruments as ik +from instruments.thorlabs._packets import ThorLabsPacket, hw_info_data +from instruments.thorlabs._cmds import ThorLabsCommands +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + +# pylint: disable=protected-access,unused-argument + + +def test_apt_hw_info(): + with expected_protocol( + ik.thorlabs.ThorLabsAPT, + [ + ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + ], + [ + ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "ABC-123".encode('ascii'), + # HW type + 3, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 2 + ) + ).pack() + ], + sep="" + ) as apt: + # Check internal representations. + # NB: we shouldn't do this in some sense, but these fields + # act as an API to the APT subclasses. + assert apt._hw_type == "Unknown type: 3" + assert apt._fw_version == "a1.a2.a3" + assert apt._notes == "abcdefg" + assert apt._hw_version == 42 + assert apt._mod_state == 43 + + # Check external API. + assert apt.serial_number == '01020304' + assert apt.model_number == 'ABC-123' + assert apt.name == ( + "ThorLabs APT Instrument model ABC-123, " + "serial 01020304 (HW version 42, FW version a1.a2.a3)" + ) diff --git a/instruments/thorlabs/_abstract.py b/instruments/thorlabs/_abstract.py index 820950dac..423e1d465 100644 --- a/instruments/thorlabs/_abstract.py +++ b/instruments/thorlabs/_abstract.py @@ -9,8 +9,13 @@ from __future__ import absolute_import from __future__ import division +import time + from instruments.thorlabs import _packets from instruments.abstract_instruments.instrument import Instrument +from instruments.util_fns import assume_units + +from quantities import second # CLASSES ##################################################################### @@ -35,10 +40,10 @@ def sendpacket(self, packet): :param packet: The thorlabs data packet that will be queried :type packet: `ThorLabsPacket` """ - self.sendcmd(packet.pack()) + self._file.write_raw(packet.pack()) # pylint: disable=protected-access - def querypacket(self, packet, expect=None): + def querypacket(self, packet, expect=None, timeout=None, expect_data_len=None): """ Sends a packet to the connected APT instrument, and waits for a packet in response. Optionally, checks whether the received packet type is @@ -52,11 +57,40 @@ def querypacket(self, packet, expect=None): with the default value of `None` then no checking occurs. :type expect: `str` or `None` + :param timeout: Sets a timeout to wait before returning `None`, indicating + no packet was received. If the timeout is set to `None`, then the + timeout is inherited from the underlying communicator and no additional + timeout is added. If timeout is set to `False`, then this method waits + indefinitely. If timeout is set to a unitful quantity, then it is interpreted + as a time and used as the timeout value. Finally, if the timeout is a unitless + number (e.g. `float` or `int`), then seconds are assumed. + + :param int expect_data_len: Number of bytes to expect as the + data for the returned packet. + :return: Returns the response back from the instrument wrapped up in - a thorlabs packet + a ThorLabs APT packet, or None if no packet was received. :rtype: `ThorLabsPacket` """ - resp = self.query(packet.pack()) + t_start = time.time() + + if timeout: + timeout = assume_units(timeout, second).rescale('second').magnitude + + while True: + self._file.write_raw(packet.pack()) + resp = self._file.read_raw( + expect_data_len + 6 # the header is six bytes. + if expect_data_len else + 6 + ) + if resp or timeout is None: + break + else: + tic = time.time() + if tic - t_start > timeout: + break + if not resp: if expect is None: return None @@ -71,4 +105,5 @@ def querypacket(self, packet, expect=None): raise IOError("APT returned message ID {}, expected {}".format( pkt._message_id, expect )) + return pkt diff --git a/instruments/thorlabs/_packets.py b/instruments/thorlabs/_packets.py index 7a9ef8ded..5c4171970 100644 --- a/instruments/thorlabs/_packets.py +++ b/instruments/thorlabs/_packets.py @@ -15,6 +15,18 @@ message_header_nopacket = struct.Struct(' Date: Tue, 19 Feb 2019 14:00:12 -0500 Subject: [PATCH 023/108] Bump version v0.4.3 -> v0.5.0 (#210) --- instruments/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index 3e096bee5..9289f052d 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -37,7 +37,7 @@ # In keeping with PEP-396, we define a version number of the form # {major}.{minor}[.{postrelease}]{prerelease-tag} -__version__ = "0.4.3" +__version__ = "0.5.0" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" From 141861fc067f30ddb6a1d59ec79a6e2388a8c2a5 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 1 Jan 2020 23:43:22 -0500 Subject: [PATCH 024/108] Pin hypothesis version to fix build (#222) --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index b4082436b..0c2383434 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,6 @@ mock pytest pytest-mock -hypothesis +hypothesis==4.28.2 pylint==1.7.1 astroid==1.5.3 From eba82b2ebb3552812f3ae49dceb05af6e3246b8a Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 1 Jan 2020 23:50:05 -0500 Subject: [PATCH 025/108] GPIBUSB connection enhancements (#221) --- .../abstract_instruments/comm/__init__.py | 10 +-- ...b_communicator.py => gpib_communicator.py} | 79 ++++++++++++------- .../abstract_instruments/instrument.py | 24 ++++-- instruments/tests/test_base_instrument.py | 5 +- .../{test_gi_gpibusb.py => test_gpibusb.py} | 6 +- 5 files changed, 80 insertions(+), 44 deletions(-) rename instruments/abstract_instruments/comm/{gi_gpib_communicator.py => gpib_communicator.py} (81%) rename instruments/tests/test_comm/{test_gi_gpibusb.py => test_gpibusb.py} (97%) diff --git a/instruments/abstract_instruments/comm/__init__.py b/instruments/abstract_instruments/comm/__init__.py index 0cf070538..b4274638c 100644 --- a/instruments/abstract_instruments/comm/__init__.py +++ b/instruments/abstract_instruments/comm/__init__.py @@ -8,12 +8,12 @@ from .abstract_comm import AbstractCommunicator +from .file_communicator import FileCommunicator +from .gpib_communicator import GPIBCommunicator +from .loopback_communicator import LoopbackCommunicator +from .serial_communicator import SerialCommunicator from .socket_communicator import SocketCommunicator from .usb_communicator import USBCommunicator -from .serial_communicator import SerialCommunicator -from .visa_communicator import VisaCommunicator -from .loopback_communicator import LoopbackCommunicator -from .gi_gpib_communicator import GPIBCommunicator -from .file_communicator import FileCommunicator from .usbtmc_communicator import USBTMCCommunicator +from .visa_communicator import VisaCommunicator from .vxi11_communicator import VXI11Communicator diff --git a/instruments/abstract_instruments/comm/gi_gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py similarity index 81% rename from instruments/abstract_instruments/comm/gi_gpib_communicator.py rename to instruments/abstract_instruments/comm/gpib_communicator.py index 3123059d7..65e4051e8 100644 --- a/instruments/abstract_instruments/comm/gi_gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ Provides a communication layer for an instrument connected via a Galvant -Industries GPIB adapter. +Industries or Prologix GPIB adapter. """ # IMPORTS ##################################################################### @@ -11,6 +11,7 @@ from __future__ import division from __future__ import unicode_literals +from enum import Enum import io import time @@ -27,29 +28,43 @@ class GPIBCommunicator(io.IOBase, AbstractCommunicator): """ Communicates with a SocketCommunicator or SerialCommunicator object for - use with Galvant Industries GPIBUSB or GPIBETHERNET adapters. + use with Galvant Industries or Prologix GPIBUSB or GPIBETHERNET adapters. It essentially wraps those physical communication layers with the extra - overhead required by the Galvant GPIB adapters. + overhead required by the GPIB adapters. """ # pylint: disable=too-many-instance-attributes - def __init__(self, filelike, gpib_address): + def __init__(self, filelike, gpib_address, model="gi"): super(GPIBCommunicator, self).__init__(self) - + self._model = self.Model(model) self._file = filelike self._gpib_address = gpib_address self._file.terminator = "\r" - self._version = int(self._file.query("+ver")) + if self._model == GPIBCommunicator.Model.gi: + self._version = int(self._file.query("+ver")) + if self._model == GPIBCommunicator.Model.pl: + self._file.sendcmd("++auto 0") self._terminator = None self.terminator = "\n" self._eoi = True self._timeout = 1000 * pq.millisecond - if self._version <= 4: + if self._model == GPIBCommunicator.Model.gi and self._version <= 4: self._eos = 10 else: self._eos = "\n" + # ENUMS # + + class Model(Enum): + """ + Enum containing the supported GPIB controller models + """ + #: Galvant Industries + gi = "gi" + #: Prologix, LLC + pl = "pl" + # PROPERTIES # @property @@ -95,12 +110,12 @@ def timeout(self): @timeout.setter def timeout(self, newval): newval = assume_units(newval, pq.second) - if self._version <= 4: + if self._model == GPIBCommunicator.Model.gi and self._version <= 4: newval = newval.rescale(pq.second) - self._file.sendcmd('+t:{}'.format(newval.magnitude)) - elif self._version >= 5: + self._file.sendcmd('+t:{}'.format(int(newval.magnitude))) + else: newval = newval.rescale(pq.millisecond) - self._file.sendcmd("++read_tmo_ms {}".format(newval.magnitude)) + self._file.sendcmd("++read_tmo_ms {}".format(int(newval.magnitude))) self._file.timeout = newval.rescale(pq.second) self._timeout = newval.rescale(pq.second) @@ -127,7 +142,7 @@ def terminator(self, newval): if isinstance(newval, str): newval = newval.lower() - if self._version <= 4: + if self._model == GPIBCommunicator.Model.gi and self._version <= 4: if newval == 'eoi': self.eoi = True elif not isinstance(newval, int): @@ -148,7 +163,7 @@ def terminator(self, newval): self.eoi = False self.eos = newval self._terminator = chr(newval) - elif self._version >= 5: + else: if newval != "eoi": self.eos = newval self.eoi = False @@ -182,10 +197,10 @@ def eoi(self, newval): if not isinstance(newval, bool): raise TypeError("EOI status must be specified as a boolean") self._eoi = newval - if self._version >= 5: - self._file.sendcmd("++eoi {}".format('1' if newval else '0')) - else: + if self._model == GPIBCommunicator.Model.gi and self._version <= 4: self._file.sendcmd("+eoi:{}".format('1' if newval else '0')) + else: + self._file.sendcmd("++eoi {}".format('1' if newval else '0')) @property def eos(self): @@ -203,12 +218,12 @@ def eos(self): @eos.setter def eos(self, newval): - if self._version <= 4: + if self._model == GPIBCommunicator.Model.gi and self._version <= 4: if isinstance(newval, (str, bytes)): newval = ord(newval) self._file.sendcmd("+eos:{}".format(newval)) self._eos = newval - elif self._version >= 5: + else: if isinstance(newval, int): newval = str(chr(newval)) if newval == "\r\n": @@ -299,8 +314,8 @@ def flush_input(self): def _sendcmd(self, msg): """ This is the implementation of ``sendcmd`` for communicating with - the Galvant Industries GPIB adapter. This function is in turn wrapped by - the concrete method `AbstractCommunicator.sendcmd` to provide consistent + the GPIB adapters. This function is in turn wrapped by the concrete + method `AbstractCommunicator.sendcmd` to provide consistent logging functionality across all communication layers. :param str msg: The command message to send to the instrument @@ -309,7 +324,10 @@ def _sendcmd(self, msg): if msg == '': return - self._file.sendcmd('+a:' + str(self._gpib_address)) + if self._model == GPIBCommunicator.Model.gi: + self._file.sendcmd("+a:{0}".format(str(self._gpib_address))) + else: + self._file.sendcmd("++addr {0}".format(str(self._gpib_address))) time.sleep(sleep_time) self.eoi = self.eoi time.sleep(sleep_time) @@ -323,13 +341,18 @@ def _sendcmd(self, msg): def _query(self, msg, size=-1): """ This is the implementation of ``query`` for communicating with - the Galvant Industries GPIB adapter. This function is in turn wrapped by - the concrete method `AbstractCommunicator.query` to provide consistent + the GPIB adapters. This function is in turn wrapped by the concrete + method `AbstractCommunicator.query` to provide consistent logging functionality across all communication layers. - If a ``?`` is not present in ``msg`` then the adapter will be - instructed to get the response from the instrument via the ``+read`` - command. + The Galvant Industries adaptor is set to automatically get a + response if a ``?`` is present in ``msg``. If it is not present, + then the adapter will be instructed to get the response from the + instrument via the ``+read`` command. + + The Prologix adapter is set to not get a response unless told to do + so. It is instructed to get a response from the instrument via the + ``++read`` command. :param str msg: The query message to send to the instrument :param int size: The number of bytes to read back from the instrument @@ -338,6 +361,8 @@ def _query(self, msg, size=-1): :rtype: `str` """ self.sendcmd(msg) - if '?' not in msg: + if self._model == GPIBCommunicator.Model.gi and '?' not in msg: self._file.sendcmd('+read') + if self._model == GPIBCommunicator.Model.pl: + self._file.sendcmd('++read') return self._file.read(size).strip() diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 2b294e96c..b025cfa47 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -529,7 +529,7 @@ def open_serial(cls, port=None, baud=9600, vid=None, pid=None, return cls(ser) @classmethod - def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3): + def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3, model="gi"): """ Opens an instrument, connecting via a `Galvant Industries GPIB-USB adapter`_. @@ -544,6 +544,8 @@ def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3): instrument before timing out. :param float write_timeout: Number of seconds to wait when writing to the instrument before timing out. + :param str model: The brand of adapter to be connected to. Currently supported + is "gi" for Galvant Industries, and "pl" for Prologix LLC. :rtype: `Instrument` :return: Object representing the connected instrument. @@ -559,18 +561,26 @@ def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3): timeout=timeout, write_timeout=write_timeout ) - return cls(GPIBCommunicator(ser, gpib_address)) + return cls(GPIBCommunicator(ser, gpib_address, model)) @classmethod - def open_gpibethernet(cls, host, port, gpib_address): + def open_gpibethernet(cls, host, port, gpib_address, model="pl"): """ - .. warning:: The GPIB-Ethernet adapter that this connection would - use does not actually exist, and thus this class method should - not be used. + Opens an instrument, connecting via a Prologix GPIBETHERNET adapter. + + :param str host: Name or IP address of the instrument. + :param int port: TCP port on which the insturment is listening. + :param int gpib_address: Address on the connected GPIB bus assigned to + the instrument. + :param str model: The brand of adapter to be connected to. Currently supported + is "gi" for Galvant Industries, and "pl" for Prologix LLC. + + .. warning:: This function has been setup for use with the Prologix + GPIBETHERNET adapter but has not been tested as confirmed working. """ conn = socket.socket() conn.connect((host, port)) - return cls(GPIBCommunicator(conn, gpib_address)) + return cls(GPIBCommunicator(conn, gpib_address, model)) @classmethod def open_visa(cls, resource_name): diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index f369f3d95..e97c36b1b 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -257,7 +257,7 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator mock_gpib_comm.return_value.__class__ = GPIBCommunicator - inst = ik.Instrument.open_gpibusb("/dev/port", gpib_address=1) + inst = ik.Instrument.open_gpibusb("/dev/port", gpib_address=1, model="gi") assert isinstance(inst._file, GPIBCommunicator) is True @@ -270,7 +270,8 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): mock_gpib_comm.assert_called_with( mock_serial_manager.new_serial_connection.return_value, - 1 + 1, + "gi" ) diff --git a/instruments/tests/test_comm/test_gi_gpibusb.py b/instruments/tests/test_comm/test_gpibusb.py similarity index 97% rename from instruments/tests/test_comm/test_gi_gpibusb.py rename to instruments/tests/test_comm/test_gpibusb.py index b6b75f428..e68c360d3 100644 --- a/instruments/tests/test_comm/test_gi_gpibusb.py +++ b/instruments/tests/test_comm/test_gpibusb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -Unit tests for the GI GPIBUSB communication layer +Unit tests for the GPIBUSB communication layer """ # IMPORTS #################################################################### @@ -199,7 +199,7 @@ def test_gpibusbcomm_timeout(): unit_eq(comm.timeout, 1000 * pq.millisecond) comm.timeout = 5000 * pq.millisecond - comm._file.sendcmd.assert_called_with("++read_tmo_ms 5000.0") + comm._file.sendcmd.assert_called_with("++read_tmo_ms 5000") def test_gpibusbcomm_close(): @@ -235,7 +235,7 @@ def test_gpibusbcomm_sendcmd(): comm._file.sendcmd.assert_has_calls([ mock.call("+a:1"), mock.call("++eoi 1"), - mock.call("++read_tmo_ms 1000.0"), + mock.call("++read_tmo_ms 1000"), mock.call("++eos 2"), mock.call("mock") ]) From fb86f04f6f23f2bf47d88096efc8b6637543f8da Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 1 Jan 2020 23:20:06 -0600 Subject: [PATCH 026/108] Update srsdg645.py (#219) * Update srsdg645.py Added the capability to set the level offset for each output. Used the exact same formalism as in level_amplitude subroutine. * Update test_srsdg645.py Added the testcase * Update test_srsdg645.py typos fixed Co-authored-by: Steven Casagrande --- instruments/srs/srsdg645.py | 18 ++++++++++++++++++ instruments/tests/test_srs/test_srsdg645.py | 20 +++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index b1ea42072..932858f97 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -220,6 +220,24 @@ def level_amplitude(self, newval): newval = assume_units(newval, 'V').magnitude self._parent.sendcmd("LAMP {},{}".format(self._idx, newval)) + @property + def level_offset(self): + """ + Amplitude offset (in voltage) of the output level for this output. + + :type: `float` or :class:`~quantities.Quantity` + :units: As specified, or :math:`\\text{V}` by default. + """ + return pq.Quantity( + float(self._parent.query('LOFF? {}'.format(self._idx))), + 'V' + ) + + @level_offset.setter + def level_offset(self, newval): + newval = assume_units(newval, 'V').magnitude + self._parent.sendcmd("LOFF {},{}".format(self._idx, newval)) + # PROPERTIES # @property diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index 000142556..69727a09f 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -20,7 +20,7 @@ def test_srsdg645_output_level(): """ - SRSDG645: Checks getting/setting unitful ouput level. + SRSDG645: Checks getting/setting unitful output level. """ with expected_protocol( ik.srs.SRSDG645, @@ -36,6 +36,24 @@ def test_srsdg645_output_level(): ddg.output['AB'].level_amplitude = 4.0 +def test_srsdg645_output_offset(): + """ + SRSDG645: Checks getting/setting unitful output offset. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "LOFF? 1", + "LOFF 1,2.0", + ], + [ + "1.2" + ] + ) as ddg: + unit_eq(ddg.output['AB'].level_offset, pq.Quantity(1.2, "V")) + ddg.output['AB'].level_offset = 2.0 + + def test_srsdg645_output_polarity(): """ SRSDG645: Checks getting/setting From 93a10d4b2db666bf450799e6ac1557d86f79c4c7 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 4 Jan 2020 15:57:22 -0500 Subject: [PATCH 027/108] Add several instruments from francois-drielsma (#224) --- doc/source/apiref/fluke.rst | 15 + doc/source/apiref/glassman.rst | 15 + doc/source/apiref/hp.rst | 7 + doc/source/apiref/index.rst | 4 + doc/source/apiref/keithley.rst | 7 + instruments/__init__.py | 2 + instruments/fluke/__init__.py | 9 + instruments/fluke/fluke3000.py | 495 ++++++++++++++++++ instruments/glassman/__init__.py | 9 + instruments/glassman/glassmanfr.py | 489 +++++++++++++++++ instruments/hp/__init__.py | 1 + instruments/hp/hpe3631a.py | 278 ++++++++++ instruments/keithley/__init__.py | 1 + instruments/keithley/keithley485.py | 429 +++++++++++++++ instruments/tests/test_fluke/__init__.py | 0 .../tests/test_fluke/test_fluke3000.py | 164 ++++++ instruments/tests/test_glassman/__init__.py | 0 .../tests/test_glassman/test_glassmanfr.py | 237 +++++++++ instruments/tests/test_hp/test_hpe3631a.py | 160 ++++++ .../tests/test_keithley/test_keithley485.py | 153 ++++++ 20 files changed, 2475 insertions(+) create mode 100644 doc/source/apiref/fluke.rst create mode 100644 doc/source/apiref/glassman.rst create mode 100644 instruments/fluke/__init__.py create mode 100644 instruments/fluke/fluke3000.py create mode 100644 instruments/glassman/__init__.py create mode 100644 instruments/glassman/glassmanfr.py create mode 100644 instruments/hp/hpe3631a.py create mode 100644 instruments/keithley/keithley485.py create mode 100644 instruments/tests/test_fluke/__init__.py create mode 100644 instruments/tests/test_fluke/test_fluke3000.py create mode 100644 instruments/tests/test_glassman/__init__.py create mode 100644 instruments/tests/test_glassman/test_glassmanfr.py create mode 100644 instruments/tests/test_hp/test_hpe3631a.py create mode 100644 instruments/tests/test_keithley/test_keithley485.py diff --git a/doc/source/apiref/fluke.rst b/doc/source/apiref/fluke.rst new file mode 100644 index 000000000..06a728900 --- /dev/null +++ b/doc/source/apiref/fluke.rst @@ -0,0 +1,15 @@ +.. + TODO: put documentation license header here. + +.. currentmodule:: instruments.fluke + +===== +Fluke +===== + +:class:`Fluke3000` Industrial System +==================================== + +.. autoclass:: Fluke3000 + :members: + :undoc-members: diff --git a/doc/source/apiref/glassman.rst b/doc/source/apiref/glassman.rst new file mode 100644 index 000000000..acf1204f7 --- /dev/null +++ b/doc/source/apiref/glassman.rst @@ -0,0 +1,15 @@ +.. + TODO: put documentation license header here. + +.. currentmodule:: instruments.glassman + +======== +Glassman +======== + +:class:`GlassmanFR` Single Output Power Supply +============================================== + +.. autoclass:: GlassmanFR + :members: + :undoc-members: diff --git a/doc/source/apiref/hp.rst b/doc/source/apiref/hp.rst index 410548e62..52b99f26a 100644 --- a/doc/source/apiref/hp.rst +++ b/doc/source/apiref/hp.rst @@ -34,3 +34,10 @@ Hewlett-Packard .. autoclass:: HP6652a :members: :undoc-members: + +:class:`HPe3631a` Power Supply +============================== + +.. autoclass:: HPe3631a + :members: + :undoc-members: diff --git a/doc/source/apiref/index.rst b/doc/source/apiref/index.rst index cd9e4a3c1..1d73f35e7 100644 --- a/doc/source/apiref/index.rst +++ b/doc/source/apiref/index.rst @@ -14,11 +14,15 @@ Contents: instrument generic_scpi agilent + fluke + glassman holzworth hp keithley lakeshore + minghe newport + ondax other oxford phasematrix diff --git a/doc/source/apiref/keithley.rst b/doc/source/apiref/keithley.rst index d1f466225..8a974c2c3 100644 --- a/doc/source/apiref/keithley.rst +++ b/doc/source/apiref/keithley.rst @@ -13,6 +13,13 @@ Keithley .. autoclass:: Keithley195 :members: :undoc-members: + +:class:`Keithley485` Picoammeter +================================ + +.. autoclass:: Keithley485 + :members: + :undoc-members: :class:`Keithley580` Microohm Meter =================================== diff --git a/instruments/__init__.py b/instruments/__init__.py index 9289f052d..0d3efa551 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -13,6 +13,8 @@ from . import agilent from . import generic_scpi +from . import fluke +from . import glassman from . import holzworth from . import hp from . import keithley diff --git a/instruments/fluke/__init__.py b/instruments/fluke/__init__.py new file mode 100644 index 000000000..ac860c293 --- /dev/null +++ b/instruments/fluke/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing Fluke instruments +""" + +from __future__ import absolute_import + +from .fluke3000 import Fluke3000 diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py new file mode 100644 index 000000000..82b0cc7a4 --- /dev/null +++ b/instruments/fluke/fluke3000.py @@ -0,0 +1,495 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# fluke3000.py: Driver for the Fluke 3000 FC Industrial System +# +# © 2019 Francois Drielsma (francois.drielsma@gmail.com). +# +# This file is a part of the InstrumentKit project. +# Licensed under the AGPL version 3. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Driver for the Fluke 3000 FC Industrial System + +Originally contributed and copyright held by Francois Drielsma +(francois.drielsma@gmail.com) + +An unrestricted license has been provided to the maintainers of the Instrument +Kit project. +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division +import time +from builtins import range + +from enum import Enum + +import quantities as pq + +from instruments.abstract_instruments import Multimeter + +# CLASSES ##################################################################### + + +class Fluke3000(Multimeter): + + """The `Fluke3000` is an ecosystem of devices produced by Fluke that may be + connected simultaneously to a Fluke PC3000 wireless adapter which exposes + a serial port to the computer to send and receive commands. + + The `Fluke3000` ecosystem supports the following instruments: + - Fluke 3000 FC Series Wireless Multimeter + - Fluke v3000 FC Wireless AC Voltage Module + - Fluke v3001 FC Wireless DC Voltage Module + - Fluke t3000 FC Wireless Temperature Module + + `Fluke3000` is a USB instrument that communicates through a serial port + via the PC3000 dongle. The commands used to communicate to the dongle + do not follow the SCPI standard. + + When the device is reset, it searches for available wireless modules + and binds them to the PC3000 dongle. At each initialization, this class + checks what device has been bound and saves their module number. + + This class has been tested with the 3000 FC Wireless Multimeter and + the t3000 FC Wireless Temperature Module. They have been operated + separately and simultaneously. It does not support the Wireless AC/DC + Voltage Modules as the author did not have them on hand. + + It is important to note that the mode of the multimeter cannot be set + remotely. If must be set on the device prior to the measurement. If + the measurement read back from the multimeter is not expressed in the + expected units, this module will raise an error. + + Example usage: + + >>> import instruments as ik + >>> mult = ik.fluke.Fluke3000.open_serial("/dev/ttyUSB0", 115200) + >>> mult.measure(mult.Mode.voltage_dc) # Measures the DC voltage + array(12.345) * V + + It is crucial not to kill this program in the process of making a measurement, + as for the Fluke 3000 FC Wireless Multimeter, one has to open continuous + readout, make a read and close it. If the process is killed, the read out + may not be closed and the serial cache will be constantly filled with measurements + that will interfere with any status query. If the multimeter is stuck in + continuous trigger after a bad kill, simply do: + + >>> mult.reset() + >>> mult.flush() + >>> mult.connect() + + Follow the same procedure if you want to add/remove an instrument to/from + the readout chain as the code will not look for new instruments if some + have already been connected to the PC3000 dongle. + """ + + def __init__(self, filelike): + """ + Initialize the instrument, and set the properties needed for communication. + """ + super(Fluke3000, self).__init__(filelike) + self.timeout = 3 * pq.second + self.terminator = "\r" + self.positions = {} + self.connect() + + # ENUMS ## + + class Module(Enum): + """ + Enum containing the supported modules serial numbers. + """ + #: Multimeter + m3000 = 46333030304643 + #: Temperature module + t3000 = 54333030304643 + + class Mode(Enum): + """ + Enum containing the supported mode codes. + """ + #: AC Voltage + voltage_ac = "01" + #: DC Voltage + voltage_dc = "02" + #: AC Current + current_ac = "03" + #: DC Current + current_dc = "04" + #: Frequency + frequency = "05" + #: Temperature + temperature = "07" + #: Resistance + resistance = "0B" + #: Capacitance + capacitance = "0F" + + # PROPERTIES ## + + @property + def mode(self): + """ + Gets/sets the measurement mode for the multimeter. + + The measurement mode of the multimeter must be set on the + device manually and cannot be set remotely. If a multimeter + is bound to the PC3000, returns its measurement mode by + making a measurement and checking the units bytes in response. + + :rtype: `Fluke3000.Mode` + """ + if self.Module.m3000 not in self.positions.keys(): + raise KeyError("No `Fluke3000` FC multimeter is bound") + port_id = self.positions[self.Module.m3000] + value = self.query_lines("rfemd 0{} 1".format(port_id), 2)[-1] + self.query("rfemd 0{} 2".format(port_id)) + data = value.split("PH=")[-1] + return self.Mode(self._parse_mode(data)) + + @property + def trigger_mode(self): + """ + Gets/sets the trigger mode for the multimeter. + + The only supported mode is to trigger the device once when a + measurement is queried. This device does support continuous + triggering but it would quickly flood the serial input cache as + readouts do not overwrite each other and are accumulated. + + :rtype: `str` + """ + raise AttributeError("The `Fluke3000` only supports single trigger when queried") + + @property + def relative(self): + """ + Gets/sets the status of relative measuring mode for the multimeter. + + The `Fluke3000` FC does not support relative measurements. + + :rtype: `bool` + """ + raise AttributeError("The `Fluke3000` FC does not support relative measurements") + + @property + def input_range(self): + """ + Gets/sets the current input range setting of the multimeter. + + The `Fluke3000` FC is an autoranging only multimeter. + + :rtype: `str` + """ + return AttributeError('The `Fluke3000` FC is an autoranging only multimeter') + + # METHODS # + + def connect(self): + """ + Connect to available modules and returns a dictionary + of the modules found and their port ID. + """ + self.scan() # Look for connected devices + if not self.positions: + self.reset() # Reset the PC3000 dongle + timeout = self.timeout # Store default timeout + self.timeout = 30 * pq.second # PC 3000 can take a while to bind with wireless devices + self.query_lines("rfdis", 3) # Discover available modules and bind them + self.timeout = timeout # Restore default timeout + self.scan() # Look for connected devices + + if not self.positions: + raise ValueError("No `Fluke3000` modules available") + + def scan(self): + """ + Search for available modules and reformatturns a dictionary + of the modules found and their port ID. + """ + # Loop over possible channels, store device locations + positions = {} + for port_id in range(1, 7): + # Check if a device is connected to port port_id + output = self.query("rfebd 0{} 0".format(port_id)) + if "RFEBD" not in output: + continue + + # If it is, identify the device + self.read() + output = self.query_lines("rfgus 0{}".format(port_id), 2)[-1] + module_id = int(output.split("PH=")[-1]) + if module_id == self.Module.m3000.value: + positions[self.Module.m3000] = port_id + elif module_id == self.Module.t3000.value: + positions[self.Module.t3000] = port_id + else: + raise NotImplementedError("Module ID {} not implemented".format(module_id)) + + self.positions = positions + + def reset(self): + """ + Resets the device and unbinds all modules. + """ + self.query_lines("ri", 3) # Resets the device + self.query_lines("rfsm 1", 2) # Turns comms on + + def read_lines(self, nlines=1): + """ + Function that keeps reading until reaches a termination + character a set amount of times. This is implemented + to handle the mutiline output of the PC3000. + + :param nlines: Number of termination characters to reach + + :type nlines: 'int' + + :return: Array of lines read out + :rtype: Array of `str` + + """ + return [self.read() for _ in range(nlines)] + + def query_lines(self, cmd, nlines=1): + """ + Function used to send a query to the instrument while allowing + for the multiline output of the PC3000. + + :param cmd: Command that will be sent to the instrument + :param nlines: Number of termination characters to reach + + :type cmd: 'str' + :type nlines: 'int' + + :return: The multiline result from the query + :rtype: Array of `str` + + """ + self.sendcmd(cmd) + return self.read_lines(nlines) + + def flush(self): + """ + Flushes the serial input cache. + + This device outputs a terminator after each output line. + The serial input cache is flushed by repeatedly reading + until a terminator is not found. + """ + timeout = self.timeout + self.timeout = 0.1 * pq.second + init_time = time.time() + while time.time() - init_time < 1.: + try: + self.read() + except OSError: + break + self.timeout = timeout + + def measure(self, mode): + """Instruct the Fluke3000 to perform a one time measurement. + + :param mode: Desired measurement mode. + + :type mode: `Fluke3000.Mode` + + :return: A measurement from the multimeter. + :rtype: `~quantities.quantity.Quantity` + + """ + # Check that the mode is supported + if not isinstance(mode, self.Mode): + raise ValueError("Mode {} is not supported".format(mode)) + + # Check that the module associated with this mode is available + module = self._get_module(mode) + if module not in self.positions.keys(): + raise ValueError("Device necessary to measure {} is not available".format(mode)) + + # Query the module + value = '' + port_id = self.positions[module] + init_time = time.time() + while time.time() - init_time < 3.: + # Read out + if mode == self.Mode.temperature: + # The temperature module supports single readout + value = self.query_lines("rfemd 0{} 0".format(port_id), 2)[-1] + else: + # The multimeter does not support single readout, + # have to open continuous readout, read, then close it + value = self.query_lines("rfemd 0{} 1".format(port_id), 2)[-1] + self.query("rfemd 0{} 2".format(port_id)) + + # Check that value is consistent with the request, break + if "PH" in value: + data = value.split("PH=")[-1] + if self._parse_mode(data) != mode.value: + if self.Module.m3000 in self.positions.keys(): + self.query("rfemd 0{} 2".format(self.positions[self.Module.m3000])) + self.flush() + else: + break + + # Parse the output + value = self._parse(value, mode) + + # Return with the appropriate units + units = UNITS[mode] + return value * units + + def _get_module(self, mode): + """Gets the module associated with this measurement mode. + + :param mode: Desired measurement mode. + + :type mode: `Fluke3000.Mode` + + :return: A Fluke3000 module. + :rtype: `Fluke3000.Module` + + """ + if mode == self.Mode.temperature: + return self.Module.t3000 + + return self.Module.m3000 + + def _parse(self, result, mode): + """Parses the module output. + + :param result: Output of the query. + :param mode: Desired measurement mode. + + :type result: `string` + :type mode: `Fluke3000.Mode` + + :return: A measurement from the multimeter. + :rtype: `Quantity` + + """ + # Check that a value is contained + if "PH" not in result: + raise ValueError("Cannot parse a string that does not contain a return value") + + # Isolate the data string from the output + data = result.split('PH=')[-1] + + # Check that the multimeter is in the right mode (fifth byte) + if self._parse_mode(data) != mode.value: + error = ("Mode {} was requested but the Fluke 3000FC Multimeter " + "is in mode {} instead. Could not read the requested " + "quantity.").format(mode.name, self.Mode(data[8:10]).name) + raise ValueError(error) + + # Extract the value from the first two bytes + value = self._parse_value(data) + + # Extract the prefactor from the fourth byte + scale = self._parse_factor(data) + + # Combine and return + return scale*value + + @staticmethod + def _parse_mode(data): + """Parses the measurement mode. + + :param data: Measurement output. + + :type data: `str` + + :return: A Mode string. + :rtype: `str` + + """ + # The fixth dual hex byte encodes the measurement mode + return data[8:10] + + @staticmethod + def _parse_value(data): + """Parses the measurement value. + + :param data: Measurement output. + + :type data: `str` + + :return: A value. + :rtype: `float` + + """ + # The second dual hex byte is the most significant byte + return int(data[2:4]+data[:2], 16) + + @staticmethod + def _parse_factor(data): + """Parses the measurement prefactor. + + :param data: Measurement output. + + :type data: `str` + + :return: A prefactor. + :rtype: `float` + + """ + # Convert the fourth dual hex byte to an 8 bits string + byte = format(int(data[6:8], 16), '08b') + + # The first bit encodes the sign (0 positive, 1 negative) + sign = 1 if byte[0] == '0' else -1 + + # The second to fourth bits encode the metric prefix + code = int(byte[1:4], 2) + if code not in PREFIXES.keys(): + raise ValueError("Metric prefix not recognized: {}".format(code)) + prefix = PREFIXES[code] + + # The sixth and seventh bit encode the decimal place + scale = 10**(-int(byte[5:7], 2)) + + # Return the combination + return sign*prefix*scale + + +# UNITS ####################################################################### + +UNITS = { + None: 1, + Fluke3000.Mode.voltage_ac: pq.volt, + Fluke3000.Mode.voltage_dc: pq.volt, + Fluke3000.Mode.current_ac: pq.amp, + Fluke3000.Mode.current_dc: pq.amp, + Fluke3000.Mode.frequency: pq.hertz, + Fluke3000.Mode.temperature: pq.celsius, + Fluke3000.Mode.resistance: pq.ohm, + Fluke3000.Mode.capacitance: pq.farad +} + +# METRIC PREFIXES ############################################################# + +PREFIXES = { + 0: 1e0, # None + 2: 1e6, # Mega + 3: 1e3, # Kilo + 4: 1e-3, # milli + 5: 1e-6, # micro + 6: 1e-9 # nano +} diff --git a/instruments/glassman/__init__.py b/instruments/glassman/__init__.py new file mode 100644 index 000000000..18daacfef --- /dev/null +++ b/instruments/glassman/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing Glassman power supplies +""" + +from __future__ import absolute_import + +from .glassmanfr import GlassmanFR diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py new file mode 100644 index 000000000..3069e93ec --- /dev/null +++ b/instruments/glassman/glassmanfr.py @@ -0,0 +1,489 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# hpe3631a.py: Driver for the Glassman FR Series Power Supplies +# +# © 2019 Francois Drielsma (francois.drielsma@gmail.com). +# +# This file is a part of the InstrumentKit project. +# Licensed under the AGPL version 3. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Driver for the Glassman FR Series Power Supplies + +Originally contributed and copyright held by Francois Drielsma +(francois.drielsma@gmail.com) + +An unrestricted license has been provided to the maintainers of the Instrument +Kit project. +""" +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division +from builtins import bytes, round +from struct import unpack + +from enum import Enum + +import quantities as pq + +from instruments.abstract_instruments import ( + PowerSupply, + PowerSupplyChannel +) +from instruments.util_fns import assume_units + +# CLASSES ##################################################################### + + +class GlassmanFR(PowerSupply, PowerSupplyChannel): + + """ + The GlassmanFR is a single output power supply. + + Because it is a single channel output, this object inherits from both + PowerSupply and PowerSupplyChannel. + + This class should work for any of the Glassman FR Series power supplies + and is also likely to work for the EJ, ET, EY and FJ Series which seem + to share their communication protocols. The code has only been tested + by the author with an Glassman FR50R6 power supply. + + Before this power supply can be remotely operated, remote communication + must be enabled and the HV must be on. Please refer to the manual. + + Example usage: + + >>> import instruments as ik + >>> psu = ik.glassman.GlassmanFR.open_serial('/dev/ttyUSB0', 9600) + >>> psu.voltage = 100 # Sets output voltage to 100V. + >>> psu.voltage + array(100.0) * V + >>> psu.output = True # Turns on the power supply + >>> psu.voltage_sense < 200 * pq.volt + True + + This code uses default values of `voltage_max`, `current_max` and + `polarity` that are only valid of the FR50R6 in its positive setting. + If your power supply differs, reset those values by calling: + + >>> import quantities as pq + >>> psu.voltage_max = 40.0 * pq.kilovolt + >>> psu.current_max = 7.5 * pq.milliamp + >>> psu.polarity = -1 + """ + + def __init__(self, filelike): + """ + Initialize the instrument, and set the properties needed for communication. + """ + super(GlassmanFR, self).__init__(filelike) + self.terminator = "\r" + self.voltage_max = 50.0 * pq.kilovolt + self.current_max = 6.0 * pq.milliamp + self.polarity = +1 + self._device_timeout = True + self._voltage = 0. * pq.volt + self._current = 0. * pq.amp + + # ENUMS ## + + class Mode(Enum): + """ + Enum containing the possible modes of operations of the instrument + """ + #: Constant voltage mode + voltage = "0" + #: Constant current mode + current = "1" + + class ResponseCode(Enum): + """ + Enum containing the possible reponse codes returned by the instrument. + """ + #: A set command expects an acknoledge response (`A`) + S = "A" + #: A query command expects a response packet (`R`) + Q = "R" + #: A version query expects a different response packet (`B`) + V = "B" + #: A configure command expects an acknoledge response (`A`) + C = "A" + + class ErrorCode(Enum): + """ + Enum containing the possible error codes returned by the instrument. + """ + #: Undefined command received (not S, Q, V or C) + undefined_command = "1" + #: The checksum calculated by the instrument does not correspond to the one received + checksum_error = "2" + #: The command was longer than expected + extra_bytes = "3" + #: The digital control byte set was not one of HV On, HV Off or Power supply Reset + illegal_control = "4" + #: A send command was sent without a reset byte while the power supply is faulted + illegal_while_fault = "5" + #: Command valid, error while executing it + processing_error = "6" + + # PROPERTIES ## + + @property + def channel(self): + """ + Return the channel (which in this case is the entire instrument, since + there is only 1 channel on the GlassmanFR.) + + :rtype: 'tuple' of length 1 containing a reference back to the parent + GlassmanFR object. + """ + return [self] + + @property + def voltage(self): + """ + Gets/sets the output voltage setting. + + :units: As specified, or assumed to be :math:`\\text{V}` otherwise. + :type: `float` or `~quantities.Quantity` + """ + return self.polarity*self._voltage + + @voltage.setter + def voltage(self, newval): + self.set_status(voltage=assume_units(newval, pq.volt)) + + @property + def current(self): + """ + Gets/sets the output current setting. + + :units: As specified, or assumed to be :math:`\\text{A}` otherwise. + :type: `float` or `~quantities.Quantity` + """ + return self.polarity*self._current + + @current.setter + def current(self, newval): + self.set_status(current=assume_units(newval, pq.amp)) + + @property + def voltage_sense(self): + """ + Gets the output voltage as measured by the sense wires. + + :units: As specified, or assumed to be :math:`\\text{V}` otherwise. + :type: `~quantities.Quantity` + """ + return self.get_status()["voltage"] + + @property + def current_sense(self): + """ + Gets/sets the output current as measured by the sense wires. + + :units: As specified, or assumed to be :math:`\\text{A}` otherwise. + :type: `~quantities.Quantity` + """ + return self.get_status()["current"] + + @property + def mode(self): + """ + Gets/sets the mode for the specified channel. + + The constant-voltage/constant-current modes of the power supply + are selected automatically depending on the load (resistance) + connected to the power supply. If the load greater than the set + V/I is connected, a voltage V is applied and the current flowing + is lower than I. If the load is smaller than V/I, the set current + I acts as a current limiter and the voltage is lower than V. + + :type: `GlassmanFR.Mode` + """ + return self.get_status()["mode"] + + @property + def output(self): + """ + Gets/sets the output status. + + This is a toggle setting. True will turn on the instrument output + while False will turn it off. + + :type: `bool` + """ + return self.get_status()["output"] + + @output.setter + def output(self, newval): + if not isinstance(newval, bool): + raise TypeError("Ouput status mode must be a boolean.") + self.set_status(output=newval) + + @property + def fault(self): + """ + Gets/sets the output status. + + Returns True if the instrument has a fault. + + :type: `bool` + """ + return self.get_status()["fault"] + + @property + def version(self): + """ + The software revision level of the power supply's + data intereface via the `V` command + + :rtype: `str` + """ + return self.query("V") + + @property + def device_timeout(self): + """ + Gets/sets the timeout instrument side. + + This is a toggle setting. ON will set the timeout to 1.5 + seconds while OFF will disable it. + + :type: `bool` + """ + return self._device_timeout + + @device_timeout.setter + def device_timeout(self, newval): + if not isinstance(newval, bool): + raise TypeError("Device timeout mode must be a boolean.") + self._device_timeout = newval + self.query("C{}".format(int(not newval))) # Device acknowledges + + # METHODS ## + + def sendcmd(self, cmd): + """ + Overrides the default `setcmd` by padding the front of each + command sent to the instrument with an SOH character and the + back of it with a checksum. + + :param str cmd: The command message to send to the instrument + """ + checksum = self._get_checksum(cmd) + self._file.sendcmd("\x01" + cmd + checksum) # Add SOH and checksum + + def query(self, cmd, size=-1): + """ + Overrides the default `query` by padding the front of each + command sent to the instrument with an SOH character and the + back of it with a checksum. + + This implementation also automatically check that the checksum + returned by the instrument is consistent with the message. If + the message returned is an error, it parses it and raises. + + :param str cmd: The query message to send to the instrument + :param int size: The number of bytes to read back from the instrument + response. + :return: The instrument response to the query + :rtype: `str` + """ + self.sendcmd(cmd) + result = self._file.read(size) + if result[0] != getattr(self.ResponseCode, cmd[0]).value and result[0] != "E": + raise ValueError("Invalid response code: {}".format(result)) + if result[0] == "A": + return "Acknowledged" + if not self._verify_checksum(result): + raise ValueError("Invalid checksum: {}".format(result)) + if result[0] == "E": + error_name = self.ErrorCode(result[1]).name + raise ValueError("Instrument responded with error: {}".format(error_name)) + + return result[1:-2] # Remove SOH and checksum + + def reset(self): + """ + Reset device to default status (HV Off, V=0.0, A=0.0) + """ + self.set_status(reset=True) + + def set_status(self, voltage=None, current=None, output=None, reset=False): + """ + Sets the requested variables on the instrument. + + This instrument can only set all of its variables simultaneously, + if some of them are omitted in this function, they will simply be + kept as what they were set to previously. + """ + if reset: + self._voltage = 0. * pq.volt + self._current = 0. * pq.amp + cmd = format(4, "013d") + else: + # The maximum value is encoded as the maximum of three hex characters (4095) + cmd = '' + value_max = int(0xfff) + + # If the voltage is not specified, keep it as is + voltage = assume_units(voltage, pq.volt) if voltage is not None else self.voltage + ratio = float(voltage.rescale(pq.volt)/self.voltage_max.rescale(pq.volt)) + voltage_int = int(round(value_max*ratio)) + self._voltage = self.voltage_max*float(voltage_int)/value_max + assert 0. * pq.volt <= self._voltage <= self.voltage_max + cmd += format(voltage_int, "03X") + + # If the current is not specified, keep it as is + current = assume_units(current, pq.amp) if current is not None else self.current + ratio = float(current.rescale(pq.amp)/self.current_max.rescale(pq.amp)) + current_int = int(round(value_max*ratio)) + self._current = self.current_max*float(current_int)/value_max + assert 0. * pq.amp <= self._current <= self.current_max + cmd += format(current_int, "03X") + + # If the output status is not specified, keep it as is + output = output if output is not None else self.output + control = "00{}{}".format(int(output), int(not output)) + cmd += format(int(control, 2), "07X") + + self.query("S" + cmd) # Device acknowledges + + def get_status(self): + """ + Gets and parses the response packet. + + Returns a `dict` with the following keys: + ``{voltage,current,mode,fault,output}`` + + :rtype: `dict` + """ + return self._parse_response(self.query("Q")) + + def _parse_response(self, response): + """ + Parse the response packet returned by the power supply. + + Returns a `dict` with the following keys: + ``{voltage,current,mode,fault,output}`` + + :param response: Byte string to be unpacked and parsed + :type: `str` + + :rtype: `dict` + """ + (voltage, current, monitors) = \ + unpack("@3s3s3x1c2x", bytes(response, "utf-8")) + + try: + voltage = self._parse_voltage(voltage) + current = self._parse_current(current) + mode, fault, output = self._parse_monitors(monitors) + except: + raise RuntimeError("Cannot parse response " + "packet: {}".format(response)) + + return {"voltage": voltage, + "current": current, + "mode": mode, + "fault": fault, + "output": output} + + def _parse_voltage(self, word): + """ + Converts the three-bytes voltage word returned in the + response packet to a single voltage quantity. + + :param word: Byte string to be parsed + :type: `bytes` + + :rtype: `~quantities.quantity.Quantity` + """ + value = int(word.decode('utf-8'), 16) + value_max = int(0x3ff) + return self.polarity*self.voltage_max*float(value)/value_max + + def _parse_current(self, word): + """ + Converts the three-bytes current word returned in the + response packet to a single current quantity. + + :param word: Byte string to be parsed + :type: `bytes` + + :rtype: `~quantities.quantity.Quantity` + """ + value = int(word.decode("utf-8"), 16) + value_max = int(0x3ff) + return self.polarity*self.current_max*float(value)/value_max + + def _parse_monitors(self, word): + """ + Converts the monitors byte returned in the response packet + to a mode, a fault boolean and an output boolean. + + :param word: Byte to be parsed + :type: `byte` + + :rtype: `str, bool, bool` + """ + bits = format(int(word, 16), "04b") + mode = self.Mode(bits[-1]) + fault = bits[-2] == "1" + output = bits[-3] == "1" + return mode, fault, output + + def _verify_checksum(self, word): + """ + Calculates the modulo 256 checksum of a string of characters + and compares it to the one returned by the instrument. + + Returns True if they agree, False otherwise. + + :param word: Byte string to be checked + :type: `str` + + :rtype: `bool` + """ + data = word[1:-2] + inst_checksum = word[-2:] + calc_checksum = self._get_checksum(data) + return inst_checksum == calc_checksum + + @staticmethod + def _get_checksum(data): + """ + Calculates the modulo 256 checksum of a string of characters. + This checksum, expressed in hexadecimal, is used in every + communication of this instrument, as a sanity check. + + Returns a string corresponding to the hexademical value + of the checksum, without the `0x` prefix. + + :param data: Byte string to be checksummed + :type: `str` + + :rtype: `str` + """ + chrs = list(data) + total = 0 + for c in chrs: + total += ord(c) + + return format(total % 256, "02X") diff --git a/instruments/hp/__init__.py b/instruments/hp/__init__.py index f8864cea6..0f7893fe2 100644 --- a/instruments/hp/__init__.py +++ b/instruments/hp/__init__.py @@ -10,3 +10,4 @@ from .hp6624a import HP6624a from .hp6632b import HP6632b from .hp6652a import HP6652a +from .hpe3631a import HPe3631a diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py new file mode 100644 index 000000000..295f99a6d --- /dev/null +++ b/instruments/hp/hpe3631a.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# hpe3631a.py: Driver for the HP E3631A Power Supply +# +# © 2019 Francois Drielsma (francois.drielsma@gmail.com). +# +# This file is a part of the InstrumentKit project. +# Licensed under the AGPL version 3. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Driver for the HP E3631A Power Supply + +Originally contributed and copyright held by Francois Drielsma +(francois.drielsma@gmail.com) + +An unrestricted license has been provided to the maintainers of the Instrument +Kit project. +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division +import time + +import quantities as pq + +from instruments.abstract_instruments import ( + PowerSupply, + PowerSupplyChannel +) +from instruments.generic_scpi import SCPIInstrument +from instruments.util_fns import ( + int_property, + unitful_property, + bounded_unitful_property, + bool_property, + split_unit_str, + assume_units +) + +# CLASSES ##################################################################### + + +class HPe3631a(PowerSupply, PowerSupplyChannel, SCPIInstrument): + + """ + The HPe3631a is a three channels voltage/current supply. + - Channel 1 is a positive +6V/5A channel (P6V) + - Channel 2 is a positive +25V/1A channel (P25V) + - Channel 3 is a negative -25V/1A channel (N25V) + + This module is designed for the power supply to be set to + a specific channel and remain set afterwards as this device + does not offer commands to set or read multiple channels + without calling the channel set command each time (0.5s). It is + possible to call a specific channel through psu.channel[idx], + which will automatically reset the channel id, when necessary. + + This module is likely to work as is for the Agilent E3631 and + Keysight E3631 which seem to be rebranded but identical devices. + + Example usage: + + >>> import instruments as ik + >>> psu = ik.hp.HPe3631a.open_gpibusb("/dev/ttyUSB0", 10) + >>> psu.channelid = 2 # Sets channel to P25V + >>> psu.voltage = 12.5 # Sets voltage to 12.5V + >>> psu.voltage # Reads back set voltage + array(12.5) * V + >>> psu.voltage_sense # Reads back sensed voltage + array(12.501) * V + """ + + def __init__(self, filelike): + super(HPe3631a, self).__init__(filelike) + self.sendcmd("SYST:REM") # Puts the device in remote operation + time.sleep(0.1) + + # INNER CLASSES # + + class Channel(object): + """ + Class representing a power output channel on the HPe3631a. + + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `HPe3631a` class. + """ + + def __init__(self, parent, valid_set): + self._parent = parent + self._valid_set = valid_set + + def __getitem__(self, idx): + # Check that the channel is available. If it is, set the + # channelid of the device and return the device object. + if self._parent.channelid != idx: + self._parent.channelid = idx + time.sleep(0.5) + return self._parent + + def __len__(self): + return len(self._valid_set) + + # PROPERTIES ## + + @property + def channel(self): + """ + Gets a specific channel object. The desired channel is specified like + one would access a list. + + :rtype: `HPe3631a.Channel` + + .. seealso:: + `HPe3631a` for example using this property. + """ + return self.Channel(self, [1, 2, 3]) + + @property + def mode(self): + """ + Gets/sets the mode for the specified channel. + + The constant-voltage/constant-current modes of the power supply + are selected automatically depending on the load (resistance) + connected to the power supply. If the load greater than the set + V/I is connected, a voltage V is applied and the current flowing + is lower than I. If the load is smaller than V/I, the set current + I acts as a current limiter and the voltage is lower than V. + """ + return AttributeError("The `HPe3631a` sets its mode automatically") + + channelid = int_property( + "INST:NSEL", + valid_set=[1, 2, 3], + doc=""" + Gets/Sets the active channel ID. + + :type: `HPe3631a.ChannelType` + """ + ) + + @property + def voltage(self): + """ + Gets/sets the output voltage of the source. + + :units: As specified, or assumed to be :math:`\\text{V}` otherwise. + :type: `float` or `~quantities.Quantity` + """ + raw = self.query("SOUR:VOLT?") + return pq.Quantity(*split_unit_str(raw, pq.volt)).rescale(pq.volt) + + @voltage.setter + def voltage(self, newval): + """ + Gets/sets the output voltage of the source. + + :units: As specified, or assumed to be :math:`\\text{V}` otherwise. + :type: `float` or `~quantities.Quantity` + """ + min_value, max_value = self.voltage_range + if newval < min_value: + raise ValueError("Voltage quantity is too low. Got {}, minimum " + "value is {}".format(newval, min_value)) + + if newval > max_value: + raise ValueError("Voltage quantity is too high. Got {}, maximum " + "value is {}".format(newval, max_value)) + + # Rescale to the correct unit before printing. This will also + # catch bad units. + strval = "{:e}".format(assume_units(newval, pq.volt).rescale(pq.volt).item()) + self.sendcmd('SOUR:VOLT {}'.format(strval)) + + @property + def voltage_min(self): + """ + Gets the minimum voltage for the current channel. + + :units: :math:`\\text{V}`. + :type: `~quantities.Quantity` + """ + return self.voltage_range[0] + + @property + def voltage_max(self): + """ + Gets the maximum voltage for the current channel. + + :units: :math:`\\text{V}`. + :type: `~quantities.Quantity` + """ + return self.voltage_range[1] + + @property + def voltage_range(self): + """ + Gets the voltage range for the current channel. + + The MAX function SCPI command is designed in such a way + on this device that it always returns the largest absolute value. + There is no need to query MIN, as it is always 0., but one has to + order the values as MAX can be negative. + + :units: :math:`\\text{V}`. + :type: array of `~quantities.Quantity` + """ + value = pq.Quantity(*split_unit_str(self.query("SOUR:VOLT? MAX"), pq.volt)) + if value < 0.: + return value, 0. + return 0., value + + current, current_min, current_max = bounded_unitful_property( + "SOUR:CURR", + pq.amp, + min_fmt_str="{}? MIN", + max_fmt_str="{}? MAX", + doc=""" + Gets/sets the output current of the source. + + :units: As specified, or assumed to be :math:`\\text{A}` otherwise. + :type: `float` or `~quantities.Quantity` + """ + ) + + voltage_sense = unitful_property( + "MEAS:VOLT", + pq.volt, + readonly=True, + doc=""" + Gets the actual output voltage as measured by the sense wires. + + :units: As specified, or assumed to be :math:`\\text{V}` otherwise. + :type: `~quantities.Quantity` + """ + ) + + current_sense = unitful_property( + "MEAS:CURR", + pq.amp, + readonly=True, + doc=""" + Gets the actual output current as measured by the sense wires. + + :units: As specified, or assumed to be :math:`\\text{A}` otherwise. + :type: `~quantities.Quantity` + """ + ) + + output = bool_property( + "OUTP", + inst_true="1", + inst_false="0", + doc=""" + Gets/sets the outputting status of the specified channel. + + This is a toggle setting. ON will turn on the channel output + while OFF will turn it off. + + :type: `bool` + """ + ) diff --git a/instruments/keithley/__init__.py b/instruments/keithley/__init__.py index 0c8c34e4f..36b8eaf48 100644 --- a/instruments/keithley/__init__.py +++ b/instruments/keithley/__init__.py @@ -7,6 +7,7 @@ from __future__ import absolute_import from .keithley195 import Keithley195 +from .keithley485 import Keithley485 from .keithley580 import Keithley580 from .keithley2182 import Keithley2182 from .keithley6220 import Keithley6220 diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py new file mode 100644 index 000000000..569e9fc98 --- /dev/null +++ b/instruments/keithley/keithley485.py @@ -0,0 +1,429 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# keithley485.py: Driver for the Keithley 485 picoammeter. +# +# © 2019 Francois Drielsma (francois.drielsma@gmail.com). +# +# This file is a part of the InstrumentKit project. +# Licensed under the AGPL version 3. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Driver for the Keithley 485 picoammeter. + +Originally contributed and copyright held by Francois Drielsma +(francois.drielsma@gmail.com). + +An unrestricted license has been provided to the maintainers of the Instrument +Kit project. +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division +from builtins import bytes +from struct import unpack + +from enum import Enum + +import quantities as pq + +from instruments.abstract_instruments import Instrument + +# CLASSES ##################################################################### + + +class Keithley485(Instrument): + + """ + The Keithley Model 485 is a 4 1/2 digit resolution autoranging + picoammeter with a +- 20000 count LCD. It is designed for low + current measurement requirements from 0.1pA to 2mA. + + The device needs some processing time (manual reports 300-500ms) after a + command has been transmitted. + + Example usage: + + >>> import instruments as ik + >>> inst = ik.keithley.Keithley485.open_gpibusb("/dev/ttyUSB0", 22) + >>> inst.measure() # Measures the current + array(-1.278e-10) * A + """ + + # ENUMS # + + class TriggerMode(Enum): + """ + Enum containing valid trigger modes for the Keithley 485 + """ + #: Continuously measures current, returns on talk + continuous_ontalk = 0 + #: Measures current once and returns on talk + oneshot_ontalk = 1 + #: Continuously measures current, returns on `GET` + continuous_onget = 2 + #: Measures current once and returns on `GET` + oneshot_onget = 3 + #: Continuously measures current, returns on `X` + continuous_onx = 4 + #: Measures current once and returns on `X` + oneshot_onx = 5 + + class SRQDataMask(Enum): + """ + Enum containing valid SRQ data masks for the Keithley 485 + """ + #: Service request (SRQ) disabled + srq_disabled = 0 + #: Read overflow + read_ovf = 1 + #: Read done + read_done = 8 + #: Read done or read overflow + read_done_ovf = 9 + #: Device busy + busy = 16 + #: Device busy or read overflow + busy_read_ovf = 17 + #: Device busy or read overflow + busy_read_done = 24 + #: Device busy, read done or read overflow + busy_read_done_ovf = 25 + + class SRQErrorMask(Enum): + """ + Enum containing valid SRQ error masks for the Keithley 485 + """ + #: Service request (SRQ) disabled + srq_disabled = 0 + #: Illegal Device-Dependent Command Option (IDDCO) + idcco = 1 + #: Illegal Device-Dependent Command (IDDC) + idcc = 2 + #: IDDCO or IDDC + idcco_idcc = 3 + #: Device not in remote + not_remote = 4 + #: Device not in remote or IDDCO + not_remote_idcco = 5 + #: Device not in remote or IDDC + not_remote_idcc = 6 + #: Device not in remote, IDDCO or IDDC + not_remote_idcco_idcc = 7 + + class Status(Enum): + """ + Enum containing valid status keys in the measurement string + """ + #: Measurement normal + normal = b"N" + #: Measurement zero-check + zerocheck = b"C" + #: Measurement overflow + overflow = b"O" + #: Measurement relative + relative = b"Z" + + # PROPERTIES # + + @property + def zero_check(self): + """ + Gets/sets the 'zero check' mode (C) of the Keithley 485. + + Once zero check is enabled (C1 sent), the display can be + zeroed with the REL feature or the front panel pot. + + See the Keithley 485 manual for more information. + + :type: `bool` + """ + return self.get_status()["zerocheck"] + + @zero_check.setter + def zero_check(self, newval): + if not isinstance(newval, bool): + raise TypeError("Zero Check mode must be a boolean.") + self.sendcmd("C{}X".format(int(newval))) + + @property + def log(self): + """ + Gets/sets the 'log' mode (D) of the Keithley 485. + + Once log is enabled (D1 sent), the device will return + the logarithm of the current readings. + + See the Keithley 485 manual for more information. + + :type: `bool` + """ + return self.get_status()["log"] + + @log.setter + def log(self, newval): + if not isinstance(newval, bool): + raise TypeError("Log mode must be a boolean.") + self.sendcmd("D{}X".format(int(newval))) + + @property + def input_range(self): + """ + Gets/sets the range (R) of the Keithley 485 input terminals. The valid + ranges are one of ``{auto|2e-9|2e-8|2e-7|2e-6|2e-5|2e-4|2e-3}`` + + :type: `~quantities.quantity.Quantity` or `str` + """ + value = self.get_status()["range"] + if isinstance(value, str): + return value + return value * pq.amp + + @input_range.setter + def input_range(self, newval): + valid = ("auto", 2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3) + if isinstance(newval, str): + newval = newval.lower() + if newval == "auto": + self.sendcmd("R0X") + return + else: + raise ValueError("Only `auto` is acceptable when specifying " + "the range as a string.") + if isinstance(newval, pq.quantity.Quantity): + newval = float(newval) + + if isinstance(newval, (float, int)): + if newval in valid: + newval = valid.index(newval) + else: + raise ValueError("Valid range settings are: {}".format(valid)) + else: + raise TypeError("Range setting must be specified as a float, int, " + "or the string `auto`, got {}".format(type(newval))) + self.sendcmd("R{}X".format(newval)) + + @property + def relative(self): + """ + Gets/sets the relative measurement mode (Z) of the Keithley 485. + + As stated in the manual: The relative function is used to establish a + baseline reading. This reading is subtracted from all subsequent + readings. The purpose of making relative measurements is to cancel test + lead and offset currents or to store an input as a reference level. + + Once a relative level is established, it remains in effect until another + relative level is set. The relative value is only good for the range the + value was taken on and higher ranges. If a lower range is selected than + that on which the relative was taken, inaccurate results may occur. + Relative cannot be activated when "OL" is displayed. + + See the manual for more information. + + :type: `bool` + """ + return self.get_status()["relative"] + + @relative.setter + def relative(self, newval): + if not isinstance(newval, bool): + raise TypeError("Relative mode must be a boolean.") + self.sendcmd("Z{}X".format(int(newval))) + + @property + def eoi_mode(self): + """ + Gets/sets the 'eoi' mode (K) of the Keithley 485. + + The model 485 will normally send an end of interrupt (EOI) + during the last byte of its data string or status word. + The EOI reponse of the instrument may be included or omitted. + Warning: the default setting (K0) includes it. + + See the Keithley 485 manual for more information. + + :type: `bool` + """ + return self.get_status()["eoi_mode"] + + @eoi_mode.setter + def eoi_mode(self, newval): + if not isinstance(newval, bool): + raise TypeError("EOI mode must be a boolean.") + self.sendcmd("K{}X".format(1 - int(newval))) + + @property + def trigger_mode(self): + """ + Gets/sets the trigger mode (T) of the Keithley 485. + + There are two different trigger settings for three different sources. + This means there are six different settings for the trigger mode. + + The two types are continuous and one-shot. Continuous has the instrument + continuously sample the current. One-shot performs a single + current measurement when requested to do so. + + The three trigger sources are on talk, on GET, and on "X". On talk + refers to addressing the instrument to talk over GPIB. On GET is when + the instrument receives the GPIB command byte for "group execute + trigger". Last, on "X" is when one sends the ASCII character "X" to the + instrument. + + It is recommended to leave it in the default mode (T0, continuous on talk), + and simply ignore the output when other commands are called. + + :type: `Keithley485.TriggerMode` + """ + return self.get_status()["trigger"] + + @trigger_mode.setter + def trigger_mode(self, newval): + if isinstance(newval, str): + newval = Keithley485.TriggerMode[newval] + if not isinstance(newval, Keithley485.TriggerMode): + raise TypeError("Drive must be specified as a " + "Keithley485.TriggerMode, got {} " + "instead.".format(newval)) + self.sendcmd("T{}X".format(newval.value)) + + # METHODS # + + def auto_range(self): + """ + Turn on auto range for the Keithley 485. + + This is the same as calling the `Keithley485.set_current_range` + method and setting the parameter to "AUTO". + """ + self.sendcmd("R0X") + + def get_status(self): + """ + Gets and parses the status word. + + Returns a `dict` with the following keys: + ``{zerocheck,log,range,relative,eoi,relative, + trigger,datamask,errormask,terminator}`` + + :rtype: `dict` + """ + return self._parse_status_word(self._get_status_word()) + + def _get_status_word(self): + """ + The device will not always respond with the statusword when asked. We + use a simple heuristic here: request it up to 5 times. + + :rtype: `str` + """ + tries = 5 + statusword = "" + while statusword[:3] != "485" and tries != 0: + statusword = self.query("U0X") + tries -= 1 + + if statusword is None: + raise IOError("Could not retrieve status word") + + return statusword[:-1] + + def _parse_status_word(self, statusword): + """ + Parse the status word returned by the function + `~Keithley485.get_status_word`. + + Returns a `dict` with the following keys: + ``{zerocheck,log,range,relative,eoi,relative, + trigger,datamask,errormask,terminator}`` + + :param statusword: Byte string to be unpacked and parsed + :type: `str` + + :rtype: `dict` + """ + if statusword[:3] != "485": + raise ValueError("Status word starts with wrong " + "prefix: {}".format(statusword)) + + (zerocheck, log, device_range, relative, eoi_mode, + trigger, datamask, errormask) = \ + unpack("@6c2s2s", bytes(statusword[3:], "utf-8")) + + valid_range = {b"0": "auto", + b"1": 2e-9, + b"2": 2e-8, + b"3": 2e-7, + b"4": 2e-6, + b"5": 2e-5, + b"6": 2e-4, + b"7": 2e-3} + + try: + device_range = valid_range[device_range] + trigger = self.TriggerMode(int(trigger)).name + datamask = self.SRQDataMask(int(datamask)).name + errormask = self.SRQErrorMask(int(errormask)).name + except: + raise RuntimeError("Cannot parse status " + "word: {}".format(statusword)) + + return {"zerocheck": zerocheck == b"1", + "log": log == b"1", + "range": device_range, + "relative": relative == b"1", + "eoi_mode": eoi_mode == b"0", + "trigger": trigger, + "datamask": datamask, + "errormask": errormask, + "terminator": self.terminator} + + def measure(self): + """ + Perform a current measurement with the Keithley 485. + + :rtype: `~quantities.quantity.Quantity` + """ + return self._parse_measurement(self.query("X")) + + def _parse_measurement(self, measurement): + """ + Parse the measurement string returned by the instrument. + + Returns the current formatted as a Quantity. + + :param measurement: String to be unpacked and parsed + :type: `str` + + :rtype: `~quantities.quantity.Quantity` + """ + (status, function, base, current) = \ + unpack("@1c2s1c10s", bytes(measurement, "utf-8")) + + try: + status = self.Status(status) + if status != self.Status.normal: + raise ValueError("Instrument not in normal mode: {}".format(status.name)) + if function != b"DC": + raise ValueError("Instrument not returning DC function: {}".format(function)) + current = float(current) * pq.amp if base == b"A" else 10 ** (float(current)) * pq.amp + except: + raise Exception("Cannot parse measurement: {}".format(measurement)) + + return current diff --git a/instruments/tests/test_fluke/__init__.py b/instruments/tests/test_fluke/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py new file mode 100644 index 000000000..94d9eacfa --- /dev/null +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Fluke 3000 FC multimeter +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import + +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + +# Empty initialization sequence (scan function) that does not uncover +# any available Fluke 3000 FC device. +none_sequence = [ + "rfebd 01 0", + "rfebd 02 0", + "rfebd 03 0", + "rfebd 04 0", + "rfebd 05 0", + "rfebd 06 0" +] +none_response = [ + "CR:Ack=2", + "CR:Ack=2", + "CR:Ack=2", + "CR:Ack=2", + "CR:Ack=2", + "CR:Ack=2" +] + +# Default initialization sequence (scan function) that binds a multimeter +# to port 1 and a temperature module to port 2. +init_sequence = [ + "rfebd 01 0", # 1 + "rfgus 01", # 2 + "rfebd 02 0", # 3 + "rfgus 02", # 4 + "rfebd 03 0", # 5 + "rfebd 04 0", # 6 + "rfebd 05 0", # 7 + "rfebd 06 0" # 8 +] +init_response = [ + "CR:Ack=0:RFEBD", # 1.1 + "ME:R:S#=01:DCC=012:PH=64", # 1.2 + "CR:Ack=0:RFGUS", # 2.1 + "ME:R:S#=01:DCC=004:PH=46333030304643", # 2.2 + "CR:Ack=0:RFEBD", # 3.1 + "ME:R:S#=01:DCC=012:PH=64", # 3.2 + "CR:Ack=0:RFGUS", # 4.1 + "ME:R:S#=02:DCC=004:PH=54333030304643", # 4.2 + "CR:Ack=2", # 5 + "CR:Ack=2", # 6 + "CR:Ack=2", # 7 + "CR:Ack=2" # 8 +] + + +def test_mode(): + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence + + [ + "rfemd 01 1", # 1 + "rfemd 01 2" # 2 + ], + init_response + + [ + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=00000006020C0600", # 1.2 + "CR:Ack=0:RFEMD" # 2 + ], + "\r" + ) as inst: + assert inst.mode == inst.Mode.voltage_dc + + +def test_connect(): + with expected_protocol( + ik.fluke.Fluke3000, + none_sequence + + [ + "ri", # 1 + "rfsm 1", # 2 + "rfdis", # 3 + ] + + init_sequence, + none_response + + [ + "CR:Ack=0:RI", # 1.1 + "SI:PON=Power On", # 1.2 + "RE:O", # 1.3 + "CR:Ack=0:RFSM:Radio On Master", # 2.1 + "RE:M", # 2.2 + "CR:Ack=0:RFDIS", # 3.1 + "ME:S", # 3.2 + "ME:D:010200000000", # 3.3 + ] + + init_response, + "\r" + ) as inst: + assert inst.positions[ik.fluke.Fluke3000.Module.m3000] == 1 + assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 + + +def test_scan(): + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + assert inst.positions[ik.fluke.Fluke3000.Module.m3000] == 1 + assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 + + +def test_reset(): + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence + + [ + "ri", # 1 + "rfsm 1" # 2 + ], + init_response + + [ + "CR:Ack=0:RI", # 1.1 + "SI:PON=Power On", # 1.2 + "RE:O", # 1.3 + "CR:Ack=0:RFSM:Radio On Master", # 2.1 + "RE:M" # 2.2 + ], + "\r" + ) as inst: + inst.reset() + + +def test_measure(): + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence + + [ + "rfemd 01 1", # 1 + "rfemd 01 2", # 2 + "rfemd 02 0" # 3 + ], + init_response + + [ + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + "CR:Ack=0:RFEMD", # 3.1 + "ME:R:S#=02:DCC=010:PH=FD00C08207220000" # 3.2 + ], + "\r" + ) as inst: + assert inst.measure(inst.Mode.voltage_dc) == 0.509 * pq.volt + assert inst.measure(inst.Mode.temperature) == -25.3 * pq.celsius diff --git a/instruments/tests/test_glassman/__init__.py b/instruments/tests/test_glassman/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py new file mode 100644 index 000000000..4eab974d2 --- /dev/null +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Glassman FR power supply +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import +from builtins import round + +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + +def set_defaults(inst): + """ + Sets default values for the voltage and current range of the Glassman FR + to be used to test the voltage and current property getters/setters. + """ + inst.voltage_max = 50.0 * pq.kilovolt + inst.current_max = 6.0 * pq.milliamp + inst.polarity = +1 + + +def test_channel(): + with expected_protocol( + ik.glassman.GlassmanFR, + [], + [], + "\r" + ) as inst: + assert len(inst.channel) == 1 + assert inst.channel[0] == inst + + +def test_voltage(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q51", + "\x01S3330000000001CD" + ], + [ + "R00000000000040", + "A" + ], + "\r" + ) as inst: + set_defaults(inst) + inst.voltage = 10.0 * pq.kilovolt + assert inst.voltage == 10.0 * pq.kilovolt + + +def test_current(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q51", + "\x01S0003330000001CD" + ], + [ + "R00000000000040", + "A" + ], + "\r" + ) as inst: + set_defaults(inst) + inst.current = 1.2 * pq.milliamp + assert inst.current == 1.2 * pq.milliamp + + +def test_voltage_sense(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q51" + ], + [ + "R10A00000010053" + ], + "\r" + ) as inst: + set_defaults(inst) + assert round(inst.voltage_sense) == 13.0 * pq.kilovolt + + +def test_current_sense(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q51" + ], + [ + "R0001550001004C" + ], + "\r" + ) as inst: + set_defaults(inst) + assert inst.current_sense == 2.0 * pq.milliamp + + +def test_mode(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q51", + "\x01Q51" + ], + [ + "R00000000000040", + "R00000000010041" + ], + "\r" + ) as inst: + assert inst.mode == inst.Mode.voltage + assert inst.mode == inst.Mode.current + + +def test_output(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01S0000000000001C4", + "\x01Q51", + "\x01S0000000000002C5", + "\x01Q51" + ], + [ + "A", + "R00000000000040", + "A", + "R00000000040044" + ], + "\r" + ) as inst: + inst.output = False + assert not inst.output + inst.output = True + assert inst.output + + +def test_version(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01V56" + ], + [ + "B1465" + ], + "\r" + ) as inst: + assert inst.version == "14" + + +def test_device_timeout(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01C073", + "\x01C174" + ], + [ + "A", + "A" + ], + "\r" + ) as inst: + inst.device_timeout = True + assert inst.device_timeout + inst.device_timeout = False + assert not inst.device_timeout + + +def test_sendcmd(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01123ABC5C" + ], + [], + "\r" + ) as inst: + inst.sendcmd("123ABC") + + +def test_query(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q123ABCAD" + ], + [ + "R123ABC5C" + ], + "\r" + ) as inst: + inst.query("Q123ABC") + + +def test_reset(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01S0000000000004C7" + ], + [ + "A" + ], + "\r" + ) as inst: + inst.reset() + + +def test_set_status(): + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01S3333330000002D7", + "\x01Q51" + ], + [ + "A", + "R00000000040044" + ], + "\r" + ) as inst: + set_defaults(inst) + inst.set_status(voltage=10*pq.kilovolt, current=1.2*pq.milliamp, output=True) + assert inst.output + assert inst.voltage == 10 * pq.kilovolt + assert inst.current == 1.2 * pq.milliamp diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py new file mode 100644 index 000000000..f2eda8286 --- /dev/null +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the HP E3631A power supply +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import + +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ####################################################################### + +def test_channel(): + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", + "INST:NSEL?", + "INST:NSEL?", + "INST:NSEL 2", + "INST:NSEL?" + ], + [ + "1", + "1", + "2" + ] + ) as inst: + assert inst.channelid == 1 + assert inst.channel[2] == inst + assert inst.channelid == 2 + + +def test_channelid(): + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "INST:NSEL?", # 1 + "INST:NSEL 2", # 2 + "INST:NSEL?" # 3 + ], + [ + "1", # 1 + "2" # 3 + ] + ) as inst: + assert inst.channelid == 1 + inst.channelid = 2 + assert inst.channelid == 2 + + +def test_voltage(): + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:VOLT? MAX", # 1 + "SOUR:VOLT? MAX", # 2 + "SOUR:VOLT? MAX", # 3.1 + "SOUR:VOLT 3.000000e+00", # 3.2 + "SOUR:VOLT?", # 4 + "SOUR:VOLT? MAX", # 5 + "SOUR:VOLT? MAX" # 6 + ], + [ + "6.0", # 1 + "6.0", # 2 + "6.0", # 3.1 + "3.0", # 4 + "6.0", # 5 + "6.0" # 6 + ] + ) as inst: + assert inst.voltage_min == 0.0 * pq.volt + assert inst.voltage_max == 6.0 * pq.volt + inst.voltage = 3.0 * pq.volt + assert inst.voltage == 3.0 * pq.volt + try: + inst.voltage = -1.0 * pq.volt + except ValueError: + pass + try: + inst.voltage = 7.0 * pq.volt + except ValueError: + pass + + +def test_current(): + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:CURR? MIN", # 1.1 + "SOUR:CURR? MAX", # 1.2 + "SOUR:CURR? MIN", # 2.1 + "SOUR:CURR? MAX", # 2.2 + "SOUR:CURR 2.000000e+00", # 3 + "SOUR:CURR?", # 4 + "SOUR:CURR? MIN", # 5 + "SOUR:CURR? MIN", # 6.1 + "SOUR:CURR? MAX" # 6.2 + ], + [ + "0.0", # 1.1 + "5.0", # 1.2 + "0.0", # 2.1 + "5.0", # 2.2 + "2.0", # 4 + "0.0", # 5 + "0.0", # 6.1 + "5.0" # 6.2 + ] + ) as inst: + assert inst.current_min == 0.0 * pq.amp + assert inst.current_max == 5.0 * pq.amp + inst.current = 2.0 * pq.amp + assert inst.current == 2.0 * pq.amp + try: + inst.current = -1.0 * pq.amp + except ValueError: + pass + try: + inst.current = 6.0 * pq.amp + except ValueError: + pass + + +def test_voltage_sense(): + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "MEAS:VOLT?" # 1 + ], + [ + "1.234" # 1 + ] + ) as inst: + assert inst.voltage_sense == 1.234 * pq.volt + + +def test_current_sense(): + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "MEAS:CURR?" # 1 + ], + [ + "1.234" # 1 + ] + ) as inst: + assert inst.current_sense == 1.234 * pq.amp diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py new file mode 100644 index 000000000..6ea2e5bf3 --- /dev/null +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Keithley 485 picoammeter +""" + +# IMPORTS #################################################################### + +from __future__ import absolute_import + +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + +def test_zero_check(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "C0X", + "C1X", + "U0X" + ], [ + "4851000000000:" + ] + ) as inst: + inst.zero_check = False + inst.zero_check = True + assert inst.zero_check + + +def test_log(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "D0X", + "D1X", + "U0X" + ], [ + "4850100000000:" + ] + ) as inst: + inst.log = False + inst.log = True + assert inst.log + + +def test_input_range(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "R0X", + "R7X", + "U0X" + ], [ + "4850070000000:" + ] + ) as inst: + inst.input_range = "auto" + inst.input_range = 2e-3 + assert inst.input_range == 2. * pq.milliamp + + +def test_relative(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "Z0X", + "Z1X", + "U0X" + ], [ + "4850001000000:" + ] + ) as inst: + inst.relative = False + inst.relative = True + assert inst.relative + + +def test_eoi_mode(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "K0X", + "K1X", + "U0X" + ], [ + "4850000100000:" + ] + ) as inst: + inst.eoi_mode = True + inst.eoi_mode = False + assert not inst.eoi_mode + + +def test_trigger_mode(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "T0X", + "T5X", + "U0X" + ], [ + "4850000050000:" + ] + ) as inst: + inst.trigger_mode = "continuous_ontalk" + inst.trigger_mode = "oneshot_onx" + assert inst.trigger_mode == "oneshot_onx" + + +def test_auto_range(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "R0X", + "U0X" + ], [ + "4850000000000:" + ] + ) as inst: + inst.auto_range() + assert inst.input_range == "auto" + + +def test_get_status(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "U0X" + ], [ + "4850000000000:" + ] + ) as inst: + inst.get_status() + + +def test_measure(): + with expected_protocol( + ik.keithley.Keithley485, + [ + "X", + "X" + ], [ + "NDCA+1.2345E-9", + "NDCL-9.0000E+0" + ] + ) as inst: + assert inst.measure() == 1.2345 * pq.nanoamp + assert inst.measure() == 1. * pq.nanoamp From a79a7eed920d3616cf7611f71253c805a313be75 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 4 Jan 2020 16:25:31 -0500 Subject: [PATCH 028/108] A few fixes from Francois for the glassman FR (#225) --- instruments/glassman/glassmanfr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 3069e93ec..51b26295d 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -96,7 +96,7 @@ def __init__(self, filelike): self.voltage_max = 50.0 * pq.kilovolt self.current_max = 6.0 * pq.milliamp self.polarity = +1 - self._device_timeout = True + self._device_timeout = False self._voltage = 0. * pq.volt self._current = 0. * pq.amp @@ -273,8 +273,8 @@ def device_timeout(self): def device_timeout(self, newval): if not isinstance(newval, bool): raise TypeError("Device timeout mode must be a boolean.") - self._device_timeout = newval self.query("C{}".format(int(not newval))) # Device acknowledges + self._device_timeout = newval # METHODS ## From c9dac25415d8878082136e072e87bf161d606859 Mon Sep 17 00:00:00 2001 From: Jed Date: Sat, 11 Jan 2020 12:00:58 -0500 Subject: [PATCH 029/108] <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="? (#226) --- instruments/thorlabs/tc200.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index 4478c80d8..0beef5de7 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -111,7 +111,7 @@ def enable(self): :type: `bool` """ response = self.status - return True if int(response) % 2 is 1 else False + return True if int(response) % 2 == 1 else False @enable.setter def enable(self, newval): From e007259d3505cbf620cafca59b92face306bcf1d Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 12 Jan 2020 17:00:08 -0500 Subject: [PATCH 030/108] Optical spectrum analyzers (#223) * Revert change to _extras_require_ * Implement abstract optical spectrum analyzer class * Basic functunality for Yokogawa6370 OSA * Add Yokogawa6370 OSA to docs * Formatting changes * Revert "Revert change to _extras_require_" This reverts commit 290e75e2728990f82e68a70343e693f54083177e. * Fix linting * Sort import * Add unittests for Yokogawa 6370 Co-authored-by: timhellwig --- doc/source/apiref/yokogawa.rst | 7 + instruments/abstract_instruments/__init__.py | 5 + .../optical_spectrum_analyzer.py | 136 ++++++++++ instruments/tests/test_yokogawa/__init__.py | 0 .../tests/test_yokogawa/test_yokogawa_6370.py | 236 ++++++++++++++++++ instruments/yokogawa/__init__.py | 1 + instruments/yokogawa/yokogawa6370.py | 202 +++++++++++++++ 7 files changed, 587 insertions(+) create mode 100644 instruments/abstract_instruments/optical_spectrum_analyzer.py create mode 100644 instruments/tests/test_yokogawa/__init__.py create mode 100644 instruments/tests/test_yokogawa/test_yokogawa_6370.py create mode 100644 instruments/yokogawa/yokogawa6370.py diff --git a/doc/source/apiref/yokogawa.rst b/doc/source/apiref/yokogawa.rst index 372ffd223..7dcd32589 100644 --- a/doc/source/apiref/yokogawa.rst +++ b/doc/source/apiref/yokogawa.rst @@ -7,6 +7,13 @@ Yokogawa ======== +:class:`Yokogawa6370` Optical Spectrum Analyzer +=============================================== + +.. autoclass:: Yokogawa6370 + :members: + :undoc-members: + :class:`Yokogawa7651` Power Supply ================================== diff --git a/instruments/abstract_instruments/__init__.py b/instruments/abstract_instruments/__init__.py index 7b7eea482..390473b74 100644 --- a/instruments/abstract_instruments/__init__.py +++ b/instruments/abstract_instruments/__init__.py @@ -19,3 +19,8 @@ PowerSupplyChannel, PowerSupply, ) + +from .optical_spectrum_analyzer import ( + OSAChannel, + OpticalSpectrumAnalyzer, +) diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py new file mode 100644 index 000000000..3cc9f4ab2 --- /dev/null +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Provides an abstract base class for optical spectrum analyzer instruments +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import +from __future__ import division + +import abc + +from future.utils import with_metaclass + +from instruments.abstract_instruments import Instrument + +# CLASSES ##################################################################### + + +class OSAChannel(with_metaclass(abc.ABCMeta, object)): + + """ + Abstract base class for physical channels on an optical spectrum analyzer. + + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. + """ + + # METHODS # + + @abc.abstractmethod + def wavelength(self, bin_format=True): + """ + Gets the x-axis of the specified data source channel. This is an + abstract property. + + :param bool bin_format: If the waveform should be transfered in binary + (``True``) or ASCII (``False``) formats. + :return: The wavelength component of the waveform. + :rtype: `numpy.ndarray` + """ + raise NotImplementedError + + @abc.abstractmethod + def data(self, bin_format=True): + """ + Gets the y-axis of the specified data source channel. This is an + abstract property. + + :param bool bin_format: If the waveform should be transfered in binary + (``True``) or ASCII (``False``) formats. + :return: The y-component of the waveform. + :rtype: `numpy.ndarray` + """ + raise NotImplementedError + + +class OpticalSpectrumAnalyzer(with_metaclass(abc.ABCMeta, Instrument)): + + """ + Abstract base class for optical spectrum analyzer instruments. + + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. + """ + + # PROPERTIES # + + @abc.abstractproperty + def channel(self): + """ + Gets an iterator or list for easy Pythonic access to the various + channel objects on the OSA instrument. Typically generated + by the `~instruments.util_fns.ProxyList` helper. + """ + raise NotImplementedError + + + @property + @abc.abstractmethod + def start_wl(self): + """ + Gets/sets the the start wavelength of the OSA. This is + an abstract property. + + :type: `~quantities.Quantity` + """ + raise NotImplementedError + + @start_wl.setter + @abc.abstractmethod + def start_wl(self, newval): + raise NotImplementedError + + @property + @abc.abstractmethod + def stop_wl(self): + """ + Gets/sets the the stop wavelength of the OSA. This is + an abstract property. + + :type: `~quantities.Quantity` + """ + raise NotImplementedError + + @stop_wl.setter + @abc.abstractmethod + def stop_wl(self, newval): + raise NotImplementedError + + @property + @abc.abstractmethod + def bandwidth(self): + """ + Gets/sets the the bandwidth of the OSA. This is + an abstract property. + + :type: `~quantities.Quantity` + """ + raise NotImplementedError + + @bandwidth.setter + @abc.abstractmethod + def bandwidth(self, newval): + raise NotImplementedError + + + # METHODS # + + @abc.abstractmethod + def start_sweep(self): + """ + Forces a start sweep on the attached OSA. + """ + raise NotImplementedError diff --git a/instruments/tests/test_yokogawa/__init__.py b/instruments/tests/test_yokogawa/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py new file mode 100644 index 000000000..6e6e4da99 --- /dev/null +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the Yokogawa 6370 +""" + +# IMPORTS ##################################################################### + +from __future__ import absolute_import + +import struct + +import numpy as np +import quantities as pq + +import instruments as ik +from instruments.tests import expected_protocol + +from hypothesis import ( + given, + strategies as st, +) + +# TESTS ####################################################################### + + +def test_channel_is_channel_class(): + inst = ik.yokogawa.Yokogawa6370.open_test() + assert isinstance(inst.channel["A"], inst.Channel) is True + + +def test_init(): + with expected_protocol( + ik.yokogawa.Yokogawa6370, + [ + ":FORMat:DATA REAL,64" + ], + [] + ) as _: + pass + + +@given(values=st.lists(st.decimals(allow_infinity=False, allow_nan=False), min_size=1), + channel=st.sampled_from(ik.yokogawa.Yokogawa6370.Traces)) +def test_channel_data(values, channel): + values_packed = b"".join(struct.pack(">> import instruments as ik + >>> import quantities as pq + >>> inst = ik.yokogawa.Yokogawa6370.open_visa('TCPIP0:192.168.0.35') + >>> inst.start_wl = 1030e-9 * pq.m + """ + + def __init__(self, *args, **kwargs): + super(Yokogawa6370, self).__init__(*args, **kwargs) + # Set data Format to binary + self.sendcmd(":FORMat:DATA REAL,64") # TODO: Find out where we want this + + # INNER CLASSES # + + class Channel(OSAChannel): + + """ + Class representing the channels on the Yokogawa 6370. + + This class inherits from `OSAChannel`. + + .. warning:: This class should NOT be manually created by the user. It + is designed to be initialized by the `Yokogawa6370` class. + """ + def __init__(self, parent, idx): + self._parent = parent + self._name = idx + + # METHODS # + + def data(self, bin_format=True): + cmd = ":TRAC:Y? {0}".format(self._name) + self._parent.sendcmd(cmd) + data = self._parent.binblockread(data_width=4, fmt=">> import instruments as ik + >>> osa = ik.yokogawa.Yokogawa6370.open_gpibusb('/dev/ttyUSB0') + >>> dat = osa.channel["A"].data # Gets the data of channel 0 + + :rtype: `list`[`~Yokogawa6370.Channel`] + """ + return ProxyList(self, Yokogawa6370.Channel, Yokogawa6370.Traces) + + start_wl, start_wl_min, start_wl_max = bounded_unitful_property( + ":SENS:WAV:STAR", + pq.meter, + doc=""" + The start wavelength in m. + """, + valid_range=(600e-9, 1700e-9) + ) + + stop_wl, stop_wl_min, stop_wl_max = bounded_unitful_property( + ":SENS:WAV:STOP", + pq.meter, + doc=""" + The stop wavelength in m. + """, + valid_range=(600e-9, 1700e-9) + ) + + bandwidth = unitful_property( + ":SENS:BAND:RES", + pq.meter, + doc=""" + The bandwidth in m. + """ + ) + + span = unitful_property( + ":SENS:WAV:SPAN", + pq.meter, + doc=""" + A floating point property that controls the wavelength span in m. + """ + ) + + center_wl = unitful_property( + ":SENS:WAV:CENT", + pq.meter, + doc=""" + A floating point property that controls the center wavelength m. + """ + ) + + points = unitless_property( + ":SENS:SWE:POIN", + doc=""" + An integer property that controls the number of points in a trace. + """ + ) + + sweep_mode = enum_property( + ":INIT:SMOD", + SweepModes, + input_decoration=int, + doc=""" + A property to control the Sweep Mode as one of Yokogawa6370.SweepMode. + Effective only after a self.start_sweep().""" + ) + + active_trace = enum_property( + ":TRAC:ACTIVE", + Traces, + doc=""" + The active trace of the OSA of enum Yokogawa6370.Traces. Determines the + result of Yokogawa6370.data() and Yokogawa6370.wavelength().""" + ) + + # METHODS # + + def data(self): + """ + Function to query the active Trace data of the OSA. + """ + return self.channel[self.active_trace].data + + def wavelength(self): + """ + Query the wavelength axis of the active trace. + """ + return self.channel[self.active_trace].wavelength + + def start_sweep(self): + """ + Triggering function for the Yokogawa 6370. + + After changing the sweep mode, the device needs to be triggered before it will update. + """ + self.sendcmd("*CLS;:init") From e5845eb470651feead815237f4a139c55a0911ea Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 12 Jan 2020 21:06:11 -0500 Subject: [PATCH 031/108] Update copyrights for 2020 (#227) --- doc/source/conf.py | 2 +- instruments/__init__.py | 2 +- license/AUTHOR.TXT | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 7ad9faa69..6002ac35d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,7 +42,7 @@ # General information about the project. project = u'InstrumentKit Library' -copyright = u'2013-2019, Steven Casagrande' +copyright = u'2013-2020, Steven Casagrande' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/instruments/__init__.py b/instruments/__init__.py index 0d3efa551..9fabf75db 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -49,4 +49,4 @@ __email__ = "scasagrande@galvant.ca" __license__ = "AGPLv3" -__copyright__ = "Copyright (c) 2012-2019 Steven Casagrande" +__copyright__ = "Copyright (c) 2012-2020 Steven Casagrande" diff --git a/license/AUTHOR.TXT b/license/AUTHOR.TXT index 1c4b0f138..1d8b4f96d 100644 --- a/license/AUTHOR.TXT +++ b/license/AUTHOR.TXT @@ -7,4 +7,4 @@ Steven Casagrande scasagrande@galvant.ca twitter.com/stevecasagrande -2012-2019 +2012-2020 From 373786d0789af64c7ec944fd66ba218a0e1dc29b Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 12 Jan 2020 21:17:41 -0500 Subject: [PATCH 032/108] Bump version v0.5.0 -> v0.6.0 (#228) --- instruments/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index 9fabf75db..a08b18726 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -39,7 +39,7 @@ # In keeping with PEP-396, we define a version number of the form # {major}.{minor}[.{postrelease}]{prerelease-tag} -__version__ = "0.5.0" +__version__ = "0.6.0" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" From 511308321e25ba146119605086db6c1e7c29c0ed Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 12 Jan 2020 22:58:31 -0500 Subject: [PATCH 033/108] Officially drop Py2.7 and Py3.5 (#229) --- .travis.yml | 2 -- README.rst | 4 ++-- setup.py | 3 --- tox.ini | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d34c5b7e..3997b0bcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ dist: xenial sudo: false language: python python: - - "2.7" - - "3.5" - "3.6" - "3.7" install: diff --git a/README.rst b/README.rst index d37d1674a..fbf097a11 100644 --- a/README.rst +++ b/README.rst @@ -111,7 +111,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 2.7, 3.5, 3.6, and 3.7 are supported. Should you encounter +At this time, Python 3.6, and 3.7 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. @@ -134,7 +134,7 @@ To run the tests against all supported version of Python, you will need to have the binary for each installed, as well as any requirements needed to install ``numpy`` under each Python version. On Debian/Ubuntu systems this means you will need to install the ``python-dev`` package for each version of Python -supported (``python2.7-dev``, ``python3.7-dev``, etc). +supported (``python3.7-dev``, etc). With the required system packages installed, all tests can be run with ``tox``: diff --git a/setup.py b/setup.py index 0987eb5a4..5b07727e2 100644 --- a/setup.py +++ b/setup.py @@ -20,10 +20,7 @@ CLASSIFIERS = [ "Development Status :: 4 - Beta", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Operating System :: OS Independent", diff --git a/tox.ini b/tox.ini index d4a105448..d6ecd6528 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py35,py36,py37 +envlist = py36,py37 [testenv] deps = -rdev-requirements.txt commands = pytest From 0dd1ae6cba37c672606af4736889685949da7e34 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 12 Jan 2020 23:06:25 -0500 Subject: [PATCH 034/108] Remove enum34 from dependencies (#230) --- doc/source/intro.rst | 2 -- requirements.txt | 1 - setup.py | 1 - 3 files changed, 4 deletions(-) diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 87d723ae8..b2f2b146e 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -30,7 +30,6 @@ $ pip install -r requirements.txt - NumPy - `PySerial`_ - `quantities`_ -- `enum34`_ - `future`_ - `python-vxi11`_ - `PyUSB`_ (version 1.0a or higher, required for raw USB support) @@ -43,7 +42,6 @@ Optional Dependencies .. _PySerial: http://pyserial.sourceforge.net/ .. _quantities: http://pythonhosted.org/quantities/ -.. _enum34: https://pypi.python.org/pypi/enum34 .. _future: https://pypi.python.org/pypi/future .. _ruamel.yaml: http://yaml.readthedocs.io .. _PyUSB: http://sourceforge.net/apps/trac/pyusb/ diff --git a/requirements.txt b/requirements.txt index c6153e9af..9158772a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,6 @@ pyserial pyvisa>=1.9 quantities>=0.12.1 future>=0.15 -enum34 python-vxi11>=0.8 pyusb python-usbtmc diff --git a/setup.py b/setup.py index 5b07727e2..e285ad0cf 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ "pyserial>=3.3", "pyvisa>=1.9", "quantities>=0.12.1", - "enum34", "future>=0.15", "python-vxi11>=0.8", "python-usbtmc", From 5c7ba2172fbe0eeef34693d08dd8d543491f81fb Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 15 Jan 2020 01:01:21 -0500 Subject: [PATCH 035/108] Update pylint 2.4 (#231) * Update pylint to 2.4.4 * Fix linting for abstract_instruments * Fix linting for thorlabs * Fix linting for tektronix * Fix linting for toptica topmode tests * Fix linting for test_abstract_int * Fix linting for test_agilent * Fix linting for test_fluke * Fix linting for test_generic_scpi * Fix linting for test_glassman * Fix linting for test_holzworth * Fix linting for test_hp * Fix linting for test_keithley * Fix linting for test_minghe * Fix linting for test_newport * Fix linting for test_oxford * Fix linting for test_phasematrix * Fix linting for test_picowatt * Fix linting for test_qubitekk * Fix linting for test_srs * Fix linting for test_tektronix * Fix linting for test_thorlabs * Fix linting for test_yokogawa * Fix linting for tests * Finish fixing pylint 2.4 * Finish fixing pylint 2.4, round 2 --- .pylintrc | 2 +- .travis.yml | 2 +- dev-requirements.txt | 3 +- .../comm/abstract_comm.py | 10 +- .../comm/loopback_communicator.py | 7 +- .../comm/usbtmc_communicator.py | 1 - .../comm/visa_communicator.py | 1 - .../abstract_instruments/electrometer.py | 6 - .../function_generator.py | 27 +- .../abstract_instruments/instrument.py | 10 +- .../abstract_instruments/multimeter.py | 5 - .../abstract_instruments/power_supply.py | 6 - .../signal_generator/channel.py | 4 - .../signal_generator/signal_generator.py | 3 +- instruments/errors.py | 2 - instruments/fluke/fluke3000.py | 39 +- instruments/named_struct.py | 13 +- instruments/tektronix/tektds224.py | 60 +- instruments/tektronix/tektds5xx.py | 1 - .../test_function_generator.py | 2 + .../tests/test_agilent/test_agilent_33220a.py | 156 ++-- .../tests/test_agilent/test_agilent_34410a.py | 110 +-- instruments/tests/test_base_instrument.py | 12 +- .../tests/test_fluke/test_fluke3000.py | 138 ++-- .../test_scpi_function_generator.py | 74 +- .../test_generic_scpi/test_scpi_multimeter.py | 178 ++--- .../tests/test_glassman/test_glassmanfr.py | 228 +++--- .../test_holzworth/test_holzworth_hs9000.py | 278 +++---- instruments/tests/test_hp/test_hp3456a.py | 456 ++++++----- instruments/tests/test_hp/test_hp6624a.py | 330 ++++---- instruments/tests/test_hp/test_hp6632b.py | 324 ++++---- instruments/tests/test_hp/test_hp6652a.py | 48 +- instruments/tests/test_hp/test_hpe3631a.py | 164 ++-- .../tests/test_keithley/test_keithley2182.py | 223 +++--- .../tests/test_keithley/test_keithley485.py | 138 ++-- .../tests/test_keithley/test_keithley6220.py | 26 +- .../tests/test_keithley/test_keithley6514.py | 212 +++-- .../tests/test_minghe/test_minghe_mhs5200a.py | 232 +++--- instruments/tests/test_named_struct.py | 3 +- .../tests/test_newport/test_newportesp301.py | 20 +- .../tests/test_oxford/test_oxforditc503.py | 30 +- .../test_phasematrix_fsw0020.py | 102 +-- .../test_picowatt/test_picowatt_avs47.py | 158 ++-- .../tests/test_qubitekk/test_qubitekk_cc1.py | 489 ++++++------ .../tests/test_qubitekk/test_qubitekk_mc1.py | 203 +++-- instruments/tests/test_srs/test_srs345.py | 82 +- instruments/tests/test_srs/test_srs830.py | 517 ++++++------ instruments/tests/test_srs/test_srsdg645.py | 66 +- .../test_tektronix/test_tektronix_tds224.py | 98 +-- .../tests/test_thorlabs/test_thorlabs_apt.py | 72 +- .../test_thorlabs/test_thorlabs_lcc25.py | 470 ++++++----- .../tests/test_thorlabs/test_thorlabs_sc10.py | 360 +++++---- .../test_thorlabs/test_thorlabs_tc200.py | 540 ++++++------- .../test_toptica/test_toptica_topmode.py | 755 +++++++++--------- .../tests/test_yokogawa/test_yokogawa_6370.py | 224 +++--- instruments/thorlabs/_abstract.py | 6 +- instruments/thorlabs/thorlabsapt.py | 10 +- instruments/util_fns.py | 34 +- 58 files changed, 3832 insertions(+), 3938 deletions(-) diff --git a/.pylintrc b/.pylintrc index 0cb0a5c85..9ecd7ce4d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -144,7 +144,7 @@ spelling-store-unknown-words=no [FORMAT] # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=120 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ diff --git a/.travis.yml b/.travis.yml index 3997b0bcc..5f971854e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ before_script: script: - pytest --cov=instruments - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --py3k instruments; fi - - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --disable=I instruments; fi + - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --disable=I,R instruments; fi after_success: - coveralls deploy: diff --git a/dev-requirements.txt b/dev-requirements.txt index 0c2383434..1f8c048cc 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,5 +2,4 @@ mock pytest pytest-mock hypothesis==4.28.2 -pylint==1.7.1 -astroid==1.5.3 +pylint==2.4.4 diff --git a/instruments/abstract_instruments/comm/abstract_comm.py b/instruments/abstract_instruments/comm/abstract_comm.py index b29b5e97d..5f93fb492 100644 --- a/instruments/abstract_instruments/comm/abstract_comm.py +++ b/instruments/abstract_instruments/comm/abstract_comm.py @@ -126,7 +126,6 @@ def read_raw(self, size=-1): :return: The read bytes :rtype: `bytes` """ - pass @abc.abstractmethod def write_raw(self, msg): @@ -136,7 +135,6 @@ def write_raw(self, msg): :param bytes msg: Bytes to be sent to the instrument over the connection. """ - pass @abc.abstractmethod def _sendcmd(self, msg): @@ -147,7 +145,6 @@ def _sendcmd(self, msg): Note that this is called by :class:`AbstractCommunicator.sendcmd`, which also handles debug, event and capture support. """ - pass @abc.abstractmethod def _query(self, msg, size=-1): @@ -164,7 +161,6 @@ def _query(self, msg, size=-1): Note that this is called by :class:`AbstractCommunicator.query`, which also handles debug, event and capture support. """ - pass @abc.abstractmethod def flush_input(self): @@ -208,10 +204,10 @@ def read(self, size=-1, encoding="utf-8"): codecs.lookup(encoding) return self.read_raw(size).decode(encoding) except LookupError: - if encoding == 'IEEE-754/64': - return struct.unpack('>d', self.read_raw(size))[0] + if encoding == "IEEE-754/64": + return struct.unpack(">d", self.read_raw(size))[0] else: - raise ValueError("Encoding {} is not currently supported.".format(encoding)) + raise ValueError(f"Encoding {encoding} is not currently supported.") def sendcmd(self, msg): """ diff --git a/instruments/abstract_instruments/comm/loopback_communicator.py b/instruments/abstract_instruments/comm/loopback_communicator.py index 029c9d34f..ca48331c6 100644 --- a/instruments/abstract_instruments/comm/loopback_communicator.py +++ b/instruments/abstract_instruments/comm/loopback_communicator.py @@ -140,7 +140,7 @@ def write_raw(self, msg): if self._stdout is not None: self._stdout.write(msg) else: - print(" <- {} ".format(repr(msg))) + print(f" <- {repr(msg)} ") def seek(self, offset): # pylint: disable=unused-argument,no-self-use """ @@ -164,7 +164,6 @@ def flush_input(self): For the loopback communicator, this will do nothing and just `pass`. """ - pass # METHODS # @@ -177,8 +176,8 @@ def _sendcmd(self, msg): :param str msg: The command message to send to the instrument """ - if msg != '': - msg = "{}{}".format(msg, self._terminator) + if msg != "": + msg = f"{msg}{self._terminator}" self.write(msg) def _query(self, msg, size=-1): diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 88855d4e5..795b4a4dd 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -150,7 +150,6 @@ def flush_input(self): For a USBTMC connection, this function does not actually do anything and simply returns. """ - pass # METHODS # diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 0b16ef2a5..c487d7c12 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -160,7 +160,6 @@ def flush_input(self): entirety of its contents. """ # TODO: Find out how to flush with pyvisa - pass # METHODS # diff --git a/instruments/abstract_instruments/electrometer.py b/instruments/abstract_instruments/electrometer.py index 9f794cdaf..2b655e1de 100644 --- a/instruments/abstract_instruments/electrometer.py +++ b/instruments/abstract_instruments/electrometer.py @@ -37,7 +37,6 @@ def mode(self): :type: `~enum.Enum` """ - pass @mode.setter @abc.abstractmethod @@ -53,7 +52,6 @@ def unit(self): :type: `~quantities.UnitQuantity` """ - pass @property @abc.abstractmethod @@ -64,7 +62,6 @@ def trigger_mode(self): :type: `~enum.Enum` """ - pass @trigger_mode.setter @abc.abstractmethod @@ -80,7 +77,6 @@ def input_range(self): :type: `~enum.Enum` """ - pass @input_range.setter @abc.abstractmethod @@ -96,7 +92,6 @@ def zero_check(self): :type: `bool` """ - pass @zero_check.setter @abc.abstractmethod @@ -112,7 +107,6 @@ def zero_correct(self): :type: `bool` """ - pass @zero_correct.setter @abc.abstractmethod diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index 107d6591f..cad895110 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -215,21 +215,36 @@ class Function(Enum): """ Enum containg valid output function modes for many function generators """ - sinusoid = 'SIN' - square = 'SQU' - triangle = 'TRI' - ramp = 'RAMP' - noise = 'NOIS' - arbitrary = 'ARB' + sinusoid = "SIN" + square = "SQU" + triangle = "TRI" + ramp = "RAMP" + noise = "NOIS" + arbitrary = "ARB" @property def channel(self): + """ + Gets a channel object for the function generator. This should use + `~instruments.util_fns.ProxyList` to achieve this. + + The number of channels accessable depends on the value + of FunctionGenerator._channel_count + + :rtype: `FunctionGenerator.Channel` + """ return ProxyList(self, self.Channel, range(self._channel_count)) # PASSTHROUGH PROPERTIES # @property def amplitude(self): + """ + Gets/sets the output amplitude of the first channel + of the function generator + + :type: `~quantities.Quantity` + """ return self.channel[0].amplitude @amplitude.setter diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index b025cfa47..98e5df010 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -94,7 +94,7 @@ def sendcmd(self, cmd): be sent. """ self._file.sendcmd(str(cmd)) - ack_expected_list = self._ack_expected(cmd) + ack_expected_list = self._ack_expected(cmd) # pylint: disable=assignment-from-none if not isinstance(ack_expected_list, (list, tuple)): ack_expected_list = [ack_expected_list] for ack_expected in ack_expected_list: @@ -126,7 +126,7 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - ack_expected_list = self._ack_expected(cmd) + ack_expected_list = self._ack_expected(cmd) # pylint: disable=assignment-from-none if not isinstance(ack_expected_list, (list, tuple)): ack_expected_list = [ack_expected_list] @@ -138,16 +138,14 @@ def query(self, cmd, size=-1): ack = self.read() if ack != ack_expected: raise AcknowledgementError( - "Incorrect ACK message received: got {} " - "expected {}".format(ack, ack_expected) + f"Incorrect ACK message received: got {ack} expected {ack_expected}" ) value = self.read(size) # Now read in our return data if self.prompt is not None: prompt = self.read(len(self.prompt)) if prompt != self.prompt: raise PromptError( - "Incorrect prompt message received: got {} " - "expected {}".format(prompt, self.prompt) + f"Incorrect prompt message received: got {prompt} expected {self.prompt}" ) return value diff --git a/instruments/abstract_instruments/multimeter.py b/instruments/abstract_instruments/multimeter.py index 0bd9e90b8..150dfd1a9 100644 --- a/instruments/abstract_instruments/multimeter.py +++ b/instruments/abstract_instruments/multimeter.py @@ -38,7 +38,6 @@ def mode(self): :type: `~enum.Enum` """ - pass @mode.setter @abc.abstractmethod @@ -54,7 +53,6 @@ def trigger_mode(self): :type: `~enum.Enum` """ - pass @trigger_mode.setter @abc.abstractmethod @@ -70,7 +68,6 @@ def relative(self): :type: `bool` """ - pass @relative.setter @abc.abstractmethod @@ -86,7 +83,6 @@ def input_range(self): :type: `~quantities.quantity.Quantity` or `~enum.Enum` """ - pass @input_range.setter @abc.abstractmethod @@ -100,4 +96,3 @@ def measure(self, mode): """ Perform a measurement as specified by mode parameter. """ - pass diff --git a/instruments/abstract_instruments/power_supply.py b/instruments/abstract_instruments/power_supply.py index 4b54e951b..9fea6bf9e 100644 --- a/instruments/abstract_instruments/power_supply.py +++ b/instruments/abstract_instruments/power_supply.py @@ -38,7 +38,6 @@ def mode(self): :type: `~enum.Enum` """ - pass @mode.setter @abc.abstractmethod @@ -54,7 +53,6 @@ def voltage(self): :type: `~quantities.quantity.Quantity` """ - pass @voltage.setter @abc.abstractmethod @@ -70,7 +68,6 @@ def current(self): :type: `~quantities.quantity.Quantity` """ - pass @current.setter @abc.abstractmethod @@ -86,7 +83,6 @@ def output(self): :type: `bool` """ - pass @output.setter @abc.abstractmethod @@ -127,7 +123,6 @@ def voltage(self): :type: `~quantities.quantity.Quantity` """ - pass @voltage.setter @abc.abstractmethod @@ -143,7 +138,6 @@ def current(self): :type: `~quantities.quantity.Quantity` """ - pass @current.setter @abc.abstractmethod diff --git a/instruments/abstract_instruments/signal_generator/channel.py b/instruments/abstract_instruments/signal_generator/channel.py index 79334349c..dc6387551 100644 --- a/instruments/abstract_instruments/signal_generator/channel.py +++ b/instruments/abstract_instruments/signal_generator/channel.py @@ -36,7 +36,6 @@ def frequency(self): :type: `~quantities.quantity.Quantity` """ - pass @frequency.setter @abc.abstractmethod @@ -51,7 +50,6 @@ def power(self): :type: `~quantities.quantity.Quantity` """ - pass @power.setter @abc.abstractmethod @@ -66,7 +64,6 @@ def phase(self): :type: `~quantities.quantity.Quantity` """ - pass @phase.setter @abc.abstractmethod @@ -81,7 +78,6 @@ def output(self): :type: `bool` """ - pass @output.setter @abc.abstractmethod diff --git a/instruments/abstract_instruments/signal_generator/signal_generator.py b/instruments/abstract_instruments/signal_generator/signal_generator.py index a8b9748d8..434a34684 100644 --- a/instruments/abstract_instruments/signal_generator/signal_generator.py +++ b/instruments/abstract_instruments/signal_generator/signal_generator.py @@ -31,7 +31,8 @@ class SignalGenerator(with_metaclass(abc.ABCMeta, Instrument)): # PROPERTIES # - @abc.abstractproperty + @property + @abc.abstractmethod def channel(self): """ Gets a specific channel object for the SignalGenerator. diff --git a/instruments/errors.py b/instruments/errors.py index 3e1eba2d1..ce4ebe5ff 100644 --- a/instruments/errors.py +++ b/instruments/errors.py @@ -16,7 +16,6 @@ class AcknowledgementError(IOError): This error is raised when an instrument fails to send the expected acknowledgement string. """ - pass class PromptError(IOError): @@ -26,4 +25,3 @@ class PromptError(IOError): these characters, but some do in a misguided attempt to be more "user friendly". """ - pass diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index 82b0cc7a4..b57bdcb61 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -156,11 +156,11 @@ def mode(self): :rtype: `Fluke3000.Mode` """ - if self.Module.m3000 not in self.positions.keys(): + if self.Module.m3000 not in self.positions: raise KeyError("No `Fluke3000` FC multimeter is bound") port_id = self.positions[self.Module.m3000] - value = self.query_lines("rfemd 0{} 1".format(port_id), 2)[-1] - self.query("rfemd 0{} 2".format(port_id)) + value = self.query_lines(f"rfemd 0{port_id} 1", 2)[-1] + self.query(f"rfemd 0{port_id} 2") data = value.split("PH=")[-1] return self.Mode(self._parse_mode(data)) @@ -228,20 +228,20 @@ def scan(self): positions = {} for port_id in range(1, 7): # Check if a device is connected to port port_id - output = self.query("rfebd 0{} 0".format(port_id)) + output = self.query(f"rfebd 0{port_id} 0") if "RFEBD" not in output: continue # If it is, identify the device self.read() - output = self.query_lines("rfgus 0{}".format(port_id), 2)[-1] + output = self.query_lines(f"rfgus 0{port_id}", 2)[-1] module_id = int(output.split("PH=")[-1]) if module_id == self.Module.m3000.value: positions[self.Module.m3000] = port_id elif module_id == self.Module.t3000.value: positions[self.Module.t3000] = port_id else: - raise NotImplementedError("Module ID {} not implemented".format(module_id)) + raise NotImplementedError(f"Module ID {module_id} not implemented") self.positions = positions @@ -317,12 +317,12 @@ def measure(self, mode): """ # Check that the mode is supported if not isinstance(mode, self.Mode): - raise ValueError("Mode {} is not supported".format(mode)) + raise ValueError(f"Mode {mode} is not supported") # Check that the module associated with this mode is available module = self._get_module(mode) - if module not in self.positions.keys(): - raise ValueError("Device necessary to measure {} is not available".format(mode)) + if module not in self.positions: + raise ValueError(f"Device necessary to measure {mode} is not available") # Query the module value = '' @@ -332,19 +332,19 @@ def measure(self, mode): # Read out if mode == self.Mode.temperature: # The temperature module supports single readout - value = self.query_lines("rfemd 0{} 0".format(port_id), 2)[-1] + value = self.query_lines(f"rfemd 0{port_id} 0", 2)[-1] else: # The multimeter does not support single readout, # have to open continuous readout, read, then close it - value = self.query_lines("rfemd 0{} 1".format(port_id), 2)[-1] - self.query("rfemd 0{} 2".format(port_id)) + value = self.query_lines(f"rfemd 0{port_id} 1", 2)[-1] + self.query(f"rfemd 0{port_id} 2") # Check that value is consistent with the request, break if "PH" in value: data = value.split("PH=")[-1] if self._parse_mode(data) != mode.value: if self.Module.m3000 in self.positions.keys(): - self.query("rfemd 0{} 2".format(self.positions[self.Module.m3000])) + self.query(f"rfemd 0{self.positions[self.Module.m3000]} 2") self.flush() else: break @@ -394,9 +394,8 @@ def _parse(self, result, mode): # Check that the multimeter is in the right mode (fifth byte) if self._parse_mode(data) != mode.value: - error = ("Mode {} was requested but the Fluke 3000FC Multimeter " - "is in mode {} instead. Could not read the requested " - "quantity.").format(mode.name, self.Mode(data[8:10]).name) + error = (f"Mode {mode.name} was requested but the Fluke 3000FC Multimeter is in " + f"mode {self.Mode(data[8:10]).name} instead. Could not read the requested quantity.") raise ValueError(error) # Extract the value from the first two bytes @@ -451,15 +450,15 @@ def _parse_factor(data): """ # Convert the fourth dual hex byte to an 8 bits string - byte = format(int(data[6:8], 16), '08b') + byte = format(int(data[6:8], 16), "08b") # The first bit encodes the sign (0 positive, 1 negative) - sign = 1 if byte[0] == '0' else -1 + sign = 1 if byte[0] == "0" else -1 # The second to fourth bits encode the metric prefix code = int(byte[1:4], 2) - if code not in PREFIXES.keys(): - raise ValueError("Metric prefix not recognized: {}".format(code)) + if code not in PREFIXES: + raise ValueError(f"Metric prefix not recognized: {code}") prefix = PREFIXES[code] # The sixth and seventh bit encode the decimal place diff --git a/instruments/named_struct.py b/instruments/named_struct.py index ef8522ab7..2c6a44834 100644 --- a/instruments/named_struct.py +++ b/instruments/named_struct.py @@ -36,7 +36,7 @@ # CLASSES ##################################################################### -class Field(object): +class Field: """ A named field within a C-style structure. @@ -66,12 +66,14 @@ def __init__(self, fmt, strip_null=False): if self._fmt[:-1] and int(self._fmt[:-1]) < 0: raise TypeError("Field is specified with negative length.") - def is_significant(self): return not self._fmt.endswith('x') @property def fmt_char(self): + """ + Gets the format character + """ return self._fmt[-1] def __len__(self): @@ -83,7 +85,7 @@ def __len__(self): raise TypeError("Field is scalar and has no len().") def __repr__(self): - if self._owner_type: + if self._owner_type: # pylint: disable=using-constant-test return "".format( self._name, self._owner_type, self._fmt ) @@ -132,6 +134,7 @@ def __get__(self, obj, type=None): def __set__(self, obj, value): obj._values[self._name] = value + class StringField(Field): """ Represents a field that is interpreted as a Python string. @@ -178,7 +181,11 @@ class Padding(Field): def __init__(self, n_bytes=1): super(Padding, self).__init__('{}x'.format(n_bytes)) + class HasFields(type): + """ + Metaclass used for NamedStruct + """ def __new__(mcs, name, bases, attrs): # Since this is a metaclass, the __new__ method observes # creation of new *classes* and not new instances. diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index e5574da80..ad6b9329c 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -24,11 +24,11 @@ from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList + # CLASSES ##################################################################### class _TekTDS224DataSource(OscilloscopeDataSource): - """ Class representing a data source (channel, math, or ref) on the Tektronix TDS 224. @@ -71,47 +71,42 @@ def read_waveform(self, bin_format=True): with self: if not bin_format: - self._tek.sendcmd('DAT:ENC ASCI') - # Set the data encoding format to ASCII - raw = self._tek.query('CURVE?') + self._tek.sendcmd("DAT:ENC ASCI") + # Set the data encoding format to ASCII + raw = self._tek.query("CURVE?") raw = raw.split(',') # Break up comma delimited string raw = map(float, raw) # Convert each list element to int raw = np.array(raw) # Convert into numpy array else: - self._tek.sendcmd('DAT:ENC RIB') - # Set encoding to signed, big-endian + self._tek.sendcmd("DAT:ENC RIB") + # Set encoding to signed, big-endian data_width = self._tek.data_width - self._tek.sendcmd('CURVE?') + self._tek.sendcmd("CURVE?") raw = self._tek.binblockread( data_width) # Read in the binary block, - # data width of 2 bytes + # data width of 2 bytes # pylint: disable=protected-access self._tek._file.flush_input() # Flush input buffer - yoffs = self._tek.query( - 'WFMP:{}:YOF?'.format(self.name)) # Retrieve Y offset - ymult = self._tek.query( - 'WFMP:{}:YMU?'.format(self.name)) # Retrieve Y multiply - yzero = self._tek.query( - 'WFMP:{}:YZE?'.format(self.name)) # Retrieve Y zero + yoffs = self._tek.query(f"WFMP:{self.name}:YOF?") # Retrieve Y offset + ymult = self._tek.query(f"WFMP:{self.name}:YMU?") # Retrieve Y multiply + yzero = self._tek.query(f"WFMP:{self.name}:YZE?") # Retrieve Y zero y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) - xzero = self._tek.query('WFMP:XZE?') # Retrieve X zero - xincr = self._tek.query('WFMP:XIN?') # Retrieve X incr - ptcnt = self._tek.query( - 'WFMP:{}:NR_P?'.format(self.name)) # Retrieve number - # of data - # points + xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero + xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr + ptcnt = self._tek.query(f"WFMP:{self.name}:NR_P?") # Retrieve number + # of data + # points x = np.arange(float(ptcnt)) * float(xincr) + float(xzero) - return (x, y) + return x, y class _TekTDS224Channel(_TekTDS224DataSource, OscilloscopeChannel): - """ Class representing a channel on the Tektronix TDS 224. @@ -122,7 +117,7 @@ class _TekTDS224Channel(_TekTDS224DataSource, OscilloscopeChannel): """ def __init__(self, parent, idx): - super(_TekTDS224Channel, self).__init__(parent, "CH{}".format(idx + 1)) + super(_TekTDS224Channel, self).__init__(parent, f"CH{idx + 1}") self._idx = idx + 1 @property @@ -133,19 +128,18 @@ def coupling(self): :type: `TekTDS224.Coupling` """ return TekTDS224.Coupling( - self._tek.query("CH{}:COUPL?".format(self._idx)) + self._tek.query(f'CH{self._idx}:COUPL?') ) @coupling.setter def coupling(self, newval): if not isinstance(newval, TekTDS224.Coupling): - raise TypeError("Coupling setting must be a `TekTDS224.Coupling`" - " value, got {} instead.".format(type(newval))) - self._tek.sendcmd("CH{}:COUPL {}".format(self._idx, newval.value)) + raise TypeError(f"Coupling setting must be a `TekTDS224.Coupling` value," + f"got {type(newval)} instead.") + self._tek.sendcmd(f"CH{self._idx}:COUPL {newval.value}") class TekTDS224(SCPIInstrument, Oscilloscope): - """ The Tektronix TDS224 is a multi-channel oscilloscope with analog bandwidths of 100MHz. @@ -205,10 +199,7 @@ def ref(self): :rtype: `_TekTDS224DataSource` """ - return ProxyList(self, - lambda s, idx: _TekTDS224DataSource( - s, "REF{}".format(idx + 1)), - range(4)) + return ProxyList(self, lambda s, idx: _TekTDS224DataSource(s, f"REF{idx + 1}"), range(4)) @property def math(self): @@ -238,7 +229,7 @@ def data_source(self, newval): newval = newval.value elif hasattr(newval, "name"): # Is a datasource with a name. newval = newval.name - self.sendcmd("DAT:SOU {}".format(newval)) + self.sendcmd(f"DAT:SOU {newval}") if not self._testing: time.sleep(0.01) # Let the instrument catch up. @@ -257,8 +248,7 @@ def data_width(self, newval): if int(newval) not in [1, 2]: raise ValueError("Only one or two byte-width is supported.") - self.sendcmd("DATA:WIDTH {}".format(newval)) + self.sendcmd(f"DATA:WIDTH {newval}") - @property def force_trigger(self): raise NotImplementedError diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index 91bf28cfa..62654c89b 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -461,7 +461,6 @@ def data_width(self, newval): self.sendcmd("DATA:WIDTH {}".format(newval)) - @property def force_trigger(self): raise NotImplementedError diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index 00198669b..860c6dbf5 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -16,6 +16,8 @@ # TESTS ###################################################################### +# pylint: disable=missing-function-docstring,redefined-outer-name,protected-access + @pytest.fixture def fg(): return ik.abstract_instruments.FunctionGenerator.open_test() diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index 425e03a19..e85bd905f 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -20,18 +20,18 @@ def test_agilent33220a_amplitude(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "VOLT:UNIT?", - "VOLT?", - "VOLT:UNIT VPP", - "VOLT 2.0", - "VOLT:UNIT DBM", - "VOLT 1.5" - ], [ - "VPP", - "+1.000000E+00" - ] + ik.agilent.Agilent33220a, + [ + "VOLT:UNIT?", + "VOLT?", + "VOLT:UNIT VPP", + "VOLT 2.0", + "VOLT:UNIT DBM", + "VOLT 1.5" + ], [ + "VPP", + "+1.000000E+00" + ] ) as fg: assert fg.amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) fg.amplitude = 2 * pq.V @@ -40,13 +40,13 @@ def test_agilent33220a_amplitude(): def test_agilent33220a_frequency(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FREQ?", - "FREQ 1.005000e+02" - ], [ - "+1.234000E+03" - ] + ik.agilent.Agilent33220a, + [ + "FREQ?", + "FREQ 1.005000e+02" + ], [ + "+1.234000E+03" + ] ) as fg: assert fg.frequency == 1234 * pq.Hz fg.frequency = 100.5 * pq.Hz @@ -54,13 +54,13 @@ def test_agilent33220a_frequency(): def test_agilent33220a_function(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FUNC?", - "FUNC:SQU" - ], [ - "SIN" - ] + ik.agilent.Agilent33220a, + [ + "FUNC?", + "FUNC:SQU" + ], [ + "SIN" + ] ) as fg: assert fg.function == fg.Function.sinusoid fg.function = fg.Function.square @@ -68,13 +68,13 @@ def test_agilent33220a_function(): def test_agilent33220a_offset(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "VOLT:OFFS?", - "VOLT:OFFS 4.321000e-01" - ], [ - "+1.234000E+01", - ] + ik.agilent.Agilent33220a, + [ + "VOLT:OFFS?", + "VOLT:OFFS 4.321000e-01" + ], [ + "+1.234000E+01", + ] ) as fg: assert fg.offset == 12.34 * pq.V fg.offset = 0.4321 * pq.V @@ -82,13 +82,13 @@ def test_agilent33220a_offset(): def test_agilent33220a_duty_cycle(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FUNC:SQU:DCYC?", - "FUNC:SQU:DCYC 75" - ], [ - "53", - ] + ik.agilent.Agilent33220a, + [ + "FUNC:SQU:DCYC?", + "FUNC:SQU:DCYC 75" + ], [ + "53", + ] ) as fg: assert fg.duty_cycle == 53 fg.duty_cycle = 75 @@ -96,13 +96,13 @@ def test_agilent33220a_duty_cycle(): def test_agilent33220a_ramp_symmetry(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FUNC:RAMP:SYMM?", - "FUNC:RAMP:SYMM 75" - ], [ - "53", - ] + ik.agilent.Agilent33220a, + [ + "FUNC:RAMP:SYMM?", + "FUNC:RAMP:SYMM 75" + ], [ + "53", + ] ) as fg: assert fg.ramp_symmetry == 53 fg.ramp_symmetry = 75 @@ -110,13 +110,13 @@ def test_agilent33220a_ramp_symmetry(): def test_agilent33220a_output(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP?", - "OUTP OFF" - ], [ - "ON", - ] + ik.agilent.Agilent33220a, + [ + "OUTP?", + "OUTP OFF" + ], [ + "ON", + ] ) as fg: assert fg.output is True fg.output = False @@ -124,13 +124,13 @@ def test_agilent33220a_output(): def test_agilent33220a_output_sync(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP:SYNC?", - "OUTP:SYNC OFF" - ], [ - "ON", - ] + ik.agilent.Agilent33220a, + [ + "OUTP:SYNC?", + "OUTP:SYNC OFF" + ], [ + "ON", + ] ) as fg: assert fg.output_sync is True fg.output_sync = False @@ -138,13 +138,13 @@ def test_agilent33220a_output_sync(): def test_agilent33220a_output_polarity(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP:POL?", - "OUTP:POL NORM" - ], [ - "INV", - ] + ik.agilent.Agilent33220a, + [ + "OUTP:POL?", + "OUTP:POL NORM" + ], [ + "INV", + ] ) as fg: assert fg.output_polarity == fg.OutputPolarity.inverted fg.output_polarity = fg.OutputPolarity.normal @@ -152,16 +152,16 @@ def test_agilent33220a_output_polarity(): def test_agilent33220a_load_resistance(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP:LOAD?", - "OUTP:LOAD?", - "OUTP:LOAD 100.0", - "OUTP:LOAD MAX" - ], [ - "50", - "INF" - ] + ik.agilent.Agilent33220a, + [ + "OUTP:LOAD?", + "OUTP:LOAD?", + "OUTP:LOAD 100.0", + "OUTP:LOAD MAX" + ], [ + "50", + "INF" + ] ) as fg: assert fg.load_resistance == 50 * pq.Ohm assert fg.load_resistance == fg.LoadResistance.high_impedance diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 301e657db..4ffbf910e 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -22,56 +22,56 @@ def test_agilent34410a_read(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "READ?" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+1.86850000E-03" - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + "READ?" + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + "+1.86850000E-03" + ] ) as dmm: unit_eq(dmm.read_meter(), +1.86850000E-03 * pq.volt) def test_agilent34410a_data_point_count(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:POIN?", - ], [ - "+215", - ] + ik.agilent.Agilent34410a, + [ + "DATA:POIN?", + ], [ + "+215", + ] ) as dmm: assert dmm.data_point_count == 215 def test_agilent34410a_r(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FORM:DATA REAL,64", - "R? 1" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - # pylint: disable=no-member - b"#18" + bytes.fromhex("3FF0000000000000") - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + "FORM:DATA REAL,64", + "R? 1" + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + # pylint: disable=no-member + b"#18" + bytes.fromhex("3FF0000000000000") + ] ) as dmm: unit_eq(dmm.r(1), np.array([1]) * pq.volt) def test_agilent34410a_fetch(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FETC?" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + "FETC?" + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + "+4.27150000E-03,5.27150000E-03" + ] ) as dmm: data = dmm.fetch() unit_eq(data[0], 4.27150000E-03 * pq.volt) @@ -80,15 +80,15 @@ def test_agilent34410a_fetch(): def test_agilent34410a_read_data(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FORM:DATA ASC", - "DATA:REM? 2" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + "FORM:DATA ASC", + "DATA:REM? 2" + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + "+4.27150000E-03,5.27150000E-03" + ] ) as dmm: data = dmm.read_data(2) unit_eq(data[0], 4.27150000E-03 * pq.volt) @@ -97,14 +97,14 @@ def test_agilent34410a_read_data(): def test_agilent34410a_read_data_nvmem(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "DATA:DATA? NVMEM", - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + "DATA:DATA? NVMEM", + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + "+4.27150000E-03,5.27150000E-03" + ] ) as dmm: data = dmm.read_data_nvmem() unit_eq(data[0], 4.27150000E-03 * pq.volt) @@ -113,11 +113,11 @@ def test_agilent34410a_read_data_nvmem(): def test_agilent34410a_read_last_data(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:LAST?", - ], [ - "+1.73730000E-03 VDC", - ] + ik.agilent.Agilent34410a, + [ + "DATA:LAST?", + ], [ + "+1.73730000E-03 VDC", + ] ) as dmm: unit_eq(dmm.read_last_data(), 1.73730000E-03 * pq.volt) diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index e97c36b1b..7644f352b 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -39,12 +39,12 @@ def test_instrument_binblockread(): with expected_protocol( - ik.Instrument, - [], - [ - b"#210" + bytes.fromhex("00000001000200030004") + b"0", - ], - sep="\n" + ik.Instrument, + [], + [ + b"#210" + bytes.fromhex("00000001000200030004") + b"0", + ], + sep="\n" ) as inst: np.testing.assert_array_equal(inst.binblockread(2), [0, 1, 2, 3, 4]) diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index 94d9eacfa..b01444c37 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -64,46 +64,46 @@ def test_mode(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - "rfemd 01 1", # 1 - "rfemd 01 2" # 2 - ], - init_response + - [ - "CR:Ack=0:RFEMD", # 1.1 - "ME:R:S#=01:DCC=010:PH=00000006020C0600", # 1.2 - "CR:Ack=0:RFEMD" # 2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + + [ + "rfemd 01 1", # 1 + "rfemd 01 2" # 2 + ], + init_response + + [ + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=00000006020C0600", # 1.2 + "CR:Ack=0:RFEMD" # 2 + ], + "\r" ) as inst: assert inst.mode == inst.Mode.voltage_dc def test_connect(): with expected_protocol( - ik.fluke.Fluke3000, - none_sequence + - [ - "ri", # 1 - "rfsm 1", # 2 - "rfdis", # 3 - ] + - init_sequence, - none_response + - [ - "CR:Ack=0:RI", # 1.1 - "SI:PON=Power On", # 1.2 - "RE:O", # 1.3 - "CR:Ack=0:RFSM:Radio On Master", # 2.1 - "RE:M", # 2.2 - "CR:Ack=0:RFDIS", # 3.1 - "ME:S", # 3.2 - "ME:D:010200000000", # 3.3 - ] + - init_response, - "\r" + ik.fluke.Fluke3000, + none_sequence + + [ + "ri", # 1 + "rfsm 1", # 2 + "rfdis", # 3 + ] + + init_sequence, + none_response + + [ + "CR:Ack=0:RI", # 1.1 + "SI:PON=Power On", # 1.2 + "RE:O", # 1.3 + "CR:Ack=0:RFSM:Radio On Master", # 2.1 + "RE:M", # 2.2 + "CR:Ack=0:RFDIS", # 3.1 + "ME:S", # 3.2 + "ME:D:010200000000", # 3.3 + ] + + init_response, + "\r" ) as inst: assert inst.positions[ik.fluke.Fluke3000.Module.m3000] == 1 assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 @@ -111,10 +111,10 @@ def test_connect(): def test_scan(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" ) as inst: assert inst.positions[ik.fluke.Fluke3000.Module.m3000] == 1 assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 @@ -122,43 +122,43 @@ def test_scan(): def test_reset(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - "ri", # 1 - "rfsm 1" # 2 - ], - init_response + - [ - "CR:Ack=0:RI", # 1.1 - "SI:PON=Power On", # 1.2 - "RE:O", # 1.3 - "CR:Ack=0:RFSM:Radio On Master", # 2.1 - "RE:M" # 2.2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + + [ + "ri", # 1 + "rfsm 1" # 2 + ], + init_response + + [ + "CR:Ack=0:RI", # 1.1 + "SI:PON=Power On", # 1.2 + "RE:O", # 1.3 + "CR:Ack=0:RFSM:Radio On Master", # 2.1 + "RE:M" # 2.2 + ], + "\r" ) as inst: inst.reset() def test_measure(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - "rfemd 01 1", # 1 - "rfemd 01 2", # 2 - "rfemd 02 0" # 3 - ], - init_response + - [ - "CR:Ack=0:RFEMD", # 1.1 - "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 - "CR:Ack=0:RFEMD", # 2 - "CR:Ack=0:RFEMD", # 3.1 - "ME:R:S#=02:DCC=010:PH=FD00C08207220000" # 3.2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + + [ + "rfemd 01 1", # 1 + "rfemd 01 2", # 2 + "rfemd 02 0" # 3 + ], + init_response + + [ + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + "CR:Ack=0:RFEMD", # 3.1 + "ME:R:S#=02:DCC=010:PH=FD00C08207220000" # 3.2 + ], + "\r" ) as inst: assert inst.measure(inst.Mode.voltage_dc) == 0.509 * pq.volt assert inst.measure(inst.Mode.temperature) == -25.3 * pq.celsius diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index d32456728..e2073f80d 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -20,19 +20,19 @@ def test_scpi_func_gen_amplitude(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "VOLT:UNIT?", - "VOLT?", - "VOLT:UNIT VPP", - "VOLT 2.0", - "VOLT:UNIT DBM", - "VOLT 1.5" - ], [ - "VPP", - "+1.000000E+00" - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + [ + "VOLT:UNIT?", + "VOLT?", + "VOLT:UNIT VPP", + "VOLT 2.0", + "VOLT:UNIT DBM", + "VOLT 1.5" + ], [ + "VPP", + "+1.000000E+00" + ], + repeat=2 ) as fg: assert fg.amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) fg.amplitude = 2 * pq.V @@ -45,14 +45,14 @@ def test_scpi_func_gen_amplitude(): def test_scpi_func_gen_frequency(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "FREQ?", - "FREQ 1.005000e+02" - ], [ - "+1.234000E+03" - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + [ + "FREQ?", + "FREQ 1.005000e+02" + ], [ + "+1.234000E+03" + ], + repeat=2 ) as fg: assert fg.frequency == 1234 * pq.Hz fg.frequency = 100.5 * pq.Hz @@ -63,14 +63,14 @@ def test_scpi_func_gen_frequency(): def test_scpi_func_gen_function(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "FUNC?", - "FUNC SQU" - ], [ - "SIN" - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + [ + "FUNC?", + "FUNC SQU" + ], [ + "SIN" + ], + repeat=2 ) as fg: assert fg.function == fg.Function.sinusoid fg.function = fg.Function.square @@ -81,14 +81,14 @@ def test_scpi_func_gen_function(): def test_scpi_func_gen_offset(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "VOLT:OFFS?", - "VOLT:OFFS 4.321000e-01" - ], [ - "+1.234000E+01", - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + [ + "VOLT:OFFS?", + "VOLT:OFFS 4.321000e-01" + ], [ + "+1.234000E+01", + ], + repeat=2 ) as fg: assert fg.offset == 12.34 * pq.V fg.offset = 0.4321 * pq.V diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index a65a55cd9..622863a00 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -20,13 +20,13 @@ def test_scpi_multimeter_mode(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", - "CONF:CURR:AC" - ], [ - "FRES +1.000000E+01,+3.000000E-06" - ] + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", + "CONF:CURR:AC" + ], [ + "FRES +1.000000E+01,+3.000000E-06" + ] ) as dmm: assert dmm.mode == dmm.Mode.fourpt_resistance dmm.mode = dmm.Mode.current_ac @@ -34,13 +34,13 @@ def test_scpi_multimeter_mode(): def test_scpi_multimeter_trigger_mode(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "TRIG:SOUR?", - "TRIG:SOUR EXT" - ], [ - "BUS" - ] + ik.generic_scpi.SCPIMultimeter, + [ + "TRIG:SOUR?", + "TRIG:SOUR EXT" + ], [ + "BUS" + ] ) as dmm: assert dmm.trigger_mode == dmm.TriggerMode.bus dmm.trigger_mode = dmm.TriggerMode.external @@ -48,20 +48,20 @@ def test_scpi_multimeter_trigger_mode(): def test_scpi_multimeter_input_range(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", # 1 - "CONF?", # 2 - "CONF?", # 3.1 - "CONF:FRES MIN", # 3.2 - "CONF?", # 4.1 - "CONF:CURR:DC 1.0" # 4.2 - ], [ - "CURR:AC +1.000000E+01,+3.000000E-06", # 1 - "CURR:AC AUTO,+3.000000E-06", # 2 - "FRES +1.000000E+01,+3.000000E-06", # 3 - "CURR:DC +1.000000E+01,+3.000000E-06" # 4 - ] + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", # 1 + "CONF?", # 2 + "CONF?", # 3.1 + "CONF:FRES MIN", # 3.2 + "CONF?", # 4.1 + "CONF:CURR:DC 1.0" # 4.2 + ], [ + "CURR:AC +1.000000E+01,+3.000000E-06", # 1 + "CURR:AC AUTO,+3.000000E-06", # 2 + "FRES +1.000000E+01,+3.000000E-06", # 3 + "CURR:DC +1.000000E+01,+3.000000E-06" # 4 + ] ) as dmm: unit_eq(dmm.input_range, 1e1 * pq.amp) assert dmm.input_range == dmm.InputRange.automatic @@ -71,20 +71,20 @@ def test_scpi_multimeter_input_range(): def test_scpi_multimeter_resolution(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", # 1 - "CONF?", # 2 - "CONF?", # 3.1 - "CONF:FRES +1.000000E+01,MIN", # 3.2 - "CONF?", # 4.1 - "CONF:CURR:DC +1.000000E+01,3e-06" # 4.2 - ], [ - "VOLT +1.000000E+01,+3.000000E-06", # 1 - "VOLT +1.000000E+01,MAX", # 2 - "FRES +1.000000E+01,+3.000000E-06", # 3 - "CURR:DC +1.000000E+01,+3.000000E-06" # 4 - ] + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", # 1 + "CONF?", # 2 + "CONF?", # 3.1 + "CONF:FRES +1.000000E+01,MIN", # 3.2 + "CONF?", # 4.1 + "CONF:CURR:DC +1.000000E+01,3e-06" # 4.2 + ], [ + "VOLT +1.000000E+01,+3.000000E-06", # 1 + "VOLT +1.000000E+01,MAX", # 2 + "FRES +1.000000E+01,+3.000000E-06", # 3 + "CURR:DC +1.000000E+01,+3.000000E-06" # 4 + ] ) as dmm: assert dmm.resolution == 3e-06 assert dmm.resolution == dmm.Resolution.maximum @@ -94,16 +94,16 @@ def test_scpi_multimeter_resolution(): def test_scpi_multimeter_trigger_count(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "TRIG:COUN?", - "TRIG:COUN?", - "TRIG:COUN MIN", - "TRIG:COUN 10" - ], [ - "+10", - "INF", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "TRIG:COUN?", + "TRIG:COUN?", + "TRIG:COUN MIN", + "TRIG:COUN 10" + ], [ + "+10", + "INF", + ] ) as dmm: assert dmm.trigger_count == 10 assert dmm.trigger_count == dmm.TriggerCount.infinity @@ -113,16 +113,16 @@ def test_scpi_multimeter_trigger_count(): def test_scpi_multimeter_sample_count(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "SAMP:COUN?", - "SAMP:COUN?", - "SAMP:COUN MIN", - "SAMP:COUN 10" - ], [ - "+10", - "MAX", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "SAMP:COUN?", + "SAMP:COUN?", + "SAMP:COUN MIN", + "SAMP:COUN 10" + ], [ + "+10", + "MAX", + ] ) as dmm: assert dmm.sample_count == 10 assert dmm.sample_count == dmm.SampleCount.maximum @@ -132,13 +132,13 @@ def test_scpi_multimeter_sample_count(): def test_scpi_multimeter_trigger_delay(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "TRIG:DEL?", - "TRIG:DEL {:e}".format(1), - ], [ - "+1", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "TRIG:DEL?", + f"TRIG:DEL {1:e}", + ], [ + "+1", + ] ) as dmm: unit_eq(dmm.trigger_delay, 1 * pq.second) dmm.trigger_delay = 1000 * pq.millisecond @@ -146,13 +146,13 @@ def test_scpi_multimeter_trigger_delay(): def test_scpi_multimeter_sample_source(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "SAMP:SOUR?", - "SAMP:SOUR TIM", - ], [ - "IMM", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "SAMP:SOUR?", + "SAMP:SOUR TIM", + ], [ + "IMM", + ] ) as dmm: assert dmm.sample_source == dmm.SampleSource.immediate dmm.sample_source = dmm.SampleSource.timer @@ -160,13 +160,13 @@ def test_scpi_multimeter_sample_source(): def test_scpi_multimeter_sample_timer(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "SAMP:TIM?", - "SAMP:TIM {:e}".format(1), - ], [ - "+1", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "SAMP:TIM?", + f"SAMP:TIM {1:e}", + ], [ + "+1", + ] ) as dmm: unit_eq(dmm.sample_timer, 1 * pq.second) dmm.sample_timer = 1000 * pq.millisecond @@ -174,11 +174,11 @@ def test_scpi_multimeter_sample_timer(): def test_scpi_multimeter_measure(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "MEAS:VOLT:DC?", - ], [ - "+4.23450000E-03", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "MEAS:VOLT:DC?", + ], [ + "+4.23450000E-03", + ] ) as dmm: unit_eq(dmm.measure(dmm.Mode.voltage_dc), 4.2345e-03 * pq.volt) diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index 4eab974d2..e048571ad 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -29,10 +29,10 @@ def set_defaults(inst): def test_channel(): with expected_protocol( - ik.glassman.GlassmanFR, - [], - [], - "\r" + ik.glassman.GlassmanFR, + [], + [], + "\r" ) as inst: assert len(inst.channel) == 1 assert inst.channel[0] == inst @@ -40,16 +40,16 @@ def test_channel(): def test_voltage(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51", - "\x01S3330000000001CD" - ], - [ - "R00000000000040", - "A" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01Q51", + "\x01S3330000000001CD" + ], + [ + "R00000000000040", + "A" + ], + "\r" ) as inst: set_defaults(inst) inst.voltage = 10.0 * pq.kilovolt @@ -58,16 +58,16 @@ def test_voltage(): def test_current(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51", - "\x01S0003330000001CD" - ], - [ - "R00000000000040", - "A" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01Q51", + "\x01S0003330000001CD" + ], + [ + "R00000000000040", + "A" + ], + "\r" ) as inst: set_defaults(inst) inst.current = 1.2 * pq.milliamp @@ -76,14 +76,14 @@ def test_current(): def test_voltage_sense(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51" - ], - [ - "R10A00000010053" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01Q51" + ], + [ + "R10A00000010053" + ], + "\r" ) as inst: set_defaults(inst) assert round(inst.voltage_sense) == 13.0 * pq.kilovolt @@ -91,14 +91,14 @@ def test_voltage_sense(): def test_current_sense(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51" - ], - [ - "R0001550001004C" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01Q51" + ], + [ + "R0001550001004C" + ], + "\r" ) as inst: set_defaults(inst) assert inst.current_sense == 2.0 * pq.milliamp @@ -106,16 +106,16 @@ def test_current_sense(): def test_mode(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51", - "\x01Q51" - ], - [ - "R00000000000040", - "R00000000010041" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01Q51", + "\x01Q51" + ], + [ + "R00000000000040", + "R00000000010041" + ], + "\r" ) as inst: assert inst.mode == inst.Mode.voltage assert inst.mode == inst.Mode.current @@ -123,20 +123,20 @@ def test_mode(): def test_output(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01S0000000000001C4", - "\x01Q51", - "\x01S0000000000002C5", - "\x01Q51" - ], - [ - "A", - "R00000000000040", - "A", - "R00000000040044" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01S0000000000001C4", + "\x01Q51", + "\x01S0000000000002C5", + "\x01Q51" + ], + [ + "A", + "R00000000000040", + "A", + "R00000000040044" + ], + "\r" ) as inst: inst.output = False assert not inst.output @@ -146,30 +146,30 @@ def test_output(): def test_version(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01V56" - ], - [ - "B1465" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01V56" + ], + [ + "B1465" + ], + "\r" ) as inst: assert inst.version == "14" def test_device_timeout(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01C073", - "\x01C174" - ], - [ - "A", - "A" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01C073", + "\x01C174" + ], + [ + "A", + "A" + ], + "\r" ) as inst: inst.device_timeout = True assert inst.device_timeout @@ -179,56 +179,56 @@ def test_device_timeout(): def test_sendcmd(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01123ABC5C" - ], - [], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01123ABC5C" + ], + [], + "\r" ) as inst: inst.sendcmd("123ABC") def test_query(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q123ABCAD" - ], - [ - "R123ABC5C" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01Q123ABCAD" + ], + [ + "R123ABC5C" + ], + "\r" ) as inst: inst.query("Q123ABC") def test_reset(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01S0000000000004C7" - ], - [ - "A" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01S0000000000004C7" + ], + [ + "A" + ], + "\r" ) as inst: inst.reset() def test_set_status(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01S3333330000002D7", - "\x01Q51" - ], - [ - "A", - "R00000000040044" - ], - "\r" + ik.glassman.GlassmanFR, + [ + "\x01S3333330000002D7", + "\x01Q51" + ], + [ + "A", + "R00000000040044" + ], + "\r" ) as inst: set_defaults(inst) inst.set_status(voltage=10*pq.kilovolt, current=1.2*pq.milliamp, output=True) diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index 4245b92d2..71d0e5217 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -22,44 +22,44 @@ def test_hs9000_name(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:IDN?" - ], - [ - ":CH1:CH2:FOO", - "Foobar name" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:IDN?" + ], + [ + ":CH1:CH2:FOO", + "Foobar name" + ], + sep="\n" ) as hs: assert hs.name == "Foobar name" def test_channel_idx_list(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ], - [ - ":CH1:CH2:FOO" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ], + [ + ":CH1:CH2:FOO" + ], + sep="\n" ) as hs: assert hs._channel_idxs() == [0, 1, "FOO"] def test_channel_returns_inner_class(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ], - [ - ":CH1:CH2:FOO" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ], + [ + ":CH1:CH2:FOO" + ], + sep="\n" ) as hs: channel = hs.channel[0] assert isinstance(channel, hs.Channel) is True @@ -107,16 +107,16 @@ def test_channel_save_state(): def test_channel_temperature(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:TEMP?" - ], - [ - ":CH1:CH2:FOO", - "10 C" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:TEMP?" + ], + [ + ":CH1:CH2:FOO", + "10 C" + ], + sep="\n" ) as hs: channel = hs.channel[0] assert channel.temperature == 10 * pq.degC @@ -124,20 +124,20 @@ def test_channel_temperature(): def test_channel_frequency_getter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:FREQ?", - ":CH1:FREQ:MIN?", - ":CH1:FREQ:MAX?" - ], - [ - ":CH1:CH2:FOO", - "1000 MHz", - "100 MHz", - "10 GHz" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:FREQ?", + ":CH1:FREQ:MIN?", + ":CH1:FREQ:MAX?" + ], + [ + ":CH1:CH2:FOO", + "1000 MHz", + "100 MHz", + "10 GHz" + ], + sep="\n" ) as hs: channel = hs.channel[0] assert channel.frequency == 1 * pq.GHz @@ -147,19 +147,19 @@ def test_channel_frequency_getter(): def test_channel_frequency_setter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:FREQ:MIN?", - ":CH1:FREQ:MAX?", - ":CH1:FREQ {:e}".format(1) - ], - [ - ":CH1:CH2:FOO", - "100 MHz", - "10 GHz" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:FREQ:MIN?", + ":CH1:FREQ:MAX?", + ":CH1:FREQ {:e}".format(1) + ], + [ + ":CH1:CH2:FOO", + "100 MHz", + "10 GHz" + ], + sep="\n" ) as hs: channel = hs.channel[0] channel.frequency = 1 * pq.GHz @@ -167,20 +167,20 @@ def test_channel_frequency_setter(): def test_channel_power_getter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PWR?", - ":CH1:PWR:MIN?", - ":CH1:PWR:MAX?" - ], - [ - ":CH1:CH2:FOO", - "0", - "-100", - "20" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:PWR?", + ":CH1:PWR:MIN?", + ":CH1:PWR:MAX?" + ], + [ + ":CH1:CH2:FOO", + "0", + "-100", + "20" + ], + sep="\n" ) as hs: channel = hs.channel[0] assert channel.power == 0 * dBm @@ -190,19 +190,19 @@ def test_channel_power_getter(): def test_channel_power_setter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PWR:MIN?", - ":CH1:PWR:MAX?", - ":CH1:PWR {:e}".format(0) - ], - [ - ":CH1:CH2:FOO", - "-100", - "20" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:PWR:MIN?", + ":CH1:PWR:MAX?", + ":CH1:PWR {:e}".format(0) + ], + [ + ":CH1:CH2:FOO", + "-100", + "20" + ], + sep="\n" ) as hs: channel = hs.channel[0] channel.power = 0 * dBm @@ -210,20 +210,20 @@ def test_channel_power_setter(): def test_channel_phase_getter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PHASE?", - ":CH1:PHASE:MIN?", - ":CH1:PHASE:MAX?" - ], - [ - ":CH1:CH2:FOO", - "0", - "-180", - "+180" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:PHASE?", + ":CH1:PHASE:MIN?", + ":CH1:PHASE:MAX?" + ], + [ + ":CH1:CH2:FOO", + "0", + "-180", + "+180" + ], + sep="\n" ) as hs: channel = hs.channel[0] assert channel.phase == 0 * pq.degree @@ -233,19 +233,19 @@ def test_channel_phase_getter(): def test_channel_phase_setter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PHASE:MIN?", - ":CH1:PHASE:MAX?", - ":CH1:PHASE {:e}".format(0) - ], - [ - ":CH1:CH2:FOO", - "-180", - "+180" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:PHASE:MIN?", + ":CH1:PHASE:MAX?", + ":CH1:PHASE {:e}".format(0) + ], + [ + ":CH1:CH2:FOO", + "-180", + "+180" + ], + sep="\n" ) as hs: channel = hs.channel[0] channel.phase = 0 * pq.degree @@ -253,18 +253,18 @@ def test_channel_phase_setter(): def test_channel_output(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PWR:RF?", - ":CH1:PWR:RF:ON", - ":CH1:PWR:RF:OFF" - ], - [ - ":CH1:CH2:FOO", - "OFF" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ":CH1:PWR:RF?", + ":CH1:PWR:RF:ON", + ":CH1:PWR:RF:OFF" + ], + [ + ":CH1:CH2:FOO", + "OFF" + ], + sep="\n" ) as hs: channel = hs.channel[0] assert channel.output is False @@ -274,16 +274,16 @@ def test_channel_output(): def test_hs9000_is_ready(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":COMM:READY?", - ":COMM:READY?" - ], - [ - "Ready", - "DANGER DANGER" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":COMM:READY?", + ":COMM:READY?" + ], + [ + "Ready", + "DANGER DANGER" + ], + sep="\n" ) as hs: assert hs.ready is True assert hs.ready is False diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index 555a8c459..94b2ccce5 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -22,21 +22,20 @@ def test_hp3456a_trigger_mode(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "T4", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "T4", + ], [ + "" + ], + sep="\r" ) as dmm: dmm.trigger_mode = dmm.TriggerMode.hold def test_hp3456a_number_of_digits(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP3456a, [ "HO0T4SO1", @@ -46,21 +45,21 @@ def test_hp3456a_number_of_digits(): "+06.00000E+0" ], sep="\r" - ) as dmm: - dmm.number_of_digits = 7 + ) as dmm: + dmm.number_of_digits = 7 def test_hp3456a_number_of_digits_invalid(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W6STG", - "REG" - ], [ - "+06.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "W6STG", + "REG" + ], [ + "+06.00000E+0" + ], + sep="\r" ) as dmm: dmm.number_of_digits = 6 assert dmm.number_of_digits == 6 @@ -68,29 +67,29 @@ def test_hp3456a_number_of_digits_invalid(): def test_hp3456a_auto_range(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "R1W", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "R1W", + ], [ + "" + ], + sep="\r" ) as dmm: dmm.auto_range() def test_hp3456a_number_of_readings(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W10STN", - "REN" - ], [ - "+10.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "W10STN", + "REN" + ], [ + "+10.00000E+0" + ], + sep="\r" ) as dmm: dmm.number_of_readings = 10 assert dmm.number_of_readings == 10 @@ -98,23 +97,22 @@ def test_hp3456a_number_of_readings(): def test_hp3456a_nplc(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W1STI", - "REI" - ], [ - "+1.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "W1STI", + "REI" + ], [ + "+1.00000E+0" + ], + sep="\r" ) as dmm: dmm.nplc = 1 assert dmm.nplc == 1 def test_hp3456a_nplc_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP3456a, [ "HO0T4SO1", @@ -124,63 +122,63 @@ def test_hp3456a_nplc_invalid(): "+1.00000E+0" ], sep="\r" - ) as dmm: - dmm.nplc = 0 + ) as dmm: + dmm.nplc = 0 def test_hp3456a_mode(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "S0F4", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "S0F4", + ], [ + "" + ], + sep="\r" ) as dmm: dmm.mode = dmm.Mode.resistance_2wire def test_hp3456a_math_mode(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "M2", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "M2", + ], [ + "" + ], + sep="\r" ) as dmm: dmm.math_mode = dmm.MathMode.statistic def test_hp3456a_trigger(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "T3", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "T3", + ], [ + "" + ], + sep="\r" ) as dmm: dmm.trigger() def test_hp3456a_fetch(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1" - ], - [ - "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0", - "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1" + ], + [ + "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0", + "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0" + ], + sep="\r" ) as dmm: v = dmm.fetch(dmm.Mode.resistance_2wire) np.testing.assert_array_equal( @@ -196,57 +194,57 @@ def test_hp3456a_fetch(): def test_hp3456a_variance(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REV", - ], [ - "+04.93111E-6" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REV", + ], [ + "+04.93111E-6" + ], + sep="\r" ) as dmm: assert dmm.variance == +04.93111E-6 def test_hp3456a_count(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REC", - ], [ - "+10.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REC", + ], [ + "+10.00000E+0" + ], + sep="\r" ) as dmm: assert dmm.count == +10 def test_hp3456a_mean(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REM", - ], [ - "+102.1000E-3" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REM", + ], [ + "+102.1000E-3" + ], + sep="\r" ) as dmm: assert dmm.mean == +102.1000E-3 def test_hp3456a_delay(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "RED", - "W1.0STD" - ], [ - "-000.0000E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "RED", + "W1.0STD" + ], [ + "-000.0000E+0" + ], + sep="\r" ) as dmm: assert dmm.delay == 0 dmm.delay = 1 * pq.sec @@ -254,15 +252,15 @@ def test_hp3456a_delay(): def test_hp3456a_lower(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REL", - "W0.0993STL" - ], [ - "+099.3000E-3" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REL", + "W0.0993STL" + ], [ + "+099.3000E-3" + ], + sep="\r" ) as dmm: assert dmm.lower == +099.3000E-3 dmm.lower = +099.3000E-3 @@ -270,15 +268,15 @@ def test_hp3456a_lower(): def test_hp3456a_upper(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REU", - "W0.1055STU" - ], [ - "+105.5000E-3" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REU", + "W0.1055STU" + ], [ + "+105.5000E-3" + ], + sep="\r" ) as dmm: assert dmm.upper == +105.5000E-3 dmm.upper = +105.5000E-3 @@ -286,21 +284,21 @@ def test_hp3456a_upper(): def test_hp3456a_ryz(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "RER", - "REY", - "REZ", - "W600.0STR", - "W1.0STY", - "W0.1055STZ" - ], [ - "+0600.000E+0", - "+1.000000E+0", - "+105.5000E-3" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "RER", + "REY", + "REZ", + "W600.0STR", + "W1.0STY", + "W0.1055STZ" + ], [ + "+0600.000E+0", + "+1.000000E+0", + "+105.5000E-3" + ], + sep="\r" ) as dmm: assert dmm.r == +0600.000E+0 assert dmm.y == +1.000000E+0 @@ -312,21 +310,21 @@ def test_hp3456a_ryz(): def test_hp3456a_measure(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "S1F1W1STNT3", - "S0F4W1STNT3", - "S0F1W1STNT3", - "W1STNT3" - ], - [ - "+00.00000E-3", - "+000.1010E+0", - "+000.0002E-3", - "+000.0002E-3" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "S1F1W1STNT3", + "S0F4W1STNT3", + "S0F1W1STNT3", + "W1STNT3" + ], + [ + "+00.00000E-3", + "+000.1010E+0", + "+000.0002E-3", + "+000.0002E-3" + ], + sep="\r" ) as dmm: assert dmm.measure(dmm.Mode.ratio_dcv_dcv) == 0 assert dmm.measure(dmm.Mode.resistance_2wire) == +000.1010E+0 * pq.ohm @@ -336,75 +334,71 @@ def test_hp3456a_measure(): def test_hp3456a_input_range(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "R2W", - "R3W" - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "R2W", + "R3W" + ], [ + "" + ], + sep="\r" ) as dmm: dmm.input_range = 10 ** -1 * pq.volt dmm.input_range = 1e3 * pq.ohm def test_hp3456a_input_range_invalid_str(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm.input_range = "derp" + ) as dmm: + dmm.input_range = "derp" def test_hp3456a_input_range_invalid_range(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm.input_range = 1 * pq.ohm + ) as dmm: + dmm.input_range = 1 * pq.ohm def test_hp3456a_input_range_bad_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm.input_range = True + ) as dmm: + dmm.input_range = True def test_hp3456a_input_range_bad_units(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm.input_range = 1 * pq.amp + ) as dmm: + dmm.input_range = 1 * pq.amp def test_hp3456a_relative(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "M0", - "M3", - ], [ - "", - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "M0", + "M3", + ], [ + "", + ], + sep="\r" ) as dmm: dmm.relative = False dmm.relative = True @@ -412,27 +406,26 @@ def test_hp3456a_relative(): def test_hp3456a_relative_bad_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm.relative = "derp" + ) as dmm: + dmm.relative = "derp" def test_hp3456a_auto_zero(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "Z0", - "Z1", - ], [ - "", - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "Z0", + "Z1", + ], [ + "", + ], + sep="\r" ) as dmm: dmm.autozero = False dmm.autozero = True @@ -440,48 +433,45 @@ def test_hp3456a_auto_zero(): def test_hp3456a_filter(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "FL0", - "FL1", - ], [ - "", - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "FL0", + "FL1", + ], [ + "", + ], + sep="\r" ) as dmm: dmm.filter = False dmm.filter = True def test_hp3456a_register_read_bad_name(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm._register_read("foobar") + ) as dmm: + dmm._register_read("foobar") def test_hp3456a_register_write_bad_name(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm._register_write("foobar", 1) + ) as dmm: + dmm._register_write("foobar", 1) def test_hp3456a_register_write_bad_register(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP3456a, [], [], sep="\r" - ) as dmm: - dmm._register_write(dmm.Register.mean, 1) + ) as dmm: + dmm._register_write(dmm.Register.mean, 1) diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 215874865..08ff94f99 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -22,10 +22,10 @@ def test_channel_returns_inner_class(): with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" + ik.hp.HP6624a, + [], + [], + sep="\n" ) as hp: channel = hp.channel[0] assert isinstance(channel, hp.Channel) is True @@ -60,15 +60,15 @@ def test_channel_query(): def test_channel_voltage(): with expected_protocol( - ik.hp.HP6624a, - [ - "VSET? 1", - "VSET 1,{:.1f}".format(5) - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, + [ + "VSET? 1", + "VSET 1,{:.1f}".format(5) + ], + [ + "2" + ], + sep="\n" ) as hp: assert hp.channel[0].voltage == 2 * pq.V hp.channel[0].voltage = 5 * pq.V @@ -76,15 +76,15 @@ def test_channel_voltage(): def test_channel_current(): with expected_protocol( - ik.hp.HP6624a, - [ - "ISET? 1", - "ISET 1,{:.1f}".format(5) - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, + [ + "ISET? 1", + "ISET 1,{:.1f}".format(5) + ], + [ + "2" + ], + sep="\n" ) as hp: assert hp.channel[0].current == 2 * pq.amp hp.channel[0].current = 5 * pq.amp @@ -92,43 +92,43 @@ def test_channel_current(): def test_channel_voltage_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "VOUT? 1" - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, + [ + "VOUT? 1" + ], + [ + "2" + ], + sep="\n" ) as hp: assert hp.channel[0].voltage_sense == 2 * pq.V def test_channel_current_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "IOUT? 1", - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, + [ + "IOUT? 1", + ], + [ + "2" + ], + sep="\n" ) as hp: assert hp.channel[0].current_sense == 2 * pq.A def test_channel_overvoltage(): with expected_protocol( - ik.hp.HP6624a, - [ - "OVSET? 1", - "OVSET 1,{:.1f}".format(5) - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, + [ + "OVSET? 1", + f"OVSET 1,{5:.1f}" + ], + [ + "2" + ], + sep="\n" ) as hp: assert hp.channel[0].overvoltage == 2 * pq.V hp.channel[0].overvoltage = 5 * pq.V @@ -136,15 +136,15 @@ def test_channel_overvoltage(): def test_channel_overcurrent(): with expected_protocol( - ik.hp.HP6624a, - [ - "OVP? 1", - "OVP 1,1" - ], - [ - "1" - ], - sep="\n" + ik.hp.HP6624a, + [ + "OVP? 1", + "OVP 1,1" + ], + [ + "1" + ], + sep="\n" ) as hp: assert hp.channel[0].overcurrent is True hp.channel[0].overcurrent = True @@ -152,15 +152,15 @@ def test_channel_overcurrent(): def test_channel_output(): with expected_protocol( - ik.hp.HP6624a, - [ - "OUT? 1", - "OUT 1,1" - ], - [ - "1" - ], - sep="\n" + ik.hp.HP6624a, + [ + "OUT? 1", + "OUT 1,1" + ], + [ + "1" + ], + sep="\n" ) as hp: assert hp.channel[0].output is True hp.channel[0].output = True @@ -176,30 +176,30 @@ def test_channel_reset(): def test_all_voltage(): with expected_protocol( - ik.hp.HP6624a, - [ - "VSET? 1", - "VSET? 2", - "VSET? 3", - "VSET? 4", - - "VSET 1,{:.1f}".format(5), - "VSET 2,{:.1f}".format(5), - "VSET 3,{:.1f}".format(5), - "VSET 4,{:.1f}".format(5), - - "VSET 1,{:.1f}".format(1), - "VSET 2,{:.1f}".format(2), - "VSET 3,{:.1f}".format(3), - "VSET 4,{:.1f}".format(4) - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + [ + "VSET? 1", + "VSET? 2", + "VSET? 3", + "VSET? 4", + + "VSET 1,{:.1f}".format(5), + "VSET 2,{:.1f}".format(5), + "VSET 3,{:.1f}".format(5), + "VSET 4,{:.1f}".format(5), + + "VSET 1,{:.1f}".format(1), + "VSET 2,{:.1f}".format(2), + "VSET 3,{:.1f}".format(3), + "VSET 4,{:.1f}".format(4) + ], + [ + "2", + "3", + "4", + "5" + ], + sep="\n" ) as hp: assert sorted(hp.voltage) == sorted((2, 3, 4, 5) * pq.V) hp.voltage = 5 * pq.V @@ -207,42 +207,41 @@ def test_all_voltage(): def test_all_voltage_wrong_length(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP6624a, [], [], sep="\n" - ) as hp: - hp.voltage = (1 * pq.volt, 2 * pq.volt) + ) as hp: + hp.voltage = (1 * pq.volt, 2 * pq.volt) def test_all_current(): with expected_protocol( - ik.hp.HP6624a, - [ - "ISET? 1", - "ISET? 2", - "ISET? 3", - "ISET? 4", - - "ISET 1,{:.1f}".format(5), - "ISET 2,{:.1f}".format(5), - "ISET 3,{:.1f}".format(5), - "ISET 4,{:.1f}".format(5), - - "ISET 1,{:.1f}".format(1), - "ISET 2,{:.1f}".format(2), - "ISET 3,{:.1f}".format(3), - "ISET 4,{:.1f}".format(4) - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + [ + "ISET? 1", + "ISET? 2", + "ISET? 3", + "ISET? 4", + + "ISET 1,{:.1f}".format(5), + "ISET 2,{:.1f}".format(5), + "ISET 3,{:.1f}".format(5), + "ISET 4,{:.1f}".format(5), + + "ISET 1,{:.1f}".format(1), + "ISET 2,{:.1f}".format(2), + "ISET 3,{:.1f}".format(3), + "ISET 4,{:.1f}".format(4) + ], + [ + "2", + "3", + "4", + "5" + ], + sep="\n" ) as hp: assert sorted(hp.current) == sorted((2, 3, 4, 5) * pq.A) hp.current = 5 * pq.A @@ -250,96 +249,93 @@ def test_all_current(): def test_all_current_wrong_length(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP6624a, [], [], sep="\n" - ) as hp: - hp.current = (1 * pq.amp, 2 * pq.amp) + ) as hp: + hp.current = (1 * pq.amp, 2 * pq.amp) def test_all_voltage_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "VOUT? 1", - "VOUT? 2", - "VOUT? 3", - "VOUT? 4" - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + [ + "VOUT? 1", + "VOUT? 2", + "VOUT? 3", + "VOUT? 4" + ], + [ + "2", + "3", + "4", + "5" + ], + sep="\n" ) as hp: assert sorted(hp.voltage_sense) == sorted((2, 3, 4, 5) * pq.V) def test_all_current_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "IOUT? 1", - "IOUT? 2", - "IOUT? 3", - "IOUT? 4" - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + [ + "IOUT? 1", + "IOUT? 2", + "IOUT? 3", + "IOUT? 4" + ], + [ + "2", + "3", + "4", + "5" + ], + sep="\n" ) as hp: assert sorted(hp.current_sense) == sorted((2, 3, 4, 5) * pq.A) def test_clear(): with expected_protocol( - ik.hp.HP6624a, - [ - "CLR" - ], - [], - sep="\n" + ik.hp.HP6624a, + [ + "CLR" + ], + [], + sep="\n" ) as hp: hp.clear() def test_channel_count(): with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" + ik.hp.HP6624a, + [], + [], + sep="\n" ) as hp: assert hp.channel_count == 4 hp.channel_count = 3 def test_channel_count_wrong_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.hp.HP6624a, [], [], sep="\n" - ) as hp: - hp.channel_count = "foobar" + ) as hp: + hp.channel_count = "foobar" def test_channel_count_too_small(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.hp.HP6624a, [], [], sep="\n" - ) as hp: - hp.channel_count = 0 + ) as hp: + hp.channel_count = 0 diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index afe215085..a181019fa 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -20,13 +20,13 @@ def test_hp6632b_display_textmode(): with expected_protocol( - ik.hp.HP6632b, - [ - "DISP:MODE?", - "DISP:MODE TEXT" - ], [ - "NORM" - ] + ik.hp.HP6632b, + [ + "DISP:MODE?", + "DISP:MODE TEXT" + ], [ + "NORM" + ] ) as psu: assert psu.display_textmode is False psu.display_textmode = True @@ -34,12 +34,12 @@ def test_hp6632b_display_textmode(): def test_hp6632b_display_text(): with expected_protocol( - ik.hp.HP6632b, - [ - 'DISP:TEXT "TEST"', - 'DISP:TEXT "TEST AAAAAAAAAA"' - ], - [] + ik.hp.HP6632b, + [ + 'DISP:TEXT "TEST"', + 'DISP:TEXT "TEST AAAAAAAAAA"' + ], + [] ) as psu: assert psu.display_text("TEST") == "TEST" assert psu.display_text("TEST AAAAAAAAAAAAAAAA") == "TEST AAAAAAAAAA" @@ -47,13 +47,13 @@ def test_hp6632b_display_text(): def test_hp6632b_output(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP?", - "OUTP 1" - ], [ - "0" - ] + ik.hp.HP6632b, + [ + "OUTP?", + "OUTP 1" + ], [ + "0" + ] ) as psu: assert psu.output is False psu.output = True @@ -61,13 +61,13 @@ def test_hp6632b_output(): def test_hp6632b_voltage(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT?", - "VOLT {:e}".format(1) - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "VOLT?", + "VOLT {:e}".format(1) + ], [ + "10.0" + ] ) as psu: unit_eq(psu.voltage, 10 * pq.volt) psu.voltage = 1.0 * pq.volt @@ -75,25 +75,25 @@ def test_hp6632b_voltage(): def test_hp6632b_voltage_sense(): with expected_protocol( - ik.hp.HP6632b, - [ - "MEAS:VOLT?", - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "MEAS:VOLT?", + ], [ + "10.0" + ] ) as psu: unit_eq(psu.voltage_sense, 10 * pq.volt) def test_hp6632b_overvoltage(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT:PROT?", - "VOLT:PROT {:e}".format(1) - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "VOLT:PROT?", + "VOLT:PROT {:e}".format(1) + ], [ + "10.0" + ] ) as psu: unit_eq(psu.overvoltage, 10 * pq.volt) psu.overvoltage = 1.0 * pq.volt @@ -101,13 +101,13 @@ def test_hp6632b_overvoltage(): def test_hp6632b_current(): with expected_protocol( - ik.hp.HP6632b, - [ - "CURR?", - "CURR {:e}".format(1) - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "CURR?", + "CURR {:e}".format(1) + ], [ + "10.0" + ] ) as psu: unit_eq(psu.current, 10 * pq.amp) psu.current = 1.0 * pq.amp @@ -115,25 +115,25 @@ def test_hp6632b_current(): def test_hp6632b_current_sense(): with expected_protocol( - ik.hp.HP6632b, - [ - "MEAS:CURR?", - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "MEAS:CURR?", + ], [ + "10.0" + ] ) as psu: unit_eq(psu.current_sense, 10 * pq.amp) def test_hp6632b_overcurrent(): with expected_protocol( - ik.hp.HP6632b, - [ - "CURR:PROT:STAT?", - "CURR:PROT:STAT 1" - ], [ - "0" - ] + ik.hp.HP6632b, + [ + "CURR:PROT:STAT?", + "CURR:PROT:STAT 1" + ], [ + "0" + ] ) as psu: assert psu.overcurrent is False psu.overcurrent = True @@ -141,13 +141,13 @@ def test_hp6632b_overcurrent(): def test_hp6632b_current_sense_range(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:CURR:RANGE?", - "SENS:CURR:RANGE {:e}".format(1) - ], [ - "0.05" - ] + ik.hp.HP6632b, + [ + "SENS:CURR:RANGE?", + "SENS:CURR:RANGE {:e}".format(1) + ], [ + "0.05" + ] ) as psu: unit_eq(psu.current_sense_range, 0.05 * pq.amp) psu.current_sense_range = 1 * pq.amp @@ -155,13 +155,13 @@ def test_hp6632b_current_sense_range(): def test_hp6632b_output_dfi_source(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP:DFI:SOUR?", - "OUTP:DFI:SOUR QUES" - ], [ - "OPER" - ] + ik.hp.HP6632b, + [ + "OUTP:DFI:SOUR?", + "OUTP:DFI:SOUR QUES" + ], [ + "OPER" + ] ) as psu: assert psu.output_dfi_source == psu.DFISource.operation psu.output_dfi_source = psu.DFISource.questionable @@ -169,13 +169,13 @@ def test_hp6632b_output_dfi_source(): def test_hp6632b_output_remote_inhibit(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP:RI:MODE?", - "OUTP:RI:MODE LATC" - ], [ - "LIVE" - ] + ik.hp.HP6632b, + [ + "OUTP:RI:MODE?", + "OUTP:RI:MODE LATC" + ], [ + "LIVE" + ] ) as psu: assert psu.output_remote_inhibit == psu.RemoteInhibit.live psu.output_remote_inhibit = psu.RemoteInhibit.latching @@ -183,13 +183,13 @@ def test_hp6632b_output_remote_inhibit(): def test_hp6632b_digital_function(): with expected_protocol( - ik.hp.HP6632b, - [ - "DIG:FUNC?", - "DIG:FUNC DIG" - ], [ - "RIDF" - ] + ik.hp.HP6632b, + [ + "DIG:FUNC?", + "DIG:FUNC DIG" + ], [ + "RIDF" + ] ) as psu: assert psu.digital_function == psu.DigitalFunction.remote_inhibit psu.digital_function = psu.DigitalFunction.data @@ -197,13 +197,13 @@ def test_hp6632b_digital_function(): def test_hp6632b_digital_data(): with expected_protocol( - ik.hp.HP6632b, - [ - "DIG:DATA?", - "DIG:DATA 1" - ], [ - "5" - ] + ik.hp.HP6632b, + [ + "DIG:DATA?", + "DIG:DATA 1" + ], [ + "5" + ] ) as psu: assert psu.digital_data == 5 psu.digital_data = 1 @@ -211,13 +211,13 @@ def test_hp6632b_digital_data(): def test_hp6632b_sense_sweep_points(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:SWE:POIN?", - "SENS:SWE:POIN {:e}".format(2048) - ], [ - "5" - ] + ik.hp.HP6632b, + [ + "SENS:SWE:POIN?", + "SENS:SWE:POIN {:e}".format(2048) + ], [ + "5" + ] ) as psu: assert psu.sense_sweep_points == 5 psu.sense_sweep_points = 2048 @@ -225,13 +225,13 @@ def test_hp6632b_sense_sweep_points(): def test_hp6632b_sense_sweep_interval(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:SWE:TINT?", - "SENS:SWE:TINT {:e}".format(1e-05) - ], [ - "1.56e-05" - ] + ik.hp.HP6632b, + [ + "SENS:SWE:TINT?", + "SENS:SWE:TINT {:e}".format(1e-05) + ], [ + "1.56e-05" + ] ) as psu: unit_eq(psu.sense_sweep_interval, 1.56e-05 * pq.second) psu.sense_sweep_interval = 1e-05 * pq.second @@ -239,13 +239,13 @@ def test_hp6632b_sense_sweep_interval(): def test_hp6632b_sense_window(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:WIND?", - "SENS:WIND RECT" - ], [ - "HANN" - ] + ik.hp.HP6632b, + [ + "SENS:WIND?", + "SENS:WIND RECT" + ], [ + "HANN" + ] ) as psu: assert psu.sense_window == psu.SenseWindow.hanning psu.sense_window = psu.SenseWindow.rectangular @@ -253,13 +253,13 @@ def test_hp6632b_sense_window(): def test_hp6632b_output_protection_delay(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP:PROT:DEL?", - "OUTP:PROT:DEL {:e}".format(5e-02) - ], [ - "8e-02" - ] + ik.hp.HP6632b, + [ + "OUTP:PROT:DEL?", + "OUTP:PROT:DEL {:e}".format(5e-02) + ], [ + "8e-02" + ] ) as psu: unit_eq(psu.output_protection_delay, 8e-02 * pq.second) psu.output_protection_delay = 5e-02 * pq.second @@ -267,25 +267,25 @@ def test_hp6632b_output_protection_delay(): def test_hp6632b_voltage_alc_bandwidth(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT:ALC:BAND?", - ], [ - "6e4" - ] + ik.hp.HP6632b, + [ + "VOLT:ALC:BAND?", + ], [ + "6e4" + ] ) as psu: assert psu.voltage_alc_bandwidth == psu.ALCBandwidth.fast def test_hp6632b_voltage_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT:TRIG?", - "VOLT:TRIG {:e}".format(1) - ], [ - "1e+0" - ] + ik.hp.HP6632b, + [ + "VOLT:TRIG?", + "VOLT:TRIG {:e}".format(1) + ], [ + "1e+0" + ] ) as psu: unit_eq(psu.voltage_trigger, 1 * pq.volt) psu.voltage_trigger = 1 * pq.volt @@ -293,13 +293,13 @@ def test_hp6632b_voltage_trigger(): def test_hp6632b_current_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "CURR:TRIG?", - "CURR:TRIG {:e}".format(0.1) - ], [ - "1e-01" - ] + ik.hp.HP6632b, + [ + "CURR:TRIG?", + "CURR:TRIG {:e}".format(0.1) + ], [ + "1e-01" + ] ) as psu: unit_eq(psu.current_trigger, 0.1 * pq.amp) psu.current_trigger = 0.1 * pq.amp @@ -307,38 +307,38 @@ def test_hp6632b_current_trigger(): def test_hp6632b_init_output_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "INIT:NAME TRAN", - ], - [] + ik.hp.HP6632b, + [ + "INIT:NAME TRAN", + ], + [] ) as psu: psu.init_output_trigger() def test_hp6632b_abort_output_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "ABORT", - ], - [] + ik.hp.HP6632b, + [ + "ABORT", + ], + [] ) as psu: psu.abort_output_trigger() def test_hp6632b_check_error_queue(): with expected_protocol( - ik.hp.HP6632b, - [ - "SYST:ERR?", - "SYST:ERR?", - ], [ - '-222,"Data out of range"', - '+0,"No error"' - ] + ik.hp.HP6632b, + [ + "SYST:ERR?", + "SYST:ERR?", + ], [ + '-222,"Data out of range"', + '+0,"No error"' + ] ) as psu: err_queue = psu.check_error_queue() assert err_queue == [ psu.ErrorCodes.data_out_of_range - ], "got {}".format(err_queue) + ], f"got {err_queue}" diff --git a/instruments/tests/test_hp/test_hp6652a.py b/instruments/tests/test_hp/test_hp6652a.py index a5d1cd13c..738048a3e 100644 --- a/instruments/tests/test_hp/test_hp6652a.py +++ b/instruments/tests/test_hp/test_hp6652a.py @@ -16,38 +16,38 @@ def test_name(): with expected_protocol( - ik.hp.HP6652a, - [ - "*IDN?" - ], - [ - "FOO,BAR,AAA,BBBB" - ], - sep="\n" + ik.hp.HP6652a, + [ + "*IDN?" + ], + [ + "FOO,BAR,AAA,BBBB" + ], + sep="\n" ) as hp: assert hp.name == "FOO BAR" def test_reset(): with expected_protocol( - ik.hp.HP6652a, - [ - "OUTP:PROT:CLE" - ], - [], - sep="\n" + ik.hp.HP6652a, + [ + "OUTP:PROT:CLE" + ], + [], + sep="\n" ) as hp: hp.reset() def test_display_text(): with expected_protocol( - ik.hp.HP6652a, - [ - 'DISP:TEXT "TEST"', - 'DISP:TEXT "TEST AAAAAAAAAA"' - ], - [] + ik.hp.HP6652a, + [ + 'DISP:TEXT "TEST"', + 'DISP:TEXT "TEST AAAAAAAAAA"' + ], + [] ) as psu: assert psu.display_text("TEST") == "TEST" assert psu.display_text("TEST AAAAAAAAAAAAAAAA") == "TEST AAAAAAAAAA" @@ -55,10 +55,10 @@ def test_display_text(): def test_channel(): with expected_protocol( - ik.hp.HP6652a, - [], - [], - sep="\n" + ik.hp.HP6652a, + [], + [], + sep="\n" ) as hp: assert hp.channel[0] == hp assert len(hp.channel) == 1 diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index f2eda8286..2a7d8eaad 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -18,19 +18,19 @@ def test_channel(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", - "INST:NSEL?", - "INST:NSEL?", - "INST:NSEL 2", - "INST:NSEL?" - ], - [ - "1", - "1", - "2" - ] + ik.hp.HPe3631a, + [ + "SYST:REM", + "INST:NSEL?", + "INST:NSEL?", + "INST:NSEL 2", + "INST:NSEL?" + ], + [ + "1", + "1", + "2" + ] ) as inst: assert inst.channelid == 1 assert inst.channel[2] == inst @@ -39,17 +39,17 @@ def test_channel(): def test_channelid(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "INST:NSEL?", # 1 - "INST:NSEL 2", # 2 - "INST:NSEL?" # 3 - ], - [ - "1", # 1 - "2" # 3 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "INST:NSEL?", # 1 + "INST:NSEL 2", # 2 + "INST:NSEL?" # 3 + ], + [ + "1", # 1 + "2" # 3 + ] ) as inst: assert inst.channelid == 1 inst.channelid = 2 @@ -58,25 +58,25 @@ def test_channelid(): def test_voltage(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "SOUR:VOLT? MAX", # 1 - "SOUR:VOLT? MAX", # 2 - "SOUR:VOLT? MAX", # 3.1 - "SOUR:VOLT 3.000000e+00", # 3.2 - "SOUR:VOLT?", # 4 - "SOUR:VOLT? MAX", # 5 - "SOUR:VOLT? MAX" # 6 - ], - [ - "6.0", # 1 - "6.0", # 2 - "6.0", # 3.1 - "3.0", # 4 - "6.0", # 5 - "6.0" # 6 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:VOLT? MAX", # 1 + "SOUR:VOLT? MAX", # 2 + "SOUR:VOLT? MAX", # 3.1 + "SOUR:VOLT 3.000000e+00", # 3.2 + "SOUR:VOLT?", # 4 + "SOUR:VOLT? MAX", # 5 + "SOUR:VOLT? MAX" # 6 + ], + [ + "6.0", # 1 + "6.0", # 2 + "6.0", # 3.1 + "3.0", # 4 + "6.0", # 5 + "6.0" # 6 + ] ) as inst: assert inst.voltage_min == 0.0 * pq.volt assert inst.voltage_max == 6.0 * pq.volt @@ -94,29 +94,29 @@ def test_voltage(): def test_current(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "SOUR:CURR? MIN", # 1.1 - "SOUR:CURR? MAX", # 1.2 - "SOUR:CURR? MIN", # 2.1 - "SOUR:CURR? MAX", # 2.2 - "SOUR:CURR 2.000000e+00", # 3 - "SOUR:CURR?", # 4 - "SOUR:CURR? MIN", # 5 - "SOUR:CURR? MIN", # 6.1 - "SOUR:CURR? MAX" # 6.2 - ], - [ - "0.0", # 1.1 - "5.0", # 1.2 - "0.0", # 2.1 - "5.0", # 2.2 - "2.0", # 4 - "0.0", # 5 - "0.0", # 6.1 - "5.0" # 6.2 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:CURR? MIN", # 1.1 + "SOUR:CURR? MAX", # 1.2 + "SOUR:CURR? MIN", # 2.1 + "SOUR:CURR? MAX", # 2.2 + "SOUR:CURR 2.000000e+00", # 3 + "SOUR:CURR?", # 4 + "SOUR:CURR? MIN", # 5 + "SOUR:CURR? MIN", # 6.1 + "SOUR:CURR? MAX" # 6.2 + ], + [ + "0.0", # 1.1 + "5.0", # 1.2 + "0.0", # 2.1 + "5.0", # 2.2 + "2.0", # 4 + "0.0", # 5 + "0.0", # 6.1 + "5.0" # 6.2 + ] ) as inst: assert inst.current_min == 0.0 * pq.amp assert inst.current_max == 5.0 * pq.amp @@ -134,27 +134,27 @@ def test_current(): def test_voltage_sense(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "MEAS:VOLT?" # 1 - ], - [ - "1.234" # 1 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "MEAS:VOLT?" # 1 + ], + [ + "1.234" # 1 + ] ) as inst: assert inst.voltage_sense == 1.234 * pq.volt def test_current_sense(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "MEAS:CURR?" # 1 - ], - [ - "1.234" # 1 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "MEAS:CURR?" # 1 + ], + [ + "1.234" # 1 + ] ) as inst: assert inst.current_sense == 1.234 * pq.amp diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 8d7878948..73b553dee 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -25,13 +25,13 @@ def test_channel(): def test_channel_mode(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - ], - [ - "VOLT", - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + ], + [ + "VOLT", + ] ) as inst: channel = inst.channel[0] assert channel.mode == inst.Mode.voltage_dc @@ -39,16 +39,16 @@ def test_channel_mode(): def test_channel_measure_voltage(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:CHAN 1", - "SENS:DATA:FRES?", - "SENS:FUNC?" - ], - [ - "1.234", - "VOLT", - ] + ik.keithley.Keithley2182, + [ + "SENS:CHAN 1", + "SENS:DATA:FRES?", + "SENS:FUNC?" + ], + [ + "1.234", + "VOLT", + ] ) as inst: channel = inst.channel[0] assert channel.measure() == 1.234 * pq.volt @@ -56,26 +56,25 @@ def test_channel_measure_voltage(): def test_channel_measure_temperature(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:CHAN 1", - "SENS:DATA:FRES?", - "SENS:FUNC?", - "UNIT:TEMP?" - ], - [ - "1.234", - "TEMP", - "C" - ] + ik.keithley.Keithley2182, + [ + "SENS:CHAN 1", + "SENS:DATA:FRES?", + "SENS:FUNC?", + "UNIT:TEMP?" + ], + [ + "1.234", + "TEMP", + "C" + ] ) as inst: channel = inst.channel[0] assert channel.measure() == 1.234 * pq.celsius def test_channel_measure_unknown_temperature_units(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.keithley.Keithley2182, [ "SENS:CHAN 1", @@ -88,37 +87,37 @@ def test_channel_measure_unknown_temperature_units(): "TEMP", "Z" ] - ) as inst: - inst.channel[0].measure() + ) as inst: + inst.channel[0].measure() def test_units(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "UNIT:TEMP?", + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "UNIT:TEMP?", - "SENS:FUNC?", - "UNIT:TEMP?", + "SENS:FUNC?", + "UNIT:TEMP?", - "SENS:FUNC?", - "UNIT:TEMP?", + "SENS:FUNC?", + "UNIT:TEMP?", - "SENS:FUNC?" - ], - [ - "TEMP", - "C", + "SENS:FUNC?" + ], + [ + "TEMP", + "C", - "TEMP", - "F", + "TEMP", + "F", - "TEMP", - "K", + "TEMP", + "K", - "VOLT" - ] + "VOLT" + ] ) as inst: units = str(inst.units.units).split()[1] assert units == "degC" @@ -134,15 +133,15 @@ def test_units(): def test_fetch(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "FETC?", - "SENS:FUNC?" - ], - [ - "1.234,1,5.678", - "VOLT", - ] + ik.keithley.Keithley2182, + [ + "FETC?", + "SENS:FUNC?" + ], + [ + "1.234,1,5.678", + "VOLT", + ] ) as inst: np.testing.assert_array_equal( inst.fetch(), [1.234, 1, 5.678] * pq.volt @@ -151,87 +150,85 @@ def test_fetch(): def test_measure(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "MEAS:VOLT?", - "SENS:FUNC?", - ], - [ - "VOLT", - "1.234", - "VOLT" - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "MEAS:VOLT?", + "SENS:FUNC?", + ], + [ + "VOLT", + "1.234", + "VOLT" + ] ) as inst: assert inst.measure() == 1.234 * pq.volt def test_measure_invalid_mode(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.keithley.Keithley2182, [], [] - ) as inst: - inst.measure("derp") + ) as inst: + inst.measure("derp") def test_relative_get(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "SENS:VOLT:CHAN1:REF:STAT?" - ], - [ - "VOLT", - "ON" - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "SENS:VOLT:CHAN1:REF:STAT?" + ], + [ + "VOLT", + "ON" + ] ) as inst: assert inst.relative is True def test_relative_set_already_enabled(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "SENS:FUNC?", - "SENS:VOLT:CHAN1:REF:STAT?", - "SENS:VOLT:CHAN1:REF:ACQ" - ], - [ - "VOLT", - "VOLT", - "ON", - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "SENS:FUNC?", + "SENS:VOLT:CHAN1:REF:STAT?", + "SENS:VOLT:CHAN1:REF:ACQ" + ], + [ + "VOLT", + "VOLT", + "ON", + ] ) as inst: inst.relative = True def test_relative_set_start_disabled(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "SENS:FUNC?", - "SENS:VOLT:CHAN1:REF:STAT?", - "SENS:VOLT:CHAN1:REF:STAT ON" - ], - [ - "VOLT", - "VOLT", - "OFF", - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "SENS:FUNC?", + "SENS:VOLT:CHAN1:REF:STAT?", + "SENS:VOLT:CHAN1:REF:STAT ON" + ], + [ + "VOLT", + "VOLT", + "OFF", + ] ) as inst: inst.relative = True def test_relative_set_wrong_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.keithley.Keithley2182, [], [] - ) as inst: - inst.relative = "derp" + ) as inst: + inst.relative = "derp" diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index 6ea2e5bf3..753b7d2c8 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -18,14 +18,14 @@ def test_zero_check(): with expected_protocol( - ik.keithley.Keithley485, - [ - "C0X", - "C1X", - "U0X" - ], [ - "4851000000000:" - ] + ik.keithley.Keithley485, + [ + "C0X", + "C1X", + "U0X" + ], [ + "4851000000000:" + ] ) as inst: inst.zero_check = False inst.zero_check = True @@ -34,14 +34,14 @@ def test_zero_check(): def test_log(): with expected_protocol( - ik.keithley.Keithley485, - [ - "D0X", - "D1X", - "U0X" - ], [ - "4850100000000:" - ] + ik.keithley.Keithley485, + [ + "D0X", + "D1X", + "U0X" + ], [ + "4850100000000:" + ] ) as inst: inst.log = False inst.log = True @@ -50,14 +50,14 @@ def test_log(): def test_input_range(): with expected_protocol( - ik.keithley.Keithley485, - [ - "R0X", - "R7X", - "U0X" - ], [ - "4850070000000:" - ] + ik.keithley.Keithley485, + [ + "R0X", + "R7X", + "U0X" + ], [ + "4850070000000:" + ] ) as inst: inst.input_range = "auto" inst.input_range = 2e-3 @@ -66,14 +66,14 @@ def test_input_range(): def test_relative(): with expected_protocol( - ik.keithley.Keithley485, - [ - "Z0X", - "Z1X", - "U0X" - ], [ - "4850001000000:" - ] + ik.keithley.Keithley485, + [ + "Z0X", + "Z1X", + "U0X" + ], [ + "4850001000000:" + ] ) as inst: inst.relative = False inst.relative = True @@ -82,14 +82,14 @@ def test_relative(): def test_eoi_mode(): with expected_protocol( - ik.keithley.Keithley485, - [ - "K0X", - "K1X", - "U0X" - ], [ - "4850000100000:" - ] + ik.keithley.Keithley485, + [ + "K0X", + "K1X", + "U0X" + ], [ + "4850000100000:" + ] ) as inst: inst.eoi_mode = True inst.eoi_mode = False @@ -98,14 +98,14 @@ def test_eoi_mode(): def test_trigger_mode(): with expected_protocol( - ik.keithley.Keithley485, - [ - "T0X", - "T5X", - "U0X" - ], [ - "4850000050000:" - ] + ik.keithley.Keithley485, + [ + "T0X", + "T5X", + "U0X" + ], [ + "4850000050000:" + ] ) as inst: inst.trigger_mode = "continuous_ontalk" inst.trigger_mode = "oneshot_onx" @@ -114,13 +114,13 @@ def test_trigger_mode(): def test_auto_range(): with expected_protocol( - ik.keithley.Keithley485, - [ - "R0X", - "U0X" - ], [ - "4850000000000:" - ] + ik.keithley.Keithley485, + [ + "R0X", + "U0X" + ], [ + "4850000000000:" + ] ) as inst: inst.auto_range() assert inst.input_range == "auto" @@ -128,26 +128,26 @@ def test_auto_range(): def test_get_status(): with expected_protocol( - ik.keithley.Keithley485, - [ - "U0X" - ], [ - "4850000000000:" - ] + ik.keithley.Keithley485, + [ + "U0X" + ], [ + "4850000000000:" + ] ) as inst: inst.get_status() def test_measure(): with expected_protocol( - ik.keithley.Keithley485, - [ - "X", - "X" - ], [ - "NDCA+1.2345E-9", - "NDCL-9.0000E+0" - ] + ik.keithley.Keithley485, + [ + "X", + "X" + ], [ + "NDCA+1.2345E-9", + "NDCL-9.0000E+0" + ] ) as inst: assert inst.measure() == 1.2345 * pq.nanoamp assert inst.measure() == 1. * pq.nanoamp diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index 35e12a99b..4fc91105b 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -23,14 +23,14 @@ def test_channel(): def test_current(): with expected_protocol( - ik.keithley.Keithley6220, - [ - "SOUR:CURR?", - "SOUR:CURR {:e}".format(0.05) - ], - [ - "0.1", - ] + ik.keithley.Keithley6220, + [ + "SOUR:CURR?", + "SOUR:CURR {:e}".format(0.05) + ], + [ + "0.1", + ] ) as inst: assert inst.current == 100 * pq.milliamp assert inst.current_min == -105 * pq.milliamp @@ -40,10 +40,10 @@ def test_current(): def test_disable(): with expected_protocol( - ik.keithley.Keithley6220, - [ - "SOUR:CLE:IMM" - ], - [] + ik.keithley.Keithley6220, + [ + "SOUR:CLE:IMM" + ], + [] ) as inst: inst.disable() diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index 9864e5bda..845464b04 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -23,8 +23,7 @@ def test_valid_range(): inst = ik.keithley.Keithley6514.open_test() assert inst._valid_range(inst.Mode.voltage) == inst.ValidRange.voltage assert inst._valid_range(inst.Mode.current) == inst.ValidRange.current - assert inst._valid_range( - inst.Mode.resistance) == inst.ValidRange.resistance + assert inst._valid_range(inst.Mode.resistance) == inst.ValidRange.resistance assert inst._valid_range(inst.Mode.charge) == inst.ValidRange.charge @@ -36,13 +35,13 @@ def test_valid_range_invalid(): def test_parse_measurement(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + ], + [ + '"VOLT:DC"' + ] ) as inst: reading, timestamp, status = inst._parse_measurement("1.0,1234,5678") assert reading == 1.0 * pq.volt @@ -52,14 +51,14 @@ def test_parse_measurement(): def test_mode(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - 'FUNCTION "VOLT:DC"' - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + 'FUNCTION "VOLT:DC"' + ], + [ + '"VOLT:DC"' + ] ) as inst: assert inst.mode == inst.Mode.voltage inst.mode = inst.Mode.voltage @@ -67,14 +66,14 @@ def test_mode(): def test_trigger_source(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "TRIGGER:SOURCE?", - "TRIGGER:SOURCE IMM" - ], - [ - "TLINK" - ] + ik.keithley.Keithley6514, + [ + "TRIGGER:SOURCE?", + "TRIGGER:SOURCE IMM" + ], + [ + "TLINK" + ] ) as inst: assert inst.trigger_mode == inst.TriggerMode.tlink inst.trigger_mode = inst.TriggerMode.immediate @@ -82,14 +81,14 @@ def test_trigger_source(): def test_arm_source(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "ARM:SOURCE?", - "ARM:SOURCE IMM" - ], - [ - "TIM" - ] + ik.keithley.Keithley6514, + [ + "ARM:SOURCE?", + "ARM:SOURCE IMM" + ], + [ + "TIM" + ] ) as inst: assert inst.arm_source == inst.ArmSource.timer inst.arm_source = inst.ArmSource.immediate @@ -97,14 +96,14 @@ def test_arm_source(): def test_zero_check(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "SYST:ZCH?", - "SYST:ZCH ON" - ], - [ - "OFF" - ] + ik.keithley.Keithley6514, + [ + "SYST:ZCH?", + "SYST:ZCH ON" + ], + [ + "OFF" + ] ) as inst: assert inst.zero_check is False inst.zero_check = True @@ -112,14 +111,14 @@ def test_zero_check(): def test_zero_correct(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "SYST:ZCOR?", - "SYST:ZCOR ON" - ], - [ - "OFF" - ] + ik.keithley.Keithley6514, + [ + "SYST:ZCOR?", + "SYST:ZCOR ON" + ], + [ + "OFF" + ] ) as inst: assert inst.zero_correct is False inst.zero_correct = True @@ -127,31 +126,31 @@ def test_zero_correct(): def test_unit(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + ], + [ + '"VOLT:DC"' + ] ) as inst: assert inst.unit == pq.volt def test_auto_range(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - "VOLT:DC:RANGE:AUTO?", - "FUNCTION?", - "VOLT:DC:RANGE:AUTO 1" - ], - [ - '"VOLT:DC"', - "0", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + "VOLT:DC:RANGE:AUTO?", + "FUNCTION?", + "VOLT:DC:RANGE:AUTO 1" + ], + [ + '"VOLT:DC"', + "0", + '"VOLT:DC"' + ] ) as inst: assert inst.auto_range is False inst.auto_range = True @@ -159,26 +158,25 @@ def test_auto_range(): def test_input_range(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - "VOLT:DC:RANGE:UPPER?", - "FUNCTION?", - "VOLT:DC:RANGE:UPPER {:e}".format(20) - ], - [ - '"VOLT:DC"', - "10", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + "VOLT:DC:RANGE:UPPER?", + "FUNCTION?", + "VOLT:DC:RANGE:UPPER {:e}".format(20) + ], + [ + '"VOLT:DC"', + "10", + '"VOLT:DC"' + ] ) as inst: assert inst.input_range == 10 * pq.volt inst.input_range = 20 * pq.volt def test_input_range_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.keithley.Keithley6514, [ "FUNCTION?" @@ -186,32 +184,32 @@ def test_input_range_invalid(): [ '"VOLT:DC"' ] - ) as inst: - inst.input_range = 10 * pq.volt + ) as inst: + inst.input_range = 10 * pq.volt def test_auto_config(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "CONF:VOLT:DC", - ], - [] + ik.keithley.Keithley6514, + [ + "CONF:VOLT:DC", + ], + [] ) as inst: inst.auto_config(inst.Mode.voltage) def test_fetch(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FETC?", - "FUNCTION?", - ], - [ - "1.0,1234,5678", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FETC?", + "FUNCTION?", + ], + [ + "1.0,1234,5678", + '"VOLT:DC"' + ] ) as inst: reading, timestamp = inst.fetch() assert reading == 1.0 * pq.volt @@ -220,15 +218,15 @@ def test_fetch(): def test_read(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "READ?", - "FUNCTION?", - ], - [ - "1.0,1234,5678", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "READ?", + "FUNCTION?", + ], + [ + "1.0,1234,5678", + '"VOLT:DC"' + ] ) as inst: reading, timestamp = inst.read_measurements() assert reading == 1.0 * pq.volt diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index 1f70afdc8..ebb176a04 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -20,20 +20,20 @@ def test_mhs_amplitude(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1a", - ":r2a", - ":s1a660", - ":s2a800" - ], - [ - ":r1a330", - ":r2a500", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1a", + ":r2a", + ":s1a660", + ":s2a800" + ], + [ + ":r1a330", + ":r2a500", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].amplitude[0] == 3.3*pq.V assert mhs.channel[1].amplitude[0] == 5.0*pq.V @@ -43,10 +43,10 @@ def test_mhs_amplitude(): def test_mhs_amplitude_dbm_notimplemented(): with expected_protocol( - ik.minghe.MHS5200, - [], - [], - sep="\r\n" + ik.minghe.MHS5200, + [], + [], + sep="\r\n" ) as mhs: with pytest.raises(NotImplementedError): mhs.channel[0].amplitude = 6.6*ik.units.dBm @@ -54,21 +54,21 @@ def test_mhs_amplitude_dbm_notimplemented(): def test_mhs_duty_cycle(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1d", - ":r2d", - ":s1d6", - ":s2d80" - - ], - [ - ":r1d010", - ":r2d100", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1d", + ":r2d", + ":s1d6", + ":s2d80" + + ], + [ + ":r1d010", + ":r2d100", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].duty_cycle == 1.0 assert mhs.channel[1].duty_cycle == 10.0 @@ -78,20 +78,20 @@ def test_mhs_duty_cycle(): def test_mhs_enable(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1b", - ":r2b", - ":s1b0", - ":s2b1" - ], - [ - ":r1b1", - ":r2b0", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1b", + ":r2b", + ":s1b0", + ":s2b1" + ], + [ + ":r1b1", + ":r2b0", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].enable assert not mhs.channel[1].enable @@ -101,21 +101,21 @@ def test_mhs_enable(): def test_mhs_frequency(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1f", - ":r2f", - ":s1f600000", - ":s2f800000" - - ], - [ - ":r1f3300000", - ":r2f50000000", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1f", + ":r2f", + ":s1f600000", + ":s2f800000" + + ], + [ + ":r1f3300000", + ":r2f50000000", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].frequency == 33.0*pq.kHz assert mhs.channel[1].frequency == 500.0*pq.kHz @@ -125,21 +125,21 @@ def test_mhs_frequency(): def test_mhs_offset(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1o", - ":r2o", - ":s1o60", - ":s2o180" - - ], - [ - ":r1o120", - ":r2o0", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1o", + ":r2o", + ":s1o60", + ":s2o180" + + ], + [ + ":r1o120", + ":r2o0", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].offset == 0 assert mhs.channel[1].offset == -1.2 @@ -149,21 +149,21 @@ def test_mhs_offset(): def test_mhs_phase(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1p", - ":r2p", - ":s1p60", - ":s2p180" - - ], - [ - ":r1p120", - ":r2p0", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1p", + ":r2p", + ":s1p60", + ":s2p180" + + ], + [ + ":r1p120", + ":r2p0", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].phase == 120 assert mhs.channel[1].phase == 0 @@ -173,21 +173,21 @@ def test_mhs_phase(): def test_mhs_wave_type(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1w", - ":r2w", - ":s1w2", - ":s2w3" - - ], - [ - ":r1w0", - ":r2w1", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r1w", + ":r2w", + ":s1w2", + ":s2w3" + + ], + [ + ":r1w0", + ":r2w1", + "ok", + "ok" + ], + sep="\r\n" ) as mhs: assert mhs.channel[0].function == mhs.Function.sine assert mhs.channel[1].function == mhs.Function.square @@ -197,14 +197,14 @@ def test_mhs_wave_type(): def test_mhs_serial_number(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r0c" - - ], - [ - ":r0c5225A1", - ], - sep="\r\n" + ik.minghe.MHS5200, + [ + ":r0c" + + ], + [ + ":r0c5225A1", + ], + sep="\r\n" ) as mhs: assert mhs.serial_number == "5225A1" diff --git a/instruments/tests/test_named_struct.py b/instruments/tests/test_named_struct.py index adeef46da..5b7260e9b 100644 --- a/instruments/tests/test_named_struct.py +++ b/instruments/tests/test_named_struct.py @@ -17,6 +17,7 @@ Field, StringField, Padding, NamedStruct ) + # TESTS ###################################################################### # We disable pylint warnings that are not as applicable for unit tests. @@ -33,7 +34,6 @@ class Foo(NamedStruct): foo = Foo(a=var1, b=var2) assert Foo.unpack(foo.pack()) == foo - def test_str(self): class Foo(NamedStruct): a = StringField(8, strip_null=False) @@ -48,7 +48,6 @@ class Foo(NamedStruct): self.assertEqual(foo.b, 'abc') self.assertEqual(foo.c, u'α') - def test_negative_len(self): """ Checks whether negative field lengths correctly raise. diff --git a/instruments/tests/test_newport/test_newportesp301.py b/instruments/tests/test_newport/test_newportesp301.py index 3d0499eb2..07541d9a7 100644 --- a/instruments/tests/test_newport/test_newportesp301.py +++ b/instruments/tests/test_newport/test_newportesp301.py @@ -16,16 +16,16 @@ def test_axis_returns_axis_class(): with expected_protocol( - ik.newport.NewportESP301, - [ - "1SN?", - "TB?" # error check query - ], - [ - "1", - "0,0,0" - ], - sep="\r" + ik.newport.NewportESP301, + [ + "1SN?", + "TB?" # error check query + ], + [ + "1", + "0,0,0" + ], + sep="\r" ) as inst: axis = inst.axis[0] assert isinstance(axis, ik.newport.NewportESP301Axis) is True diff --git a/instruments/tests/test_oxford/test_oxforditc503.py b/instruments/tests/test_oxford/test_oxforditc503.py index 73915bee6..54f1dff45 100644 --- a/instruments/tests/test_oxford/test_oxforditc503.py +++ b/instruments/tests/test_oxford/test_oxforditc503.py @@ -18,12 +18,12 @@ def test_sensor_returns_sensor_class(): with expected_protocol( - ik.oxford.OxfordITC503, - [ - "C3" - ], - [], - sep="\r" + ik.oxford.OxfordITC503, + [ + "C3" + ], + [], + sep="\r" ) as inst: sensor = inst.sensor[0] assert isinstance(sensor, inst.Sensor) is True @@ -31,15 +31,15 @@ def test_sensor_returns_sensor_class(): def test_sensor_temperature(): with expected_protocol( - ik.oxford.OxfordITC503, - [ - "C3", - "R1" - ], - [ - "R123" - ], - sep="\r" + ik.oxford.OxfordITC503, + [ + "C3", + "R1" + ], + [ + "R123" + ], + sep="\r" ) as inst: sensor = inst.sensor[0] assert sensor.temperature == 123 * pq.kelvin diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index 220580fbc..908feb937 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -19,25 +19,25 @@ def test_reset(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0E." - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "0E." + ], + [] ) as inst: inst.reset() def test_frequency(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "04.", - "0C{:012X}.".format(int((10 * pq.GHz).rescale(mHz).magnitude)) - ], - [ - "00E8D4A51000" - ] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "04.", + "0C{:012X}.".format(int((10 * pq.GHz).rescale(mHz).magnitude)) + ], + [ + "00E8D4A51000" + ] ) as inst: assert inst.frequency == 1 * pq.GHz inst.frequency = 10 * pq.GHz @@ -45,14 +45,14 @@ def test_frequency(): def test_power(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0D.", - "03{:04X}.".format(int((10 * dBm).rescale(cBm).magnitude)) - ], - [ - "-064" - ] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "0D.", + "03{:04X}.".format(int((10 * dBm).rescale(cBm).magnitude)) + ], + [ + "-064" + ] ) as inst: assert inst.power == -10 * dBm inst.power = 10 * dBm @@ -60,12 +60,12 @@ def test_power(): def test_blanking(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "05{:02X}.".format(1), - "05{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "05{:02X}.".format(1), + "05{:02X}.".format(0) + ], + [] ) as inst: inst.blanking = True inst.blanking = False @@ -73,12 +73,12 @@ def test_blanking(): def test_ref_output(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "08{:02X}.".format(1), - "08{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "08{:02X}.".format(1), + "08{:02X}.".format(0) + ], + [] ) as inst: inst.ref_output = True inst.ref_output = False @@ -86,12 +86,12 @@ def test_ref_output(): def test_output(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0F{:02X}.".format(1), - "0F{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "0F{:02X}.".format(1), + "0F{:02X}.".format(0) + ], + [] ) as inst: inst.output = True inst.output = False @@ -99,12 +99,12 @@ def test_output(): def test_pulse_modulation(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "09{:02X}.".format(1), - "09{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "09{:02X}.".format(1), + "09{:02X}.".format(0) + ], + [] ) as inst: inst.pulse_modulation = True inst.pulse_modulation = False @@ -112,12 +112,12 @@ def test_pulse_modulation(): def test_am_modulation(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0A{:02X}.".format(1), - "0A{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + [ + "0A{:02X}.".format(1), + "0A{:02X}.".format(0) + ], + [] ) as inst: inst.am_modulation = True inst.am_modulation = False diff --git a/instruments/tests/test_picowatt/test_picowatt_avs47.py b/instruments/tests/test_picowatt/test_picowatt_avs47.py index 2d741be87..9b316f7b6 100644 --- a/instruments/tests/test_picowatt/test_picowatt_avs47.py +++ b/instruments/tests/test_picowatt/test_picowatt_avs47.py @@ -23,66 +23,66 @@ def test_sensor_is_sensor_class(): def test_init(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0" - ], - [] - ) as _: + ik.picowatt.PicowattAVS47, + [ + "HDR 0" + ], + [] + ): pass def test_sensor_resistance_same_channel(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "MUX?", - "ADC", - "RES?" - ], - [ - "0", - "123" - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "MUX?", + "ADC", + "RES?" + ], + [ + "0", + "123" + ] ) as inst: assert inst.sensor[0].resistance == 123 * pq.ohm def test_sensor_resistance_different_channel(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "MUX?", - "INP 0", - "MUX 0", - "INP 1", - "ADC", - "RES?" - ], - [ - "1", - "123" - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "MUX?", + "INP 0", + "MUX 0", + "INP 1", + "ADC", + "RES?" + ], + [ + "1", + "123" + ] ) as inst: assert inst.sensor[0].resistance == 123 * pq.ohm def test_remote(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "REM?", - "REM?", - "REM 1", - "REM 0" - ], - [ - "0", - "1" - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "REM?", + "REM?", + "REM 1", + "REM 0" + ], + [ + "0", + "1" + ] ) as inst: assert inst.remote is False assert inst.remote is True @@ -92,15 +92,15 @@ def test_remote(): def test_input_source(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "INP?", - "INP 1" - ], - [ - "0", - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "INP?", + "INP 1" + ], + [ + "0", + ] ) as inst: assert inst.input_source == inst.InputSource.ground inst.input_source = inst.InputSource.actual @@ -108,15 +108,15 @@ def test_input_source(): def test_mux_channel(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "MUX?", - "MUX 1" - ], - [ - "3", - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "MUX?", + "MUX 1" + ], + [ + "3", + ] ) as inst: assert inst.mux_channel == 3 inst.mux_channel = 1 @@ -124,15 +124,15 @@ def test_mux_channel(): def test_excitation(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "EXC?", - "EXC 1" - ], - [ - "3", - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "EXC?", + "EXC 1" + ], + [ + "3", + ] ) as inst: assert inst.excitation == 3 inst.excitation = 1 @@ -140,15 +140,15 @@ def test_excitation(): def test_display(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "DIS?", - "DIS 1" - ], - [ - "3", - ] + ik.picowatt.PicowattAVS47, + [ + "HDR 0", + "DIS?", + "DIS 1" + ], + [ + "3", + ] ) as inst: assert inst.display == 3 inst.display = 1 diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index d3d968ef3..a1a3c98d2 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -20,45 +20,44 @@ def test_cc1_count(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "COUN:C1?" - ], - [ - "", - "Firmware v2.010", - "20" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "COUN:C1?" + ], + [ + "", + "Firmware v2.010", + "20" + ], + sep="\n" ) as cc: assert cc.channel[0].count == 20.0 def test_cc1_window(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "WIND?", - ":WIND 7" - ], - [ - "", - "Firmware v2.010", - "2", - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "WIND?", + ":WIND 7" + ], + [ + "", + "Firmware v2.010", + "2", + ], + sep="\n" ) as cc: unit_eq(cc.window, pq.Quantity(2, "ns")) cc.window = 7 def test_cc1_window_error(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -70,34 +69,33 @@ def test_cc1_window_error(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.window = 10 + ) as cc: + cc.window = 10 def test_cc1_delay(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "DELA?", - ":DELA 2" - ], - [ - "", - "Firmware v2.010", - "8", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "DELA?", + ":DELA 2" + ], + [ + "", + "Firmware v2.010", + "8", + "" + ], + sep="\n" ) as cc: unit_eq(cc.delay, pq.Quantity(8, "ns")) cc.delay = 2 def test_cc1_delay_error1(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -109,13 +107,12 @@ def test_cc1_delay_error1(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.delay = -1 + ) as cc: + cc.delay = -1 def test_cc1_delay_error2(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -127,26 +124,26 @@ def test_cc1_delay_error2(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.delay = 1 + ) as cc: + cc.delay = 1 def test_cc1_dwell_old_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "DWEL?", - ":DWEL 2" - ], - [ - "Unknown Command", - "Firmware v2.001", - "8000", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "DWEL?", + ":DWEL 2" + ], + [ + "Unknown Command", + "Firmware v2.001", + "8000", + "" + ], + sep="\n" ) as cc: unit_eq(cc.dwell_time, pq.Quantity(8, "s")) cc.dwell_time = 2 @@ -154,27 +151,26 @@ def test_cc1_dwell_old_firmware(): def test_cc1_dwell_new_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "DWEL?", - ":DWEL 2" - ], - [ - "", - "Firmware v2.010", - "8" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "DWEL?", + ":DWEL 2" + ], + [ + "", + "Firmware v2.010", + "8" + ], + sep="\n" ) as cc: unit_eq(cc.dwell_time, pq.Quantity(8, "s")) cc.dwell_time = 2 def test_cc1_dwell_time_error(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -186,92 +182,92 @@ def test_cc1_dwell_time_error(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.dwell_time = -1 + ) as cc: + cc.dwell_time = -1 def test_cc1_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" ) as cc: assert cc.firmware == (2, 10, 0) def test_cc1_firmware_2(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?" + ], + [ + "Unknown Command", + "Firmware v2" + ], + sep="\n" ) as cc: assert cc.firmware == (2, 0, 0) def test_cc1_firmware_3(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2.010.1" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?" + ], + [ + "Unknown Command", + "Firmware v2.010.1" + ], + sep="\n" ) as cc: assert cc.firmware == (2, 10, 1) def test_cc1_firmware_repeat_query(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "FIRM?" - ], - [ - "Unknown Command", - "Unknown", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "FIRM?" + ], + [ + "Unknown Command", + "Unknown", + "Firmware v2.010" + ], + sep="\n" ) as cc: assert cc.firmware == (2, 10, 0) def test_cc1_gate_new_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "GATE?", - ":GATE:ON", - ":GATE:OFF" - - ], - [ - "", - "Firmware v2.010", - "ON" - ], + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "GATE?", + ":GATE:ON", + ":GATE:OFF" + + ], + [ + "", + "Firmware v2.010", + "ON" + ], ) as cc: assert cc.gate is True cc.gate = True @@ -280,23 +276,23 @@ def test_cc1_gate_new_firmware(): def test_cc1_gate_old_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "GATE?", - ":GATE 1", - ":GATE 0" - - ], - [ - "Unknown Command", - "Firmware v2.001", - "1", - "", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "GATE?", + ":GATE 1", + ":GATE 0" + + ], + [ + "Unknown Command", + "Firmware v2.001", + "1", + "", + "" + ], + sep="\n" ) as cc: assert cc.gate is True cc.gate = True @@ -304,8 +300,7 @@ def test_cc1_gate_old_firmware(): def test_cc1_gate_error(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -317,28 +312,28 @@ def test_cc1_gate_error(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.gate = "blo" + ) as cc: + cc.gate = "blo" def test_cc1_subtract_new_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "SUBT?", - ":SUBT:ON", - ":SUBT:OFF" - - ], - [ - "", - "Firmware v2.010", - "ON", - ":SUBT:OFF" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "SUBT?", + ":SUBT:ON", + ":SUBT:OFF" + + ], + [ + "", + "Firmware v2.010", + "ON", + ":SUBT:OFF" + ], + sep="\n" ) as cc: assert cc.subtract is True cc.subtract = True @@ -346,8 +341,7 @@ def test_cc1_subtract_new_firmware(): def test_cc1_subtract_error(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -360,26 +354,26 @@ def test_cc1_subtract_error(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.subtract = "blo" + ) as cc: + cc.subtract = "blo" def test_cc1_trigger_mode(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "TRIG?", - ":TRIG:MODE CONT", - ":TRIG:MODE STOP" - ], - [ - "", - "Firmware v2.010", - "MODE STOP" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "TRIG?", + ":TRIG:MODE CONT", + ":TRIG:MODE STOP" + ], + [ + "", + "Firmware v2.010", + "MODE STOP" + ], + sep="\n" ) as cc: assert cc.trigger_mode is cc.TriggerMode.start_stop cc.trigger_mode = cc.TriggerMode.continuous @@ -388,22 +382,22 @@ def test_cc1_trigger_mode(): def test_cc1_trigger_mode_old_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "TRIG?", - ":TRIG 0", - ":TRIG 1" - ], - [ - "Unknown Command", - "Firmware v2.001", - "1", - "", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "TRIG?", + ":TRIG 0", + ":TRIG 1" + ], + [ + "Unknown Command", + "Firmware v2.001", + "1", + "", + "" + ], + sep="\n" ) as cc: assert cc.trigger_mode == cc.TriggerMode.start_stop cc.trigger_mode = cc.TriggerMode.continuous @@ -411,8 +405,7 @@ def test_cc1_trigger_mode_old_firmware(): def test_cc1_trigger_mode_error(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -423,46 +416,46 @@ def test_cc1_trigger_mode_error(): "Firmware v2.010" ], sep="\n" - ) as cc: - cc.trigger_mode = "blo" + ) as cc: + cc.trigger_mode = "blo" def test_cc1_clear(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "CLEA" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "CLEA" + ], + [ + "", + "Firmware v2.010" + ], + sep="\n" ) as cc: cc.clear_counts() def test_acknowledge(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":ACKN ON", - "CLEA", - ":ACKN OF", - "CLEA" - ], - [ - "", - "Firmware v2.010", - "CLEA", - ":ACKN OF" - - ], - sep="\n" + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + ":ACKN ON", + "CLEA", + ":ACKN OF", + "CLEA" + ], + [ + "", + "Firmware v2.010", + "CLEA", + ":ACKN OF" + + ], + sep="\n" ) as cc: assert not cc.acknowledge cc.acknowledge = True @@ -474,8 +467,7 @@ def test_acknowledge(): def test_acknowledge_notimplementederror(): - with pytest.raises(NotImplementedError): - with expected_protocol( + with pytest.raises(NotImplementedError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -487,13 +479,12 @@ def test_acknowledge_notimplementederror(): ], sep="\n" - ) as cc: - cc.acknowledge = True + ) as cc: + cc.acknowledge = True def test_acknowledge_not_implemented_error(): # pylint: disable=protected-access - with pytest.raises(NotImplementedError): - with expected_protocol( + with pytest.raises(NotImplementedError), expected_protocol( ik.qubitekk.CC1, [ ":ACKN OF", @@ -505,5 +496,5 @@ def test_acknowledge_not_implemented_error(): # pylint: disable=protected-acces ], sep="\n" - ) as cc: - cc.acknowledge = True + ) as cc: + cc.acknowledge = True diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index e6cfef952..7175416cf 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -20,15 +20,15 @@ def test_mc1_setting(): with expected_protocol( - ik.qubitekk.MC1, - [ - "OUTP?", - ":OUTP 0" - ], - [ - "1" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "OUTP?", + ":OUTP 0" + ], + [ + "1" + ], + sep="\r" ) as mc: assert mc.setting == 1 mc.setting = 0 @@ -36,177 +36,176 @@ def test_mc1_setting(): def test_mc1_internal_position(): with expected_protocol( - ik.qubitekk.MC1, - [ - "POSI?", - "STEP?" - - ], - [ - "-100", - "1" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "POSI?", + "STEP?" + + ], + [ + "-100", + "1" + ], + sep="\r" ) as mc: assert mc.internal_position == -100*pq.ms def test_mc1_metric_position(): with expected_protocol( - ik.qubitekk.MC1, - [ - "METR?" - ], - [ - "-3.14159" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "METR?" + ], + [ + "-3.14159" + ], + sep="\r" ) as mc: assert mc.metric_position == -3.14159*pq.mm def test_mc1_direction(): with expected_protocol( - ik.qubitekk.MC1, - [ - "DIRE?" - ], - [ - "-100" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "DIRE?" + ], + [ + "-100" + ], + sep="\r" ) as mc: assert mc.direction == -100 def test_mc1_firmware(): with expected_protocol( - ik.qubitekk.MC1, - [ - "FIRM?" - ], - [ - "1.0.1" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "FIRM?" + ], + [ + "1.0.1" + ], + sep="\r" ) as mc: assert mc.firmware == (1, 0, 1) def test_mc1_inertia(): with expected_protocol( - ik.qubitekk.MC1, - [ - "INER?" - ], - [ - "20" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "INER?" + ], + [ + "20" + ], + sep="\r" ) as mc: assert mc.inertia == 20 def test_mc1_step(): with expected_protocol( - ik.qubitekk.MC1, - [ - "STEP?" - ], - [ - "20" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "STEP?" + ], + [ + "20" + ], + sep="\r" ) as mc: assert mc.step_size == 20*pq.ms def test_mc1_motor(): with expected_protocol( - ik.qubitekk.MC1, - [ - "MOTO?" - ], - [ - "Radio" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "MOTO?" + ], + [ + "Radio" + ], + sep="\r" ) as mc: assert mc.controller == mc.MotorType.radio def test_mc1_move_timeout(): with expected_protocol( - ik.qubitekk.MC1, - [ - "TIME?", - "STEP?" - ], - [ - "200", - "1" - ], - sep="\r" + ik.qubitekk.MC1, + [ + "TIME?", + "STEP?" + ], + [ + "200", + "1" + ], + sep="\r" ) as mc: assert mc.move_timeout == 200*pq.ms def test_mc1_is_centering(): with expected_protocol( - ik.qubitekk.MC1, - ["CENT?"], - ["1"], - sep="\r" + ik.qubitekk.MC1, + ["CENT?"], + ["1"], + sep="\r" ) as mc: assert mc.is_centering() is True def test_mc1_is_centering_false(): with expected_protocol( - ik.qubitekk.MC1, - ["CENT?"], - ["0"], - sep="\r" + ik.qubitekk.MC1, + ["CENT?"], + ["0"], + sep="\r" ) as mc: assert mc.is_centering() is False def test_mc1_center(): with expected_protocol( - ik.qubitekk.MC1, - [":CENT"], - [""], - sep="\r" + ik.qubitekk.MC1, + [":CENT"], + [""], + sep="\r" ) as mc: mc.center() def test_mc1_reset(): with expected_protocol( - ik.qubitekk.MC1, - [":RESE"], - [""], - sep="\r" + ik.qubitekk.MC1, + [":RESE"], + [""], + sep="\r" ) as mc: mc.reset() def test_mc1_move(): with expected_protocol( - ik.qubitekk.MC1, - ["STEP?", ":MOVE 0"], - ["1"], - sep="\r" + ik.qubitekk.MC1, + ["STEP?", ":MOVE 0"], + ["1"], + sep="\r" ) as mc: mc.move(0) def test_mc1_move_value_error(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.qubitekk.MC1, [":MOVE -1000"], [""], sep="\r" - ) as mc: - mc.move(-1000) + ) as mc: + mc.move(-1000) diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index bacfa5882..aa8fed2f9 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -19,15 +19,15 @@ def test_amplitude(): with expected_protocol( - ik.srs.SRS345, - [ - "AMPL?", - "AMPL 0.1VP", - "AMPL 0.1VR" - ], - [ - "1.234VP", - ] + ik.srs.SRS345, + [ + "AMPL?", + "AMPL 0.1VP", + "AMPL 0.1VR" + ], + [ + "1.234VP", + ] ) as inst: np.testing.assert_array_equal( inst.amplitude, (1.234 * pq.V, inst.VoltageMode.peak_to_peak) @@ -38,14 +38,14 @@ def test_amplitude(): def test_frequency(): with expected_protocol( - ik.srs.SRS345, - [ - "FREQ?", - "FREQ {:e}".format(0.1), - ], - [ - "1.234", - ] + ik.srs.SRS345, + [ + "FREQ?", + "FREQ {:e}".format(0.1), + ], + [ + "1.234", + ] ) as inst: assert inst.frequency == 1.234 * pq.Hz inst.frequency = 0.1 * pq.Hz @@ -53,14 +53,14 @@ def test_frequency(): def test_function(): with expected_protocol( - ik.srs.SRS345, - [ - "FUNC?", - "FUNC 0" - ], - [ - "1", - ] + ik.srs.SRS345, + [ + "FUNC?", + "FUNC 0" + ], + [ + "1", + ] ) as inst: assert inst.function == inst.Function.square inst.function = inst.Function.sinusoid @@ -68,14 +68,14 @@ def test_function(): def test_offset(): with expected_protocol( - ik.srs.SRS345, - [ - "OFFS?", - "OFFS {:e}".format(0.1), - ], - [ - "1.234", - ] + ik.srs.SRS345, + [ + "OFFS?", + "OFFS {:e}".format(0.1), + ], + [ + "1.234", + ] ) as inst: assert inst.offset == 1.234 * pq.V inst.offset = 0.1 * pq.V @@ -83,14 +83,14 @@ def test_offset(): def test_phase(): with expected_protocol( - ik.srs.SRS345, - [ - "PHSE?", - "PHSE {:e}".format(0.1), - ], - [ - "1.234", - ] + ik.srs.SRS345, + [ + "PHSE?", + "PHSE {:e}".format(0.1), + ], + [ + "1.234", + ] ) as inst: assert inst.phase == 1.234 * pq.degree inst.phase = 0.1 * pq.degree diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index a339623ec..c7794acad 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -20,14 +20,14 @@ def test_frequency_source(): with expected_protocol( - ik.srs.SRS830, - [ - "FMOD?", - "FMOD 0" - ], - [ - "1", - ] + ik.srs.SRS830, + [ + "FMOD?", + "FMOD 0" + ], + [ + "1", + ] ) as inst: assert inst.frequency_source == inst.FreqSource.internal inst.frequency_source = inst.FreqSource.external @@ -35,14 +35,14 @@ def test_frequency_source(): def test_frequency(): with expected_protocol( - ik.srs.SRS830, - [ - "FREQ?", - "FREQ {:e}".format(1000) - ], - [ - "12.34", - ] + ik.srs.SRS830, + [ + "FREQ?", + "FREQ {:e}".format(1000) + ], + [ + "12.34", + ] ) as inst: assert inst.frequency == 12.34 * pq.Hz inst.frequency = 1 * pq.kHz @@ -50,14 +50,14 @@ def test_frequency(): def test_phase(): with expected_protocol( - ik.srs.SRS830, - [ - "PHAS?", - "PHAS {:e}".format(10) - ], - [ - "-45", - ] + ik.srs.SRS830, + [ + "PHAS?", + "PHAS {:e}".format(10) + ], + [ + "-45", + ] ) as inst: assert inst.phase == -45 * pq.degrees inst.phase = 10 * pq.degrees @@ -65,14 +65,14 @@ def test_phase(): def test_amplitude(): with expected_protocol( - ik.srs.SRS830, - [ - "SLVL?", - "SLVL {:e}".format(1) - ], - [ - "0.1", - ] + ik.srs.SRS830, + [ + "SLVL?", + "SLVL {:e}".format(1) + ], + [ + "0.1", + ] ) as inst: assert inst.amplitude == 0.1 * pq.V inst.amplitude = 1 * pq.V @@ -80,14 +80,14 @@ def test_amplitude(): def test_input_shield_ground(): with expected_protocol( - ik.srs.SRS830, - [ - "IGND?", - "IGND 1" - ], - [ - "0", - ] + ik.srs.SRS830, + [ + "IGND?", + "IGND 1" + ], + [ + "0", + ] ) as inst: assert inst.input_shield_ground is False inst.input_shield_ground = True @@ -95,14 +95,14 @@ def test_input_shield_ground(): def test_coupling(): with expected_protocol( - ik.srs.SRS830, - [ - "ICPL?", - "ICPL 0" - ], - [ - "1", - ] + ik.srs.SRS830, + [ + "ICPL?", + "ICPL 0" + ], + [ + "1", + ] ) as inst: assert inst.coupling == inst.Coupling.dc inst.coupling = inst.Coupling.ac @@ -110,17 +110,17 @@ def test_coupling(): def test_sample_rate(): # sends index of VALID_SAMPLE_RATES with expected_protocol( - ik.srs.SRS830, - [ - "SRAT?", - "SRAT?", - "SRAT {:d}".format(5), - "SRAT 14" - ], - [ - "8", - "14" - ] + ik.srs.SRS830, + [ + "SRAT?", + "SRAT?", + "SRAT {:d}".format(5), + "SRAT 14" + ], + [ + "8", + "14" + ] ) as inst: assert inst.sample_rate == 16 * pq.Hz assert inst.sample_rate == "trigger" @@ -129,25 +129,24 @@ def test_sample_rate(): # sends index of VALID_SAMPLE_RATES def test_sample_rate_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.sample_rate = "foobar" + ) as inst: + inst.sample_rate = "foobar" def test_buffer_mode(): with expected_protocol( - ik.srs.SRS830, - [ - "SEND?", - "SEND 1" - ], - [ - "0", - ] + ik.srs.SRS830, + [ + "SEND?", + "SEND 1" + ], + [ + "0", + ] ) as inst: assert inst.buffer_mode == inst.BufferMode.one_shot inst.buffer_mode = inst.BufferMode.loop @@ -155,27 +154,27 @@ def test_buffer_mode(): def test_num_data_points(): with expected_protocol( - ik.srs.SRS830, - [ - "SPTS?" - ], - [ - "5", - ] + ik.srs.SRS830, + [ + "SPTS?" + ], + [ + "5", + ] ) as inst: assert inst.num_data_points == 5 def test_data_transfer(): with expected_protocol( - ik.srs.SRS830, - [ - "FAST?", - "FAST 2" - ], - [ - "0", - ] + ik.srs.SRS830, + [ + "FAST?", + "FAST 2" + ], + [ + "0", + ] ) as inst: assert inst.data_transfer is False inst.data_transfer = True @@ -183,206 +182,199 @@ def test_data_transfer(): def test_auto_offset(): with expected_protocol( - ik.srs.SRS830, - [ - "AOFF 1", - "AOFF 1" - ], - [] + ik.srs.SRS830, + [ + "AOFF 1", + "AOFF 1" + ], + [] ) as inst: inst.auto_offset(inst.Mode.x) inst.auto_offset("x") def test_auto_offset_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [ "AOFF 1", ], [] - ) as inst: - inst.auto_offset(inst.Mode.theta) + ) as inst: + inst.auto_offset(inst.Mode.theta) def test_auto_phase(): with expected_protocol( - ik.srs.SRS830, - [ - "APHS" - ], - [] + ik.srs.SRS830, + [ + "APHS" + ], + [] ) as inst: inst.auto_phase() def test_init(): with expected_protocol( - ik.srs.SRS830, - [ - "REST", - "SRAT 5", - "SEND 1" - ], - [] + ik.srs.SRS830, + [ + "REST", + "SRAT 5", + "SEND 1" + ], + [] ) as inst: inst.init(sample_rate=2, buffer_mode=inst.BufferMode.loop) def test_start_data_transfer(): with expected_protocol( - ik.srs.SRS830, - [ - "FAST 2", - "STRD" - ], - [] + ik.srs.SRS830, + [ + "FAST 2", + "STRD" + ], + [] ) as inst: inst.start_data_transfer() def test_take_measurement(): with expected_protocol( - ik.srs.SRS830, - [ - "REST", - "SRAT 4", - "SEND 0", - "FAST 2", - "STRD", - "PAUS", - "SPTS?", - "SPTS?", - "TRCA?1,0,2", - "SPTS?", - "TRCA?2,0,2" - ], - [ - "2", - "2", - "1.234,5.678", - "2", - "0.456,5.321" - ] + ik.srs.SRS830, + [ + "REST", + "SRAT 4", + "SEND 0", + "FAST 2", + "STRD", + "PAUS", + "SPTS?", + "SPTS?", + "TRCA?1,0,2", + "SPTS?", + "TRCA?2,0,2" + ], + [ + "2", + "2", + "1.234,5.678", + "2", + "0.456,5.321" + ] ) as inst: resp = inst.take_measurement(sample_rate=1, num_samples=2) np.testing.assert_array_equal(resp, [[1.234, 5.678], [0.456, 5.321]]) def test_take_measurement_invalid_num_samples(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - _ = inst.take_measurement(sample_rate=1, num_samples=16384) + ) as inst: + _ = inst.take_measurement(sample_rate=1, num_samples=16384) def test_set_offset_expand(): with expected_protocol( - ik.srs.SRS830, - [ - "OEXP 1,0,0" - ], - [] + ik.srs.SRS830, + [ + "OEXP 1,0,0" + ], + [] ) as inst: inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=1) def test_set_offset_expand_mode_as_str(): with expected_protocol( - ik.srs.SRS830, - [ - "OEXP 1,0,0" - ], - [] + ik.srs.SRS830, + [ + "OEXP 1,0,0" + ], + [] ) as inst: inst.set_offset_expand(mode="x", offset=0, expand=1) def test_set_offset_expand_invalid_mode(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.theta, offset=0, expand=1) + ) as inst: + inst.set_offset_expand(mode=inst.Mode.theta, offset=0, expand=1) def test_set_offset_expand_invalid_offset(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset=106, expand=1) + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset=106, expand=1) def test_set_offset_expand_invalid_expand(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=5) + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=5) def test_set_offset_expand_invalid_type_offset(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset="derp", expand=1) + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset="derp", expand=1) def test_set_offset_expand_invalid_type_expand(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand="derp") + ) as inst: + inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand="derp") def test_start_scan(): with expected_protocol( - ik.srs.SRS830, - [ - "STRD" - ], - [] + ik.srs.SRS830, + [ + "STRD" + ], + [] ) as inst: inst.start_scan() def test_pause(): with expected_protocol( - ik.srs.SRS830, - [ - "PAUS" - ], - [] + ik.srs.SRS830, + [ + "PAUS" + ], + [] ) as inst: inst.pause() def test_data_snap(): with expected_protocol( - ik.srs.SRS830, - [ - "SNAP? 1,2" - ], - [ - "1.234,9.876" - ] + ik.srs.SRS830, + [ + "SNAP? 1,2" + ], + [ + "1.234,9.876" + ] ) as inst: data = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.y) expected = [1.234, 9.876] @@ -391,13 +383,13 @@ def test_data_snap(): def test_data_snap_mode_as_str(): with expected_protocol( - ik.srs.SRS830, - [ - "SNAP? 1,2" - ], - [ - "1.234,9.876" - ] + ik.srs.SRS830, + [ + "SNAP? 1,2" + ], + [ + "1.234,9.876" + ] ) as inst: data = inst.data_snap(mode1='x', mode2='y') expected = [1.234, 9.876] @@ -405,46 +397,43 @@ def test_data_snap_mode_as_str(): def test_data_snap_invalid_snap_mode1(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - _ = inst.data_snap(mode1=inst.Mode.xnoise, mode2=inst.Mode.y) + ) as inst: + _ = inst.data_snap(mode1=inst.Mode.xnoise, mode2=inst.Mode.y) def test_data_snap_invalid_snap_mode2(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.ynoise) + ) as inst: + _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.ynoise) def test_data_snap_identical_modes(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.x) + ) as inst: + _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.x) def test_read_data_buffer(): with expected_protocol( - ik.srs.SRS830, - [ - "SPTS?", - "TRCA?1,0,2" - ], - [ - "2", - "1.234,9.876" - ] + ik.srs.SRS830, + [ + "SPTS?", + "TRCA?1,0,2" + ], + [ + "2", + "1.234,9.876" + ] ) as inst: data = inst.read_data_buffer(channel=inst.Mode.ch1) expected = [1.234, 9.876] @@ -453,15 +442,15 @@ def test_read_data_buffer(): def test_read_data_buffer_mode_as_str(): with expected_protocol( - ik.srs.SRS830, - [ - "SPTS?", - "TRCA?1,0,2" - ], - [ - "2", - "1.234,9.876" - ] + ik.srs.SRS830, + [ + "SPTS?", + "TRCA?1,0,2" + ], + [ + "2", + "1.234,9.876" + ] ) as inst: data = inst.read_data_buffer(channel="ch1") expected = [1.234, 9.876] @@ -469,33 +458,32 @@ def test_read_data_buffer_mode_as_str(): def test_read_data_buffer_invalid_mode(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - _ = inst.read_data_buffer(channel=inst.Mode.x) + ) as inst: + _ = inst.read_data_buffer(channel=inst.Mode.x) def test_clear_data_buffer(): with expected_protocol( - ik.srs.SRS830, - [ - "REST" - ], - [] + ik.srs.SRS830, + [ + "REST" + ], + [] ) as inst: inst.clear_data_buffer() def test_set_channel_display(): with expected_protocol( - ik.srs.SRS830, - [ - "DDEF 1,0,0" - ], - [] + ik.srs.SRS830, + [ + "DDEF 1,0,0" + ], + [] ) as inst: inst.set_channel_display( channel=inst.Mode.ch1, @@ -506,11 +494,11 @@ def test_set_channel_display(): def test_set_channel_display_params_as_str(): with expected_protocol( - ik.srs.SRS830, - [ - "DDEF 1,0,0" - ], - [] + ik.srs.SRS830, + [ + "DDEF 1,0,0" + ], + [] ) as inst: inst.set_channel_display( channel="ch1", @@ -520,42 +508,39 @@ def test_set_channel_display_params_as_str(): def test_set_channel_display_invalid_channel(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_channel_display( - channel=inst.Mode.x, - display=inst.Mode.x, - ratio=inst.Mode.none - ) + ) as inst: + inst.set_channel_display( + channel=inst.Mode.x, + display=inst.Mode.x, + ratio=inst.Mode.none + ) def test_set_channel_display_invalid_display(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_channel_display( - channel=inst.Mode.ch1, - display=inst.Mode.y, # y is only valid for ch2, not ch1! - ratio=inst.Mode.none - ) + ) as inst: + inst.set_channel_display( + channel=inst.Mode.ch1, + display=inst.Mode.y, # y is only valid for ch2, not ch1! + ratio=inst.Mode.none + ) def test_set_channel_display_invalid_ratio(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, [], [] - ) as inst: - inst.set_channel_display( - channel=inst.Mode.ch1, - display=inst.Mode.x, - ratio=inst.Mode.xnoise - ) + ) as inst: + inst.set_channel_display( + channel=inst.Mode.ch1, + display=inst.Mode.x, + ratio=inst.Mode.xnoise + ) diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index 69727a09f..d842eef82 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -23,17 +23,17 @@ def test_srsdg645_output_level(): SRSDG645: Checks getting/setting unitful output level. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "LAMP? 1", - "LAMP 1,4.0", - ], - [ - "3.2" - ] + ik.srs.SRSDG645, + [ + "LAMP? 1", + "LAMP 1,4.0", + ], + [ + "3.2" + ] ) as ddg: - unit_eq(ddg.output['AB'].level_amplitude, pq.Quantity(3.2, "V")) - ddg.output['AB'].level_amplitude = 4.0 + unit_eq(ddg.output["AB"].level_amplitude, pq.Quantity(3.2, "V")) + ddg.output["AB"].level_amplitude = 4.0 def test_srsdg645_output_offset(): @@ -41,17 +41,17 @@ def test_srsdg645_output_offset(): SRSDG645: Checks getting/setting unitful output offset. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "LOFF? 1", - "LOFF 1,2.0", - ], - [ - "1.2" - ] + ik.srs.SRSDG645, + [ + "LOFF? 1", + "LOFF 1,2.0", + ], + [ + "1.2" + ] ) as ddg: - unit_eq(ddg.output['AB'].level_offset, pq.Quantity(1.2, "V")) - ddg.output['AB'].level_offset = 2.0 + unit_eq(ddg.output["AB"].level_offset, pq.Quantity(1.2, "V")) + ddg.output["AB"].level_offset = 2.0 def test_srsdg645_output_polarity(): @@ -59,22 +59,22 @@ def test_srsdg645_output_polarity(): SRSDG645: Checks getting/setting """ with expected_protocol( - ik.srs.SRSDG645, - [ - "LPOL? 1", - "LPOL 2,0" - ], - [ - "1" - ] + ik.srs.SRSDG645, + [ + "LPOL? 1", + "LPOL 2,0" + ], + [ + "1" + ] ) as ddg: - assert ddg.output['AB'].polarity == ddg.LevelPolarity.positive - ddg.output['CD'].polarity = ddg.LevelPolarity.negative + assert ddg.output["AB"].polarity == ddg.LevelPolarity.positive + ddg.output["CD"].polarity = ddg.LevelPolarity.negative def test_srsdg645_trigger_source(): with expected_protocol(ik.srs.SRSDG645, "DLAY?2\nDLAY 3,2,60.0\n", "0,42\n") as ddg: - ref, t = ddg.channel['A'].delay + ref, t = ddg.channel["A"].delay assert ref == ddg.Channels.T0 - assert abs((t - pq.Quantity(42, 's')).magnitude) < 1e5 - ddg.channel['B'].delay = (ddg.channel['A'], pq.Quantity(1, "minute")) + assert abs((t - pq.Quantity(42, "s")).magnitude) < 1e5 + ddg.channel["B"].delay = (ddg.channel["A"], pq.Quantity(1, "minute")) diff --git a/instruments/tests/test_tektronix/test_tektronix_tds224.py b/instruments/tests/test_tektronix/test_tektronix_tds224.py index fa9830644..8e466215c 100644 --- a/instruments/tests/test_tektronix/test_tektronix_tds224.py +++ b/instruments/tests/test_tektronix/test_tektronix_tds224.py @@ -23,13 +23,13 @@ def test_tektds224_data_width(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DATA:WIDTH?", - "DATA:WIDTH 1" - ], [ - "2" - ] + ik.tektronix.TekTDS224, + [ + "DATA:WIDTH?", + "DATA:WIDTH 1" + ], [ + "2" + ] ) as tek: assert tek.data_width == 2 tek.data_width = 1 @@ -37,13 +37,13 @@ def test_tektds224_data_width(): def test_tektds224_data_source(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DAT:SOU?", - "DAT:SOU MATH" - ], [ - "CH1" - ] + ik.tektronix.TekTDS224, + [ + "DAT:SOU?", + "DAT:SOU MATH" + ], [ + "CH1" + ] ) as tek: assert tek.data_source == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) tek.data_source = tek.math @@ -51,9 +51,9 @@ def test_tektds224_data_source(): def test_tektds224_channel(): with expected_protocol( - ik.tektronix.TekTDS224, - [], - [] + ik.tektronix.TekTDS224, + [], + [] ) as tek: assert tek.channel[ 0] == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) @@ -61,13 +61,13 @@ def test_tektds224_channel(): def test_tektds224_channel_coupling(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "CH1:COUPL?", - "CH2:COUPL AC" - ], [ - "DC" - ] + ik.tektronix.TekTDS224, + [ + "CH1:COUPL?", + "CH2:COUPL AC" + ], [ + "DC" + ] ) as tek: assert tek.channel[0].coupling == tek.Coupling.dc tek.channel[1].coupling = tek.Coupling.ac @@ -75,31 +75,31 @@ def test_tektds224_channel_coupling(): def test_tektds224_data_source_read_waveform(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DAT:SOU?", - "DAT:SOU CH2", - "DAT:ENC RIB", - "DATA:WIDTH?", - "CURVE?", - "WFMP:CH2:YOF?", - "WFMP:CH2:YMU?", - "WFMP:CH2:YZE?", - "WFMP:XZE?", - "WFMP:XIN?", - "WFMP:CH2:NR_P?", - "DAT:SOU CH1" - ], [ - "CH1", - "2", - # pylint: disable=no-member - "#210" + bytes.fromhex("00000001000200030004").decode("utf-8") + "0", - "1", - "0", - "0", - "1", - "5" - ] + ik.tektronix.TekTDS224, + [ + "DAT:SOU?", + "DAT:SOU CH2", + "DAT:ENC RIB", + "DATA:WIDTH?", + "CURVE?", + "WFMP:CH2:YOF?", + "WFMP:CH2:YMU?", + "WFMP:CH2:YZE?", + "WFMP:XZE?", + "WFMP:XIN?", + "WFMP:CH2:NR_P?", + "DAT:SOU CH1" + ], [ + "CH1", + "2", + # pylint: disable=no-member + "#210" + bytes.fromhex("00000001000200030004").decode("utf-8") + "0", + "1", + "0", + "0", + "1", + "5" + ] ) as tek: data = np.array([0, 1, 2, 3, 4]) (x, y) = tek.channel[1].read_waveform() diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py index 286d585f0..82db76047 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_apt.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -27,42 +27,42 @@ def test_apt_hw_info(): with expected_protocol( - ik.thorlabs.ThorLabsAPT, - [ - ThorLabsPacket( - message_id=ThorLabsCommands.HW_REQ_INFO, - param1=0x00, param2=0x00, - dest=0x50, - source=0x01, - data=None - ).pack() - ], - [ - ThorLabsPacket( - message_id=ThorLabsCommands.HW_GET_INFO, - dest=0x01, - source=0x50, - data=hw_info_data.pack( - # Serial number - b'\x01\x02\x03\x04', - # Model number - "ABC-123".encode('ascii'), - # HW type - 3, - # FW version, - 0xa1, 0xa2, 0xa3, - # Notes - "abcdefg".encode('ascii'), - # HW version - 42, - # Mod state - 43, - # Number of channels - 2 - ) - ).pack() - ], - sep="" + ik.thorlabs.ThorLabsAPT, + [ + ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + ], + [ + ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "ABC-123".encode('ascii'), + # HW type + 3, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 2 + ) + ).pack() + ], + sep="" ) as apt: # Check internal representations. # NB: we shouldn't do this in some sense, but these fields diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index 1865b5f51..7d591bee8 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -19,43 +19,42 @@ def test_lcc25_name(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "*idn?" - ], - [ - "*idn?", - "bloopbloop", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "*idn?" + ], + [ + "*idn?", + "bloopbloop", + ">" + ], + sep="\r" ) as lcc: name = lcc.name - assert name == "bloopbloop", "got {} expected bloopbloop".format(name) + assert name == "bloopbloop", f"got {name} expected bloopbloop" def test_lcc25_frequency(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "freq?", - "freq=10.0" - ], - [ - "freq?", - "20", - ">freq=10.0", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "freq?", + "freq=10.0" + ], + [ + "freq?", + "20", + ">freq=10.0", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.frequency, pq.Quantity(20, "Hz")) lcc.frequency = 10.0 def test_lcc25_frequency_lowlimit(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [ "freq=0.0" @@ -65,13 +64,12 @@ def test_lcc25_frequency_lowlimit(): ">" ], sep="\r" - ) as lcc: - lcc.frequency = 0.0 + ) as lcc: + lcc.frequency = 0.0 def test_lcc25_frequency_highlimit(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [ "freq=160.0" @@ -81,140 +79,136 @@ def test_lcc25_frequency_highlimit(): ">" ], sep="\r" - ) as lcc: - lcc.frequency = 160.0 + ) as lcc: + lcc.frequency = 160.0 def test_lcc25_mode(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "mode?", - "mode=1" - ], - [ - "mode?", - "2", - ">mode=1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "mode?", + "mode=1" + ], + [ + "mode?", + "2", + ">mode=1", + ">" + ], + sep="\r" ) as lcc: assert lcc.mode == ik.thorlabs.LCC25.Mode.voltage2 lcc.mode = ik.thorlabs.LCC25.Mode.voltage1 def test_lcc25_mode_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [], [] - ) as lcc: - lcc.mode = "blo" + ) as lcc: + lcc.mode = "blo" def test_lcc25_enable(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "enable?", - "enable=1" - ], - [ - "enable?", - "0", - ">enable=1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "enable?", + "enable=1" + ], + [ + "enable?", + "0", + ">enable=1", + ">" + ], + sep="\r" ) as lcc: assert lcc.enable is False lcc.enable = True def test_lcc25_enable_invalid_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.LCC25, [], [] - ) as lcc: - lcc.enable = "blo" + ) as lcc: + lcc.enable = "blo" def test_lcc25_extern(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "extern?", - "extern=1" - ], - [ - "extern?", - "0", - ">extern=1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "extern?", + "extern=1" + ], + [ + "extern?", + "0", + ">extern=1", + ">" + ], + sep="\r" ) as lcc: assert lcc.extern is False lcc.extern = True def test_tc200_extern_invalid_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.LCC25, [], [] - ) as tc: - tc.extern = "blo" + ) as tc: + tc.extern = "blo" def test_lcc25_remote(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "remote?", - "remote=1" - ], - [ - "remote?", - "0", - ">remote=1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "remote?", + "remote=1" + ], + [ + "remote?", + "0", + ">remote=1", + ">" + ], + sep="\r" ) as lcc: assert lcc.remote is False lcc.remote = True def test_tc200_remote_invalid_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.LCC25, [], [] - ) as tc: - tc.remote = "blo" + ) as tc: + tc.remote = "blo" def test_lcc25_voltage1(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "volt1?", - "volt1=10.0" - ], - [ - "volt1?", - "20", - ">volt1=10.0", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "volt1?", + "volt1=10.0" + ], + [ + "volt1?", + "20", + ">volt1=10.0", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.voltage1, pq.Quantity(20, "V")) lcc.voltage1 = 10.0 @@ -228,18 +222,18 @@ def test_check_cmd(): def test_lcc25_voltage2(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "volt2?", - "volt2=10.0", - ], - [ - "volt2?", - "20", - ">volt2=10.0", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "volt2?", + "volt2=10.0", + ], + [ + "volt2?", + "20", + ">volt2=10.0", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.voltage2, pq.Quantity(20, "V")) lcc.voltage2 = 10.0 @@ -247,18 +241,18 @@ def test_lcc25_voltage2(): def test_lcc25_minvoltage(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "min?", - "min=10.0" - ], - [ - "min?", - "20", - ">min=10.0", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "min?", + "min=10.0" + ], + [ + "min?", + "20", + ">min=10.0", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.min_voltage, pq.Quantity(20, "V")) lcc.min_voltage = 10.0 @@ -266,18 +260,18 @@ def test_lcc25_minvoltage(): def test_lcc25_maxvoltage(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "max?", - "max=10.0" - ], - [ - "max?", - "20", - ">max=10.0", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "max?", + "max=10.0" + ], + [ + "max?", + "20", + ">max=10.0", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.max_voltage, pq.Quantity(20, "V")) lcc.max_voltage = 10.0 @@ -285,26 +279,25 @@ def test_lcc25_maxvoltage(): def test_lcc25_dwell(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "dwell?", - "dwell=10" - ], - [ - "dwell?", - "20", - ">dwell=10", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "dwell?", + "dwell=10" + ], + [ + "dwell?", + "20", + ">dwell=10", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.dwell, pq.Quantity(20, "ms")) lcc.dwell = 10 def test_lcc25_dwell_positive(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [ "dwell=-10" @@ -314,32 +307,31 @@ def test_lcc25_dwell_positive(): ">" ], sep="\r" - ) as lcc: - lcc.dwell = -10 + ) as lcc: + lcc.dwell = -10 def test_lcc25_increment(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "increment?", - "increment=10.0" - ], - [ - "increment?", - "20", - ">increment=10.0", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "increment?", + "increment=10.0" + ], + [ + "increment?", + "20", + ">increment=10.0", + ">" + ], + sep="\r" ) as lcc: unit_eq(lcc.increment, pq.Quantity(20, "V")) lcc.increment = 10.0 def test_lcc25_increment_positive(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [ "increment=-10" @@ -349,127 +341,123 @@ def test_lcc25_increment_positive(): ">" ], sep="\r" - ) as lcc: - lcc.increment = -10 + ) as lcc: + lcc.increment = -10 def test_lcc25_default(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "default" - ], - [ - "default", - "1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "default" + ], + [ + "default", + "1", + ">" + ], + sep="\r" ) as lcc: lcc.default() def test_lcc25_save(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "save" - ], - [ - "save", - "1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "save" + ], + [ + "save", + "1", + ">" + ], + sep="\r" ) as lcc: lcc.save() def test_lcc25_set_settings(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "set=2" - ], - [ - "set=2", - "1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "set=2" + ], + [ + "set=2", + "1", + ">" + ], + sep="\r" ) as lcc: lcc.set_settings(2) def test_lcc25_set_settings_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [], [], sep="\r" - ) as lcc: - lcc.set_settings(5) + ) as lcc: + lcc.set_settings(5) def test_lcc25_get_settings(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "get=2" - ], - [ - "get=2", - "1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "get=2" + ], + [ + "get=2", + "1", + ">" + ], + sep="\r" ) as lcc: lcc.get_settings(2) def test_lcc25_get_settings_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.LCC25, [], [], sep="\r" - ) as lcc: - lcc.get_settings(5) + ) as lcc: + lcc.get_settings(5) def test_lcc25_test_mode(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "test" - ], - [ - "test", - "1", - ">" - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "test" + ], + [ + "test", + "1", + ">" + ], + sep="\r" ) as lcc: lcc.test_mode() def test_lcc25_remote_invalid_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.LCC25, [], [] - ) as lcc: - lcc.remote = "blo" + ) as lcc: + lcc.remote = "blo" def test_lcc25_extern_invalid_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.LCC25, [], [] - ) as lcc: - lcc.extern = "blo" + ) as lcc: + lcc.extern = "blo" diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 4f1c16003..8714cacd3 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -19,124 +19,121 @@ def test_sc10_name(): with expected_protocol( - ik.thorlabs.SC10, - [ - "id?" - ], - [ - "id?", - "bloopbloop", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "id?" + ], + [ + "id?", + "bloopbloop", + ">" + ], + sep="\r" ) as sc: assert sc.name == "bloopbloop" def test_sc10_enable(): with expected_protocol( - ik.thorlabs.SC10, - [ - "ens?", - "ens=1" - ], - [ - "ens?", - "0", - ">ens=1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "ens?", + "ens=1" + ], + [ + "ens?", + "0", + ">ens=1", + ">" + ], + sep="\r" ) as sc: assert sc.enable is False sc.enable = True def test_sc10_enable_invalid(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.SC10, [], [], sep="\r" - ) as sc: - sc.enable = 10 + ) as sc: + sc.enable = 10 def test_sc10_repeat(): with expected_protocol( - ik.thorlabs.SC10, - [ - "rep?", - "rep=10" - ], - [ - "rep?", - "20", - ">rep=10", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "rep?", + "rep=10" + ], + [ + "rep?", + "20", + ">rep=10", + ">" + ], + sep="\r" ) as sc: assert sc.repeat == 20 sc.repeat = 10 def test_sc10_repeat_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.SC10, [], [], sep="\r" - ) as sc: - sc.repeat = -1 + ) as sc: + sc.repeat = -1 def test_sc10_mode(): with expected_protocol( - ik.thorlabs.SC10, - [ - "mode?", - "mode=2" - ], - [ - "mode?", - "1", - ">mode=2", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "mode?", + "mode=2" + ], + [ + "mode?", + "1", + ">mode=2", + ">" + ], + sep="\r" ) as sc: assert sc.mode == ik.thorlabs.SC10.Mode.manual sc.mode = ik.thorlabs.SC10.Mode.auto def test_sc10_mode_invalid(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.SC10, [], [], sep="\r" - ) as sc: - sc.mode = "blo" + ) as sc: + sc.mode = "blo" def test_sc10_trigger(): with expected_protocol( - ik.thorlabs.SC10, - [ - "trig?", - "trig=1" - ], - [ - "trig?", - "0", - ">trig=1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "trig?", + "trig=1" + ], + [ + "trig?", + "0", + ">trig=1", + ">" + ], + sep="\r" ) as sc: assert sc.trigger == 0 sc.trigger = 1 @@ -144,18 +141,18 @@ def test_sc10_trigger(): def test_sc10_out_trigger(): with expected_protocol( - ik.thorlabs.SC10, - [ - "xto?", - "xto=1" - ], - [ - "xto?", - "0", - ">xto=1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "xto?", + "xto=1" + ], + [ + "xto?", + "0", + ">xto=1", + ">" + ], + sep="\r" ) as sc: assert sc.out_trigger == 0 sc.out_trigger = 1 @@ -163,18 +160,18 @@ def test_sc10_out_trigger(): def test_sc10_open_time(): with expected_protocol( - ik.thorlabs.SC10, - [ - "open?", - "open=10" - ], - [ - "open?", - "20", - ">open=10", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "open?", + "open=10" + ], + [ + "open?", + "20", + ">open=10", + ">" + ], + sep="\r" ) as sc: unit_eq(sc.open_time, pq.Quantity(20, "ms")) sc.open_time = 10 @@ -182,18 +179,18 @@ def test_sc10_open_time(): def test_sc10_shut_time(): with expected_protocol( - ik.thorlabs.SC10, - [ - "shut?", - "shut=10" - ], - [ - "shut?", - "20", - ">shut=10", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "shut?", + "shut=10" + ], + [ + "shut?", + "20", + ">shut=10", + ">" + ], + sep="\r" ) as sc: unit_eq(sc.shut_time, pq.Quantity(20, "ms")) sc.shut_time = 10.0 @@ -201,125 +198,124 @@ def test_sc10_shut_time(): def test_sc10_baud_rate(): with expected_protocol( - ik.thorlabs.SC10, - [ - "baud?", - "baud=1" - ], - [ - "baud?", - "0", - ">baud=1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "baud?", + "baud=1" + ], + [ + "baud?", + "0", + ">baud=1", + ">" + ], + sep="\r" ) as sc: assert sc.baud_rate == 9600 sc.baud_rate = 115200 def test_sc10_baud_rate_error(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.SC10, [], [], sep="\r" - ) as sc: - sc.baud_rate = 115201 + ) as sc: + sc.baud_rate = 115201 def test_sc10_closed(): with expected_protocol( - ik.thorlabs.SC10, - [ - "closed?" - ], - [ - "closed?", - "1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "closed?" + ], + [ + "closed?", + "1", + ">" + ], + sep="\r" ) as sc: assert sc.closed def test_sc10_interlock(): with expected_protocol( - ik.thorlabs.SC10, - [ - "interlock?" - ], - [ - "interlock?", - "1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "interlock?" + ], + [ + "interlock?", + "1", + ">" + ], + sep="\r" ) as sc: assert sc.interlock def test_sc10_default(): with expected_protocol( - ik.thorlabs.SC10, - [ - "default" - ], - [ - "default", - "1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "default" + ], + [ + "default", + "1", + ">" + ], + sep="\r" ) as sc: assert sc.default() def test_sc10_save(): with expected_protocol( - ik.thorlabs.SC10, - [ - "savp" - ], - [ - "savp", - "1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "savp" + ], + [ + "savp", + "1", + ">" + ], + sep="\r" ) as sc: assert sc.save() def test_sc10_save_mode(): with expected_protocol( - ik.thorlabs.SC10, - [ - "save" - ], - [ - "save", - "1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "save" + ], + [ + "save", + "1", + ">" + ], + sep="\r" ) as sc: assert sc.save_mode() def test_sc10_restore(): with expected_protocol( - ik.thorlabs.SC10, - [ - "resp" - ], - [ - "resp", - "1", - ">" - ], - sep="\r" + ik.thorlabs.SC10, + [ + "resp" + ], + [ + "resp", + "1", + ">" + ], + sep="\r" ) as sc: assert sc.restore() diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index ac3766d97..4625d1765 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -20,35 +20,35 @@ def test_tc200_name(): with expected_protocol( - ik.thorlabs.TC200, - [ - "*idn?" - ], - [ - "*idn?", - "bloopbloop", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "*idn?" + ], + [ + "*idn?", + "bloopbloop", + "> " + ], + sep="\r" ) as tc: assert tc.name() == "bloopbloop" def test_tc200_mode(): with expected_protocol( - ik.thorlabs.TC200, - [ - "stat?", - "stat?", - "mode=cycle" - ], - [ - "stat?", - "0 > stat?", - "2 > mode=cycle", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "stat?", + "stat?", + "mode=cycle" + ], + [ + "stat?", + "0 > stat?", + "2 > mode=cycle", + "> " + ], + sep="\r" ) as tc: assert tc.mode == tc.Mode.normal assert tc.mode == tc.Mode.cycle @@ -57,64 +57,62 @@ def test_tc200_mode(): def test_tc200_mode_2(): with expected_protocol( - ik.thorlabs.TC200, - [ - "mode=normal" - ], - [ - "mode=normal", - "Command error CMD_ARG_RANGE_ERR\n", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "mode=normal" + ], + [ + "mode=normal", + "Command error CMD_ARG_RANGE_ERR\n", + "> " + ], + sep="\r" ) as tc: tc.mode = ik.thorlabs.TC200.Mode.normal def test_tc200_mode_error(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.TC200, [], [], sep="\r" - ) as tc: - tc.mode = "blo" + ) as tc: + tc.mode = "blo" def test_tc200_mode_error2(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.TC200, [], [], sep="\r" - ) as tc: - class TestEnum(IntEnum): - blo = 1 - beep = 2 - tc.mode = TestEnum.blo + ) as tc: + class TestEnum(IntEnum): + blo = 1 + beep = 2 + tc.mode = TestEnum.blo def test_tc200_enable(): with expected_protocol( - ik.thorlabs.TC200, - [ - "stat?", - "stat?", - "ens", - "stat?", - "ens" - ], - [ - "stat?", - "54 > stat?", - "54 > ens", - "> stat?", - "55 > ens", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "stat?", + "stat?", + "ens", + "stat?", + "ens" + ], + [ + "stat?", + "54 > stat?", + "54 > ens", + "> stat?", + "55 > ens", + "> " + ], + sep="\r" ) as tc: assert tc.enable == 0 tc.enable = True @@ -122,57 +120,55 @@ def test_tc200_enable(): def test_tc200_enable_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.TC200, [], [], sep="\r" - ) as tc: - tc.enable = "blo" + ) as tc: + tc.enable = "blo" def test_tc200_temperature(): with expected_protocol( - ik.thorlabs.TC200, - [ - "tact?", - ], - [ - "tact?", - "30 C", - "> ", - ], - sep="\r" + ik.thorlabs.TC200, + [ + "tact?", + ], + [ + "tact?", + "30 C", + "> ", + ], + sep="\r" ) as tc: assert tc.temperature == 30.0 * pq.degC def test_tc200_temperature_set(): with expected_protocol( - ik.thorlabs.TC200, - [ - "tset?", - "tmax?", - "tset=40.0" - ], - [ - "tset?", - "30 C", - "> tmax?", - "250", - "> tset=40.0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "tset?", + "tmax?", + "tset=40.0" + ], + [ + "tset?", + "30 C", + "> tmax?", + "250", + "> tset=40.0", + "> " + ], + sep="\r" ) as tc: assert tc.temperature_set == 30.0 * pq.degC tc.temperature_set = 40 * pq.degC def test_tc200_temperature_range(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "tmax?" @@ -183,98 +179,96 @@ def test_tc200_temperature_range(): "> " ], sep="\r" - ) as tc: - tc.temperature_set = 50 * pq.degC + ) as tc: + tc.temperature_set = 50 * pq.degC def test_tc200_pid(): with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "pgain=2" - ], - [ - "pid?", - "2 0 220", - "> pgain=2", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "pid?", + "pgain=2" + ], + [ + "pid?", + "2 0 220", + "> pgain=2", + "> " + ], + sep="\r" ) as tc: assert tc.p == 2 tc.p = 2 with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "igain=0" - ], - [ - "pid?", - "2 0 220", - "> igain=0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "pid?", + "igain=0" + ], + [ + "pid?", + "2 0 220", + "> igain=0", + "> " + ], + sep="\r" ) as tc: assert tc.i == 0 tc.i = 0 with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "dgain=220" - ], - [ - "pid?", - "2 0 220", - "> dgain=220", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "pid?", + "dgain=220" + ], + [ + "pid?", + "2 0 220", + "> dgain=220", + "> " + ], + sep="\r" ) as tc: assert tc.d == 220 tc.d = 220 with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "pgain=2", - "igain=0", - "dgain=220" - ], - [ - "pid?", - "2 0 220", - "> pgain=2", - "> igain=0", - "> dgain=220", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "pid?", + "pgain=2", + "igain=0", + "dgain=220" + ], + [ + "pid?", + "2 0 220", + "> pgain=2", + "> igain=0", + "> dgain=220", + "> " + ], + sep="\r" ) as tc: assert tc.pid == [2, 0, 220] tc.pid = (2, 0, 220) def test_tc200_pid_invalid_type(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.TC200, [], [], sep="\r" - ) as tc: - tc.pid = "foo" + ) as tc: + tc.pid = "foo" def test_tc200_pmin(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "pgain=-1" @@ -284,13 +278,12 @@ def test_tc200_pmin(): "> " ], sep="\r" - ) as tc: - tc.p = -1 + ) as tc: + tc.p = -1 def test_tc200_pmax(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "pgain=260" @@ -300,13 +293,12 @@ def test_tc200_pmax(): "> " ], sep="\r" - ) as tc: - tc.p = 260 + ) as tc: + tc.p = 260 def test_tc200_imin(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "igain=-1" @@ -316,13 +308,12 @@ def test_tc200_imin(): "> " ], sep="\r" - ) as tc: - tc.i = -1 + ) as tc: + tc.i = -1 def test_tc200_imax(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "igain=260" @@ -332,13 +323,12 @@ def test_tc200_imax(): "> " ], sep="\r" - ) as tc: - tc.i = 260 + ) as tc: + tc.i = 260 def test_tc200_dmin(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "dgain=-1" @@ -348,13 +338,12 @@ def test_tc200_dmin(): "> " ], sep="\r" - ) as tc: - tc.d = -1 + ) as tc: + tc.d = -1 def test_tc200_dmax(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "dgain=260" @@ -364,31 +353,31 @@ def test_tc200_dmax(): "> " ], sep="\r" - ) as tc: - tc.d = 260 + ) as tc: + tc.d = 260 def test_tc200_degrees(): with expected_protocol( - ik.thorlabs.TC200, - [ - "stat?", - "stat?", - "stat?", - "unit=c", - "unit=f", - "unit=k" - ], - [ - "stat?", - "44 > stat?", - "54 > stat?", - "0 > unit=c", - "> unit=f", - "> unit=k", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "stat?", + "stat?", + "stat?", + "unit=c", + "unit=f", + "unit=k" + ], + [ + "stat?", + "44 > stat?", + "54 > stat?", + "0 > unit=c", + "> unit=f", + "> unit=k", + "> " + ], + sep="\r" ) as tc: assert str(tc.degrees).split(" ")[1] == "K" assert str(tc.degrees).split(" ")[1] == "degC" @@ -400,80 +389,76 @@ def test_tc200_degrees(): def test_tc200_degrees_invalid(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.thorlabs.TC200, [], [], sep="\r" - ) as tc: - tc.degrees = "blo" + ) as tc: + tc.degrees = "blo" def test_tc200_sensor(): with expected_protocol( - ik.thorlabs.TC200, - [ - "sns?", - "sns=ptc100" - ], - [ - "sns?", - "Sensor = NTC10K, Beta = 5600", - "> sns=ptc100", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "sns?", + "sns=ptc100" + ], + [ + "sns?", + "Sensor = NTC10K, Beta = 5600", + "> sns=ptc100", + "> " + ], + sep="\r" ) as tc: assert tc.sensor == tc.Sensor.ntc10k tc.sensor = tc.Sensor.ptc100 def test_tc200_sensor_error(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [], [] - ) as tc: - tc.sensor = "blo" + ) as tc: + tc.sensor = "blo" def test_tc200_sensor_error2(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [], [] - ) as tc: - class TestEnum(IntEnum): - blo = 1 - beep = 2 - tc.sensor = TestEnum.blo + ) as tc: + class TestEnum(IntEnum): + blo = 1 + beep = 2 + tc.sensor = TestEnum.blo def test_tc200_beta(): with expected_protocol( - ik.thorlabs.TC200, - [ - "beta?", - "beta=2000" - ], - [ - "beta?", - "5600", - "> beta=2000", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "beta?", + "beta=2000" + ], + [ + "beta?", + "5600", + "> beta=2000", + "> " + ], + sep="\r" ) as tc: assert tc.beta == 5600 tc.beta = 2000 def test_tc200_beta_min(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "beta=200" @@ -483,13 +468,12 @@ def test_tc200_beta_min(): "> " ], sep="\r" - ) as tc: - tc.beta = 200 + ) as tc: + tc.beta = 200 def test_tc200_beta_max(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "beta=20000" @@ -499,32 +483,31 @@ def test_tc200_beta_max(): "> " ], sep="\r" - ) as tc: - tc.beta = 20000 + ) as tc: + tc.beta = 20000 def test_tc200_max_power(): with expected_protocol( - ik.thorlabs.TC200, - [ - "pmax?", - "pmax=12.0" - ], - [ - "pmax?", - "15.0", - "> pmax=12.0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "pmax?", + "pmax=12.0" + ], + [ + "pmax?", + "15.0", + "> pmax=12.0", + "> " + ], + sep="\r" ) as tc: assert tc.max_power == 15.0 * pq.W tc.max_power = 12 * pq.W def test_tc200_power_min(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "PMAX=-2" @@ -534,13 +517,12 @@ def test_tc200_power_min(): "> " ], sep="\r" - ) as tc: - tc.max_power = -1 + ) as tc: + tc.max_power = -1 def test_tc200_power_max(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "PMAX=20000" @@ -550,32 +532,31 @@ def test_tc200_power_max(): "> " ], sep="\r" - ) as tc: - tc.max_power = 20000 + ) as tc: + tc.max_power = 20000 def test_tc200_max_temperature(): with expected_protocol( - ik.thorlabs.TC200, - [ - "tmax?", - "tmax=180.0" - ], - [ - "tmax?", - "200.0", - "> tmax=180.0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + [ + "tmax?", + "tmax=180.0" + ], + [ + "tmax?", + "200.0", + "> tmax=180.0", + "> " + ], + sep="\r" ) as tc: assert tc.max_temperature == 200.0 * pq.degC tc.max_temperature = 180 * pq.degC def test_tc200_temp_min(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "TMAX=-2" @@ -585,13 +566,12 @@ def test_tc200_temp_min(): ">" ], sep="\r" - ) as tc: - tc.max_temperature = -1 + ) as tc: + tc.max_temperature = -1 def test_tc200_temp_max(): - with pytest.raises(ValueError): - with expected_protocol( + with pytest.raises(ValueError), expected_protocol( ik.thorlabs.TC200, [ "TMAX=20000" @@ -601,5 +581,5 @@ def test_tc200_temp_max(): ">" ], sep="\r" - ) as tc: - tc.max_temperature = 20000 + ) as tc: + tc.max_temperature = 20000 diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index 2b0ebe639..e6bc81216 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -20,19 +20,19 @@ def test_laser_serial_number(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:serial-number)", - "(param-ref 'laser2:serial-number)" - ], - [ - "(param-ref 'laser1:serial-number)", - "bloop1", - "> (param-ref 'laser2:serial-number)", - "bloop2", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:serial-number)", + "(param-ref 'laser2:serial-number)" + ], + [ + "(param-ref 'laser1:serial-number)", + "bloop1", + "> (param-ref 'laser2:serial-number)", + "bloop2", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].serial_number == "bloop1" assert tm.laser[1].serial_number == "bloop2" @@ -40,19 +40,19 @@ def test_laser_serial_number(): def test_model(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:model)", - "(param-ref 'laser2:model)" - ], - [ - "(param-ref 'laser1:model)", - "bloop1", - "> (param-ref 'laser2:model)", - "bloop2", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:model)", + "(param-ref 'laser2:model)" + ], + [ + "(param-ref 'laser1:model)", + "bloop1", + "> (param-ref 'laser2:model)", + "bloop2", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].model == "bloop1" assert tm.laser[1].model == "bloop2" @@ -60,19 +60,19 @@ def test_model(): def test_wavelength(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:wavelength)", - "(param-ref 'laser2:wavelength)" - ], - [ - "(param-ref 'laser1:wavelength)", - "640", - "> (param-ref 'laser2:wavelength)", - "405.3", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:wavelength)", + "(param-ref 'laser2:wavelength)" + ], + [ + "(param-ref 'laser1:wavelength)", + "640", + "> (param-ref 'laser2:wavelength)", + "405.3", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].wavelength == 640 * pq.nm assert tm.laser[1].wavelength == 405.3 * pq.nm @@ -80,30 +80,29 @@ def test_wavelength(): def test_laser_enable(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:emission)", - "(param-ref 'laser1:serial-number)", - "(param-set! 'laser1:enable-emission #t)" - ], - [ - "(param-ref 'laser1:emission)", - "#f", - "> (param-ref 'laser1:serial-number)", - "bloop1", - "> (param-set! 'laser1:enable-emission #t)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:emission)", + "(param-ref 'laser1:serial-number)", + "(param-set! 'laser1:enable-emission #t)" + ], + [ + "(param-ref 'laser1:emission)", + "#f", + "> (param-ref 'laser1:serial-number)", + "bloop1", + "> (param-set! 'laser1:enable-emission #t)", + "0", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].enable is False tm.laser[0].enable = True def test_laser_enable_no_laser(): - with pytest.raises(RuntimeError): - with expected_protocol( + with pytest.raises(RuntimeError), expected_protocol( ik.toptica.TopMode, [ "(param-ref 'laser1:serial-number)", @@ -117,13 +116,12 @@ def test_laser_enable_no_laser(): "> " ], sep="\r\n" - ) as tm: - tm.laser[0].enable = True + ) as tm: + tm.laser[0].enable = True def test_laser_enable_error(): - with pytest.raises(TypeError): - with expected_protocol( + with pytest.raises(TypeError), expected_protocol( ik.toptica.TopMode, [ "(param-ref 'laser1:serial-number)", @@ -137,80 +135,80 @@ def test_laser_enable_error(): "> " ], sep="\n" - ) as tm: - tm.laser[0].enable = 'True' + ) as tm: + tm.laser[0].enable = 'True' def test_laser_tec_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:tec:ready)" - ], - [ - "(param-ref 'laser1:tec:ready)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:tec:ready)" + ], + [ + "(param-ref 'laser1:tec:ready)", + "#f", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].tec_status is False def test_laser_intensity(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:intensity)" - ], - [ - "(param-ref 'laser1:intensity)", - "0.666", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:intensity)" + ], + [ + "(param-ref 'laser1:intensity)", + "0.666", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].intensity == 0.666 def test_laser_mode_hop(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)" + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#f", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].mode_hop is False def test_laser_lock_start(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)", - "(param-ref 'laser1:charm:reg:started)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", - "2", - "> (param-ref 'laser1:charm:reg:started)", - "\"2012-12-01 01:02:01\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)", + "(param-ref 'laser1:charm:reg:started)" + ], + [ + "(param-ref 'laser1:charm:correction-status)", + "2", + "> (param-ref 'laser1:charm:reg:started)", + "\"2012-12-01 01:02:01\"", + "> " + ], + sep="\r\n" ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].lock_start == _date + def test_laser_lock_start_runtime_error(): - with pytest.raises(RuntimeError): - with expected_protocol( + with pytest.raises(RuntimeError), expected_protocol( ik.toptica.TopMode, [ "(param-ref 'laser1:charm:correction-status)", @@ -224,14 +222,13 @@ def test_laser_lock_start_runtime_error(): "> " ], sep="\r\n" - ) as tm: - _date = datetime(2012, 12, 1, 1, 2, 1) - assert tm.laser[0].lock_start == _date + ) as tm: + _date = datetime(2012, 12, 1, 1, 2, 1) + assert tm.laser[0].lock_start == _date def test_laser_first_mode_hop_time_runtime_error(): - with pytest.raises(RuntimeError): - with expected_protocol( + with pytest.raises(RuntimeError), expected_protocol( ik.toptica.TopMode, [ "(param-ref 'laser1:charm:reg:mh-occurred)", @@ -245,33 +242,32 @@ def test_laser_first_mode_hop_time_runtime_error(): "> " ], sep="\r\n" - ) as tm: - assert tm.laser[0].first_mode_hop_time is None + ) as tm: + assert tm.laser[0].first_mode_hop_time is None def test_laser_first_mode_hop_time(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:first-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#t", - "> (param-ref 'laser1:charm:reg:first-mh)", - "\"2012-12-01 01:02:01\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:first-mh)" + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#t", + "> (param-ref 'laser1:charm:reg:first-mh)", + "\"2012-12-01 01:02:01\"", + "> " + ], + sep="\r\n" ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].first_mode_hop_time == _date def test_laser_latest_mode_hop_time_none(): - with pytest.raises(RuntimeError): - with expected_protocol( + with pytest.raises(RuntimeError), expected_protocol( ik.toptica.TopMode, [ "(param-ref 'laser1:charm:reg:mh-occurred)", @@ -285,25 +281,25 @@ def test_laser_latest_mode_hop_time_none(): "> " ], sep="\r\n" - ) as tm: - assert tm.laser[0].latest_mode_hop_time is None + ) as tm: + assert tm.laser[0].latest_mode_hop_time is None def test_laser_latest_mode_hop_time(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:latest-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#t", - "> (param-ref 'laser1:charm:reg:latest-mh)", - "\"2012-12-01 01:02:01\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:latest-mh)" + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#t", + "> (param-ref 'laser1:charm:reg:latest-mh)", + "\"2012-12-01 01:02:01\"", + "> " + ], + sep="\r\n" ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].latest_mode_hop_time == _date @@ -311,51 +307,50 @@ def test_laser_latest_mode_hop_time(): def test_laser_correction_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)" + ], + [ + "(param-ref 'laser1:charm:correction-status)", + "0", + "> " + ], + sep="\r\n" ) as tm: - assert tm.laser[ - 0].correction_status == ik.toptica.TopMode.CharmStatus.un_initialized + assert tm.laser[0].correction_status == ik.toptica.TopMode.CharmStatus.un_initialized def test_laser_correction(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)", # 1st - "(exec 'laser1:charm:start-correction-initial)", - "(param-ref 'laser1:charm:correction-status)", # 2nd - "(exec 'laser1:charm:start-correction)", - "(param-ref 'laser1:charm:correction-status)", # 3rd - "(param-ref 'laser1:charm:correction-status)", # 4th - "(exec 'laser1:charm:start-correction)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", # 1st - "0", - "> (exec 'laser1:charm:start-correction-initial)", - "()", - "> (param-ref 'laser1:charm:correction-status)", # 3nd - "1", - "> (exec 'laser1:charm:start-correction)", - "()", - "> (param-ref 'laser1:charm:correction-status)", # 3rd - "3", - "> (param-ref 'laser1:charm:correction-status)", # 4th - "2", - "> (exec 'laser1:charm:start-correction)", - "()", - "> ", - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)", # 1st + "(exec 'laser1:charm:start-correction-initial)", + "(param-ref 'laser1:charm:correction-status)", # 2nd + "(exec 'laser1:charm:start-correction)", + "(param-ref 'laser1:charm:correction-status)", # 3rd + "(param-ref 'laser1:charm:correction-status)", # 4th + "(exec 'laser1:charm:start-correction)" + ], + [ + "(param-ref 'laser1:charm:correction-status)", # 1st + "0", + "> (exec 'laser1:charm:start-correction-initial)", + "()", + "> (param-ref 'laser1:charm:correction-status)", # 3nd + "1", + "> (exec 'laser1:charm:start-correction)", + "()", + "> (param-ref 'laser1:charm:correction-status)", # 3rd + "3", + "> (param-ref 'laser1:charm:correction-status)", # 4th + "2", + "> (exec 'laser1:charm:start-correction)", + "()", + "> ", + ], + sep="\r\n" ) as tm: tm.laser[0].correction() tm.laser[0].correction() @@ -365,163 +360,163 @@ def test_laser_correction(): def test_reboot_system(): with expected_protocol( - ik.toptica.TopMode, - [ - "(exec 'reboot-system)" - ], - [ - "(exec 'reboot-system)", - "reboot process started.", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(exec 'reboot-system)" + ], + [ + "(exec 'reboot-system)", + "reboot process started.", + "> " + ], + sep="\r\n" ) as tm: tm.reboot() def test_laser_ontime(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:ontime)" - ], - [ - "(param-ref 'laser1:ontime)", - "10000", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:ontime)" + ], + [ + "(param-ref 'laser1:ontime)", + "10000", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].on_time == 10000 * pq.s def test_laser_charm_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:health)" - ], - [ - "(param-ref 'laser1:health)", - "230", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:health)" + ], + [ + "(param-ref 'laser1:health)", + "230", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].charm_status == 1 def test_laser_temperature_control_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:health)" - ], - [ - "(param-ref 'laser1:health)", - "230", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:health)" + ], + [ + "(param-ref 'laser1:health)", + "230", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].temperature_control_status == 1 def test_laser_current_control_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:health)" - ], - [ - "(param-ref 'laser1:health)", - "230", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:health)" + ], + [ + "(param-ref 'laser1:health)", + "230", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].current_control_status == 1 def test_laser_production_date(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:production-date)" - ], - [ - "(param-ref 'laser1:production-date)", - "2016-01-16", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:production-date)" + ], + [ + "(param-ref 'laser1:production-date)", + "2016-01-16", + "> " + ], + sep="\r\n" ) as tm: assert tm.laser[0].production_date == "2016-01-16" def test_set_str(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'blo \"blee\")" - ], - [ - "(param-set! 'blo \"blee\")", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-set! 'blo \"blee\")" + ], + [ + "(param-set! 'blo \"blee\")", + "0", + "> " + ], + sep="\r\n" ) as tm: - tm.set('blo', 'blee') + tm.set("blo", "blee") def test_set_list(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'blo '(blee blo))" - ], - [ - "(param-set! 'blo '(blee blo))", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-set! 'blo '(blee blo))" + ], + [ + "(param-set! 'blo '(blee blo))", + "0", + "> " + ], + sep="\r\n" ) as tm: - tm.set('blo', ['blee', 'blo']) + tm.set("blo", ["blee", "blo"]) def test_display(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-disp 'blo)" - ], - [ - "(param-disp 'blo)", - "bloop", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-disp 'blo)" + ], + [ + "(param-disp 'blo)", + "bloop", + "> " + ], + sep="\r\n" ) as tm: - assert tm.display('blo') == "bloop" + assert tm.display("blo") == "bloop" def test_enable(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'emission)", - "(param-set! 'enable-emission #f)" - ], - [ - "(param-ref 'emission)", - "#f", - "> (param-set! 'enable-emission #f)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'emission)", + "(param-set! 'enable-emission #f)" + ], + [ + "(param-ref 'emission)", + "#f", + "> (param-set! 'enable-emission #f)", + "0", + "> " + ], + sep="\r\n" ) as tm: assert tm.enable is False tm.enable = False @@ -529,32 +524,32 @@ def test_enable(): def test_firmware(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'fw-ver)" - ], - [ - "(param-ref 'fw-ver)", - "1.02.01", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'fw-ver)" + ], + [ + "(param-ref 'fw-ver)", + "1.02.01", + "> " + ], + sep="\r\n" ) as tm: assert tm.firmware == (1, 2, 1) def test_serial_number(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'serial-number)" - ], - [ - "(param-ref 'serial-number)", - "010101", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'serial-number)" + ], + [ + "(param-ref 'serial-number)", + "010101", + "> " + ], + sep="\r\n" ) as tm: assert tm.serial_number == '010101' @@ -562,110 +557,110 @@ def test_serial_number(): def test_enable_error(): with pytest.raises(TypeError): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'enable-emission #f)" - ], - [ - "(param-set! 'enable-emission #f)", - ">" - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-set! 'enable-emission #f)" + ], + [ + "(param-set! 'enable-emission #f)", + ">" + ], + sep="\r\n" ) as tm: tm.enable = "False" def test_front_key(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'front-key-locked)" - ], - [ - "(param-ref 'front-key-locked)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'front-key-locked)" + ], + [ + "(param-ref 'front-key-locked)", + "#f", + "> " + ], + sep="\r\n" ) as tm: assert tm.locked is False def test_interlock(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'interlock-open)" - ], - [ - "(param-ref 'interlock-open)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'interlock-open)" + ], + [ + "(param-ref 'interlock-open)", + "#f", + "> " + ], + sep="\r\n" ) as tm: assert tm.interlock is False def test_fpga_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'system-health)" + ], + [ + "(param-ref 'system-health)", + "0", + "> " + ], + sep="\r\n" ) as tm: assert tm.fpga_status is True def test_fpga_status_false(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'system-health)" + ], + [ + "(param-ref 'system-health)", + "#f", + "> " + ], + sep="\r\n" ) as tm: assert tm.fpga_status is False def test_temperature_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "2", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'system-health)" + ], + [ + "(param-ref 'system-health)", + "2", + "> " + ], + sep="\r\n" ) as tm: assert tm.temperature_status is False def test_current_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "4", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'system-health)" + ], + [ + "(param-ref 'system-health)", + "4", + "> " + ], + sep="\r\n" ) as tm: assert tm.current_status is False diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index 6e6e4da99..8eb6c8e37 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -10,16 +10,16 @@ import struct +from hypothesis import ( + given, + strategies as st, +) import numpy as np import quantities as pq import instruments as ik from instruments.tests import expected_protocol -from hypothesis import ( - given, - strategies as st, -) # TESTS ####################################################################### @@ -31,11 +31,11 @@ def test_channel_is_channel_class(): def test_init(): with expected_protocol( - ik.yokogawa.Yokogawa6370, - [ - ":FORMat:DATA REAL,64" - ], - [] + ik.yokogawa.Yokogawa6370, + [ + ":FORMat:DATA REAL,64" + ], + [] ) as _: pass @@ -47,14 +47,14 @@ def test_channel_data(values, channel): values_len = str(len(values_packed)).encode() values_len_of_len = str(len(values_len)).encode() with expected_protocol( - ik.yokogawa.Yokogawa6370, - [ - ":FORMat:DATA REAL,64", - ":TRAC:Y? {}".format(channel.value), - ], - [ - b"#" + values_len_of_len + values_len + values_packed - ] + ik.yokogawa.Yokogawa6370, + [ + ":FORMat:DATA REAL,64", + ":TRAC:Y? {}".format(channel.value), + ], + [ + b"#" + values_len_of_len + values_len + values_packed + ] ) as inst: np.testing.assert_array_equal(inst.channel[channel].data(), np.array(values, dtype=" max_value: - raise ValueError("Unitful quantity is too high. Got {}, maximum" - " value is {}".format(newval, max_value)) + raise ValueError(f"Unitful quantity is too high. Got {newval}, " + f"maximum value is {max_value}") # Rescale to the correct unit before printing. This will also # catch bad units. strval = format_code.format( @@ -614,7 +611,7 @@ def _setter(self, newval): # CLASSES ##################################################################### -class ProxyList(object): +class ProxyList: """ This is a special class used to generate lists of objects where the valid keys are defined by the `valid_set` init parameter. This allows an @@ -666,8 +663,7 @@ def __getitem__(self, idx): if not isinstance(idx, self._valid_set): raise IndexError("Index out of range. Must be " "in {}.".format(self._valid_set)) - else: - idx = idx.value + idx = idx.value else: if idx not in self._valid_set: raise IndexError("Index out of range. Must be " From 1f43a47ceb5453a68a72e6e35ea6fec6171ce1c9 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Fri, 24 Jan 2020 00:20:16 -0500 Subject: [PATCH 036/108] Add support for Py38 (#232) --- .travis.yml | 6 +++--- README.rst | 4 ++-- setup.py | 1 + tox.ini | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5f971854e..40a3b6624 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ language: python python: - "3.6" - "3.7" + - "3.8" install: - "pip install -r requirements.txt" - "pip install -r dev-requirements.txt" @@ -23,8 +24,7 @@ before_script: - pylint --version script: - pytest --cov=instruments - - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --py3k instruments; fi - - if [[ $TRAVIS_PYTHON_VERSION != 3.7 ]]; then pylint --disable=I,R instruments; fi + - pylint --disable=I,R instruments after_success: - coveralls deploy: @@ -34,4 +34,4 @@ deploy: distributions: "sdist bdist_wheel" on: tags: true - condition: "$TRAVIS_PYTHON_VERSION == 3.7" + condition: "$TRAVIS_PYTHON_VERSION == 3.8" diff --git a/README.rst b/README.rst index fbf097a11..396316947 100644 --- a/README.rst +++ b/README.rst @@ -111,7 +111,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 3.6, and 3.7 are supported. Should you encounter +At this time, Python 3.6, 3.7, and 3.8 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. @@ -134,7 +134,7 @@ To run the tests against all supported version of Python, you will need to have the binary for each installed, as well as any requirements needed to install ``numpy`` under each Python version. On Debian/Ubuntu systems this means you will need to install the ``python-dev`` package for each version of Python -supported (``python3.7-dev``, etc). +supported (``python3.8-dev``, etc). With the required system packages installed, all tests can be run with ``tox``: diff --git a/setup.py b/setup.py index e285ad0cf..c9f89e896 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Operating System :: OS Independent", "License :: OSI Approved :: GNU Affero General Public License v3", "Intended Audience :: Science/Research", diff --git a/tox.ini b/tox.ini index d6ecd6528..d1bd79109 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37 +envlist = py36,py37,py38 [testenv] deps = -rdev-requirements.txt commands = pytest From fef054a4c97a0fbbb9b3ad5485951581490e6c8b Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 2 Feb 2020 23:21:14 -0500 Subject: [PATCH 037/108] Change all quantities imports to instruments.units (#233) --- doc/examples/ex_hp3456.py | 4 +- doc/examples/ex_topticatopmode.py | 2 +- doc/examples/minghe/ex_minghe_mhs5200.py | 10 +- doc/examples/qubitekk/ex_qubitekk_mc1.py | 12 +- doc/examples/srs_DG645.ipynb | 4 +- doc/examples/srs_DG645.py | 4 +- doc/source/devguide/testing.rst | 2 +- doc/source/devguide/util_fns.rst | 4 +- .../comm/gpib_communicator.py | 14 +- .../comm/serial_communicator.py | 6 +- .../comm/socket_communicator.py | 6 +- .../comm/usbtmc_communicator.py | 6 +- .../comm/visa_communicator.py | 6 +- .../function_generator.py | 7 +- instruments/agilent/agilent33220a.py | 10 +- instruments/agilent/agilent34410a.py | 28 ++-- instruments/config.py | 11 +- instruments/fluke/fluke3000.py | 24 ++-- .../generic_scpi/scpi_function_generator.py | 10 +- instruments/generic_scpi/scpi_instrument.py | 4 +- instruments/generic_scpi/scpi_multimeter.py | 30 ++--- instruments/glassman/glassmanfr.py | 38 +++--- instruments/holzworth/holzworth_hs9000.py | 8 +- instruments/hp/hp3456a.py | 32 ++--- instruments/hp/hp6624a.py | 12 +- instruments/hp/hp6632b.py | 12 +- instruments/hp/hp6652a.py | 12 +- instruments/hp/hpe3631a.py | 14 +- instruments/keithley/keithley195.py | 28 ++-- instruments/keithley/keithley2182.py | 10 +- instruments/keithley/keithley485.py | 8 +- instruments/keithley/keithley580.py | 8 +- instruments/keithley/keithley6220.py | 10 +- instruments/keithley/keithley6514.py | 12 +- instruments/lakeshore/lakeshore340.py | 6 +- instruments/lakeshore/lakeshore370.py | 4 +- instruments/lakeshore/lakeshore475.py | 38 +++--- instruments/minghe/mhs5200a.py | 12 +- instruments/newport/newportesp301.py | 126 +++++++++--------- instruments/ondax/lm.py | 42 +++--- instruments/oxford/oxforditc503.py | 4 +- .../phasematrix/phasematrix_fsw0020.py | 4 +- instruments/picowatt/picowattavs47.py | 4 +- instruments/qubitekk/cc1.py | 16 +-- instruments/qubitekk/mc1.py | 28 ++-- instruments/srs/srs345.py | 12 +- instruments/srs/srs830.py | 18 +-- instruments/srs/srsctc100.py | 28 ++-- instruments/srs/srsdg645.py | 26 ++-- instruments/tektronix/tekawg2000.py | 20 +-- instruments/tektronix/tekdpo70000.py | 42 +++--- instruments/tektronix/tektds224.py | 4 +- .../test_function_generator.py | 4 +- .../tests/test_agilent/test_agilent_33220a.py | 20 +-- .../tests/test_agilent/test_agilent_34410a.py | 20 +-- instruments/tests/test_comm/test_gpibusb.py | 8 +- instruments/tests/test_comm/test_serial.py | 6 +- instruments/tests/test_comm/test_socket.py | 6 +- instruments/tests/test_comm/test_usbtmc.py | 6 +- instruments/tests/test_config.py | 8 +- .../tests/test_fluke/test_fluke3000.py | 6 +- .../test_scpi_function_generator.py | 30 ++--- .../test_generic_scpi/test_scpi_multimeter.py | 16 +-- .../tests/test_glassman/test_glassmanfr.py | 24 ++-- .../test_holzworth/test_holzworth_hs9000.py | 20 +-- instruments/tests/test_hp/test_hp3456a.py | 22 +-- instruments/tests/test_hp/test_hp6624a.py | 38 +++--- instruments/tests/test_hp/test_hp6632b.py | 38 +++--- instruments/tests/test_hp/test_hpe3631a.py | 30 ++--- .../tests/test_keithley/test_keithley2182.py | 12 +- .../tests/test_keithley/test_keithley485.py | 8 +- .../tests/test_keithley/test_keithley6220.py | 10 +- .../tests/test_keithley/test_keithley6514.py | 16 +-- .../tests/test_minghe/test_minghe_mhs5200a.py | 18 +-- .../tests/test_oxford/test_oxforditc503.py | 4 +- .../test_phasematrix_fsw0020.py | 8 +- .../test_picowatt/test_picowatt_avs47.py | 6 +- .../test_bounded_unitful_property.py | 52 ++++---- .../test_unitful_property.py | 70 +++++----- .../test_unitless_property.py | 4 +- .../tests/test_qubitekk/test_qubitekk_cc1.py | 10 +- .../tests/test_qubitekk/test_qubitekk_mc1.py | 10 +- instruments/tests/test_split_str.py | 11 +- instruments/tests/test_srs/test_srs345.py | 20 +-- instruments/tests/test_srs/test_srs830.py | 16 +-- instruments/tests/test_srs/test_srsdg645.py | 10 +- .../tests/test_thorlabs/test_thorlabs_apt.py | 2 +- .../test_thorlabs/test_thorlabs_lcc25.py | 16 +-- .../tests/test_thorlabs/test_thorlabs_sc10.py | 6 +- .../test_thorlabs/test_thorlabs_tc200.py | 26 ++-- .../test_toptica/test_toptica_topmode.py | 8 +- instruments/tests/test_util_fns.py | 34 ++--- .../tests/test_yokogawa/test_yokogawa_6370.py | 22 +-- instruments/thorlabs/lcc25.py | 16 +-- instruments/thorlabs/pm100usb.py | 12 +- instruments/thorlabs/sc10.py | 6 +- instruments/thorlabs/tc200.py | 28 ++-- instruments/thorlabs/thorlabsapt.py | 40 +++--- instruments/toptica/topmode.py | 8 +- instruments/units.py | 4 +- instruments/util_fns.py | 36 ++--- instruments/yokogawa/yokogawa6370.py | 16 +-- instruments/yokogawa/yokogawa7651.py | 10 +- 103 files changed, 858 insertions(+), 861 deletions(-) diff --git a/doc/examples/ex_hp3456.py b/doc/examples/ex_hp3456.py index 1ddbe5884..bd060c504 100644 --- a/doc/examples/ex_hp3456.py +++ b/doc/examples/ex_hp3456.py @@ -6,7 +6,7 @@ import logging import time import instruments as ik -import quantities as pq +import instruments.units as u dmm = ik.hp.HP3456a.open_gpibusb('/dev/ttyUSB0', 22) logging.basicConfig(level=logging.DEBUG) @@ -50,7 +50,7 @@ print(dmm.measure(dmm.Mode.resistance_2wire)) dmm.nplc = 1 for i in range(-1, 4): - value = (10 ** i) * pq.volt + value = (10 ** i) * u.volt dmm.input_range = value print(dmm.measure(dmm.Mode.dcv)) diff --git a/doc/examples/ex_topticatopmode.py b/doc/examples/ex_topticatopmode.py index 3ef51a3eb..4db019846 100644 --- a/doc/examples/ex_topticatopmode.py +++ b/doc/examples/ex_topticatopmode.py @@ -5,7 +5,7 @@ """ import instruments as ik -import quantities as pq +import instruments.units as u from platform import system if system() == 'Windows': tm = ik.toptica.TopMode.open_serial('COM17', 115200) diff --git a/doc/examples/minghe/ex_minghe_mhs5200.py b/doc/examples/minghe/ex_minghe_mhs5200.py index 60b69bedb..9c3880d58 100644 --- a/doc/examples/minghe/ex_minghe_mhs5200.py +++ b/doc/examples/minghe/ex_minghe_mhs5200.py @@ -1,25 +1,25 @@ #!/usr/bin/python from instruments.minghe import MHS5200 -import quantities as pq +import instruments.units as u mhs = MHS5200.open_serial(vid=6790, pid=29987, baud=57600) print(mhs.serial_number) -mhs.channel[0].frequency = 3000000*pq.Hz +mhs.channel[0].frequency = 3000000*u.Hz print(mhs.channel[0].frequency) mhs.channel[0].function = MHS5200.Function.sawtooth_down print(mhs.channel[0].function) -mhs.channel[0].amplitude = 9.0*pq.V +mhs.channel[0].amplitude = 9.0*u.V print(mhs.channel[0].amplitude) mhs.channel[0].offset = -0.5 print(mhs.channel[0].offset) mhs.channel[0].phase = 90 print(mhs.channel[0].phase) -mhs.channel[1].frequency = 2000000*pq.Hz +mhs.channel[1].frequency = 2000000*u.Hz print(mhs.channel[1].frequency) mhs.channel[1].function = MHS5200.Function.square print(mhs.channel[1].function) -mhs.channel[1].amplitude = 2.0*pq.V +mhs.channel[1].amplitude = 2.0*u.V print(mhs.channel[1].amplitude) mhs.channel[1].offset = 0.0 print(mhs.channel[1].offset) diff --git a/doc/examples/qubitekk/ex_qubitekk_mc1.py b/doc/examples/qubitekk/ex_qubitekk_mc1.py index cd124e354..f30792de0 100644 --- a/doc/examples/qubitekk/ex_qubitekk_mc1.py +++ b/doc/examples/qubitekk/ex_qubitekk_mc1.py @@ -3,14 +3,14 @@ from time import sleep from instruments.qubitekk import MC1 -import quantities as pq +import instruments.units as u if __name__ == "__main__": mc1 = MC1.open_serial(vid=1027, pid=24577, baud=9600, timeout=1) - mc1.step_size = 25*pq.ms - mc1.inertia = 10*pq.ms + mc1.step_size = 25*u.ms + mc1.inertia = 10*u.ms print("step size:", mc1.step_size) print("inertial force: ", mc1.inertia) @@ -26,9 +26,9 @@ print("Stage Centered") # for the motor in the mechanical delay line, the travel is limited from # the full range of travel. Here's how to set the limits. - mc1.lower_limit = -260*pq.ms - mc1.upper_limit = 300*pq.ms - mc1.increment = 5*pq.ms + mc1.lower_limit = -260*u.ms + mc1.upper_limit = 300*u.ms + mc1.increment = 5*u.ms x_pos = mc1.lower_limit while x_pos <= mc1.upper_limit: print(str(mc1.metric_position)+" "+str(mc1.direction)) diff --git a/doc/examples/srs_DG645.ipynb b/doc/examples/srs_DG645.ipynb index a19ad882f..92a348725 100644 --- a/doc/examples/srs_DG645.ipynb +++ b/doc/examples/srs_DG645.ipynb @@ -43,7 +43,7 @@ "collapsed": false, "input": [ "from instruments.srs import SRSDG645\n", - "import quantities as pq" + "import instruments.units as u" ], "language": "python", "metadata": {}, @@ -100,7 +100,7 @@ "cell_type": "code", "collapsed": false, "input": [ - "ddg.channel[ddg.Channels.A].delay = (ddg.Channels.B, pq.Quantity(10, 'us'))" + "ddg.channel[ddg.Channels.A].delay = (ddg.Channels.B, u.Quantity(10, 'us'))" ], "language": "python", "metadata": {}, diff --git a/doc/examples/srs_DG645.py b/doc/examples/srs_DG645.py index c79f675de..4ab5c54d5 100644 --- a/doc/examples/srs_DG645.py +++ b/doc/examples/srs_DG645.py @@ -21,7 +21,7 @@ # from instruments.srs import SRSDG645 -import quantities as pq +import instruments.units as u # @@ -38,7 +38,7 @@ # -ddg.channel[ddg.Channels.A].delay = (ddg.Channels.B, pq.Quantity(10, 'us')) +ddg.channel[ddg.Channels.A].delay = (ddg.Channels.B, u.Quantity(10, 'us')) # diff --git a/doc/source/devguide/testing.rst b/doc/source/devguide/testing.rst index 6d249c5d6..47d6232a2 100644 --- a/doc/source/devguide/testing.rst +++ b/doc/source/devguide/testing.rst @@ -63,7 +63,7 @@ a simple test case for :class:`instruments.srs.SRSDG645``:: ], sep="\n" ) as ddg: - unit_eq(ddg.output['AB'].level_amplitude, pq.Quantity(3.2, "V")) + unit_eq(ddg.output['AB'].level_amplitude, u.Quantity(3.2, "V")) ddg.output['AB'].level_amplitude = 4.0 Here, we see that the test has a name beginning with ``test_``, has a simple diff --git a/doc/source/devguide/util_fns.rst b/doc/source/devguide/util_fns.rst index 9fa06769b..e316538ac 100644 --- a/doc/source/devguide/util_fns.rst +++ b/doc/source/devguide/util_fns.rst @@ -74,7 +74,7 @@ These properties, when implemented in your class, might look like this:: voltage, voltage_min, voltage_max = bounded_unitful_property( voltage = unitful_property( "VOLT", - pq.volt, + u.volt, valid_range=(0*quantities.volt, 10*quantities.volt) doc=""" Gets/sets the output voltage. @@ -129,7 +129,7 @@ the `~instruments.thorlabs.TC200` class:: temperature = unitful_property( "tact", - units=pq.degC, + units=u.degC, readonly=True, input_decoration=lambda x: x.replace( " C", "").replace(" F", "").replace(" K", ""), diff --git a/instruments/abstract_instruments/comm/gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py index 65e4051e8..3341bb5a8 100644 --- a/instruments/abstract_instruments/comm/gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -16,7 +16,7 @@ import time from builtins import chr, str, bytes -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -48,7 +48,7 @@ def __init__(self, filelike, gpib_address, model="gi"): self._terminator = None self.terminator = "\n" self._eoi = True - self._timeout = 1000 * pq.millisecond + self._timeout = 1000 * u.millisecond if self._model == GPIBCommunicator.Model.gi and self._version <= 4: self._eos = 10 else: @@ -109,15 +109,15 @@ def timeout(self): @timeout.setter def timeout(self, newval): - newval = assume_units(newval, pq.second) + newval = assume_units(newval, u.second) if self._model == GPIBCommunicator.Model.gi and self._version <= 4: - newval = newval.rescale(pq.second) + newval = newval.rescale(u.second) self._file.sendcmd('+t:{}'.format(int(newval.magnitude))) else: - newval = newval.rescale(pq.millisecond) + newval = newval.rescale(u.millisecond) self._file.sendcmd("++read_tmo_ms {}".format(int(newval.magnitude))) - self._file.timeout = newval.rescale(pq.second) - self._timeout = newval.rescale(pq.second) + self._file.timeout = newval.rescale(u.second) + self._timeout = newval.rescale(u.second) @property def terminator(self): diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index 40bc5b30d..4ac94df8c 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -16,7 +16,7 @@ from builtins import bytes, str import serial -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -88,11 +88,11 @@ def timeout(self): :type: `~quantities.Quantity` :units: As specified or assumed to be of units ``seconds`` """ - return self._conn.timeout * pq.second + return self._conn.timeout * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, pq.second).rescale(pq.second).magnitude + newval = assume_units(newval, u.second).rescale(u.second).magnitude self._conn.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index aefdd5878..355b683f4 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -15,7 +15,7 @@ import socket from builtins import str, bytes -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -77,11 +77,11 @@ def timeout(self): :type: `~quantities.Quantity` :units: As specified or assumed to be of units ``seconds`` """ - return self._conn.gettimeout() * pq.second + return self._conn.gettimeout() * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, pq.second).rescale(pq.second).magnitude + newval = assume_units(newval, u.second).rescale(u.second).magnitude self._conn.settimeout(newval) # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 795b4a4dd..29926275c 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -15,7 +15,7 @@ from builtins import str, bytes import usbtmc -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -74,11 +74,11 @@ def timeout(self): :type: `~quantities.Quantity` :units: As specified or assumed to be of units ``seconds`` """ - return self._filelike.timeout * pq.second + return self._filelike.timeout * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, pq.second).rescale(pq.s).magnitude + newval = assume_units(newval, u.second).rescale(u.s).magnitude self._filelike.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index c487d7c12..8cafee38d 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -16,7 +16,7 @@ import io from builtins import str -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -95,11 +95,11 @@ def terminator(self, newval): @property def timeout(self): - return self._conn.timeout * pq.second + return self._conn.timeout * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, pq.second).rescale(pq.second).magnitude + newval = assume_units(newval, u.second).rescale(u.second).magnitude self._conn.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index cad895110..525cbb920 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -14,7 +14,6 @@ from builtins import range from future.utils import with_metaclass -import quantities as pq from instruments.abstract_instruments import Instrument import instruments.units as u @@ -170,9 +169,9 @@ def amplitude(self): mag, units = self._get_amplitude_() if units == self._parent.VoltageMode.dBm: - return pq.Quantity(mag, u.dBm) + return u.Quantity(mag, u.dBm) - return pq.Quantity(mag, pq.V), units + return u.Quantity(mag, u.V), units @amplitude.setter def amplitude(self, newval): @@ -191,7 +190,7 @@ def amplitude(self, newval): mag, units = newval # Finally, convert the magnitude out to a float. - mag = float(assume_units(mag, pq.V).rescale(pq.V).magnitude) + mag = float(assume_units(mag, u.V).rescale(u.V).magnitude) self._set_amplitude_(mag, units) diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index b5282414d..93ad97bd2 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -12,7 +12,7 @@ from enum import Enum -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIFunctionGenerator from instruments.util_fns import ( @@ -33,10 +33,10 @@ class Agilent33220a(SCPIFunctionGenerator): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> inst = ik.agilent.Agilent33220a.open_gpibusb('/dev/ttyUSB0', 1) >>> inst.function = inst.Function.sinusoid - >>> inst.frequency = 1 * pq.kHz + >>> inst.frequency = 1 * u.kHz >>> inst.output = True .. _Agilent/Keysight 33220a: http://www.keysight.com/en/pd-127539-pn-33220A @@ -166,7 +166,7 @@ def load_resistance(self): """ value = self.query("OUTP:LOAD?") try: - return int(value) * pq.ohm + return int(value) * u.ohm except ValueError: return self.LoadResistance(value.strip()) @@ -175,7 +175,7 @@ def load_resistance(self, newval): if isinstance(newval, self.LoadResistance): newval = newval.value else: - newval = assume_units(newval, pq.ohm).rescale(pq.ohm).magnitude + newval = assume_units(newval, u.ohm).rescale(u.ohm).magnitude if (newval < 0) or (newval > 10000): raise ValueError( "Load resistance must be between 0 and 10,000") diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 8c444f552..96ef70a55 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -10,7 +10,7 @@ from __future__ import division from builtins import map -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIMultimeter @@ -28,7 +28,7 @@ class Agilent34410a(SCPIMultimeter): # pylint: disable=abstract-method Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> dmm = ik.agilent.Agilent34410a.open_gpibusb('/dev/ttyUSB0', 1) >>> print(dmm.measure(dmm.Mode.resistance)) @@ -173,7 +173,7 @@ def read_last_data(self): data[0] = float(data[0]) if data[1] in unit_map: data[1] = unit_map[data[1]] - return pq.Quantity(*data) + return u.Quantity(*data) def read_meter(self): """ @@ -193,16 +193,16 @@ def read_meter(self): # UNITS ####################################################################### UNITS = { - Agilent34410a.Mode.capacitance: pq.farad, - Agilent34410a.Mode.voltage_dc: pq.volt, - Agilent34410a.Mode.voltage_ac: pq.volt, - Agilent34410a.Mode.diode: pq.volt, - Agilent34410a.Mode.current_ac: pq.amp, - Agilent34410a.Mode.current_dc: pq.amp, - Agilent34410a.Mode.resistance: pq.ohm, - Agilent34410a.Mode.fourpt_resistance: pq.ohm, - Agilent34410a.Mode.frequency: pq.hertz, - Agilent34410a.Mode.period: pq.second, - Agilent34410a.Mode.temperature: pq.kelvin, + Agilent34410a.Mode.capacitance: u.farad, + Agilent34410a.Mode.voltage_dc: u.volt, + Agilent34410a.Mode.voltage_ac: u.volt, + Agilent34410a.Mode.diode: u.volt, + Agilent34410a.Mode.current_ac: u.amp, + Agilent34410a.Mode.current_dc: u.amp, + Agilent34410a.Mode.resistance: u.ohm, + Agilent34410a.Mode.fourpt_resistance: u.ohm, + Agilent34410a.Mode.frequency: u.hertz, + Agilent34410a.Mode.period: u.second, + Agilent34410a.Mode.temperature: u.kelvin, Agilent34410a.Mode.continuity: 1, } diff --git a/instruments/config.py b/instruments/config.py index 2e6d7b6eb..240eb5e49 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -21,12 +21,11 @@ # a false positive from its import-error checker, so we locally disable # it here. Once the cause for the false positive has been identified, # the import-error check should be re-enabled. - import ruamel_yaml as yaml # pylint: disable=import-error - -import quantities as pq + import ruamel_yaml as yaml # pylint: disable=import-error from future.builtins import str +import instruments.units as u from instruments.util_fns import setattr_expression, split_unit_str # FUNCTIONS ################################################################### @@ -62,12 +61,12 @@ def walk_dict(d, path): def quantity_constructor(loader, node): """ - Constructs a `pq.Quantity` instance from a PyYAML + Constructs a `u.Quantity` instance from a PyYAML node tagged as ``!Q``. """ # Follows the example of http://stackoverflow.com/a/43081967/267841. value = loader.construct_scalar(node) - return pq.Quantity(*split_unit_str(value)) + return u.Quantity(*split_unit_str(value)) # We avoid having to register !Q every time by doing as soon as the # relevant constructor is defined. @@ -102,7 +101,7 @@ def load_instruments(conf_file_name, conf_path="/"): channel[0].motor_model: PRM1-Z8 Unitful attributes can be specified by using the ``!Q`` tag to quickly create - instances of `pq.Quantity`. In the example above, for instance, we can set a motion + instances of `u.Quantity`. In the example above, for instance, we can set a motion timeout as a unitful quantity:: attrs: diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index b57bdcb61..ca7156008 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -40,7 +40,7 @@ from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Multimeter @@ -105,7 +105,7 @@ def __init__(self, filelike): Initialize the instrument, and set the properties needed for communication. """ super(Fluke3000, self).__init__(filelike) - self.timeout = 3 * pq.second + self.timeout = 3 * u.second self.terminator = "\r" self.positions = {} self.connect() @@ -211,7 +211,7 @@ def connect(self): if not self.positions: self.reset() # Reset the PC3000 dongle timeout = self.timeout # Store default timeout - self.timeout = 30 * pq.second # PC 3000 can take a while to bind with wireless devices + self.timeout = 30 * u.second # PC 3000 can take a while to bind with wireless devices self.query_lines("rfdis", 3) # Discover available modules and bind them self.timeout = timeout # Restore default timeout self.scan() # Look for connected devices @@ -295,7 +295,7 @@ def flush(self): until a terminator is not found. """ timeout = self.timeout - self.timeout = 0.1 * pq.second + self.timeout = 0.1 * u.second init_time = time.time() while time.time() - init_time < 1.: try: @@ -472,14 +472,14 @@ def _parse_factor(data): UNITS = { None: 1, - Fluke3000.Mode.voltage_ac: pq.volt, - Fluke3000.Mode.voltage_dc: pq.volt, - Fluke3000.Mode.current_ac: pq.amp, - Fluke3000.Mode.current_dc: pq.amp, - Fluke3000.Mode.frequency: pq.hertz, - Fluke3000.Mode.temperature: pq.celsius, - Fluke3000.Mode.resistance: pq.ohm, - Fluke3000.Mode.capacitance: pq.farad + Fluke3000.Mode.voltage_ac: u.volt, + Fluke3000.Mode.voltage_dc: u.volt, + Fluke3000.Mode.current_ac: u.amp, + Fluke3000.Mode.current_dc: u.amp, + Fluke3000.Mode.frequency: u.hertz, + Fluke3000.Mode.temperature: u.celsius, + Fluke3000.Mode.resistance: u.ohm, + Fluke3000.Mode.capacitance: u.farad } # METRIC PREFIXES ############################################################# diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 122d29894..03ba78238 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from __future__ import division -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import FunctionGenerator from instruments.generic_scpi import SCPIInstrument @@ -27,9 +27,9 @@ class SCPIFunctionGenerator(FunctionGenerator, SCPIInstrument): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> inst = ik.generic_scpi.SCPIFunctionGenerator.open_tcpip("192.168.1.1") - >>> inst.frequency = 1 * pq.kHz + >>> inst.frequency = 1 * u.kHz """ # CONSTANTS # @@ -76,7 +76,7 @@ def _set_amplitude_(self, magnitude, units): frequency = unitful_property( command="FREQ", - units=pq.Hz, + units=u.Hz, doc=""" Gets/sets the output frequency. @@ -97,7 +97,7 @@ def _set_amplitude_(self, magnitude, units): offset = unitful_property( command="VOLT:OFFS", - units=pq.volt, + units=u.volt, doc=""" Gets/sets the offset voltage of the function generator. diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index 5f4f0d93b..66b10357e 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -12,7 +12,7 @@ from builtins import map from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.util_fns import assume_units @@ -151,7 +151,7 @@ def line_frequency(self): :units: Hertz :type: `~quantities.quantity.Quantity` """ - return pq.Quantity( + return u.Quantity( float(self.query("SYST:LFR?")), "Hz" ) diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index 1a273f7de..41454d08c 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -11,7 +11,7 @@ from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Multimeter from instruments.generic_scpi import SCPIInstrument @@ -178,7 +178,7 @@ def input_range(self): Example usages: >>> dmm.input_range = dmm.InputRange.automatic - >>> dmm.input_range = 1 * pq.millivolt + >>> dmm.input_range = 1 * u.millivolt :units: As appropriate for the current mode setting. :type: `~quantities.Quantity`, or `~SCPIMultimeter.InputRange` @@ -318,7 +318,7 @@ def sample_count(self, newval): trigger_delay = unitful_property( command="TRIG:DEL", - units=pq.second, + units=u.second, doc=""" Gets/sets the time delay which the multimeter will use following receiving a trigger event before starting the measurement. @@ -345,7 +345,7 @@ def sample_count(self, newval): sample_timer = unitful_property( command="SAMP:TIM", - units=pq.second, + units=u.second, doc=""" Gets/sets the sample interval when the sample counter is greater than one and when the sample source is set to timer (see @@ -418,16 +418,16 @@ def _mode_parse(val): # UNITS ####################################################################### UNITS = { - SCPIMultimeter.Mode.capacitance: pq.farad, - SCPIMultimeter.Mode.voltage_dc: pq.volt, - SCPIMultimeter.Mode.voltage_ac: pq.volt, - SCPIMultimeter.Mode.diode: pq.volt, - SCPIMultimeter.Mode.current_ac: pq.amp, - SCPIMultimeter.Mode.current_dc: pq.amp, - SCPIMultimeter.Mode.resistance: pq.ohm, - SCPIMultimeter.Mode.fourpt_resistance: pq.ohm, - SCPIMultimeter.Mode.frequency: pq.hertz, - SCPIMultimeter.Mode.period: pq.second, - SCPIMultimeter.Mode.temperature: pq.kelvin, + SCPIMultimeter.Mode.capacitance: u.farad, + SCPIMultimeter.Mode.voltage_dc: u.volt, + SCPIMultimeter.Mode.voltage_ac: u.volt, + SCPIMultimeter.Mode.diode: u.volt, + SCPIMultimeter.Mode.current_ac: u.amp, + SCPIMultimeter.Mode.current_dc: u.amp, + SCPIMultimeter.Mode.resistance: u.ohm, + SCPIMultimeter.Mode.fourpt_resistance: u.ohm, + SCPIMultimeter.Mode.frequency: u.hertz, + SCPIMultimeter.Mode.period: u.second, + SCPIMultimeter.Mode.temperature: u.kelvin, SCPIMultimeter.Mode.continuity: 1, } diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 51b26295d..0e0bfb186 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -39,7 +39,7 @@ from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( PowerSupply, @@ -74,16 +74,16 @@ class GlassmanFR(PowerSupply, PowerSupplyChannel): >>> psu.voltage array(100.0) * V >>> psu.output = True # Turns on the power supply - >>> psu.voltage_sense < 200 * pq.volt + >>> psu.voltage_sense < 200 * u.volt True This code uses default values of `voltage_max`, `current_max` and `polarity` that are only valid of the FR50R6 in its positive setting. If your power supply differs, reset those values by calling: - >>> import quantities as pq - >>> psu.voltage_max = 40.0 * pq.kilovolt - >>> psu.current_max = 7.5 * pq.milliamp + >>> import instruments.units as u + >>> psu.voltage_max = 40.0 * u.kilovolt + >>> psu.current_max = 7.5 * u.milliamp >>> psu.polarity = -1 """ @@ -93,12 +93,12 @@ def __init__(self, filelike): """ super(GlassmanFR, self).__init__(filelike) self.terminator = "\r" - self.voltage_max = 50.0 * pq.kilovolt - self.current_max = 6.0 * pq.milliamp + self.voltage_max = 50.0 * u.kilovolt + self.current_max = 6.0 * u.milliamp self.polarity = +1 self._device_timeout = False - self._voltage = 0. * pq.volt - self._current = 0. * pq.amp + self._voltage = 0. * u.volt + self._current = 0. * u.amp # ENUMS ## @@ -166,7 +166,7 @@ def voltage(self): @voltage.setter def voltage(self, newval): - self.set_status(voltage=assume_units(newval, pq.volt)) + self.set_status(voltage=assume_units(newval, u.volt)) @property def current(self): @@ -180,7 +180,7 @@ def current(self): @current.setter def current(self, newval): - self.set_status(current=assume_units(newval, pq.amp)) + self.set_status(current=assume_units(newval, u.amp)) @property def voltage_sense(self): @@ -334,8 +334,8 @@ def set_status(self, voltage=None, current=None, output=None, reset=False): kept as what they were set to previously. """ if reset: - self._voltage = 0. * pq.volt - self._current = 0. * pq.amp + self._voltage = 0. * u.volt + self._current = 0. * u.amp cmd = format(4, "013d") else: # The maximum value is encoded as the maximum of three hex characters (4095) @@ -343,19 +343,19 @@ def set_status(self, voltage=None, current=None, output=None, reset=False): value_max = int(0xfff) # If the voltage is not specified, keep it as is - voltage = assume_units(voltage, pq.volt) if voltage is not None else self.voltage - ratio = float(voltage.rescale(pq.volt)/self.voltage_max.rescale(pq.volt)) + voltage = assume_units(voltage, u.volt) if voltage is not None else self.voltage + ratio = float(voltage.rescale(u.volt)/self.voltage_max.rescale(u.volt)) voltage_int = int(round(value_max*ratio)) self._voltage = self.voltage_max*float(voltage_int)/value_max - assert 0. * pq.volt <= self._voltage <= self.voltage_max + assert 0. * u.volt <= self._voltage <= self.voltage_max cmd += format(voltage_int, "03X") # If the current is not specified, keep it as is - current = assume_units(current, pq.amp) if current is not None else self.current - ratio = float(current.rescale(pq.amp)/self.current_max.rescale(pq.amp)) + current = assume_units(current, u.amp) if current is not None else self.current + ratio = float(current.rescale(u.amp)/self.current_max.rescale(u.amp)) current_int = int(round(value_max*ratio)) self._current = self.current_max*float(current_int)/value_max - assert 0. * pq.amp <= self._current <= self.current_max + assert 0. * u.amp <= self._current <= self.current_max cmd += format(current_int, "03X") # If the output status is not specified, keep it as is diff --git a/instruments/holzworth/holzworth_hs9000.py b/instruments/holzworth/holzworth_hs9000.py index 35608b3af..ae9a36424 100644 --- a/instruments/holzworth/holzworth_hs9000.py +++ b/instruments/holzworth/holzworth_hs9000.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from __future__ import division -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.signal_generator import ( SignalGenerator, @@ -127,11 +127,11 @@ def temperature(self): """ val, units = split_unit_str(self.query("TEMP?")) units = "deg{}".format(units) - return pq.Quantity(val, units) + return u.Quantity(val, units) frequency, frequency_min, frequency_max = bounded_unitful_property( "FREQ", - units=pq.GHz, + units=u.GHz, doc=""" Gets/sets the frequency of the specified channel. When setting, values are bounded between what is returned by `frequency_min` @@ -169,7 +169,7 @@ def temperature(self): ) phase, phase_min, phase_max = bounded_unitful_property( "PHASE", - units=pq.degree, + units=u.degree, doc=""" Gets/sets the output phase of the specified channel. When setting, values are bounded between what is returned by `phase_min` diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index 27ea43153..da6e8fa04 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -39,7 +39,7 @@ from enum import Enum, IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Multimeter from instruments.util_fns import assume_units, bool_property, enum_property @@ -67,7 +67,7 @@ def __init__(self, filelike): communication. """ super(HP3456a, self).__init__(filelike) - self.timeout = 15 * pq.second + self.timeout = 15 * u.second self.terminator = "\r" self.sendcmd("HO0T4SO1") self._null = False @@ -298,11 +298,11 @@ def delay(self): :rtype: `~quantaties.Quantity.s` """ - return self._register_read(HP3456a.Register.delay) * pq.s + return self._register_read(HP3456a.Register.delay) * u.s @delay.setter def delay(self, value): - delay = assume_units(value, pq.s).rescale(pq.s).magnitude + delay = assume_units(value, u.s).rescale(u.s).magnitude self._register_write(HP3456a.Register.delay, delay) @property @@ -434,13 +434,13 @@ def input_range(self, value): raise ValueError("Only 'auto' is acceptable when specifying " "the input range as a string.") - elif isinstance(value, pq.quantity.Quantity): - if value.units == pq.volt: + elif isinstance(value, u.quantity.Quantity): + if value.units == u.volt: valid = HP3456a.ValidRange.voltage.value - value = value.rescale(pq.volt) - elif value.units == pq.ohm: + value = value.rescale(u.volt) + elif value.units == u.ohm: valid = HP3456a.ValidRange.resistance.value - value = value.rescale(pq.ohm) + value = value.rescale(u.ohm) else: raise ValueError("Value {} not quantity.volt or quantity.ohm" "".format(value)) @@ -619,14 +619,14 @@ def trigger(self): UNITS = { None: 1, - HP3456a.Mode.dcv: pq.volt, - HP3456a.Mode.acv: pq.volt, - HP3456a.Mode.acvdcv: pq.volt, - HP3456a.Mode.resistance_2wire: pq.ohm, - HP3456a.Mode.resistance_4wire: pq.ohm, + HP3456a.Mode.dcv: u.volt, + HP3456a.Mode.acv: u.volt, + HP3456a.Mode.acvdcv: u.volt, + HP3456a.Mode.resistance_2wire: u.ohm, + HP3456a.Mode.resistance_4wire: u.ohm, HP3456a.Mode.ratio_dcv_dcv: 1, HP3456a.Mode.ratio_acv_dcv: 1, HP3456a.Mode.ratio_acvdcv_dcv: 1, - HP3456a.Mode.oc_resistence_2wire: pq.ohm, - HP3456a.Mode.oc_resistence_4wire: pq.ohm, + HP3456a.Mode.oc_resistence_2wire: u.ohm, + HP3456a.Mode.oc_resistence_4wire: u.ohm, } diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index d2c16bfd6..d1d32d288 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -12,7 +12,7 @@ from builtins import range from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( PowerSupply, @@ -111,7 +111,7 @@ def mode(self, newval): voltage = unitful_property( "VSET", - pq.volt, + u.volt, set_fmt="{} {:.1f}", output_decoration=float, doc=""" @@ -127,7 +127,7 @@ def mode(self, newval): current = unitful_property( "ISET", - pq.amp, + u.amp, set_fmt="{} {:.1f}", output_decoration=float, doc=""" @@ -143,7 +143,7 @@ def mode(self, newval): voltage_sense = unitful_property( "VOUT", - pq.volt, + u.volt, readonly=True, doc=""" Gets the actual voltage as measured by the sense wires for the @@ -156,7 +156,7 @@ def mode(self, newval): current_sense = unitful_property( "IOUT", - pq.amp, + u.amp, readonly=True, doc=""" Gets the actual output current as measured by the instrument for @@ -169,7 +169,7 @@ def mode(self, newval): overvoltage = unitful_property( "OVSET", - pq.volt, + u.volt, set_fmt="{} {:.1f}", output_decoration=float, doc=""" diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index 06e352558..f879f2f9a 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -37,7 +37,7 @@ from builtins import range from enum import Enum, IntEnum -import quantities as pq +import instruments.units as u from instruments.generic_scpi.scpi_instrument import SCPIInstrument from instruments.hp.hp6652a import HP6652a @@ -259,7 +259,7 @@ class SenseWindow(Enum): voltage_trigger = unitful_property( "VOLT:TRIG", - pq.volt, + u.volt, doc=""" Gets/sets the pending triggered output voltage. @@ -272,7 +272,7 @@ class SenseWindow(Enum): current_trigger = unitful_property( "CURR:TRIG", - pq.amp, + u.amp, doc=""" Gets/sets the pending triggered output current. @@ -299,7 +299,7 @@ class SenseWindow(Enum): current_sense_range = unitful_property( 'SENS:CURR:RANGE', - pq.ampere, + u.ampere, doc=""" Get/set the sense current range by the current max value. @@ -379,7 +379,7 @@ class SenseWindow(Enum): sense_sweep_interval = unitful_property( "SENS:SWE:TINT", - pq.second, + u.second, doc=""" Get/set the digitizer sample spacing. Can be set from 15.6 us to 31200 seconds, the interval will be rounded to the nearest 15.6 us increment. @@ -401,7 +401,7 @@ class SenseWindow(Enum): output_protection_delay = unitful_property( "OUTP:PROT:DEL", - pq.second, + u.second, doc=""" Get/set the time between programming of an output change that produces a constant current condition and the recording of that condigition in diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index f5d88ce03..4092ff8a4 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -11,7 +11,7 @@ from __future__ import absolute_import from __future__ import division -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import (PowerSupply, PowerSupplyChannel) from instruments.util_fns import unitful_property, bool_property @@ -64,7 +64,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): voltage = unitful_property( "VOLT", - pq.volt, + u.volt, doc=""" Gets/sets the output voltage. @@ -77,7 +77,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): current = unitful_property( "CURR", - pq.amp, + u.amp, doc=""" Gets/sets the output current. @@ -90,7 +90,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): voltage_sense = unitful_property( "MEAS:VOLT", - pq.volt, + u.volt, readonly=True, doc=""" Gets the actual output voltage as measured by the sense wires. @@ -102,7 +102,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): current_sense = unitful_property( "MEAS:CURR", - pq.amp, + u.amp, readonly=True, doc=""" Gets the actual output current as measured by the sense wires. @@ -114,7 +114,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): overvoltage = unitful_property( "VOLT:PROT", - pq.volt, + u.volt, doc=""" Gets/sets the overvoltage protection setting in volts. diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index 295f99a6d..3ea88f7fc 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -37,7 +37,7 @@ from __future__ import division import time -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( PowerSupply, @@ -164,7 +164,7 @@ def voltage(self): :type: `float` or `~quantities.Quantity` """ raw = self.query("SOUR:VOLT?") - return pq.Quantity(*split_unit_str(raw, pq.volt)).rescale(pq.volt) + return u.Quantity(*split_unit_str(raw, u.volt)).rescale(u.volt) @voltage.setter def voltage(self, newval): @@ -185,7 +185,7 @@ def voltage(self, newval): # Rescale to the correct unit before printing. This will also # catch bad units. - strval = "{:e}".format(assume_units(newval, pq.volt).rescale(pq.volt).item()) + strval = "{:e}".format(assume_units(newval, u.volt).rescale(u.volt).item()) self.sendcmd('SOUR:VOLT {}'.format(strval)) @property @@ -221,14 +221,14 @@ def voltage_range(self): :units: :math:`\\text{V}`. :type: array of `~quantities.Quantity` """ - value = pq.Quantity(*split_unit_str(self.query("SOUR:VOLT? MAX"), pq.volt)) + value = u.Quantity(*split_unit_str(self.query("SOUR:VOLT? MAX"), u.volt)) if value < 0.: return value, 0. return 0., value current, current_min, current_max = bounded_unitful_property( "SOUR:CURR", - pq.amp, + u.amp, min_fmt_str="{}? MIN", max_fmt_str="{}? MAX", doc=""" @@ -241,7 +241,7 @@ def voltage_range(self): voltage_sense = unitful_property( "MEAS:VOLT", - pq.volt, + u.volt, readonly=True, doc=""" Gets the actual output voltage as measured by the sense wires. @@ -253,7 +253,7 @@ def voltage_range(self): current_sense = unitful_property( "MEAS:CURR", - pq.amp, + u.amp, readonly=True, doc=""" Gets the actual output current as measured by the sense wires. diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index 620be342b..3bf334aaa 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -13,7 +13,7 @@ import struct from enum import Enum, IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Multimeter @@ -29,7 +29,7 @@ class Keithley195(Multimeter): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> dmm = ik.keithley.Keithley195.open_gpibusb('/dev/ttyUSB0', 12) >>> print dmm.measure(dmm.Mode.resistance) @@ -210,7 +210,7 @@ def input_range(self, newval): else: raise ValueError('Only "auto" is acceptable when specifying ' 'the input range as a string.') - if isinstance(newval, pq.quantity.Quantity): + if isinstance(newval, u.quantity.Quantity): newval = float(newval) mode = self.mode @@ -246,7 +246,7 @@ def measure(self, mode=None): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> dmm = ik.keithley.Keithley195.open_gpibusb('/dev/ttyUSB0', 12) >>> print(dmm.measure(dmm.Mode.resistance)) @@ -341,17 +341,17 @@ def auto_range(self): # UNITS ####################################################################### UNITS = { - 'DCV': pq.volt, - 'ACV': pq.volt, - 'ACA': pq.amp, - 'DCA': pq.amp, - 'OHM': pq.ohm, + 'DCV': u.volt, + 'ACV': u.volt, + 'ACA': u.amp, + 'DCA': u.amp, + 'OHM': u.ohm, } UNITS2 = { - Keithley195.Mode.voltage_dc: pq.volt, - Keithley195.Mode.voltage_ac: pq.volt, - Keithley195.Mode.current_dc: pq.amp, - Keithley195.Mode.current_ac: pq.amp, - Keithley195.Mode.resistance: pq.ohm, + Keithley195.Mode.voltage_dc: u.volt, + Keithley195.Mode.voltage_ac: u.volt, + Keithley195.Mode.current_dc: u.amp, + Keithley195.Mode.current_ac: u.amp, + Keithley195.Mode.resistance: u.ohm, } diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index 8e3e245b1..d45265417 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -11,7 +11,7 @@ from builtins import range, map from enum import Enum -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIMultimeter from instruments.abstract_instruments import Multimeter @@ -190,14 +190,14 @@ def units(self): """ mode = self.channel[0].mode if mode == Keithley2182.Mode.voltage_dc: - return pq.volt + return u.volt unit = self.query("UNIT:TEMP?") if unit == "C": - unit = pq.celsius + unit = u.celsius elif unit == "K": - unit = pq.kelvin + unit = u.kelvin elif unit == "F": - unit = pq.fahrenheit + unit = u.fahrenheit else: raise ValueError("Unknown temperature units.") return unit diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index 569e9fc98..7c4aec308 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -40,7 +40,7 @@ from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument @@ -192,7 +192,7 @@ def input_range(self): value = self.get_status()["range"] if isinstance(value, str): return value - return value * pq.amp + return value * u.amp @input_range.setter def input_range(self, newval): @@ -205,7 +205,7 @@ def input_range(self, newval): else: raise ValueError("Only `auto` is acceptable when specifying " "the range as a string.") - if isinstance(newval, pq.quantity.Quantity): + if isinstance(newval, u.quantity.Quantity): newval = float(newval) if isinstance(newval, (float, int)): @@ -422,7 +422,7 @@ def _parse_measurement(self, measurement): raise ValueError("Instrument not in normal mode: {}".format(status.name)) if function != b"DC": raise ValueError("Instrument not returning DC function: {}".format(function)) - current = float(current) * pq.amp if base == b"A" else 10 ** (float(current)) * pq.amp + current = float(current) * u.amp if base == b"A" else 10 ** (float(current)) * u.amp except: raise Exception("Cannot parse measurement: {}".format(measurement)) diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index 5ed6bbd26..a599c4d85 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -40,7 +40,7 @@ from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument @@ -265,7 +265,7 @@ def input_range(self): :type: `~quantities.quantity.Quantity` or `str` """ value = float(self.parse_status_word(self.get_status_word())['range']) - return value * pq.ohm + return value * u.ohm @input_range.setter def input_range(self, newval): @@ -278,7 +278,7 @@ def input_range(self, newval): else: raise ValueError('Only "auto" is acceptable when specifying ' 'the input range as a string.') - if isinstance(newval, pq.quantity.Quantity): + if isinstance(newval, u.quantity.Quantity): newval = float(newval) if isinstance(newval, (float, int)): @@ -451,7 +451,7 @@ def parse_measurement(measurement): polarity = valid['polarity'][polarity] drycircuit = valid['drycircuit'][drycircuit] drive = valid['drive'][drive] - resistance = float(resistance) * pq.ohm + resistance = float(resistance) * u.ohm except: raise Exception('Cannot parse measurement: {}'.format(measurement)) diff --git a/instruments/keithley/keithley6220.py b/instruments/keithley/keithley6220.py index fae1dff99..9744a4dfa 100644 --- a/instruments/keithley/keithley6220.py +++ b/instruments/keithley/keithley6220.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from __future__ import division -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import PowerSupply from instruments.generic_scpi import SCPIInstrument @@ -28,10 +28,10 @@ class Keithley6220(SCPIInstrument, PowerSupply): Example usage: - >>> import quantities as pq + >>> import instruments.units as u >>> import instruments as ik >>> ccs = ik.keithley.Keithley6220.open_gpibusb("/dev/ttyUSB0", 10) - >>> ccs.current = 10 * pq.milliamp # Sets current to 10mA + >>> ccs.current = 10 * u.milliamp # Sets current to 10mA >>> ccs.disable() # Turns off the output and sets the current to 0A """ @@ -68,8 +68,8 @@ def voltage(self, newval): current, current_min, current_max = bounded_unitful_property( "SOUR:CURR", - pq.amp, - valid_range=(-105 * pq.milliamp, +105 * pq.milliamp), + u.amp, + valid_range=(-105 * u.milliamp, +105 * u.milliamp), doc=""" Gets/sets the output current of the source. Value must be between -105mA and +105mA. diff --git a/instruments/keithley/keithley6514.py b/instruments/keithley/keithley6514.py index 80eaecda5..d3bab173d 100644 --- a/instruments/keithley/keithley6514.py +++ b/instruments/keithley/keithley6514.py @@ -12,7 +12,7 @@ from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Electrometer from instruments.generic_scpi import SCPIInstrument @@ -30,7 +30,7 @@ class Keithley6514(SCPIInstrument, Electrometer): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> dmm = ik.keithley.Keithley6514.open_gpibusb('/dev/ttyUSB0', 12) """ @@ -78,10 +78,10 @@ class ValidRange(Enum): # CONSTANTS # _MODE_UNITS = { - Mode.voltage: pq.volt, - Mode.current: pq.amp, - Mode.resistance: pq.ohm, - Mode.charge: pq.coulomb + Mode.voltage: u.volt, + Mode.current: u.amp, + Mode.resistance: u.ohm, + Mode.charge: u.coulomb } # PRIVATE METHODS # diff --git a/instruments/lakeshore/lakeshore340.py b/instruments/lakeshore/lakeshore340.py index 051d32d82..9830869fe 100644 --- a/instruments/lakeshore/lakeshore340.py +++ b/instruments/lakeshore/lakeshore340.py @@ -10,7 +10,7 @@ from __future__ import division from builtins import range -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList @@ -26,7 +26,7 @@ class Lakeshore340(SCPIInstrument): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> inst = ik.lakeshore.Lakeshore340.open_gpibusb('/dev/ttyUSB0', 1) >>> print(inst.sensor[0].temperature) >>> print(inst.sensor[1].temperature) @@ -58,7 +58,7 @@ def temperature(self): :type: `~quantities.quantity.Quantity` """ value = self._parent.query('KRDG?{}'.format(self._idx)) - return pq.Quantity(float(value), pq.Kelvin) + return u.Quantity(float(value), u.Kelvin) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore370.py b/instruments/lakeshore/lakeshore370.py index 8293b8de4..fa79e3a18 100644 --- a/instruments/lakeshore/lakeshore370.py +++ b/instruments/lakeshore/lakeshore370.py @@ -10,7 +10,7 @@ from __future__ import division from builtins import range -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList @@ -62,7 +62,7 @@ def resistance(self): :rtype: `~quantities.quantity.Quantity` """ value = self._parent.query('RDGR? {}'.format(self._idx)) - return pq.Quantity(float(value), pq.ohm) + return u.Quantity(float(value), u.ohm) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 0db502339..649a8448d 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -12,7 +12,7 @@ from builtins import range from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import assume_units, bool_property @@ -20,15 +20,15 @@ # CONSTANTS ################################################################### LAKESHORE_FIELD_UNITS = { - 1: pq.gauss, - 2: pq.tesla, - 3: pq.oersted, - 4: pq.CompoundUnit('A/m') + 1: u.gauss, + 2: u.tesla, + 3: u.oersted, + 4: u.CompoundUnit('A/m') } LAKESHORE_TEMP_UNITS = { - 1: pq.celsius, - 2: pq.kelvin + 1: u.celsius, + 2: u.kelvin } LAKESHORE_FIELD_UNITS_INV = dict((v, k) for k, v in @@ -47,11 +47,11 @@ class Lakeshore475(SCPIInstrument): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> gm = ik.lakeshore.Lakeshore475.open_gpibusb('/dev/ttyUSB0', 1) >>> print(gm.field) - >>> gm.field_units = pq.tesla - >>> gm.field_setpoint = 0.05 * pq.tesla + >>> gm.field_units = u.tesla + >>> gm.field_setpoint = 0.05 * u.tesla """ # ENUMS ## @@ -112,7 +112,7 @@ def field_units(self): @field_units.setter def field_units(self, newval): - if isinstance(newval, pq.unitquantity.UnitQuantity): + if isinstance(newval, u.unitquantity.UnitQuantity): if newval in LAKESHORE_FIELD_UNITS_INV: self.sendcmd('UNIT ' + LAKESHORE_FIELD_UNITS_INV[newval]) else: @@ -134,7 +134,7 @@ def temp_units(self): @temp_units.setter def temp_units(self, newval): - if isinstance(newval, pq.unitquantity.UnitQuantity): + if isinstance(newval, u.unitquantity.UnitQuantity): if newval in LAKESHORE_TEMP_UNITS_INV: self.sendcmd('TUNIT ' + LAKESHORE_TEMP_UNITS_INV[newval]) else: @@ -158,7 +158,7 @@ def field_setpoint(self): @field_setpoint.setter def field_setpoint(self, newval): units = self.field_units - newval = float(assume_units(newval, pq.gauss).rescale(units).magnitude) + newval = float(assume_units(newval, u.gauss).rescale(units).magnitude) self.sendcmd('CSETP {}'.format(newval)) @property @@ -171,8 +171,8 @@ def field_control_params(self): """ params = self.query('CPARAM?').strip().split(',') params = [float(x) for x in params] - params[2] = params[2] * self.field_units / pq.minute - params[3] = params[3] * pq.volt / pq.minute + params[2] = params[2] * self.field_units / u.minute + params[3] = params[3] * u.volt / u.minute return tuple(params) @field_control_params.setter @@ -184,10 +184,10 @@ def field_control_params(self, newval): newval[0] = float(newval[0]) newval[1] = float(newval[1]) - unit = self.field_units / pq.minute + unit = self.field_units / u.minute newval[2] = float( assume_units(newval[2], unit).rescale(unit).magnitude) - unit = pq.volt / pq.minute + unit = u.volt / u.minute newval[3] = float( assume_units(newval[3], unit).rescale(unit).magnitude) @@ -243,7 +243,7 @@ def ramp_rate(self): @ramp_rate.setter def ramp_rate(self, newval): - unit = self.field_units / pq.minute + unit = self.field_units / u.minute newval = float(assume_units(newval, unit).rescale(unit).magnitude) values = list(self.field_control_params) values[2] = newval @@ -262,7 +262,7 @@ def control_slope_limit(self): @control_slope_limit.setter def control_slope_limit(self, newval): - unit = pq.volt / pq.minute + unit = u.volt / u.minute newval = float(assume_units(newval, unit).rescale(unit).magnitude) values = list(self.field_control_params) values[3] = newval diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py index cfe04b2ab..f22873414 100644 --- a/instruments/minghe/mhs5200a.py +++ b/instruments/minghe/mhs5200a.py @@ -14,7 +14,7 @@ from builtins import range from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import FunctionGenerator from instruments.util_fns import ProxyList, assume_units @@ -70,7 +70,7 @@ def _get_amplitude_(self): def _set_amplitude_(self, magnitude, units): if units == self._mhs.VoltageMode.peak_to_peak or \ units == self._mhs.VoltageMode.rms: - magnitude = assume_units(magnitude, "V").rescale(pq.V).magnitude + magnitude = assume_units(magnitude, "V").rescale(u.V).magnitude elif units == self._mhs.VoltageMode.dBm: raise NotImplementedError("Decibel units are not supported.") magnitude *= 100 @@ -122,12 +122,12 @@ def frequency(self): """ query = ":r{0}f".format(self._chan) response = self._mhs.query(query) - freq = float(response.replace(query, ""))*pq.Hz + freq = float(response.replace(query, ""))*u.Hz return freq/100.0 @frequency.setter def frequency(self, new_val): - new_val = assume_units(new_val, pq.Hz).rescale(pq.Hz).\ + new_val = assume_units(new_val, u.Hz).rescale(u.Hz).\ magnitude*100.0 query = ":s{0}f{1}".format(self._chan, int(new_val)) self._mhs.sendcmd(query) @@ -164,11 +164,11 @@ def phase(self): # need to convert query = ":r{0}p".format(self._chan) response = self._mhs.query(query) - return int(response.replace(query, ""))*pq.deg + return int(response.replace(query, ""))*u.deg @phase.setter def phase(self, new_val): - new_val = assume_units(new_val, pq.deg).rescale("deg").magnitude + new_val = assume_units(new_val, u.deg).rescale("deg").magnitude query = ":s{0}p{1}".format(self._chan, int(new_val)) self._mhs.sendcmd(query) diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index b1adc13e2..601ae4c4b 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -19,7 +19,7 @@ from builtins import range, map from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.newport.errors import NewportError @@ -304,7 +304,7 @@ class NewportESP301Axis(object): returned by `NewportESP301.axis`. """ # quantities micro inch - micro_inch = pq.UnitQuantity('micro-inch', pq.inch / 1e6, symbol='uin') + micro_inch = u.UnitQuantity('micro-inch', u.inch / 1e6, symbol='uin') # Some more work might need to be done here to make # the encoder_step and motor_step functional @@ -312,18 +312,18 @@ class NewportESP301Axis(object): # going to do this until I have a physical device _unit_dict = { - 0: pq.count, - 1: pq.count, - 2: pq.mm, - 3: pq.um, - 4: pq.inch, - 5: pq.mil, + 0: u.count, + 1: u.count, + 2: u.mm, + 3: u.um, + 4: u.inch, + 5: u.mil, 6: micro_inch, # compound unit for micro-inch - 7: pq.deg, - 8: pq.grad, - 9: pq.rad, - 10: pq.mrad, - 11: pq.urad, + 7: u.deg, + 8: u.grad, + 9: u.rad, + 10: u.mrad, + 11: u.urad, } def __init__(self, controller, axis_id): @@ -405,15 +405,15 @@ def acceleration(self): return assume_units( float(self._newport_cmd("AC?", target=self.axis_id)), - self._units / (pq.s**2) + self._units / (u.s**2) ) @acceleration.setter def acceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (pq.s**2)).rescale( - self._units / (pq.s**2)).magnitude) + newval = float(assume_units(newval, self._units / (u.s**2)).rescale( + self._units / (u.s**2)).magnitude) self._newport_cmd("AC", target=self.axis_id, params=[newval]) @property @@ -427,15 +427,15 @@ def deceleration(self): """ return assume_units( float(self._newport_cmd("AG?", target=self.axis_id)), - self._units / (pq.s**2) + self._units / (u.s**2) ) @deceleration.setter def deceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (pq.s**2)).rescale( - self._units / (pq.s**2)).magnitude) + newval = float(assume_units(newval, self._units / (u.s**2)).rescale( + self._units / (u.s**2)).magnitude) self._newport_cmd("AG", target=self.axis_id, params=[newval]) @property @@ -449,13 +449,13 @@ def estop_deceleration(self): """ return assume_units( float(self._newport_cmd("AE?", target=self.axis_id)), - self._units / (pq.s**2) + self._units / (u.s**2) ) @estop_deceleration.setter def estop_deceleration(self, decel): - decel = float(assume_units(decel, self._units / (pq.s**2)).rescale( - self._units / (pq.s**2)).magnitude) + decel = float(assume_units(decel, self._units / (u.s**2)).rescale( + self._units / (u.s**2)).magnitude) self._newport_cmd("AE", target=self.axis_id, params=[decel]) @property @@ -470,13 +470,13 @@ def jerk(self): return assume_units( float(self._newport_cmd("JK?", target=self.axis_id)), - self._units / (pq.s**3) + self._units / (u.s**3) ) @jerk.setter def jerk(self, jerk): - jerk = float(assume_units(jerk, self._units / (pq.s**3)).rescale( - self._units / (pq.s**3)).magnitude) + jerk = float(assume_units(jerk, self._units / (u.s**3)).rescale( + self._units / (u.s**3)).magnitude) self._newport_cmd("JK", target=self.axis_id, params=[jerk]) @property @@ -490,13 +490,13 @@ def velocity(self): """ return assume_units( float(self._newport_cmd("VA?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @velocity.setter def velocity(self, velocity): - velocity = float(assume_units(velocity, self._units / (pq.s)).rescale( - self._units / pq.s).magnitude) + velocity = float(assume_units(velocity, self._units / (u.s)).rescale( + self._units / u.s).magnitude) self._newport_cmd("VA", target=self.axis_id, params=[velocity]) @property @@ -510,15 +510,15 @@ def max_velocity(self): """ return assume_units( float(self._newport_cmd("VU?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @max_velocity.setter def max_velocity(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / pq.s).rescale( - self._units / pq.s).magnitude) + newval = float(assume_units(newval, self._units / u.s).rescale( + self._units / u.s).magnitude) self._newport_cmd("VU", target=self.axis_id, params=[newval]) @property @@ -532,15 +532,15 @@ def max_base_velocity(self): """ return assume_units( float(self._newport_cmd("VB?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @max_base_velocity.setter def max_base_velocity(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / pq.s).rescale( - self._units / pq.s).magnitude) + newval = float(assume_units(newval, self._units / u.s).rescale( + self._units / u.s).magnitude) self._newport_cmd("VB", target=self.axis_id, params=[newval]) @property @@ -554,7 +554,7 @@ def jog_high_velocity(self): """ return assume_units( float(self._newport_cmd("JH?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @jog_high_velocity.setter @@ -563,8 +563,8 @@ def jog_high_velocity(self, newval): return newval = float(assume_units( newval, - self._units / pq.s - ).rescale(self._units / pq.s).magnitude) + self._units / u.s + ).rescale(self._units / u.s).magnitude) self._newport_cmd("JH", target=self.axis_id, params=[newval]) @property @@ -578,7 +578,7 @@ def jog_low_velocity(self): """ return assume_units( float(self._newport_cmd("JW?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @jog_low_velocity.setter @@ -587,8 +587,8 @@ def jog_low_velocity(self, newval): return newval = float(assume_units( newval, - self._units / pq.s - ).rescale(self._units / pq.s).magnitude) + self._units / u.s + ).rescale(self._units / u.s).magnitude) self._newport_cmd("JW", target=self.axis_id, params=[newval]) @property @@ -602,7 +602,7 @@ def homing_velocity(self): """ return assume_units( float(self._newport_cmd("OH?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @homing_velocity.setter @@ -611,8 +611,8 @@ def homing_velocity(self, newval): return newval = float(assume_units( newval, - self._units / pq.s - ).rescale(self._units / pq.s).magnitude) + self._units / u.s + ).rescale(self._units / u.s).magnitude) self._newport_cmd("OH", target=self.axis_id, params=[newval]) @property @@ -626,15 +626,15 @@ def max_acceleration(self): """ return assume_units( float(self._newport_cmd("AU?", target=self.axis_id)), - self._units / (pq.s**2) + self._units / (u.s**2) ) @max_acceleration.setter def max_acceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (pq.s**2)).rescale( - self._units / (pq.s**2)).magnitude) + newval = float(assume_units(newval, self._units / (u.s**2)).rescale( + self._units / (u.s**2)).magnitude) self._newport_cmd("AU", target=self.axis_id, params=[newval]) @property @@ -651,8 +651,8 @@ def max_deceleration(self): @max_deceleration.setter def max_deceleration(self, decel): - decel = float(assume_units(decel, self._units / (pq.s**2)).rescale( - self._units / (pq.s**2)).magnitude) + decel = float(assume_units(decel, self._units / (u.s**2)).rescale( + self._units / (u.s**2)).magnitude) self.max_acceleration = decel @property @@ -694,7 +694,7 @@ def desired_velocity(self): """ return assume_units( float(self._newport_cmd("DP?", target=self.axis_id)), - self._units / pq.s + self._units / u.s ) @property @@ -738,7 +738,7 @@ def units(self, newval): return if isinstance(newval, int): self._units = self._get_pq_unit(NewportESP301Units(int(newval))) - elif isinstance(newval, pq.Quantity): + elif isinstance(newval, u.Quantity): self._units = newval newval = self._get_unit_num(newval) self._set_units(newval) @@ -863,15 +863,15 @@ def current(self): """ return assume_units( float(self._newport_cmd("QI?", target=self.axis_id)), - pq.A + u.A ) @current.setter def current(self, newval): if newval is None: return - current = float(assume_units(newval, pq.A).rescale( - pq.A).magnitude) + current = float(assume_units(newval, u.A).rescale( + u.A).magnitude) self._newport_cmd("QI", target=self.axis_id, params=[current]) @property @@ -885,15 +885,15 @@ def voltage(self): """ return assume_units( float(self._newport_cmd("QV?", target=self.axis_id)), - pq.V + u.V ) @voltage.setter def voltage(self, newval): if newval is None: return - voltage = float(assume_units(newval, pq.V).rescale( - pq.V).magnitude) + voltage = float(assume_units(newval, u.V).rescale( + u.V).magnitude) self._newport_cmd("QV", target=self.axis_id, params=[voltage]) @property @@ -1192,10 +1192,10 @@ def wait_for_motion(self, poll_interval=0.01, max_wait=None): # In programming mode, the "WS" command should be # sent instead, and the two parameters to this method should # be ignored. - poll_interval = float(assume_units(poll_interval, pq.s).rescale( - pq.s).magnitude) - max_wait = float(assume_units(max_wait, pq.s).rescale( - pq.s).magnitude) + poll_interval = float(assume_units(poll_interval, u.s).rescale( + u.s).magnitude) + max_wait = float(assume_units(max_wait, u.s).rescale( + u.s).magnitude) tic = time() while True: if self.is_motion_done: @@ -1282,12 +1282,12 @@ def setup_axis(self, **kwargs): 'configuration') if 'reduce_motor_torque_time' in kwargs and 'reduce_motor_torque_percentage' in kwargs: motor_time = kwargs['reduce_motor_torque_time'] - motor_time = int(assume_units(motor_time, pq.ms).rescale(pq.ms).magnitude) + motor_time = int(assume_units(motor_time, u.ms).rescale(u.ms).magnitude) if motor_time < 0 or motor_time > 60000: raise ValueError("Time must be between 0 and 60000 ms") percentage = kwargs['reduce_motor_torque_percentage'] - percentage = int(assume_units(percentage, pq.percent).rescale( - pq.percent).magnitude) + percentage = int(assume_units(percentage, u.percent).rescale( + u.percent).magnitude) if percentage < 0 or percentage > 100: raise ValueError("Time must be between 0 and 60000 ms") self._newport_cmd( diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index 0f4dfbfa3..d0e5c8978 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -13,7 +13,7 @@ from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.util_fns import convert_temperature, assume_units @@ -85,7 +85,7 @@ def target(self): :type: `~quantities.Quantity` """ response = float(self._parent.query("rstli?")) - return response*pq.mA + return response*u.mA @property def enabled(self): @@ -174,7 +174,7 @@ def target(self): :type: `~quantities.Quantities` """ response = self._parent.query("rslp?") - return float(response)*pq.mW + return float(response)*u.mW @property def enabled(self): @@ -254,10 +254,10 @@ def on_time(self): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> laser = ik.ondax.LM.open_serial('/dev/ttyUSB0', baud=1234) >>> print(laser.modulation.on_time) - >>> laser.modulation.on_time = 1 * pq.ms + >>> laser.modulation.on_time = 1 * u.ms :return: The TTL modulation on time :units: As specified (if a `~quantities.Quantity`) or assumed @@ -265,11 +265,11 @@ def on_time(self): :type: `~quantities.Quantity` """ response = self._parent.query("stsont?") - return float(response)*pq.ms + return float(response)*u.ms @on_time.setter def on_time(self, newval): - newval = assume_units(newval, pq.ms).rescale(pq.ms).magnitude + newval = assume_units(newval, u.ms).rescale(u.ms).magnitude self._parent.sendcmd("stsont:"+str(newval)) @property @@ -282,10 +282,10 @@ def off_time(self): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> laser = ik.ondax.LM.open_serial('/dev/ttyUSB0', baud=1234) >>> print(laser.modulation.on_time) - >>> laser.modulation.on_time = 1 * pq.ms + >>> laser.modulation.on_time = 1 * u.ms :return: The TTL modulation off time. :units: As specified (if a `~quantities.Quantity`) or assumed @@ -293,11 +293,11 @@ def off_time(self): :type: `~quantities.Quantity` """ response = self._parent.query("stsofft?") - return float(response)*pq.ms + return float(response)*u.ms @off_time.setter def off_time(self, newval): - newval = assume_units(newval, pq.ms).rescale(pq.ms).magnitude + newval = assume_units(newval, u.ms).rescale(u.ms).magnitude self._parent.sendcmd("stsofft:"+str(newval)) @property @@ -358,7 +358,7 @@ def current(self): :type: `~quantities.Quantity` """ response = self._parent.query("rti?") - return float(response)*pq.mA + return float(response)*u.mA @property def target(self): @@ -377,7 +377,7 @@ def target(self): :type: `~quantities.Quantity` """ response = self._parent.query("rstt?") - return float(response)*pq.degC + return float(response)*u.degC @property def enabled(self): @@ -434,11 +434,11 @@ def current(self): :type: `~quantities.Quantity` """ response = self.query("rli?") - return float(response)*pq.mA + return float(response)*u.mA @current.setter def current(self, newval): - newval = assume_units(newval, pq.mA).rescale(pq.mA).magnitude + newval = assume_units(newval, u.mA).rescale(u.mA).magnitude self.sendcmd("slc:"+str(newval)) @property @@ -452,11 +452,11 @@ def maximum_current(self): :type: `~quantities.Quantity` """ response = self.query("rlcm?") - return float(response)*pq.mA + return float(response)*u.mA @maximum_current.setter def maximum_current(self, newval): - newval = assume_units(newval, pq.mA).rescale('mA').magnitude + newval = assume_units(newval, u.mA).rescale('mA').magnitude self.sendcmd("smlc:" + str(newval)) @property @@ -469,11 +469,11 @@ def power(self): :rtype: `~quantities.Quantity` """ response = self.query("rlp?") - return float(response)*pq.mW + return float(response)*u.mW @power.setter def power(self, newval): - newval = assume_units(newval, pq.mW).rescale(pq.mW).magnitude + newval = assume_units(newval, u.mW).rescale(u.mW).magnitude self.sendcmd("slp:"+str(newval)) @property @@ -506,11 +506,11 @@ def temperature(self): :type: `~quantities.Quantity` """ response = self.query("rtt?") - return float(response)*pq.degC + return float(response)*u.degC @temperature.setter def temperature(self, newval): - newval = convert_temperature(newval, pq.degC).magnitude + newval = convert_temperature(newval, u.degC).magnitude self.sendcmd("stt:"+str(newval)) @property diff --git a/instruments/oxford/oxforditc503.py b/instruments/oxford/oxforditc503.py index ab64ebcff..4aa452817 100644 --- a/instruments/oxford/oxforditc503.py +++ b/instruments/oxford/oxforditc503.py @@ -10,7 +10,7 @@ from __future__ import division from builtins import range -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.util_fns import ProxyList @@ -62,7 +62,7 @@ def temperature(self): :type: `~quantities.quantity.Quantity` """ value = float(self._parent.query('R{}'.format(self._idx))[1:]) - return pq.Quantity(value, pq.Kelvin) + return u.Quantity(value, u.Kelvin) # PROPERTIES # diff --git a/instruments/phasematrix/phasematrix_fsw0020.py b/instruments/phasematrix/phasematrix_fsw0020.py index ba4a7f917..86184ac9e 100644 --- a/instruments/phasematrix/phasematrix_fsw0020.py +++ b/instruments/phasematrix/phasematrix_fsw0020.py @@ -27,9 +27,9 @@ class PhaseMatrixFSW0020(SingleChannelSG): Example:: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> inst = ik.phasematrix.PhaseMatrixFSW0020.open_serial("/dev/ttyUSB0", baud=115200) - >>> inst.frequency = 1 * pq.GHz + >>> inst.frequency = 1 * u.GHz >>> inst.power = 0 * ik.units.dBm # Can omit units and will assume dBm >>> inst.output = True """ diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index acf1d8d0f..e72b07ff1 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -12,7 +12,7 @@ from builtins import range from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import (enum_property, bool_property, int_property, @@ -69,7 +69,7 @@ def resistance(self): self._parent.input_source = self._parent.InputSource.actual # Next, prep a measurement with the ADC command self._parent.sendcmd("ADC") - return float(self._parent.query("RES?")) * pq.ohm + return float(self._parent.query("RES?")) * u.ohm # ENUMS # diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index e2c785b31..c7ccf658d 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -13,7 +13,7 @@ from builtins import range, map from enum import Enum -import quantities as pq +import instruments.units as u from instruments.generic_scpi.scpi_instrument import SCPIInstrument from instruments.util_fns import ( @@ -229,11 +229,11 @@ def window(self): of units nanoseconds. :type: `~quantities.Quantity` """ - return pq.Quantity(*split_unit_str(self.query("WIND?"), "ns")) + return u.Quantity(*split_unit_str(self.query("WIND?"), "ns")) @window.setter def window(self, new_val): - new_val_mag = int(assume_units(new_val, pq.ns).rescale(pq.ns).magnitude) + new_val_mag = int(assume_units(new_val, u.ns).rescale(u.ns).magnitude) if new_val_mag < 0 or new_val_mag > 7: raise ValueError("Window is out of range.") # window must be an integer! @@ -249,12 +249,12 @@ def delay(self): :rtype: quantities.ns :return: the delay value """ - return pq.Quantity(*split_unit_str(self.query("DELA?"), "ns")) + return u.Quantity(*split_unit_str(self.query("DELA?"), "ns")) @delay.setter def delay(self, new_val): - new_val = assume_units(new_val, pq.ns).rescale(pq.ns) - if new_val < 0*pq.ns or new_val > 14*pq.ns: + new_val = assume_units(new_val, u.ns).rescale(u.ns) + if new_val < 0*u.ns or new_val > 14*u.ns: raise ValueError("New delay value is out of bounds.") if new_val.magnitude % 2 != 0: raise ValueError("New magnitude must be an even number") @@ -272,7 +272,7 @@ def dwell_time(self): """ # the older versions of the firmware erroneously report the units of the # dwell time as being seconds rather than ms - dwell_time = pq.Quantity(*split_unit_str(self.query("DWEL?"), "s")) + dwell_time = u.Quantity(*split_unit_str(self.query("DWEL?"), "s")) if self.firmware[0] <= 2 and self.firmware[1] <= 1: return dwell_time/1000.0 @@ -280,7 +280,7 @@ def dwell_time(self): @dwell_time.setter def dwell_time(self, new_val): - new_val_mag = assume_units(new_val, pq.s).rescale(pq.s).magnitude + new_val_mag = assume_units(new_val, u.s).rescale(u.s).magnitude if new_val_mag < 0: raise ValueError("Dwell time cannot be negative.") self.sendcmd(":DWEL {}".format(new_val_mag)) diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index facc54eab..38affab67 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -13,7 +13,7 @@ from builtins import range, map from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.util_fns import ( @@ -31,9 +31,9 @@ class MC1(Instrument): def __init__(self, filelike): super(MC1, self).__init__(filelike) self.terminator = "\r" - self._increment = 1*pq.ms - self._lower_limit = -300*pq.ms - self._upper_limit = 300*pq.ms + self._increment = 1*u.ms + self._lower_limit = -300*u.ms + self._upper_limit = 300*u.ms self._firmware = None self._controller = None @@ -60,7 +60,7 @@ def increment(self): @increment.setter def increment(self, newval): - self._increment = assume_units(newval, pq.ms).rescale(pq.ms) + self._increment = assume_units(newval, u.ms).rescale(u.ms) @property def lower_limit(self): @@ -74,7 +74,7 @@ def lower_limit(self): @lower_limit.setter def lower_limit(self, newval): - self._lower_limit = assume_units(newval, pq.ms).rescale(pq.ms) + self._lower_limit = assume_units(newval, u.ms).rescale(u.ms) @property def upper_limit(self): @@ -88,7 +88,7 @@ def upper_limit(self): @upper_limit.setter def upper_limit(self, newval): - self._upper_limit = assume_units(newval, pq.ms).rescale(pq.ms) + self._upper_limit = assume_units(newval, u.ms).rescale(u.ms) direction = unitful_property( command="DIRE", @@ -99,7 +99,7 @@ def upper_limit(self, newval): :type: `~quantities.Quantity` :units: milliseconds """, - units=pq.ms, + units=u.ms, readonly=True ) @@ -113,8 +113,8 @@ def upper_limit(self, newval): :units: milliseconds """, format_code='{:.0f}', - units=pq.ms, - valid_range=(0*pq.ms, 100*pq.ms), + units=u.ms, + valid_range=(0*u.ms, 100*u.ms), set_fmt=":{} {}" ) @@ -140,7 +140,7 @@ def internal_position(self): :type: `~quantities.Quantity` :units: millimeters """, - units=pq.mm, + units=u.mm, readonly=True ) @@ -167,8 +167,8 @@ def internal_position(self): :units: milliseconds """, format_code='{:.0f}', - units=pq.ms, - valid_range=(1*pq.ms, 100*pq.ms), + units=u.ms, + valid_range=(1*u.ms, 100*u.ms), set_fmt=":{} {}" ) @@ -247,7 +247,7 @@ def move(self, new_position): :type new_position: `~quantities.Quantity` """ if self.lower_limit <= new_position <= self.upper_limit: - new_position = assume_units(new_position, pq.ms).rescale(pq.ms) + new_position = assume_units(new_position, u.ms).rescale(u.ms) clock_cycles = new_position/self.step_size cmd = ":MOVE "+str(int(clock_cycles)) self.sendcmd(cmd) diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index c86de3c63..26df0825f 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -11,7 +11,7 @@ from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import FunctionGenerator from instruments.generic_scpi import SCPIInstrument @@ -28,9 +28,9 @@ class SRS345(SCPIInstrument, FunctionGenerator): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> srs = ik.srs.SRS345.open_gpib('/dev/ttyUSB0', 1) - >>> srs.frequency = 1 * pq.MHz + >>> srs.frequency = 1 * u.MHz >>> print(srs.offset) >>> srs.function = srs.Function.triangle """ @@ -79,7 +79,7 @@ class Function(IntEnum): frequency = unitful_property( command="FREQ", - units=pq.Hz, + units=u.Hz, doc=""" Gets/sets the output frequency. @@ -101,7 +101,7 @@ class Function(IntEnum): offset = unitful_property( command="OFFS", - units=pq.volt, + units=u.volt, doc=""" Gets/sets the offset voltage for the output waveform. @@ -112,7 +112,7 @@ class Function(IntEnum): phase = unitful_property( command="PHSE", - units=pq.degree, + units=u.degree, doc=""" Gets/sets the phase for the output waveform. diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index 93f195c43..3a5aefe6e 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -17,7 +17,7 @@ from enum import Enum, IntEnum import numpy as np -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.abstract_instruments.comm import ( @@ -45,9 +45,9 @@ class SRS830(SCPIInstrument): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> srs = ik.srs.SRS830.open_gpibusb('/dev/ttyUSB0', 1) - >>> srs.frequency = 1000 * pq.hertz # Lock-In frequency + >>> srs.frequency = 1000 * u.hertz # Lock-In frequency >>> data = srs.take_measurement(1, 10) # 1Hz sample rate, 10 samples total """ @@ -139,7 +139,7 @@ class Mode(Enum): frequency = unitful_property( "FREQ", - pq.hertz, + u.hertz, valid_range=(0, None), doc=""" Gets/sets the lock-in amplifier reference frequency. @@ -152,8 +152,8 @@ class Mode(Enum): phase, phase_min, phase_max = bounded_unitful_property( "PHAS", - pq.degrees, - valid_range=(-360 * pq.degrees, 730 * pq.degrees), + u.degrees, + valid_range=(-360 * u.degrees, 730 * u.degrees), doc=""" Gets/set the phase of the internal reference signal. @@ -167,8 +167,8 @@ class Mode(Enum): amplitude, amplitude_min, amplitude_max = bounded_unitful_property( "SLVL", - pq.volt, - valid_range=(0.004 * pq.volt, 5 * pq.volt), + u.volt, + valid_range=(0.004 * u.volt, 5 * u.volt), doc=""" Gets/set the amplitude of the internal reference signal. @@ -215,7 +215,7 @@ def sample_rate(self): value = int(self.query('SRAT?')) if value == 14: return "trigger" - return pq.Quantity(VALID_SAMPLE_RATES[value], pq.Hz) + return u.Quantity(VALID_SAMPLE_RATES[value], u.Hz) @sample_rate.setter def sample_rate(self, newval): diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 3042ebc5b..5f7767ad5 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -10,14 +10,12 @@ from __future__ import division from contextlib import contextmanager from builtins import range - from enum import Enum -import quantities as pq import numpy as np - from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -43,11 +41,11 @@ def __init__(self, filelike): # Note that the SRS CTC-100 uses '\xb0' to represent '°'. _UNIT_NAMES = { - '\xb0C': pq.celsius, - 'W': pq.watt, - 'V': pq.volt, - '\xea': pq.ohm, - '': pq.dimensionless + '\xb0C': u.celsius, + 'W': u.watt, + 'V': u.volt, + '\xea': u.ohm, + '': u.dimensionless } # INNER CLASSES ## @@ -126,7 +124,7 @@ def value(self): # WARNING: Queries all units all the time. # TODO: Make an OutputChannel that subclasses this class, # and add a setter for value. - return pq.Quantity( + return u.Quantity( float(self._get('value')), self.units ) @@ -197,7 +195,7 @@ def average(self): :type: `~quantities.Quantity` """ - return pq.Quantity( + return u.Quantity( float(self._get('average')), self.units ) @@ -210,7 +208,7 @@ def std_dev(self): :type: `~quantities.Quantity` """ - return pq.Quantity( + return u.Quantity( float(self._get('SD')), self.units ) @@ -240,7 +238,7 @@ def get_log_point(self, which='next', units=None): 'getLog.xy {}, {}'.format(self._chan_name, which) ).split(',') ] - return pq.Quantity(point[0], 'ms'), pq.Quantity(point[1], units) + return u.Quantity(point[0], 'ms'), u.Quantity(point[1], units) def get_log(self): """ @@ -261,8 +259,8 @@ def get_log(self): # Make an empty quantity that size for the times and for the channel # values. - ts = pq.Quantity(np.empty((n_points,)), 'ms') - temps = pq.Quantity(np.empty((n_points,)), units) + ts = u.Quantity(np.empty((n_points,)), 'ms') + temps = u.Quantity(np.empty((n_points,)), units) # Reset the position to the first point, then save it. # pylint: disable=protected-access @@ -309,7 +307,7 @@ def channel_units(self): Returns a dictionary from channel names to channel units, using the ``getOutput.units`` command. Unknown units and dimensionless quantities are presented the same way by the instrument, and so both are reported - using `pq.dimensionless`. + using `u.dimensionless`. :rtype: `dict` with channel names as keys and units as values """ diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index 932858f97..972c0c920 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -12,7 +12,7 @@ from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.abstract_instruments.comm import GPIBCommunicator @@ -59,11 +59,11 @@ def delay(self): """ Gets/sets the delay of this channel. Formatted as a two-tuple of the reference and the delay time. - For example, ``(SRSDG645.Channels.A, pq.Quantity(10, "ps"))`` + For example, ``(SRSDG645.Channels.A, u.Quantity(10, "ps"))`` indicates a delay of 10 picoseconds from delay channel A. """ resp = self._ddg.query("DLAY?{}".format(int(self._chan))).split(",") - return SRSDG645.Channels(int(resp[0])), pq.Quantity(float(resp[1]), "s") + return SRSDG645.Channels(int(resp[0])), u.Quantity(float(resp[1]), "s") @delay.setter def delay(self, newval): @@ -83,10 +83,10 @@ class SRSDG645(SCPIInstrument): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> srs = ik.srs.SRSDG645.open_gpibusb('/dev/ttyUSB0', 1) - >>> srs.channel["B"].delay = (srs.channel["A"], pq.Quantity(10, 'ns')) - >>> srs.output["AB"].level_amplitude = pq.Quantity(4.0, "V") + >>> srs.channel["B"].delay = (srs.channel["A"], u.Quantity(10, 'ns')) + >>> srs.output["AB"].level_amplitude = u.Quantity(4.0, "V") .. _user's guide: http://www.thinksrs.com/downloads/PDFs/Manuals/DG645m.pdf """ @@ -210,7 +210,7 @@ def level_amplitude(self): :type: `float` or :class:`~quantities.Quantity` :units: As specified, or :math:`\\text{V}` by default. """ - return pq.Quantity( + return u.Quantity( float(self._parent.query('LAMP? {}'.format(self._idx))), 'V' ) @@ -228,7 +228,7 @@ def level_offset(self): :type: `float` or :class:`~quantities.Quantity` :units: As specified, or :math:`\\text{V}` by default. """ - return pq.Quantity( + return u.Quantity( float(self._parent.query('LOFF? {}'.format(self._idx))), 'V' ) @@ -304,12 +304,12 @@ def trigger_rate(self): :type: `~quantities.Quantity` or `float` :units: As passed or Hz if not specified. """ - return pq.Quantity(float(self.query("TRAT?")), pq.Hz) + return u.Quantity(float(self.query("TRAT?")), u.Hz) @trigger_rate.setter def trigger_rate(self, newval): - newval = assume_units(newval, pq.Hz) - self.sendcmd("TRAT {}".format(newval.rescale(pq.Hz).magnitude)) + newval = assume_units(newval, u.Hz) + self.sendcmd("TRAT {}".format(newval.rescale(u.Hz).magnitude)) @property def trigger_source(self): @@ -332,8 +332,8 @@ def holdoff(self): :type: `~quantities.Quantity` or `float` :units: As passed, or s if not specified. """ - return pq.Quantity(float(self.query("HOLD?")), pq.s) + return u.Quantity(float(self.query("HOLD?")), u.s) @holdoff.setter def holdoff(self, newval): - self.sendcmd("HOLD {}".format(newval.rescale(pq.s).magnitude)) + self.sendcmd("HOLD {}".format(newval.rescale(u.s).magnitude)) diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index c595f78f9..ec2d85eb9 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -13,7 +13,7 @@ from enum import Enum import numpy as np -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import assume_units, ProxyList @@ -68,16 +68,16 @@ def amplitude(self): of units Volts. :type: `~quantities.Quantity` with units Volts peak-to-peak. """ - return pq.Quantity( + return u.Quantity( float(self._tek.query("FG:{}:AMPL?".format(self._name)).strip()), - pq.V + u.V ) @amplitude.setter def amplitude(self, newval): self._tek.sendcmd("FG:{}:AMPL {}".format( self._name, - assume_units(newval, pq.V).rescale(pq.V).magnitude + assume_units(newval, u.V).rescale(u.V).magnitude )) @property @@ -89,16 +89,16 @@ def offset(self): of units Volts. :type: `~quantities.Quantity` with units Volts. """ - return pq.Quantity( + return u.Quantity( float(self._tek.query("FG:{}:OFFS?".format(self._name)).strip()), - pq.V + u.V ) @offset.setter def offset(self, newval): self._tek.sendcmd("FG:{}:OFFS {}".format( self._name, - assume_units(newval, pq.V).rescale(pq.V).magnitude + assume_units(newval, u.V).rescale(u.V).magnitude )) @property @@ -111,15 +111,15 @@ def frequency(self): of units Hertz. :type: `~quantities.Quantity` with units Hertz. """ - return pq.Quantity( + return u.Quantity( float(self._tek.query("FG:FREQ?").strip()), - pq.Hz + u.Hz ) @frequency.setter def frequency(self, newval): self._tek.sendcmd("FG:FREQ {}HZ".format( - assume_units(newval, pq.Hz).rescale(pq.Hz).magnitude + assume_units(newval, u.Hz).rescale(u.Hz).magnitude )) @property diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index a70767b7c..8381818d8 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -15,7 +15,7 @@ from builtins import range from enum import Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource @@ -315,7 +315,7 @@ class SpectralWindow(Enum): filter_risetime = unitful_property( "FILT:RIS", - pq.second + u.second ) label = string_property( @@ -348,7 +348,7 @@ class SpectralWindow(Enum): spectral_center = unitful_property( "SPEC:CENTER", - pq.Hz, + u.Hz, doc=""" The desired frequency of the spectral analyzer output data span in Hz. @@ -357,7 +357,7 @@ class SpectralWindow(Enum): spectral_gatepos = unitful_property( "SPEC:GATEPOS", - pq.second, + u.second, doc=""" The gate position. Units are represented in seconds, with respect to trigger position. @@ -366,7 +366,7 @@ class SpectralWindow(Enum): spectral_gatewidth = unitful_property( "SPEC:GATEWIDTH", - pq.second, + u.second, doc=""" The time across the 10-division screen in seconds. """ @@ -408,7 +408,7 @@ class SpectralWindow(Enum): spectral_resolution_bandwidth = unitful_property( "SPEC:RESB", - pq.Hz, + u.Hz, doc=""" The desired resolution bandwidth value. Units are represented in Hertz. @@ -417,7 +417,7 @@ class SpectralWindow(Enum): spectral_span = unitful_property( "SPEC:SPAN", - pq.Hz, + u.Hz, doc=""" Specifies the frequency span of the output data vector from the spectral analyzer. @@ -448,7 +448,7 @@ class SpectralWindow(Enum): threshhold = unitful_property( "THRESH", - pq.volt, + u.volt, doc=""" The math threshhold in volts """ @@ -479,7 +479,7 @@ class SpectralWindow(Enum): scale = unitful_property( "VERT:SCALE", - pq.volt, + u.volt, doc=""" The scale in volts per division. The range is from ``100e-36`` to ``100e+36``. @@ -562,17 +562,17 @@ class Coupling(Enum): bandwidth = unitful_property( 'BAN', - pq.Hz + u.Hz ) deskew = unitful_property( 'DESK', - pq.second + u.second ) termination = unitful_property( 'TERM', - pq.ohm + u.ohm ) label = string_property( @@ -598,7 +598,7 @@ class Coupling(Enum): offset = unitful_property( 'OFFS', - pq.volt, + u.volt, doc=""" The vertical offset in units of volts. Voltage is given by ``offset+scale*(5*raw/2^15 - position)``. @@ -616,7 +616,7 @@ class Coupling(Enum): scale = unitful_property( 'SCALE', - pq.volt, + u.volt, doc=""" Vertical channel scale in units volts/division. Voltage is given by ``offset+scale*(5*raw/2^15 - position)``. @@ -810,7 +810,7 @@ def data_source(self, newval): horiz_acq_duration = unitful_property( 'HOR:ACQDURATION', - pq.second, + u.second, readonly=True, doc=""" The duration of the acquisition. @@ -833,7 +833,7 @@ def data_source(self, newval): horiz_delay_pos = unitful_property( 'HOR:DEL:POS', - pq.percent, + u.percent, doc=""" The percentage of the waveform that is displayed left of the center graticule. @@ -842,7 +842,7 @@ def data_source(self, newval): horiz_delay_time = unitful_property( 'HOR:DEL:TIM', - pq.second, + u.second, doc=""" The base trigger delay time setting. """ @@ -858,7 +858,7 @@ def data_source(self, newval): horiz_main_pos = unitful_property( 'HOR:MAI:POS', - pq.percent, + u.percent, doc=""" The percentage of the waveform that is displayed left of the center graticule. @@ -890,7 +890,7 @@ def data_source(self, newval): horiz_sample_rate = unitful_property( 'HOR:MODE:SAMPLER', - pq.Hz, + u.Hz, doc=""" The sample rate in samples per second. """ @@ -898,7 +898,7 @@ def data_source(self, newval): horiz_scale = unitful_property( 'HOR:MODE:SCA', - pq.second, + u.second, doc=""" The horizontal scale in seconds per division. The horizontal scale is readonly when `horiz_mode` is manual. @@ -907,7 +907,7 @@ def data_source(self, newval): horiz_pos = unitful_property( 'HOR:POS', - pq.percent, + u.percent, doc=""" The position of the trigger point on the screen, left is 0%, right is 100%. diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index ad6b9329c..445ccfb4e 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -14,7 +14,7 @@ from enum import Enum import numpy as np -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( OscilloscopeChannel, @@ -155,7 +155,7 @@ class TekTDS224(SCPIInstrument, Oscilloscope): def __init__(self, filelike): super(TekTDS224, self).__init__(filelike) - self._file.timeout = 3 * pq.second + self._file.timeout = 3 * u.second # ENUMS # diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index 860c6dbf5..29ca511b7 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u import instruments as ik @@ -107,7 +107,7 @@ def test_func_gen_two_channel_passes_thru_call_getter(fg, mocker): def test_func_gen_one_channel_passes_thru_call_getter(fg, mocker): mock_properties = [mocker.PropertyMock(return_value=1) for _ in range(4)] - mock_method = mocker.MagicMock(return_value=(1, pq.V)) + mock_method = mocker.MagicMock(return_value=(1, u.V)) mocker.patch("instruments.abstract_instruments.FunctionGenerator.frequency", new=mock_properties[0]) mocker.patch("instruments.abstract_instruments.FunctionGenerator.function", new=mock_properties[1]) diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index e85bd905f..b9c18ede8 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, make_name_test @@ -33,9 +33,9 @@ def test_agilent33220a_amplitude(): "+1.000000E+00" ] ) as fg: - assert fg.amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) - fg.amplitude = 2 * pq.V - fg.amplitude = (1.5 * pq.V, fg.VoltageMode.dBm) + assert fg.amplitude == (1 * u.V, fg.VoltageMode.peak_to_peak) + fg.amplitude = 2 * u.V + fg.amplitude = (1.5 * u.V, fg.VoltageMode.dBm) def test_agilent33220a_frequency(): @@ -48,8 +48,8 @@ def test_agilent33220a_frequency(): "+1.234000E+03" ] ) as fg: - assert fg.frequency == 1234 * pq.Hz - fg.frequency = 100.5 * pq.Hz + assert fg.frequency == 1234 * u.Hz + fg.frequency = 100.5 * u.Hz def test_agilent33220a_function(): @@ -76,8 +76,8 @@ def test_agilent33220a_offset(): "+1.234000E+01", ] ) as fg: - assert fg.offset == 12.34 * pq.V - fg.offset = 0.4321 * pq.V + assert fg.offset == 12.34 * u.V + fg.offset = 0.4321 * u.V def test_agilent33220a_duty_cycle(): @@ -163,7 +163,7 @@ def test_agilent33220a_load_resistance(): "INF" ] ) as fg: - assert fg.load_resistance == 50 * pq.Ohm + assert fg.load_resistance == 50 * u.Ohm assert fg.load_resistance == fg.LoadResistance.high_impedance - fg.load_resistance = 100 * pq.Ohm + fg.load_resistance = 100 * u.Ohm fg.load_resistance = fg.LoadResistance.maximum diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 4ffbf910e..451a0b306 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -9,11 +9,11 @@ from __future__ import absolute_import from builtins import bytes -import quantities as pq import numpy as np import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq +import instruments.units as u # TESTS ###################################################################### @@ -31,7 +31,7 @@ def test_agilent34410a_read(): "+1.86850000E-03" ] ) as dmm: - unit_eq(dmm.read_meter(), +1.86850000E-03 * pq.volt) + unit_eq(dmm.read_meter(), +1.86850000E-03 * u.volt) def test_agilent34410a_data_point_count(): @@ -59,7 +59,7 @@ def test_agilent34410a_r(): b"#18" + bytes.fromhex("3FF0000000000000") ] ) as dmm: - unit_eq(dmm.r(1), np.array([1]) * pq.volt) + unit_eq(dmm.r(1), np.array([1]) * u.volt) def test_agilent34410a_fetch(): @@ -74,8 +74,8 @@ def test_agilent34410a_fetch(): ] ) as dmm: data = dmm.fetch() - unit_eq(data[0], 4.27150000E-03 * pq.volt) - unit_eq(data[1], 5.27150000E-03 * pq.volt) + unit_eq(data[0], 4.27150000E-03 * u.volt) + unit_eq(data[1], 5.27150000E-03 * u.volt) def test_agilent34410a_read_data(): @@ -91,8 +91,8 @@ def test_agilent34410a_read_data(): ] ) as dmm: data = dmm.read_data(2) - unit_eq(data[0], 4.27150000E-03 * pq.volt) - unit_eq(data[1], 5.27150000E-03 * pq.volt) + unit_eq(data[0], 4.27150000E-03 * u.volt) + unit_eq(data[1], 5.27150000E-03 * u.volt) def test_agilent34410a_read_data_nvmem(): @@ -107,8 +107,8 @@ def test_agilent34410a_read_data_nvmem(): ] ) as dmm: data = dmm.read_data_nvmem() - unit_eq(data[0], 4.27150000E-03 * pq.volt) - unit_eq(data[1], 5.27150000E-03 * pq.volt) + unit_eq(data[0], 4.27150000E-03 * u.volt) + unit_eq(data[1], 5.27150000E-03 * u.volt) def test_agilent34410a_read_last_data(): @@ -120,4 +120,4 @@ def test_agilent34410a_read_last_data(): "+1.73730000E-03 VDC", ] ) as dmm: - unit_eq(dmm.read_last_data(), 1.73730000E-03 * pq.volt) + unit_eq(dmm.read_last_data(), 1.73730000E-03 * u.volt) diff --git a/instruments/tests/test_comm/test_gpibusb.py b/instruments/tests/test_comm/test_gpibusb.py index e68c360d3..310c634d0 100644 --- a/instruments/tests/test_comm/test_gpibusb.py +++ b/instruments/tests/test_comm/test_gpibusb.py @@ -10,7 +10,7 @@ import pytest import serial -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import GPIBCommunicator, SerialCommunicator from instruments.tests import unit_eq @@ -38,7 +38,7 @@ def test_gpibusbcomm_init_correct_values_new_firmware(): assert comm._version == 5 assert comm._eos == "\n" assert comm._eoi is True - unit_eq(comm._timeout, 1000 * pq.millisecond) + unit_eq(comm._timeout, 1000 * u.millisecond) def test_gpibusbcomm_init_correct_values_old_firmware(): @@ -196,9 +196,9 @@ def test_gpibusbcomm_timeout(): comm = GPIBCommunicator(mock.MagicMock(), 1) comm._version = 5 - unit_eq(comm.timeout, 1000 * pq.millisecond) + unit_eq(comm.timeout, 1000 * u.millisecond) - comm.timeout = 5000 * pq.millisecond + comm.timeout = 5000 * u.millisecond comm._file.sendcmd.assert_called_with("++read_tmo_ms 5000") diff --git a/instruments/tests/test_comm/test_serial.py b/instruments/tests/test_comm/test_serial.py index 870238f47..003214b98 100644 --- a/instruments/tests/test_comm/test_serial.py +++ b/instruments/tests/test_comm/test_serial.py @@ -10,7 +10,7 @@ import pytest import serial -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import SerialCommunicator from instruments.tests import unit_eq @@ -65,13 +65,13 @@ def test_serialcomm_timeout(): timeout = mock.PropertyMock(return_value=30) type(comm._conn).timeout = timeout - unit_eq(comm.timeout, 30 * pq.second) + unit_eq(comm.timeout, 30 * u.second) timeout.assert_called_with() comm.timeout = 10 timeout.assert_called_with(10) - comm.timeout = 1000 * pq.millisecond + comm.timeout = 1000 * u.millisecond timeout.assert_called_with(1) diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index aee17e5c5..b7b9867db 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -11,7 +11,7 @@ import socket import pytest -import quantities as pq +import instruments.units as u from instruments.abstract_instruments.comm import SocketCommunicator from instruments.tests import unit_eq @@ -73,13 +73,13 @@ def test_socketcomm_timeout(): comm._conn = mock.MagicMock() comm._conn.gettimeout.return_value = 1.234 - unit_eq(comm.timeout, 1.234 * pq.second) + unit_eq(comm.timeout, 1.234 * u.second) comm._conn.gettimeout.assert_called_with() comm.timeout = 10 comm._conn.settimeout.assert_called_with(10) - comm.timeout = 1000 * pq.millisecond + comm.timeout = 1000 * u.millisecond comm._conn.settimeout.assert_called_with(1) diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index 988b928d3..8193487a9 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -10,11 +10,11 @@ import pytest -import quantities as pq from numpy import array from instruments.abstract_instruments.comm import USBTMCCommunicator from instruments.tests import unit_eq +import instruments.units as u from .. import mock # TEST CASES ################################################################# @@ -70,13 +70,13 @@ def test_usbtmccomm_timeout(mock_usbtmc): timeout = mock.PropertyMock(return_value=1) type(comm._filelike).timeout = timeout - unit_eq(comm.timeout, 1 * pq.second) + unit_eq(comm.timeout, 1 * u.second) timeout.assert_called_with() comm.timeout = 10 timeout.assert_called_with(array(10.0)) - comm.timeout = 1000 * pq.millisecond + comm.timeout = 1000 * u.millisecond timeout.assert_called_with(array(1.0)) diff --git a/instruments/tests/test_config.py b/instruments/tests/test_config.py index 6a2119fa5..62711e856 100644 --- a/instruments/tests/test_config.py +++ b/instruments/tests/test_config.py @@ -10,7 +10,7 @@ from io import StringIO -import quantities as pq +import instruments.units as u from instruments import Instrument from instruments.config import ( @@ -48,8 +48,8 @@ def test_yaml_quantity_tag(): d: !Q 98 """) data = yaml.load(yaml_data, Loader=yaml.Loader) - assert data['a']['b'] == pq.Quantity(37, 'tesla') - assert data['a']['c'] == pq.Quantity(41.2, 'inches') + assert data['a']['b'] == u.Quantity(37, 'tesla') + assert data['a']['c'] == u.Quantity(41.2, 'inches') assert data['a']['d'] == 98 def test_load_test_instrument_setattr(): @@ -61,4 +61,4 @@ def test_load_test_instrument_setattr(): foo: !Q 111 GHz """) insts = load_instruments(config_data) - assert insts['test'].foo == pq.Quantity(111, 'GHz') + assert insts['test'].foo == u.Quantity(111, 'GHz') diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index b01444c37..696b61480 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -160,5 +160,5 @@ def test_measure(): ], "\r" ) as inst: - assert inst.measure(inst.Mode.voltage_dc) == 0.509 * pq.volt - assert inst.measure(inst.Mode.temperature) == -25.3 * pq.celsius + assert inst.measure(inst.Mode.voltage_dc) == 0.509 * u.volt + assert inst.measure(inst.Mode.temperature) == -25.3 * u.celsius diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index e2073f80d..4ae2a4926 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, make_name_test @@ -34,13 +34,13 @@ def test_scpi_func_gen_amplitude(): ], repeat=2 ) as fg: - assert fg.amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) - fg.amplitude = 2 * pq.V - fg.amplitude = (1.5 * pq.V, fg.VoltageMode.dBm) + assert fg.amplitude == (1 * u.V, fg.VoltageMode.peak_to_peak) + fg.amplitude = 2 * u.V + fg.amplitude = (1.5 * u.V, fg.VoltageMode.dBm) - assert fg.channel[0].amplitude == (1 * pq.V, fg.VoltageMode.peak_to_peak) - fg.channel[0].amplitude = 2 * pq.V - fg.channel[0].amplitude = (1.5 * pq.V, fg.VoltageMode.dBm) + assert fg.channel[0].amplitude == (1 * u.V, fg.VoltageMode.peak_to_peak) + fg.channel[0].amplitude = 2 * u.V + fg.channel[0].amplitude = (1.5 * u.V, fg.VoltageMode.dBm) def test_scpi_func_gen_frequency(): @@ -54,11 +54,11 @@ def test_scpi_func_gen_frequency(): ], repeat=2 ) as fg: - assert fg.frequency == 1234 * pq.Hz - fg.frequency = 100.5 * pq.Hz + assert fg.frequency == 1234 * u.Hz + fg.frequency = 100.5 * u.Hz - assert fg.channel[0].frequency == 1234 * pq.Hz - fg.channel[0].frequency = 100.5 * pq.Hz + assert fg.channel[0].frequency == 1234 * u.Hz + fg.channel[0].frequency = 100.5 * u.Hz def test_scpi_func_gen_function(): @@ -90,8 +90,8 @@ def test_scpi_func_gen_offset(): ], repeat=2 ) as fg: - assert fg.offset == 12.34 * pq.V - fg.offset = 0.4321 * pq.V + assert fg.offset == 12.34 * u.V + fg.offset = 0.4321 * u.V - assert fg.channel[0].offset == 12.34 * pq.V - fg.channel[0].offset = 0.4321 * pq.V + assert fg.channel[0].offset == 12.34 * u.V + fg.channel[0].offset = 0.4321 * u.V diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index 622863a00..4b2d460bd 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq @@ -63,10 +63,10 @@ def test_scpi_multimeter_input_range(): "CURR:DC +1.000000E+01,+3.000000E-06" # 4 ] ) as dmm: - unit_eq(dmm.input_range, 1e1 * pq.amp) + unit_eq(dmm.input_range, 1e1 * u.amp) assert dmm.input_range == dmm.InputRange.automatic dmm.input_range = dmm.InputRange.minimum - dmm.input_range = 1 * pq.amp + dmm.input_range = 1 * u.amp def test_scpi_multimeter_resolution(): @@ -140,8 +140,8 @@ def test_scpi_multimeter_trigger_delay(): "+1", ] ) as dmm: - unit_eq(dmm.trigger_delay, 1 * pq.second) - dmm.trigger_delay = 1000 * pq.millisecond + unit_eq(dmm.trigger_delay, 1 * u.second) + dmm.trigger_delay = 1000 * u.millisecond def test_scpi_multimeter_sample_source(): @@ -168,8 +168,8 @@ def test_scpi_multimeter_sample_timer(): "+1", ] ) as dmm: - unit_eq(dmm.sample_timer, 1 * pq.second) - dmm.sample_timer = 1000 * pq.millisecond + unit_eq(dmm.sample_timer, 1 * u.second) + dmm.sample_timer = 1000 * u.millisecond def test_scpi_multimeter_measure(): @@ -181,4 +181,4 @@ def test_scpi_multimeter_measure(): "+4.23450000E-03", ] ) as dmm: - unit_eq(dmm.measure(dmm.Mode.voltage_dc), 4.2345e-03 * pq.volt) + unit_eq(dmm.measure(dmm.Mode.voltage_dc), 4.2345e-03 * u.volt) diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index e048571ad..b789136a7 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from builtins import round -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -22,8 +22,8 @@ def set_defaults(inst): Sets default values for the voltage and current range of the Glassman FR to be used to test the voltage and current property getters/setters. """ - inst.voltage_max = 50.0 * pq.kilovolt - inst.current_max = 6.0 * pq.milliamp + inst.voltage_max = 50.0 * u.kilovolt + inst.current_max = 6.0 * u.milliamp inst.polarity = +1 @@ -52,8 +52,8 @@ def test_voltage(): "\r" ) as inst: set_defaults(inst) - inst.voltage = 10.0 * pq.kilovolt - assert inst.voltage == 10.0 * pq.kilovolt + inst.voltage = 10.0 * u.kilovolt + assert inst.voltage == 10.0 * u.kilovolt def test_current(): @@ -70,8 +70,8 @@ def test_current(): "\r" ) as inst: set_defaults(inst) - inst.current = 1.2 * pq.milliamp - assert inst.current == 1.2 * pq.milliamp + inst.current = 1.2 * u.milliamp + assert inst.current == 1.2 * u.milliamp def test_voltage_sense(): @@ -86,7 +86,7 @@ def test_voltage_sense(): "\r" ) as inst: set_defaults(inst) - assert round(inst.voltage_sense) == 13.0 * pq.kilovolt + assert round(inst.voltage_sense) == 13.0 * u.kilovolt def test_current_sense(): @@ -101,7 +101,7 @@ def test_current_sense(): "\r" ) as inst: set_defaults(inst) - assert inst.current_sense == 2.0 * pq.milliamp + assert inst.current_sense == 2.0 * u.milliamp def test_mode(): @@ -231,7 +231,7 @@ def test_set_status(): "\r" ) as inst: set_defaults(inst) - inst.set_status(voltage=10*pq.kilovolt, current=1.2*pq.milliamp, output=True) + inst.set_status(voltage=10*u.kilovolt, current=1.2*u.milliamp, output=True) assert inst.output - assert inst.voltage == 10 * pq.kilovolt - assert inst.current == 1.2 * pq.milliamp + assert inst.voltage == 10 * u.kilovolt + assert inst.current == 1.2 * u.milliamp diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index 71d0e5217..d84218576 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -119,7 +119,7 @@ def test_channel_temperature(): sep="\n" ) as hs: channel = hs.channel[0] - assert channel.temperature == 10 * pq.degC + assert channel.temperature == 10 * u.degC def test_channel_frequency_getter(): @@ -140,9 +140,9 @@ def test_channel_frequency_getter(): sep="\n" ) as hs: channel = hs.channel[0] - assert channel.frequency == 1 * pq.GHz - assert channel.frequency_min == 100 * pq.MHz - assert channel.frequency_max == 10 * pq.GHz + assert channel.frequency == 1 * u.GHz + assert channel.frequency_min == 100 * u.MHz + assert channel.frequency_max == 10 * u.GHz def test_channel_frequency_setter(): @@ -162,7 +162,7 @@ def test_channel_frequency_setter(): sep="\n" ) as hs: channel = hs.channel[0] - channel.frequency = 1 * pq.GHz + channel.frequency = 1 * u.GHz def test_channel_power_getter(): @@ -226,9 +226,9 @@ def test_channel_phase_getter(): sep="\n" ) as hs: channel = hs.channel[0] - assert channel.phase == 0 * pq.degree - assert channel.phase_min == -180 * pq.degree - assert channel.phase_max == 180 * pq.degree + assert channel.phase == 0 * u.degree + assert channel.phase_min == -180 * u.degree + assert channel.phase_max == 180 * u.degree def test_channel_phase_setter(): @@ -248,7 +248,7 @@ def test_channel_phase_setter(): sep="\n" ) as hs: channel = hs.channel[0] - channel.phase = 0 * pq.degree + channel.phase = 0 * u.degree def test_channel_output(): diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index 94b2ccce5..5d28464d8 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -8,12 +8,12 @@ from __future__ import absolute_import -import quantities as pq import numpy as np import pytest import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u # TESTS ####################################################################### @@ -182,12 +182,12 @@ def test_hp3456a_fetch(): ) as dmm: v = dmm.fetch(dmm.Mode.resistance_2wire) np.testing.assert_array_equal( - v, [0.1055, 0.1043, 0.1005, 0.1014] * pq.ohm + v, [0.1055, 0.1043, 0.1005, 0.1014] * u.ohm ) - assert v[0].units == pq.ohm + assert v[0].units == u.ohm v = dmm.fetch() np.testing.assert_array_equal( - v, [0.1055, 0.1043, 0.1005, 0.1014] * pq.ohm + v, [0.1055, 0.1043, 0.1005, 0.1014] * u.ohm ) assert isinstance(v[0], float) @@ -247,7 +247,7 @@ def test_hp3456a_delay(): sep="\r" ) as dmm: assert dmm.delay == 0 - dmm.delay = 1 * pq.sec + dmm.delay = 1 * u.sec def test_hp3456a_lower(): @@ -327,8 +327,8 @@ def test_hp3456a_measure(): sep="\r" ) as dmm: assert dmm.measure(dmm.Mode.ratio_dcv_dcv) == 0 - assert dmm.measure(dmm.Mode.resistance_2wire) == +000.1010E+0 * pq.ohm - assert dmm.measure(dmm.Mode.dcv) == +000.0002E-3 * pq.volt + assert dmm.measure(dmm.Mode.resistance_2wire) == +000.1010E+0 * u.ohm + assert dmm.measure(dmm.Mode.dcv) == +000.0002E-3 * u.volt assert dmm.measure() == +000.0002E-3 @@ -344,8 +344,8 @@ def test_hp3456a_input_range(): ], sep="\r" ) as dmm: - dmm.input_range = 10 ** -1 * pq.volt - dmm.input_range = 1e3 * pq.ohm + dmm.input_range = 10 ** -1 * u.volt + dmm.input_range = 1e3 * u.ohm def test_hp3456a_input_range_invalid_str(): @@ -365,7 +365,7 @@ def test_hp3456a_input_range_invalid_range(): [], sep="\r" ) as dmm: - dmm.input_range = 1 * pq.ohm + dmm.input_range = 1 * u.ohm def test_hp3456a_input_range_bad_type(): @@ -385,7 +385,7 @@ def test_hp3456a_input_range_bad_units(): [], sep="\r" ) as dmm: - dmm.input_range = 1 * pq.amp + dmm.input_range = 1 * u.amp def test_hp3456a_relative(): diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 08ff94f99..8fb955a47 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -8,11 +8,11 @@ from __future__ import absolute_import -import quantities as pq import pytest import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u from .. import mock # TESTS ####################################################################### @@ -70,8 +70,8 @@ def test_channel_voltage(): ], sep="\n" ) as hp: - assert hp.channel[0].voltage == 2 * pq.V - hp.channel[0].voltage = 5 * pq.V + assert hp.channel[0].voltage == 2 * u.V + hp.channel[0].voltage = 5 * u.V def test_channel_current(): @@ -86,8 +86,8 @@ def test_channel_current(): ], sep="\n" ) as hp: - assert hp.channel[0].current == 2 * pq.amp - hp.channel[0].current = 5 * pq.amp + assert hp.channel[0].current == 2 * u.amp + hp.channel[0].current = 5 * u.amp def test_channel_voltage_sense(): @@ -101,7 +101,7 @@ def test_channel_voltage_sense(): ], sep="\n" ) as hp: - assert hp.channel[0].voltage_sense == 2 * pq.V + assert hp.channel[0].voltage_sense == 2 * u.V def test_channel_current_sense(): @@ -115,7 +115,7 @@ def test_channel_current_sense(): ], sep="\n" ) as hp: - assert hp.channel[0].current_sense == 2 * pq.A + assert hp.channel[0].current_sense == 2 * u.A def test_channel_overvoltage(): @@ -130,8 +130,8 @@ def test_channel_overvoltage(): ], sep="\n" ) as hp: - assert hp.channel[0].overvoltage == 2 * pq.V - hp.channel[0].overvoltage = 5 * pq.V + assert hp.channel[0].overvoltage == 2 * u.V + hp.channel[0].overvoltage = 5 * u.V def test_channel_overcurrent(): @@ -201,9 +201,9 @@ def test_all_voltage(): ], sep="\n" ) as hp: - assert sorted(hp.voltage) == sorted((2, 3, 4, 5) * pq.V) - hp.voltage = 5 * pq.V - hp.voltage = (1 * pq.V, 2 * pq.V, 3 * pq.V, 4 * pq.V) + assert sorted(hp.voltage) == sorted((2, 3, 4, 5) * u.V) + hp.voltage = 5 * u.V + hp.voltage = (1 * u.V, 2 * u.V, 3 * u.V, 4 * u.V) def test_all_voltage_wrong_length(): @@ -213,7 +213,7 @@ def test_all_voltage_wrong_length(): [], sep="\n" ) as hp: - hp.voltage = (1 * pq.volt, 2 * pq.volt) + hp.voltage = (1 * u.volt, 2 * u.volt) def test_all_current(): @@ -243,9 +243,9 @@ def test_all_current(): ], sep="\n" ) as hp: - assert sorted(hp.current) == sorted((2, 3, 4, 5) * pq.A) - hp.current = 5 * pq.A - hp.current = (1 * pq.A, 2 * pq.A, 3 * pq.A, 4 * pq.A) + assert sorted(hp.current) == sorted((2, 3, 4, 5) * u.A) + hp.current = 5 * u.A + hp.current = (1 * u.A, 2 * u.A, 3 * u.A, 4 * u.A) def test_all_current_wrong_length(): @@ -255,7 +255,7 @@ def test_all_current_wrong_length(): [], sep="\n" ) as hp: - hp.current = (1 * pq.amp, 2 * pq.amp) + hp.current = (1 * u.amp, 2 * u.amp) def test_all_voltage_sense(): @@ -275,7 +275,7 @@ def test_all_voltage_sense(): ], sep="\n" ) as hp: - assert sorted(hp.voltage_sense) == sorted((2, 3, 4, 5) * pq.V) + assert sorted(hp.voltage_sense) == sorted((2, 3, 4, 5) * u.V) def test_all_current_sense(): @@ -295,7 +295,7 @@ def test_all_current_sense(): ], sep="\n" ) as hp: - assert sorted(hp.current_sense) == sorted((2, 3, 4, 5) * pq.A) + assert sorted(hp.current_sense) == sorted((2, 3, 4, 5) * u.A) def test_clear(): diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index a181019fa..427384290 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq @@ -69,8 +69,8 @@ def test_hp6632b_voltage(): "10.0" ] ) as psu: - unit_eq(psu.voltage, 10 * pq.volt) - psu.voltage = 1.0 * pq.volt + unit_eq(psu.voltage, 10 * u.volt) + psu.voltage = 1.0 * u.volt def test_hp6632b_voltage_sense(): @@ -82,7 +82,7 @@ def test_hp6632b_voltage_sense(): "10.0" ] ) as psu: - unit_eq(psu.voltage_sense, 10 * pq.volt) + unit_eq(psu.voltage_sense, 10 * u.volt) def test_hp6632b_overvoltage(): @@ -95,8 +95,8 @@ def test_hp6632b_overvoltage(): "10.0" ] ) as psu: - unit_eq(psu.overvoltage, 10 * pq.volt) - psu.overvoltage = 1.0 * pq.volt + unit_eq(psu.overvoltage, 10 * u.volt) + psu.overvoltage = 1.0 * u.volt def test_hp6632b_current(): @@ -109,8 +109,8 @@ def test_hp6632b_current(): "10.0" ] ) as psu: - unit_eq(psu.current, 10 * pq.amp) - psu.current = 1.0 * pq.amp + unit_eq(psu.current, 10 * u.amp) + psu.current = 1.0 * u.amp def test_hp6632b_current_sense(): @@ -122,7 +122,7 @@ def test_hp6632b_current_sense(): "10.0" ] ) as psu: - unit_eq(psu.current_sense, 10 * pq.amp) + unit_eq(psu.current_sense, 10 * u.amp) def test_hp6632b_overcurrent(): @@ -149,8 +149,8 @@ def test_hp6632b_current_sense_range(): "0.05" ] ) as psu: - unit_eq(psu.current_sense_range, 0.05 * pq.amp) - psu.current_sense_range = 1 * pq.amp + unit_eq(psu.current_sense_range, 0.05 * u.amp) + psu.current_sense_range = 1 * u.amp def test_hp6632b_output_dfi_source(): @@ -233,8 +233,8 @@ def test_hp6632b_sense_sweep_interval(): "1.56e-05" ] ) as psu: - unit_eq(psu.sense_sweep_interval, 1.56e-05 * pq.second) - psu.sense_sweep_interval = 1e-05 * pq.second + unit_eq(psu.sense_sweep_interval, 1.56e-05 * u.second) + psu.sense_sweep_interval = 1e-05 * u.second def test_hp6632b_sense_window(): @@ -261,8 +261,8 @@ def test_hp6632b_output_protection_delay(): "8e-02" ] ) as psu: - unit_eq(psu.output_protection_delay, 8e-02 * pq.second) - psu.output_protection_delay = 5e-02 * pq.second + unit_eq(psu.output_protection_delay, 8e-02 * u.second) + psu.output_protection_delay = 5e-02 * u.second def test_hp6632b_voltage_alc_bandwidth(): @@ -287,8 +287,8 @@ def test_hp6632b_voltage_trigger(): "1e+0" ] ) as psu: - unit_eq(psu.voltage_trigger, 1 * pq.volt) - psu.voltage_trigger = 1 * pq.volt + unit_eq(psu.voltage_trigger, 1 * u.volt) + psu.voltage_trigger = 1 * u.volt def test_hp6632b_current_trigger(): @@ -301,8 +301,8 @@ def test_hp6632b_current_trigger(): "1e-01" ] ) as psu: - unit_eq(psu.current_trigger, 0.1 * pq.amp) - psu.current_trigger = 0.1 * pq.amp + unit_eq(psu.current_trigger, 0.1 * u.amp) + psu.current_trigger = 0.1 * u.amp def test_hp6632b_init_output_trigger(): diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index 2a7d8eaad..eb6da9257 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -78,16 +78,16 @@ def test_voltage(): "6.0" # 6 ] ) as inst: - assert inst.voltage_min == 0.0 * pq.volt - assert inst.voltage_max == 6.0 * pq.volt - inst.voltage = 3.0 * pq.volt - assert inst.voltage == 3.0 * pq.volt + assert inst.voltage_min == 0.0 * u.volt + assert inst.voltage_max == 6.0 * u.volt + inst.voltage = 3.0 * u.volt + assert inst.voltage == 3.0 * u.volt try: - inst.voltage = -1.0 * pq.volt + inst.voltage = -1.0 * u.volt except ValueError: pass try: - inst.voltage = 7.0 * pq.volt + inst.voltage = 7.0 * u.volt except ValueError: pass @@ -118,16 +118,16 @@ def test_current(): "5.0" # 6.2 ] ) as inst: - assert inst.current_min == 0.0 * pq.amp - assert inst.current_max == 5.0 * pq.amp - inst.current = 2.0 * pq.amp - assert inst.current == 2.0 * pq.amp + assert inst.current_min == 0.0 * u.amp + assert inst.current_max == 5.0 * u.amp + inst.current = 2.0 * u.amp + assert inst.current == 2.0 * u.amp try: - inst.current = -1.0 * pq.amp + inst.current = -1.0 * u.amp except ValueError: pass try: - inst.current = 6.0 * pq.amp + inst.current = 6.0 * u.amp except ValueError: pass @@ -143,7 +143,7 @@ def test_voltage_sense(): "1.234" # 1 ] ) as inst: - assert inst.voltage_sense == 1.234 * pq.volt + assert inst.voltage_sense == 1.234 * u.volt def test_current_sense(): @@ -157,4 +157,4 @@ def test_current_sense(): "1.234" # 1 ] ) as inst: - assert inst.current_sense == 1.234 * pq.amp + assert inst.current_sense == 1.234 * u.amp diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 73b553dee..41abc2cdc 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -8,12 +8,12 @@ from __future__ import absolute_import -import quantities as pq import numpy as np import pytest import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u # TESTS ####################################################################### @@ -51,7 +51,7 @@ def test_channel_measure_voltage(): ] ) as inst: channel = inst.channel[0] - assert channel.measure() == 1.234 * pq.volt + assert channel.measure() == 1.234 * u.volt def test_channel_measure_temperature(): @@ -70,7 +70,7 @@ def test_channel_measure_temperature(): ] ) as inst: channel = inst.channel[0] - assert channel.measure() == 1.234 * pq.celsius + assert channel.measure() == 1.234 * u.celsius def test_channel_measure_unknown_temperature_units(): @@ -128,7 +128,7 @@ def test_units(): units = str(inst.units.units).split()[1] assert units == "K" - assert inst.units == pq.volt + assert inst.units == u.volt def test_fetch(): @@ -144,7 +144,7 @@ def test_fetch(): ] ) as inst: np.testing.assert_array_equal( - inst.fetch(), [1.234, 1, 5.678] * pq.volt + inst.fetch(), [1.234, 1, 5.678] * u.volt ) @@ -162,7 +162,7 @@ def test_measure(): "VOLT" ] ) as inst: - assert inst.measure() == 1.234 * pq.volt + assert inst.measure() == 1.234 * u.volt def test_measure_invalid_mode(): diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index 753b7d2c8..d75f88229 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -61,7 +61,7 @@ def test_input_range(): ) as inst: inst.input_range = "auto" inst.input_range = 2e-3 - assert inst.input_range == 2. * pq.milliamp + assert inst.input_range == 2. * u.milliamp def test_relative(): @@ -149,5 +149,5 @@ def test_measure(): "NDCL-9.0000E+0" ] ) as inst: - assert inst.measure() == 1.2345 * pq.nanoamp - assert inst.measure() == 1. * pq.nanoamp + assert inst.measure() == 1.2345 * u.nanoamp + assert inst.measure() == 1. * u.nanoamp diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index 4fc91105b..dc688c929 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -32,10 +32,10 @@ def test_current(): "0.1", ] ) as inst: - assert inst.current == 100 * pq.milliamp - assert inst.current_min == -105 * pq.milliamp - assert inst.current_max == +105 * pq.milliamp - inst.current = 50 * pq.milliamp + assert inst.current == 100 * u.milliamp + assert inst.current_min == -105 * u.milliamp + assert inst.current_max == +105 * u.milliamp + inst.current = 50 * u.milliamp def test_disable(): diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index 845464b04..b89fa7c16 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -8,11 +8,11 @@ from __future__ import absolute_import -import quantities as pq import pytest import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u # TESTS ####################################################################### @@ -44,7 +44,7 @@ def test_parse_measurement(): ] ) as inst: reading, timestamp, status = inst._parse_measurement("1.0,1234,5678") - assert reading == 1.0 * pq.volt + assert reading == 1.0 * u.volt assert timestamp == 1234 assert status == 5678 @@ -134,7 +134,7 @@ def test_unit(): '"VOLT:DC"' ] ) as inst: - assert inst.unit == pq.volt + assert inst.unit == u.volt def test_auto_range(): @@ -171,8 +171,8 @@ def test_input_range(): '"VOLT:DC"' ] ) as inst: - assert inst.input_range == 10 * pq.volt - inst.input_range = 20 * pq.volt + assert inst.input_range == 10 * u.volt + inst.input_range = 20 * u.volt def test_input_range_invalid(): @@ -185,7 +185,7 @@ def test_input_range_invalid(): '"VOLT:DC"' ] ) as inst: - inst.input_range = 10 * pq.volt + inst.input_range = 10 * u.volt def test_auto_config(): @@ -212,7 +212,7 @@ def test_fetch(): ] ) as inst: reading, timestamp = inst.fetch() - assert reading == 1.0 * pq.volt + assert reading == 1.0 * u.volt assert timestamp == 1234 @@ -229,5 +229,5 @@ def test_read(): ] ) as inst: reading, timestamp = inst.read_measurements() - assert reading == 1.0 * pq.volt + assert reading == 1.0 * u.volt assert timestamp == 1234 diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index ebb176a04..53fba0ed6 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -35,10 +35,10 @@ def test_mhs_amplitude(): ], sep="\r\n" ) as mhs: - assert mhs.channel[0].amplitude[0] == 3.3*pq.V - assert mhs.channel[1].amplitude[0] == 5.0*pq.V - mhs.channel[0].amplitude = 6.6*pq.V - mhs.channel[1].amplitude = 8.0*pq.V + assert mhs.channel[0].amplitude[0] == 3.3*u.V + assert mhs.channel[1].amplitude[0] == 5.0*u.V + mhs.channel[0].amplitude = 6.6*u.V + mhs.channel[1].amplitude = 8.0*u.V def test_mhs_amplitude_dbm_notimplemented(): @@ -117,10 +117,10 @@ def test_mhs_frequency(): ], sep="\r\n" ) as mhs: - assert mhs.channel[0].frequency == 33.0*pq.kHz - assert mhs.channel[1].frequency == 500.0*pq.kHz - mhs.channel[0].frequency = 6*pq.kHz - mhs.channel[1].frequency = 8*pq.kHz + assert mhs.channel[0].frequency == 33.0*u.kHz + assert mhs.channel[1].frequency == 500.0*u.kHz + mhs.channel[0].frequency = 6*u.kHz + mhs.channel[1].frequency = 8*u.kHz def test_mhs_offset(): diff --git a/instruments/tests/test_oxford/test_oxforditc503.py b/instruments/tests/test_oxford/test_oxforditc503.py index 54f1dff45..9fe3a4a8c 100644 --- a/instruments/tests/test_oxford/test_oxforditc503.py +++ b/instruments/tests/test_oxford/test_oxforditc503.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -42,4 +42,4 @@ def test_sensor_temperature(): sep="\r" ) as inst: sensor = inst.sensor[0] - assert sensor.temperature == 123 * pq.kelvin + assert sensor.temperature == 123 * u.kelvin diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index 908feb937..0f3bcc8ed 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -33,14 +33,14 @@ def test_frequency(): ik.phasematrix.PhaseMatrixFSW0020, [ "04.", - "0C{:012X}.".format(int((10 * pq.GHz).rescale(mHz).magnitude)) + "0C{:012X}.".format(int((10 * u.GHz).rescale(mHz).magnitude)) ], [ "00E8D4A51000" ] ) as inst: - assert inst.frequency == 1 * pq.GHz - inst.frequency = 10 * pq.GHz + assert inst.frequency == 1 * u.GHz + inst.frequency = 10 * u.GHz def test_power(): diff --git a/instruments/tests/test_picowatt/test_picowatt_avs47.py b/instruments/tests/test_picowatt/test_picowatt_avs47.py index 9b316f7b6..e7bfb9f86 100644 --- a/instruments/tests/test_picowatt/test_picowatt_avs47.py +++ b/instruments/tests/test_picowatt/test_picowatt_avs47.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -46,7 +46,7 @@ def test_sensor_resistance_same_channel(): "123" ] ) as inst: - assert inst.sensor[0].resistance == 123 * pq.ohm + assert inst.sensor[0].resistance == 123 * u.ohm def test_sensor_resistance_different_channel(): @@ -66,7 +66,7 @@ def test_sensor_resistance_different_channel(): "123" ] ) as inst: - assert inst.sensor[0].resistance == 123 * pq.ohm + assert inst.sensor[0].resistance == 123 * u.ohm def test_remote(): diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index 8826f0c8e..097a179ec 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u from instruments.util_fns import bounded_unitful_property from . import MockInstrument @@ -24,17 +24,17 @@ def test_bounded_unitful_property_basics(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz + units=u.hertz ) mock_inst = BoundedUnitfulMock( {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) - assert mock_inst.property == 1000 * pq.hertz - assert mock_inst.property_min == 10 * pq.hertz - assert mock_inst.property_max == 9999 * pq.hertz + assert mock_inst.property == 1000 * u.hertz + assert mock_inst.property_min == 10 * u.hertz + assert mock_inst.property_max == 9999 * u.hertz - mock_inst.property = 1000 * pq.hertz + mock_inst.property = 1000 * u.hertz def test_bounded_unitful_property_set_outside_max(): @@ -42,13 +42,13 @@ def test_bounded_unitful_property_set_outside_max(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz + units=u.hertz ) mock_inst = BoundedUnitfulMock( {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) - mock_inst.property = 10000 * pq.hertz # Should raise ValueError + mock_inst.property = 10000 * u.hertz # Should raise ValueError def test_bounded_unitful_property_set_outside_min(): @@ -56,26 +56,26 @@ def test_bounded_unitful_property_set_outside_min(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz + units=u.hertz ) mock_inst = BoundedUnitfulMock( {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) - mock_inst.property = 1 * pq.hertz # Should raise ValueError + mock_inst.property = 1 * u.hertz # Should raise ValueError def test_bounded_unitful_property_min_fmt_str(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz, + units=u.hertz, min_fmt_str="{} MIN?" ) mock_inst = BoundedUnitfulMock({'MOCK MIN?': '10'}) - assert mock_inst.property_min == 10 * pq.Hz + assert mock_inst.property_min == 10 * u.Hz assert mock_inst.value == 'MOCK MIN?\n' @@ -83,13 +83,13 @@ def test_bounded_unitful_property_max_fmt_str(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz, + units=u.hertz, max_fmt_str="{} MAX?" ) mock_inst = BoundedUnitfulMock({'MOCK MAX?': '9999'}) - assert mock_inst.property_max == 9999 * pq.Hz + assert mock_inst.property_max == 9999 * u.Hz assert mock_inst.value == 'MOCK MAX?\n' @@ -97,40 +97,40 @@ def test_bounded_unitful_property_static_range(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz, + units=u.hertz, valid_range=(10, 9999) ) mock_inst = BoundedUnitfulMock() - assert mock_inst.property_min == 10 * pq.Hz - assert mock_inst.property_max == 9999 * pq.Hz + assert mock_inst.property_min == 10 * u.Hz + assert mock_inst.property_max == 9999 * u.Hz def test_bounded_unitful_property_static_range_with_units(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz, - valid_range=(10 * pq.kilohertz, 9999 * pq.kilohertz) + units=u.hertz, + valid_range=(10 * u.kilohertz, 9999 * u.kilohertz) ) mock_inst = BoundedUnitfulMock() - assert mock_inst.property_min == 10 * 1000 * pq.Hz - assert mock_inst.property_max == 9999 * 1000 * pq.Hz + assert mock_inst.property_min == 10 * 1000 * u.Hz + assert mock_inst.property_max == 9999 * 1000 * u.Hz @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): bounded_unitful_property( command='MOCK', - units=pq.Hz, + units=u.Hz, derp="foobar" ) mock_unitful_property.assert_called_with( 'MOCK', - pq.Hz, + u.Hz, derp="foobar", valid_range=(mock.ANY, mock.ANY) ) @@ -140,12 +140,12 @@ def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): def test_bounded_unitful_property_valid_range_none(mock_unitful_property): bounded_unitful_property( command='MOCK', - units=pq.Hz, + units=u.Hz, valid_range=(None, None) ) mock_unitful_property.assert_called_with( 'MOCK', - pq.Hz, + u.Hz, valid_range=(None, None) ) @@ -154,7 +154,7 @@ def test_bounded_unitful_property_returns_none(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( 'MOCK', - units=pq.hertz, + units=u.hertz, valid_range=(None, None) ) diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index ebdc97bf6..9f35711a7 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u from instruments.util_fns import unitful_property from . import MockInstrument @@ -21,40 +21,40 @@ def test_unitful_property_basics(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', units=pq.hertz) + unitful_property = unitful_property('MOCK', units=u.hertz) mock_inst = UnitfulMock({'MOCK?': '1000'}) - assert mock_inst.unitful_property == 1000 * pq.hertz + assert mock_inst.unitful_property == 1000 * u.hertz - mock_inst.unitful_property = 1000 * pq.hertz + mock_inst.unitful_property = 1000 * u.hertz assert mock_inst.value == 'MOCK?\nMOCK {:e}\n'.format(1000) def test_unitful_property_format_code(): class UnitfulMock(MockInstrument): unitful_property = unitful_property( - 'MOCK', pq.hertz, format_code='{:f}') + 'MOCK', u.hertz, format_code='{:f}') mock_inst = UnitfulMock() - mock_inst.unitful_property = 1000 * pq.hertz + mock_inst.unitful_property = 1000 * u.hertz assert mock_inst.value == 'MOCK {:f}\n'.format(1000) def test_unitful_property_rescale_units(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz) + unitful_property = unitful_property('MOCK', u.hertz) mock_inst = UnitfulMock() - mock_inst.unitful_property = 1 * pq.kilohertz + mock_inst.unitful_property = 1 * u.kilohertz assert mock_inst.value == 'MOCK {:e}\n'.format(1000) def test_unitful_property_no_units_on_set(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz) + unitful_property = unitful_property('MOCK', u.hertz) mock_inst = UnitfulMock() @@ -65,17 +65,17 @@ class UnitfulMock(MockInstrument): def test_unitful_property_wrong_units(): with pytest.raises(ValueError): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz) + unitful_property = unitful_property('MOCK', u.hertz) mock_inst = UnitfulMock() - mock_inst.unitful_property = 1 * pq.volt + mock_inst.unitful_property = 1 * u.volt def test_unitful_property_writeonly_reading_fails(): with pytest.raises(AttributeError): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz, writeonly=True) + unitful_property = unitful_property('MOCK', u.hertz, writeonly=True) mock_inst = UnitfulMock() @@ -84,37 +84,37 @@ class UnitfulMock(MockInstrument): def test_unitful_property_writeonly_writing_passes(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz, writeonly=True) + unitful_property = unitful_property('MOCK', u.hertz, writeonly=True) mock_inst = UnitfulMock() - mock_inst.unitful_property = 1 * pq.hertz + mock_inst.unitful_property = 1 * u.hertz assert mock_inst.value == 'MOCK {:e}\n'.format(1) def test_unitful_property_readonly_writing_fails(): with pytest.raises(AttributeError): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz, readonly=True) + unitful_property = unitful_property('MOCK', u.hertz, readonly=True) mock_inst = UnitfulMock({'MOCK?': '1'}) - mock_inst.unitful_property = 1 * pq.hertz + mock_inst.unitful_property = 1 * u.hertz def test_unitful_property_readonly_reading_passes(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', pq.hertz, readonly=True) + unitful_property = unitful_property('MOCK', u.hertz, readonly=True) mock_inst = UnitfulMock({'MOCK?': '1'}) - assert mock_inst.unitful_property == 1 * pq.hertz + assert mock_inst.unitful_property == 1 * u.hertz def test_unitful_property_valid_range(): class UnitfulMock(MockInstrument): unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(0, 10)) + 'MOCK', u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock() @@ -134,7 +134,7 @@ def max_value(self): return 10 unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(min_value, max_value)) + 'MOCK', u.hertz, valid_range=(min_value, max_value)) mock_inst = UnitfulMock() @@ -148,7 +148,7 @@ def test_unitful_property_minimum_value(): with pytest.raises(ValueError): class UnitfulMock(MockInstrument): unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(0, 10)) + 'MOCK', u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock() @@ -159,7 +159,7 @@ def test_unitful_property_maximum_value(): with pytest.raises(ValueError): class UnitfulMock(MockInstrument): unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(0, 10)) + 'MOCK', u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock() @@ -174,13 +174,13 @@ def _input_decorator(_): return '1' a = unitful_property( 'MOCK:A', - pq.hertz, + u.hertz, input_decoration=_input_decorator ) mock_instrument = UnitfulMock({'MOCK:A?': 'garbage'}) - assert mock_instrument.a == 1 * pq.Hz + assert mock_instrument.a == 1 * u.Hz def test_unitful_property_input_decoration_not_a_function(): @@ -188,13 +188,13 @@ class UnitfulMock(MockInstrument): a = unitful_property( 'MOCK:A', - pq.hertz, + u.hertz, input_decoration=float ) mock_instrument = UnitfulMock({'MOCK:A?': '.123'}) - assert mock_instrument.a == 0.123 * pq.Hz + assert mock_instrument.a == 0.123 * u.Hz def test_unitful_property_output_decoration(): @@ -205,13 +205,13 @@ def _output_decorator(_): return '1' a = unitful_property( 'MOCK:A', - pq.hertz, + u.hertz, output_decoration=_output_decorator ) mock_instrument = UnitfulMock() - mock_instrument.a = 345 * pq.hertz + mock_instrument.a = 345 * u.hertz assert mock_instrument.value == 'MOCK:A 1\n' @@ -221,13 +221,13 @@ class UnitfulMock(MockInstrument): a = unitful_property( 'MOCK:A', - pq.hertz, + u.hertz, output_decoration=bool ) mock_instrument = UnitfulMock() - mock_instrument.a = 1 * pq.hertz + mock_instrument.a = 1 * u.hertz assert mock_instrument.value == 'MOCK:A True\n' @@ -235,25 +235,25 @@ class UnitfulMock(MockInstrument): def test_unitful_property_split_str(): class UnitfulMock(MockInstrument): unitful_property = unitful_property( - 'MOCK', pq.hertz, valid_range=(0, 10)) + 'MOCK', u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock({"MOCK?": "1 kHz"}) value = mock_inst.unitful_property assert value.magnitude == 1000 - assert value.units == pq.hertz + assert value.units == u.hertz def test_unitful_property_name_read_not_none(): class UnitfulMock(MockInstrument): a = unitful_property( 'MOCK', - units=pq.hertz, + units=u.hertz, set_cmd='FOOBAR' ) mock_inst = UnitfulMock({'MOCK?': '1000'}) - assert mock_inst.a == 1000 * pq.hertz - mock_inst.a = 1000 * pq.hertz + assert mock_inst.a == 1000 * u.hertz + mock_inst.a = 1000 * u.hertz assert mock_inst.value == 'MOCK?\nFOOBAR {:e}\n'.format(1000) diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index 5a4616763..74ed6f4ff 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u from instruments.util_fns import unitless_property from . import MockInstrument @@ -38,7 +38,7 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock({'MOCK?': '1'}) - mock_inst.mock_property = 1 * pq.volt + mock_inst.mock_property = 1 * u.volt def test_unitless_property_format_code(): diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index a1a3c98d2..f65ebfc55 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, unit_eq @@ -52,7 +52,7 @@ def test_cc1_window(): ], sep="\n" ) as cc: - unit_eq(cc.window, pq.Quantity(2, "ns")) + unit_eq(cc.window, u.Quantity(2, "ns")) cc.window = 7 @@ -90,7 +90,7 @@ def test_cc1_delay(): ], sep="\n" ) as cc: - unit_eq(cc.delay, pq.Quantity(8, "ns")) + unit_eq(cc.delay, u.Quantity(8, "ns")) cc.delay = 2 @@ -145,7 +145,7 @@ def test_cc1_dwell_old_firmware(): ], sep="\n" ) as cc: - unit_eq(cc.dwell_time, pq.Quantity(8, "s")) + unit_eq(cc.dwell_time, u.Quantity(8, "s")) cc.dwell_time = 2 @@ -165,7 +165,7 @@ def test_cc1_dwell_new_firmware(): ], sep="\n" ) as cc: - unit_eq(cc.dwell_time, pq.Quantity(8, "s")) + unit_eq(cc.dwell_time, u.Quantity(8, "s")) cc.dwell_time = 2 diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index 7175416cf..461e796ce 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -10,7 +10,7 @@ import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -48,7 +48,7 @@ def test_mc1_internal_position(): ], sep="\r" ) as mc: - assert mc.internal_position == -100*pq.ms + assert mc.internal_position == -100*u.ms def test_mc1_metric_position(): @@ -62,7 +62,7 @@ def test_mc1_metric_position(): ], sep="\r" ) as mc: - assert mc.metric_position == -3.14159*pq.mm + assert mc.metric_position == -3.14159*u.mm def test_mc1_direction(): @@ -118,7 +118,7 @@ def test_mc1_step(): ], sep="\r" ) as mc: - assert mc.step_size == 20*pq.ms + assert mc.step_size == 20*u.ms def test_mc1_motor(): @@ -148,7 +148,7 @@ def test_mc1_move_timeout(): ], sep="\r" ) as mc: - assert mc.move_timeout == 200*pq.ms + assert mc.move_timeout == 200*u.ms def test_mc1_is_centering(): diff --git a/instruments/tests/test_split_str.py b/instruments/tests/test_split_str.py index cbb5c4102..375c17d48 100644 --- a/instruments/tests/test_split_str.py +++ b/instruments/tests/test_split_str.py @@ -8,10 +8,9 @@ from __future__ import absolute_import -import quantities as pq - import pytest +import instruments.units as u from instruments.util_fns import ( split_unit_str ) @@ -85,15 +84,15 @@ def test_split_unit_str_scientific_notation(): # No signs, no units mag, units = split_unit_str("123E1") assert mag == 1230 - assert units == pq.dimensionless + assert units == u.dimensionless # Negative exponential, no units mag, units = split_unit_str("123E-1") assert mag == 12.3 - assert units == pq.dimensionless + assert units == u.dimensionless # Negative magnitude, no units mag, units = split_unit_str("-123E1") assert mag == -1230 - assert units == pq.dimensionless + assert units == u.dimensionless # No signs, with units mag, units = split_unit_str("123E1 foobars") assert mag == 1230 @@ -105,7 +104,7 @@ def test_split_unit_str_scientific_notation(): # Lower case e mag, units = split_unit_str("123e1") assert mag == 1230 - assert units == pq.dimensionless + assert units == u.dimensionless def test_split_unit_str_empty_string(): diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index aa8fed2f9..b921fa46f 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -8,11 +8,11 @@ from __future__ import absolute_import -import quantities as pq import numpy as np import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u # TESTS ####################################################################### @@ -30,10 +30,10 @@ def test_amplitude(): ] ) as inst: np.testing.assert_array_equal( - inst.amplitude, (1.234 * pq.V, inst.VoltageMode.peak_to_peak) + inst.amplitude, (1.234 * u.V, inst.VoltageMode.peak_to_peak) ) - inst.amplitude = 0.1 * pq.V - inst.amplitude = (0.1 * pq.V, inst.VoltageMode.rms) + inst.amplitude = 0.1 * u.V + inst.amplitude = (0.1 * u.V, inst.VoltageMode.rms) def test_frequency(): @@ -47,8 +47,8 @@ def test_frequency(): "1.234", ] ) as inst: - assert inst.frequency == 1.234 * pq.Hz - inst.frequency = 0.1 * pq.Hz + assert inst.frequency == 1.234 * u.Hz + inst.frequency = 0.1 * u.Hz def test_function(): @@ -77,8 +77,8 @@ def test_offset(): "1.234", ] ) as inst: - assert inst.offset == 1.234 * pq.V - inst.offset = 0.1 * pq.V + assert inst.offset == 1.234 * u.V + inst.offset = 0.1 * u.V def test_phase(): @@ -92,5 +92,5 @@ def test_phase(): "1.234", ] ) as inst: - assert inst.phase == 1.234 * pq.degree - inst.phase = 0.1 * pq.degree + assert inst.phase == 1.234 * u.degree + inst.phase = 0.1 * u.degree diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index c7794acad..8bfd8e163 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -8,12 +8,12 @@ from __future__ import absolute_import -import quantities as pq import numpy as np import pytest import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u # TESTS ####################################################################### @@ -44,8 +44,8 @@ def test_frequency(): "12.34", ] ) as inst: - assert inst.frequency == 12.34 * pq.Hz - inst.frequency = 1 * pq.kHz + assert inst.frequency == 12.34 * u.Hz + inst.frequency = 1 * u.kHz def test_phase(): @@ -59,8 +59,8 @@ def test_phase(): "-45", ] ) as inst: - assert inst.phase == -45 * pq.degrees - inst.phase = 10 * pq.degrees + assert inst.phase == -45 * u.degrees + inst.phase = 10 * u.degrees def test_amplitude(): @@ -74,8 +74,8 @@ def test_amplitude(): "0.1", ] ) as inst: - assert inst.amplitude == 0.1 * pq.V - inst.amplitude = 1 * pq.V + assert inst.amplitude == 0.1 * u.V + inst.amplitude = 1 * u.V def test_input_shield_ground(): @@ -122,7 +122,7 @@ def test_sample_rate(): # sends index of VALID_SAMPLE_RATES "14" ] ) as inst: - assert inst.sample_rate == 16 * pq.Hz + assert inst.sample_rate == 16 * u.Hz assert inst.sample_rate == "trigger" inst.sample_rate = 2 inst.sample_rate = "trigger" diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index d842eef82..eedbe8978 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -8,7 +8,7 @@ from __future__ import absolute_import -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq @@ -32,7 +32,7 @@ def test_srsdg645_output_level(): "3.2" ] ) as ddg: - unit_eq(ddg.output["AB"].level_amplitude, pq.Quantity(3.2, "V")) + unit_eq(ddg.output["AB"].level_amplitude, u.Quantity(3.2, "V")) ddg.output["AB"].level_amplitude = 4.0 @@ -50,7 +50,7 @@ def test_srsdg645_output_offset(): "1.2" ] ) as ddg: - unit_eq(ddg.output["AB"].level_offset, pq.Quantity(1.2, "V")) + unit_eq(ddg.output["AB"].level_offset, u.Quantity(1.2, "V")) ddg.output["AB"].level_offset = 2.0 @@ -76,5 +76,5 @@ def test_srsdg645_trigger_source(): with expected_protocol(ik.srs.SRSDG645, "DLAY?2\nDLAY 3,2,60.0\n", "0,42\n") as ddg: ref, t = ddg.channel["A"].delay assert ref == ddg.Channels.T0 - assert abs((t - pq.Quantity(42, "s")).magnitude) < 1e5 - ddg.channel["B"].delay = (ddg.channel["A"], pq.Quantity(1, "minute")) + assert abs((t - u.Quantity(42, "s")).magnitude) < 1e5 + ddg.channel["B"].delay = (ddg.channel["A"], u.Quantity(1, "minute")) diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py index 82db76047..6106c52ac 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_apt.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -13,7 +13,7 @@ import struct import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.thorlabs._packets import ThorLabsPacket, hw_info_data diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index 7d591bee8..dabccce2c 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, unit_eq @@ -49,7 +49,7 @@ def test_lcc25_frequency(): ], sep="\r" ) as lcc: - unit_eq(lcc.frequency, pq.Quantity(20, "Hz")) + unit_eq(lcc.frequency, u.Quantity(20, "Hz")) lcc.frequency = 10.0 @@ -210,7 +210,7 @@ def test_lcc25_voltage1(): ], sep="\r" ) as lcc: - unit_eq(lcc.voltage1, pq.Quantity(20, "V")) + unit_eq(lcc.voltage1, u.Quantity(20, "V")) lcc.voltage1 = 10.0 @@ -235,7 +235,7 @@ def test_lcc25_voltage2(): ], sep="\r" ) as lcc: - unit_eq(lcc.voltage2, pq.Quantity(20, "V")) + unit_eq(lcc.voltage2, u.Quantity(20, "V")) lcc.voltage2 = 10.0 @@ -254,7 +254,7 @@ def test_lcc25_minvoltage(): ], sep="\r" ) as lcc: - unit_eq(lcc.min_voltage, pq.Quantity(20, "V")) + unit_eq(lcc.min_voltage, u.Quantity(20, "V")) lcc.min_voltage = 10.0 @@ -273,7 +273,7 @@ def test_lcc25_maxvoltage(): ], sep="\r" ) as lcc: - unit_eq(lcc.max_voltage, pq.Quantity(20, "V")) + unit_eq(lcc.max_voltage, u.Quantity(20, "V")) lcc.max_voltage = 10.0 @@ -292,7 +292,7 @@ def test_lcc25_dwell(): ], sep="\r" ) as lcc: - unit_eq(lcc.dwell, pq.Quantity(20, "ms")) + unit_eq(lcc.dwell, u.Quantity(20, "ms")) lcc.dwell = 10 @@ -326,7 +326,7 @@ def test_lcc25_increment(): ], sep="\r" ) as lcc: - unit_eq(lcc.increment, pq.Quantity(20, "V")) + unit_eq(lcc.increment, u.Quantity(20, "V")) lcc.increment = 10.0 diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 8714cacd3..958185915 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -9,7 +9,7 @@ from __future__ import absolute_import import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol, unit_eq @@ -173,7 +173,7 @@ def test_sc10_open_time(): ], sep="\r" ) as sc: - unit_eq(sc.open_time, pq.Quantity(20, "ms")) + unit_eq(sc.open_time, u.Quantity(20, "ms")) sc.open_time = 10 @@ -192,7 +192,7 @@ def test_sc10_shut_time(): ], sep="\r" ) as sc: - unit_eq(sc.shut_time, pq.Quantity(20, "ms")) + unit_eq(sc.shut_time, u.Quantity(20, "ms")) sc.shut_time = 10.0 diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index 4625d1765..746a26bfc 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -10,7 +10,7 @@ from enum import IntEnum import pytest -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -142,7 +142,7 @@ def test_tc200_temperature(): ], sep="\r" ) as tc: - assert tc.temperature == 30.0 * pq.degC + assert tc.temperature == 30.0 * u.degC def test_tc200_temperature_set(): @@ -163,8 +163,8 @@ def test_tc200_temperature_set(): ], sep="\r" ) as tc: - assert tc.temperature_set == 30.0 * pq.degC - tc.temperature_set = 40 * pq.degC + assert tc.temperature_set == 30.0 * u.degC + tc.temperature_set = 40 * u.degC def test_tc200_temperature_range(): @@ -180,7 +180,7 @@ def test_tc200_temperature_range(): ], sep="\r" ) as tc: - tc.temperature_set = 50 * pq.degC + tc.temperature_set = 50 * u.degC def test_tc200_pid(): @@ -381,11 +381,11 @@ def test_tc200_degrees(): ) as tc: assert str(tc.degrees).split(" ")[1] == "K" assert str(tc.degrees).split(" ")[1] == "degC" - assert tc.degrees == pq.degF + assert tc.degrees == u.degF - tc.degrees = pq.degC - tc.degrees = pq.degF - tc.degrees = pq.degK + tc.degrees = u.degC + tc.degrees = u.degF + tc.degrees = u.degK def test_tc200_degrees_invalid(): @@ -502,8 +502,8 @@ def test_tc200_max_power(): ], sep="\r" ) as tc: - assert tc.max_power == 15.0 * pq.W - tc.max_power = 12 * pq.W + assert tc.max_power == 15.0 * u.W + tc.max_power = 12 * u.W def test_tc200_power_min(): @@ -551,8 +551,8 @@ def test_tc200_max_temperature(): ], sep="\r" ) as tc: - assert tc.max_temperature == 200.0 * pq.degC - tc.max_temperature = 180 * pq.degC + assert tc.max_temperature == 200.0 * u.degC + tc.max_temperature = 180 * u.degC def test_tc200_temp_min(): diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index e6bc81216..1b110432f 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from datetime import datetime import pytest -import quantities as pq +import instruments.units as u import instruments as ik @@ -74,8 +74,8 @@ def test_wavelength(): ], sep="\r\n" ) as tm: - assert tm.laser[0].wavelength == 640 * pq.nm - assert tm.laser[1].wavelength == 405.3 * pq.nm + assert tm.laser[0].wavelength == 640 * u.nm + assert tm.laser[1].wavelength == 405.3 * u.nm def test_laser_enable(): @@ -387,7 +387,7 @@ def test_laser_ontime(): ], sep="\r\n" ) as tm: - assert tm.laser[0].on_time == 10000 * pq.s + assert tm.laser[0].on_time == 10000 * u.s def test_laser_charm_status(): diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index a968c8e1e..4878753d0 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -9,11 +9,11 @@ from __future__ import absolute_import from builtins import range - from enum import Enum -import quantities as pq + import pytest +import instruments.units as u from instruments.util_fns import ( ProxyList, assume_units, convert_temperature, @@ -126,7 +126,7 @@ def __init__(self, parent, name): def test_assume_units_correct(): - m = pq.Quantity(1, 'm') + m = u.Quantity(1, 'm') # Check that unitful quantities are kept unitful. assert assume_units(m, 'mm').rescale('mm').magnitude == 1000 @@ -136,35 +136,35 @@ def test_assume_units_correct(): def test_temperature_conversion(): - blo = 70.0 * pq.degF - out = convert_temperature(blo, pq.degC) + blo = 70.0 * u.degF + out = convert_temperature(blo, u.degC) assert out.magnitude == 21.11111111111111 - out = convert_temperature(blo, pq.degK) + out = convert_temperature(blo, u.degK) assert out.magnitude == 294.2055555555555 - out = convert_temperature(blo, pq.degF) + out = convert_temperature(blo, u.degF) assert out.magnitude == 70.0 - blo = 20.0 * pq.degC - out = convert_temperature(blo, pq.degF) + blo = 20.0 * u.degC + out = convert_temperature(blo, u.degF) assert out.magnitude == 68 - out = convert_temperature(blo, pq.degC) + out = convert_temperature(blo, u.degC) assert out.magnitude == 20.0 - out = convert_temperature(blo, pq.degK) + out = convert_temperature(blo, u.degK) assert out.magnitude == 293.15 - blo = 270 * pq.degK - out = convert_temperature(blo, pq.degC) + blo = 270 * u.degK + out = convert_temperature(blo, u.degC) assert out.magnitude == -3.1499999999999773 - out = convert_temperature(blo, pq.degF) + out = convert_temperature(blo, u.degF) assert out.magnitude == 141.94736842105263 - out = convert_temperature(blo, pq.K) + out = convert_temperature(blo, u.K) assert out.magnitude == 270 def test_temperater_conversion_failure(): with pytest.raises(ValueError): - blo = 70.0 * pq.degF - convert_temperature(blo, pq.V) + blo = 70.0 * u.degF + convert_temperature(blo, u.V) def test_assume_units_failures(): diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index 8eb6c8e37..8a1986fcc 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -15,7 +15,7 @@ strategies as st, ) import numpy as np -import quantities as pq +import instruments.units as u import instruments as ik from instruments.tests import expected_protocol @@ -94,8 +94,8 @@ def test_start_wavelength(value): "6.000000e-06" ] ) as inst: - assert inst.start_wl == 6e-6 * pq.meter - inst.start_wl = value * pq.meter + assert inst.start_wl == 6e-6 * u.meter + inst.start_wl = value * u.meter @given(value=st.floats(min_value=600e-9, max_value=1700e-9)) @@ -111,8 +111,8 @@ def test_end_wavelength(value): "6.000000e-06" ] ) as inst: - assert inst.stop_wl == 6e-6 * pq.meter - inst.stop_wl = value * pq.meter + assert inst.stop_wl == 6e-6 * u.meter + inst.stop_wl = value * u.meter def test_bandwidth(): @@ -127,8 +127,8 @@ def test_bandwidth(): "6.000000e-06" ] ) as inst: - assert inst.bandwidth == 6e-6 * pq.meter - inst.bandwidth = 1e-6 * pq.meter + assert inst.bandwidth == 6e-6 * u.meter + inst.bandwidth = 1e-6 * u.meter def test_span(): @@ -143,8 +143,8 @@ def test_span(): "6.000000e-06" ] ) as inst: - assert inst.span == 6e-6 * pq.meter - inst.span = 1e-6 * pq.meter + assert inst.span == 6e-6 * u.meter + inst.span = 1e-6 * u.meter def test_center_wl(): @@ -159,8 +159,8 @@ def test_center_wl(): "6.000000e-06" ] ) as inst: - assert inst.center_wl == 6e-6 * pq.meter - inst.center_wl = 1e-6 * pq.meter + assert inst.center_wl == 6e-6 * u.meter + inst.center_wl = 1e-6 * u.meter def test_points(): diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index 2989283d9..4f216c999 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -14,7 +14,7 @@ from builtins import range from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.thorlabs.thorlabs_utils import check_cmd @@ -67,7 +67,7 @@ def name(self): frequency = unitful_property( "freq", - pq.Hz, + u.Hz, format_code="{:.1f}", set_fmt="{}={}", valid_range=(5, 150), @@ -139,7 +139,7 @@ def name(self): voltage1 = unitful_property( "volt1", - pq.V, + u.V, format_code="{:.1f}", set_fmt="{}={}", valid_range=(0, 25), @@ -154,7 +154,7 @@ def name(self): voltage2 = unitful_property( "volt2", - pq.V, + u.V, format_code="{:.1f}", set_fmt="{}={}", valid_range=(0, 25), @@ -169,7 +169,7 @@ def name(self): min_voltage = unitful_property( "min", - pq.V, + u.V, format_code="{:.1f}", set_fmt="{}={}", valid_range=(0, 25), @@ -184,7 +184,7 @@ def name(self): max_voltage = unitful_property( "max", - pq.V, + u.V, format_code="{:.1f}", set_fmt="{}={}", valid_range=(0, 25), @@ -200,7 +200,7 @@ def name(self): dwell = unitful_property( "dwell", - units=pq.ms, + units=u.ms, format_code="{:n}", set_fmt="{}={}", valid_range=(0, None), @@ -215,7 +215,7 @@ def name(self): increment = unitful_property( "increment", - units=pq.V, + units=u.V, format_code="{:.1f}", set_fmt="{}={}", valid_range=(0, None), diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index e9ad6e99b..007ed23eb 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -14,7 +14,7 @@ from enum import Enum, IntEnum -import quantities as pq +import instruments.units as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import enum_property @@ -213,12 +213,12 @@ def averaging_count(self, newval): # METHODS ## - _READ_UNITS = defaultdict(lambda: pq.dimensionless) + _READ_UNITS = defaultdict(lambda: u.dimensionless) _READ_UNITS.update({ - MeasurementConfiguration.power: pq.W, - MeasurementConfiguration.current: pq.A, - MeasurementConfiguration.frequency: pq.Hz, - MeasurementConfiguration.voltage: pq.V, + MeasurementConfiguration.power: u.W, + MeasurementConfiguration.current: u.A, + MeasurementConfiguration.frequency: u.Hz, + MeasurementConfiguration.voltage: u.V, }) diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index cfce252b4..79a884c29 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -13,7 +13,7 @@ from builtins import range from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.util_fns import ( @@ -133,7 +133,7 @@ def name(self): open_time = unitful_property( "open", - pq.ms, + u.ms, format_code="{:.0f}", set_fmt="{}={}", valid_range=(0, 999999), @@ -148,7 +148,7 @@ def name(self): shut_time = unitful_property( "shut", - pq.ms, + u.ms, format_code="{:.0f}", set_fmt="{}={}", valid_range=(0, 999999), diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index 0beef5de7..145b78568 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -14,7 +14,7 @@ from builtins import range, map from enum import IntEnum, Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import Instrument from instruments.util_fns import convert_temperature @@ -151,7 +151,7 @@ def status(self): temperature = unitful_property( "tact", - units=pq.degC, + units=u.degC, readonly=True, input_decoration=lambda x: x.replace( " C", "").replace(" F", "").replace(" K", ""), @@ -168,10 +168,10 @@ def status(self): max_temperature = unitful_property( "tmax", - units=pq.degC, + units=u.degC, format_code="{:.1f}", set_fmt="{}={}", - valid_range=(20*pq.degC, 205*pq.degC), + valid_range=(20*u.degC, 205*u.degC), doc=""" Gets/sets the maximum temperature @@ -195,12 +195,12 @@ def temperature_set(self): """ response = self.query("tset?").replace( " C", "").replace(" F", "").replace(" K", "") - return float(response) * pq.degC + return float(response) * u.degC @temperature_set.setter def temperature_set(self, newval): # the set temperature is always in celsius - newval = convert_temperature(newval, pq.degC).magnitude + newval = convert_temperature(newval, u.degC).magnitude if newval < 20.0 or newval > self.max_temperature: raise ValueError("Temperature set is out of range.") out_query = "tset={}".format(newval) @@ -289,19 +289,19 @@ def degrees(self): """ response = self.status if (response >> 4) % 2 and (response >> 5) % 2: - return pq.degC + return u.degC elif (response >> 5) % 2: - return pq.degK + return u.degK - return pq.degF + return u.degF @degrees.setter def degrees(self, newval): - if newval is pq.degC: + if newval is u.degC: self.sendcmd("unit=c") - elif newval is pq.degF: + elif newval is u.degF: self.sendcmd("unit=f") - elif newval is pq.degK: + elif newval is u.degK: self.sendcmd("unit=k") else: raise TypeError("Invalid temperature type") @@ -337,10 +337,10 @@ def degrees(self, newval): max_power = unitful_property( "pmax", - units=pq.W, + units=u.W, format_code="{:.1f}", set_fmt="{}={}", - valid_range=(0.1*pq.W, 18.0*pq.W), + valid_range=(0.1*u.W, 18.0*u.W), doc=""" Gets/sets the maximum power diff --git a/instruments/thorlabs/thorlabsapt.py b/instruments/thorlabs/thorlabsapt.py index bbd6a75ce..9913d00cf 100644 --- a/instruments/thorlabs/thorlabsapt.py +++ b/instruments/thorlabs/thorlabsapt.py @@ -16,7 +16,7 @@ import warnings from builtins import range -import quantities as pq +import instruments.units as u from instruments.thorlabs import _abstract, _packets, _cmds from instruments.util_fns import assume_units @@ -284,7 +284,7 @@ def max_travel(self): # chan, int_maxtrav _, int_maxtrav = struct.unpack('>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> tm = ik.toptica.TopMode.open_serial('/dev/ttyUSB0', 115200) >>> print(tm.laser[0].wavelength) diff --git a/instruments/units.py b/instruments/units.py index 632608e8f..45d90bc37 100644 --- a/instruments/units.py +++ b/instruments/units.py @@ -6,10 +6,12 @@ # IMPORTS ##################################################################### +# pylint: disable=unused-wildcard-import, wildcard-import + from __future__ import absolute_import from __future__ import division -from quantities import Hz, milli, UnitQuantity +from quantities import * from quantities.unitquantity import IrreducibleUnit # UNITS ####################################################################### diff --git a/instruments/util_fns.py b/instruments/util_fns.py index f23ed7075..c0b0a9948 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -12,7 +12,7 @@ import re from enum import Enum, IntEnum -import quantities as pq +import instruments.units as u # CONSTANTS ################################################################### @@ -36,8 +36,8 @@ def assume_units(value, units): ``units``, depending on if ``value`` is unitful. :rtype: `Quantity` """ - if not isinstance(value, pq.Quantity): - value = pq.Quantity(value, units) + if not isinstance(value, u.Quantity): + value = u.Quantity(value, units) return value @@ -86,22 +86,22 @@ def convert_temperature(temperature, base): """ # quantities reports equivalence between degC and degK, so a string # comparison is needed - newval = assume_units(temperature, pq.degC) - if newval.units == pq.degF and str(base).split(" ")[1] == 'degC': + newval = assume_units(temperature, u.degC) + if newval.units == u.degF and str(base).split(" ")[1] == 'degC': return_val = ((newval.magnitude - 32.0) * 5.0 / 9.0) * base elif str(newval.units).split(" ")[1] == 'K' and str(base).split(" ")[1] == 'degC': return_val = (newval.magnitude - 273.15) * base - elif str(newval.units).split(" ")[1] == 'K' and base == pq.degF: + elif str(newval.units).split(" ")[1] == 'K' and base == u.degF: return_val = (newval.magnitude / 1.8 - 459 / 57) * base - elif str(newval.units).split(" ")[1] == 'degC' and base == pq.degF: + elif str(newval.units).split(" ")[1] == 'degC' and base == u.degF: return_val = (newval.magnitude * 9.0 / 5.0 + 32.0) * base - elif newval.units == pq.degF and str(base).split(" ")[1] == 'K': + elif newval.units == u.degF and str(base).split(" ")[1] == 'K': return_val = ((newval.magnitude + 459.57) * 5.0 / 9.0) * base elif str(newval.units).split(" ")[1] == 'degC' and str(base).split(" ")[1] == 'K': return_val = (newval.magnitude + 273.15) * base elif str(newval.units).split(" ")[1] == 'degC' and str(base).split(" ")[1] == 'degC': return_val = newval - elif newval.units == pq.degF and base == pq.degF: + elif newval.units == u.degF and base == u.degF: return_val = newval elif str(newval.units).split(" ")[1] == 'K' and str(base).split(" ")[1] == 'K': return_val = newval @@ -110,16 +110,16 @@ def convert_temperature(temperature, base): return return_val -def split_unit_str(s, default_units=pq.dimensionless, lookup=None): +def split_unit_str(s, default_units=u.dimensionless, lookup=None): """ Given a string of the form "12 C" or "14.7 GHz", returns a tuple of the numeric part and the unit part, irrespective of how many (if any) whitespace characters appear between. By design, the tuple should be such that it can be unpacked into - :func:`pq.Quantity`:: + :func:`u.Quantity`:: - >>> pq.Quantity(*split_unit_str("1 s")) + >>> u.Quantity(*split_unit_str("1 s")) array(1) * s For this reason, the second element of the tuple may be a unit or @@ -131,7 +131,7 @@ def split_unit_str(s, default_units=pq.dimensionless, lookup=None): :param callable lookup: If specified, this function is called on the units part of the input string. If `None`, no lookup is performed. Lookups are never performed on the default units. - :rtype: `tuple` of a `float` and a `str` or `pq.Quantity` + :rtype: `tuple` of a `float` and a `str` or `u.Quantity` """ if lookup is None: lookup = lambda x: x @@ -340,8 +340,8 @@ def _getter(self): return float(raw) def _setter(self, newval): - if isinstance(newval, pq.Quantity): - if newval.units == pq.dimensionless: + if isinstance(newval, u.Quantity): + if newval.units == u.dimensionless: newval = float(newval.magnitude) else: raise ValueError @@ -470,7 +470,7 @@ def _out_decor_fcn(val): def _getter(self): raw = _in_decor_fcn(self.query("{}?".format(command))) - return pq.Quantity(*split_unit_str(raw, units)).rescale(units) + return u.Quantity(*split_unit_str(raw, units)).rescale(units) def _setter(self, newval): min_value, max_value = valid_range @@ -545,13 +545,13 @@ def bounded_unitful_property(command, units, min_fmt_str="{}:MIN?", def _min_getter(self): if valid_range[0] == "query": - return pq.Quantity(*split_unit_str(self.query(min_fmt_str.format(command)), units)) + return u.Quantity(*split_unit_str(self.query(min_fmt_str.format(command)), units)) return assume_units(valid_range[0], units).rescale(units) def _max_getter(self): if valid_range[1] == "query": - return pq.Quantity(*split_unit_str(self.query(max_fmt_str.format(command)), units)) + return u.Quantity(*split_unit_str(self.query(max_fmt_str.format(command)), units)) return assume_units(valid_range[1], units).rescale(units) diff --git a/instruments/yokogawa/yokogawa6370.py b/instruments/yokogawa/yokogawa6370.py index 1419ad1c9..4794c684e 100644 --- a/instruments/yokogawa/yokogawa6370.py +++ b/instruments/yokogawa/yokogawa6370.py @@ -11,7 +11,7 @@ from enum import IntEnum, Enum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( OpticalSpectrumAnalyzer, @@ -33,9 +33,9 @@ class Yokogawa6370(OpticalSpectrumAnalyzer): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> inst = ik.yokogawa.Yokogawa6370.open_visa('TCPIP0:192.168.0.35') - >>> inst.start_wl = 1030e-9 * pq.m + >>> inst.start_wl = 1030e-9 * u.m """ def __init__(self, *args, **kwargs): @@ -115,7 +115,7 @@ def channel(self): start_wl, start_wl_min, start_wl_max = bounded_unitful_property( ":SENS:WAV:STAR", - pq.meter, + u.meter, doc=""" The start wavelength in m. """, @@ -124,7 +124,7 @@ def channel(self): stop_wl, stop_wl_min, stop_wl_max = bounded_unitful_property( ":SENS:WAV:STOP", - pq.meter, + u.meter, doc=""" The stop wavelength in m. """, @@ -133,7 +133,7 @@ def channel(self): bandwidth = unitful_property( ":SENS:BAND:RES", - pq.meter, + u.meter, doc=""" The bandwidth in m. """ @@ -141,7 +141,7 @@ def channel(self): span = unitful_property( ":SENS:WAV:SPAN", - pq.meter, + u.meter, doc=""" A floating point property that controls the wavelength span in m. """ @@ -149,7 +149,7 @@ def channel(self): center_wl = unitful_property( ":SENS:WAV:CENT", - pq.meter, + u.meter, doc=""" A floating point property that controls the center wavelength m. """ diff --git a/instruments/yokogawa/yokogawa7651.py b/instruments/yokogawa/yokogawa7651.py index 3d7d03e08..f1862c25b 100644 --- a/instruments/yokogawa/yokogawa7651.py +++ b/instruments/yokogawa/yokogawa7651.py @@ -11,7 +11,7 @@ from enum import IntEnum -import quantities as pq +import instruments.units as u from instruments.abstract_instruments import ( PowerSupply, @@ -31,9 +31,9 @@ class Yokogawa7651(PowerSupply, Instrument): Example usage: >>> import instruments as ik - >>> import quantities as pq + >>> import instruments.units as u >>> inst = ik.yokogawa.Yokogawa7651.open_gpibusb("/dev/ttyUSB0", 1) - >>> inst.voltage = 10 * pq.V + >>> inst.voltage = 10 * u.V """ # INNER CLASSES # @@ -93,7 +93,7 @@ def voltage(self): @voltage.setter def voltage(self, newval): - newval = assume_units(newval, pq.volt).rescale(pq.volt).magnitude + newval = assume_units(newval, u.volt).rescale(u.volt).magnitude self.mode = self._parent.Mode.voltage self._parent.sendcmd('SA{};'.format(newval)) self._parent.trigger() @@ -115,7 +115,7 @@ def current(self): @current.setter def current(self, newval): - newval = assume_units(newval, pq.amp).rescale(pq.amp).magnitude + newval = assume_units(newval, u.amp).rescale(u.amp).magnitude self.mode = self._parent.Mode.current self._parent.sendcmd('SA{};'.format(newval)) self._parent.trigger() From 8b9fe5e9e21895d934b83184387ba2ec02037037 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 2 Feb 2020 23:54:25 -0500 Subject: [PATCH 038/108] Remove all __future__ imports (#234) --- instruments/__init__.py | 1 - instruments/abstract_instruments/__init__.py | 1 - instruments/abstract_instruments/comm/__init__.py | 1 - instruments/abstract_instruments/comm/abstract_comm.py | 3 --- instruments/abstract_instruments/comm/file_communicator.py | 3 --- instruments/abstract_instruments/comm/gpib_communicator.py | 3 --- .../abstract_instruments/comm/loopback_communicator.py | 4 ---- instruments/abstract_instruments/comm/serial_communicator.py | 3 --- instruments/abstract_instruments/comm/serial_manager.py | 2 -- instruments/abstract_instruments/comm/socket_communicator.py | 3 --- instruments/abstract_instruments/comm/usb_communicator.py | 3 --- instruments/abstract_instruments/comm/usbtmc_communicator.py | 3 --- instruments/abstract_instruments/comm/visa_communicator.py | 3 --- instruments/abstract_instruments/comm/vxi11_communicator.py | 3 --- instruments/abstract_instruments/electrometer.py | 2 -- instruments/abstract_instruments/function_generator.py | 2 -- instruments/abstract_instruments/instrument.py | 3 --- instruments/abstract_instruments/multimeter.py | 2 -- instruments/abstract_instruments/optical_spectrum_analyzer.py | 4 ---- instruments/abstract_instruments/oscilloscope.py | 2 -- instruments/abstract_instruments/power_supply.py | 2 -- instruments/abstract_instruments/signal_generator/__init__.py | 1 - instruments/abstract_instruments/signal_generator/channel.py | 2 -- .../abstract_instruments/signal_generator/signal_generator.py | 2 -- .../signal_generator/single_channel_sg.py | 2 -- instruments/agilent/__init__.py | 1 - instruments/agilent/agilent33220a.py | 2 -- instruments/agilent/agilent34410a.py | 2 -- instruments/config.py | 3 --- instruments/errors.py | 1 - instruments/fluke/__init__.py | 1 - instruments/fluke/fluke3000.py | 2 -- instruments/generic_scpi/__init__.py | 1 - instruments/generic_scpi/scpi_function_generator.py | 2 -- instruments/generic_scpi/scpi_instrument.py | 2 -- instruments/generic_scpi/scpi_multimeter.py | 2 -- instruments/glassman/__init__.py | 1 - instruments/glassman/glassmanfr.py | 2 -- instruments/holzworth/__init__.py | 1 - instruments/holzworth/holzworth_hs9000.py | 2 -- instruments/hp/__init__.py | 1 - instruments/hp/hp3456a.py | 2 -- instruments/hp/hp6624a.py | 2 -- instruments/hp/hp6632b.py | 2 -- instruments/hp/hp6652a.py | 2 -- instruments/hp/hpe3631a.py | 2 -- instruments/keithley/__init__.py | 1 - instruments/keithley/keithley195.py | 2 -- instruments/keithley/keithley2182.py | 2 -- instruments/keithley/keithley485.py | 2 -- instruments/keithley/keithley580.py | 2 -- instruments/keithley/keithley6220.py | 2 -- instruments/keithley/keithley6514.py | 2 -- instruments/lakeshore/__init__.py | 1 - instruments/lakeshore/lakeshore340.py | 2 -- instruments/lakeshore/lakeshore370.py | 2 -- instruments/lakeshore/lakeshore475.py | 2 -- instruments/minghe/__init__.py | 1 - instruments/minghe/mhs5200a.py | 2 -- instruments/named_struct.py | 2 -- instruments/newport/__init__.py | 1 - instruments/newport/errors.py | 2 -- instruments/newport/newportesp301.py | 2 -- instruments/ondax/__init__.py | 1 - instruments/ondax/lm.py | 2 -- instruments/oxford/__init__.py | 2 -- instruments/oxford/oxforditc503.py | 2 -- instruments/phasematrix/__init__.py | 1 - instruments/phasematrix/phasematrix_fsw0020.py | 2 -- instruments/picowatt/__init__.py | 1 - instruments/picowatt/picowattavs47.py | 2 -- instruments/qubitekk/__init__.py | 1 - instruments/qubitekk/cc1.py | 2 -- instruments/qubitekk/mc1.py | 1 - instruments/rigol/__init__.py | 1 - instruments/rigol/rigolds1000.py | 2 -- instruments/srs/__init__.py | 1 - instruments/srs/srs345.py | 2 -- instruments/srs/srs830.py | 2 -- instruments/srs/srsctc100.py | 2 -- instruments/srs/srsdg645.py | 2 -- instruments/tektronix/__init__.py | 1 - instruments/tektronix/tekawg2000.py | 2 -- instruments/tektronix/tekdpo4104.py | 2 -- instruments/tektronix/tekdpo70000.py | 2 -- instruments/tektronix/tektds224.py | 2 -- instruments/tektronix/tektds5xx.py | 2 -- instruments/tests/__init__.py | 2 -- .../tests/test_abstract_inst/test_function_generator.py | 1 - instruments/tests/test_agilent/test_agilent_33220a.py | 1 - instruments/tests/test_agilent/test_agilent_34410a.py | 1 - instruments/tests/test_base_instrument.py | 1 - instruments/tests/test_comm/test_file.py | 1 - instruments/tests/test_comm/test_gpibusb.py | 1 - instruments/tests/test_comm/test_loopback.py | 1 - instruments/tests/test_comm/test_serial.py | 1 - instruments/tests/test_comm/test_socket.py | 1 - instruments/tests/test_comm/test_usbtmc.py | 1 - instruments/tests/test_comm/test_vxi11.py | 1 - instruments/tests/test_config.py | 1 - instruments/tests/test_fluke/test_fluke3000.py | 1 - .../tests/test_generic_scpi/test_scpi_function_generator.py | 1 - instruments/tests/test_generic_scpi/test_scpi_multimeter.py | 1 - instruments/tests/test_glassman/test_glassmanfr.py | 1 - instruments/tests/test_holzworth/test_holzworth_hs9000.py | 1 - instruments/tests/test_hp/test_hp3456a.py | 1 - instruments/tests/test_hp/test_hp6624a.py | 1 - instruments/tests/test_hp/test_hp6632b.py | 1 - instruments/tests/test_hp/test_hp6652a.py | 1 - instruments/tests/test_hp/test_hpe3631a.py | 1 - instruments/tests/test_keithley/test_keithley2182.py | 1 - instruments/tests/test_keithley/test_keithley485.py | 1 - instruments/tests/test_keithley/test_keithley6220.py | 1 - instruments/tests/test_keithley/test_keithley6514.py | 1 - instruments/tests/test_minghe/test_minghe_mhs5200a.py | 1 - instruments/tests/test_named_struct.py | 1 - instruments/tests/test_newport/test_newportesp301.py | 1 - instruments/tests/test_ondax/test_lm.py | 2 -- instruments/tests/test_oxford/test_oxforditc503.py | 1 - .../tests/test_phasematrix/test_phasematrix_fsw0020.py | 1 - instruments/tests/test_picowatt/test_picowatt_avs47.py | 1 - instruments/tests/test_property_factories/__init__.py | 2 -- .../tests/test_property_factories/test_bool_property.py | 1 - .../test_property_factories/test_bounded_unitful_property.py | 1 - .../tests/test_property_factories/test_enum_property.py | 1 - .../tests/test_property_factories/test_int_property.py | 1 - instruments/tests/test_property_factories/test_rproperty.py | 1 - .../tests/test_property_factories/test_string_property.py | 1 - .../tests/test_property_factories/test_unitful_property.py | 1 - .../tests/test_property_factories/test_unitless_property.py | 1 - instruments/tests/test_qubitekk/test_qubitekk_cc1.py | 1 - instruments/tests/test_qubitekk/test_qubitekk_mc1.py | 1 - instruments/tests/test_split_str.py | 1 - instruments/tests/test_srs/test_srs345.py | 1 - instruments/tests/test_srs/test_srs830.py | 1 - instruments/tests/test_srs/test_srsdg645.py | 1 - instruments/tests/test_tektronix/test_tektronix_tds224.py | 1 - instruments/tests/test_thorlabs/test_thorlabs_apt.py | 1 - instruments/tests/test_thorlabs/test_thorlabs_lcc25.py | 1 - instruments/tests/test_thorlabs/test_thorlabs_sc10.py | 1 - instruments/tests/test_thorlabs/test_thorlabs_tc200.py | 1 - instruments/tests/test_thorlabs/test_utils.py | 1 - instruments/tests/test_toptica/test_toptica_topmode.py | 1 - instruments/tests/test_toptica/test_toptica_utils.py | 1 - instruments/tests/test_util_fns.py | 1 - instruments/tests/test_yokogawa/test_yokogawa_6370.py | 1 - instruments/thorlabs/__init__.py | 1 - instruments/thorlabs/_abstract.py | 2 -- instruments/thorlabs/_cmds.py | 2 -- instruments/thorlabs/_packets.py | 2 -- instruments/thorlabs/lcc25.py | 2 -- instruments/thorlabs/pm100usb.py | 2 -- instruments/thorlabs/sc10.py | 2 -- instruments/thorlabs/tc200.py | 2 -- instruments/thorlabs/thorlabsapt.py | 3 --- instruments/toptica/__init__.py | 1 - instruments/toptica/topmode.py | 3 --- instruments/toptica/toptica_utils.py | 1 - instruments/units.py | 2 -- instruments/util_fns.py | 2 -- instruments/yokogawa/__init__.py | 1 - instruments/yokogawa/yokogawa6370.py | 2 -- instruments/yokogawa/yokogawa7651.py | 2 -- 163 files changed, 260 deletions(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index a08b18726..7d9f7e2be 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from . import abstract_instruments from .abstract_instruments import Instrument diff --git a/instruments/abstract_instruments/__init__.py b/instruments/abstract_instruments/__init__.py index 390473b74..5390ce15e 100644 --- a/instruments/abstract_instruments/__init__.py +++ b/instruments/abstract_instruments/__init__.py @@ -4,7 +4,6 @@ Module containing instrument abstract base classes and communication layers """ -from __future__ import absolute_import from .instrument import Instrument from .multimeter import Multimeter diff --git a/instruments/abstract_instruments/comm/__init__.py b/instruments/abstract_instruments/comm/__init__.py index b4274638c..342f1ec6f 100644 --- a/instruments/abstract_instruments/comm/__init__.py +++ b/instruments/abstract_instruments/comm/__init__.py @@ -4,7 +4,6 @@ Module containing communication layers """ -from __future__ import absolute_import from .abstract_comm import AbstractCommunicator diff --git a/instruments/abstract_instruments/comm/abstract_comm.py b/instruments/abstract_instruments/comm/abstract_comm.py index 5f93fb492..66b00da2f 100644 --- a/instruments/abstract_instruments/comm/abstract_comm.py +++ b/instruments/abstract_instruments/comm/abstract_comm.py @@ -6,9 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import abc import codecs diff --git a/instruments/abstract_instruments/comm/file_communicator.py b/instruments/abstract_instruments/comm/file_communicator.py index 46245660e..6cb797947 100644 --- a/instruments/abstract_instruments/comm/file_communicator.py +++ b/instruments/abstract_instruments/comm/file_communicator.py @@ -6,9 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import errno import io diff --git a/instruments/abstract_instruments/comm/gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py index 3341bb5a8..35ba49783 100644 --- a/instruments/abstract_instruments/comm/gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -7,9 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals from enum import Enum import io diff --git a/instruments/abstract_instruments/comm/loopback_communicator.py b/instruments/abstract_instruments/comm/loopback_communicator.py index ca48331c6..1bfd8b390 100644 --- a/instruments/abstract_instruments/comm/loopback_communicator.py +++ b/instruments/abstract_instruments/comm/loopback_communicator.py @@ -7,10 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals import io import sys diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index 4ac94df8c..139290a8b 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -7,9 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import io diff --git a/instruments/abstract_instruments/comm/serial_manager.py b/instruments/abstract_instruments/comm/serial_manager.py index f82d74842..3c995cbe0 100644 --- a/instruments/abstract_instruments/comm/serial_manager.py +++ b/instruments/abstract_instruments/comm/serial_manager.py @@ -10,8 +10,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import weakref import serial diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index 355b683f4..e51558b7d 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -7,9 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import io import socket diff --git a/instruments/abstract_instruments/comm/usb_communicator.py b/instruments/abstract_instruments/comm/usb_communicator.py index 3d7587537..e914bc4c3 100644 --- a/instruments/abstract_instruments/comm/usb_communicator.py +++ b/instruments/abstract_instruments/comm/usb_communicator.py @@ -7,9 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import io from builtins import str diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 29926275c..3a1b83f7b 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -7,9 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import io from builtins import str, bytes diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 8cafee38d..7197e378d 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -9,9 +9,6 @@ # pylint: disable=wrong-import-position -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import io from builtins import str diff --git a/instruments/abstract_instruments/comm/vxi11_communicator.py b/instruments/abstract_instruments/comm/vxi11_communicator.py index 889e05628..7621b4ae5 100644 --- a/instruments/abstract_instruments/comm/vxi11_communicator.py +++ b/instruments/abstract_instruments/comm/vxi11_communicator.py @@ -7,9 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import io import logging diff --git a/instruments/abstract_instruments/electrometer.py b/instruments/abstract_instruments/electrometer.py index 2b655e1de..a1697410e 100644 --- a/instruments/abstract_instruments/electrometer.py +++ b/instruments/abstract_instruments/electrometer.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc from future.utils import with_metaclass diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index 525cbb920..7d682b69f 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc from enum import Enum diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 98e5df010..bb25dcd7c 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -8,9 +8,6 @@ # pylint: disable=wrong-import-position -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals import os import collections diff --git a/instruments/abstract_instruments/multimeter.py b/instruments/abstract_instruments/multimeter.py index 150dfd1a9..2f659fe35 100644 --- a/instruments/abstract_instruments/multimeter.py +++ b/instruments/abstract_instruments/multimeter.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py index 3cc9f4ab2..6f6f7d106 100644 --- a/instruments/abstract_instruments/optical_spectrum_analyzer.py +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc @@ -76,7 +74,6 @@ def channel(self): """ raise NotImplementedError - @property @abc.abstractmethod def start_wl(self): @@ -125,7 +122,6 @@ def bandwidth(self): def bandwidth(self, newval): raise NotImplementedError - # METHODS # @abc.abstractmethod diff --git a/instruments/abstract_instruments/oscilloscope.py b/instruments/abstract_instruments/oscilloscope.py index 16fa662ee..e42574142 100644 --- a/instruments/abstract_instruments/oscilloscope.py +++ b/instruments/abstract_instruments/oscilloscope.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc diff --git a/instruments/abstract_instruments/power_supply.py b/instruments/abstract_instruments/power_supply.py index 9fea6bf9e..15a85978d 100644 --- a/instruments/abstract_instruments/power_supply.py +++ b/instruments/abstract_instruments/power_supply.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc diff --git a/instruments/abstract_instruments/signal_generator/__init__.py b/instruments/abstract_instruments/signal_generator/__init__.py index 6eaa9b7a5..2debc44fd 100644 --- a/instruments/abstract_instruments/signal_generator/__init__.py +++ b/instruments/abstract_instruments/signal_generator/__init__.py @@ -4,7 +4,6 @@ Module containing signal generator abstract base classes """ -from __future__ import absolute_import from .signal_generator import SignalGenerator from .single_channel_sg import SingleChannelSG diff --git a/instruments/abstract_instruments/signal_generator/channel.py b/instruments/abstract_instruments/signal_generator/channel.py index dc6387551..b055c3e13 100644 --- a/instruments/abstract_instruments/signal_generator/channel.py +++ b/instruments/abstract_instruments/signal_generator/channel.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc diff --git a/instruments/abstract_instruments/signal_generator/signal_generator.py b/instruments/abstract_instruments/signal_generator/signal_generator.py index 434a34684..d690dc96d 100644 --- a/instruments/abstract_instruments/signal_generator/signal_generator.py +++ b/instruments/abstract_instruments/signal_generator/signal_generator.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc diff --git a/instruments/abstract_instruments/signal_generator/single_channel_sg.py b/instruments/abstract_instruments/signal_generator/single_channel_sg.py index f478c7013..d7fdba629 100644 --- a/instruments/abstract_instruments/signal_generator/single_channel_sg.py +++ b/instruments/abstract_instruments/signal_generator/single_channel_sg.py @@ -7,8 +7,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from instruments.abstract_instruments.signal_generator import SignalGenerator from instruments.abstract_instruments.signal_generator.channel import SGChannel diff --git a/instruments/agilent/__init__.py b/instruments/agilent/__init__.py index 15796603a..dee4156ff 100644 --- a/instruments/agilent/__init__.py +++ b/instruments/agilent/__init__.py @@ -4,7 +4,6 @@ Module containing Agilent instruments """ -from __future__ import absolute_import from instruments.agilent.agilent33220a import Agilent33220a from instruments.agilent.agilent34410a import Agilent34410a diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 93ad97bd2..cb7fc57d8 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import Enum diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 96ef70a55..5f1a6cdbe 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import map import instruments.units as u diff --git a/instruments/config.py b/instruments/config.py index 240eb5e49..7513573a8 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import warnings @@ -170,5 +168,4 @@ def load_instruments(conf_file_name, conf_path="/"): "{}:\n\t{}.".format(value["uri"], ex), RuntimeWarning) inst_dict[name] = None - return inst_dict diff --git a/instruments/errors.py b/instruments/errors.py index ce4ebe5ff..769dc1dec 100644 --- a/instruments/errors.py +++ b/instruments/errors.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import # CLASSES ##################################################################### diff --git a/instruments/fluke/__init__.py b/instruments/fluke/__init__.py index ac860c293..2d0e41fb8 100644 --- a/instruments/fluke/__init__.py +++ b/instruments/fluke/__init__.py @@ -4,6 +4,5 @@ Module containing Fluke instruments """ -from __future__ import absolute_import from .fluke3000 import Fluke3000 diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index ca7156008..8c16ce815 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -33,8 +33,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time from builtins import range diff --git a/instruments/generic_scpi/__init__.py b/instruments/generic_scpi/__init__.py index f91dac4e8..3dca40432 100644 --- a/instruments/generic_scpi/__init__.py +++ b/instruments/generic_scpi/__init__.py @@ -4,7 +4,6 @@ Module containing generic SCPI instruments """ -from __future__ import absolute_import from .scpi_instrument import SCPIInstrument from .scpi_multimeter import SCPIMultimeter diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 03ba78238..669d11831 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import instruments.units as u diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index 66b10357e..dd3694bc2 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import map diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index 41454d08c..da6f9eb8e 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from enum import Enum diff --git a/instruments/glassman/__init__.py b/instruments/glassman/__init__.py index 18daacfef..4e7582521 100644 --- a/instruments/glassman/__init__.py +++ b/instruments/glassman/__init__.py @@ -4,6 +4,5 @@ Module containing Glassman power supplies """ -from __future__ import absolute_import from .glassmanfr import GlassmanFR diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 0e0bfb186..ed0e6bcf0 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -32,8 +32,6 @@ """ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import bytes, round from struct import unpack diff --git a/instruments/holzworth/__init__.py b/instruments/holzworth/__init__.py index 9b4fd1d3d..06815dab1 100644 --- a/instruments/holzworth/__init__.py +++ b/instruments/holzworth/__init__.py @@ -4,6 +4,5 @@ Module containing Holzworth instruments """ -from __future__ import absolute_import from .holzworth_hs9000 import HS9000 diff --git a/instruments/holzworth/holzworth_hs9000.py b/instruments/holzworth/holzworth_hs9000.py index ae9a36424..06cca8344 100644 --- a/instruments/holzworth/holzworth_hs9000.py +++ b/instruments/holzworth/holzworth_hs9000.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import instruments.units as u diff --git a/instruments/hp/__init__.py b/instruments/hp/__init__.py index 0f7893fe2..71a26416a 100644 --- a/instruments/hp/__init__.py +++ b/instruments/hp/__init__.py @@ -4,7 +4,6 @@ Module containing HP instruments """ -from __future__ import absolute_import from .hp3456a import HP3456a from .hp6624a import HP6624a diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index da6e8fa04..00366e1bf 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -32,8 +32,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time from builtins import range diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index d1d32d288..9ae7700a7 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import Enum diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index f879f2f9a..64303e3c4 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -32,8 +32,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import Enum, IntEnum diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index 4092ff8a4..f1175dc4d 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import instruments.units as u diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index 3ea88f7fc..3f687f814 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -33,8 +33,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time import instruments.units as u diff --git a/instruments/keithley/__init__.py b/instruments/keithley/__init__.py index 36b8eaf48..c00a31f06 100644 --- a/instruments/keithley/__init__.py +++ b/instruments/keithley/__init__.py @@ -4,7 +4,6 @@ Module containing Keithley instruments """ -from __future__ import absolute_import from .keithley195 import Keithley195 from .keithley485 import Keithley485 diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index 3bf334aaa..637fad39b 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time import struct diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index d45265417..ecb96f0cf 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range, map from enum import Enum diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index 7c4aec308..ae7004487 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -33,8 +33,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import bytes from struct import unpack diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index a599c4d85..6f787f9b1 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -33,8 +33,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time import struct diff --git a/instruments/keithley/keithley6220.py b/instruments/keithley/keithley6220.py index 9744a4dfa..cb7497f04 100644 --- a/instruments/keithley/keithley6220.py +++ b/instruments/keithley/keithley6220.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import instruments.units as u diff --git a/instruments/keithley/keithley6514.py b/instruments/keithley/keithley6514.py index d3bab173d..06fcb7011 100644 --- a/instruments/keithley/keithley6514.py +++ b/instruments/keithley/keithley6514.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import map from enum import Enum diff --git a/instruments/lakeshore/__init__.py b/instruments/lakeshore/__init__.py index e9343a49e..238fd4deb 100644 --- a/instruments/lakeshore/__init__.py +++ b/instruments/lakeshore/__init__.py @@ -4,7 +4,6 @@ Module containing Lakeshore instruments """ -from __future__ import absolute_import from instruments.lakeshore.lakeshore340 import Lakeshore340 from instruments.lakeshore.lakeshore370 import Lakeshore370 diff --git a/instruments/lakeshore/lakeshore340.py b/instruments/lakeshore/lakeshore340.py index 9830869fe..14000d60e 100644 --- a/instruments/lakeshore/lakeshore340.py +++ b/instruments/lakeshore/lakeshore340.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range import instruments.units as u diff --git a/instruments/lakeshore/lakeshore370.py b/instruments/lakeshore/lakeshore370.py index fa79e3a18..9205e08f8 100644 --- a/instruments/lakeshore/lakeshore370.py +++ b/instruments/lakeshore/lakeshore370.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range import instruments.units as u diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 649a8448d..2af3e96cb 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import IntEnum diff --git a/instruments/minghe/__init__.py b/instruments/minghe/__init__.py index 997950f6c..4b17756e1 100644 --- a/instruments/minghe/__init__.py +++ b/instruments/minghe/__init__.py @@ -3,5 +3,4 @@ """ Module containing MingHe instruments """ -from __future__ import absolute_import from .mhs5200a import MHS5200 diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py index f22873414..819c926ec 100644 --- a/instruments/minghe/mhs5200a.py +++ b/instruments/minghe/mhs5200a.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import Enum diff --git a/instruments/named_struct.py b/instruments/named_struct.py index 2c6a44834..bbd800c6b 100644 --- a/instruments/named_struct.py +++ b/instruments/named_struct.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import struct from collections import OrderedDict diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index d55e1d62f..462123fa8 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -4,7 +4,6 @@ Module containing Newport instruments """ -from __future__ import absolute_import from .errors import NewportError from .newportesp301 import ( diff --git a/instruments/newport/errors.py b/instruments/newport/errors.py index 6659ae70c..7deb8cf48 100644 --- a/instruments/newport/errors.py +++ b/instruments/newport/errors.py @@ -6,8 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import -from __future__ import division import datetime diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index 601ae4c4b..168bb3c57 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -10,8 +10,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from functools import reduce from time import time, sleep from contextlib import contextmanager diff --git a/instruments/ondax/__init__.py b/instruments/ondax/__init__.py index 5b59b2cb7..435949c86 100644 --- a/instruments/ondax/__init__.py +++ b/instruments/ondax/__init__.py @@ -3,5 +3,4 @@ """ Module containing Ondax Instruments """ -from __future__ import absolute_import from .lm import LM diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index d0e5c8978..b4f4aa33f 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from enum import IntEnum diff --git a/instruments/oxford/__init__.py b/instruments/oxford/__init__.py index a56fb0fbf..8917268e7 100644 --- a/instruments/oxford/__init__.py +++ b/instruments/oxford/__init__.py @@ -5,6 +5,4 @@ """ -from __future__ import absolute_import - from .oxforditc503 import OxfordITC503 diff --git a/instruments/oxford/oxforditc503.py b/instruments/oxford/oxforditc503.py index 4aa452817..67ff89ae9 100644 --- a/instruments/oxford/oxforditc503.py +++ b/instruments/oxford/oxforditc503.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range import instruments.units as u diff --git a/instruments/phasematrix/__init__.py b/instruments/phasematrix/__init__.py index c48687914..1ea5a8a7f 100644 --- a/instruments/phasematrix/__init__.py +++ b/instruments/phasematrix/__init__.py @@ -4,6 +4,5 @@ Module containing Phase Matrix instruments """ -from __future__ import absolute_import from .phasematrix_fsw0020 import PhaseMatrixFSW0020 diff --git a/instruments/phasematrix/phasematrix_fsw0020.py b/instruments/phasematrix/phasematrix_fsw0020.py index 86184ac9e..215d375f8 100644 --- a/instruments/phasematrix/phasematrix_fsw0020.py +++ b/instruments/phasematrix/phasematrix_fsw0020.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from quantities import GHz diff --git a/instruments/picowatt/__init__.py b/instruments/picowatt/__init__.py index 296865e91..e56534b69 100644 --- a/instruments/picowatt/__init__.py +++ b/instruments/picowatt/__init__.py @@ -4,6 +4,5 @@ Module containing Picowatt instruments """ -from __future__ import absolute_import from .picowattavs47 import PicowattAVS47 diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index e72b07ff1..00c080719 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import IntEnum diff --git a/instruments/qubitekk/__init__.py b/instruments/qubitekk/__init__.py index e71003a05..07c6d0fff 100644 --- a/instruments/qubitekk/__init__.py +++ b/instruments/qubitekk/__init__.py @@ -4,7 +4,6 @@ Module containing Qubitekk instruments """ -from __future__ import absolute_import from .cc1 import CC1 from .mc1 import MC1 diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index c7ccf658d..ee5b83ea0 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range, map from enum import Enum diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index 38affab67..63138343d 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -8,7 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import, division from builtins import range, map from enum import Enum diff --git a/instruments/rigol/__init__.py b/instruments/rigol/__init__.py index ebe8bdd04..73a6b401a 100644 --- a/instruments/rigol/__init__.py +++ b/instruments/rigol/__init__.py @@ -4,6 +4,5 @@ Module containing Rigol instruments """ -from __future__ import absolute_import from .rigolds1000 import RigolDS1000Series diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 91acc4777..159176e30 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import Enum diff --git a/instruments/srs/__init__.py b/instruments/srs/__init__.py index 3854c870b..c3572d505 100644 --- a/instruments/srs/__init__.py +++ b/instruments/srs/__init__.py @@ -4,7 +4,6 @@ Module containing Lakeshore instruments """ -from __future__ import absolute_import from .srs345 import SRS345 from .srs830 import SRS830 diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index 26df0825f..0be746929 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from enum import IntEnum diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index 3a5aefe6e..7fc8ea230 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import math import time diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 5f7767ad5..5c2d1c1bd 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from contextlib import contextmanager from builtins import range from enum import Enum diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index 972c0c920..a5f195b6e 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import map from enum import IntEnum diff --git a/instruments/tektronix/__init__.py b/instruments/tektronix/__init__.py index 5718e2d63..4aff3f5fd 100644 --- a/instruments/tektronix/__init__.py +++ b/instruments/tektronix/__init__.py @@ -4,7 +4,6 @@ Module containing Tektronix instruments """ -from __future__ import absolute_import from .tekdpo4104 import ( TekDPO4104, diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index ec2d85eb9..d6612a35d 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import Enum diff --git a/instruments/tektronix/tekdpo4104.py b/instruments/tektronix/tekdpo4104.py index 0baea2240..efb417201 100644 --- a/instruments/tektronix/tekdpo4104.py +++ b/instruments/tektronix/tekdpo4104.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from time import sleep from builtins import range, map diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index 8381818d8..4500e2ce6 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import abc import time diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index 445ccfb4e..b5024fc92 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time from builtins import range, map diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index 62654c89b..deab70d4a 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -33,8 +33,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from functools import reduce import time diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index b54a355ad..18a79b681 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -9,8 +9,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import -from __future__ import unicode_literals import contextlib from io import BytesIO diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index 29ca511b7..16aa42029 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index b9c18ede8..0c7e05078 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 451a0b306..deaf2b8cb 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from builtins import bytes import numpy as np diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 7644f352b..b55dc1353 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import socket import io diff --git a/instruments/tests/test_comm/test_file.py b/instruments/tests/test_comm/test_file.py index 173924087..380db641f 100644 --- a/instruments/tests/test_comm/test_file.py +++ b/instruments/tests/test_comm/test_file.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_comm/test_gpibusb.py b/instruments/tests/test_comm/test_gpibusb.py index 310c634d0..5731eee40 100644 --- a/instruments/tests/test_comm/test_gpibusb.py +++ b/instruments/tests/test_comm/test_gpibusb.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import serial diff --git a/instruments/tests/test_comm/test_loopback.py b/instruments/tests/test_comm/test_loopback.py index 943fd6a0e..b88d7d0ad 100644 --- a/instruments/tests/test_comm/test_loopback.py +++ b/instruments/tests/test_comm/test_loopback.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_comm/test_serial.py b/instruments/tests/test_comm/test_serial.py index 003214b98..1f1502e0c 100644 --- a/instruments/tests/test_comm/test_serial.py +++ b/instruments/tests/test_comm/test_serial.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import serial diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index b7b9867db..67328425f 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import socket diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index 8193487a9..b17747a97 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_comm/test_vxi11.py b/instruments/tests/test_comm/test_vxi11.py index f7925c459..aed550ca5 100644 --- a/instruments/tests/test_comm/test_vxi11.py +++ b/instruments/tests/test_comm/test_vxi11.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_config.py b/instruments/tests/test_config.py index 62711e856..e3ba05110 100644 --- a/instruments/tests/test_config.py +++ b/instruments/tests/test_config.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import, unicode_literals from io import StringIO diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index 696b61480..3abdca8ba 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index 4ae2a4926..e0feb8919 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index 4b2d460bd..a38faff14 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index b789136a7..b624a0ac0 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from builtins import round import instruments.units as u diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index d84218576..c29a78ae1 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index 5d28464d8..0578b9749 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import numpy as np import pytest diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 8fb955a47..5e2372cb9 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index 427384290..e2986a488 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_hp/test_hp6652a.py b/instruments/tests/test_hp/test_hp6652a.py index 738048a3e..713fba3b5 100644 --- a/instruments/tests/test_hp/test_hp6652a.py +++ b/instruments/tests/test_hp/test_hp6652a.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments as ik from instruments.tests import expected_protocol diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index eb6da9257..64060aabb 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 41abc2cdc..3fbdd88a6 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import numpy as np import pytest diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index d75f88229..eea8636b0 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index dc688c929..1bf486e90 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index b89fa7c16..fc093df2d 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index 53fba0ed6..28996d8f0 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_named_struct.py b/instruments/tests/test_named_struct.py index 5b7260e9b..b65943e7b 100644 --- a/instruments/tests/test_named_struct.py +++ b/instruments/tests/test_named_struct.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import, unicode_literals from unittest import TestCase diff --git a/instruments/tests/test_newport/test_newportesp301.py b/instruments/tests/test_newport/test_newportesp301.py index 07541d9a7..1484ba029 100644 --- a/instruments/tests/test_newport/test_newportesp301.py +++ b/instruments/tests/test_newport/test_newportesp301.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments as ik from instruments.tests import expected_protocol diff --git a/instruments/tests/test_ondax/test_lm.py b/instruments/tests/test_ondax/test_lm.py index 8d7477cdc..74c3b8ab7 100644 --- a/instruments/tests/test_ondax/test_lm.py +++ b/instruments/tests/test_ondax/test_lm.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import pytest @@ -156,7 +155,6 @@ def test_apc_enable_not_boolean(): lm.apc.enabled = "foobar" - def test_apc_start(): with expected_protocol( ondax.LM, diff --git a/instruments/tests/test_oxford/test_oxforditc503.py b/instruments/tests/test_oxford/test_oxforditc503.py index 9fe3a4a8c..ca18f0bea 100644 --- a/instruments/tests/test_oxford/test_oxforditc503.py +++ b/instruments/tests/test_oxford/test_oxforditc503.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index 0f3bcc8ed..ec400ecc8 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_picowatt/test_picowatt_avs47.py b/instruments/tests/test_picowatt/test_picowatt_avs47.py index e7bfb9f86..1b4d97885 100644 --- a/instruments/tests/test_picowatt/test_picowatt_avs47.py +++ b/instruments/tests/test_picowatt/test_picowatt_avs47.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_property_factories/__init__.py b/instruments/tests/test_property_factories/__init__.py index 7e23dba58..ad11d1f11 100644 --- a/instruments/tests/test_property_factories/__init__.py +++ b/instruments/tests/test_property_factories/__init__.py @@ -6,8 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import -from __future__ import unicode_literals from io import StringIO diff --git a/instruments/tests/test_property_factories/test_bool_property.py b/instruments/tests/test_property_factories/test_bool_property.py index cb3ab923c..d0bfbe94b 100644 --- a/instruments/tests/test_property_factories/test_bool_property.py +++ b/instruments/tests/test_property_factories/test_bool_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index 097a179ec..9d3b0b4ce 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_property_factories/test_enum_property.py b/instruments/tests/test_property_factories/test_enum_property.py index 55be52610..09f8e68af 100644 --- a/instruments/tests/test_property_factories/test_enum_property.py +++ b/instruments/tests/test_property_factories/test_enum_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from enum import Enum, IntEnum import pytest diff --git a/instruments/tests/test_property_factories/test_int_property.py b/instruments/tests/test_property_factories/test_int_property.py index d92249211..fd0bb8fdc 100644 --- a/instruments/tests/test_property_factories/test_int_property.py +++ b/instruments/tests/test_property_factories/test_int_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_property_factories/test_rproperty.py b/instruments/tests/test_property_factories/test_rproperty.py index 7ba644598..06b09da3b 100644 --- a/instruments/tests/test_property_factories/test_rproperty.py +++ b/instruments/tests/test_property_factories/test_rproperty.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_property_factories/test_string_property.py b/instruments/tests/test_property_factories/test_string_property.py index 02cba688e..9af227f13 100644 --- a/instruments/tests/test_property_factories/test_string_property.py +++ b/instruments/tests/test_property_factories/test_string_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from instruments.util_fns import string_property from . import MockInstrument diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index 9f35711a7..de61b1435 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index 74ed6f4ff..ba932b6b8 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index f65ebfc55..795cbb95b 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index 461e796ce..6fd22358f 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_split_str.py b/instruments/tests/test_split_str.py index 375c17d48..85ff9a58d 100644 --- a/instruments/tests/test_split_str.py +++ b/instruments/tests/test_split_str.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index b921fa46f..b428f0f6a 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import numpy as np diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index 8bfd8e163..4bb2b99bf 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import numpy as np import pytest diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index eedbe8978..83081e0f8 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments.units as u diff --git a/instruments/tests/test_tektronix/test_tektronix_tds224.py b/instruments/tests/test_tektronix/test_tektronix_tds224.py index 8e466215c..52f8a1eb6 100644 --- a/instruments/tests/test_tektronix/test_tektronix_tds224.py +++ b/instruments/tests/test_tektronix/test_tektronix_tds224.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from builtins import bytes import numpy as np diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py index 6106c52ac..da280ff2c 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_apt.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -8,7 +8,6 @@ # pylint: disable=unused-import -from __future__ import absolute_import import struct diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index dabccce2c..62a52f0e8 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 958185915..4293b5ac1 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import pytest import instruments.units as u diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index 746a26bfc..370f62cd1 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from enum import IntEnum import pytest diff --git a/instruments/tests/test_thorlabs/test_utils.py b/instruments/tests/test_thorlabs/test_utils.py index 2752b9095..d12c307db 100644 --- a/instruments/tests/test_thorlabs/test_utils.py +++ b/instruments/tests/test_thorlabs/test_utils.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import import instruments as ik diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index 1b110432f..41a045ebf 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import from datetime import datetime import pytest import instruments.units as u diff --git a/instruments/tests/test_toptica/test_toptica_utils.py b/instruments/tests/test_toptica/test_toptica_utils.py index 790197036..cd517997c 100644 --- a/instruments/tests/test_toptica/test_toptica_utils.py +++ b/instruments/tests/test_toptica/test_toptica_utils.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import datetime import pytest diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index 4878753d0..3a3680b66 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -6,7 +6,6 @@ # IMPORTS #################################################################### -from __future__ import absolute_import from builtins import range from enum import Enum diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index 8a1986fcc..0b382bfbb 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -6,7 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import import struct diff --git a/instruments/thorlabs/__init__.py b/instruments/thorlabs/__init__.py index 336aecb18..ca5efc510 100644 --- a/instruments/thorlabs/__init__.py +++ b/instruments/thorlabs/__init__.py @@ -4,7 +4,6 @@ Module containing Thorlabs instruments """ -from __future__ import absolute_import from .thorlabsapt import ( ThorLabsAPT, APTPiezoStage, APTStrainGaugeReader, APTMotorController diff --git a/instruments/thorlabs/_abstract.py b/instruments/thorlabs/_abstract.py index 50e9d4d95..1f189ddd8 100644 --- a/instruments/thorlabs/_abstract.py +++ b/instruments/thorlabs/_abstract.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import time diff --git a/instruments/thorlabs/_cmds.py b/instruments/thorlabs/_cmds.py index a92ad885c..f2bca1bf0 100644 --- a/instruments/thorlabs/_cmds.py +++ b/instruments/thorlabs/_cmds.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from enum import IntEnum # CLASSES ##################################################################### diff --git a/instruments/thorlabs/_packets.py b/instruments/thorlabs/_packets.py index 5c4171970..012a54716 100644 --- a/instruments/thorlabs/_packets.py +++ b/instruments/thorlabs/_packets.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import struct diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index 4f216c999..44b011dc4 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import IntEnum diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index 007ed23eb..b4d41ee7c 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import logging from collections import defaultdict, namedtuple diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index 79a884c29..4710250ac 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range from enum import IntEnum diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index 145b78568..400d6eb36 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -8,8 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from builtins import range, map from enum import IntEnum, Enum diff --git a/instruments/thorlabs/thorlabsapt.py b/instruments/thorlabs/thorlabsapt.py index 9913d00cf..a4a54814d 100644 --- a/instruments/thorlabs/thorlabsapt.py +++ b/instruments/thorlabs/thorlabsapt.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import re import struct @@ -578,7 +576,6 @@ def motor_model(self, newval): self._set_scale(newval) self._motor_model = newval - # MOTOR COMMANDS # @property diff --git a/instruments/toptica/__init__.py b/instruments/toptica/__init__.py index 1b98505bf..4bafc68e9 100644 --- a/instruments/toptica/__init__.py +++ b/instruments/toptica/__init__.py @@ -4,6 +4,5 @@ Module containing Toptica instruments """ -from __future__ import absolute_import from .topmode import TopMode diff --git a/instruments/toptica/topmode.py b/instruments/toptica/topmode.py index 49126170e..fe8821668 100644 --- a/instruments/toptica/topmode.py +++ b/instruments/toptica/topmode.py @@ -8,9 +8,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division -from __future__ import unicode_literals from builtins import range, map from enum import IntEnum diff --git a/instruments/toptica/toptica_utils.py b/instruments/toptica/toptica_utils.py index 0ffb424e5..f4a64251e 100644 --- a/instruments/toptica/toptica_utils.py +++ b/instruments/toptica/toptica_utils.py @@ -5,7 +5,6 @@ Contains common utility functions for Toptica-brand instruments """ -from __future__ import absolute_import from datetime import datetime diff --git a/instruments/units.py b/instruments/units.py index 45d90bc37..7aba67008 100644 --- a/instruments/units.py +++ b/instruments/units.py @@ -8,8 +8,6 @@ # pylint: disable=unused-wildcard-import, wildcard-import -from __future__ import absolute_import -from __future__ import division from quantities import * from quantities.unitquantity import IrreducibleUnit diff --git a/instruments/util_fns.py b/instruments/util_fns.py index c0b0a9948..7911ff088 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division import re diff --git a/instruments/yokogawa/__init__.py b/instruments/yokogawa/__init__.py index 83e5adb23..673e8d26f 100644 --- a/instruments/yokogawa/__init__.py +++ b/instruments/yokogawa/__init__.py @@ -4,7 +4,6 @@ Module containing Yokogawa instruments """ -from __future__ import absolute_import from .yokogawa6370 import Yokogawa6370 from .yokogawa7651 import Yokogawa7651 diff --git a/instruments/yokogawa/yokogawa6370.py b/instruments/yokogawa/yokogawa6370.py index 4794c684e..e1d28cbd4 100644 --- a/instruments/yokogawa/yokogawa6370.py +++ b/instruments/yokogawa/yokogawa6370.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from enum import IntEnum, Enum diff --git a/instruments/yokogawa/yokogawa7651.py b/instruments/yokogawa/yokogawa7651.py index f1862c25b..f0ec10aed 100644 --- a/instruments/yokogawa/yokogawa7651.py +++ b/instruments/yokogawa/yokogawa7651.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from __future__ import absolute_import -from __future__ import division from enum import IntEnum From d430271d726cee471fdb0f5c5619f0c0a569e163 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Mon, 3 Feb 2020 22:49:17 -0500 Subject: [PATCH 039/108] Remove py2/3 compat code (#235) * Remove all builtins imports * Remove all inheritance to "object" * Remove with_metaclass * Remove `future` package --- doc/examples/ex_hp3456.py | 2 -- doc/source/devguide/code_style.rst | 6 ++--- doc/source/intro.rst | 2 -- .../comm/abstract_comm.py | 4 +-- .../comm/file_communicator.py | 2 -- .../comm/gpib_communicator.py | 1 - .../comm/loopback_communicator.py | 2 -- .../comm/serial_communicator.py | 2 -- .../comm/socket_communicator.py | 1 - .../comm/usb_communicator.py | 1 - .../comm/usbtmc_communicator.py | 3 +-- .../comm/visa_communicator.py | 14 ++-------- .../comm/vxi11_communicator.py | 2 -- .../abstract_instruments/electrometer.py | 3 +-- .../function_generator.py | 7 ++--- .../abstract_instruments/instrument.py | 21 +++------------ .../abstract_instruments/multimeter.py | 4 +-- .../optical_spectrum_analyzer.py | 6 ++--- .../abstract_instruments/oscilloscope.py | 8 +++--- .../abstract_instruments/power_supply.py | 7 ++--- .../signal_generator/channel.py | 5 +--- .../signal_generator/signal_generator.py | 4 +-- instruments/agilent/agilent33220a.py | 4 +-- instruments/agilent/agilent34410a.py | 5 +--- instruments/config.py | 2 -- instruments/fluke/fluke3000.py | 5 +--- instruments/generic_scpi/scpi_instrument.py | 5 +--- instruments/glassman/glassmanfr.py | 5 +--- instruments/hp/hp3456a.py | 5 +--- instruments/hp/hp6624a.py | 5 +--- instruments/hp/hp6632b.py | 4 +-- instruments/hp/hpe3631a.py | 2 +- instruments/keithley/keithley2182.py | 2 -- instruments/keithley/keithley485.py | 5 +--- instruments/keithley/keithley6514.py | 5 +--- instruments/lakeshore/lakeshore340.py | 7 ++--- instruments/lakeshore/lakeshore370.py | 7 ++--- instruments/lakeshore/lakeshore475.py | 5 +--- instruments/minghe/mhs5200a.py | 4 +-- instruments/named_struct.py | 4 +-- instruments/newport/newportesp301.py | 11 +++----- instruments/ondax/lm.py | 8 +++--- instruments/oxford/oxforditc503.py | 7 ++--- instruments/picowatt/picowattavs47.py | 7 ++--- instruments/qubitekk/cc1.py | 6 ++--- instruments/qubitekk/mc1.py | 5 +--- instruments/rigol/rigolds1000.py | 2 -- instruments/srs/srs830.py | 6 ++--- instruments/srs/srsctc100.py | 3 +-- instruments/srs/srsdg645.py | 11 +++----- instruments/tektronix/tekawg2000.py | 6 ++--- instruments/tektronix/tekdpo4104.py | 3 +-- instruments/tektronix/tekdpo70000.py | 8 ++---- instruments/tektronix/tektds224.py | 3 +-- instruments/tektronix/tektds5xx.py | 13 ++++------ instruments/tests/__init__.py | 8 +----- .../tests/test_agilent/test_agilent_34410a.py | 2 -- instruments/tests/test_base_instrument.py | 4 +-- .../tests/test_glassman/test_glassmanfr.py | 5 +--- .../tests/test_property_factories/__init__.py | 2 +- .../test_tektronix/test_tektronix_tds224.py | 2 -- instruments/tests/test_util_fns.py | 26 +++++++++---------- instruments/thorlabs/_packets.py | 2 +- instruments/thorlabs/lcc25.py | 5 +--- instruments/thorlabs/pm100usb.py | 2 +- instruments/thorlabs/sc10.py | 6 ++--- instruments/thorlabs/tc200.py | 10 +++---- instruments/thorlabs/thorlabsapt.py | 6 ++--- instruments/toptica/topmode.py | 8 +++--- requirements.txt | 1 - setup.py | 1 - 71 files changed, 104 insertions(+), 273 deletions(-) diff --git a/doc/examples/ex_hp3456.py b/doc/examples/ex_hp3456.py index bd060c504..558e8dd9a 100644 --- a/doc/examples/ex_hp3456.py +++ b/doc/examples/ex_hp3456.py @@ -1,8 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function - import logging import time import instruments as ik diff --git a/doc/source/devguide/code_style.rst b/doc/source/devguide/code_style.rst index 11247bde7..5d50d4b5e 100644 --- a/doc/source/devguide/code_style.rst +++ b/doc/source/devguide/code_style.rst @@ -92,7 +92,7 @@ For example:: class SomeInstrument(Instrument): # If there's a more appropriate base class, please use it # in preference to object! - class Channel(object): + class Channel: # We use a three-argument initializer, # to remember which instrument this channel belongs to, # as well as its index or label on that instrument. @@ -114,7 +114,7 @@ and appears with the instrument in documentation. Since this convention is somewhat recent, you may find older code that uses a style more like this:: - class _SomeInstrumentChannel(object): + class _SomeInstrumentChannel: # stuff class SomeInstrument(Instrument): @@ -126,7 +126,7 @@ This can be redefined in a backwards-compatible way by bringing the channel class inside, then defining a new module-level variable for the old name:: class SomeInstrument(Instrument): - class Channel(object): + class Channel: # stuff @property diff --git a/doc/source/intro.rst b/doc/source/intro.rst index b2f2b146e..42f62ed43 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -30,7 +30,6 @@ $ pip install -r requirements.txt - NumPy - `PySerial`_ - `quantities`_ -- `future`_ - `python-vxi11`_ - `PyUSB`_ (version 1.0a or higher, required for raw USB support) - `python-usbtmc`_ @@ -42,7 +41,6 @@ Optional Dependencies .. _PySerial: http://pyserial.sourceforge.net/ .. _quantities: http://pythonhosted.org/quantities/ -.. _future: https://pypi.python.org/pypi/future .. _ruamel.yaml: http://yaml.readthedocs.io .. _PyUSB: http://sourceforge.net/apps/trac/pyusb/ .. _PyVISA: http://pyvisa.sourceforge.net/ diff --git a/instruments/abstract_instruments/comm/abstract_comm.py b/instruments/abstract_instruments/comm/abstract_comm.py index 66b00da2f..8915f4791 100644 --- a/instruments/abstract_instruments/comm/abstract_comm.py +++ b/instruments/abstract_instruments/comm/abstract_comm.py @@ -12,12 +12,10 @@ import logging import struct -from future.utils import with_metaclass - # CLASSES #################################################################### -class AbstractCommunicator(with_metaclass(abc.ABCMeta, object)): +class AbstractCommunicator(metaclass=abc.ABCMeta): """ Abstract base class for electrometer instruments. diff --git a/instruments/abstract_instruments/comm/file_communicator.py b/instruments/abstract_instruments/comm/file_communicator.py index 6cb797947..060b66ba9 100644 --- a/instruments/abstract_instruments/comm/file_communicator.py +++ b/instruments/abstract_instruments/comm/file_communicator.py @@ -12,8 +12,6 @@ import time import logging -from builtins import str, bytes - from instruments.abstract_instruments.comm import AbstractCommunicator logger = logging.getLogger(__name__) diff --git a/instruments/abstract_instruments/comm/gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py index 35ba49783..58229927e 100644 --- a/instruments/abstract_instruments/comm/gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -12,7 +12,6 @@ import io import time -from builtins import chr, str, bytes import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator diff --git a/instruments/abstract_instruments/comm/loopback_communicator.py b/instruments/abstract_instruments/comm/loopback_communicator.py index 1bfd8b390..3f89ff441 100644 --- a/instruments/abstract_instruments/comm/loopback_communicator.py +++ b/instruments/abstract_instruments/comm/loopback_communicator.py @@ -11,8 +11,6 @@ import io import sys -from builtins import input, bytes, str - from instruments.abstract_instruments.comm import AbstractCommunicator # CLASSES ##################################################################### diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index 139290a8b..d456bb5a6 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -9,8 +9,6 @@ import io - -from builtins import bytes, str import serial import instruments.units as u diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index e51558b7d..e3f082ba3 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -11,7 +11,6 @@ import io import socket -from builtins import str, bytes import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator diff --git a/instruments/abstract_instruments/comm/usb_communicator.py b/instruments/abstract_instruments/comm/usb_communicator.py index e914bc4c3..27770ef27 100644 --- a/instruments/abstract_instruments/comm/usb_communicator.py +++ b/instruments/abstract_instruments/comm/usb_communicator.py @@ -9,7 +9,6 @@ import io -from builtins import str from instruments.abstract_instruments.comm import AbstractCommunicator diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 3a1b83f7b..9e2e6c1ad 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -9,13 +9,12 @@ import io -from builtins import str, bytes import usbtmc -import instruments.units as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units +import instruments.units as u # CLASSES ##################################################################### diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 7197e378d..9700cf134 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -7,24 +7,14 @@ # IMPORTS ##################################################################### -# pylint: disable=wrong-import-position - import io -from builtins import str -import instruments.units as u +import visa from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units - -if not getattr(__builtins__, "WindowsError", None): - class WindowsError(OSError): - pass -try: - import visa -except (ImportError, WindowsError, OSError): - visa = None +import instruments.units as u # CLASSES ##################################################################### diff --git a/instruments/abstract_instruments/comm/vxi11_communicator.py b/instruments/abstract_instruments/comm/vxi11_communicator.py index 7621b4ae5..b5f9efa2f 100644 --- a/instruments/abstract_instruments/comm/vxi11_communicator.py +++ b/instruments/abstract_instruments/comm/vxi11_communicator.py @@ -11,8 +11,6 @@ import io import logging -from builtins import str, bytes - import vxi11 from instruments.abstract_instruments.comm import AbstractCommunicator diff --git a/instruments/abstract_instruments/electrometer.py b/instruments/abstract_instruments/electrometer.py index a1697410e..26aa251b8 100644 --- a/instruments/abstract_instruments/electrometer.py +++ b/instruments/abstract_instruments/electrometer.py @@ -8,14 +8,13 @@ import abc -from future.utils import with_metaclass from instruments.abstract_instruments import Instrument # CLASSES ##################################################################### -class Electrometer(with_metaclass(abc.ABCMeta, Instrument)): +class Electrometer(Instrument, metaclass=abc.ABCMeta): """ Abstract base class for electrometer instruments. diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index 7d682b69f..46d4563de 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -10,9 +10,6 @@ import abc from enum import Enum -from builtins import range -from future.utils import with_metaclass - from instruments.abstract_instruments import Instrument import instruments.units as u from instruments.util_fns import assume_units, ProxyList @@ -20,7 +17,7 @@ # CLASSES ##################################################################### -class FunctionGenerator(with_metaclass(abc.ABCMeta, Instrument)): +class FunctionGenerator(Instrument, metaclass=abc.ABCMeta): """ Abstract base class for function generator instruments. @@ -34,7 +31,7 @@ def __init__(self, filelike): self._channel_count = 1 # pylint:disable=protected-access - class Channel(with_metaclass(abc.ABCMeta, object)): + class Channel(metaclass=abc.ABCMeta): """ Abstract base class for physical channels on a function generator. diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index bb25dcd7c..0a8f846a8 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -6,35 +6,20 @@ # IMPORTS ##################################################################### -# pylint: disable=wrong-import-position - import os import collections import socket +import urllib.parse as parse -from builtins import map from serial import SerialException from serial.tools.list_ports import comports - -from future.standard_library import install_aliases import numpy as np - +import visa import usb import usb.core import usb.util -install_aliases() -import urllib.parse as parse # pylint: disable=wrong-import-order,import-error - -if not getattr(__builtins__, "WindowsError", None): - class WindowsError(OSError): - pass -try: - import visa -except (ImportError, WindowsError, OSError): - visa = None - from instruments.abstract_instruments.comm import ( SocketCommunicator, USBCommunicator, VisaCommunicator, FileCommunicator, LoopbackCommunicator, GPIBCommunicator, AbstractCommunicator, @@ -54,7 +39,7 @@ class WindowsError(OSError): # CLASSES ##################################################################### -class Instrument(object): +class Instrument: """ This is the base instrument class from which all others are derived from. diff --git a/instruments/abstract_instruments/multimeter.py b/instruments/abstract_instruments/multimeter.py index 2f659fe35..a75acec13 100644 --- a/instruments/abstract_instruments/multimeter.py +++ b/instruments/abstract_instruments/multimeter.py @@ -9,14 +9,12 @@ import abc -from future.utils import with_metaclass - from instruments.abstract_instruments import Instrument # CLASSES ##################################################################### -class Multimeter(with_metaclass(abc.ABCMeta, Instrument)): +class Multimeter(Instrument, metaclass=abc.ABCMeta): """ Abstract base class for multimeter instruments. diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py index 6f6f7d106..6bd4e64d1 100644 --- a/instruments/abstract_instruments/optical_spectrum_analyzer.py +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -9,14 +9,12 @@ import abc -from future.utils import with_metaclass - from instruments.abstract_instruments import Instrument # CLASSES ##################################################################### -class OSAChannel(with_metaclass(abc.ABCMeta, object)): +class OSAChannel(metaclass=abc.ABCMeta): """ Abstract base class for physical channels on an optical spectrum analyzer. @@ -54,7 +52,7 @@ def data(self, bin_format=True): raise NotImplementedError -class OpticalSpectrumAnalyzer(with_metaclass(abc.ABCMeta, Instrument)): +class OpticalSpectrumAnalyzer(Instrument, metaclass=abc.ABCMeta): """ Abstract base class for optical spectrum analyzer instruments. diff --git a/instruments/abstract_instruments/oscilloscope.py b/instruments/abstract_instruments/oscilloscope.py index e42574142..4d544a2c8 100644 --- a/instruments/abstract_instruments/oscilloscope.py +++ b/instruments/abstract_instruments/oscilloscope.py @@ -9,14 +9,12 @@ import abc -from future.utils import with_metaclass - from instruments.abstract_instruments import Instrument # CLASSES ##################################################################### -class OscilloscopeDataSource(with_metaclass(abc.ABCMeta, object)): +class OscilloscopeDataSource(metaclass=abc.ABCMeta): """ Abstract base class for data sources (physical channels, math, ref) on @@ -79,7 +77,7 @@ def read_waveform(self, bin_format=True): raise NotImplementedError -class OscilloscopeChannel(with_metaclass(abc.ABCMeta, object)): +class OscilloscopeChannel(metaclass=abc.ABCMeta): """ Abstract base class for physical channels on an oscilloscope. @@ -107,7 +105,7 @@ def coupling(self, newval): raise NotImplementedError -class Oscilloscope(with_metaclass(abc.ABCMeta, Instrument)): +class Oscilloscope(Instrument, metaclass=abc.ABCMeta): """ Abstract base class for oscilloscope instruments. diff --git a/instruments/abstract_instruments/power_supply.py b/instruments/abstract_instruments/power_supply.py index 15a85978d..f1c309ad7 100644 --- a/instruments/abstract_instruments/power_supply.py +++ b/instruments/abstract_instruments/power_supply.py @@ -6,17 +6,14 @@ # IMPORTS ##################################################################### - import abc -from future.utils import with_metaclass - from instruments.abstract_instruments import Instrument # CLASSES ##################################################################### -class PowerSupplyChannel(with_metaclass(abc.ABCMeta, object)): +class PowerSupplyChannel(metaclass=abc.ABCMeta): """ Abstract base class for power supply output channels. @@ -88,7 +85,7 @@ def output(self, newval): pass -class PowerSupply(with_metaclass(abc.ABCMeta, Instrument)): +class PowerSupply(Instrument, metaclass=abc.ABCMeta): """ Abstract base class for power supply instruments. diff --git a/instruments/abstract_instruments/signal_generator/channel.py b/instruments/abstract_instruments/signal_generator/channel.py index b055c3e13..c65b38937 100644 --- a/instruments/abstract_instruments/signal_generator/channel.py +++ b/instruments/abstract_instruments/signal_generator/channel.py @@ -6,15 +6,12 @@ # IMPORTS ##################################################################### - import abc -from future.utils import with_metaclass - # CLASSES ##################################################################### -class SGChannel(with_metaclass(abc.ABCMeta, object)): +class SGChannel(metaclass=abc.ABCMeta): """ Python abstract base class representing a single channel for a signal diff --git a/instruments/abstract_instruments/signal_generator/signal_generator.py b/instruments/abstract_instruments/signal_generator/signal_generator.py index d690dc96d..29ce632e5 100644 --- a/instruments/abstract_instruments/signal_generator/signal_generator.py +++ b/instruments/abstract_instruments/signal_generator/signal_generator.py @@ -9,14 +9,12 @@ import abc -from future.utils import with_metaclass - from instruments.abstract_instruments import Instrument # CLASSES ##################################################################### -class SignalGenerator(with_metaclass(abc.ABCMeta, Instrument)): +class SignalGenerator(Instrument, metaclass=abc.ABCMeta): """ Python abstract base class for signal generators (eg microwave sources). diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index cb7fc57d8..68179d393 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -6,13 +6,11 @@ # IMPORTS ##################################################################### -from builtins import range - from enum import Enum -import instruments.units as u from instruments.generic_scpi import SCPIFunctionGenerator +import instruments.units as u from instruments.util_fns import ( enum_property, int_property, bool_property, assume_units ) diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 5f1a6cdbe..ea0c4a709 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -6,11 +6,8 @@ # IMPORTS ##################################################################### -from builtins import map - -import instruments.units as u - from instruments.generic_scpi import SCPIMultimeter +import instruments.units as u # CLASSES ##################################################################### diff --git a/instruments/config.py b/instruments/config.py index 7513573a8..d477974e8 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -21,8 +21,6 @@ # the import-error check should be re-enabled. import ruamel_yaml as yaml # pylint: disable=import-error -from future.builtins import str - import instruments.units as u from instruments.util_fns import setattr_expression, split_unit_str diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index 8c16ce815..25f3437f6 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -34,13 +34,10 @@ # IMPORTS ##################################################################### import time -from builtins import range - from enum import Enum -import instruments.units as u - from instruments.abstract_instruments import Multimeter +import instruments.units as u # CLASSES ##################################################################### diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index dd3694bc2..42c9b7c31 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -6,13 +6,10 @@ # IMPORTS ##################################################################### - -from builtins import map - from enum import IntEnum -import instruments.units as u from instruments.abstract_instruments import Instrument +import instruments.units as u from instruments.util_fns import assume_units # CLASSES ##################################################################### diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index ed0e6bcf0..3ea060ede 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -32,17 +32,14 @@ """ # IMPORTS ##################################################################### -from builtins import bytes, round from struct import unpack - from enum import Enum -import instruments.units as u - from instruments.abstract_instruments import ( PowerSupply, PowerSupplyChannel ) +import instruments.units as u from instruments.util_fns import assume_units # CLASSES ##################################################################### diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index 00366e1bf..f628451be 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -33,13 +33,10 @@ # IMPORTS ##################################################################### import time -from builtins import range - from enum import Enum, IntEnum -import instruments.units as u - from instruments.abstract_instruments import Multimeter +import instruments.units as u from instruments.util_fns import assume_units, bool_property, enum_property # CLASSES ##################################################################### diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index 9ae7700a7..638074ef2 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -6,16 +6,13 @@ # IMPORTS ##################################################################### - -from builtins import range from enum import Enum -import instruments.units as u - from instruments.abstract_instruments import ( PowerSupply, PowerSupplyChannel ) +import instruments.units as u from instruments.util_fns import ProxyList, unitful_property, bool_property # CLASSES ##################################################################### diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index 64303e3c4..958911685 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -32,13 +32,11 @@ # IMPORTS ##################################################################### -from builtins import range - from enum import Enum, IntEnum -import instruments.units as u from instruments.generic_scpi.scpi_instrument import SCPIInstrument from instruments.hp.hp6652a import HP6652a +import instruments.units as u from instruments.util_fns import (unitful_property, unitless_property, bool_property, enum_property, int_property) diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index 3f687f814..a3e6e6a5b 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -91,7 +91,7 @@ def __init__(self, filelike): # INNER CLASSES # - class Channel(object): + class Channel: """ Class representing a power output channel on the HPe3631a. diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index ecb96f0cf..e43a9a23e 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from builtins import range, map - from enum import Enum import instruments.units as u diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index ae7004487..365677341 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -33,14 +33,11 @@ # IMPORTS ##################################################################### -from builtins import bytes from struct import unpack - from enum import Enum -import instruments.units as u - from instruments.abstract_instruments import Instrument +import instruments.units as u # CLASSES ##################################################################### diff --git a/instruments/keithley/keithley6514.py b/instruments/keithley/keithley6514.py index 06fcb7011..ec97f6b2d 100644 --- a/instruments/keithley/keithley6514.py +++ b/instruments/keithley/keithley6514.py @@ -6,14 +6,11 @@ # IMPORTS ##################################################################### -from builtins import map - from enum import Enum -import instruments.units as u - from instruments.abstract_instruments import Electrometer from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import bool_property, enum_property # CLASSES ##################################################################### diff --git a/instruments/lakeshore/lakeshore340.py b/instruments/lakeshore/lakeshore340.py index 14000d60e..03b2cf190 100644 --- a/instruments/lakeshore/lakeshore340.py +++ b/instruments/lakeshore/lakeshore340.py @@ -6,11 +6,8 @@ # IMPORTS ##################################################################### -from builtins import range - -import instruments.units as u - from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -32,7 +29,7 @@ class Lakeshore340(SCPIInstrument): # INNER CLASSES ## - class Sensor(object): + class Sensor: """ Class representing a sensor attached to the Lakeshore 340. diff --git a/instruments/lakeshore/lakeshore370.py b/instruments/lakeshore/lakeshore370.py index 9205e08f8..e19bc2981 100644 --- a/instruments/lakeshore/lakeshore370.py +++ b/instruments/lakeshore/lakeshore370.py @@ -6,11 +6,8 @@ # IMPORTS ##################################################################### -from builtins import range - -import instruments.units as u - from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -36,7 +33,7 @@ def __init__(self, filelike): # INNER CLASSES ## - class Channel(object): + class Channel: """ Class representing a sensor attached to the Lakeshore 370. diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 2af3e96cb..d8eb80ec6 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -6,13 +6,10 @@ # IMPORTS ##################################################################### - -from builtins import range from enum import IntEnum -import instruments.units as u - from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import assume_units, bool_property # CONSTANTS ################################################################### diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py index 819c926ec..8d737b3a1 100644 --- a/instruments/minghe/mhs5200a.py +++ b/instruments/minghe/mhs5200a.py @@ -8,12 +8,10 @@ # IMPORTS ##################################################################### - -from builtins import range from enum import Enum -import instruments.units as u from instruments.abstract_instruments import FunctionGenerator +import instruments.units as u from instruments.util_fns import ProxyList, assume_units # CLASSES ##################################################################### diff --git a/instruments/named_struct.py b/instruments/named_struct.py index bbd800c6b..61a85ca0b 100644 --- a/instruments/named_struct.py +++ b/instruments/named_struct.py @@ -10,8 +10,6 @@ import struct from collections import OrderedDict -from future.utils import with_metaclass - # DESIGN NOTES ################################################################ # This class uses the Django-like strategy described at @@ -224,7 +222,7 @@ def __new__(mcs, name, bases, attrs): return cls -class NamedStruct(with_metaclass(HasFields, object)): +class NamedStruct(metaclass=HasFields): """ Represents a C-style struct with one or more named fields, useful for packing and unpacking serialized data documented diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index 168bb3c57..47a93d2d5 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -10,17 +10,14 @@ # IMPORTS ##################################################################### -from functools import reduce -from time import time, sleep from contextlib import contextmanager - -from builtins import range, map from enum import IntEnum - -import instruments.units as u +from functools import reduce +from time import time, sleep from instruments.abstract_instruments import Instrument from instruments.newport.errors import NewportError +import instruments.units as u from instruments.util_fns import assume_units, ProxyList # ENUMS ####################################################################### @@ -293,7 +290,7 @@ def run_program(self, program_id): # pylint: disable=too-many-public-methods,too-many-instance-attributes -class NewportESP301Axis(object): +class NewportESP301Axis: """ Encapsulates communication concerning a single axis diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index b4f4aa33f..85853f0cf 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -53,7 +53,7 @@ class Status(IntEnum): # INNER CLASSES # - class _AutomaticCurrentControl(object): + class _AutomaticCurrentControl: """ Options and functions related to the laser diode's automatic current control driver. @@ -142,7 +142,7 @@ def off(self): """ self._parent.sendcmd("lcoff") - class _AutomaticPowerControl(object): + class _AutomaticPowerControl: """ Options and functions related to the laser diode's automatic power control driver. @@ -231,7 +231,7 @@ def stop(self): """ self._parent.sendcmd("cps") - class _Modulation(object): + class _Modulation: """ Options and functions related to the laser's optical output modulation. @@ -327,7 +327,7 @@ def enabled(self, newval): self._parent.sendcmd("ctm") self._enabled = newval - class _ThermoElectricCooler(object): + class _ThermoElectricCooler: """ Options and functions relating to the laser diode's thermo electric cooler. diff --git a/instruments/oxford/oxforditc503.py b/instruments/oxford/oxforditc503.py index 67ff89ae9..5772498fd 100644 --- a/instruments/oxford/oxforditc503.py +++ b/instruments/oxford/oxforditc503.py @@ -6,11 +6,8 @@ # IMPORTS ##################################################################### -from builtins import range - -import instruments.units as u - from instruments.abstract_instruments import Instrument +import instruments.units as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -36,7 +33,7 @@ def __init__(self, filelike): # INNER CLASSES # - class Sensor(object): + class Sensor: """ Class representing a probe sensor on the Oxford ITC 503. diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index 00c080719..4bc37eed9 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -6,13 +6,10 @@ # IMPORTS ##################################################################### - -from builtins import range from enum import IntEnum -import instruments.units as u - from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import (enum_property, bool_property, int_property, ProxyList) @@ -38,7 +35,7 @@ def __init__(self, filelike): # INNER CLASSES # - class Sensor(object): + class Sensor: """ Class representing a sensor on the PicowattAVS47 diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index ee5b83ea0..ff7f83d9b 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -8,12 +8,10 @@ # IMPORTS ##################################################################### -from builtins import range, map - from enum import Enum -import instruments.units as u from instruments.generic_scpi.scpi_instrument import SCPIInstrument +import instruments.units as u from instruments.util_fns import ( ProxyList, assume_units, split_unit_str ) @@ -88,7 +86,7 @@ class _TriggerModeOld(Enum): # INNER CLASSES # - class Channel(object): + class Channel: """ Class representing a channel on the Qubitekk CC1. diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index 63138343d..029f8f91c 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -8,13 +8,10 @@ # IMPORTS ##################################################################### - -from builtins import range, map from enum import Enum -import instruments.units as u - from instruments.abstract_instruments import Instrument +import instruments.units as u from instruments.util_fns import ( int_property, enum_property, unitful_property, assume_units ) diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 159176e30..9885d4780 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -6,8 +6,6 @@ # IMPORTS ##################################################################### -from builtins import range - from enum import Enum from instruments.abstract_instruments import ( diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index 7fc8ea230..1f80a28c4 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -10,19 +10,17 @@ import math import time import warnings - -from builtins import range, map from enum import Enum, IntEnum import numpy as np -import instruments.units as u -from instruments.generic_scpi import SCPIInstrument from instruments.abstract_instruments.comm import ( GPIBCommunicator, SerialCommunicator, LoopbackCommunicator ) +from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import ( bool_property, bounded_unitful_property, enum_property, unitful_property ) diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 5c2d1c1bd..0fa5fa50e 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -7,7 +7,6 @@ # IMPORTS ##################################################################### from contextlib import contextmanager -from builtins import range from enum import Enum import numpy as np @@ -57,7 +56,7 @@ class SensorType(Enum): diode = 'Diode' rox = 'ROX' - class Channel(object): + class Channel: """ Represents an input or output channel on an SRS CTC-100 cryogenic diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index a5f195b6e..f15cbb688 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -6,20 +6,17 @@ # IMPORTS ##################################################################### -from builtins import map - from enum import IntEnum -import instruments.units as u - -from instruments.generic_scpi import SCPIInstrument from instruments.abstract_instruments.comm import GPIBCommunicator +from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import assume_units, ProxyList # CLASSES ##################################################################### -class _SRSDG645Channel(object): +class _SRSDG645Channel: """ Class representing a sensor attached to the SRS DG645. @@ -169,7 +166,7 @@ class TriggerSource(IntEnum): # INNER CLASSES # - class Output(object): + class Output: """ An output from the DDG. diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index d6612a35d..6e08c4e9f 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -6,14 +6,12 @@ # IMPORTS ##################################################################### -from builtins import range - from enum import Enum import numpy as np -import instruments.units as u from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import assume_units, ProxyList # CLASSES ##################################################################### @@ -28,7 +26,7 @@ class TekAWG2000(SCPIInstrument): # INNER CLASSES # - class Channel(object): + class Channel: """ Class representing a physical channel on the Tektronix AWG 2000 diff --git a/instruments/tektronix/tekdpo4104.py b/instruments/tektronix/tekdpo4104.py index efb417201..fe046470e 100644 --- a/instruments/tektronix/tekdpo4104.py +++ b/instruments/tektronix/tekdpo4104.py @@ -8,9 +8,8 @@ from time import sleep -from builtins import range, map - from enum import Enum + import numpy as np from instruments.abstract_instruments import ( diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index 4500e2ce6..f9a8bfd5b 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -6,19 +6,15 @@ # IMPORTS ##################################################################### - import abc -import time - -from builtins import range from enum import Enum - -import instruments.units as u +import time from instruments.abstract_instruments import ( Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource ) from instruments.generic_scpi import SCPIInstrument +import instruments.units as u from instruments.util_fns import ( enum_property, string_property, int_property, unitful_property, unitless_property, bool_property, ProxyList diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index b5024fc92..f5e0e985b 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -8,11 +8,9 @@ import time -from builtins import range, map from enum import Enum import numpy as np -import instruments.units as u from instruments.abstract_instruments import ( OscilloscopeChannel, @@ -21,6 +19,7 @@ ) from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList +import instruments.units as u # CLASSES ##################################################################### diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index deab70d4a..6a2f9e818 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -33,16 +33,13 @@ # IMPORTS ##################################################################### -from functools import reduce - -import time -from time import sleep from datetime import datetime +from enum import Enum +from functools import reduce import operator import struct - -from builtins import range, map, round -from enum import Enum +import time +from time import sleep import numpy as np @@ -57,7 +54,7 @@ # CLASSES ##################################################################### -class _TekTDS5xxMeasurement(object): +class _TekTDS5xxMeasurement: """ Class representing a measurement channel on the Tektronix TDS5xx diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index 18a79b681..5cbc3c6be 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -12,13 +12,7 @@ import contextlib from io import BytesIO - -from builtins import bytes, str - -try: - from unittest import mock # from Python 3.3 onward, this is in the stdlib -except ImportError: - import mock +from unittest import mock # FUNCTIONS ################################################################## diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index deaf2b8cb..0a922fedc 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -6,8 +6,6 @@ # IMPORTS #################################################################### -from builtins import bytes - import numpy as np import instruments as ik diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index b55dc1353..84c65e5a5 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -9,8 +9,6 @@ import socket import io - -from builtins import bytes import serial from serial.tools.list_ports_common import ListPortInfo @@ -113,7 +111,7 @@ def test_instrument_open_serial(mock_serial_manager): ) -class fake_serial(object): +class fake_serial: """ Create a fake serial.Serial() object so that tests can be run without accessing a non-existant port. diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index b624a0ac0..222a609d2 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -6,12 +6,9 @@ # IMPORTS #################################################################### -from builtins import round - -import instruments.units as u - import instruments as ik from instruments.tests import expected_protocol +import instruments.units as u # TESTS ###################################################################### diff --git a/instruments/tests/test_property_factories/__init__.py b/instruments/tests/test_property_factories/__init__.py index ad11d1f11..504c12f94 100644 --- a/instruments/tests/test_property_factories/__init__.py +++ b/instruments/tests/test_property_factories/__init__.py @@ -14,7 +14,7 @@ # pylint: disable=missing-docstring -class MockInstrument(object): +class MockInstrument: """ Mock class that admits sendcmd/query but little else such that property diff --git a/instruments/tests/test_tektronix/test_tektronix_tds224.py b/instruments/tests/test_tektronix/test_tektronix_tds224.py index 52f8a1eb6..7cd75a05e 100644 --- a/instruments/tests/test_tektronix/test_tektronix_tds224.py +++ b/instruments/tests/test_tektronix/test_tektronix_tds224.py @@ -6,8 +6,6 @@ # IMPORTS #################################################################### -from builtins import bytes - import numpy as np import instruments as ik diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index 3a3680b66..80952157a 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -6,8 +6,6 @@ # IMPORTS #################################################################### - -from builtins import range from enum import Enum import pytest @@ -25,7 +23,7 @@ def test_ProxyList_basics(): - class ProxyChild(object): + class ProxyChild: def __init__(self, parent, name): self._parent = parent @@ -41,7 +39,7 @@ def __init__(self, parent, name): def test_ProxyList_valid_range_is_enum(): - class ProxyChild(object): + class ProxyChild: def __init__(self, parent, name): self._parent = parent @@ -60,7 +58,7 @@ class MockEnum(Enum): def test_ProxyList_length(): - class ProxyChild(object): + class ProxyChild: def __init__(self, parent, name): self._parent = parent @@ -74,7 +72,7 @@ def __init__(self, parent, name): def test_ProxyList_iterator(): - class ProxyChild(object): + class ProxyChild: def __init__(self, parent, name): self._parent = parent @@ -92,7 +90,7 @@ def __init__(self, parent, name): def test_ProxyList_invalid_idx_enum(): with pytest.raises(IndexError): - class ProxyChild(object): + class ProxyChild: def __init__(self, parent, name): self._parent = parent @@ -111,7 +109,7 @@ class MockEnum(Enum): def test_ProxyList_invalid_idx(): with pytest.raises(IndexError): - class ProxyChild(object): + class ProxyChild: def __init__(self, parent, name): self._parent = parent @@ -171,7 +169,7 @@ def test_assume_units_failures(): assume_units(1, 'm').rescale('s') def test_setattr_expression_simple(): - class A(object): + class A: x = 'x' y = 'y' z = 'z' @@ -181,7 +179,7 @@ class A(object): assert a.x == 'foo' def test_setattr_expression_index(): - class A(object): + class A: x = ['x', 'y', 'z'] a = A() @@ -189,9 +187,9 @@ class A(object): assert a.x[1] == 'foo' def test_setattr_expression_nested(): - class B(object): + class B: x = 'x' - class A(object): + class A: b = None def __init__(self): self.b = B() @@ -201,9 +199,9 @@ def __init__(self): assert a.b.x == 'foo' def test_setattr_expression_both(): - class B(object): + class B: x = 'x' - class A(object): + class A: b = None def __init__(self): self.b = [B()] diff --git a/instruments/thorlabs/_packets.py b/instruments/thorlabs/_packets.py index 012a54716..010a96c1f 100644 --- a/instruments/thorlabs/_packets.py +++ b/instruments/thorlabs/_packets.py @@ -29,7 +29,7 @@ # CLASSES ##################################################################### -class ThorLabsPacket(object): +class ThorLabsPacket: """ This class is used to wrap data to-/from- the instrument. Because of the diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index 44b011dc4..84a7d5b3c 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -8,15 +8,12 @@ # IMPORTS ##################################################################### - -from builtins import range from enum import IntEnum -import instruments.units as u - from instruments.thorlabs.thorlabs_utils import check_cmd from instruments.abstract_instruments import Instrument +import instruments.units as u from instruments.util_fns import enum_property, bool_property, unitful_property # CLASSES ##################################################################### diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index b4d41ee7c..448aeb439 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -72,7 +72,7 @@ class MeasurementConfiguration(Enum): # INNER CLASSES # - class Sensor(object): + class Sensor: """ Class representing a sensor on the ThorLabs PM100USB diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index 4710250ac..0aa82656f 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -8,16 +8,14 @@ # IMPORTS ##################################################################### -from builtins import range - from enum import IntEnum -import instruments.units as u from instruments.abstract_instruments import Instrument +from instruments.thorlabs.thorlabs_utils import check_cmd +import instruments.units as u from instruments.util_fns import ( bool_property, enum_property, int_property, unitful_property ) -from instruments.thorlabs.thorlabs_utils import check_cmd # CLASSES ##################################################################### diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index 400d6eb36..d76a5107c 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -8,15 +8,13 @@ # IMPORTS ##################################################################### - -from builtins import range, map from enum import IntEnum, Enum -import instruments.units as u - from instruments.abstract_instruments import Instrument -from instruments.util_fns import convert_temperature -from instruments.util_fns import enum_property, unitful_property, int_property +import instruments.units as u +from instruments.util_fns import ( + convert_temperature, enum_property, unitful_property, int_property +) # CLASSES ##################################################################### diff --git a/instruments/thorlabs/thorlabsapt.py b/instruments/thorlabs/thorlabsapt.py index a4a54814d..5047798d2 100644 --- a/instruments/thorlabs/thorlabsapt.py +++ b/instruments/thorlabs/thorlabsapt.py @@ -13,10 +13,8 @@ import codecs import warnings -from builtins import range -import instruments.units as u - from instruments.thorlabs import _abstract, _packets, _cmds +import instruments.units as u from instruments.util_fns import assume_units # LOGGING ##################################################################### @@ -35,7 +33,7 @@ class ThorLabsAPT(_abstract.ThorLabsInstrument): thorlabs source folder. """ - class APTChannel(object): + class APTChannel: """ Represents a channel within the hardware device. One device can have diff --git a/instruments/toptica/topmode.py b/instruments/toptica/topmode.py index fe8821668..46cacd81f 100644 --- a/instruments/toptica/topmode.py +++ b/instruments/toptica/topmode.py @@ -8,14 +8,12 @@ # IMPORTS ##################################################################### - -from builtins import range, map from enum import IntEnum -import instruments.units as u +from instruments.abstract_instruments import Instrument from instruments.toptica.toptica_utils import convert_toptica_boolean as ctbool from instruments.toptica.toptica_utils import convert_toptica_datetime as ctdate -from instruments.abstract_instruments import Instrument +import instruments.units as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -62,7 +60,7 @@ class CharmStatus(IntEnum): # INNER CLASSES # - class Laser(object): + class Laser: """ Class representing a laser on the Toptica Topmode. diff --git a/requirements.txt b/requirements.txt index 9158772a2..ccc81b3f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ numpy pyserial pyvisa>=1.9 quantities>=0.12.1 -future>=0.15 python-vxi11>=0.8 pyusb python-usbtmc diff --git a/setup.py b/setup.py index c9f89e896..6bfadef89 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,6 @@ "pyserial>=3.3", "pyvisa>=1.9", "quantities>=0.12.1", - "future>=0.15", "python-vxi11>=0.8", "python-usbtmc", "pyusb", From 19e57d6e88fd6e7f60b780dfb9d94c390d6d0735 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch <10662464+trappitsch@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:59:26 -0700 Subject: [PATCH 040/108] Added instrument: Newport Agilis (#238) * Added instrument: Newport Agilis Added a new instrument for Newport Agilis devices. Currently, only the AG-UC2 Controller is supported. Added the controller to the respective ini file in the newport folder. Axis class implemented to send device specific commands. This can be used from other controllers, once implemented, as well. Some caveats / non-implemented and only partially tested routines are noted on top of the `agilis.py` file. I don't have a device with a limit switch available for testing, therefore, some routines that only work on such devices are not implemented and some routines are untested. The doc strings for every routine states this clearly as well. A full test suite for all functions is also provided. Everything testing great with tox for py36, py37, and py38. * Proper linting, change remote mode to property Ensure proper linting by fixing all errors and warning that occured when running `pylint --disable I,R instruments`. Incorporated `enable_remote_mode` for the controller as an @property. This is now in the same style as in other instrumentkit classes, e.g., srs645.py. * Added agilis classes to doc/source/apiref/newport.rst Also tested locally by creating the whole documentation to ensure it works. Co-authored-by: Reto Trappitsch --- doc/source/apiref/newport.rst | 11 + instruments/newport/__init__.py | 1 + instruments/newport/agilis.py | 579 ++++++++++++++++++ instruments/tests/test_newport/test_agilis.py | 424 +++++++++++++ 4 files changed, 1015 insertions(+) create mode 100644 instruments/newport/agilis.py create mode 100644 instruments/tests/test_newport/test_agilis.py diff --git a/doc/source/apiref/newport.rst b/doc/source/apiref/newport.rst index 1aaefae16..ec5fc1631 100644 --- a/doc/source/apiref/newport.rst +++ b/doc/source/apiref/newport.rst @@ -7,6 +7,17 @@ Newport ======= +:class:`Agilis` Piezo Motor Controller +====================================== + +.. autoclass:: AGUC2 + :members: + :undoc-members: + +.. autoclass:: _Axis + :members: + :undoc-members: + :class:`NewportESP301` Motor Controller ======================================= diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index 462123fa8..cfc97f125 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -4,6 +4,7 @@ Module containing Newport instruments """ +from .agilis import AGUC2 from .errors import NewportError from .newportesp301 import ( diff --git a/instruments/newport/agilis.py b/instruments/newport/agilis.py new file mode 100644 index 000000000..b3e954a3b --- /dev/null +++ b/instruments/newport/agilis.py @@ -0,0 +1,579 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Provides support for the Newport Agilis Controller AG-UC2 only (currently). + +Agilis controllers are piezo driven motors that do not have support for units. +All units used in this document are given as steps. + +Currently I only have a AG-PR100 rotation stage available for testing. This +device does not contain a limit switch and certain routines are therefore +completely untested! These are labeled in their respective docstring with: + `UNTESTED: SEE COMMENT ON TOP` + +The governing document for the commands and implementation is: + +Agilis Series, Piezo Motor Driven Components, User's Manual, v2.2.x, +by Newport, especially chapter 4.7: "ASCII Command Set" +Document number from footer: EDH0224En5022 — 10/12 + +Routines not implemented at all: +- Measure current position (MA command): + This routine interrupts the communication and + restarts it afterwards. It can, according to the documentation, take up to + 2 minutes to complete. It is furthermore only available on stages with limit + switches. I currently do not have the capability to implement this therefore. +- Absolute Move (PA command): + Exactly the same reason as for MA command. +""" + +# IMPORTS ##################################################################### + +from time import sleep + +from enum import IntEnum + +from instruments.abstract_instruments import Instrument +from instruments.util_fns import ProxyList + +# CLASSES ##################################################################### + + +class _Axis: + + """ + Class representing one axis attached to a Controller. This will likely + work with the AG-UC8 controller as well. + + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by a Controller class + """ + + def __init__(self, cont, ax): + if not isinstance(cont, AGUC2): + raise TypeError("Don't do that.") + + # set axis integer + if isinstance(ax, AGUC2.Axes): + self._ax = ax.value + else: + self._ax = ax + + # set controller + self._cont = cont + + # PROPERTIES # + + @property + def axis_status(self): + """ + Returns the status of the current axis. + """ + resp = self._cont.ag_query("{} TS".format( + int(self._ax) + )) + if resp.find('TS') == -1: + return "Status code query failed." + + resp = int(resp.replace(str(int(self._ax)) + 'TS', '')) + status_message = agilis_status_message(resp) + return status_message + + @property + def jog(self): + """ + Start jog motion / get jog mode + Defined jog steps are defined with `step_amplitude` function (default + 16). If a jog mode is supplied, the jog motion is started. Otherwise + the current jog mode is queried. Valid jog modes are: + + -4 — Negative direction, 666 steps/s at defined step amplitude. + -3 — Negative direction, 1700 steps/s at max. step amplitude. + -2 — Negative direction, 100 step/s at max. step amplitude. + -1 — Negative direction, 5 steps/s at defined step amplitude. + 0 — No move, go to READY state. + 1 — Positive direction, 5 steps/s at defined step amplitude. + 2 — Positive direction, 100 steps/s at max. step amplitude. + 3 — Positive direction, 1700 steps/s at max. step amplitude. + 4 — Positive direction, 666 steps/s at defined step amplitude. + + If the jog mode is queried it is returend as a string. + """ + resp = self._cont.ag_query("{} JA?".format( + int(self._ax) + )) + return resp + + @jog.setter + def jog(self, mode): + mode = int(mode) + if mode < -4 or mode > 4: + raise ValueError("Jog mode out of range. Must be between -4 and " + "4.") + + self._cont.ag_sendcmd("{} JA {}".format( + int(self._ax), + mode + )) + + @property + def number_of_steps(self): + """ + Returns the number of accumulated steps in forward direction minus + the number of steps in backward direction since powering the + controller or since the last ZP (zero position) command, whatever + was last. + + Note: + The step size of the Agilis devices are not 100% repeatable and + vary between forward and backward direction. Furthermore, the step + size can be modified using the SU command. Consequently, the TP + command provides only limited information about the actual position + of the device. In particular, an Agilis device can be at very + different positions even though a TP command may return the same + result. + + Returns xTPnn where x is the axis queried and nn are the steps. + """ + resp = self._cont.ag_query("{} TP".format( + int(self._ax) + )) + return resp + + @property + def move_relative(self): + """ + Moves the axis by nn steps / Queries the status of the axis. + Steps must be given a number that can be converted to a signed integer + between -2,147,483,648 and 2,147,483,647. + If queried, command returns the current target position. At least this + is the expected behaviour, never worked with the rotation stage. + """ + resp = self._cont.ag_query("{} PR?".format( + int(self._ax) + )) + return resp + + @move_relative.setter + def move_relative(self, steps): + steps = int(steps) + if steps < -2147483648 or steps > 2147483647: + raise ValueError("Number of steps are out of range. They must be " + "between -2,147,483,648 and 2,147,483,647") + + self._cont.ag_sendcmd("{} PR {}".format( + int(self._ax), + steps + )) + + @property + def move_to_limit(self): + """ + UNTESTED: SEE COMMENT ON TOP + + The command functions properly only with devices that feature a + limit switch like models AG-LS25, AG-M050L and AG-M100L. + + Starts a jog motion at a defined speed to the limit and stops + automatically when the limit is activated. See `jog` command for + details on available modes. + + Returns the distance of the current position to the limit in + 1/1000th of the total travel. + """ + resp = self._cont.ag_query("{} MA?".format( + int(self._ax) + )) + return resp + + @move_to_limit.setter + def move_to_limit(self, mode): + mode = int(mode) + if mode < -4 or mode > 4: + raise ValueError("Jog mode out of range. Must be between -4 and " + "4.") + + self._cont.ag_sendcmd("{} MA {}".format( + int(self._ax), + mode + )) + + @property + def step_amplitude(self): + """ + Sets / Gets the step_amplitude. + + Sets the step amplitude (step size) in positive and / or negative + direction. If the parameter is positive, it will set the step + amplitude in the forward direction. If the parameter is negative, + it will set the step amplitude in the backward direction. You can also + provide a tuple or list of two values (one positive, one negative), + which will set both values. + Valid values are between -50 and 50, except for 0. + If queried, returns a tuple of first the negative, then the positive + step amplitude response in the format xSUnn where x is the axis and + nn the step amplitude + """ + resp_neg = self._cont.ag_query("{} SU-?".format( + int(self._ax) + )) + resp_pos = self._cont.ag_query("{} SU+?".format( + int(self._ax) + )) + return resp_neg, resp_pos + + @step_amplitude.setter + def step_amplitude(self, nns): + if not isinstance(nns, tuple) and not isinstance(nns, list): + nns = [nns] + + # check all values for validity + for nn in nns: + nn = int(nn) + if nn < -50 or nn > 50 or nn == 0: + raise ValueError("Step amplitude {} outside the valid range. " + "It must be between -50 and -1 or between " + "1 and 50.".format(nn)) + + for nn in nns: + self._cont.ag_sendcmd("{} SU {}".format( + int(self._ax), + int(nn) + )) + + @property + def step_delay(self): + """ + Sets/gets the step delay of stepping mode. The delay applies for both + positive and negative directions. The delay is programmed as multiple + of 10µs. For example, a delay of 40 is equivalent to + 40 x 10 µs = 400 µs. The maximum value of the parameter is equal to a + delay of 2 seconds between pulses. By default, after reset, the value + is 0. + Setter: value must be integer between 0 and 200000 included + If queried, command returns the currently set step delay as a string + in the format xDLnn where x is the axis number and nn the step delay + value. + """ + resp = self._cont.ag_query("{} DL?".format( + int(self._ax) + )) + return resp + + @step_delay.setter + def step_delay(self, nn): + nn = int(nn) + if nn < 0 or nn > 200000: + raise ValueError("Step delay is out of range. It must be between " + "0 and 200000.") + + self._cont.ag_sendcmd("{} DL {}".format( + int(self._ax), + nn + )) + + # MODES # + + def am_i_still(self, max_retries=5): + """ + Function to test if an axis stands still. It queries the status of + the given axis and returns True (if axis is still) or False if it is + moving. + The reason this routine is implemented is because the status messages + can time out. If a timeout occurs, this routine will retry the query + until `max_retries` is reached. If query is still not successful, an + IOError will be raised. + + :param int max_retries: Maximum number of retries + + :return: True if the axis is still, False if the axis is moving + :rtype: bool + """ + retries = 0 + + while retries < max_retries: + status = self.axis_status + if status == agilis_status_message(0): + return True + elif status == agilis_status_message(1) or \ + status == agilis_status_message(2) or \ + status == agilis_status_message(3): + return False + else: + retries += 1 + + raise IOError("The function `am_i_still` ran out of maximum retries. " + "Could not query the status of the axis.") + + def stop(self): + """ + Stops the axis. This is useful to interrupt a jogging motion. + """ + self._cont.ag_sendcmd("{} ST".format( + int(self._ax) + )) + + def zero_position(self): + """ + Resets the step counter to zero. See `number_of_steps` for details. + """ + self._cont.ag_sendcmd("{} ZP".format( + int(self._ax) + )) + + +class AGUC2(Instrument): + + """ + Handles the communication with the AGUC2 controller using the serial + connection. + + Example usage: + + >>> import instruments as ik + >>> agl = ik.newport.AGUC2.open_serial(port='COM5', baud=921600) + + This loads a controller into the instance `agl`. The two axis are + called 'X' (axis 1) and 'Y' (axis 2). Controller commands and settings + can be executed as following, as examples: + + Reset the controller: + + >>> agl.reset_controller() + + Print the firmware version: + + >>> print(agl.firmware_version) + + Individual axes can be controlled and queried as following: + + Relative move by 1000 steps: + + >>> agl.axis["X"].move_relative(1000) + + Activate jogging in mode 3: + + >>> agl.axis["X"].jog(3) + + Jogging will continue until the axis is stopped + + >>> agl.axis["X"].stop() + + Query the step amplitude, then set the postive one to +10 and the + negative one to -20 + + >>> print(agl.axis["X"].step_amplitude) + >>> agl.axis["X"].step_amplitude = 10 + >>> agl.axis["X"].step_amplitude = -20 + """ + + def __init__(self, filelike): + super(AGUC2, self).__init__(filelike) + + # Instrument requires '\r\n' line termination + self.terminator = '\r\n' + + # Some local variables + self._remote_mode = False + self._sleep_time = 0.25 + + # ENUMS # + + class Axes(IntEnum): + """ + Enumeration of valid delay channels for the AG-UC2 controller. + """ + X = 1 + Y = 2 + + # INNER CLASSES # + + # PROPERTIES # + + @property + def axis(self): + """ + Gets a specific axis object. + + The desired axis is accessed by passing an EnumValue from + `~AGUC2.Channels`. For example, to access the X axis (axis 1): + + >>> import instruments as ik + >>> agl = ik.newport.AGUC2.open_serial(port='COM5', baud=921600) + >>> agl.axis["X"].move_relative(1000) + + See example in `AGUC2` for a more details + + :rtype: `_Axis` + """ + self.enable_remote_mode = True + return ProxyList(self, _Axis, AGUC2.Axes) + + @property + def enable_remote_mode(self): + """ + Gets / sets the status of remote mode. + """ + return self._remote_mode + + @enable_remote_mode.setter + def enable_remote_mode(self, newval): + if newval and not self._remote_mode: + self._remote_mode = True + self.ag_sendcmd('MR') + elif not newval and self._remote_mode: + self._remote_mode = False + self.ag_sendcmd('ML') + + @property + def error_previous_command(self): + """ + Retrieves the error of the previous command and translates it into a + string. The string is returned + """ + resp = self.ag_query('TE') + + if resp.find('TE') == -1: + return "Error code query failed." + + resp = int(resp.replace('TE', '')) + error_message = agilis_error_message(resp) + return error_message + + @property + def firmware_version(self): + """ + Returns the firmware version of the controller + """ + resp = self.ag_query('VE') + return resp + + @property + def limit_status(self): + """ + PARTLY UNTESTED: SEE COMMENT ABOVE + + Returns the limit switch status of the controller. Possible returns + are: + - PH0: No limit switch is active + - PH1: Limit switch of axis #1 (X) is active, + limit switch of axis #2 (Y) is not active + - PH2: Limit switch of axis #2 (Y) is active, + limit switch of axis #1 (X) is not active + - PH3: Limit switches of axis #1 (X) and axis #2 (Y) are active + + If device has no limit switch, this routine always returns PH0 + """ + self.enable_remote_mode = True + resp = self.ag_query("PH") + return resp + + @property + def sleep_time(self): + """ + The device often times out. Therefore a sleep time can be set. The + routine will wait for this amount (in seconds) every time after a + command or a query are sent. + Setting the sleep time: Give time in seconds + If queried: Returns the sleep time in seconds as a float + """ + return self._sleep_time + + @sleep_time.setter + def sleep_time(self, t): + if t < 0: + raise ValueError("Sleep time must be >= 0.") + + self._sleep_time = float(t) + + # MODES # + + def reset_controller(self): + """ + Resets the controller. All temporary settings are reset to the default + value. Controller is put into local model. + """ + self._remote_mode = False + self.ag_sendcmd("RS") + + # SEND COMMAND AND QUERY ROUTINES AGILIS STYLE # + + def ag_sendcmd(self, cmd): + """ + Sends the command, then sleeps + """ + self.sendcmd(cmd) + sleep(self._sleep_time) + + def ag_query(self, cmd, size=-1): + """ + This runs the query command. However, the query command often times + out for this device. The response of all queries are always strings. + If timeout occurs, the response will be: + "Query timed out." + """ + try: + resp = self.query(cmd, size=size) + except IOError: + resp = "Query timed out." + + # sleep + sleep(self._sleep_time) + + return resp + + +def agilis_error_message(error_code): + """ + Returns a string with th error message for a given Agilis error code. + + :param int error_code: error code as an integer + + :return: error message + :rtype: string + """ + if not isinstance(error_code, int): + return "Error code is not an integer." + + error_dict = { + 0: "No error", + -1: "Unknown command", + -2: "Axis out of range (must be 1 or 2, or must not be specified)", + -3: "Wrong format for parameter nn (or must not be specified)", + -4: "Parameter nn out of range", + -5: "Not allowed in local mode", + -6: "Not allowed in current state" + } + + if error_code in error_dict.keys(): + return error_dict[error_code] + else: + return "An unknown error occurred." + + +def agilis_status_message(status_code): + """ + Returns a string with the status message for a given Agilis status + code. + + :param int status_code: status code as returned + + :return: status message + :rtype: string + """ + if not isinstance(status_code, int): + return "Status code is not an integer." + + status_dict = { + 0: "Ready (not moving).", + 1: "Stepping (currently executing a `move_relative` command).", + 2: "Jogging (currently executing a `jog` command with command" + "parameter different than 0).", + 3: "Moving to limit (currently executing `measure_current_position`, " + "`move_to_limit`, or `move_absolute` command).", + } + + if status_code in status_dict.keys(): + return status_dict[status_code] + else: + return "An unknown status occurred." diff --git a/instruments/tests/test_newport/test_agilis.py b/instruments/tests/test_newport/test_agilis.py new file mode 100644 index 000000000..50c59da6e --- /dev/null +++ b/instruments/tests/test_newport/test_agilis.py @@ -0,0 +1,424 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Agilis Controller +""" + +# IMPORTS ##################################################################### + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + +# TESTS ####################################################################### + +# CONTROLLER TESTS # + + +def test_aguc2_enable_remote_mode(): + """ + Check enabling of remote mode. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", + "ML" + ], + [ + ], + sep="\r\n" + ) as agl: + agl.enable_remote_mode = True + assert agl.enable_remote_mode is True + agl.enable_remote_mode = False + assert agl.enable_remote_mode is False + + +def test_aguc2_error_previous_command(): + """ + Check the call error of previous command routine. Note that the test will + return "Error code must be given as an integer." will be returned because + no actual error code is fed to the error message checker. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "TE" + ], + [ + ], + sep="\r\n" + ) as agl: + assert agl.error_previous_command == "Error code query failed." + + +def test_aguc2_firmware_version(): + """ + Check firmware version + AG-UC2 v2.2.1 + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "VE" + ], + [ + "AG-UC2 v2.2.1" + ], + sep="\r\n" + ) as agl: + assert agl.firmware_version == "AG-UC2 v2.2.1" + + +def test_aguc2_limit_status(): + """ + Check the limit status routine. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "PH" + ], + [ + "PH0" + ], + sep="\r\n" + ) as agl: + assert agl.limit_status == "PH0" + + +def test_aguc2_sleep_time(): + """ + Check setting, getting the sleep time. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + ], + [ + ], + sep="\r\n" + ) as agl: + agl.sleep_time = 3 + assert agl.sleep_time == 3 + with pytest.raises(ValueError): + agl.sleep_time = -3.14 + + +def test_aguc2_reset_controller(): + """ + Check reset controller function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "RS" + ], + [ + ], + sep="\r\n" + ) as agl: + agl.reset_controller() + assert agl.enable_remote_mode is False + + +def test_aguc2_ag_sendcmd(): + """ + Check agilis sendcommand wrapper. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR" # some command, here remote mode + ], + [ + ], + sep="\r\n" + ) as agl: + agl.ag_sendcmd("MR") + + +def test_aguc2_ag_query(): + """ + Check agilis query wrapper. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "VE" + ], + [ + "AG-UC2 v2.2.1" + ], + sep="\r\n" + ) as agl: + assert agl.ag_query("VE") == "AG-UC2 v2.2.1" + + +# AXIS TESTS # + + +def test_aguc2_axis_am_i_still(): + """ + Check if a given axis is still or not. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 TS", + "2 TS", + "2 TS", + "2 TS" + ], + [ + ], + sep="\r\n" + ) as agl: + with pytest.raises(IOError): + agl.axis["X"].am_i_still(max_retries=1) + with pytest.raises(IOError): + agl.axis["Y"].am_i_still(max_retries=3) + + +def test_aguc2_axis_axis_status(): + """ + Check the status of the axis. Note that the test will return + "Status code not valid." since no instrument is connected. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 TS", + "2 TS" + ], + [ + ], + sep="\r\n" + ) as agl: + assert agl.axis["X"].axis_status == "Status code query failed." + assert agl.axis["Y"].axis_status == "Status code query failed." + + +def test_aguc2_axis_jog(): + """ + Check the jog function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 JA 3", + "2 JA -4", + ], + [ + ], + sep="\r\n" + ) as agl: + agl.axis["X"].jog = 3 + agl.axis["Y"].jog = -4 + with pytest.raises(ValueError): + agl.axis["X"].jog = -5 + with pytest.raises(ValueError): + agl.axis["Y"].jog = 5 + + +def test_aguc2_axis_number_of_steps(): + """ + Check the number of steps function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 TP", + ], + [ + "1TP0" + ], + sep="\r\n" + ) as agl: + assert agl.axis["X"].number_of_steps == "1TP0" + + +def test_aguc2_axis_move_relative(): + """ + Check the move relative function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 PR 1000", + "2 PR -340" + ], + [ + ], + sep="\r\n" + ) as agl: + agl.axis["X"].move_relative = 1000 + agl.axis["Y"].move_relative = -340 + with pytest.raises(ValueError): + agl.axis["X"].move_relative = 2147483648 + with pytest.raises(ValueError): + agl.axis["Y"].move_relative = -2147483649 + + +def test_aguc2_axis_move_to_limit(): + """ + Check for move to limit function. + This function is UNTESTED to work, here simply command sending is checked + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "2 MA 3" + ], + [ + ], + sep="\r\n" + ) as agl: + agl.axis["Y"].move_to_limit = 3 + with pytest.raises(ValueError): + agl.axis["Y"].move_to_limit = -5 + with pytest.raises(ValueError): + agl.axis["X"].move_to_limit = 5 + + +def test_aguc2_axis_step_amplitude(): + """ + Check for step amplitude function + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 SU-?", + "1 SU+?", + "1 SU -35", + "1 SU 47" + + ], + [ + "1SU-35", "1SU+35" + ], + sep="\r\n" + ) as agl: + assert agl.axis["X"].step_amplitude == ("1SU-35", "1SU+35") + agl.axis["X"].step_amplitude = -35 + agl.axis["X"].step_amplitude = 47 + with pytest.raises(ValueError): + agl.axis["X"].step_amplitude = 0 + with pytest.raises(ValueError): + agl.axis["Y"].step_amplitude = -51 + with pytest.raises(ValueError): + agl.axis["Y"].step_amplitude = 51 + + +def test_aguc2_axis_step_delay(): + """ + Check the step delay function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "2 DL?", + "1 DL 1000", + "1 DL 200" + ], + [ + "2DL0" + ], + sep="\r\n" + ) as agl: + assert agl.axis["Y"].step_delay == "2DL0" + agl.axis["X"].step_delay = 1000 + agl.axis["X"].step_delay = 200 + with pytest.raises(ValueError): + agl.axis["X"].step_delay = -1 + with pytest.raises(ValueError): + agl.axis["Y"].step_delay = 2000001 + + +def test_aguc2_axis_stop(): + """ + Check the stop function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 ST", + "2 ST" + ], + [ + ], + sep="\r\n" + ) as agl: + agl.axis["X"].stop() + agl.axis["Y"].stop() + + +def test_aguc2_axis_zero_position(): + """ + Check the stop function. + """ + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 ZP", + "2 ZP" + ], + [ + ], + sep="\r\n" + ) as agl: + agl.axis["X"].zero_position() + agl.axis["Y"].zero_position() + + +# FUNCTION TESTS # + + +def test_agilis_error_message(): + # regular error messages + assert ik.newport.agilis.agilis_error_message(0) == "No error" + assert ik.newport.agilis.agilis_error_message(-6) == "Not allowed in " \ + "current state" + # out of range integers + assert ik.newport.agilis.agilis_error_message(1) == "An unknown error " \ + "occurred." + assert ik.newport.agilis.agilis_error_message(-7) == "An unknown error " \ + "occurred." + # non-integers + assert ik.newport.agilis.agilis_error_message(-7.5) == "Error code is " \ + "not an integer." + assert ik.newport.agilis.agilis_error_message("TE0") == "Error code is " \ + "not an integer." + + +def test_agilis_status_message(): + # regular status messages + assert ik.newport.agilis.agilis_status_message(0) == "Ready (not moving)." + assert ik.newport.agilis.agilis_status_message(3) == \ + "Moving to limit (currently executing " \ + "`measure_current_position`, `move_to_limit`, or " \ + "`move_absolute` command)." + # out of range integers + assert ik.newport.agilis.agilis_status_message(4) == "An unknown " \ + "status occurred." + assert ik.newport.agilis.agilis_status_message(-1) == "An unknown " \ + "status occurred." + # non integers + assert ik.newport.agilis.agilis_status_message(3.14) == "Status code is " \ + "not an integer." + assert ik.newport.agilis.agilis_status_message("1TS0") == "Status code " \ + "is not an " \ + "integer." From 3303cec4433b493af5eac94994d694be2853231b Mon Sep 17 00:00:00 2001 From: Reto Trappitsch <10662464+trappitsch@users.noreply.github.com> Date: Tue, 7 Jul 2020 11:20:48 -0700 Subject: [PATCH 041/108] Enhancement: SRS645 delay generator added burst mode support (#240) * Enhancement: SRS645 delay generator added burst mode support Added routines for enabling burst mode and controlling all sub properties. Set/get implemented for all sub menus of burst mode. A full test suite for each of the new functions has been implemented as well. Every call has been tested with an actual instrument and is functional. tox testing: all passed pylint rating: 10/10 * Fixed singleton-comparison error in pylint Co-authored-by: Reto Trappitsch --- instruments/srs/srsdg645.py | 69 +++++++++++++++ instruments/tests/test_srs/test_srsdg645.py | 94 +++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index f15cbb688..f793cdcbe 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -332,3 +332,72 @@ def holdoff(self): @holdoff.setter def holdoff(self, newval): self.sendcmd("HOLD {}".format(newval.rescale(u.s).magnitude)) + + @property + def enable_burst_mode(self): + """ + Gets/sets whether burst mode is enabled. + + :type: `bool` + """ + return bool(int(self.query("BURM?"))) + + @enable_burst_mode.setter + def enable_burst_mode(self, newval): + self.sendcmd("BURM {}".format(1 if newval else 0)) + + @property + def enable_burst_t0_first(self): + """ + Gets/sets whether T0 output in burst mode is on first. If + enabled, the T0 output is enabled for first delay cycle of the + burst only. If disabled, the T0 output is enabled for all delay + cycles of the burst. + + :type: `bool` + """ + return bool(int(self.query("BURT?"))) + + @enable_burst_t0_first.setter + def enable_burst_t0_first(self, newval): + self.sendcmd("BURT {}".format(1 if newval else 0)) + + @property + def burst_count(self): + """ + Gets/sets the burst count. When burst mode is enabled, the + DG645 outputs burst count delay cycles per trigger. + Valid numbers for burst count are between 1 and 2**32 - 1 + """ + return int(self.query("BURC?")) + + @burst_count.setter + def burst_count(self, newval): + self.sendcmd("BURC {}".format(int(newval))) + + @property + def burst_period(self): + """ + Gets/sets the burst period. The burst period sets the time + between delay cycles during a burst. The burst period may + range from 100 ns to 2000 – 10 ns in 10 ns steps. + """ + return u.Quantity(float(self.query("BURP?")), u.s) + + @burst_period.setter + def burst_period(self, newval): + self.sendcmd("BURP {}".format(newval.rescale(u.s).magnitude)) + + @property + def burst_delay(self): + """ + Gets/sets the burst delay. When burst mode is enabled the DG645 + delays the first burst pulse relative to the trigger by the + burst delay. The burst delay may range from 0 ps to < 2000 s + with a resolution of 5 ps. + """ + return u.Quantity(float(self.query("BURD?")), u.s) + + @burst_delay.setter + def burst_delay(self, newval): + self.sendcmd("BURD {}".format(newval.rescale(u.s).magnitude)) diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index 83081e0f8..46ee9ea39 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -77,3 +77,97 @@ def test_srsdg645_trigger_source(): assert ref == ddg.Channels.T0 assert abs((t - u.Quantity(42, "s")).magnitude) < 1e5 ddg.channel["B"].delay = (ddg.channel["A"], u.Quantity(1, "minute")) + + +def test_srsdg645_enable_burst_mode(): + """ + SRSDG645: Checks getting/setting of enabling burst mode. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "BURM?", + "BURM 1" + ], + [ + "0" + ] + ) as ddg: + assert ddg.enable_burst_mode is False + ddg.enable_burst_mode = True + + +def test_srsdg645_enable_burst_t0_first(): + """ + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "BURT?", + "BURT 1" + ], + [ + "0" + ] + ) as ddg: + assert ddg.enable_burst_t0_first is False + ddg.enable_burst_t0_first = True + + +def test_srsdg645_burst_count(): + """ + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "BURC?", + "BURC 42" + ], + [ + "10" + ] + ) as ddg: + assert ddg.burst_count == 10 + ddg.burst_count = 42 + + +def test_srsdg645_burst_period(): + """ + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "BURP?", + "BURP 13" + ], + [ + "100E-9" + ] + ) as ddg: + unit_eq(ddg.burst_period, u.Quantity(100, "ns").rescale(u.s)) + ddg.burst_period = u.Quantity(13, "s") + + +def test_srsdg645_burst_delay(): + """ + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "BURD?", + "BURD 42" + ], + [ + "0" + ] + ) as ddg: + unit_eq(ddg.burst_delay, u.Quantity(0, "s")) + ddg.burst_delay = u.Quantity(42, "s") From d8c56e6b731a37cbf5e20b8a2dd90ce5de44e322 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 21 Jul 2020 17:26:40 -0700 Subject: [PATCH 042/108] Thorlabs APT Inertial Actuator (#242) * Improvements and addition to APT Thorlabs & its test suite APT Piezo Inertia Actuator - Tested with KIM101 connected to PIM1 mirror mount. - Newer Piezo drivers have different command structure, thus implemented a new class. - Wrote a test suite for all APT routines tested with KIM101 - Using @pytest.fixture to initialize `stdin`, and `stdout` for testing. - Extended docstring examples for most routines. - Added exceptions to parent `ThorlabsAPTClass` when commands are not allowed for KIM101. APT Motor Controller - Tested with KDC101 controller connected to PRM1-Z8 rotation stage. - Bug fix for `channel.position` routine, package length for request was not specified. Fixed and tested. Checked that expected byte length also agrees with manual. - Exact same bug fix for `channel.position_encoder`. - Added test suite for all routines that could be tested with KDC101 controller. AptMotorController -> MotorChannel -> status_bits: - There is a note in there that says that the statusupdate and dcstatusupdate is confusing. For the KDC101 controller, only the latter is implemented. The new documentation is much clearer than the previously used one with respect to clarifying which controller goes with which command. * Docs updated, docstring formatting corrected Added the new class to the docs and fixed some issues in the docstrings. Now all examples should be displayed nicely. Co-authored-by: Reto Trappitsch --- .gitignore | 2 +- doc/source/apiref/thorlabs.rst | 4 + .../tests/test_thorlabs/test_thorlabs_apt.py | 862 ++++++++++++++++++ instruments/thorlabs/__init__.py | 6 +- instruments/thorlabs/_cmds.py | 9 + instruments/thorlabs/thorlabsapt.py | 675 +++++++++++++- 6 files changed, 1552 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c02194b2c..0140fc170 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ doc/_build ## Venv .venv/ - +.python-version ## setup.py generated files ## MANIFEST diff --git a/doc/source/apiref/thorlabs.rst b/doc/source/apiref/thorlabs.rst index 6abf2f88c..d50520b4b 100644 --- a/doc/source/apiref/thorlabs.rst +++ b/doc/source/apiref/thorlabs.rst @@ -21,6 +21,10 @@ ThorLabs :members: :undoc-members: +.. autoclass:: APTPiezoInertiaActuator + :members: + :undoc-members: + .. autoclass:: APTPiezoStage :members: :undoc-members: diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py index da280ff2c..9ff701c0e 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_apt.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -79,3 +79,865 @@ def test_apt_hw_info(): "ThorLabs APT Instrument model ABC-123, " "serial 01020304 (HW version 42, FW version a1.a2.a3)" ) + + +# FIXTURES FOR APT TEST SUITE # + + +@pytest.fixture +def init_kdc101(): + """Return the send, receive value to initialize a KIM101 unit.""" + stdin = ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + stdout = ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "KDC101".encode('ascii'), + # HW type + 16, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 1 + ) + ).pack() + return stdin, stdout + + +@pytest.fixture +def init_kim101(): + """Return the send, receive value to initialize a KIM101 unit.""" + stdin = ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + stdout = ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "KIM101".encode('ascii'), + # HW type + 16, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 4 + ) + ).pack() + return stdin, stdout + + +@pytest.fixture +def init_tim101(): + """Return the send, receive value to initialize a TIM101 unit. + + Currently only used to test failure modes. + """ + stdin = ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + stdout = ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "TIM101".encode('ascii'), + # HW type + 16, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 4 + ) + ).pack() + return stdin, stdout + + +# pylint: disable=redefined-outer-name + + +# TESTS FOR APT PIEZO INERTIA ACTUATOR CLASS (APT_PIA) # + + +# CHANNELS # + + +def test_apt_pia_channel_drive_op_parameters(init_kim101): + """Test the drive op parameters for the APT Piezo Inertia Actuator. + + Tested with KIM101 driver connected to PIM1 mirror mount. + """ + with expected_protocol( + ik.thorlabs.APTPiezoInertiaActuator, + [ + init_kim101[0], + ThorLabsPacket( # receive a package + message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x07, param2=0x01, + dest=0x50, + source=0x01, + data=None + ).pack(), + ThorLabsPacket( # send a packet + message_id=ThorLabsCommands.PZMOT_SET_PARAMS, + param1=None, param2=None, + dest=0x50, + source=0x01, + data=struct.pack(' stop + message_id=ThorLabsCommands.PZMOT_MOVE_JOG, + param1=0x01, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + ], + [ + init_kim101[1] + ], + sep="" + ) as apt: + apt.channel[0].move_jog_stop() + + +# CONTROLLER # + + +def test_apt_pia_enabled_multi(init_kim101): + """Multi-channel enabling APT Piezo Inertia Actuator KIM101. + + Tested with KIM101 driver connected to PIM1 mirror mount. + """ + with expected_protocol( + ik.thorlabs.APTPiezoInertiaActuator, + [ + init_kim101[0], + ThorLabsPacket( # all off + message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x2B, param2=0x00, + dest=0x50, + source=0x01, + data=None, + ).pack(), + ThorLabsPacket( # read channel 0 & 1 + message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x2B, param2=0x00, + dest=0x50, + source=0x01, + data=None, + ).pack(), + ThorLabsPacket( # read channel 2 & 3 + message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x2B, param2=0x00, + dest=0x50, + source=0x01, + data=None, + ).pack(), + ThorLabsPacket( # send off + message_id=ThorLabsCommands.PZMOT_SET_PARAMS, + param1=None, param2=None, + dest=0x50, + source=0x01, + data=struct.pack('>> import instruments as ik + >>> import instruments.units as u + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # set first channel to enabled + >>> ch = kim.channel[0] + >>> ch.enabled_single = True + >>> # define and set drive parameters + >>> max_volts = u.Quantity(110, u.V) + >>> step_rate = u.Quantity(1000, 1/u.s) + >>> acceleration = u.Quantity(10000, 1/u.s**2) + >>> ch.drive_op_parameters = [max_volts, step_rate, acceleration] + >>> # aboslute move to 1000 steps + >>> ch.move_abs(1000) + """ + + class PiezoChannel(APTPiezoDevice.PiezoDeviceChannel): + """ + Class representing a single piezo channel within a piezo stage + on the Thorlabs APT controller. + """ + + # PROPERTIES # + + @property + def drive_op_parameters(self): + """Get / Set various drive parameters for move motion. + + Defines the speed and acceleration of moves initiated in + the following ways: + - by clicking in the position display + - via the top panel controls when ‘Go To Position’ mode is + selected (in the Set_TIM_JogParameters (09) or + Set_KCubeMMIParams (15) sub‐messages). + - via software using the MoveVelocity, MoveAbsoluteStepsEx + or MoveRelativeStepsEx methods. + + :setter: The setter must be be given as a list of 3 + entries. The three entries are: + - Maximum Voltage: + The maximum piezo drive voltage, in the range 85V + to 125V. Unitful, if no unit given, V are assumed. + - Step Rate: + The piezo motor moves by ramping up the drive + voltage to the value set in the MaxVoltage parameter + and then dropping quickly to zero, then repeating. + One cycle is termed a step. This parameter specifies + the velocity to move when a command is initiated. + The step rate is specified in steps/sec, in the range 1 + to 2,000. Unitful, if no unit given, 1 / sec assumed. + - Step Acceleration: + This parameter specifies the acceleration up to the + step rate, in the range 1 to 100,000 cycles/sec/sec. + Unitful, if no unit given, 1/sec**2 assumed. + + :return: List with the drive parameters, unitful. + + :raises TypeError: The setter was not a list or tuple. + :raises ValueError: The setter was not given a tuple with + three values. + :raises ValueError: One of the parameters was out of range. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # grab channel 0 + >>> ch = kim.channel[0] + >>> # change the step rate to 2000 /s + >>> drive_params = ch.drive_op_parameters + >>> drive_params[1] = 2000 + >>> ch.drive_op_parameters = drive_params + """ + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x07, + param2=self._idx_chan, + dest=self._apt.destination, + source=0x01, + data=None + ) + + resp = self._apt.querypacket( + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, + expect_data_len=14) + + # unpack + ret_val = struct.unpack(' 125: + raise ValueError("The voltage ({} V) is out of range. It must " + "be between 85 V and 125 V.".format(volt)) + if rate < 1 or rate > 2000: + raise ValueError("The step rate ({} /s) is out of range. It " + "must be between 1 /s and 2,000 /s." + .format(rate)) + + if accl < 1 or accl > 100000: + raise ValueError("The acceleration ({} /s/s) is out of range. " + "It must be between 1 /s/s and 100,000 /s/s." + .format(accl)) + + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_SET_PARAMS, + param1=None, + param2=None, + dest=self._apt.destination, + source=0x01, + data=struct.pack('>> import instruments as ik + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # grab channel 0 + >>> ch = kim.channel[0] + >>> # enable channel 0 + >>> ch.enabled_single = True + """ + if self._apt.model_number[0:3] != 'KIM': + raise("This command is only valid with KIM001 and " + "KIM101 controllers. Your controller is a {}." + .format(self._apt.model_number)) + + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x2B, + param2=self._idx_chan, + dest=self._apt.destination, + source=0x01, + data=None + ) + + resp = self._apt.querypacket( + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, + expect_data_len=4) + + ret_val = struct.unpack('>> import instruments as ik + >>> import instruments.units as u + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # grab channel 0 + >>> ch = kim.channel[0] + >>> # set jog parameters + >>> mode = 2 # only move by set step size + >>> step = 100 # step size + >>> rate = u.Quantity(1000, 1/u.s) # step rate + >>> # if no quantity given, SI units assumed + >>> accl = 10000 + >>> ch.jog_parameters = [mode, step, step, rate, accl] + >>> ch.jog_parameters + [2, 100, 100, array(1000) * 1/s, array(10000) * 1/s**2] + """ + if self._apt.model_number[0:3] != 'KIM': + raise TypeError("This command is only valid with " + "KIM001 and KIM101 controllers. Your " + "controller is a {}." + .format(self._apt.model_number)) + + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x2D, + param2=self._idx_chan, + dest=self._apt.destination, + source=0x01, + data=None + ) + + resp = self._apt.querypacket( + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, + expect_data_len=22) + + # unpack response + ret_val = struct.unpack(' 2000: + raise ValueError("The steps forward ({}) are out of range. It " + "must be between 1 and 2,000." + .format(steps_fwd)) + if steps_bkw < 1 or steps_bkw > 2000: + raise ValueError("The steps backward ({}) are out of range. " + "It must be between 1 and 2,000." + .format(steps_bkw)) + if rate < 1 or rate > 2000: + raise ValueError("The step rate ({} /s) is out of range. It " + "must be between 1 /s and 2,000 /s." + .format(rate)) + if accl < 1 or accl > 100000: + raise ValueError("The acceleration ({} /s/s) is out of range. " + "It must be between 1 /s/s and 100,000 /s/s." + .format(accl)) + + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_SET_PARAMS, + param1=None, + param2=None, + dest=self._apt.destination, + source=0x01, + data=struct.pack('>> import instruments as ik + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # grab channel 0 + >>> ch = kim.channel[0] + >>> # set position count to zero + >>> ch.position_count = 0 + >>> ch.position_count + 0 + """ + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x05, + param2=self._idx_chan, + dest=self._apt.destination, + source=0x01, + data=None + ) + + resp = self._apt.querypacket( + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, + expect_data_len=12) + + ret_val = int(struct.unpack('>> import instruments as ik + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # grab channel 0 + >>> ch = kim.channel[0] + >>> # move to 314 steps + >>> ch.move_abs(314) + """ + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_MOVE_ABSOLUTE, + param1=None, + param2=None, + dest=self._apt.destination, + source=0x01, + data=struct.pack('>> import instruments as ik + >>> # call the controller + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # grab channel 0 + >>> ch = kim.channel[0] + >>> # set jog parameters + >>> params = ch.jog_parameters + >>> params[0] = 2 # move by number of steps + >>> params[1] = 100 # step size forward + >>> params[2] = 200 # step size reverse + >>> ch.jog_parameters = params # set parameters + >>> # jog forward (default) + >>> ch.move_jog() + >>> # jog reverse + >>> ch.move_jog('rev') + """ + if direction == 'rev': + param2 = 0x02 + else: + param2 = 0x01 + + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_MOVE_JOG, + param1=self._idx_chan, + param2=param2, + dest=self._apt.destination, + source=0x01, + data=None + ) + self._apt.sendpacket(pkt) + + def move_jog_stop(self): + """Stops the current motor movement. + + Stop a jog command. The regular motor move stop command does + not work for jogging. This command somehow does... + + .. note:: This information is quite empirical. It would + only be really needed if jogging parameters are set to + continuous. The safer method is to set the step range. + """ + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_MOVE_JOG, + param1=self._idx_chan, + param2=0x00, + dest=self._apt.destination, + source=0x01, + data=None + ) + + self._apt.sendpacket(pkt) + + _channel_type = PiezoChannel + + # PROPERTIES # + + @property + def enabled_multi(self): + """Enable / Query mulitple channel mode. + + For KIM101 controller, where multiple axes can be selected + simultaneously (i. e., for a mirror mount). + + :setter mode: Channel pair to be activated. + 0: All channels deactivated + 1: First channel pair activated (channel 0 & 1) + 2: Second channel pair activated (channel 2 & 3) + :type mode: int + + :return: The selected mode: + 0 - multi-channel selection disabled + 1 - Channel 0 & 1 enabled + 2 - Channel 2 & 3 enabled + :rtype: int + + :raises ValueError: No valid channel pair selected + :raises TypeError: Invalid controller for this command. + + Example: + >>> import instruments as ik + >>> kim = ik.thorlabs.APTPiezoInertiaActuator.open_serial("/dev/ttyUSB0", baud=115200) + >>> # activate the first two channels + >>> kim.enabled_multi = 1 + >>> # read back + >>> kim.enabled_multi + 1 + """ + if self.model_number != 'KIM101': + raise TypeError("This command is only valid with " + "a KIM101 controller. Your " + "controller is a {}." + .format(self.model_number)) + + pkt = _packets.ThorLabsPacket( + message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, + param1=0x2B, + param2=0x00, + dest=self.destination, + source=0x01, + data=None + ) + + resp = self.querypacket(pkt, + expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, + expect_data_len=4) + + ret_val = int(struct.unpack('>> import instruments as ik + >>> import instruments.units as u + + >>> # load the controller, a KDC101 cube + >>> kdc = ik.thorlabs.APTMotorController.open_serial("/dev/ttyUSB0", baud=115200) + >>> # assign a channel to `ch` + >>> ch = kdc.channel[0] + >>> # select the stage that is connected to the controller + >>> ch.motor_model = 'PRM1-Z8' # a rotation stage + + >>> # home the stage + >>> ch.go_home() + >>> # move to 52 degrees absolute position + >>> ch.move(u.Quantity(52, u.deg)) + >>> # move 10 degrees back from current position + >>> ch.move(u.Quantity(-10, u.deg), absolute=False) """ class MotorChannel(ThorLabsAPT.APTChannel): @@ -623,7 +1270,9 @@ def position(self): data=None ) response = self._apt.querypacket( - pkt, expect=_cmds.ThorLabsCommands.MOT_GET_POSCOUNTER) + pkt, expect=_cmds.ThorLabsCommands.MOT_GET_POSCOUNTER, + expect_data_len=6 + ) # chan, pos _, pos = struct.unpack('>> import instruments as ik + >>> import instruments.units as u + + >>> # load the controller, a KDC101 cube + >>> kdc = ik.thorlabs.APTMotorController.open_serial("/dev/ttyUSB0", baud=115200) + >>> # assign a channel to `ch` + >>> ch = kdc.channel[0] + >>> # select the stage that is connected to the controller + >>> ch.motor_model = 'PRM1-Z8' # a rotation stage + + >>> # move to 32 degrees absolute position + >>> ch.move(u.Quantity(32, u.deg)) + + >>> # move 10 degrees forward from current position + >>> ch.move(u.Quantity(10, u.deg), absolute=False) """ # Handle units as follows: # 1. Treat raw numbers as encoder counts. From 1346592be3721a6eee8cc6ba87c0c66f55ec67dd Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 5 Aug 2020 17:46:41 -0700 Subject: [PATCH 043/108] Support of Teledyne-LeCroy MAUI enabled oscilloscopes (#245) * Support of Teledyne-LeCroy MAUI enabled oscilloscopes - maui.py and associated folder structure created - Communication via pyvisa-py: requirements.txt extended - Requires LXI support of oscilloscope - test suite with full coverage of all new functions - Inherits from `Oscilloscope` - read_waveform has additional argument to allow grabbing math traces in a useful way (see docstring) - Implemented features are: - Trigger control: Trigger sources enum class is automatically regenerated when user changes number of channel on their specific oscilloscope - Channel control - Math function - Measurement control - Data Sources subclass - Extensive doc strings with notes and warnings - Not all scopes will have all functions available - User needs to insure that functions are available on their MAUI oscilloscope - Updated documentation - Added a detailed jupyter notebook in examples folder - tested and linted * Remove pyvisa-py as requirement, extend docstring MAUI class Class tested with NI-VISA back end for pyvisa (instead of pyvisa-py), and works. `pyvisa-py` removed from `requirements.txt`, MAUI class docstring extended to give hints on back end installation. --- doc/examples/ex_maui.ipynb | 862 +++++++++++ doc/source/apiref/index.rst | 3 +- doc/source/apiref/teledyne.rst | 15 + instruments/__init__.py | 1 + instruments/teledyne/__init__.py | 8 + instruments/teledyne/maui.py | 1406 ++++++++++++++++++ instruments/tests/test_teledyne/__init__.py | 0 instruments/tests/test_teledyne/test_maui.py | 1099 ++++++++++++++ 8 files changed, 3393 insertions(+), 1 deletion(-) create mode 100644 doc/examples/ex_maui.ipynb create mode 100644 doc/source/apiref/teledyne.rst create mode 100644 instruments/teledyne/__init__.py create mode 100644 instruments/teledyne/maui.py create mode 100644 instruments/tests/test_teledyne/__init__.py create mode 100644 instruments/tests/test_teledyne/test_maui.py diff --git a/doc/examples/ex_maui.ipynb b/doc/examples/ex_maui.ipynb new file mode 100644 index 000000000..dce96fca7 --- /dev/null +++ b/doc/examples/ex_maui.ipynb @@ -0,0 +1,862 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MAUI Oscilloscope controller\n", + "\n", + "The middle to high-end Teledyne-LeCroy oscilloscope come with the MAUI (Most Advanced User Interface) control interface. Each of these MAUI-enabled scopes can be controlled in the same way, assuming they have the same functionality, etc. The `MAUI` class presents a control interface to remotely access and setup an oscilloscope. Not every functionality is incorporated at this point, but the most imporant and basic ones are, i.e.:\n", + " * General Oscilloscope controls, i.e., triggering\n", + " * Channels\n", + " * Math functions\n", + " * Measurement setup and data retrieval\n", + " * Waveform retrieval\n", + "Here, some detailed examples for various applications are shown. \n", + "\n", + "## Communications\n", + "These Oscilloscopes have many different ways of communicating with the host computer. This class only supports the `LXI (VXI11)` protocol, which should come by default on theses oscilloscopes. The reason for this is that this protocoll supports the NI-VISA protocol, which can be completely replaced with the open PyVISA. Thus the oscilloscope can be controlled from any OS, in fact, most of the development have taken place on Linux. *Note*: The scope that the software was developed with is an older wavesurfer 3054, which was at least supposed to support the `LXI (VXI11)` protocol. However, it could not be activated. After contacting Teledyne-LeCroy, they responded fairly quickly and sent an activation code to enable the protocol, free of charge.\n", + "\n", + "In order to successfully communicate with the oscilloscope, PyVISA requires the [pyvisa-py](https://pyvisa-py.readthedocs.io/en/latest/) backend. This should be the requirements for the package now. If not or not yet installed on your setup, you can install it by typing:\n", + "\n", + " pip install pyvisa-py\n", + "\n", + "## Importing the pre-requisites\n", + "First let's import some packages that we'll need, mostly instrumentkit of course :)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "\n", + "# if you run this script from a cloned InstrumentKit path without a full installation, leave the following line in\n", + "sys.path.insert(0, os.path.abspath('../../'))\n", + "\n", + "# import the instrument kit\n", + "import instruments as ik\n", + "import instruments.units as u\n", + "\n", + "# imports for specific functions in this script\n", + "import matplotlib.pyplot as plt\n", + "from time import sleep" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Enabling the oscilloscope\n", + "\n", + "First let us look at how to establish communications with the oscilloscope. ON the oscilloscope itself, go to `Utilities` -> `Utilities Setup` -> `Remote` and select on the left side the `LXI (VXI11)` communications protocol. Connect the oscilloscope to your local area network and check it's IP address. The example IP address that will be used here is `192.168.8.154`.\n", + "\n", + "Then you can load the oscilloscope and enable communications in the following way:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "inst = ik.teledyne.MAUI.open_visa(\"TCPIP0::192.168.0.10::INSTR\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Specifying your oscilloscope setup\n", + "\n", + "The MAUI interface works for mulitple different Teledyne-LeCroy oscilloscopes. Not all of these oscilloscopes will have the same options, so some commands might not be available on your scope. To make the oscilloscope controller versatile, the number of available channels (default 4), available functions (default 2), and available measurements (default 6) can be adjusted. The number of channels is simply how many inputs are available on the front. The number of functions is how many functions can be set up in the scopes math menu, usually labeled as `F1`, ... `Fn` in th oscilloscope software. The number of available measurements is the number of measurements that can be configured on the scope, usually labeled as `P1`, ... `Pn`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# setting and getting the number of channels\n", + "inst.number_channels = 4\n", + "inst.number_channels" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# setting and getting the number of functions\n", + "inst.number_functions = 2\n", + "inst.number_functions" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# setting and getting the number of measurements\n", + "inst.number_measurements = 6\n", + "inst.number_measurements" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Triggering the scope\n", + "\n", + "The simplest possible way to stop and start the oscilloscope from triggering is as following:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# stop the oscilloscope from triggering\n", + "inst.stop()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# start the oscilloscope in automatic triggering mode\n", + "inst.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, the four triggering states can also be controlled manually. These states are: automatic triggering `auto`, normal triggering `normal`, a single trigger `single` and no triggering `stop`. These trigger states are implemented as a `inst.TriggerState` subclass under the instrument class. Reading the trigger state (should be `auto` from just before) and then setting it to `normal` can be done in the following way:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# get the current trigger state\n", + "inst.trigger_state" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# restart the trigger with a single trigger\n", + "inst.trigger_state = inst.TriggerState.normal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition, e.g., for a measurement, a trigger can also be forced upon request. For this to work, set the oscilloscope into stop mode, then force a trigger." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Stop the triggering\n", + "inst.stop()\n", + "\n", + "# A trigger can also be forced by calling:\n", + "inst.force_trigger()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The oscilloscope will be put back into stopped mode. To continue triggering in normal mode, run:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "inst.trigger_state = inst.TriggerState.normal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to selecting the triggering state, the triggering source, type, and level can also be chosen. For most oscilloscopes, all channels and an external triggering source can be chosen from, optional settings are possible. Possible triggering sources are stored in the `enum` class `TriggerSource`, while triggering types are stored in the `TriggerType` `enum` class.\n", + "\n", + "Let's set the triggering source to the external trigger and trigger on the edge. This can be accomplished with the following commands:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "inst.trigger_source = inst.TriggerSource.ext\n", + "inst.trigger_type = inst.TriggerType.edge" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Time base\n", + "\n", + "The timebase is the same for all channels and therefore implemented on the instrument level. Setting the timebase of the scope expects a unitful value. If no units are given, seconds are assumed. To set the time per division to 20 ns and read it back out, run the following commands." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(2.e-08) * s" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.time_div = u.Quantity(20, u.ns)\n", + "inst.time_div" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To shift the timebase with respect to the trigger, a trigger delay can be called. This call is unitful as well. To set a trigger delay of 60 ns and read it back, run the following command." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(6.e-08) * s" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.trigger_delay = u.Quantity(60, u.ns)\n", + "inst.trigger_delay" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Controlling a channel\n", + "\n", + "To control a channel, several functions are implemented. The first channel is referred to as `0`, as is common in python. To create an instance of the first channel you can run:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "channel = inst.channel[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Turning a trace on and off can be done by setting the `channel.trace` with a bool. For example, to turn the trace on (no matter what state it is in) and then read its state back, run" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "channel.trace = True\n", + "channel.trace" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Control over the coupling of the specific channel is supplied via the `channel.Coupling` class. To set the coupling to $50\\,\\Omega$ and then read it back, run the following commands:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "channel.coupling = channel.Coupling.dc50\n", + "channel.coupling" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scale (i.e., the volts per division) of a channel can be set unitful as well. If no units are given it is assumed that the user means Volts per division. To set the scale to 1 V per division and read its state back, run" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(1.) * V" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "channel.scale = u.Quantity(1, u.V)\n", + "channel.scale" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the same manner, the trace can also be shifted to, let's say -2950 mV in vertical position. This offset can be set / read as following:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(-2.95) * V" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "channel.offset = u.Quantity(-2950, u.mV)\n", + "channel.offset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, after having gone through all configurations, the waveform can be read back to the computer. The waveform is reutrned as a two dimensional numpy array representing the timebase and the signal. We can directly unpack the waveform via:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "timebase, signal = channel.read_waveform()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Furthermore we know that the signal has been shifted by -2.95 V in the negative direction and by 60 ns in the positive time base direction. Let's see how the signal looks." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'Signal (V)')" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYwAAAEGCAYAAAB2EqL0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy86wFpkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAoGElEQVR4nO3deZwcdZ3/8de758iQhJBABhJCIFweiBAkIiz+VkRFZBV0hQV3V0Fxs+qq67ru4ye6i9f68Nj1/OnioiKILh6ga0QOURDwAAkYjhAC4Q4EMgRyXzPdn98fVT3p6enu6UmqZtKd9/Px6MdUV1VXfaa6pz/zPUsRgZmZ2UgK4x2AmZm1BicMMzNrihOGmZk1xQnDzMya4oRhZmZN6RzvAEZr+vTpMWfOnPEOw8yspdx+++3PRETvjhyj5RLGnDlzWLhw4XiHYWbWUiQ9uqPHcJWUmZk1xQnDzMya4oRhZmZNccIwM7OmOGGYmVlTnDDMzKwpThhmZtYUJwwzaxsbtgxwwW8e5KG+9eMdSltywjCztnHzA3187pr7+Nw19413KG3JCcPM2sazG/oBWL9lYJwjaU9OGGbWNlZv2grAhM6OcY6kPTlhmFnbWLMpKWGUfOvpXDhhmFnbWLMxSRgbtxbHOZL2lFvCkNQj6Y+S7pS0WNInauxzjqQ+SYvSxzvzisfM2l+5hLG53wkjD3lOb74FODEi1kvqAn4r6eqIuKVqvx9GxHtzjMPMdhGb0kThEkY+cksYERFAuTN0V/pwxaKZ5WZLfwmATU4Yuci1DUNSh6RFwErguoi4tcZub5Z0l6TLJc2uc5z5khZKWtjX15dnyGbWwrYMJIlik6ukcpFrwoiIYkTMBfYDjpF0eNUuPwfmRMQRwHXAJXWOc2FEzIuIeb29O3SHQTNrY1uLLmHkaUx6SUXEauAG4OSq9asiYkv69FvA0WMRj5m1p3KVVNHdanORZy+pXklT0+XdgNcA91XtM7Pi6anAkrziMbP2t2UgTRglJ4w85NlLaiZwiaQOksT0o4i4UtIngYURsQB4v6RTgQHgWeCcHOMxsza3tSJhRASSxjmi9pJnL6m7gKNqrD+/Yvk84Ly8YjCzXUu50RuSpNHZ4YSRJY/0NrO2Ua6SAhhwtVTmnDDMrG1sGSjRlZYq3I6RPScMM2sLA8USxVIwsTupaXdPqew5YZhZWyiPwZjYnUxtXiw6YWTNCcPM2kK5h9RuacJwG0b2nDDMrC0MK2E4YWTOCcPM2kJ/WgXV01kuYZQa7W7bwQnDzNrCQFrC6OlyCSMvThhm1hb6BxNG8rXmNozsOWGYWVvYOpBWSaUljJITRuacMMysLZTbLHbrci+pvDhhmFlb6HcbRu6cMMysLZSrpDwOIz9OGGbWFspVUttKGO5WmzUnDDNrC8N6SXlqkMw5YZhZW6geuOc2jOw5YZhZWxjW6O3ZajPnhGFmbaGcMHbr9sC9vOSWMCT1SPqjpDslLZb0iRr7TJD0Q0nLJN0qaU5e8ZhZeytXSZXHYXh68+zlWcLYApwYEUcCc4GTJR1btc+5wHMRcQjwJeBzOcZjZm2sXMKY4IF7ucktYURiffq0K31Uv4OnAZeky5cDr5Lku7ab2aj1p/fDcKN3fnJtw5DUIWkRsBK4LiJurdplFvA4QEQMAGuAvfKMyczaU7lEUe5W60bv7OWaMCKiGBFzgf2AYyQdvj3HkTRf0kJJC/v6+jKN0czaw9aqXlKefDB7Y9JLKiJWAzcAJ1dtegKYDSCpE9gDWFXj9RdGxLyImNfb25tztGbWisoD9SZ0Jl9rJZcwMpdnL6leSVPT5d2A1wD3Ve22ADg7XT4duD7C77KZjV5/sURB0NWRVkm5hJG5zhyPPRO4RFIHSWL6UURcKemTwMKIWAB8G7hU0jLgWeCsHOMxsza2tViis6NAoZD0m3EJI3u5JYyIuAs4qsb68yuWNwNn5BWDme06BopBd0eBDpUTxjgH1IY80tvM2kJ/sURnh0gLGK6SyoEThpm1hf5iiS5XSeXKCcPM2kJ/dZWUSxiZc8Iws7awrUoqSRieSip7Thhm1hYGipFWSSXPXcLInhOGmbWFrWkbRofbMHLjhGFmbSFp9K6sknLCyJoThpm1hcEqKTd658YJw8zawtZiic6CKqqkxjmgNuSEYWZtob9Yoruz4IF7OXLCMLO2UK6SkoTkRu88OGGYWVvoT6ukADokJ4wcOGGYWVsYKAWdHUnCKBREej8ly5AThpm1hVIpBntIFVwllQsnDDNrC8WIwR5SHZK71ebACcPM2kKxFIMTDxYK8sC9HDhhmFlbKJVicGrzgksYuXDCMLO2UIxtJYwOlzBy4YRhZm2hWGJoCcP5InO5JQxJsyXdIOleSYsl/WONfU6QtEbSovRxfq1jmZmNJCIGR3kX5Lmk8tCZ47EHgH+OiDsk7Q7cLum6iLi3ar+bI+L1OcZhZruAIb2kCvLUIDnIrYQRESsi4o50eR2wBJiV1/nMbNdWHDIOw1VSeRiTNgxJc4CjgFtrbD5O0p2Srpb0ojqvny9poaSFfX19eYZqZi2qVNpWwigUPHAvD7knDEmTgSuAD0TE2qrNdwAHRMSRwP8D/rfWMSLiwoiYFxHzent7c43XzFpT9cA9V0llL9eEIamLJFl8PyJ+Ur09ItZGxPp0+SqgS9L0PGMys/ZUKrGtSqrgyQfzkGcvKQHfBpZExBfr7DMj3Q9Jx6TxrMorJjNrX0kJI1kueLbaXOTZS+p44K3A3ZIWpes+AuwPEBHfAE4H3i1pANgEnBXhd9nMRq9yahBXSeUjt4QREb8FNMI+XwO+llcMZrZrKI+5GBy4V3AvqTx4pLeZtbzyNCDbpgbxwL08OGGYWcsrVpcw5Lmk8uCEYWYtr9zA3eG5pHLlhGFmLa9cwqicrdZVUtlzwjCzllfODaqYfNC9pLLnhGFmLa9cmhhaJeWEkTUnDDNrecWqNowOj/TOhROGmbW8wXEYFbPVukoqew0H7knaDzgL+D/AviSjse8BfgFcHRGl3CM0MxtBdQnDA/fyUTdhSPoOyf0rrgQ+B6wEeoDnAScDH5X04Yi4aSwCNTOrZ1gvKXl68zw0KmF8ISLuqbH+HuAnkrpJ54UyMxtPpbSuY8jAPRcxMteoDeN1aZVUTRGxNSKW5RCTmdmobKuSSp67SiofjRLGvsAfJN0s6T2SfOciM9spFasavTvkgXt5qJswIuKfSKqc/hV4MXCXpGsknS1p97EK0MxsJMOmBinguaRy0LBbbSRujIh3A/sBXwI+ADw9BrGZmTWlutHbA/fy0dT9MCS9mKR77ZnAM8B5eQZlZjYa1bPVei6pfDTqVnsoSZI4CygCPwBOioiHxig2M7OmlGJ4CcNVUtlrVMK4BrgMOLNO91ozs51CsdZcUh5WnLlGCePQkUZyS1K9e3BLmg18F9gHCODCiPhK9euBrwCnABuBcyLijlHEb2Y2bLbajoIH7uWhUaP39ZLeJ2nI4DxJ3ZJOlHQJcHaD1w8A/xwRhwHHAv8g6bCqfV4HHJo+5gMXjPo3MLNdXnUvqY6CB+7loVEJ42TgHcBlkg4EVpNMDdIB/BL4ckT8qd6LI2IFsCJdXidpCclUI/dW7HYa8N20lHKLpKmSZqavNTNrSnUvKfmOe7momzAiYjPwX8B/SeoCpgObImL1aE8iaQ5wFHBr1aZZwOMVz5en64YkDEnzSUog7L+/ZyMxs6FK1b2k3K02F01Nbx4R/RGxYjuTxWTgCuADEbF2tK9Pz39hRMyLiHm9vR5wbmZD1bofhqukspfr/TDSkskVwPcj4ic1dnkCmF3xfL90nZlZ06qnBpFnq81Fbgkj7QH1bWBJRHyxzm4LgLcpcSywxu0XZjZawxq9PZdULpoa6b2djgfeCtwtaVG67iOkU6JHxDeAq0i61C4j6Vb79hzjMbM2VUwHAAzeD6PggXt5aDTSex3J+Ilhm0immZrS6MAR8dt030b7BPAPTcRpZlbXtqlBkufuJZWPRr2kPCOtmbWE4eMwcJVUDpqukpK0N8k4DAAi4rFcIjIzG6Xht2h1lVQeRmz0lnSqpAeAh4EbgUeAq3OOy8ysaeUSRnkchiQioM7MRbadmukl9SmSqT3uj4gDgVcBt+QalZnZKAwrYaSJw7VS2WomYfRHxCqgIKkQETcA83KOy8ysadWz1ZZ/evBetpppw1idjta+Cfi+pJXAhnzDMjNrXrnmqTxbbfmnB+9lq5kSxmnAJuCfSO6R8SDwhjyDMjMbjWFTg6hcJeWEkaURSxgRUVmauCTHWMzMtku9NgxXSWWrmV5SfynpAUlrJK2VtE7Sdk0iaGaWh1q9pADfdS9jzbRhfB54Q0QsyTsYM7PtMXwcRrLeVVLZaqYN42knCzPbmRWr74dRrpJywshUMyWMhZJ+CPwvsKW8ss505WZmY656apBtVVJOGFlqJmFMIZlJ9qSKdQE4YZjZTqHWbLXggXtZa6aXlKccN7Od2rZG7+R5OXG4SipbIyYMSV+tsXoNsDAifpZ9SGZmo1Pd6F1uy3CVVLaaafTuAeYCD6SPI0hupXqupC/nFpmZWZOqpwYpuJdULpppwzgCOD4iigCSLgBuBl4O3J1jbGZmTSlFIG1r7PbAvXw0U8KYBkyueD4J2DNNIFtqv8TMbOwUSzFYHQVQ8NQguWgmYXweWCTpO5IuBv4E/IekScCv6r1I0kWSVkq6p872E9LR44vSx/nb8wuYmRUjBtstoDJhjFdE7amZXlLflnQVcEy66iMR8WS6/C8NXnox8DXguw32uTkiXt9MoGZm9URsa7eA5Bat4CqprNUtYUh6QfrzJcBM4PH0MSNd11BE3AQ8m1GcZmZ11auScsLIVqMSxgeB+cAXamwL4MQMzn+cpDuBJ4EPRcTiWjtJmp/Gwv7775/Bac2snRRLtauk3ISRrboJIyLmpz9fmdO57wAOiIj1kk4hmXrk0DqxXAhcCDBv3jx/BMxsiFLEYM8o8FxSeWlUJfVSSTMqnr9N0s8kfVXSnjt64ohYGxHr0+WrgC5J03f0uGa26xlWJeVutblo1Evqv4GtAJL+HPgsSQP2GtL/9neEpBlKO01LOiaNZdWOHtfMdj2lYb2kkp/hEkamGrVhdEREudH6TODCiLgCuELSopEOLOky4ARguqTlwMeALoCI+AZwOvBuSQMkt4A9K/zumtl2qC5hdLjROxcNE4akzogYAF5F2ujcxOsAiIi3jLD9ayTdbs3MdkixxJA2jILbMHLR6Iv/MuBGSc+QlABuBpB0CEm1lJnZTiGpktr23L2k8tGol9SnJf2aZAzGLyuqiwrA+8YiODOzZgyrkvLAvVw0rFqKiFtqrLs/v3DMzEav3tQgrpLKVjNzSZmZ7dRKdUZ6ux9NtpwwzKzlFUt1Bu6Vxiui9uSEYWYtrxTbShXguaTy4oRhZi2vupdUuYThKqlsOWGYWcsbPlttut4JI1NOGGbW8oZNDeK5pHLhhGFmLa/e1CAuYGTLCcPMWl69+2G4hJEtJwwza3mlqJ7ePPnpNoxsOWGYWcurNw7DvaSy5YRhZi2vGNSpkhqviNqTE4aZtbxkapBtzz2XVD6cMMys5blKamw4YZhZyytFVE0Nkvx0L6lsOWGYWcurLmF44F4+cksYki6StFLSPXW2S9JXJS2TdJekl+QVi5m1t+r7YXjgXj7yLGFcDJzcYPvrgEPTx3zgghxjMbM2FvVmq3XGyFRuCSMibgKebbDLacB3I3ELMFXSzLziMbP2VazuJeVbtOZiPNswZgGPVzxfnq4bRtJ8SQslLezr6xuT4MysdVRPDdKVZox+D8TIVEs0ekfEhRExLyLm9fb2jnc4ZraTGT41iOgsyAkjY+OZMJ4AZlc83y9dZ2Y2KtW9pAC6Ogr0F10llaXxTBgLgLelvaWOBdZExIpxjMfMWlT1/TAAujrE1gGXMLLUmdeBJV0GnABMl7Qc+BjQBRAR3wCuAk4BlgEbgbfnFYuZtbfq+2EAdHcWXCWVsdwSRkS8ZYTtAfxDXuc3s11H/SopJ4wstUSjt5lZI6WqcRjgNow8OGGYWctLShhD13V1iK0uYWTKCcPMWl711CCQljDc6J0pJwwza3mlUtBZcKN33pwwzKzlDdToJeU2jOw5YZhZSyul80V1FIZ+nbkNI3tOGGbW0gYGE8bQ9e5Wmz0nDDNraaWoV8JwwsiaE4aZtbT6JQzRP+A2jCw5YZhZSyvWbcMouA0jY04YZtbSBhPG0E5STOzuYNPW4jhE1L6cMMyspQ2UklJER1Wd1KQJnazfMjAeIbUtJwwza2lpvhg2cG/3CZ1s2DpA+L7emXHCMLOWNljCqBq4N2lCJxGw0dVSmXHCMLOWVi5hVE9vPmlCcvcGV0tlxwnDzFraYAmjukqqxwkja04YZtbStg3cqyphdKcJY7MTRlacMMyspW0buDc0YUxOSxgbXMLITK4JQ9LJkpZKWibpwzW2nyOpT9Ki9PHOPOMxs/YzUKyTMNyGkbnc7uktqQP4OvAaYDlwm6QFEXFv1a4/jIj35hWHmbW3cpVUdbdaN3pnL88SxjHAsoh4KCK2Aj8ATsvxfGa2CypXSVXfca9cwnCVVHbyTBizgMcrni9P11V7s6S7JF0uaXaO8ZhZGyrfD6O6hFFOGOucMDIz3o3ePwfmRMQRwHXAJbV2kjRf0kJJC/v6+sY0QDPbuQ02elcN3OvpKtBRkEsYGcozYTwBVJYY9kvXDYqIVRGxJX36LeDoWgeKiAsjYl5EzOvt7c0lWDNrTaU6vaQkMam7gw1bPNI7K3kmjNuAQyUdKKkbOAtYULmDpJkVT08FluQYj5m1oXrdaiGpllrncRiZya2XVEQMSHovcC3QAVwUEYslfRJYGBELgPdLOhUYAJ4FzskrHjNrT8VGCaOn01VSGcotYQBExFXAVVXrzq9YPg84L88YzKy9FQcbvYdXmHiK82yNd6O3mdkO2datdvi2yU4YmXLCMLOWtm3g3vCvMyeMbDlhmFlL29boPXzbpAluw8iSE4aZtbQt/Um32e6OjmHbXMLIlhOGmbW0zQPJ/TB6umpXSW3Y4tu0ZsUJw8xaWrmEMaGrRgmjp5NSwKZ+D97LghOGmbW0LQ1KGIMz1nrwXiacMMyspW3uLyJBd41W78kTklKH2zGy4YRhZi1tc3+Rns4OpFpTg3QBeD6pjDhhmFlL29xfqlkdBTApLWGs29I/liG1LScMM2tpm/uL9NRo8AbY3SWMTDlhmFlL2zxQqpswJvckjd7fvOmhsQypbTlhmFlL29xfZEJn7a+yA/acyLSJXdzz5Bq2pr2pbPs5YZhZS2tUJVUoiA+e9Hw2bi1y8lduGuPI2o8Thpm1tL51W5g+eULd7W9+ySyOnD2Vh/o28Mz6LXX3s5E5YZhZS3tq7WZm7FE/YUzs7uSjp7wQgLuWrx6jqNqTE4aZtazN/UVWb+xnxpSehvsdPmsKBcGlf3iU/77xQZY/t3GMImwvud5xz8wsT4+uSr74Z03breF+E7s7OfagvbhhaR83LO3jwb71fP70I8cixLayyySMOx57jot/9whLVqxl1rTdWLd5gFlTh37IlqxYywtnTgHg/qfXsam/SGdBTOjs4JC9Jw875n1PreUFM6YMWffosxvpndzNY89u5Mj9pgKw6PHVg8eFpM5VSn7O2KOHLQOlEf9DqvTUms1M6CrwzPqtdBTgoOnDY6v26KoN9O4+gUdXbRyM5cG+9cyeNpHuOj1MxtKm/iJPrdnMgdMnDVn/8DMbmLFHD/tMmcDqjf088PR6CgUxZ6+JPNi3nhfMmEIA961Yy8TuDnp3n8DE7sYf6/ufXsfBe0+mo8bI4LJiBA+uXA/AgdMn8VDfBkoRTJvYzYSuAtMmdo/4Ow2Nq4eJ3cMbZpc+tY5D95lMoUEs1ZasWMuc6ZNqXq/xtmTFWl6y/zSOPXhPBorBnctXs3bTtmk57n963eD1LBTEgdMn1rzxUbX+Yok/PbaaF87cneXPbRr8DD+1ZjMAL541dcRjfO/cl7FloMTff+92frVkJa/90k0c1DuJR1ZtJCI4qHcSi59M4j/6gGlcd+/TbNgywL5Td6OjIHq6Orjz8dVIsHpjPy+cOaXme5qnV71wb06bO2tMz1lJeU77K+lk4CtAB/CtiPhs1fYJwHeBo4FVwJkR8UijY86bNy8WLlw46lhuWLqSt3/ntiHrDthr4uAf6sPPbABg+uRuVm/sH7wpS9nUiV1DviTWbOrn2Q1b6e4oDP53M1Aq8fizm2qef69J3UzZrWvIuSrN2WtizakNqkUEj6waWpzed4+emjN1llXHNX3yBCZ0FnhidbJuZ/jSKV+T/abtRlc6J1B/scTy52pfz7J9pkzg6bVDGzIb/T6b+4usSL9kGu1X6z1q9hz1jlH9mo1bBwZjb/Y9WLd5YEjDbeX1Gm/1rtmsqbvR3VkYcu0rbc+1nDaxi6np3+P+e07kO+e8lEKhuaS74M4n+cxVS2rGUssBe00cLMlUG+u/nbccM5v5f37wdr1W0u0RMW9Hzp9bwpDUAdwPvAZYDtwGvCUi7q3Y5z3AERHxLklnAW+KiDMbHXd7EwbAjxc+zr9cftfg84c/c8rgl/R7/+cOrrxrBRedM4/r71vJ9255bMhrP/aGw3j78QcOO9bZxx3AJ047HEi+4A796NU1z/2lM4/kTUftB8CcD/9i2PZHPvsXTf0OEcGB5101ZN1tH301vbvXb/Srjus757yUlx64J4d/7NpRnTtP5Wty36dOHuwiubm/yAv+7ZqGr7v2A3/OJ36+mN8/uGpwXaPf56k1mzn2M79m+uRuFv7ra+rud/SnrmPVhq11tzdzzf76m7c0jOuhvvWc+IUbOWTvyfzqg68Y8XgAv3/wGf76m7cOPl/67yczoXNs/8ut512X3s41i58atv6eT7yWyRM6WbFmE8d95voh27o7Ctz/6deNeOzqv5lPvfFw3nrsAdsd67KV63n1F29sat+HP3MKJ/znb2omjZ3hb6dZWSSMPKukjgGWRcRDAJJ+AJwG3Fuxz2nAx9Ply4GvSVLklMXecOS+LH1qHS+aNYWBYgz5j/781x/GrKm78fJDepk7expLn1rH6o39bC2WmLPXJP5q3uyax3rPKw8ZXNfVUeDTbzqcSd2dLFmxlhl79HDPE2uZOrGL1x0+c3C/r//1S9i4dYD7nlrHYTOn0NnRfHWEJL585lyKpeDy25czb860hsmiVlzHHzKd7s4C//b6wzhq/6lNnztPP5x/LEufXjekP31PVwefPO1FTOzu5PfLnuH4Q6bzxOpN9HQV2GdKDw+uXM+he0/m86cfwfdvfYyZe/TUrDqsNGOPHv7ltc/nlc/fu+F+l577Mm5YupKCxL5Te7j90efo7ihwyN6TG3bhrPS5Nx/BZX98jBl14jpw+iT+8VWH8oYjZ9Z4dW3HzNmTd73iYF44c3fWbOrfaZIFwMdPfREH7DWRfab0cO3ip9g8UOKkw/ZhcjrF+IwpPXzopOcxe8+JPLZqI92dBY47eK+mjv2T9/wZ5//sHt5x/IHc++Ra3vySHauWObg3ufYH9U5ixZrNDBRLzN5zIt+8+SE2bS1y5H5TOe7gvShISOLjp76IBYueZN3mfhY/uZY9duviI2nPq11JniWM04GTI+Kd6fO3Ai+LiPdW7HNPus/y9PmD6T7PVB1rPjAfYP/99z/60UcfzSVmM7N2lUUJY+eo/BxBRFwYEfMiYl5vb+94h2NmtkvKM2E8AVTW4+yXrqu5j6ROYA+Sxm8zM9vJ5JkwbgMOlXSgpG7gLGBB1T4LgLPT5dOB6/NqvzAzsx2TW6N3RAxIei9wLUm32osiYrGkTwILI2IB8G3gUknLgGdJkoqZme2Ech24FxFXAVdVrTu/YnkzcEaeMZiZWTZaotHbzMzGnxOGmZk1xQnDzMyakutcUnmQ1AeM18i96cAzI+419hzX6Diu0XFco7OzxvX8iNh9Rw7QcrPVRsS4jdyTtHBHR0rmwXGNjuMaHcc1OjtzXDt6DFdJmZlZU5wwzMysKU4Yo3PheAdQh+MaHcc1Oo5rdNo2rpZr9DYzs/HhEoaZmTXFCcPMzJrihFFF0hmSFksqSarbNU7SI5LulrSosruapD0lXSfpgfTntLGISdJsSTdIujfd9x8rtn1c0hNprIsknbKjMY0mtnS/kyUtlbRM0ocr1h8o6dZ0/Q/TmY13NKYR3wNJr6y4HoskbZb0xnTbxZIertg2d0djGk1s6X7FivMvqFg/XtdrrqQ/pO/1XZLOrNiW6fWq91mp2D4h/d2XpddiTsW289L1SyW9dkfi2I64Ppj+/d0l6deSDqjYVvP9HKO4zpHUV3H+d1ZsOzt93x+QdHb1a4eJCD8qHsALgecDvwHmNdjvEWB6jfWfBz6cLn8Y+NxYxATMBF6SLu9Ocj/1w9LnHwc+NF7Xi2S24geBg4Bu4M6K2H4EnJUufwN4dwYxjeo9APYkmS15Yvr8YuD0nK5XU7EB6+usH5frBTwPODRd3hdYAUzN+no1+qxU7PMe4Bvp8lnAD9Plw9L9JwAHpsfpGMO4XlnxGXp3Oa5G7+cYxXUO8LUar90TeCj9OS1dntbofC5hVImIJRGxdAcOcRpwSbp8CfDGsYgpIlZExB3p8jpgCbBjNz7OKDYq7u8eEVuBHwCnSRJwIsn93CGj68Xo34PTgasjYmMG5x7Jdn8+xvN6RcT9EfFAuvwksBLIYxBtzc9Kg3gvB16VXpvTgB9ExJaIeBhYlh5vTOKKiBsqPkO3kNw0Lm/NXK96XgtcFxHPRsRzwHXAyY1e4ISx/QL4paTbldxzvGyfiFiRLj8F7DPWgaVF9KOAWytWvzctKl+URTXZKM0CHq94vjxdtxewOiIGqtbvqNG+B2cBl1Wt+3R6vb4kaUIGMY02th5JCyXdUq4qYye5XpKOIflv9sGK1Vldr3qflZr7pNdiDcm1aea1ecZV6Vzg6orntd7PsYzrzen7c7mk8p1QR329Wm5qkCxI+hUwo8amj0bEz5o8zMsj4glJewPXSbovIm6q3CEiQlJT/ZYziglJk4ErgA9ExNp09QXAp0iS3KeALwDvGMUxM4ktS41iqnwy0nsgaSbwYpIbfZWdR/LF2U3Sd/3/Ap8c49gOSD9fBwHXS7qb5Itxu2R8vS4Fzo6IUrp6h65Xu5H0t8A84BUVq4e9nxHxYO0jZO7nwGURsUXS35OUzk7cngPtkgkjIl6dwTGeSH+ulPRTkqLhTcDTkmZGxIr0j2vlWMUkqYskWXw/In5SceynK/b5JnDlaI6bQWz17u++CpgqqTP9T7HWfd9HHZOk0bwHfwX8NCL6K45d/m97i6TvAB9qJqYsY6v4fD0k6TckJcYrGMfrJWkK8AuSfxRuqTj2Dl2vKvU+K7X2WS6pE9iD5LPUzGvzjAtJryZJwq+IiC3l9XXezywSxohxRcSqiqffImmzKr/2hKrX/qbRyVwltR0kTZK0e3kZOAm4J91ceZ/ys4Ex+Q88rcP9NrAkIr5YtW1mxdM3sS3WsVLz/u6RtLzdQNKGANldr9G8B2+hqjqqfL3Sa/pGsr1eI8YmaVq5WkfSdOB44N7xvF7p+/ZT4LsRcXnVtiyvV83PSoN4TweuT6/NAuAsJb2oDgQOBf64A7GMKi5JRwH/DZwaESsr1td8P8cwrsq//1NJ2jchKVWflMY3jeR7rLKkPVzWrfat/iD5Ql0ObAGeBq5N1+8LXJUuH0TSG+FOYDHJf1zl1+8F/Bp4APgVsOcYxfRykiqnu4BF6eOUdNulwN3ptgXAzLG8XunzU0h6bj1Ydb0OIvmjXgb8GJiQQUw13wOSaoJvVew3h+S/rELV669Pr9c9wPeAyRlerxFjA/4sPf+d6c9zx/t6AX8L9Fd8thYBc/O4XrU+KyRVXKemyz3p774svRYHVbz2o+nrlgKvy+p9azKuX6V/A+Xrs2Ck93OM4voMyffUnST/cLyg4rXvSK/jMuDtI53LU4OYmVlTXCVlZmZNccIwM7OmOGGYmVlTnDDMzKwpThhmZuMonX1hpaQd7r6tBpNqZsEJw8aFpL0qPtRPadtsuusl/dcYxTBP0ldzPP5cNZgZOO/zNzjvUZK+3WB7r6RrxjKmXdzFjDCHU7Mimc9qbkTMJRnNvRH4ZRbHhl10pLeNv0hGn86FZPp1ktk8/3OMY1gILBxxx+03l2Qsw1XVG9LR2nmfv3yegarVHwH+vd5rIqJP0gpJx0fE7/KMzyAiblLFFO0Akg4Gvk4yweNG4O8i4r5RHjrzSTVdwrCdiqQTJF2ZLn9c0iWSbpb0qKS/lPR5JfchuSadCgVJR0u6UclEkNdWjWwtH/cMSfdIulPSTXXOdZGk30h6SNL7K177NiUTt90p6dJ0Xa+kKyTdlj6OrzpfN8ngqTPTktOZ6TkulfQ74NKq8/cquRfFYknfSn/f6em2f1Nyv4PfSrpM0ofS9Qen1+H29Bq9IF1/saRvSLqVbdNAlOPaHTgiIu5Mn7+ioqT3p3Q7wP8Cf7P976TtoAuB90XE0SRTrWxPqbvWpJo7JssRh374sT0PKu7XQTK3zZUV638LdAFHkvyn9bp0209JpqHoAn4P9KbrzwQuqnGOu4FZ6fLUOuf6Pcm9FKaTzE3UBbyIZBTt9HS/8ijo/yGZgBJgf5IpWarPeQ4V9yFIz3E7sFuN838NOC9dPplk1P504KUko4Z7SO5z8kDFtfo12+5R8TKSKTIgqeK4khr3giC5Z8MVFc9/DhyfLk8GOtPlWcDd4/3Z2FUeJLMO3FPxPmxi6Kj6Jem2vyQZUV/9uLbqeDOBPqAryzhdJWU7u6sjol/JbK0dQLlu/W6SP7LnA4eTzBhMus+KGsf5HXCxpB8BP6mxHeAXkUwYt0XSSpJpvk8EfhwRzwBExLPpvq8GDkvPCTBF0uSIWD/C77MgIjbVWP9ykmlWiIhrJD2Xrj8e+FlEbAY2S/o5DM5K/GfAjytiqJxW/McRUaxxnvIXSdnvgC9K+j7wk4hYnq5fSTK9i429Ask09nOrN0QyqWi9z2+lYZNqZsEJw3Z2WwAioiSpP9J/n4ASyedXwOKIOK7RQSLiXZJeBvwFcLuko+udK1Wk8d9HATg2/SIfjQ2j3L/R+Wt+qYxwnk0kpRUAIuKzkn5BMh/R7yS9NpK68p50XxtjEbFWyS1vz4iIHyv5j2CwGrFJbyGZdj5TbsOwVrcU6JV0HCRTvEt6UfVOkg6OiFsj4nyS/7BnV+9Tx/XAGZL2So+zZ7r+l8D7Ko4/t8Zr15FUIzXjdyT/FSLpJJJbZpbXv0FST1qqeD0kXyrAw5LOSF8jSUc2cZ4lwCEVcR8cEXdHxOdIZj59QbrpeYz9rMa7JEmXAX8Ani9puaRzSdqPzpVUnuC02bvolW+gNhu4MetYXcKwlhYRWyWdDnxV0h4kn+kvk/yRVfoPSYeSlEh+TTJz5ysYQUQslvRp4EZJReBPJG0T7we+Lumu9Jw3Ae+qevkNwIclLSKZMbSRTwCXSXoryZfHU8C6iLhN0gKSmYafJqmKK99I6W+ACyT9K0l7yw/S36vR73OfpD0k7R7JrXw/IOmVJCW2xWy7S9wrSe59YTmLiLfU2bRdXW0j4hFyuj2zZ6s12wkouV9CMSIG0tLSBeXqpnLbiKSJJIlpfqT3b9/Oc/0TSTL6VoN9bgJOi+Rez2aASxhmO4v9gR9JKgBbgb+r2HahpMNI2hUu2ZFkkboAOKPeRkm9wBedLKyaSxhmZtYUN3qbmVlTnDDMzKwpThhmZtYUJwwzM2uKE4aZmTXl/wNvDW/WklPOOAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(timebase, signal)\n", + "plt.xlabel(\"Time since trigger (s)\")\n", + "plt.ylabel(\"Signal (V)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The offset in horizontal and vertical access are automatically taken into account such that the signal that is returned can directly be interpreted in time relative to the trigger and in the signal in absolute voltage. Clearly, this peak is almost 4 V heigh and very short. Let us move the peak in horizontal and vertical direction to the center of the oscilloscope display and read back one waveform with 1 V per division on the vertical axis and one waveform with 250 mV per division. The latter will surely clip the peak at the top." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, '1 V / division: Signal visible')" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtAAAAEWCAYAAABPDqCoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy86wFpkAAAACXBIWXMAAAsTAAALEwEAmpwYAABHl0lEQVR4nO3deZxcZZX/8c+p6n3L2iErJIEk7FvCEhFQEUVQGRFZRJYRZdzXGX+iDuPojOM44zIoLlGRRVbZRAURlV2ChECAEAIhZN86ne70Xt1ddX5/3FudStNd6equ251Ofd+vV17prnrq3lPd1U+dOvfc55q7IyIiIiIiAxMb6QBEREREREYTJdAiIiIiIjlQAi0iIiIikgMl0CIiIiIiOVACLSIiIiKSAyXQIiIiIiI5UAItI87MLjOzxzO+bzGz2QN43FfM7BcDGHe/mV061DiHwsz2D59XfBj25WZ2UI6PeYuZbcj4frmZvSXfsQ0gjuvM7D+Ge78ihc7MZoZzR1H4/YDmTTM72cxWDmDcgObrqA3X3GZmD5vZRwbxuIvM7E8DGPdTM/vX8Ovd5u8+xmpejYAS6L2EmZWa2S/NbK2ZNZvZc2b2roz705NbS8a/f+31+GvNrMnMtpjZF/IU15fN7NE+bp9oZp1mdniWxz5gZu/IdZ/uXuXuqwcw7lvuvscJyt3f5e7X5xpHrsxsupndaWbbzWynmb1oZpeFMawLn1cy6jjywd0Pc/eHRzoOkX2ZmX3KzJaYWcLMrssy7kQzazWzqj7ue9bMPpXlsVea2bdyjW2g86a7P+bu8wYwbkDz9VCZWYmZfdfMNoTvk2vM7AcZcezVc5u73+Tue3zfdPePufs3hyMm6VvRSAcgPYqA9cCpwDrgTOB2MzvC3ddkjBvr7t19PP7rwBzgAGAy8JCZveTufxxiXL8G/sPMZrn76xm3XwC84O4v9vUgM6sEFgCPDHH/o8mNwDKC30ECOILgdyEi0pdNwH8A7wTK+xvk7ovDCuO5wHXp28MCxqHALVn2cRbw5XwEO0pcSfDeczywmWA+PmVEI5J9kirQewl3b3X3r7v7GndPufvvgdeB+QPcxKXAN929wd1XAD8HLutrYNgy8YSZfd/MGs1stZm9Kbx9vZltSx+6c/cNwF+Bi3tt5hLghizxnAY84e6JPvY/wczuDavlfwcO7HW/m9lBZnZCWE2PZ9z3PjN7Pvz662b26/DrMjP7tZnVh8/paTPbL7yv51CamcXM7GthpX+bmd1gZmPC+9JV/kvNbF1YSf5qlufY23HAdeHvstvdn3X3+3ttO314dJaZPRoebfizmV2T8VyyxmFmx5vZk+Hz3GxmPzKzkoEEaGbjzexXZrbJzBrM7J5+xq0xs7dn/JzvMLPbwniXmtlRvcZeaWYvhdv8lZmVZdz/bguOqDSa2d/M7MiM+44Jt9dsZrcBZYgUCHe/y93vAeoHMPx6gnk30yXAfe7e5+PNbBwwF3iyj/viZva/4fyymiDRzrz/YTP7iAVHNxst42ijmdWaWbuZTbI3tn/9PzPbGP5NrzSz08Lbe+br8Pv3WtBO0Rju65CM+9aY2T+b2fMWHM27LXNO2YPjgLvdfZMH1rh7z3tVr7mt3MyuD+etFWb2pV7Ppd84zGycmf3ezOrCx//ezKbvKTgzmxr+7MZn3HZM+HsotoyWRgt834L3qiYzeyH9e7A+2jIsaJPZHsZ9UZYY+p2TZeCUQO+lLEj+5gLLe9211oJDU78ys4nh2HHAFILqZ9oy4LAsuzgBeB6YANwM3Eow8RwEfAj4ke06XHg9GQm0mc0Djg4f158zgT/0c981QEcY84fDf2/g7k8BrcDbMm7+YD/7vRQYA8wIn9PHgPY+xl0W/nsrMBuoAn7Ua8ybgXkEHwKuSk/sZvZmM2vs5zkBLAauMbMLzGz/LOMIn8Pfw1i/zhs/oPQbB5AEPg9MBBaG939iD/tLuxGoIHhtTAK+P8DHnQ38Bhgfxn6PmRVn3H8RQRXtQILX7dcgeGMArgX+ieC5/gy4N3xTLgHuCWMaH27//QOMR6TQ3AicYmYzICgGEMyH2dos3gn8pZ/WsY8C7waOIajYntvXBsIiyF3AhRk3nwc84u7bMseG7w2fAo5z9+pw/2t6b9PM5hJUzT8H1AL3Ab+z3QsB5wFnALOAI8koCIWJ35v7ec6LgS+Y2SfM7Agzs37GAfwbMJPgveB0gve+3vqLIwb8iqDCvT/B+03v95I3cPdNBB9oMue6DwJ3uHtXr+HvIKiezyV4fzuP/j9sTSZ4T5hG8H64KPx97CbbnLyn2GV3SqD3QmFichNwvbu/HN68nSDBPYCgKl0djoEgCQTYmbGZneGY/rzu7r8KJ9bbCBLPb7h7wt3/BHQSJNMAdwP7mdmbwu8vAe5397os2z+TYFLs/dziBBPHVWGl9kWyvwHcQjhxm1l1uN2+Dld2EUwGB7l70t2fcfemPsZdBHzP3Ve7ewvB4b4LLKwMh/7d3dvdfRnBB5GjANz9cXcfmyXWDwCPAf8KvB5+wj+uj5/B/gS/y6vcvdPdHwfu7WN7/cXxjLsvDqvcawgmwFOzxJXe7xTgXcDHwiMVXe4+0BabZ9w9PcF/j6BSfGLG/T9y9/XuvgP4T3a92V4B/Mzdnwp/L9cTtLecGP4rBn4QxnIH8PQA4xEpKO6+HniYXR+2TwNK6b9QAUFV+Q3zcOg8gr+99N/tf2XZzs0EbXtp/RUykmFMh5pZcVj9fa2PcecDf3D3B8M55X8JWljelDHm6rCKvAP4HUHRBgB3HxvOm335L+C/Ceb6JcBG6/9kyPOAb4Xz4Qbg6j7G9BmHu9e7+53u3ubuzQTz3h7n4dDN7HpfM4KfbV8/zy6C9/GDAXP3Fe6+Oct2/zV8D3+E4HVxXh9jss3JkgMl0HuZsKpwI0EC23NiiLu3uPuSMGnaGt73jjCpbAmH1WRsqgZozrKrrRlft4f76H1bVXh7G0F18JLwj/0isrRvmNkRwM5wwu+tll393mlrs8R5M3BO+On4HGCpu/c1/kbgAeBWC9oTvtOrQpo2tdf+1obx7Jdx25aMr9vY9QElq3AS/rK7HxZu7zmCSm3vCshUYEf4c03r62fVZxxmNjc8XLjFzJqAbxFUHvZkRrjfhoE8n1564nP3FLCB4Hm84X6Cn2n6vgOAL4YVo8awgj8jvH8qsNHdvddjRaRvmUcDLwZu7aNqCfS8l5wO9HcezFQGPg8/BFRY0FY3kyCJvLv3IHdfRVBV/jqwzcxuNbOpvcfRax4O55T1BNXTtMHOw0l3v8bdTwLGEiS212a2iPSKI/NnkMs8XGFmP7OgHbAJeBQYawNbaelOYGFY1DgFSBEUX3o/l78SVLWvIfh5LjKzmt7jQg3u3prxfeY8nCnbnCw5UAK9FwkTrV8SJF/v729iDKWTjliYEG0mrFCGjuKN7R9DcT3Bp9nTCT4R/y7L2D6rz6E6oJvgDzat33YHd3+JYCJ4F/1XPQgrmP/u7ocSVDHezRv7BSE4aeeAXvvuZvcPFEPm7tsJqipTCdoTMm0GxptZRcZtMxi4nwAvA3PcvQb4CpDtMGXa+nC/Y3PYV1pPfOEb83SCn+Ub7if4mabvWw/8Z1gxSv+rcPdbCH4O03p9wNhT64tIIbsLmG5mbyUoKGQ7enccsDbLkcLNDHweTgK3E1RNLwR+H1Zd+xp7s7u/mWCedYJqcG+7zcPhHDAD2NjvsxmE8AjeNUADwcmWvW0mmMvScpmHv0jQYndCOA+nT1Tc41wcvmf/iaAS/0GCD0Lez9ir3X0+QfxzgX/pZ7PjLDh5Py1zHs6UbU6WHCiB3rv8BDgEeI+779a/G37yn2fBSXATCA41Pezu6baNG4CvWXBiw8EE/W3X5TG2x4BGYBHBH3tnlrH99j+HE/FdwNfDT/CHEvRrZXMz8FmCCeo3fQ0ws7eG/W5xoIng0Feqj6G3AJ+34CS+KoLq7W3e98omOTGz/zazw82sKDwy8HFglfc6wSesoC8h+BmUmNlC4D057Kqa4Dm2hL/rjw/kQeGhv/uBH4evk2IzG+jZ6fPN7Jyw1eVzBIf8Fmfc/0kLlvEbD3yVoC0IgpNZPxa+fs3MKs3srPDn8yTBh5fPhLGcQ3DmvEhBCOeKMiAOxC04Gbrf1bHCCuMdBL23a919SZbNZzsPBYKE+DPh3+049rxSx80ECd9F9FPICN+j3hYeMewgOJLZ1zx8O3CWmZ0WHin8IsGc8rc9xLBHZvY5C05sLA9/vpcSzJnP9hPHleF8OI2Mo74DUE3w/BrDee/fcgz1ZoIiz7n0//M8Lpw7iwnOB+qg759n2r+H7yknExSR+nq/zDYnSw6UQO8lzOwAgqb+o4Ettmut5/SZtLMJDsU1Ay8STDaZJ3X8G/AaQbX2EeB/fOhL2PUIPx3fQFA1yNa+MZbgk3K2ifBTBIfBthAk+b/aw+5vIegt+2tY2e3LZII3liZgBcHP4MY+xl0b3v4owSonHcCn97B/oOeCAS1ZhlQQHNZsBFYT/Kze28/YiwhOAKwnWMbqNoLf6UD8M0HVoplgMrwt+/DdXEzw4eJlYBtBMjwQvyV482wIt3FOryMkNxNUVFYTvA7/AyB8g/8owWHIBmAV4Uk44Yewc8Lvd4TbvyuH5yIy2n2NIAn7MsEJbO3hbdlczx7m4VC2/mcI5o4HCM6vWMoe/vZ810ndUwk+iPelFPg2wTk7WwhOVL6yj22tJHi+PwzHvoegcJStMNMjfG88uZ+724DvhvvfDnyS4IhuX9cW+AZBO9rrwJ8J3kMGOg//gKBveztBMSHX99t7CZae3RKe59KXGoLfUwPBe3s98D/9jN0SjttEcH7Ux3zXOVQ9ss3Jkhvr56iByKCY2XnAue7e18kL0g8LlnB72d1zrWJEzsy+TnByZl9nqGNma4CPuPufhzMuEembBas4PQtM6681QN7IzD4OXODuAz0ZUAqYKtCSb40MfGm0ghUemjswbMk5g2CZuHtGOCwR2TeMAb6o5Dk7M5tiZieF8/A8glaSN5wcKdIXXYlQ8sqDJfBkzyYTHDKdQHAI8ePu3lePnohITtz9FeCVkY5jFCghWAZ0FkHx51bgxyMZkIweauEQEREREcmBWjhERERERHIw6lo4Jk6c6DNnzhzpMEREBuWZZ57Z7u61Ix3HcNGcLSKjWX9z9qhLoGfOnMmSJdmWvhQR2XuZWUFdbVFztoiMZv3N2WrhEBERERHJgRJoEREREZEcKIEWEREREcmBEmgRERERkRwogRYRERERyYESaBERERGRHCiBFhERERHJwahbB1pERERkuLywYScPvrSFqrIiPnzSLIriqj2KEmgRERGRfn3zDy/x99d3AHDM/uM4bub4EY5I9gb6GCUiIiLSh/bOJM+ua+At84IrOW9rSoxwRLK3UAItIiIi0odn1jbQlXTee9RUAOqaO0Y4ItlbKIEWERER6cOTq7cTjxmnH7ofRTFjW7Mq0BKILIE2szIz+7uZLTOz5Wb2732MuczM6szsufDfR6KKR0RERCQXT75Wz5HTx1BdVkxtdSl1SqAlFOVJhAngbe7eYmbFwONmdr+7L+417jZ3/1SEcYiIiIjkpKMryfMbdvLRU2YDBAl0ixJoCURWgfZAS/htcfjPo9qfiIiISL5saGinO+XM268agNqqUp1EKD0i7YE2s7iZPQdsAx5096f6GPZ+M3vezO4wsxn9bOcKM1tiZkvq6uqiDFlERIZIc7bsCzY0tAEwfVw5AJNqVIGWXSJNoN096e5HA9OB483s8F5DfgfMdPcjgQeB6/vZziJ3X+DuC2pra6MMWUREhkhztuwL1je0AzB9XAUQVKDrWxIkUzqYLsO0Coe7NwIPAWf0ur3e3dMf534BzB+OeERERESy2dDQRkk8xqTqUiDogU451LeqCi3RrsJRa2Zjw6/LgdOBl3uNmZLx7XuBFVHFIyIiIjJQG3a0M21cObGYAUECDWglDgGiXYVjCnC9mcUJEvXb3f33ZvYNYIm73wt8xszeC3QDO4DLIoxHREREZEA2NLT19D8D1FaXAbCtOcFhIxWU7DUiS6Dd/XngmD5uvyrj6yuBK6OKQURERGQw1je0886pY3q+n6QKtGTQlQhFREREMrQmutnR2tmrAq0EWnZRAi0iIiKSYUO4AseM8RU9t5UVx6kuLVICLYASaBEREZHd9F4DOq2mvJjmju6RCEn2MkqgRURERDJsbEyvAb17Al1REqetUwm0KIEWERER2c22pgTxmDGhsnS32ytLi2jtTI5QVLI3UQItIiIikmFbcwcTKkuIh2tAp1WWxmlLqAItSqBFREREdlPXnGBSTekbbq8oKaJFCbSgBFpERERkN3UtCWqr3phAV5bEaVMLh6AEWkRERGQ325oSPes+Z6ooLdJJhAIogRYRERHpkUw59a2dTAov3Z2psiROa0IVaFECLSIiItJjR2snyZT3WYGuLC2ivStJMuUjEJnsTZRAi4iIiITSVxrsM4EuKQKgvUtV6EKnBFpEREQkVNcSJNCT+uyBjgPQqpU4Cp4SaBEREZHQtqYOIHsFWgm0KIEWERERCaUr0H2uwlESVKC1lJ0ogRYREREJ1TUnqCotoiKsNmeqLFUFWgJKoEVERERC25r7XgMadiXQqkCLEmgRERGRUF22BDps4dDlvCWyBNrMyszs72a2zMyWm9m/9zGm1MxuM7NVZvaUmc2MKh4RERGRPalvSTChsqTP+yp6KtBKoAtdlBXoBPA2dz8KOBo4w8xO7DXmcqDB3Q8Cvg/8d4TxiIiIiGTV2NbFuH4S6HQFWlcjlMgSaA+0hN8Wh/96X7rnbOD68Os7gNPMzKKKSURERKQ/qZTT0NbJ+Ip+KtAlqkBLINIeaDOLm9lzwDbgQXd/qteQacB6AHfvBnYCE6KMSURERKQvzR3dpBzGVhT3eX9JUYziuNGqkwgLXqQJtLsn3f1oYDpwvJkdPpjtmNkVZrbEzJbU1dXlNUYREckvzdkyWjW0dQIwvp8WDghW4mjTSYQFb1hW4XD3RuAh4Ixed20EZgCYWREwBqjv4/GL3H2Buy+ora2NOFoRERkKzdkyWu0IE+hx/bRwQHA1whb1QBe8KFfhqDWzseHX5cDpwMu9ht0LXBp+fS7wV3fv3SctIiIiErnGdAKdpQJdURJXD7Twxsvs5M8U4HozixMk6re7++/N7BvAEne/F/glcKOZrQJ2ABdEGI+IiIhIv3a0dgEwrp8eaAiWslMPtESWQLv788Axfdx+VcbXHcAHoopBREREZKDSFeixWVs44uqBFl2JUERERARgR2sn8ZhRU9Z/fbGiRBVoUQItIiIiAkBDWxfjKorJdkmKqtI4rapAFzwl0CIiIiJAQ2tn1hU4IOiB1kmEogRaREREhGAd6D0l0JUlcV3KW5RAi4iIiAA0tnUxrrL/FTgAyorjdHQn0aq7hU0JtIiIiAjBhVT2VIEuLYrhDl1JJdCFTAm0iIiIFDx3p7GtM+tFVABKi+IAJLrVxlHIlECLiIhIwWtJdNOV9KwXUQEoLQ5Sp0R3ajjCkr2UEmgREREpeI1t6asQ7rmFA5RAFzol0CIiIlLwdrYHCXRN+Z5PIgRIdKmFo5ApgRYREZGC19wRrO1cneUqhKAKtASUQIuIiEjBawmvLlhduoce6J6TCJVAFzIl0CIiIlLwWhJBC0fVQCvQauEoaEqgRUREpOC1hC0cVaV7SKC1CoegBFpERESElvDy3HvugQ5aODpUgS5oSqBFRESk4LUkuiiKWU+LRn90EqGAEmgRERERWjq6qSorwsyyjutZxk4JdEFTAi0iIiIFrznRvcf+Z8isQKuFo5ApgRYREZGC19Ix0AQ6fSEVVaALWWQJtJnNMLOHzOwlM1tuZp/tY8xbzGynmT0X/rsqqnhERERE+tMy0Aq0VuEQYM+vlMHrBr7o7kvNrBp4xswedPeXeo17zN3fHWEcIiIiIlm1JLoZX1myx3ElcbVwSIQVaHff7O5Lw6+bgRXAtKj2JyIiIjJYA23hiMWMknhMFegCNyw90GY2EzgGeKqPuxea2TIzu9/MDuvn8VeY2RIzW1JXVxdlqCIiMkSas2U0ak5073EN6LTSoph6oAtc5Am0mVUBdwKfc/emXncvBQ5w96OAHwL39LUNd1/k7gvcfUFtbW2k8YqIyNBozpbRaKAVaAj6oDvUwlHQIk2gzayYIHm+yd3v6n2/uze5e0v49X1AsZlNjDImERERkUzdyRTtXUmqSosHNL60KK4KdIGLchUOA34JrHD37/UzZnI4DjM7PoynPqqYRERERHprDS/jXTXQFo7imE4iLHBRrsJxEnAx8IKZPRfe9hVgfwB3/ylwLvBxM+sG2oEL3N0jjElERERkN82JLgCqB9rCURTXSYQFLrIE2t0fB7JeD9PdfwT8KKoYRERERPakJdEN5FCBLtIqHIVOVyIUERGRgtaaTqAHXIGOkehSC0chUwItIiIiBa25I0igKwe8CodaOAqdEmgREREpaOkWjpzWgVYCXdCUQIuIiEhBa+kYRAuHVuEoaEqgRUREpKDlehJhWbHWgS50SqBFRESkoPX0QJeoAi0DowRaREREClpLopuKkjjxWNbVd3voSoSiBFpEREQKWntXkoqS+IDHB1ciVAJdyJRAi4iISEHr6ExSVpxDAl0UozOZIpXSxZMLVdZmHzObDlwAnAxMJbjc9ovAH4D73V0fv0RERGRUa+9KUp5TAh2M7UymKIsN/HGy7+i3Am1mvwKuBTqB/wYuBD4B/Bk4A3jczE4ZjiBFREREotLelaQ8lxaOoiB9Uh904cpWgf6uu7/Yx+0vAneZWQmwfzRhiYiIiAyP9hxbONJjg5U4iiOKSvZm2Xqg3xW2cPTJ3TvdfVUEMYmIiIgMm46cWzjCCrROJCxY2RLoqcCTZvaYmX3CzGqHKygRERGR4ZJzD3RxOoHWWtCFqt8E2t0/T9Ci8TXgCOB5M/ujmV1qZtXDFaCIiIhIlHLvgQ7GdqgHumBlXcbOA4+4+8eB6cD3gc8BW4chNhEREZHItXemcl7GDlSBLmQDumalmR1BsJzd+cB24MoogxIREREZLonB9kCrAl2w+k2gzWwOQdJ8AZAEbgXe4e6rhyk2ERERkcgFLRwDv7Zcac8qHEqgC1W2V8sfgVLgfHc/0t2/lUvybGYzzOwhM3vJzJab2Wf7GGNmdrWZrTKz583s2EE8BxEREZFB6Uqm6E75IFfhUAtHocrWwjFnT1caNDNz9/6uY9kNfNHdl4YnHT5jZg+6+0sZY94FzAn/nQD8JPxfREREJHLtXUESPJh1oHUSYeHKVoH+q5l92sx2u1iKmZWY2dvM7Hrg0v4e7O6b3X1p+HUzsAKY1mvY2cAN4cmKi4GxZjZlUM9EREREJEcdnUECncsqHGXhMnYdXapAF6psCfQZBL3Pt5jZprAVYzXwKsFlvX/g7tcNZCdmNhM4Bniq113TgPUZ32/gjUk2ZnaFmS0xsyV1dXUD2aWIiIwQzdkymqQr0Lm0cJT1LGOnBLpQ9dvC4e4dwI+BH5tZMTARaHf3xlx2YGZVwJ3A59y9aTBBuvsiYBHAggUL+msZERGRvYDmbBlNBpVAh2Pb1cJRsAa0jJ27dwGbc914mHjfCdzk7nf1MWQjMCPj++nhbSIiIiKRaw9bOMpyupCKWjgK3cDXbMmRmRnwS2CFu3+vn2H3ApeEq3GcCOx095wTdREREZHBGEwFOhYzSotidGgVjoI1oAr0IJ0EXAy8YGbPhbd9heDy4Lj7T4H7gDOBVUAb8I8RxiMiIiKym45BJNAQtHGkT0CUwhNZAu3ujwO2hzEOfDKqGERERESyae8M+phzWYUDgoRby9gVrmxXImwG+jr5wwhy35rIohIREREZBoNp4YBgKTu1cBSubKtwVA9nICIiIiLDbTAXUkmPb1cLR8EacAuHmU0CytLfu/u6SCISERERGSaDuZAKQGlxnI5utXAUqj2uwmFm7zWzV4HXgUeANcD9EcclIiIiErmeCnRRbguTlRfHtIxdARvIq+WbwInAK+4+CzgNWBxpVCIiIiLDoL0rSUk8RlE8twS6rDhOQgl0wRrIq6XL3euBmJnF3P0hYEHEcYmIiIhErr0zSVlx7pfFKCuK91SvpfAMpAe6Mbwc96PATWa2DWiNNiwRERGR6HV0JXPuf4ZwFQ4tY1ewBvKR62ygHfg88EfgNeA9UQYlIiIiMhzau5I5L2EHwUmH6oEuXHusQLt7ZrX5+ghjERERERlWQQtH7gl0qVo4CtpAVuE4x8xeNbOdZtZkZs1m1jQcwYmIiIhEqX3QLRxxEmrhKFgD6YH+DvAed18RdTAiIiIiw6ljkC0cZcUxOpMpkiknHrMIIpO92UB6oLcqeRYREZF90aB7oMPHJHQ574I0kAr0EjO7DbgHSKRvdPe7ogpKREREZDi0dyYpG2QLR/rxFSUDvrCz7CMG8huvAdqAd2Tc5oASaBERERnVOrpSg27hAHQ57wI1kFU4/nE4AhEREREZboNt4UhXoLWUXWHaYwJtZlf3cfNOYIm7/zb/IYmIiIgMj/bOwa/CkX68FJ6BnERYBhwNvBr+OxKYDlxuZj+ILDIRERGRCLk77V2DWwe6TCcRFrSB9EAfCZzk7kkAM/sJ8BjwZuCFCGMTERERiUwi7F8eVAtHUdgDrbWgC9JAKtDjgKqM7yuB8WFCnej7IWBm15rZNjN7sZ/73xJenOW58N9VOUUuIiIiMgTp9ovy4oGkQ7tTC0dhG+iFVJ4zs4cBA04BvmVmlcCfszzuOuBHwA1Zxjzm7u8eWKgiIiIi+ZO+FPdgeqDTj+lQC0dBGsgqHL80s/uA48ObvuLum8Kv/yXL4x41s5lDD1FEREQk/9IJ9KB6oIvSq3CohaMQ9XvMwswODv8/FpgCrA//TQ5vy4eFZrbMzO43s8OyxHKFmS0xsyV1dXV52rWIiERBc7aMFrtaOIawDrSWsStI2SrQXwCuAL7bx30OvG2I+14KHODuLWZ2JsGVDuf0NdDdFwGLABYsWOBD3K+IiERIc7aMFh1DaOFIX71QCXRh6jeBdvcrwv/fGsWO3b0p4+v7zOzHZjbR3bdHsT8RERGRTPlp4VACXYiytXAcZ2aTM76/xMx+a2ZXm9n4oe7YzCabmYVfHx/GUj/U7YqIiIgMxFBaOIrjRszUA12osq3b8jOgE8DMTgG+TbCixk7CQ3PZmNktwJPAPDPbYGaXm9nHzOxj4ZBzgRfNbBlwNXCBu+tQn4iIiAyLoVSgzYyy4njPNqSwZOuBjrv7jvDr84FF7n4ncKeZPbenDbv7hXu4/0cEy9yJiIiIDLuh9EBDULlWC0dhylaBjptZOsE+Dfhrxn0DWT9aREREZK81lBYOCCrXauEoTNkS4VuAR8xsO9BOcPluzOwggjYOERERkVGrvWvwl/IGKC2O6UIqBSrbKhz/aWZ/IVgD+k8Z/ckx4NPDEZyIiIhIVNL9y6VFuV/KG4KVODp0Ke+ClLUVw90X93HbK9GFIyIiIjI8OrqSlBXHiMVsUI+vKi2iJdGd56hkNBjcRy4RERGRUa69Mzno9g2AmvIimjuUQBciJdAiIiJSkNq7hphAlxXT1NGVx4hktFACLSIiIgWpvSvZc0nuwagpL2ZnuxLoQqQEWkRERApSx1BbOMqCHuhUSteBKzRKoEVERKQgDbmFo7wYd2jWiYQFRwm0iIiIFKT2ruSgr0IIQQIN0KQ2joKjBFpEREQKUntnkrIhnkQI6ETCAqQEWkRERApSx5BbOILLaehEwsKjBFpEREQK0lB7oMf0tHCoB7rQKIEWERGRgtTeOcQeaLVwFCwl0CIiIlKQOrpSQ+uB1kmEBUsJtIiIiBSc7mSKzmRqSC0c1aVFmCmBLkRKoEVERKTgdHSnACgvGXwqFIsZ1aVFNHWoB7rQFI10ACL7isWr67nmoVWkfHBXpKqtKuV/PnAUxXF9rhURiVp7ZxJgSBVoCNo4VIEuPJG9U5vZtWa2zcxe7Od+M7OrzWyVmT1vZsdGFYvIcLj/hc08+Vo9ia5Uzv+27Ozgnuc2sba+daSfhohIQejoChLoofRAQ3AioU4iLDxRVqCvA34E3NDP/e8C5oT/TgB+Ev4vMio1tncxfVw5d3z8TTk/dvHqei5YtJgtOxMcNKk6guhERCRTe5hAD2UVDgjWgtYydoUnsgq0uz8K7Mgy5GzgBg8sBsaa2ZSo4hGJWkNbF2MqSgb12Mk1ZQBsaerIZ0giItKPfLVwjCkv1oVUCtBINltOA9ZnfL8hvO0NzOwKM1tiZkvq6uqGJTiRXO1s62RsuKRRrvYLE+itSqBlH6A5W0aDngq0WjhkEEbF2UruvsjdF7j7gtra2pEOR6RPje1djK0YXAJdXhKnpqxICbTsEzRny2iQTqDLhtzCoZMIC9FIJtAbgRkZ308PbxMZlRrbugZdgQaYPKaMLTuVQIuIDIe8rcJRVkxrZ5LuZCofYckoMZIJ9L3AJeFqHCcCO9198wjGIzJoyZTT1DH4HmgI2jhUgRYRGR6tieDEv6rSoa2nMKY8eLzWgi4ska3CYWa3AG8BJprZBuDfgGIAd/8pcB9wJrAKaAP+MapYRKLW3NGFO0OrQNeU8crW5jxGJSIi/WkLK9CVQ0ygx1UGhZMdrZ2Mrxx8EUVGl8gSaHe/cA/3O/DJqPYvMpwa24L+t8H2QEPQwlHXnKA7maJIF1MREYlUS1iBrhhiD/SEylIA6lsSHDSpashxyeigd2mRPGho6wSGlkDvV1NGyqG+tTNfYYmISD/aOruJx4zSoqGlQuMzKtBSOJRAi+RBY3gG9pjyofVAAzqRUERkGLQmklSUxDGzIW1nYlUw729XAl1QlECL5MHOsIVj3FBaOHQxFRGRYdPW2U1lydA7WdM90PUtiSFvS0YPJdAiedDY08IxhAr0mKCPTitxiIhEr7UzSWXp0PqfAYrjMcaUF6uFo8AogRbJg3QLR03Z4KsZEytLiRlsa1IVQ0Qkam2J7iGvwJE2oaqE+hYl0IVECbRIHjS2dVFdVjSk1TNiMWN8ZSn1rUqgRUSilu6BzocJlSVsVwtHQVECLZIHO4dwGe9ME6tK2K4qhohI5Frz1AMNwVJ2auEoLEqgRfKgoa2TsUNYgSNtYlWpTkQRERkGbZ1JKvLZwqEEuqAogRbJg8a2/FSgNQmLiAyP1kQ3lXls4Who6ySZ8rxsT/Z+SqBF8mBnexdjhnAZ77QJlaVsb1YFWkQkam2dyTyeRFiK+66Lasm+Twm0SB40tnXmJ4GuKqG1M0l7ZzIPUYmISF/cPeyBzk8FenzPWtBKoAuFEmiRIXJ3mju6qclDAl1bFawFrZU4RESi096VxJ289kCD5u5CogRaZIjau5J0p5zqIawBnZaehLUSh4hIdFoTwVG+fFWgJ6aLH5q7C4YSaJEhau7oBqCmLB8tHOlJWFUMEZGotHUG83ZFnpaxG6/LeRccJdAiQ9TcEVyFMC8VaPXRiYhErqcCnacWjvEVJVSXFbFya3Netid7PyXQIkPUlMcKdPow4Hb10YmIRCZdga4szU8LRyxmnDBrPE++Vp+X7cneTwm0yBA1tQcV6JryoVcyykviVJbE2d6sCrSISFRaw5WO8tXCAXDi7AmsqW9j8872vG1T9l5KoEWGKN0DXZ2HCjQEfdA6k1tEJDqtifxWoAEWHjgBgMWrVYUuBJEm0GZ2hpmtNLNVZvblPu6/zMzqzOy58N9HooxHJAq7Eug8XhJWPdAiIpHpSaDzWIE+ZHINY8qL1cZRIPL3yunFzOLANcDpwAbgaTO7191f6jX0Nnf/VFRxiEStqeckwvxUoCdWlbKuvi0v2xIRkTdq62nhyF8FOhYzjp81niVrGvK2Tdl7RVmBPh5Y5e6r3b0TuBU4O8L9iYyI5o4uYpa/9UTn7VfNqroWGnVJWBGRSLT2nESY3zriwZOrWbujjc7uVF63K3ufKBPoacD6jO83hLf19n4ze97M7jCzGX1tyMyuMLMlZrakrq4uilhFBq25o5vqsmLMLC/be/uh+5FMOQ+v1GtdRifN2bK3a0skiceM0qL8pkGzaytJppx1O1rzul3Z+4z0SYS/A2a6+5HAg8D1fQ1y90XuvsDdF9TW1g5rgCJ7EiTQ+atiHDltDLXVpTy4YmvetikynDRny96utbObipJ43gofabMmVgHwWp0S6H1dlAn0RiCzojw9vK2Hu9e7e3q5gV8A8yOMRyQSTe1deVkDOi0WM047eBKPrKwj0Z3M23ZFRCTQmujO6wmEabNrKwFYrQR6nxdlAv00MMfMZplZCXABcG/mADObkvHte4EVEcYjEol8V6ABTj90P1oS3WrjEBGJQGtnkoo8LmGXVlNWzMSqUlbXteR927J3iSyBdvdu4FPAAwSJ8e3uvtzMvmFm7w2HfcbMlpvZMuAzwGVRxSMSlaaOrrytwJF26txapo4p47on1uR1uyIiAg2tnYyrKIlk27NrK1m9XRXofV2kPdDufp+7z3X3A939P8PbrnL3e8Ovr3T3w9z9KHd/q7u/HGU8IlFo7uimJs8V6KJ4jEveNJMnV9ezYnNTXrctIlLo6poT1FaVRrLtA2srVYEuACN9EqHIqNfU0UVNeX4r0AAXHDeDsuIYNz+1Lu/bFhEpZHUtCWqro0mgZ0+soqGti4ZWLUW6L1MCLTIEqZTTksh/DzTA2IoS3nTgRB5ftT3v2xYRKVSJ7iSNbV1MiiiBPnBScCLhK1ubI9m+7B2UQIsMQWtnN+75u4x3bwtnT+D17a1s2dkRyfZFRArN9pagMhxVBfqYGeMwg6de3xHJ9mXvoARaZAiaOoKrWeVzGbtMCw+cAMCTq1WFFhHJh7rmYPXcqBLocZUlHDy5hsWr6yPZvuwdlECLDEFzRxdA3lfhSDtkSg01ZUUsfk2VDBGRfNjWFBzRm1RdFtk+Fs6ewDNrG7SW/z5MCbTIEDSHFeioWjjiMeOE2RN4UpUMEZG8qGuJtgINwdHDRHeKZ9c1RrYPGVlKoEWGoLEtqECPiWAVjrSFsyewbkcbGxvbI9uHiEih2NaUwAwmVEWzDjTA8bPGEzP422sqfuyrlECLDMHa+mCx/BnjKyLbR08ftCZiEZEhq2tJML6ihOJ4dCnQmPJiFhwwnt8v24S7R7YfGTlKoEWG4PXtrdSUFTGuIroK9Lz9qhlXUawEWkQkD+qao1sDOtO5C6azensrS9c1RL4vGX5KoEWGYE19K7MmVmJmke0jFjNOnD2BxavrVckQERmibcOUQJ91xBQqSuLc8ORaNja2a/7exyiBFhmCNdvbmDmxMvL9nDh7Ahsb21m/Q33QIiJDsX2YEujK0iLOOmIKv31uEyd9+688sHxr5PuU4aMEWmSQOrqSbNrZzqxhSKBPOijog374lW2R70tEZF/l7sPWwgHwlTMP4f8uOJrqsiIeXqn5e1+iBFpkkNbtaMOdYUmgD6yt4uDJ1dzxzIbI9yUisq/a0tRBZzLFlJro1oDONK6yhLOPnsYJs8brwir7GCXQIoP0+vZgBY6ZE6JPoM2M8xbM4PkNO3l5S1Pk+xMR2Relk9gFM8cP635PnD2BNfVtvLBhJ7f+fR3JlPPSpiYee7VuWOOQ/Inm6g8iBWBNOoEehgo0wD8cM43/un8F3/3TK3zpnfOYs181bZ3ddCU90nWoRUT2FU++Vk9NWRGHTKkZ1v2mlyP94M8X05zo5vmNO/nD85tJdCf5+1ffTk1EV7OV6CiBFhmk17e3Mr6yZNiS1/GVJXz4pFksemw1D760lQMmVLC1qYPy4ji//sgJHDZ1zLDEISIyWi1evYMTZk8gHotu5aS+HDK5hrEVxTS2dXHUjLHc/NQ6SopidHan+P2yzXzwhP2HNR4ZOrVwiAxCMuU8vLKOo2eMHdb9XnnmITx15Wl88+zDmD2xkg/Mn0F5cZwP/vwpvvPHl3W1QhGRfmxsbGfdjjYWzp4w7PuOxYxPvuUgvnTGPG796Imcc+w0Fl08nzmTqvjNM+uHPR4ZOlWgRQbhsVfr2NLUwVXvOXTY9z2ppoyLF87k4oUzAbjilNl85e4X+Nmjq7njmQ3c/NETOWhS1bDHJSKyN3ti1XZgVzvFcPvoKbN7vv7eeUcD8OrWFv7zvhUs+I8HgaAqXlYc45v/cDiplHPVb5eTTDlfOH0u5x03g87uFJ++ZSmHTR3DZ06b07O9h1Zu4+q/vMr/nX8M+08IrozblUzxmVueZd7kaj739rmRPKctOzv45M1L+ejJszjj8CmR7GNvFWkCbWZnAP8HxIFfuPu3e91fCtwAzAfqgfPdfU2UMYnkw2+e2cC4imJOO2TSSIfCjPEV3Hj5CbyytZkP/nwx/3DNE7zjsP048/ApHDl9THpOBqCmrJiy4vig99WVTA3p8rfuTnfKs25jR2sn3akUMPR4ZXTLfL2lv858DXUnU8RjhpnR1tlNS6IbgIqSIqpKi3B3trd04gQXsKitKs15bHtnkuZE125jAba3JEiFF8aYWFlKLLb72PLiONVhX2suY9PPM5lyjKBy2d/PJC1zbF8/M4D6lgTJMIYJlaXEs4zNjAHIOjYtlQp+cunWiPTPs7Q41tPfm/m3Pb6ihKJwG+mxJUWx3Vri0r+nzLHZXhuplJNypyge6/P+e57dyIzx5czbr5q9xfnHz2BLUwftXcme255aXc9nb3kWd5g8poyK0iK+es8LTB9XzgPLt/DA8q08sDxo41t44AS2N3fy2Vuepamjm0/evJSfX7KAWAx+8vBr3P/iFu5/cQv7j6/gzXMm5jV2d/j0LUt5Zm0DKzY3MWVMOVPG7lrdZEx5MaVFu+bvhtZOIFiZZPft7P43HTPL+lpOf53L2ChYVFfGMbM48ApwOrABeBq40N1fyhjzCeBId/+YmV0AvM/dz8+23QULFviSJUsiiVlkIP722nYuvfbvXHTCAXz9vYeNdDi7eX17K9c8tIoHX9rKzvauN9w/rqKYz58+l40N7VSXFTFrYhVPvV7PvMnVHDV9LBBMOH97rZ6d7V0cu/84lq5rYGtTB5sa21mytoG3H7Ifn3jLgcEElnL+9tp2Glo7OXXuJMZWFDNjfEWffeEvbWriyrueZ8XmZk6eM5F3HTGFgydXs3lnBw+t3EZLRzcrNjfx6raWnsdUlxXxr2cdyvvnTyceM+qaE/zy8deZOraMMw6bzKRhWooqn8zsGXdfMNJxDJfBztnPrW/kQ794ik+/7SAqSov49n0ruPay47jpqXUsXdfAjZefwEeuf5qZEyr5zGlzuOgXT/UkxaVFMX71j8fxmyUbuPvZjT3bfMu8Wr54+ryeE7nSY3956XHc/exG7ly6a5nIk+dM5P+dcTAX/nwxzR3B2JKiGL+4ZAG/f34Tty/ZNfakgybwlTMP4cJFi2nKGLvo4vk8sHwrt/x9Xc/YhbMncNV7DuWCRYt7/kZL4jF+8qFjeWjlNh5YvpVbrziRz9/2HOXFcW68/ARKioIE4LfPbeTLd77A1Rcew+mH7gcE69Gfv2gxJXHjP/7hCC78+WI+MH86R04fyz//ZhnfP/9onnq9nl89saYnhvkHjOPb5xzBBYsW875jpjH/gHF84fZlfO+8o1i6roE7l27k1itO5Mq7XiCZcv73A0dywaLFvOeoqZw4ewKfu/U5vnPukbznqKkAdHan+NAvniLRneS2f1pIaVGMz976HPcu20Q8ZnzvvKNYuaWZHz/8Wk8Mh0+r4Tf/9CbKimN84fZl3P3sRuIx438/cCTvO2Y6L27cyYWLgt/ToVNquOPjC6koCT68PLxyG5+4aSnfPPtw1u1o41dPvM4tV5zIf933MttbEvz4omO56BdPcfKcibznqKn8043P8JGTZ3P1X17l82+fy2ffvqtyuzdaV9/GWT98jJgZv//0m6kpK+asHz7GhoagRe+ShQewbMNOlq1v7HlMdVkRXzh9Lv/+u5d229ZFJ+zPS5ubeHZdI1H56pmH8NNHXqM+TJDTpo0t5+5PvolJ1WV8/8FX+L+/vArAF0+fy6fD6rm786U7nufxVdu56SMn8ImbllJbXcqX33UwFy5azIffPItpY8v5t3uX8+OLjuWPL27hoZXbuPmjJ/Lpm59lbEUxXzvrUC78+WIuXXgAs2or+erdL3LNB4/lwRVb+fNLW7n7kycxbWz5oJ9ff3N2lAn0QuDr7v7O8PsrAdz9vzLGPBCOedLMioAtQK1nCWowk/FrdS3c/8JmXtzYxMIDJ/Qc3mju6OaRlXVUlMQ56aCJlBb3/ymlpaObR16po6w4xpsPqg3GOqzY0sTKLc1c/uZZ1FaX8vKW5p6xpUUxTp4zkdLiODi8vKWZZesbOX7WeGbVBis3tCaCGIqLYpySMXbl1maeXdfA8bMmMDsc25ZI8sgr24jHgrFlJcHYV7Y2s3RdA8fNHM+B4aH7tkSSR1+pIxaDU+bU9ox9dVszz6zdfWx7Z5JHVtZhBqfMraW8pP+KX3tnsF13OHXerrGvbWvh6TU7mH/AOOZMqgaDjs4kj75aRyoVbLeidPexx+4/jrn7ZY7dTjKV4tS5k3rGrq5r5bn1jVx43AzmTq7mxY076egKxnZ1pzh1Xi2VpbkfSFmzvZWnVu/gyBljOGRyDRgkulI8vqqOjq4Up86tpaos2O7a7a0sXr2DI6aPIdGVZNFjq5kxroJbrjiRiVXDsxh/rjq7Uzy5up71O9p6bnPgjmc2sGx9I8VxoysZ/JmlT2TprSQeozOZoiQeY8rYMmrKijl82hjuWrqBRK/x6bEAxXHjyOljKS+Oc+wB4ygtivGH5zfz0uYmJlSW8M7DJ/PIyrrd+rWrS4uYUFXC5DFlvHXeJCpLi3Dgd8s28ffXdzCxqoQ5k6pZsaWJne1duIMZLDhgHO86fErP31OmrTs7eOzV7cyaWElFaZzFq3fQHcY4rqKEEw+cwLL1jWwK4zCDo2eMZf4B4zAzkknn6TU72LSzg1Pn1jKhKqiabGvq4IHlW3n/sdM568jcD1kqgd6zxrZOzrr6cTbtbMcIqprdKae0KEZHVwqzXa859yAJHl9ZwifeehAGXPvE62xsaCfRneKDJ+zPoVNqWLejjUWPrqasOMbY8hI++bZg7HV/W8P6HW0kulNcePwMDps6hvUNbfzskdWUhhXRT582BwNueHINa+uDsecvmMER08ewsbGdnzz8GqVFMarLivns24Oxv168ljX1rXR0pfjA/OkcNWMsm3e2c81D6bFFfPa0OZgZNz21jtV1LSS6g+eWfp4AFxw3g3cePpnWRDdfuuN52ruSVJcW8Z1zj6S0OM5vn93IPc9tAoLHZf5M0n+/ie5UT6Jc15zg//7yatax6Z9v+u+8r7EVxXG+c+5RVJTGuf+FzT0fKP7h6KlMG1fONQ+9xodO3J/lm5pYvqmJzu4UZx05hYWzJ9DY1sl3H3yFdx85lZkTKvjhX1dx0Qn78/KWZl7a1MS3338E33vwFRJdKS5eeAD/+6eVnHnEFM6dP53O7hRfvvN5Gtu7KI4Hc1dmvH29NtJfm8FjX3or08dV5OfFHaFXtzZjZj3teJsa23lo5Taqy4p51+GTaeno5oHlW+gOjxKcMGs8c/ar5m+rtrM6XCGquqyIM4+YQmuimz++uGtsPs0YX8Gpc2tZs72Vx8MWGQjeg77zwMscNX0sZx05hat+u5wzDptMyp0HV2zlm2cfzrRx5Sxb38gP/vzqG35vZcXB79M9eI/qGuDrs6+xR88Yy2dOm8P4ihKOGsR5SyORQJ8LnOHuHwm/vxg4wd0/lTHmxXDMhvD718Ix23tt6wrgCoD9999//tq1a3OK5YJFT7J49Q6mjClj886O3e4bW1FMoiu12+GT/owpL6YrmaKtc/exVaVFtHV2k/na7G/s1DFlbOoVQ01ZEcmU0zrAsSmnp9KSbWx1WRE+0LFhEtrca2xf+hvb13arSoswo6eCs6exMaOngpO5v+ZENzGj52dcWRInHrM3jM1FX6+HipI4xfHYG6q36bExg5Pn1PLd847aa5PnbJIp57n1Dczdr5rWRJL1DW0cPWMsr9W1sK4+SLbNjMOm1lBTXswLG3Zy2LSa3ZZY2tDQxkubdq1Ffdi0MYwtL+bpNTvo6Erx7LoGnl3XSHtXkuWbdpJyOHb/sZx5xBTOOXY64ytLcHde2LiTLTs7qCorYsEB43uqbJlSKedPL23hDy9sYcvOdsZXlvAv75yHO9z3whbuf3EzL29p7vf5Tqoupb61k2TKOXhyNdXhh6J1O9rY2pSgpqyIuftVYxZM+C9uauo5bA3Bh4Ex5SVsb0nstt3p48r5/Nvn8v7503P+HRRCAj3UOftb963gV0+8znX/eDxX/fZFEt0p/vcDR3HZr/7OKXNqedvBk/jyXS/wtbMO4aXNTfxu2SZuvWIh8w8YB8DKLc38wzVPcNJBE1h08QJiMeupdN39bFBdTa8DvGpbM2f/6AmOnzWeX156XE/LxJfvfJ47ntnATR85gRPCk85eq2vh7B89wbEHjOO6y3aN/erdL3Dr0+v59eUn9PTXrg7HHjVjLNd/+Pie1oarfvsiNz21jhs+fDwnHRQcUl9b38q7f/g4R0wbw4XH78+nb3mWz7ztIJo6urnub2t6fi4Tq0r4yYfm85Hrl+w2R33yrQfS0ZXi2ideZ9HFC/jxw6tYW9/Gzy6ez0dvWMKcSVXc/NETew5l/9d9K1j02Gp++qH5LHp0NavrWvj5JQv46A1LmF1bxRWnzOZjv36Gj548m3jM+Okjr/GTi47l2sfX8Mq2Zn5+yQI+duMzu1UcP3zSLCpK4vzooVUAvOPQ/fjZxfPZ1pzgrKsfZ9rYMm7/2MKeQ/rfe/AVrg4rkm8/ZBKLLl7A9pYEZ/3wceqaExTHjVs+Gvyerv7Lq3zvwVd69lVdWsS1/3gcn7p5KRMqS/nSGfO4/PolnH/cDKaNLed/HljJ988/inue3cQzaxu49rLj+Oytz3LIlBquvey4nF6LMnh3PrOBL/5mGQCHTa3hzo+/CXd434+f2G3ePnVuLWcfPZUv3L6Mf3nnPDY0tHP7kvX88tIF/PcfV7KjNcEPLzyWy697mqP3H8u586fz2Vuf4wunz2Vbcwe3/H09v7hkAd99cCVbm4IjEB++7mmOnD6G84/bn8/c8mzPfq7/8PE5P49RnUBnGkw1Y8XmJsZVBNWtNdtbaWgL/uiL4zEOnlxNV9JZubWZbD+L4niMeZOrSaaclVuae/rZpowpp7wkzs/DysaJsydQWhTn4ClvHDt5TBlTxpSzrr6N+tbgDbkoFmw35buP3a+mjKljy1m/o63nzbu/sZNqypjWa2w8Zhw8uYaUO69sbe5JCvobO29y0BO2ckvzbglEb/2NnVhVyozxFWxsbGdbU8duYw3j5S1NWcfGLBgbM2PlluaePrmJVaXUVpfyy8dfJ9GV5KSDJlJWHO9zbC4mVJay/4QKNu9sZ8vO3WOIx4LtdoXVyvGVJRwwoZJtTR3EY8aEUZg4j5T6lgTJlEfaapH595SpqrSIgyZV0djWRaI7xeQxu2JIpZzX61uZMa5it8S9obWTNfWtPd/Prq2iurSIV7Y10x5+wK0sLWLOpCrMBrcMViEk0JkGM2cnupMsXdvIwgMn0NbZTTLlVJcVU9+SYGxFCfGYsa25g0nVZaRSTn1r5xsuzVzfkmBMefFuvbO5jE335fYeu6O1k5qyogGPrS4r2q0H092pa0kwqbqs37HbmjuoDeeZlzY39RwhmjWxkrEVJexo7WRt+DotL4n39PSmt5voTtLemWRsRQkNrZ1Ulhbt9jrPjCFzbGNbJ+UlcUqL4rvFkB7b2Z2iNdHNuMpgbPpiUqVFcQ6ZEr4vbG2mszvFYVPH9Hxo2NnWRWlxbLfzGdyD991EV4rDptb0/Dx3tnWxentLz/tleuwrW1to6wwKJjPGVzCxqpSd7V2UFgXbrWtOMLGqBLNdr43uZIqmjm7GV5bQ1tmNYVmPsEr+vb69lca2Tg6ZUtPz++/oSrJic1CEiYUFm6LwdT+pumy3v6eOriSJ7hRjyovf8Dcy0LFr61vD74sHdYJ9QbdwiIjsLZRAi4iMHv3N2VGuA/00MMfMZplZCXABcG+vMfcCl4Zfnwv8NVvyLCIiIiIy0iJbxs7du83sU8ADBMvYXevuy83sG8ASd78X+CVwo5mtAnYQJNkiIiIiInutSNeBdvf7gPt63XZVxtcdwAeijEFEREREJJ90KW8RERERkRwogRYRERERyYESaBERERGRHCiBFhERERHJgRJoEREREZEcRHYhlaiYWR2Q23VhR8ZEoN8rKo5yem6jk57b3uEAd68d6SCGyyias2F0vY5ypec2Oum5jbw+5+xRl0CPFma2ZF+92pie2+ik5yaS3b78OtJzG5303PZeauEQEREREcmBEmgRERERkRwogY7OopEOIEJ6bqOTnptIdvvy60jPbXTSc9tLqQdaRERERCQHqkCLiIiIiORACbSIiIiISA6UQEfIzP7HzF42s+fN7G4zGzvSMeWLmX3AzJabWcrMRu0yNJnM7AwzW2lmq8zsyyMdT76Y2bVmts3MXhzpWPLNzGaY2UNm9lL4evzsSMcko5vm7dFF8/bos6/M20qgo/UgcLi7Hwm8Alw5wvHk04vAOcCjIx1IPphZHLgGeBdwKHChmR06slHlzXXAGSMdRES6gS+6+6HAicAn96Hfm4wMzdujhObtUWufmLeVQEfI3f/k7t3ht4uB6SMZTz65+wp3XznSceTR8cAqd1/t7p3ArcDZIxxTXrj7o8COkY4jCu6+2d2Xhl83AyuAaSMblYxmmrdHFc3bo9C+Mm8rgR4+HwbuH+kgpF/TgPUZ329gFP5BFzIzmwkcAzw1wqHIvkPz9t5N8/YoN5rn7aKRDmC0M7M/A5P7uOur7v7bcMxXCQ5Z3DScsQ3VQJ6byN7AzKqAO4HPuXvTSMcjezfN2yIjb7TP20qgh8jd357tfjO7DHg3cJqPskW39/Tc9jEbgRkZ308Pb5O9nJkVE0zCN7n7XSMdj+z9NG/vMzRvj1L7wrytFo4ImdkZwJeA97p720jHI1k9Dcwxs1lmVgJcANw7wjHJHpiZAb8EVrj790Y6Hhn9NG+PKpq3R6F9Zd5WAh2tHwHVwINm9pyZ/XSkA8oXM3ufmW0AFgJ/MLMHRjqmoQhPGvoU8ADBCQ23u/vykY0qP8zsFuBJYJ6ZbTCzy0c6pjw6CbgYeFv4N/acmZ050kHJqKZ5e5TQvD1q7RPzti7lLSIiIiKSA1WgRURERERyoARaRERERCQHSqBFRERERHKgBFpEREREJAdKoEVEADO71sy2mdmLedred8xsuZmtMLOrw6WbREQkD0Z6zlYCLaOamU3IWAZni5ltDL9uMbMfR7TPz5nZJVnuf7eZfSOKfUukrgPOyMeGzOxNBEs1HQkcDhwHnJqPbYuMZpqzJY+uYwTnbCXQMqq5e727H+3uRwM/Bb4ffl/l7p/I9/7MrAj4MHBzlmF/AN5jZhX53r9Ex90fBXZk3mZmB5rZH83sGTN7zMwOHujmgDKgBCgFioGteQ1YZBTSnC35MtJzthJo2SeZ2VvM7Pfh1183s+vDP6a1ZnZOeKjmhfAPrTgcN9/MHgn/8B4wsyl9bPptwNJwAX/M7DNm9pKZPW9mtwKEl/59mOBSwDK6LQI+7e7zgX8GBlQhc/cngYeAzeG/B9x9RWRRioxymrMlT4Ztzi4aYqAio8WBwFuBQwmu7vR+d/+Smd0NnGVmfwB+CJzt7nVmdj7wnwSVi0wnAc9kfP9lYJa7J8xsbMbtS4CTgdsjeTYSOTOrAt4E/CajFa40vO8coK9Dvhvd/Z1mdhBwCDA9vP1BMzvZ3R+LOGyRfYXmbMnJcM/ZSqClUNzv7l1m9gIQB/4Y3v4CMBOYR9D39GD4hxcn+BTa2xSCS8amPQ/cZGb3APdk3L4NmJq/8GUExIDG8FDzbtz9LuCuLI99H7DY3VsAzOx+gssnK4EWGRjN2ZKrYZ2z1cIhhSIB4O4poMt3XcM+RfBB0oDl6d48dz/C3d/Rx3baCfqk0s4CrgGOBZ4O++0Ix7RH8DxkmLh7E/C6mX0AwAJHDfDh64BTzawoPNx8Kru/iYtIdpqzJSfDPWcrgRYJrARqzWwhgJkVm9lhfYxbARwUjokBM9z9IeD/AWOAqnDcXCAvS+vI8DCzWwgOFc8zsw1mdjlwEXC5mS0DlgNnD3BzdwCvEVTLlgHL3P13EYQtUqg0Zxe4kZ6z1cIhArh7p5mdC1xtZmMI/jZ+QPAHmOl+4Mbw6zjw63C8AVe7e2N431uBK6OOW/LH3S/s566cl0ly9yTwT0OLSET6ozlbRnrOtl1HRURkIMKTWL7k7q/2c/9+wM3uftrwRiYiIr1pzpYoKIEWyZGZzQP2C9eg7Ov+4wh69p4b1sBEROQNNGdLFJRAi4iIiIjkQCcRioiIiIjkQAm0iIiIiEgOlECLiIiIiORACbSIiIiISA6UQIuIiIiI5OD/A6AZDT1irX9dAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# set offsets to zero\n", + "inst.trigger_delay = 0\n", + "channel.offset = 0\n", + "\n", + "# set horizontal axis to 5 ns per division\n", + "inst.time_div = u.Quantity(5, u.ns)\n", + "\n", + "# set to 250 mV per division and read waveform back\n", + "channel.scale = u.Quantity(250, u.mV)\n", + "x1, y1 = channel.read_waveform()\n", + "\n", + "# allow for 250 ms to not have it read to fast\n", + "sleep(0.25)\n", + "\n", + "# set to 1 V per division and read the waveform back\n", + "channel.scale = u.Quantity(1, u.V)\n", + "x2, y2 = channel.read_waveform()\n", + "\n", + "# plot the results\n", + "fig, ax = plt.subplots(1, 2, sharey=True, figsize=(12,4))\n", + "\n", + "ax[0].plot(x1, y1)\n", + "ax[0].set_xlabel(\"Time (s)\")\n", + "ax[0].set_ylabel(\"Signal (V)\")\n", + "ax[0].set_title(\"250 mV / division: Signal clipped\")\n", + "ax[1].plot(x2, y2)\n", + "ax[1].set_xlabel(\"Time (s)\")\n", + "ax[1].set_title(\"1 V / division: Signal visible\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Clearly, the left signal is clipped on top. This results from the fact that the signal did not fit on the oscilloscope screen. If the signal is off scale, the data returned is simple the largest possible one, in this case, full signal. Since we artificially scale the two figures to the same y scale, this of course does not show up on top but at 1 V, which is equivalent to 4 divisions up.\n", + "\n", + "**Remember**: Reading a wave form only returns the data that is displayed on the oscilloscope screen." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Math functions\n", + "\n", + "Many math functions are available on these oscilloscopes, depending on the options that the oscilloscope is shipped with, more or less functions are available. For an overview of the implemented functions and how they work, have a look at the InstrumentKit documentation. You will find all functions in the `Math.Operators` subclass.\n", + "\n", + "For now, let's set up averaging of the first channel." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "# Access the first math function `F1`\n", + "function = inst.math[0]\n", + "\n", + "# turn on the trace of this math function\n", + "function.trace = True\n", + "\n", + "# set it to averaging of channel 1 (0 in python)\n", + "function.operator.average(('C', 0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will have set up the first function to average the first channel. The parameter that is passed on to to average, the required parameter, is the source it should average. Different operators have of course different parameters that are required / optional. \n", + "\n", + "**Note**: There are two ways that sources can be specified. If an integer is given, it is assumed that the source is a channel. Alternatively another source, e.g., a math function could also be defined. This would be done by submitting a tuple, i.e., `('F', 0)` to select the first math function (called `F1` in the oscilloscope).\n", + "\n", + "To check if this all worked we can read back the channel itself and the average math function and plot the results." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Average of the channel (math)')" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtAAAAEWCAYAAABPDqCoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy86wFpkAAAACXBIWXMAAAsTAAALEwEAmpwYAABOhElEQVR4nO3dd3xc1Zn/8c+j0agXN7nbuGCK6eDQQichJo2EJZseSEhogbTdbJLd/WWTbE12E0iBEBISCAFCCUnoxIDpYLDB3bgXucuSrDYjTdH5/XHvyGMjyRpprkbyfN+v17w8M/fMnTOy9OjRuc85x5xziIiIiIhI3xTkugMiIiIiIsOJEmgRERERkQwogRYRERERyYASaBERERGRDCiBFhERERHJgBJoEREREZEMKIGWQWNm3zOzP+S6Hwcys+fM7Is5eu87zOw/cvHeIiLZYmbXmtkuM2s1s9F9aH+Fmb00CP1yZnZ40O+TCTM7z8y2HqTNvWb2kUHqT6//F2b2JzO7eDD6MpwogZasMrNPmdlCP4juMLMnzOysXPfrUDdYv4xEpO/8P84bzaw4130JkpmFgZ8AFznnKpxz9Qccn+YnsoW56eHwYmbHAycAfw3g3P35v/ghoIGeAyiBlqwxs28ANwH/BYwDpgK3AJfksFtZpV8AItIXZjYNOBtwwIcDOP9QikXjgBJgRa47coi4GrjbDZGd7pxzrwNVZjYn130ZSpRAS1aYWTXwA+DLzrmHnHNtzrm4c+4R59w305oWmdnvzazFzFak/0Ca2bfNbL1/bKWZfTTt2BVm9pKZ/Z8/orMx/ZKSP9Lz72b2sv/6v5nZmLTjp5vZK2a218yWmNl5ffxc3zOzB83sD2bWDFxhZtVmdrs/wr7NzP7DzEJ++5lm9qyZ1ZvZHjO728xGpJ3vJDN70+/jfXi/dNLf70tmts7MGszsYTOb6D//jlGDVOmJmR0N3Aqc4Y/87+3LZxORQH0OeA24A7gcwMyK/Rh0bKqRmdWYWdTMxvqPP2hmi/12r/ijkam2m8zsW2a2FGgzs8KDxM2Qmf3Yj0Ubzez69DjSWyw7kN/3m8xsu3+7yX/uCGC132yvmT3bzctfSDveamZnpJ23p5ieSd9CZvbPaV+HRWY2Ja3Je8xsrf81vdnMzH/dweL1JjP7RzNbamZNZnafmZX4x84zs61m9g9mttvv5+cP+Hr9n5ltMa+05VYzK+2u/924GHg+7VxXmPe77Ub/M2wwszP952v99788rf0HzOwtM2v2j39vIP8XvueAD/Sx//nBOaebbgO+AXOBBFDYS5vvAe3A+4EQ8N/Aa2nHPwZMxPvD7uNAGzDBP3YFEAe+5L/2WmA7YP7x54D1wBFAqf/4f/xjk4B6/30LgPf6j2vSXvvFXvocBz7iv7YU+DPwK6AcGAu8Dlzttz/cP38xUIMXrG7yjxUBm4GvA2HgMv/c/+EfvwDYA5zsv/7nwAv+sWl4I1mFaX3r6rf/9Xkp198Huummm3cD1gHXAaf4P+fj/Od/C/xnWrsvA0/6908CdgOn+XHucmATUOwf3wQsBqYApf5zvcXNa4CVwGRgJPB0ehzpLZZ183l+gPcHwVg/tr0C/Lt/7B3x6YDXdhe/rqD3mJ5J374JLAOOBAyv/GG0f8wBjwIj8K6K1gFz/WM9xuu0r/fr/td3FLAKuMY/dh7e77wf4MXz9wMRYKR//EbgYf91lcAjwH+nvXZrD5+l3O9zzQFfqwTwef9r9R/AFuBmv+8XAS1ARdr5j/O/J44HdgEf6e//hd/mG8BDuf65Gkq3nHdAt0PjBnwa2HmQNt8Dnk57PBuI9tJ+MXCJf/8KYF3asTI/CIz3Hz8H/Gva8evY90vpW8BdB5z7KeDytNf2lkC/kPZ4HNCB/8vLf+6TwPweXv8R4C3//jndBKVX2JdA3w78KO1YhR/UpvUQ9Lr6jRJo3XQbMjfgLP9nd4z/+G3g6/799wDr09q+DHzOv/9L/KQ07fhq4Fz//ibgCwd57/S4+SxpSaf/3g4o7EcsWw+8P+3x+4BN/v13xKcDXttd/Ooxpvejb6tTn7mbYw44K+3x/cC3e2jbFa/Tvt6fSXv8I+BW//55QPSAz7QbOB0viW8DZqYdOwPYmPbanhLoSX6fSw74Wq1Ne3yc32Zc2nP1wIk9nPMm4Mb+/F+kPfcl4Nlc/UwNxdtQqqGS4a0eGGNmhc65RC/tdqbdjwAlqdeY2efw/sqd5h+vAMZ091rnXMS/ClfRy7lTxw4DPmZmH0o7HgbmH/RTeWrT7h/mv3aH//7g/ZVfC2Bm44Cf4tU+VvrHGv12E4Ftzo9Gvs1p9ycCb6YeOOdazaweL6Bu62NfRST3Lgf+5pzb4z++x3/uRry4U2Zmp+GNDJ6IN9oKXny53MxuSDtXEV5sSEmPRxwkbk48oH2fY1k3JrJ/vNp8QL/6o6eYPirDvk3BS/AP+j6k/W44SLzu6bXpn7n+gN93qXPX4CWhi9L6b3ijuwez1/+3Eu+KbcqutPtRAOfcgc+lPtdpwP8Ax+J9/xQDDxzkfQ/2+7UyrW8CSqAla17FGzH4CPBgpi82s8OAXwMXAq8655Jmthgv6AxULd4I9Jf6+fr0hLcW73OO6eEPhf/y2x/nnGswbxmiX/jHdgCTzMzSkuip7Av82/F+qQFgZuXAaLzkuc1/ugxo9u+P76GPIpIjfp3r3wMhM0slJcXACDM7wTm3xMzuxxtR3QU86pxr8dvV4pV3/Gcvb9H1s96HuLkDr3wjJb0u+GCx7ECp+JSaKDjVf64vMo1PmfatFpgJLM/wfXqL1wOxBy+hPcY5l9Hgh3OuzcxS5Yh1/Xz/e/A+x8XOuXYzu4l9f1T193fF0cCSfr72kKRJhJIVzrkm4LvAzWb2ETMrM7OwmV1sZj/qwylSdV91AP5kjGN7fUXf/QH4kJm9z59sUuJPAJl80FcewDm3A/gb8GMzqzKzAn8iyrl+k0qgFWgys0l4tXkpr+LVsX3F/9pcCpyadvxe4PNmdqJ5y179F7DAObfJOVeHl0h/xv8MX8D7hZGyC5hsZkWZfiYRyaqPAEm8ErUT/dvRwIt4EwvBS3A+jlf6dk/aa38NXGNmp5mn3J8QVtnDex0sbt4PfNXMJvmT476VOtCHWHage4F/NW/S4xi8eN/Xdf3rgE5gRl8a96NvvwH+3cxm+V+3460Pa1HTe7zuN+dcJ97/5Y22b3LoJDN7Xx9P8TjQ02fti0qgwU+eTwU+lXYso/+LNOcCTwygT4ccJdCSNc65H+NdSvxXvB/SWuB64C99eO1K4Md4SeYuvBqvl7PUr1q8pfT+Oa1f36T/3/+fw7ssthLvct+DwAT/2PfxJgE2AY8BD6X1IwZcildv1oD3CzT9+NPA/wP+hDdyNBP4RNr7fsnvdz1wDF79dMqzeCNDO81sDyKSK5cDv3PObXHO7Uzd8EYEP+2XrC3Au6o0kbSkxDm3EO/n/Bd4sWUdXrzoVh/i5q/xEtGlwFt4iVkCL8GH3mPZgf4DWOifaxleuVmf1gZ2zkWA/wRe9leROL0PL8ukbz/B+2Phb3hX6G7Hm/B9MD3G6yz4Ft7/32vmreD0NN4kx764De97pb9XYK8DfmBmLXh/6NyfOtCf/wszexfQ6rzl7MSXmu0qIiIihzB/abJbnXOHHbSx5JSZ3QPc75z7yxDoy5+A251zj+e6L0OJEmgREZFDkF+PfT7eyOw4vKtbrznnvpbLfokcCpRAi4iIHILMrAxvQ46j8Ca1PQZ81TnX3OsLReSglECLiIiIiGRAkwhFRERERDIw7NaBHjNmjJs2bVquuyEi0i+LFi3a45yryXU/BotitogMZz3F7GGXQE+bNo2FCxfmuhsiIv1iZpsP3urQoZgtIsNZTzFbJRwiIiIiIhlQAi0iIiIikgEl0CIiIiIiGVACLSIiIiKSASXQIiIiIiIZUAItIiIiIpIBJdAiIiIiIhlQAi0yyJ5asZN1u1tz3Q0REemj1o4Edy/YTCLZmeuuyBChBFpkED2zahdX37WIG59ek+uuiIhIHzjn+Mf7l/Avf17O65sact0dGSKUQIsMkt3N7Xzj/iUArNrenOPeiIhIX9y9YAtPrtgJwPq6thz3RoYKJdAig+S5NXU0ReO875hxbKxvo60jkesuiYjIQfzlrW0cM7GKsqIQG+pUficeJdAig2Tl9mbKikL83cmTcQ7e3qlRaBGRoayz07FqRzNzDhvJ9DHlbNyjEWjxBJZAm1mJmb1uZkvMbIWZfb+bNleYWZ2ZLfZvXwyqPyK5tnJ7M0dPqOK4ydUArFAZh4jIkLa5IUJbLMnsiVXMqKlgg0o4xBfkCHQHcIFz7gTgRGCumZ3eTbv7nHMn+rffBNgfkZzp7HSs3NHM7AlVjK8qYVR5ESu2KYEWERnKVvoDHcdMrGb6mHK2NkboSCRz3CsZCgJLoJ0nVSwU9m8uqPcTGcpqGyO0diQ4ZmIVZsbsCVWs3KEEWkRkKFuxvYnCAmPWuApm1pTT6WBzfSTX3ZIhINAaaDMLmdliYDcwzzm3oJtmf2dmS83sQTOb0sN5rjKzhWa2sK6uLsguiwQiVa4xe2IVAEeNr2TNrhac09+UcuhRzJZDxcodzRw+toLiwhAzxlQAaCKhAAEn0M65pHPuRGAycKqZHXtAk0eAac6544F5wJ09nOc259wc59ycmpqaILssEoiV25sJFRhHjKsEYFRFER2JTjoSWpRfDj2K2XKoWLG9mWMmevNWpo0pA7SUnXgGZRUO59xeYD4w94Dn651zHf7D3wCnDEZ/RAbbml0tzBhTTkk4BEBlSRiA5vZ4LrslIiI92BuJUdfSwVHjvYGPypIwo8uL2NoYzXHPZCgIchWOGjMb4d8vBd4LvH1AmwlpDz8MrAqqPyK5VNsYZeqosq7HlcWFALS0ay1oEZGhqLbBS5Snjt4Xu6tLw7Ro4EOAwgDPPQG408xCeIn6/c65R83sB8BC59zDwFfM7MNAAmgArgiwPyI5s7UxwqnTRnY9rixRAi0iMpRtbfQmC04eWdr1XGVJoeK2AAEm0M65pcBJ3Tz/3bT73wG+E1QfRIaCpkiclvYEU9JHoP0SDo1kiIgMTbVdCfS+2F1RUqi4LYB2IhQJXG0PoxgArRrJEBEZkrY2RqkqKaS6NNz1XGVxWCPQAiiBFgnc1m5GMVTCISIytNU2RPaL26ASDtlHCbRIwFIztqeMfGcJh1bhEBEZmrY2RpkyqnS/5ypLNIlQPEqgRQJW2xChsriQqtJ9Uw4qtAqHiMiQ5Zxja2O02xHotliSZKc2wcp3SqBFAra1McrkUWWYWddzoQKjoliXAkVEhqL6thjReJIpIw8cgdb8FfEogRYJmDeKUfqO5ys1m1tEZEiqbXjn3BWAKpXfiU8JtEiAnHPUNkZ6SaA1iiEiMtSk5q5MfkcNtMrvxKMEWiRAze0JIrEkE6u7S6DDtHRoFENEZKjZ0eQl0BNHvHMSIWgNf1ECLRKo+tYOAMZUFr3jmEagRUSGprqWDkrCBVQW77/fnEagJUUJtEiAGtpiAIwqL37HMW85JAVhEZGhZndLBzWVxftN/oa0BFpXD/OeEmiRAO1p9RLo0eXvHIH2VuFQEBYRGWrqWjoYW1nyjuf3lXBo8CPfKYEWCVBqBHpMxTtHoKtKCmlWEBYRGXLqWjqo6SZuq4RDUpRAiwQoVQM9sjz8jmOVJYXEEp10JJKD3S0REelFqoTjQCXhEEWhAi1jJ0qgRYJU3xajsqSQ4sLQO47pUqCIyNDTkUjSFI0ztpsEGjQBXDxKoEUCVN8W67b+GXQpUERkKErNXeluBBqUQItHCbRIgOpbOxjdTR0daD1REZGhqK7FK73rOYEOK26LEmiRIDVoBFpEZFjZ3dwO0O0qHKARaPEogRYJ0J7WGKMrDpZAayRDRGSoqGs92Ai0liAVJdAigensdDRGYozuZhMV8NaBBmjr0CocIiJDRaqEo+fBD22CJQEm0GZWYmavm9kSM1thZt/vpk2xmd1nZuvMbIGZTQuqPyKDrSkaJ9npGNVDCUdJ2FuZo13L2ImIDBm7WzoYVV5EONR9iqQSDoFgR6A7gAuccycAJwJzzez0A9pcCTQ65w4HbgR+GGB/RAZVfVvvoxgl/tJ20ZgSaBGRocLbhbD7K4cAZUUhonHF7XwXWALtPK3+w7B/cwc0uwS407//IHChHbjxvMgwVd/a8y6EACVF3o9fR6Jz0PokIiK9q2vp6DFugzf4kex0xJOK3fks0BpoMwuZ2WJgNzDPObfggCaTgFoA51wCaAJGd3Oeq8xsoZktrKurC7LLIllT72/j3VMJR1GoADNo10iGHGIUs2U4a47GGVH2zt1jU4rDXuqk2J3fAk2gnXNJ59yJwGTgVDM7tp/nuc05N8c5N6empiarfRQJSiqB7mkZOzOjpDCkICyHHMVsGc6aonGqS3tOoFPzV3T1ML8Nyioczrm9wHxg7gGHtgFTAMysEKgG6gejTyJBa4p4CXR1LyMZJeEC1dKJiAwRzjma2+NU9ZZA+/NXNPiR34JchaPGzEb490uB9wJvH9DsYeBy//5lwLPOuQPrpEWGpb2ROKXhEMV+sO1OaThEe1yjGCIiQ0F7vJN40lFV0pcSDsXufFYY4LknAHeaWQgvUb/fOfeomf0AWOicexi4HbjLzNYBDcAnAuyPyKBqOkgdHXiXAjWKISIyNDT7G6RUlfacHqUGRTq0BGleCyyBds4tBU7q5vnvpt1vBz4WVB9EcmnvQeroAIo1Ai0iMmQ0Rf0EupcR6BKNQAvaiVAkME2RgyfQJeECjUCLiAwRzakEupfYrRFoASXQIoHpSwlHqUo4RESGjFQJR++rcPhr+GsEOq8pgRYJyN5ojBGl3S9hl1ISDmkrbxGRIaI56m3RXVXSc4Vrahk7DX7kNyXQIgFpisZ7XcIOUiUcGsUQERkK9k0i7K2EQ7vIihJokUC0x5O0xzv7UAMdIhrTKIaIyFDQFPES6EqNQMtBKIEWCUBqJndflrHTRBQRkaGhuT1OSbig1/X7UyPQSqDzmxJokQCkEuiDjkAXahk7EZGhojma6NOVQ1AJR75TAi0SgL3+ZcCDTyLUMnYiIkNFc3u81zWgIX0EWgl0PlMCLRKAvZEYcPASjtJwiESnI55UIBYRybXm9nivEwgBCkMFFBaYVlDKc0qgRQLQ5xIOTUYRERkymqLxXpewSykJh7QOdJ5TAi0SgK4Eug/L2IEuBYqIDAXN0cRBR6DBL7/TCHReUwItEoC9kTihAqOyuPeRjGKNQIuIDBnN7fGDXjkEbztvxe38pgRaJACpy4Bm1mu70q7Z3ArEIiK55JyjOXrwSYQAxeECrcKR55RAiwRgbzTOiLLeV+CAfTXQ0ZgCsYhILrXFknQ6qCrtQw10YYgOjUDnNSXQIgFoivbtMmBXDbRGoEVEcio1d0Uj0NIXSqBFAtAUPfhSSLCvhEO1dCIiudXSntrGuw+DH6qBzntKoEUCEOlIUFHc81awKfuWsdNIhohILrV1eAlxeZ9id4Hidp5TAi0SgEgsSWm4L2uJej+CUY1kiIjkVDTmxeGyooPH7uLCkCZ/57nAEmgzm2Jm881spZmtMLOvdtPmPDNrMrPF/u27QfVHZDBFYgnKig4+ilFcqBIOEZGhoC2WAOhT7NYItBz8z6z+SwD/4Jx708wqgUVmNs85t/KAdi865z4YYD9EBl0klqSsD5cBS/1ArdncIiK5tW8Eum+DHxr4yG+BjUA753Y4597077cAq4BJQb2fyFCR7HR0JDop61MJh2qgRUSGgn0j0H0rv9MqHPltUGqgzWwacBKwoJvDZ5jZEjN7wsyO6eH1V5nZQjNbWFdXF2RXRQYs4gfhPk1EKVQNtBx6FLNlOOoage7jBHCNQOe3wBNoM6sA/gR8zTnXfMDhN4HDnHMnAD8H/tLdOZxztznn5jjn5tTU1ATaX5GBivhBuLQPlwELQwUUFpgCsRxSFLNlOEqtwlEW7ksJhzcC7ZwLulsyRAWaQJtZGC95vts599CBx51zzc65Vv/+40DYzMYE2SeRoKUS6PI+XAYEby1olXCIiORWJJ6gKFRAYejgqVGxn2SrjCN/BbkKhwG3A6uccz/poc14vx1mdqrfn/qg+iQyGNo6vBKOvoxAgxeItROhiEhuRfs4+Rv2zV/p0OBH3gpyFY53A58FlpnZYv+5fwamAjjnbgUuA641swQQBT7hdD1EhrlUPXNfZnKDvxxSTAm0iEgutXUk+1S+AV4JB+CvBX3wnQvl0BNYAu2cewmwg7T5BfCLoPogkgupEei+zOQGv4RDI9AiIjkVjScoK+5b3NYKSqKdCEWyLJO1RCE1m1tBWEQkl9o6khldOQQ0+JHHlECLZFmmkwi9Ha0UhEVEcika63sCndpFVjXQ+UsJtEiWpdaB7vMkwsKQZnKLiORYJJ7oc+mdRqBFCbRIlkUyLuHQCLSISK5FOpIZDXwAit15TAm0SJa1pTZS6fNsbo1Ai4jkWiSWpDzDGmiVcOQvJdAiWRaNJSgNhygo6HURmi7FGoEWEcm5tlgmJRz+CLRKOPKWEmiRLGuLJSnv42L8oBFoEZFcc85lOInQr4HWCHTeUgItkmXRWN/r6EA10CIiuRZLdpLodBktPwqpjVQkHymBFsmyto5En5ewA41Ai4jk2r71+/tYwlGojVTyXa/fKWY2GfgEcDYwEW+77eXAY8ATzjl954gcIBrPbAS6uLCAWKKTzk7X57ppERHJnkxXTyoOp2/lLfmoxxFoM/sd8FsgBvwQ+CRwHfA0MBd4yczOGYxOigwnkQzq6GDfpcBYUn+PiojkQubr96sGOt/1NgL9Y+fc8m6eXw48ZGZFwNRguiUyfLV1JBhVXtbn9qlA3BHv7EqmRURk8GS6g6yZUVxYQIfmr+St3mqgL/ZLOLrlnIs559YF0CeRYS0a798ItJZDEhHJjbaOzEo4wBv80PyV/NVbAj0ReNXMXjSz68ysZrA6JTKctXUk+zwRBfYfgRYRkcEXjXslHGXFfY/dJeGQVlDKYz0m0M65r+OVaPwrcByw1MyeNLPLzaxysDooMtxEYwmNQIuIDCOZTiIEbYKV73pdxs55nnfOXQtMBm4EvgbsGoS+iQw7zjki8b5vBwsagRYRybWIX8JRmsE8lBItQZrX+nStwsyOw1vO7uPAHuA7QXZKZLhqj3fiHJRmUMKhEWgRkdxKrcJRrhIO6aMev1PMbBZe0vwJIAn8EbjIObdhkPomMuy0+UE408uAoBFoEZFcaetPCUdhgZaxy2O9lXA8CRQDH3fOHe+c+69Mkmczm2Jm881spZmtMLOvdtPGzOxnZrbOzJaa2cn9+AwiQ0a0n0EY0EiGiEiORGNJCmxfPO6LknBIG6nksd6uVcw62E6DZmbOOdfD4QTwD865N/1Jh4vMbJ5zbmVam4uBWf7tNOCX/r8iw1JqIkomOxGmSjhUSycikhveBliFmPV9N9iScAENbYrb+aq3P7WeNbMbzGy/zVLMrMjMLjCzO4HLe3qxc26Hc+5N/34LsAqYdECzS4Df+5MVXwNGmNmEfn0SkSEgGtcItIjIcBONJzIa+AAoLgxp7koe6y2BnotX+3yvmW33SzE2AGvxtvW+yTl3R1/exMymAScBCw44NAmoTXu8lXcm2ZjZVWa20MwW1tXV9eUtRXKiazvYcOaTCDUCLYcKxWwZbqKxZEYrcIA3f0VzV/JXj7/lnXPtwC3ALWYWBsYAUefc3kzewMwqgD8BX3PONfenk86524DbAObMmdNTyYhIzqVGkTMZydAItBxqFLNluPFKODJLoFUDnd/6NEzmnIsDOzI9uZ94/wm42zn3UDdNtgFT0h5P9p8TGZaiMW80IqO1RDUCLSKSU9F4sisW91VxoUag81nfp5tmyLxK/NuBVc65n/TQ7GHgc/5qHKcDTc65jBN1kaEiVQOdSQJdFNIItIhILkX7OQKtGuj81fdCzcy9G/gssMzMFvvP/TPe9uA4524FHgfeD6wDIsDnA+yPSOCi/SjhKCgwikIFGoEWEcmRaDxJdWk4o9eUFIaIJx3JTkeooO+rd8ihIbAE2jn3EtDrd5S/BN6Xg+qDyGCLpiYRZjqbO1ygWjoRkRyJxpL9itsAHQlvCTzJL73tRNgCdDf5w/By36rAeiUyTKVqoEsyWIwf/OWQVEsnIpIT0Xjmq3CUdE0A76SsKIheyVDW2yoclYPZEZFDQTSepChUQGEoswS6RCPQIiI5059VOIr9hFvzV/JTn685mNlYoCT12Dm3JZAeiQxj7fEkJeHM5+ZqNreISO5E40lKMp5EmCrhUOzORwf9TW9mHzaztcBG4HlgE/BEwP0SGZYisUS/auG0nqiISG4kOx2xRCdlGWyABd4kQtAIdL7qy1DZvwOnA2ucc9OBC4HXAu2VyDAVjXdmPBEFvBFo1UCLiAy+fasnZTh3JawlSPNZX75b4s65eqDAzAqcc/OBOQH3S2RYisYyX4wfNAItIpIrka7Vk/o3Aq0SjvzUl++Wvf523C8Ad5vZbqAt2G6JDE/t8SSl/ayBbmlPBNAjERHpTXs/dpAFTSLMd335TX8JEAW+DjwJrAc+FGSnRIar/tZAFxdqBFpEJBcicW/wIuNVOAo1iTCfHfQ3vXMufbT5zgD7IjLsReOdjCrvTwmHaqBFRHIhGvNroDNdB1oj0HmtL6twXGpma82sycyazazFzJoHo3Miw017PPPdrEAj0CIiudKVQPd3GTsNfuSlvlxr/hHwIefcqqA7IzLcRWP9q4HWCLSISG50rcKRaQ101yRCDX7ko778pt+l5Fmkb6LxZP9qoLUKh4hITkT8EehMa6BLwvu28pb805ff9AvN7D7gL0BH6knn3ENBdUpkuOr3MnaFBXQkOnHOYWYB9ExERLqTGoHONHYXayOVvNaXBLoKiAAXpT3nACXQImkSyU5iyc6MLwOCNwLtHMSSnV1BWUREghft5wh0OGQUmFbhyFd9WYXj84PREZHhrt0PopnuZgX7L4ekBFpEZPDs24kws9hrZpSEQxqBzlMHTaDN7GfdPN0ELHTO/TX7XRIZnvq7FBLsvyB/VUk4q/0SEZGepWqgS/oxeFFcWEC75q/kpb4MlZUAJwJr/dvxwGTgSjO7KbCeiQwz+5ZCynwSYZmfQKfOISIig6M9nqQkXEBBQebzT0rCIS1jl6f68pv+eODdzrkkgJn9EngROAtYFmDfRIaV/i6FBFBe7L2mrUMJtIjIYOrvDrLgJdDtqoHOS30ZgR4JVKQ9LgdG+Ql1R/cvATP7rZntNrPlPRw/z9+cZbF/+25GPRcZYvbV0WVeA50K3pFYIqt9EhGR3kVj/Zv8DV4JR4dqoPNSXzdSWWxmzwEGnAP8l5mVA0/38ro7gF8Av++lzYvOuQ/2rasiQ1uq/KI/y9h1jUCrhENEZFBF44l+7SALXryPKoHOS31ZheN2M3scONV/6p+dc9v9+9/s5XUvmNm0gXdRZHiIxr3R4/5cCuwage7QCLSIyGDydpDtXwJdXhzqmoQo+aXHa81mdpT/78nABKDWv433n8uGM8xsiZk9YWbH9NKXq8xsoZktrKury9Jbi2RXNOYvY9ePQFxR7CXQGoGWQ4FitgwnkViy3yPQ5UWFtGngIy/1NlT2DeAq4MfdHHPABQN87zeBw5xzrWb2frydDmd119A5dxtwG8CcOXPcAN9XJBADmUSYWsBfNdByKFDMluGkPZ5kRFlRv15bUVxIqxLovNRjAu2cu8r/9/wg3tg515x2/3Ezu8XMxjjn9gTxfiJB69oOth+TCMv9EWgFYhGRwRWJJZk4or8lHBqBzle9lXC8y8zGpz3+nJn91cx+ZmajBvrGZjbezMy/f6rfl/qBnlckV9q7toPNvAa6uLCAAoOIlrETERlUkQHUQJcVh7T8aJ7qbajsV0AMwMzOAf4Hb0WNJvxLc70xs3uBV4EjzWyrmV1pZteY2TV+k8uA5Wa2BPgZ8AnnnC71ybC1bzerzEegzcyrpVMJh4jIoGqP978GuqKokFiyk5jWgs47vQ2VhZxzDf79jwO3Oef+BPzJzBYf7MTOuU8e5Pgv8Ja5EzkkRONJikIFFIYyT6DBG8nQCLSIyOAayAh0qvyurSNBUWH/6qhleOrtN33IzFIJ9oXAs2nH+rdlj8ghLLUdbH9pBFpEZHA554jGk10TuTNVofkreau3RPhe4Hkz2wNE8bbvxswOxyvjEJE00QEshQTeSIbWExURGTztca/0oqS/y9h1LUGqBDrf9LYKx3+a2TN4a0D/La0+uQC4YTA6JzKcROLJfk0gTCkrCmk2t4jIIEqtnlQ2gI1UAMXuPNTrb3vn3GvdPLcmuO6IDF/RWLJf23inlBcXsrulPYs9EhGR3qTW3u/3JMKuEg5dPcw3/S/YFJH9tMeTlA6gBrqsSJMIRUQGU3tqA6x+Xj1Mn0Qo+UUJtEiWRAewFBJoEqGIyGBLzTvp7yocmkSYv5RAi2SJtxTSAGqgtYydiMiginZtgDXwZewkvyiBFsmSgSzGD95IRlssgfYTEhEZHBG/hKO/81c0iTB/KYEWyZJobKA10IV0OujQjlYiIoOifYAj0MWFIcIh0yTCPKQEWiRLovH+72YFGskQERlsA62BhtQa/orb+UYJtEiWeJMIB7IOtPdabaYiIjI4utaBHuAEcE0izD9KoEWyINnpiCU6BzaK4QdwBWIRkcGRmkTY350IwZ+/oridd5RAi2RBtGst0QHUQBenRqAViEVEBkNX7B5g+V2baqDzjhJokSyIZqGOrqKrBlqBWERkMERiScIhIxzqfzpUXqwSjnykBFokC9oHuBQSpNdAKxCLiAyG9gFO/gaVcOQrJdAiWRDpWgqp/5MIy4tSC/JrBFpEZDBEYokBrd8P3gi0Euj8owRaJAuyUwPtBXGNQIuIDI5ovHNAAx/gjUCrhCP/KIEWyYKumdwDWoXDC+JakF9EZHBEY4kBxW3wJxHGktpFNs8ElkCb2W/NbLeZLe/huJnZz8xsnZktNbOTg+qLSNDaszCTuyRcQDhkNEXj2eqWiIj0IhpPDmgNaPBKOJKdTrvI5pkgR6DvAOb2cvxiYJZ/uwr4ZYB9EQlUNmqgzYyRZUXsjcSy1S0REelFJJadSYQALe0q48gngSXQzrkXgIZemlwC/N55XgNGmNmEoPojEqRsrCUKMLKsiIY2JdAiIoMhGksOeBJhdWkYQFcP80wua6AnAbVpj7f6z4kMO6kEumQAkwgBRpaH2RtREBYRGQzRLCxjN7KsCIBGXT3MK8NiEqGZXWVmC81sYV1dXa67I/IO7VnYSAW8QKwgLMOdYrYMF9HYwGugR5X7CbSuHuaVXCbQ24ApaY8n+8+9g3PuNufcHOfcnJqamkHpnEgmslXCMUIJtBwCFLNluIjGkgNehWNEmVfCoauH+SWXCfTDwOf81ThOB5qcczty2B+RfovEkhSFCigcwHawAKPKwzRG4loOSURkEGRjFY5UCUeDBj/yysBWD++Fmd0LnAeMMbOtwL8BYQDn3K3A48D7gXVABPh8UH0RCVp7PElJeOB/j44sKyLZ6WhuT3RNTBERkeyLJTpJdLoBXzksKwpRVFigq4d5JrAE2jn3yYMcd8CXg3p/kcGUjZncsG8kY28kpgRaRCRA+3aQHVjs9pYgDasGOs8Mi0mEIkNdNmZyg7cKB6Cl7EREApbaQTZbgx+NqoHOK0qgRbIgEktSOoBNVFL2jUArEIuIBCk1Aj3QGmjwE2gNfOQVJdAiWdAeT1KapRpo0Ai0iEjQIjFv58BsXT1UDXR+UQItkgXReHZroBWIRUSC1d5VA52dq4e6cphflECLZEE0lp0a6MqSQkIFpgRaRCRgkSxtgAX7NsHq7NQSpPlCCbRIFngj0AMfxSgoMEaUhjUZRUQkYKkEOis10OVFdDpoaU8M+FwyPCiBFskCbwQ6Oz9OI8uL2KsRaBGRQLV1eMluRXE2Sjj8FZQUu/OGEmiRLIjEElm5DAheINYkQhGRYLWmEuiS7K2gpPK7/KEEWmSAnHO0diSoLMnOxicjyopobFMJh4hIkFLlFlkZgS7ftwmW5Acl0CIDFI0n6XTZGcUAmFBdwva9UbzNOkVEJAitHQnCIaO4MBtLkKY2wdLgR75QAi0yQK1ZHMUAmD6mnJaOBHWtHVk5n4iIvFNre4KK4kLMbMDnSo1AN7QpbucLJdAiA9Ti19FVZmkEekZNBQAb6tqycj4REXmn1o5E1q4cVhYXMqq8iPW7FbfzhRJokQHK9gj0jDHlAGzco0AsIhKUlvYEFcXZmbtiZsyeUMWKHU1ZOZ8MfUqgRQYotRRSeZYS6EkjSikuLGBDXWtWziciIu/U2hGnMktxG+CYiVWs2dlKPNmZtXPK0KUEWmSAWrK4lih4m6lMH1OuEg4RkQC1dSQpL87O8qMAsydWEUt2sm63Bj/ygRJokQFKlXBkqwYaYEZNORtUwiEiEhivBjo7JRzgjUADrNzenLVzytClBFpkgFqzPAINMGNMBVsaIsQSuhQoIhKEFn8VjmyZPqaCknABK5RA5wUl0CIDlM3drFKmjykn2enY0hDJ2jlFRGSf1o54Vq8chgqMo8ZXsWK7JhLmAyXQIgPU0p6gKFRAcWH2aulm1HgrcWgioYhI9sWTnbTHO7M6Ag1w9IRK1qoGOi8EmkCb2VwzW21m68zs290cv8LM6sxssX/7YpD9EQlCa0c8q6PPsG8taC1lJyKSfW0BlN6BV37X0BbTlt55ILAE2sxCwM3AxcBs4JNmNrubpvc55070b78Jqj8iQWnNch0dQHVpmDEVRVqJQ0QkAC3t2S+9g31XD9crdh/yghyBPhVY55zb4JyLAX8ELgnw/URyorUj+wk0eCMZG/boUqCISLal5q5kcx1oSN9JVrH7UBdkAj0JqE17vNV/7kB/Z2ZLzexBM5vS3YnM7CozW2hmC+vq6oLoq0i/ZXM72HQzarQWtAxPitky1GV7A6yUKSNLCYdMy5DmgVxPInwEmOacOx6YB9zZXSPn3G3OuTnOuTk1NTWD2kGRgwlsBLqmnPq2GE2ReNbPLRIkxWwZ6loCWD0JoDBUwNRRZWzU4MchL8gEehuQPqI82X+ui3Ou3jnX4T/8DXBKgP0RCUQQNdDglXAArFcZh4hIVnVtgBVA7J6u8ru8EGQC/QYwy8ymm1kR8Ang4fQGZjYh7eGHgVUB9kckEEGVcEzvWspOIxkiItkUxPr9KTNrytlUHyHZ6bJ+bhk6sv+d43POJczseuApIAT81jm3wsx+ACx0zj0MfMXMPgwkgAbgiqD6IxKUlvZEIKMYU0eVUVhgbNRIhohIVqVGoIMqv4slOtnWGGXq6LKsn1+GhsASaADn3OPA4wc89920+98BvhNkH0SCFEt00pHI/mL8AOFQATNrKli6VbtaiYhkU6oGurwo+7H7yPFVACzdtlcJ9CEs15MIRYa1tgAvAwKcNmMUCzc1Ekt0BnJ+EZF8lJq7UlBgWT/3MROrKC8K8er6+qyfW4YOJdAiA9Aa0G5WKWfMGE00nmTZtr2BnF9EJB+1dsQDi9vhUAHvmj6K1zYogT6UKYEWGYDUblaVgY1AjwbQSIaISBa1tCcoLw4Fdv4zZoxmfV0bu5vbA3sPyS0l0CID0BZLjUCHAzn/qPIijhpfyasayRARyZqdze2MqyoJ7PxnzPQHPxS7D1lKoEUGILXJSVA10ABnzhzDwk2NXeUiIiIyMFsbo0wZGdwEv9kTqqgsLlQZxyFMCbTIAGyq99ZonjoquED8gePH05Ho5PGlOwJ7DxGRfNEeT1LX0sHkkaWBvUdhqIBTp4/itQ0Ngb2H5JYSaJEB2LCnjRFlYUaVFwX2HidPHcmMmnIeWFQb2HuIiOSLrY1RACaPCi6BBq+MY+OeNnY2qQ76UKQEWmQANtS1MmNMeaDvYWZ87JQpvLGpkfV12lRFRGQgtjZGAAIt4QA4PTUJfMOeQN9HckMJtMgAbKhrY/qYisDf5+9OnkRpOMR3HlpGIqk1oUVE+qs2NQIdcAI9e0IV1aVhraJ0iFICLdJPLe1xdrd0MKMm2BFogLFVJfzXpcfy+sYGfv7susDfT0TkULW1MUJRqICxlcWBvk9BgXHq9FG8vK5eAx+HICXQIv20aY93GXDmICTQAB89aTIXHDWWBxbW4pwblPcUETnUbG2MMmlkaSC7EB7ooydNYtveKDc9vTbw95LBpQRapJ827PHqkWfUBF/CkXL+kTVsb2qntiE6aO8pInIo2doQCXQFjnTvP24CH58zhV/MX8cbm7Qix6FECbRIP62va8MMDhsdbB1duvRJKe3xJHtaO9jT2kFLe3zQ+iAiMpxtbYwOWgIN8L0PH0N1aZi7Xt0MsF85R7JTVxOHq+B2fxA5xK3a0cyUkWUUFwa3HeyBDh9bwZiKYv705jZ+9ORq6ttiAJjBQ9eeyUlTRw5aX0REhpuGthj1bbHAJxCmKy0KccmJE/njG7XcOG8Nv3t5I3ddeRq3vbCB7U1RHrr2TMyCLyeR7NIItEg/7I3EeH51HRccNXZQ39fMOH3GKF7f2ECi0/G9D83mB5ccQ1k4xD0LtgxqX0REhpuHF28D4PwjBzd2//2cKcQSnfz0mbU0tyf4xG2v8diyHby1ZS9vbmkc1L5IdiiBFumHvy7eTizZycfmTB709z73iBoAfvL3J3DFu6fzuTOm8cHjJ/LYsh20abtvEZEePbBoK8dMrGL2xKpBfd9jJlZxwuRqpo8p5/dfOJVEZycXzR5HWVGIBxZuHdS+SHaohEOkHx5YVMvsCVUcM7F60N/70pMnc8bM0ftdgvzYnMnct7CW7z28gplj901qfPfMMRw3uZqnVuxk4x5v2/HKkkL+7uTJlIT7XnpS19LBW1saueiY8dn7ICIig2jl9mZWbG/m+x8+ZtDf28y464unURQqoCQc4uVvXcDoimK+9aelPLp0B9PHlJOq4igJh/joSZNwwJ/f3Eai0/GhEyYwtrIEgDe3NDKmvJipgzj/Rt5JCbRIhlbtaGb5tmb+7UOzc/L+oQJ7R/3eKYeN5LhJ1TywaP+RjLKiENecO5OfzFuz3/NLa5v44WXH9+n92uNJPn/H6yzf1syPLjuev58zZWAfQEQkBx5YVEtRqIBLTpyYk/evKgl33R9b5SXDnzn9MP7y1jb++4m392s7/+3dJDodL671djF8cNFW/nzdmSQ6HZ/+9QKqS8M89pWzGF0R7FrW0rNAE2gzmwv8FAgBv3HO/c8Bx4uB3wOnAPXAx51zm4LqT2en49YX1nPa9NGccti+yVardjTz5PKdXH/B4YRDXlWLc45bn9/AqdNHcspho7rart7ZwmPLdnD9+YdTVFjA9r1Rfv7sWqKxJJ8+/TCOGFfJjfPW0OBP7po4opRvvPcIXlhTx8NLtvufGz516lRO81dUAFi3u4W/Lt7O9Rcc3jUpzTnH7S9t5JiJ1ZwxM71tK39dvI0vn3941yiic47fvryJo8dXcubhY7rabqhr5aE3t3H9Bfu3/d3LmzhiXCUnHzaCm+ev4+/nTME5L8Bcf/4sSov2jU7e+comZtSUc/Ysr3Tg4SXbeXrlLmoqi/mHi46grKiQ+tYObnx6Dc3RBJeePInz0urLttRHuG/hFq4773DKi/d9y9312mYmjyzdrxattiHCva9v4brzD6cire3dCzYzobqEC44aB8CTy3fw+LKdjCov4h8uOoKV25u55/UtlBWF+OqFRzC+uqTrtU+t2MljS3cwqryIb1x0BFUlYZqicX7yt9U0RuJ84PgJvM8fWZ23chePLNnOyLIw37joSKpL9wW8+xfWUlVSyOsbGwmHjEtOnNTNd1lumBl//fK76Ujsm929p7WDS25+mZ/MW8O7po3kd58/lZAZv5i/lpvnr6e+rYOyooOHgG17oyzf1szMmnK++9flvLh2D+cfWcNHTpzEzfPXsXa3t5xfeXEhX3vPLOpbY/zmpQ0kkt7s8rNnjeGyUyZzy3PrWb2zxW/r/T/tjca47YV9bbtTXhzihgtmMXFEKbFEJzc+vYZtjVHOnDmaT5w6tatdc3ucW59bz6dPP4xJI/bNsH9m1S52NXfwqdP2tW1pj/Pjv+37OZ1QXcLX33sEr66vZ9veKJ85/bCutq0dCW6Zv45PnjqVKaM04pMLtQ0R7l9YyzXnztwvhtyzYAsTRpTsF0O2Nkb44+u1XHPezP1iyB9f38K4qhLOT5u3sH1vlHsWbOHqc2dQ6ceFG+etYW8kxodOmMiFR4/raruzqZ0/vLaZq86dsV8i9MDCWqpLw11XZ55Z5cWQEWVFfP29R1BdGqalPc6N89bS0NbB3GMnMPdYr+381bv561vbGFFWxNfeM4sRZUVd5/3zW1spKQxx8XETup7b3dLOna9s4otnzWBLQ4SX1+/hmnNm8sjS7YRDBbw/re2e1g5+9/JGrjxrBqPK95334SXbMbzl1X753DrOO3Isx07adyVt+bYmnlu9m2vPO5yQv05yZ6fjF/PXsaHO+1mvKCnkqxcewe6Wdn770iaSnV7cOe/IsXz4hInc+sJ6zpw5hsNGlfGblzZw+ZnTukZPwYvfkViSS0/eVwLXFIlz24vrufyMaV3JJXjxu6U9wWWnpLWNxvn1Cxv47BmHMS6t7byVu2iMxPb7I7+5Pc5tz2/g7+dM4S9vbeO9s8ft93XOtROnjGD599+332ocDyys5XuPrATg3z9yLOOrSvjS7xfy/UdWcOKUEUTjSWLJTj79mwUcNb6yz+9VXBjimvNmUlhg3Dx/He3xJADHTR7BF949jXte38KkEaWcPmM0tzy3no+cOJGiwgJunr+OaMxre+ykaq48azp/fKOWBRt632Fx9sQqvnT2DO5fWNu1G2M4VMDV586gvLiQnz2zjmjMKzs8akIVV58zgwcWbeWVdXu62n7pHO/n7Q+vbeZL58xg3e5WFm5q4KpzZvCnN7dRWVLIBUeN5Zb565l77HhGloW589VNXHX2TDbsaWXBxgauPmdGIJM0A0ugzSwE3Ay8F9gKvGFmDzvnVqY1uxJodM4dbmafAH4IfDyoPv36xQ386MnVjCwL89hXzmbiiFIa22JceccbbG9qpz2R5DsXHw3A7S9t5IdPvt31V97kkWU0ReJ84Y432LY3SqQjwT/NPYpr/7CIt3e2UBIO8ezbuzlhygheWV/P1FFlOOd4eMl2NtS18tzqOqpKC6ksCdMYifHs27t57IazmTq6jOb2OFfeuZDN9RGao3G+f8mxANy9YAv/8dgqKooLefSGs5g2ppyW9jhf+v1CNu5pozES4z8+chwAf3yjln9/dCXlRSEeueEsZtRU0NaR4Iu/X8iGujbq2zr470u9EccHFm7lB4+upKwoxGnTRzF/dR1Pr9xN0jnW7W6lrqWDH112AuD91ftvD6+gNBzi4evfTV1rB1/741uMrihmT2sHjZEY/3vZCXztvsW8tqGeypIwf1u5k79++SyOHF9JezzJVXct5O2dLWxrjHLjx0/0kr3F2/h/f1lOcWEBf77u3cyeWEV7PMnVdy1i5Y5mahuj/OwTXttHl27nX/68nKLCAh669kw6Ep1cf89bjCgrojESY8OeNt7a0kiowIjEkry9s4X7rjqDosIC3trSyPX3vEl1aZjGSJydTe3c8umT+acHl/D0qt2MLAvzxPIdPHDNmRhw3d2Lutpub2rnts+egpnx9Mpd/NODSyksMErDId5z9Lj9fikNBQUFtt8fPlNGlXHzp07mlufW8b+XndCVTHzjvUeys6kjo4kr37n4KC49eTI33Psmr2+s55El23lm1W4eW7aDKaNKKSwoYNveKKt3NrOzqZ2W9gRjKouJxBI8vGQ7z62u47FlO5g8spRwyGu7akcLdS0dNEXj1PSyI9j2vVFW7mjhgavP4IdPvs3tL21kXFUxD/tJytxjx+Oc41sPLuWJ5Tt5ad0eHrjmDIoLQyzf1sS1f3iTWLKTkWVhLj5uAs45vv2nZTyxfAeHjS73f04jrK9r44W1dcQSnVSXhvnQCRNxzvHPDy3r+gwPXXdmRqUvMnBeDFnEqh3N1DZEumLII0u2889/XtZtDFmxvZnNDZGuGPL4sh18+6FlXTHk2EnVdCSSXPOHRSzd2sSGPa384pMn880HlvDM27sZURrm8WU7efDaMzh+8ghiiU6u+cMiFtfuZe3uFm79jBcX5q3cxTcfXEo4ZDxwzZmEzLj2D29SUVJIUzTOtr1RfvWZU7q+30aWFfHo0h3cd/UZFBcWcPVdi6goLqQ5Gqe2IcKvPzeHggLjudW7+fp9SwgVGPdVFjNn2igSSS/uvb6xgTc3e/3Y0xpjxfZmHl+2gwIzRpcXcdqM0SQ7HTfc8xavbqhn6dYm7vj8qYQKjFfW7eFrf3wLgCeX7+SxZTv4/aubeewrZ1NTWUxdSwdfuOMNdrd0kOh0fO09RwBwy3Pr+Mm8NUweWUqowNixt501O1vZ3NBGW0eS0RVFRGJJ/rpkO/NW7eKxpTsYXb6RI8dX8sr6et7Y1Mg9XzyNwlABCzc18OV73iLZ6RhVXsR5R46ls9PxjfsX88zbu1mwoYF7rzqdcKiAN7c08uW73yTR6RhZFubCo8fhnOMfH1jCvJW7eGX9Hu67+gzCoQIW1+7lursXEU86RpYV8d7ZXttvPrCEp1bs4p7Xt9AYiXNZDuatHMyBMeXyM6dR2xilsMD4zGlTMTOuO28mtzy3nqdW7GJmTTlfuXAWP31mLW/V7u3z++xu7mDh5gaKCkNs3NPKuKoSYolO/rJ4O4tr9/LIku0UFxZwzhE1zFu5i0eXbKe0KMS63a2Mry4h7rddsrWJR5ZsZ1xVcY/xMNV22bbm/druaelgwcYGRpaFeXtnC+OrS0gkHX9ZvJ3l25p4dOkOxlYWU1rktX11Qz1jKopZXLuXJVv3smxbE3sjcZb5bcMh48KjxvHkip3cv7CWsVXFvLVlL0tqm1ixvYnGSJwCg6vOmTmQ/6JuWVA7mpnZGcD3nHPv8x9/B8A5999pbZ7y27xqZoXATqDG9dKpOXPmuIULF2bUl+88tIyFmxrYsKeNM2aM5q0tjZSEQ4wqL6IpGmdvJM5Zs8bw7Nu7OXxsBQZs2NPG6TNGsaS2ieLCgq62jZEY58yq4Zm3dzNpRCnb9ka59TMnM3tCNR/4+Yu0tCf41w8czRfPngHA9x9Zwe9e3sTE6hIe+8rZjCwvorYhwgd+9iKFoQJGlxfR3B5nT2uM848cy9OrvB+OAjM21bdx8tSRrN7VggFjKoppaU9Q19rxjrab6yOcOHUEa3e14IAav+3ulnYuPHoc81bu3/aEKdWsr2ujoS3GRbPHMW/VLgDee/Q4/rZyFzNqygmZsbkhwvGTqtlU30Y86UgkOxlfXcLD15/FbS9s4KfPrO36Ovz3pcdx4dFjef9PXyKe7GRsZTFtHQm2N7Vz0Wz/vGPKCRUYWxoiHD2hiu17o7THk4yrKiESS7Jtb5T3HTOOp1bs3/aoCVXsamonEkvgHIwoD/PoDWdz7+tb+J8n3qaqpJDHvnI2S7bu5fp73mLSiFLKikLsbGqnuizMYzeczf0La/nPx1d19fdfP3A0l50ymQ/87CWa/XWUq0rCPHrDWTz01jb+/dGVTBtdRjhUwNbGKNPHlNPSEae2IcrvrnjXfiNZ+SQaS/LRW17m7Z0tfOC4CfziUyd1/VH01T8upihUwJ+uPZPjJlfTHk/y0VteYdWOZuYeM55ffubkruTnhnvfIhwyHrzmTE6YMqLH93t82Q6uu/vNrv+3K86cxnfefxQfu/VV1uxqYcrIMhKdjo172rq+d7r+/5vbqSguZGxlMasPaPutuUdx7XleUP2vx1dx2wsbGFdVzPjqUt7e0czUUWUkOx0b0s47sbqkawT0indP49OnHdZjv3tiZoucc3P69cUfhvoTs+9esJk7Xt4E0GsMOWpCFTubokRj3ceQ6WPKKSwwahsjHDmukl3NHURiiW7bpr6//t8HZ3PpSZP4wM9epKUjwfhu2qbHhRk15eyNxLtiSGWxF4v+/NY2fvDoyq7zfmvuUXzqtKl88OcvsrctjhmUFRXy2FfO4tGlO/i3h1dw2Ogyivw/MKeOKiMaT1LfGmNCdQntiSS1DVHmHjOeJ1fspKwoxCmHjeTFtXs4YlwF8aSjrqVjv7ap/k4dVUaxf8V0wohSOp1jQ10b5x5Rw4KN9VQUhxlZ5g0ctLTHOX3GaF5YW8fMGu/34fq6Vj50wkRu8v94eXDRVv7xgSUUFRbw5+vO5JiJ1URiCT5y88us2dXK2bPGsGhzI5FYsqu/k0eWUhoOsaOpndEVRZSGQ2xpiDBpRCmxZCeb6yNd/U213dnUzojyMBXFYTbtaWPyyFLiyU42pbVN/1mvKgkzoizMhrru246vKuHlb1/QNbI+nCT8EecFGxv4zsVHcfW5mSeEr6zbw2duX0Cng999/l2cf+RYkp2Oy3/7Oi+t28OJU0awfW+U3S0dvOfocTz79i46Hdx++RwuPHocnZ2OK+54gxfW1HHS1BFdA1Xd6ex0XHnnG8xfXccJU0bwwNVe20WbG/j4r14j0em49TMnM/fYCXR2Oq66ayFPr9rNcZOqefBabwDkzS2NfPxXrxJPuq7/w8riQo6fUs3L6+qZPaGq63fyBUeN5cW1dfu1LS8KcdLUkby6oZ4ZY8qZM20U/33pcRl/3XqK2UEm0JcBc51zX/QffxY4zTl3fVqb5X6brf7j9X6bPQec6yrgKoCpU6eesnnz5oz68otn17JyRzOjy4v55twjWba1iXsWbMHhffaPnDiJc46o4X+fWs2OJm+Ht1HlRXzzfUexYnsTdy/Y0rV18odPmMj5R43lf59czfamKO8+fEzXL9HXNtR7f12ff3jX5YJYopObnl7DB4+fuN+s39c3NnDnq5u6zvv+47wygv97ajW1jd4W0dWlRXzzfUeyoa6VO17ZRKffdu6xE7j42PH8399WU9uQahvmHy86kk31bfzu5X1t33fMeD5w3AR+PG8Nm+u9SWRVJWH+8X1HsqUhwvOr6/jKhbP4y1vbKCiAD58wiR//bTWb0tp+46Ij2L63ndtf2khhgXH9BYczs6aCZKfjxnlr2LCnlRMmj+Aq/zLJktq9/Oaljftd1rvs5Mnc9PQa1qUuARYX8g8XHcnu5g5ue3FDV9tzj6jhY6dM4aZn1rJut3+5v6iQb1x0BPWt3uX+AoPrzj+cI8ZV0tnpuHn+Ok6bMZpTp3ulNne9uolXN+y7XHTteTM5anwVzjl+/uw63t7ZzFHjq7jhAu//adWOZm59fj3OwTXnzmT2RK/tzfPXsXJHMwClYa88IRpP8rhfwlMYyt9FbDbXt3HPgi1cf8HhVKZdzr7zlU1MqC7Zb7JhbUOEP7y2mS9fcPh+l77venUTNZUlXZeze/OH1zbzyvo9TKwu5Ztzj6S4MMS2vVFunLeGiH8J8MhxVXzlwsO59/VaXlpXB0BhgXe5cFR5ET/52xra/Lazxlby1QtndW3lG0928tOn1zL32PGMqSjmJ/NW0+qvaHJ4TQVfe88RPLhoK8+t2d3Vpw8dP3G/y+t9lQ8J9EBj9hPLdvDI0u1dj3uKId9475Hsae3wyoAOiCE/fWYtaw+IIQ1tMX71/L62Zx1ewyfeNYWfP7uO1buamT2hqit+r9zezK9eWE/c3/TizJlj+PRpU/nFs+tYtXNfXPj6e2fR2pHgl895MeTqc2dwzMRqnHPc8tx6Vmxv4ohxlXzlAu/7bc2uFm6Zv45OB1edM4NjJ3ltf/n8epZvawK8EcmvXXgEHYkkN89fR8zvw7umjeKKM6dx2wsbmD2xipOmjuTnz6zlk6dOJeHHwo6Ed6n95KkjufKs6fz6xQ0s9kcpSwpD3HDhLJxz3Lewlq9eOIsFGxt4cOHWrt+HH5szhdOmj+JHT65md0s74A3I/NPco/Yrn/ntSxuZNqasq6wO9sWFGy6cxVtbGlm5vZmrzpnBHa9s6tqFryhUwJfP90oVb3pmTVcZwTETq7nuvJn8/tXNLNi4L35fd97hlBWFuOnptUTj3s9k6v/pD69tfkesrygu5Kan13bFhVSsf2DhVsZUFu3X3+Fmd0s7d7y8iWvOm7lfLM3EXxdvoyPRuV+ZS31rB796YQNXnjWdupYOnly+k6++ZxZPLN9JNJbg4+/aV/7W0Bbj1ufX8/l3T2NCde+b0TS2xfjl8+u54sxpTEwrq3t82Q4aI7H9BiCaInFufm4dnzvjsP3m+Dy5fAd1rTE+c9pUbnluPSdNHcExE6u5ef46Pnv6YURiSf6yeBtfvXAWz6+pY1dzO589/TBufX4Dx0+u5rjJ1fzvk6upb+tg9oQqrr9gVsZfs2GdQKfrz2iGiMhQkQ8JdDrFbBEZznqK2UEOoW0D0qfrT/af67aNX8JRjTeZUERERERkSAoygX4DmGVm082sCPgE8PABbR4GLvfvXwY821v9s4iIiIhIrgW2CodzLmFm1wNP4S1j91vn3Aoz+wGw0Dn3MHA7cJeZrQMa8JJsEREREZEhK9B1oJ1zjwOPH/Dcd9PutwMfC7IPIiIiIiLZlL/LCIiIiIiI9IMSaBERERGRDCiBFhERERHJgBJoEREREZEMBLaRSlDMrA7IbFur3BgD9LghzDCnzzY86bMNDYc552py3YnBMoxiNgyv76NM6bMNT/psuddtzB52CfRwYWYLD9XdxvTZhid9NpHeHcrfR/psw5M+29ClEg4RERERkQwogRYRERERyYAS6ODclusOBEifbXjSZxPp3aH8faTPNjzpsw1RqoEWEREREcmARqBFRERERDKgBFpEREREJANKoANkZv9rZm+b2VIz+7OZjch1n7LFzD5mZivMrNPMhu0yNOnMbK6ZrTazdWb27Vz3J1vM7LdmttvMlue6L9lmZlPMbL6ZrfS/H7+a6z7J8Ka4Pbwobg8/h0rcVgIdrHnAsc6544E1wHdy3J9sWg5cCryQ645kg5mFgJuBi4HZwCfNbHZue5U1dwBzc92JgCSAf3DOzQZOB758CP2/SW4obg8TitvD1iERt5VAB8g59zfnXMJ/+BowOZf9ySbn3Crn3Opc9yOLTgXWOec2OOdiwB+BS3Lcp6xwzr0ANOS6H0Fwzu1wzr3p328BVgGTctsrGc4Ut4cVxe1h6FCJ20qgB88XgCdy3Qnp0SSgNu3xVobhD3Q+M7NpwEnAghx3RQ4dittDm+L2MDec43Zhrjsw3JnZ08D4bg79i3Pur36bf8G7ZHH3YPZtoPry2USGAjOrAP4EfM0515zr/sjQprgtknvDPW4rgR4g59x7ejtuZlcAHwQudMNs0e2DfbZDzDZgStrjyf5zMsSZWRgvCN/tnHso1/2RoU9x+5ChuD1MHQpxWyUcATKzucA/AR92zkVy3R/p1RvALDObbmZFwCeAh3PcJzkIMzPgdmCVc+4nue6PDH+K28OK4vYwdKjEbSXQwfoFUAnMM7PFZnZrrjuULWb2UTPbCpwBPGZmT+W6TwPhTxq6HngKb0LD/c65FbntVXaY2b3Aq8CRZrbVzK7MdZ+y6N3AZ4EL/J+xxWb2/lx3SoY1xe1hQnF72Dok4ra28hYRERERyYBGoEVEREREMqAEWkREREQkA0qgRUREREQyoARaRERERCQDSqBFRAAz+62Z7Taz5Vk634/MbIWZrTKzn/lLN4mISBbkOmYrgZZhzcxGpy2Ds9PMtvn3W83sloDe82tm9rlejn/QzH4QxHtLoO4A5mbjRGZ2Jt5STccDxwLvAs7NxrlFhjPFbMmiO8hhzFYCLcOac67eOXeic+5E4FbgRv9xhXPuumy/n5kVAl8A7uml2WPAh8ysLNvvL8Fxzr0ANKQ/Z2YzzexJM1tkZi+a2VF9PR1QAhQBxUAY2JXVDosMQ4rZki25jtlKoOWQZGbnmdmj/v3vmdmd/g/TZjO71L9Us8z/QQv77U4xs+f9H7ynzGxCN6e+AHjTX8AfM/uKma00s6Vm9kcAf+vf5/C2Apbh7TbgBufcKcA/An0aIXPOvQrMB3b4t6ecc6sC66XIMKeYLVkyaDG7cIAdFRkuZgLnA7Pxdnf6O+fcP5nZn4EPmNljwM+BS5xzdWb2ceA/8UYu0r0bWJT2+NvAdOdch5mNSHt+IXA2cH8gn0YCZ2YVwJnAA2mlcMX+sUuB7i75bnPOvc/MDgeOBib7z88zs7Odcy8G3G2RQ4VitmRksGO2EmjJF0845+JmtgwIAU/6zy8DpgFH4tU9zfN/8EJ4f4UeaALelrEpS4G7zewvwF/Snt8NTMxe9yUHCoC9/qXm/TjnHgIe6uW1HwVec861ApjZE3jbJyuBFukbxWzJ1KDGbJVwSL7oAHDOdQJxt28P+068PyQNWJGqzXPOHeecu6ib80Tx6qRSPgDcDJwMvOHX2+G3iQbwOWSQOOeagY1m9jEA85zQx5dvAc41s0L/cvO57P9LXER6p5gtGRnsmK0EWsSzGqgxszMAzCxsZsd0024VcLjfpgCY4pybD3wLqAYq/HZHAFlZWkcGh5ndi3ep+Egz22pmVwKfBq40syXACuCSPp7uQWA93mjZEmCJc+6RALotkq8Us/NcrmO2SjhEAOdczMwuA35mZtV4Pxs34f0ApnsCuMu/HwL+4Lc34GfOub3+sfOB7wTdb8ke59wneziU8TJJzrkkcPXAeiQiPVHMllzHbNt3VURE+sKfxPJPzrm1PRwfB9zjnLtwcHsmIiIHUsyWICiBFsmQmR0JjPPXoOzu+LvwavYWD2rHRETkHRSzJQhKoEVEREREMqBJhCIiIiIiGVACLSIiIiKSASXQIiIiIiIZUAItIiIiIpIBJdAiIiIiIhn4/xN6NBKVzRBiAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# read channel\n", + "x_ch, y_ch = channel.read_waveform()\n", + "\n", + "# allow for 250 ms to not have it read to fast\n", + "sleep(0.25)\n", + "\n", + "# read math\n", + "x_math, y_math = function.read_waveform()\n", + "\n", + "# plot\n", + "fig, ax = plt.subplots(1, 2, sharey=True, figsize=(12, 4))\n", + "\n", + "ax[0].plot(x_ch, y_ch)\n", + "ax[0].set_xlabel(\"Time (s)\")\n", + "ax[0].set_ylabel(\"Signal (V)\")\n", + "ax[0].set_title(\"Channel readout\")\n", + "ax[1].plot(x_math, y_math)\n", + "ax[1].set_xlabel(\"Time (s)\")\n", + "ax[1].set_title(\"Average of the channel (math)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With a poorer signal generator, the average should look a lot smoother than the channel. To finish up the math section, let's turn off the math trace." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "function.trace = False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Measurements and statistics\n", + "\n", + "In addition to mathematical operations on channels, oscilloscopes can take measurements and display the statistics. Many measurement parameters are already implemented, check out the documentation and look for the `inst.MeasurementParameters` class. Currently, only measurement parameters that act on a single source are available.\n", + "\n", + "As an example, let us set up 2 measurements. Measurement 1 determines the rise time (10% to 90%), measurement 2 the fall time (80% to 20%) of the first channel readout. Setting up the measurement can be done as following:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "# assign the two measurements\n", + "msr1 = inst.measurement[0]\n", + "msr2 = inst.measurement[1]\n", + "\n", + "# turn on the measurements (only one is really necessary, the other one is turned on automatically!)\n", + "msr1.measurement_state = msr1.State.both\n", + "\n", + "# assign the measurement types and which source the measurement should be on\n", + "msr1.set_parameter(inst.MeasurementParameters.rise_time_10_90, 0)\n", + "msr2.set_parameter(inst.MeasurementParameters.fall_time_80_20, 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The oscilloscope will now automatically set up these measurements and will start accumulating statistics. The statistics can be returned at any point, which will return a tuple containing 5 floats. These floats are:\n", + "\n", + " 1. Average\n", + " 2. Lowest value measured\n", + " 3. Highest value measured\n", + " 4. Standard deviation\n", + " 5. Number of sweeps\n", + " \n", + "These returns are not unitful, so you will need to know what was set up. For the rise and fall times, the returns will of course be in the form of time. SI units are always returned, in this case, seconds." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1.52152e-09, 1.49e-09, 1.56e-09, 2.553e-11, 4.0)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "msr1.statistics" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(9.6247e-10, 9.02e-10, 1.01e-09, 2.415e-11, 24.0)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "msr2.statistics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To start a new series of measurements, i.e., reset the number of sweeps, the following command can be executed:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1.56e-09, 1.56e-09, 1.56e-09, 0.0, 1.0)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inst.clear_sweeps()\n", + "\n", + "# getting statistics for `msr1` again:\n", + "msr1.statistics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To delete the measurement parameters again and turn off the table, the following commands are used:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# delete parameters\n", + "msr1.delete()\n", + "msr2.delete()\n", + "\n", + "# turn off measurement\n", + "msr1.measurement_state = msr1.State.off" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Further information\n", + "\n", + "Please check out the documention on [readthedocs.io](https://instrumentkit.readthedocs.io/en/latest/) and feel free to open an issue on the github repository if you have any problems / feature requests." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.8.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/doc/source/apiref/index.rst b/doc/source/apiref/index.rst index 1d73f35e7..e60a2eb6d 100644 --- a/doc/source/apiref/index.rst +++ b/doc/source/apiref/index.rst @@ -30,7 +30,8 @@ Contents: qubitekk rigol srs - tektronix + tektronix + teledyne thorlabs toptica yokogawa diff --git a/doc/source/apiref/teledyne.rst b/doc/source/apiref/teledyne.rst new file mode 100644 index 000000000..fe0bfc6a1 --- /dev/null +++ b/doc/source/apiref/teledyne.rst @@ -0,0 +1,15 @@ +.. + TODO: put documentation license header here. + +.. currentmodule:: instruments.teledyne + +=============== +Teledyne-LeCroy +=============== + +:class:`MAUI` Oscilloscope Controller +======================================= + +.. autoclass:: MAUI + :members: + :undoc-members: diff --git a/instruments/__init__.py b/instruments/__init__.py index 7d9f7e2be..0f0640748 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -27,6 +27,7 @@ from . import rigol from . import srs from . import tektronix +from . import teledyne from . import thorlabs from . import toptica from . import yokogawa diff --git a/instruments/teledyne/__init__.py b/instruments/teledyne/__init__.py new file mode 100644 index 000000000..40d157ce9 --- /dev/null +++ b/instruments/teledyne/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing Teledyne instruments +""" + + +from .maui import MAUI diff --git a/instruments/teledyne/maui.py b/instruments/teledyne/maui.py new file mode 100644 index 000000000..4a8517189 --- /dev/null +++ b/instruments/teledyne/maui.py @@ -0,0 +1,1406 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Provides support for the Teledyne-Lecroy Oscilloscopes that use the +MAUI interface. + +Development follows the IEEE 488.2 Command Reference from the MAUI +Oscilloscopes Remote Control and Automation Manual, document number +maui-remote-control-automation_10mar20.pdf + +Where possible, commands are sent using the enum_property, ... that +are usually used for SCPI classes, even though, this is not an SCPI +class. +""" + +# IMPORTS ##################################################################### + +from enum import Enum +import numpy as np + +from instruments.abstract_instruments import ( + Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource +) +import instruments.units as u +from instruments.util_fns import ( + assume_units, enum_property, bool_property, ProxyList +) + +# CLASSES ##################################################################### + + +# pylint: disable=too-many-lines,arguments-differ + + +class MAUI(Oscilloscope): + + """ + Medium to high-end Teledyne-Lecroy Oscilloscopes are shipped with + the MAUI user interface. This class can be used to communicate with + these instruments. + + By default, the IEEE 488.2 commands are used. However, commands + based on MAUI's `app` definition can be submitted too using the + appropriate send / query commands. + + Your scope must be set up to communicate via LXI (VXI11) to be used + with pyvisa. Make sure that either the pyvisa-py or the NI-VISA + backend is installed. Please see the pyvisa documentation for more + information. + + This class inherits from: `Oscilloscope` + + Example usage (more examples below): + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> # start the trigger in automatic mode + >>> inst.run() + >>> print(inst.trigger_state) # print the trigger state + + >>> # set timebase to 20 ns per division + >>> inst.time_div = u.Quantity(20, u.ns) + >>> # call the first oscilloscope channel + >>> channel = inst.channel[0] + >>> channel.trace = True # turn the trace on + >>> channel.coupling = channel.Coupling.dc50 # coupling to 50 Ohm + >>> channel.scale = u.Quantity(1, u.V) # vertical scale to 1V/division + >>> # transfer a waveform into xdat and ydat: + >>> xdat, ydat = channel.read_waveform() + """ + + # CONSTANTS # + + # number of horizontal and vertical divisions on the scope + # HOR_DIVS = 10 + # VERT_DIVS = 8 + + def __init__(self, filelike): + super(MAUI, self).__init__(filelike) + + # turn off command headers -> for SCPI like behavior + self.sendcmd("COMM_HEADER OFF") + + # constants + self._number_channels = 4 + self._number_functions = 2 + self._number_measurements = 6 + + # ENUMS # + + class MeasurementParameters(Enum): + """ + Enum containing valid measurement parameters that only require + one or more sources. Only single source parameters are currently + implemented. + """ + amplitude = "AMPL" + area = "AREA" + base = "BASE" + delay = "DLY" + duty_cycle = "DUTY" + fall_time_80_20 = "FALL82" + fall_time_90_10 = "FALL" + frequency = "FREQ" + maximum = "MAX" + minimum = "MIN" + mean = "MEAN" + none = "NULL" + overshoot_pos = "OVSP" + overshoot_neg = "OVSN" + peak_to_peak = "PKPK" + period = "PER" + phase = "PHASE" + rise_time_20_80 = "RISE28" + rise_time_10_90 = "RISE" + rms = "RMS" + stdev = "SDEV" + top = "TOP" + width_50_pos = "WID" + width_50_neg = "WIDN" + + class TriggerState(Enum): + + """ + Enum containing valid trigger state for the oscilloscope. + """ + auto = "AUTO" + normal = "NORM" + single = "SINGLE" + stop = "STOP" + + class TriggerType(Enum): + """Enum containing valid trigger state. + + Availability depends on oscilloscope options. Please consult + your manual. Only simple types are currently included. + + .. warning:: Some of the trigger types are untested and might + need further parameters in order to be appropriately set. + """ + dropout = "DROPOUT" + edge = "EDGE" + glitch = "GLIT" + interval = "INTV" + pattern = "PA" + runt = "RUNT" + slew_rate = "SLEW" + width = "WIDTH" + qualified = "TEQ" + tv = "TV" + + class TriggerSource(Enum): + """Enum containing valid trigger sources. + + This is an enum for the default values. + + .. note:: This class is initialized like this for four channels, + which is the default setting. If you change the number of + channels, `TriggerSource` will be recreated using the + routine `_create_trigger_source_enum`. This will make + further channels available to you or remove channels that + are not present in your setup. + """ + c0 = "C1" + c1 = "C2" + c2 = "C3" + c3 = "C4" + ext = "EX" + ext5 = "EX5" + ext10 = "EX10" + etm10 = "ETM10" + line = "LINE" + + def _create_trigger_source_enum(self): + """Create an Enum for the trigger source class. + + Needs to be dynamically generated, in case channel number + changes! + + .. note:: Not all trigger sources are available on all scopes. + Please consult the manual for your oscilloscope. + """ + names = ['ext', 'ext5', 'ext10', 'etm10', 'line'] + values = ['EX', 'EX5', 'EX10', 'ETM10', 'LINE'] + # now add the channels + for it in range(self._number_channels): + names.append("c{}".format(it)) + values.append("C{}".format(it+1)) # to send to scope + # create and store the enum + self.TriggerSource = Enum('TriggerSource', zip(names, values)) + + # CLASSES # + + class DataSource(OscilloscopeDataSource): + + """ + Class representing a data source (channel, math, ref) on a MAUI + oscilloscope. + + .. warning:: This class should NOT be manually created by the + user. It is designed to be initialized by the `MAUI` class. + """ + + # PROPERTIES # + + @property + def name(self): + return self._name + + # METHODS # + + def read_waveform(self, bin_format=False, single=True): + """ + Reads the waveform and returns an array of floats with the + data. + + :param bin_format: Not implemented, always False + :type bin_format: bool + :param single: Run a single trigger? Default True. In case + a waveform from a channel is required, this option + is recommended to be set to True. This means that the + acquisition system is first stopped, a single trigger + is issued, then the waveform is transfered, and the + system is set back into the state it was in before. + If sampling math with multiple samples, set this to + false, otherwise the sweeps are cleared by the + oscilloscope prior when a single trigger command is + issued. + :type single: bool + + :return: Data (time, signal) where time is in seconds and + signal in V + :rtype: ndarray + + :raises NotImplementedError: Bin format was chosen, but + it is not implemented. + + Example usage: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> channel = inst.channel[0] # set up channel + >>> xdat, ydat = channel.read_waveform() # read waveform + """ + if bin_format: + raise NotImplementedError("Bin format reading is currently " + "not implemented for the MAUI " + "routine.") + + if single: + # get current trigger state (to reset after read) + trig_state = self._parent.trigger_state + # trigger state to single + self._parent.trigger_state = self._parent.TriggerState.single + + # now read the data + retval = self.query("INSPECT? 'SIMPLE'") # pylint: disable=E1101 + + # read the parameters to create time-base array + horiz_off = self.query("INSPECT? 'HORIZ_OFFSET'") # pylint: disable=E1101 + horiz_int = self.query("INSPECT? 'HORIZ_INTERVAL'") # pylint: disable=E1101 + + if single: + # reset trigger + self._parent.trigger_state = trig_state + + # format the string to appropriate data + dat_val = np.array(retval.replace('"', '').split(), + dtype=np.float) + + # format horizontal data into floats + horiz_off = float(horiz_off.replace('"', '').split(':')[1]) + horiz_int = float(horiz_int.replace('"', '').split(':')[1]) + + # create time base + dat_time = np.arange( + horiz_off, + horiz_off + horiz_int * (len(dat_val)), + horiz_int + ) + + # fix length bug, sometimes dat_time is longer than dat_signal + if len(dat_time) > len(dat_val): + dat_time = dat_time[0:len(dat_val)] + else: # in case the opposite is the case + dat_val = dat_val[0:len(dat_time)] + + return np.stack((dat_time, dat_val)) + + trace = bool_property( + command="TRA", + doc=""" + Gets/Sets if a given trace is turned on or off. + + Example usage: + + >>> import instruments as ik + >>> address = "TCPIP0::192.168.0.10::INSTR" + >>> inst = inst = ik.teledyne.MAUI.open_visa(address) + >>> channel = inst.channel[0] + >>> channel.trace = False + """ + ) + + class Channel(DataSource, OscilloscopeChannel): + + """ + Class representing a channel on a MAUI oscilloscope. + + .. warning:: This class should NOT be manually created by the + user. It is designed to be initialized by the `MAUI` class. + """ + + def __init__(self, parent, idx): + self._parent = parent + self._idx = idx + 1 # 1-based + + # Initialize as a data source with name C{}. + super(MAUI.Channel, self).__init__( + self._parent, + "C{}".format(self._idx) + ) + + # ENUMS # + + class Coupling(Enum): + """ + Enum containing valid coupling modes for the oscilloscope + channel. 1 MOhm and 50 Ohm are included. + """ + ac1M = "A1M" + dc1M = "D1M" + dc50 = "D50" + ground = "GND" + + coupling = enum_property( + "CPL", + Coupling, + doc=""" + Gets/sets the coupling for the specified channel. + + Example usage: + + >>> import instruments as ik + >>> address = "TCPIP0::192.168.0.10::INSTR" + >>> inst = inst = ik.teledyne.MAUI.open_visa(address) + >>> channel = inst.channel[0] + >>> channel.coupling = channel.Coupling.dc50 + """ + ) + + # PROPERTIES # + + @property + def offset(self): + """ + Sets/gets the vertical offset of the specified input + channel. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> channel = inst.channel[0] # set up channel + >>> channel.offset = u.Quantity(-1, u.V) + """ + return u.Quantity( + float(self.query('OFST?')), u.V + ) + + @offset.setter + def offset(self, newval): + newval = assume_units(newval, 'V').rescale(u.V).magnitude + self.sendcmd('OFST {}'.format(newval)) + + @property + def scale(self): + """ + Sets/Gets the vertical scale of the channel. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> channel = inst.channel[0] # set up channel + >>> channel.scale = u.Quantity(20, u.mV) + """ + return u.Quantity( + float(self.query('VDIV?')), u.V + ) + + @scale.setter + def scale(self, newval): + newval = assume_units(newval, 'V').rescale(u.V).magnitude + self.sendcmd('VDIV {}'.format(newval)) + + # METHODS # + + def sendcmd(self, cmd): + """ + Wraps commands sent from property factories in this class + with identifiers for the specified channel. + + :param str cmd: Command to send to the instrument + """ + self._parent.sendcmd("C{}:{}".format(self._idx, cmd)) + + def query(self, cmd, size=-1): + """ + Executes the given query. Wraps commands sent from property + factories in this class with identifiers for the specified + channel. + + :param str cmd: String containing the query to + execute. + :param int size: Number of bytes to be read. Default is read + until termination character is found. + :return: The result of the query as returned by the + connected instrument. + :rtype: `str` + """ + return self._parent.query("C{}:{}".format(self._idx, cmd), + size=size) + + class Math(DataSource): + + """ + Class representing a function on a MAUI oscilloscope. + + .. warning:: This class should NOT be manually created by the + user. It is designed to be initialized by the `MAUI` class. + """ + + def __init__(self, parent, idx): + self._parent = parent + self._idx = idx + 1 # 1-based + + # Initialize as a data source with name C{}. + super(MAUI.Math, self).__init__( + self._parent, + "F{}".format(self._idx) + ) + + # CLASSES # + + class Operators: + """ + Sets the operator for a given channel. + Most operators need a source `src`. If the source is given + as an integer, it is assume that the a signal channel is + requested. If you want to select another math channel for + example, you will need to specify the source as a tuple: + Example: `src=('f', 0)` would represent the first function + channel (called F1 in the MAUI manual). A channel could be + selected by calling `src=('c', 1)`, which would request the + second channel (oscilloscope channel 2). Please consult the + oscilloscope manual / the math setup itself for further + possibilities. + + .. note:: Your oscilloscope might not have all functions + that are described here. Also: Not all possibilities are + currently implemented. However, extension of this + functionality should be simple when following the given + structure + """ + + def __init__(self, parent): + self._parent = parent + + # PROPERTIES # + + @property + def current_setting(self): + """ + Gets the current setting and returns it as the full + command, as sent to the scope when setting an operator. + """ + return self._parent.query('DEF?') + + # METHODS - OPERATORS # + + def absolute(self, src): + """ + Absolute of wave form. + + :param int,tuple src: Source, see info above + """ + src_str = _source(src) + send_str = "'ABS({})'".format(src_str) + self._send_operator(send_str) + + def average(self, src, average_type='summed', sweeps=1000): + """ + Average of wave form. + + :param int,tuple src: Source, see info above + :param str average_type: `summed` or `continuous` + :param int sweeps: In summed mode, how many sweeps to + collect. In `continuous` mode the weight of each + sweep is equal to 1/`1`sweeps` + """ + src_str = _source(src) + + avgtp_str = 'SUMMED' + if average_type == 'continuous': + avgtp_str = 'CONTINUOUS' + + send_str = "'AVG({})',AVERAGETYPE,{},SWEEPS,{}".format( + src_str, + avgtp_str, + sweeps + ) + + self._send_operator(send_str) + + def derivative(self, src, vscale=1e6, voffset=0, + autoscale=True): + """ + Derivative of waveform using subtraction of adjacent + samples. If vscale and voffset are unitless, V/s are + assumed. + + :param int,tuple src: Source, see info above + :param float vscale: vertical units to display (V/s) + :param float voffset: vertical offset (V/s) + :param bool autoscale: auto scaling of vscale, voffset? + """ + src_str = _source(src) + + vscale = assume_units(vscale, u.V/u.s).rescale( + u.V/u.s + ).magnitude + + voffset = assume_units(voffset, u.V/u.s).rescale( + u.V/u.s + ).magnitude + + autoscale_str = 'OFF' + if autoscale: + autoscale_str = 'ON' + + send_str = "'DERI({})',VERSCALE,{},VEROFFSET,{}," \ + "ENABLEAUTOSCALE,{}".format( + src_str, + vscale, + voffset, + autoscale_str) + + self._send_operator(send_str) + + def difference(self, src1, src2, vscale_variable=False): + """ + Difference between two sources, `src1`-`src2`. + + :param int,tuple src1: Source 1, see info above + :param int,tuple src2: Source 2, see info above + :param bool vscale_variable: Horizontal and vertical + scale for addition and subtraction must be + identical. Allow for variable vertical scale in + result? + """ + src1_str = _source(src1) + src2_str = _source(src2) + + opt_str = 'FALSE' + if vscale_variable: + opt_str = 'TRUE' + + send_str = "'{}-{}',VERSCALEVARIABLE,{}".format( + src1_str, + src2_str, + opt_str + ) + + self._send_operator(send_str) + + def envelope(self, src, sweeps=1000, limit_sweeps=True): + """ + Highest and lowest Y values at each X in N sweeps. + + :param int,tuple src: Source, see info above + :param int sweeps: Number of sweeps + :param bool limit_sweeps: Limit the number of sweeps? + """ + src_str = _source(src) + send_str = "'EXTR({})',SWEEPS,{},LIMITNUMSWEEPS,{}".\ + format(src_str, sweeps, limit_sweeps) + self._send_operator(send_str) + + def eres(self, src, bits=0.5): + """ + Smoothing function defined by extra bits of resolution. + + :param int,tuple src: Source, see info above + :param float bits: Number of bits. Possible values are + (0.5, 1.0, 1.5, 2.0, 2.5, 3.0). If not in list, + default to 0.5. + """ + src_str = _source(src) + + bits_possible = (0.5, 1.0, 1.5, 2.0, 2.5, 3.0) + if bits not in bits_possible: + bits = 0.5 + + send_str = "'ERES({})',BITS,{}".format(src_str, bits) + + self._send_operator(send_str) + + def fft(self, src, type='powerspectrum', window='vonhann', + suppress_dc=True): + """ + Fast Fourier Transform of signal. + + :param int,tuple src: Source, see info above + :param str type: Type of power spectrum. Possible + options are: ['real', 'imaginary', 'magnitude', + 'phase', 'powerspectrum', 'powerdensity']. + Default: 'powerspectrum' + :param str window: Window. Possible options are: + ['blackmanharris', 'flattop', 'hamming', + 'rectangular', 'vonhann']. Default: 'vonhann' + :param bool suppress_dc: Supress DC? + """ + src_str = _source(src) + + type_possible = ['real', 'imaginary', 'magnitude', 'phase', + 'powerspectrum', 'powerdensity'] + if type not in type_possible: + type = 'powerspectrum' + + window_possible = ['blackmanharris', 'flattop', 'hamming', + 'rectangular', 'vonhann'] + if window not in window_possible: + window = 'vonhann' + + if suppress_dc: + opt = 'ON' + else: + opt = 'OFF' + + send_str = "'FFT({})',TYPE,{},WINDOW,{},SUPPRESSDC,{}".format( + src_str, + type, + window, + opt + ) + + self._send_operator(send_str) + + def floor(self, src, sweeps=1000, limit_sweeps=True): + """ + Lowest vertical value at each X value in N sweeps. + + :param int,tuple src: Source, see info above + :param int sweeps: Number of sweeps + :param bool limit_sweeps: Limit the number of sweeps? + """ + src_str = _source(src) + send_str = "'FLOOR({})',SWEEPS,{},LIMITNUMSWEEPS,{}".\ + format(src_str, sweeps, limit_sweeps) + self._send_operator(send_str) + + def integral(self, src, multiplier=1, adder=0, vscale=1e-3, + voffset=0): + """ + Integral of waveform. + + :param int,tuple src: Source, see info above + :param float multiplier: 0 to 1e15 + :param float adder: 0 to 1e15 + :param float vscale: vertical units to display (Wb) + :param float voffset: vertical offset (Wb) + """ + src_str = _source(src) + + vscale = assume_units(vscale, u.Wb).rescale( + u.Wb + ).magnitude + + voffset = assume_units(voffset, u.Wb).rescale( + u.Wb + ).magnitude + + send_str = "'INTG({}),MULTIPLIER,{},ADDER,{},VERSCALE,{}," \ + "VEROFFSET,{}".format( + src_str, + multiplier, + adder, + vscale, + voffset) + + self._send_operator(send_str) + + def invert(self, src): + """ + Inversion of waveform (-waveform). + + :param int,tuple src: Source, see info above + """ + src_str = _source(src) + self._send_operator("'-{}'".format(src_str)) + + def product(self, src1, src2): + """ + Product of two sources, `src1`*`src2`. + + :param int,tuple src1: Source 1, see info above + :param int,tuple src2: Source 2, see info above + """ + src1_str = _source(src1) + src2_str = _source(src2) + + send_str = "'{}*{}'".format( + src1_str, + src2_str + ) + + self._send_operator(send_str) + + def ratio(self, src1, src2): + """ + Ratio of two sources, `src1`/`src2`. + + :param int,tuple src1: Source 1, see info above + :param int,tuple src2: Source 2, see info above + """ + src1_str = _source(src1) + src2_str = _source(src2) + + send_str = "'{}/{}'".format( + src1_str, + src2_str + ) + + self._send_operator(send_str) + + def reciprocal(self, src): + """ + Reciprocal of waveform (1/waveform). + + :param int,tuple src: Source, see info above + """ + src_str = _source(src) + self._send_operator("'1/{}'".format(src_str)) + + def rescale(self, src, multiplier=1, adder=0): + """ + Rescales the waveform (w) in the style. + multiplier * w + adder + + :param int,tuple src: Source, see info above + :param float multiplier: multiplier + :param float adder: addition in V or assuming V + """ + src_str = _source(src) + + adder = assume_units(adder, u.V).rescale( + u.V + ).magnitude + + send_str = "'RESC({})',MULTIPLIER,{},ADDER,{}".format( + src_str, + multiplier, + adder + ) + + self._send_operator(send_str) + + def sinx(self, src): + """ + Sin(x)/x interpolation to produce 10x output samples. + + :param int,tuple src: Source, see info above + """ + src_str = _source(src) + self._send_operator("'SINX({})'".format(src_str)) + + def square(self, src): + """ + Square of the input waveform. + + :param int,tuple src: Source, see info above + """ + src_str = _source(src) + self._send_operator("'SQR({})'".format(src_str)) + + def square_root(self, src): + """ + Square root of the input waveform. + + :param int,tuple src: Source, see info above + """ + src_str = _source(src) + self._send_operator("'SQRT({})'".format(src_str)) + + def sum(self, src1, src2): + """ + Product of two sources, `src1`+`src2`. + + :param int,tuple src1: Source 1, see info above + :param int,tuple src2: Source 2, see info above + """ + src1_str = _source(src1) + src2_str = _source(src2) + + send_str = "'{}+{}'".format( + src1_str, + src2_str + ) + + self._send_operator(send_str) + + def trend(self, src, vscale=1, center=0, autoscale=True): + """ + Trend of the values of a paramter + + :param float vscale: vertical units to display (V) + :param float center: center (V) + """ + src_str = _source(src) + + vscale = assume_units(vscale, u.V).rescale( + u.V + ).magnitude + + center = assume_units(center, u.V).rescale( + u.V + ).magnitude + + if autoscale: + auto_str = 'ON' + else: + auto_str = 'OFF' + + send_str = "'TREND({})',VERSCALE,{},CENTER,{}," \ + "AUTOFINDSCALE,{}".format(src_str, vscale, + center, auto_str) + + self._send_operator(send_str) + + def roof(self, src, sweeps=1000, limit_sweeps=True): + """ + Highest vertical value at each X value in N sweeps. + + :param int,tuple src: Source, see info above + :param int sweeps: Number of sweeps + :param bool limit_sweeps: Limit the number of sweeps? + """ + src_str = _source(src) + send_str = "'ROOF({})',SWEEPS,{},LIMITNUMSWEEPS,{}".\ + format(src_str, sweeps, limit_sweeps) + self._send_operator(send_str) + + def _send_operator(self, cmd): + """ + Set the operator in the scope. + """ + self._parent.sendcmd("{},{}".format( + "DEFINE EQN", + cmd)) + + # PROPERTIES # + + @property + def operator(self): + """Get an operator object to set use to do math. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> channel = inst.channel[0] # set up channel + >>> # set up the first math function + >>> function = inst.math[0] + >>> function.trace = True # turn the trace on + >>> # set function to average the first oscilloscope channel + >>> function.operator.average(0) + """ + return self.Operators(self) + + # METHODS # + + def clear_sweeps(self): + """Clear the sweeps in a measurement.""" + self._parent.clear_sweeps() # re-implemented because handy + + def sendcmd(self, cmd): + """ + Wraps commands sent from property factories in this class + with identifiers for the specified channel. + + :param str cmd: Command to send to the instrument + """ + self._parent.sendcmd("F{}:{}".format(self._idx, cmd)) + + def query(self, cmd, size=-1): + """ + Executes the given query. Wraps commands sent from property + factories in this class with identifiers for the specified + channel. + + :param str cmd: String containing the query to + execute. + :param int size: Number of bytes to be read. Default is read + until termination character is found. + :return: The result of the query as returned by the + connected instrument. + :rtype: `str` + """ + return self._parent.query("F{}:{}".format(self._idx, cmd), + size=size) + + class Measurement: + + """ + Class representing a measurement on a MAUI oscilloscope. + + .. warning:: This class should NOT be manually created by the + user. It is designed to be initialized by the `MAUI` class. + """ + + def __init__(self, parent, idx): + self._parent = parent + self._idx = idx + 1 # 1-based + + # CLASSES # + + class State(Enum): + """ + Enum class for Measurement Parameters. Required to turn it + on or off. + """ + statistics = "CUST,STAT" + histogram_icon = "CUST,HISTICON" + both = "CUST,BOTH" + off = "CUST,OFF" + + # PROPERTIES # + + measurement_state = enum_property( + command="PARM", + enum=State, + doc=""" + Sets / Gets the measurement state. Valid values are + 'statistics', 'histogram_icon', 'both', 'off'. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> msr1 = inst.measurement[0] # set up first measurement + >>> msr1.measurement_state = msr1.State.both # set to `both` + """ + ) + + @property + def statistics(self): + """ + Gets the statistics for the selected parameter. The scope + must be in `My_Measure` mode. + + :return tuple: (average, low, high, sigma, sweeps) + :return type: (float, float, float, float, float) + """ + ret_str = self.query("PAST? CUST, P{}".format(self._idx)).\ + rstrip().split(',') + # parse the return string -> put into dictionary: + ret_dict = {ret_str[it]: ret_str[it+1] for it in + range(0, len(ret_str), 2)} + try: + stats = ( + float(ret_dict['AVG']), + float(ret_dict['LOW']), + float(ret_dict['HIGH']), + float(ret_dict['SIGMA']), + float(ret_dict['SWEEPS']) + ) + except ValueError: # some statistics did not return + raise ValueError("Some statistics did not return useful " + "values. The return string is {}. Please " + "ensure that statistics is properly turned " + "on.".format(ret_str)) + return stats + + # METHODS # + + def delete(self): + """ + Deletes the given measurement parameter. + """ + self.sendcmd("PADL {}".format(self._idx)) + + def set_parameter(self, param, src): + """ + Sets a given parameter that should be measured on this + given channel. + + :param `inst.MeasurementParameters` param: The parameter + to set from the given enum list. + :param int,tuple src: Source, either as an integer if a + channel is requested (e.g., src=0 for Channel 1) or as + a tuple in the form, e.g., ('F', 1). Here 'F' refers + to a mathematical function and 1 would take the second + mathematical function `F2`. + + :raises AttributeError: The chosen parameter is invalid. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> msr1 = inst.measurement[0] # set up first measurement + >>> # setup to measure the 10 - 90% rise time on first channel + >>> msr1.set_parameter(inst.MeasurementParameters.rise_time_10_90, 0) + """ + if not isinstance(param, self._parent.MeasurementParameters): + raise AttributeError("Parameter must be selected from {}.". + format(self._parent.MeasurementParameters) + ) + + send_str = \ + "PACU {},{},{}".format( + self._idx, + param.value, + _source(src) + ) + + self.sendcmd(send_str) + + def sendcmd(self, cmd): + """ + Wraps commands sent from property factories in this class + with identifiers for the specified channel. + + :param str cmd: Command to send to the instrument + """ + self._parent.sendcmd(cmd) + + def query(self, cmd, size=-1): + """ + Executes the given query. Wraps commands sent from property + factories in this class with identifiers for the specified + channel. + + :param str cmd: String containing the query to + execute. + :param int size: Number of bytes to be read. Default is read + until termination character is found. + :return: The result of the query as returned by the + connected instrument. + :rtype: `str` + """ + return self._parent.query(cmd, + size=size) + + # PROPERTIES # + + @property + def channel(self): + """ + Gets an iterator or list for easy Pythonic access to the various + channel objects on the oscilloscope instrument. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> channel = inst.channel[0] # get first channel + """ + return ProxyList(self, self.Channel, range(self.number_channels)) + + @property + def math(self): + """ + Gets an iterator or list for easy Pythonic access to the various + math data sources objects on the oscilloscope instrument. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> math = inst.math[0] # get first math function + """ + return ProxyList(self, self.Math, range(self.number_functions)) + + @property + def measurement(self): + """ + Gets an iterator or list for easy Pythonic access to the various + measurement data sources objects on the oscilloscope instrument. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> msr = inst.measurement[0] # get first measurement parameter + """ + return ProxyList(self, self.Measurement, + range(self.number_measurements)) + + @property + def ref(self): + raise NotImplementedError + + # PROPERTIES + + @property + def number_channels(self): + """ + Sets/Gets the number of channels available on the specific + oscilloscope. Defaults to 4. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.number_channel = 2 # for a oscilloscope with 2 channels + >>> inst.number_channel + 2 + """ + return self._number_channels + + @number_channels.setter + def number_channels(self, newval): + self._number_channels = newval + # create new trigger source enum + self._create_trigger_source_enum() + + @property + def number_functions(self): + """ + Sets/Gets the number of functions available on the specific + oscilloscope. Defaults to 2. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.number_functions = 4 # for a oscilloscope with 4 math functions + >>> inst.number_functions + 4 + """ + return self._number_functions + + @number_functions.setter + def number_functions(self, newval): + self._number_functions = newval + + @property + def number_measurements(self): + """ + Sets/Gets the number of measurements available on the specific + oscilloscope. Defaults to 6. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.number_measurements = 4 # for a oscilloscope with 4 measurements + >>> inst.number_measurements + 4 + """ + return self._number_measurements + + @number_measurements.setter + def number_measurements(self, newval): + self._number_measurements = newval + + @property + def self_test(self): + """ + Runs an oscilloscope's internal self test and returns the + result. The self-test includes testing the hardware of all + channels, the timebase and the trigger circuits. + Hardware failures are identified by a unique binary code in the + returned number. A status of 0 indicates that no + failures occurred. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.self_test() + """ + # increase timeout x 10 to allow for enough time to test + self.timeout *= 10 + retval = self.query("*TST?") + self.timeout /= 10 + return retval + + @property + def show_id(self): + """ + Gets the scope information and returns it. The response + comprises manufacturer, oscilloscope model, serial number, + and firmware revision level. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.show_id() + """ + return self.query("*IDN?") + + @property + def show_options(self): + """ + Gets and returns oscilloscope options: installed software or + hardware that is additional to the standard instrument + configuration. The response consists of a series of response + fields listing all the installed options. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.show_options() + """ + return self.query("*OPT?") + + @property + def time_div(self): + """ + Sets/Gets the time per division, modifies the timebase setting. + Unitful. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.time_div = u.Quantity(200, u.ns) + """ + return u.Quantity( + float(self.query('TDIV?')), u.s + ) + + @time_div.setter + def time_div(self, newval): + newval = assume_units(newval, 's').rescale(u.s).magnitude + self.sendcmd('TDIV {}'.format(newval)) + + # TRIGGER PROPERTIES + + trigger_state = enum_property( + command="TRMD", + enum=TriggerState, + doc=""" + Sets / Gets the trigger state. Valid values are are defined + in `TriggerState` enum class. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.trigger_state = inst.TriggerState.normal + """ + ) + + @property + def trigger_delay(self): + """ + Sets/Gets the trigger offset with respect to time zero (i.e., + a horizontal shift). Unitful. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.trigger_delay = u.Quantity(60, u.ns) + + """ + return u.Quantity( + float(self.query('TRDL?')), u.s + ) + + @trigger_delay.setter + def trigger_delay(self, newval): + newval = assume_units(newval, 's').rescale(u.s).magnitude + self.sendcmd('TRDL {}'.format(newval)) + + @property + def trigger_source(self): + """Sets / Gets the trigger source. + + .. note:: The `TriggerSource` class is dynamically generated + when the number of channels is switched. The above shown class + is only the default! Channels are added and removed, as + required. + + .. warning:: If a trigger type is currently set on the + oscilloscope that is not implemented in this class, + setting the source will fail. The oscilloscope is set up + such that the the trigger type and source are set at the + same time. However, for convenience, these two properties + are split apart here. + + :return: Trigger source. + :rtype: Member of `TriggerSource` class. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.trigger_source = inst.TriggerSource.ext # external trigger + """ + retval = self.query('TRIG_SELECT?').split(',')[2] + return self.TriggerSource(retval) + + @trigger_source.setter + def trigger_source(self, newval): + curr_trig_typ = self.trigger_type + cmd = 'TRIG_SELECT {},SR,{}'.format(curr_trig_typ.value, + newval.value) + self.sendcmd(cmd) + + @property + def trigger_type(self): + """Sets / Gets the trigger type. + + .. warning:: If a trigger source is currently set on the + oscilloscope that is not implemented in this class, + setting the source will fail. The oscilloscope is set up + such that the the trigger type and source are set at the + same time. However, for convenience, these two properties + are split apart here. + + :return: Trigger type. + :rtype: Member of `TriggerType` enum class. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.trigger_type = inst.TriggerType.edge # trigger on edge + """ + retval = self.query('TRIG_SELECT?').split(',')[0] + return self.TriggerType(retval) + + @trigger_type.setter + def trigger_type(self, newval): + curr_trig_src = self.trigger_source + cmd = 'TRIG_SELECT {},SR,{}'.format(newval.value, + curr_trig_src.value) + self.sendcmd(cmd) + + # METHODS # + + def clear_sweeps(self): + """Clears the sweeps in a measurement. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.clear_sweeps() + """ + self.sendcmd("CLEAR_SWEEPS") + + def force_trigger(self): + """Forces a trigger event to occur on the attached oscilloscope. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.force_trigger() + """ + self.sendcmd("ARM") + + def run(self): + """Enables the trigger for the oscilloscope and sets it to auto. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.run() + """ + self.trigger_state = self.TriggerState.auto + + def stop(self): + """ Disables the trigger for the oscilloscope. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") + >>> inst.stop() + """ + self.sendcmd("STOP") + + +# STATICS # + + +def _source(src): + """Stich the source together properly and return it.""" + if isinstance(src, int): + return 'C{}'.format(src + 1) + elif isinstance(src, tuple) and len(src) == 2: + return '{}{}'.format(src[0].upper(), int(src[1]) + 1) + else: + raise ValueError('An invalid source was specified. ' + 'Source must be an integer or a tuple of ' + 'length 2.') diff --git a/instruments/tests/test_teledyne/__init__.py b/instruments/tests/test_teledyne/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instruments/tests/test_teledyne/test_maui.py b/instruments/tests/test_teledyne/test_maui.py new file mode 100644 index 000000000..28bcec77c --- /dev/null +++ b/instruments/tests/test_teledyne/test_maui.py @@ -0,0 +1,1099 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Thorlabs TC200 +""" + +# IMPORTS #################################################################### + +import numpy as np +import pytest + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + + +# pylint: disable=too-many-lines,redefined-outer-name,protected-access + + +# PYTEST FIXTURES FOR INITIALIZATION # + + +@pytest.fixture +def init(): + """Returns the initialization command that is sent to MAUI Scope.""" + return "COMM_HEADER OFF" + + +# TEST ENUM GENERATION # + +def test_create_trigger_source_enum(init): + """Generate trigger source enum when number of channels changes.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + osc.number_channels = 42 + + # existence of new channels, but not more + assert "c41" in osc.TriggerSource.__members__ + assert "c42" not in osc.TriggerSource.__members__ + + # proper name generation + assert osc.TriggerSource["c41"].value == "C42" + + +# TEST DATA SOURCE CLASS # + + +def test_maui_data_source_name(init): + """Return the name of the channel in the data source.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + assert osc.math[0].name == 'F1' + assert osc.channel[1].name == 'C2' + + +def test_maui_data_source_read_waveform(init): + """Return a numpy array of a waveform.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRMD?', + 'TRMD SINGLE', + "C1:INSPECT? 'SIMPLE'", + "C1:INSPECT? 'HORIZ_OFFSET'", + "C1:INSPECT? 'HORIZ_INTERVAL'", + 'TRMD AUTO' + ], + [ + 'AUTO', + '" 1. 2. 3. 4. "', + "HORIZ_OFFSET : 0. ", + "HORIZ_INTERVAL : 2.5 " + ], + sep="\n" + ) as osc: + wf = np.array( + [ + [0., 2.5, 5., 7.5], + [1., 2., 3., 4.] + ] + ) + assert (osc.channel[0].read_waveform() == wf).all() + + +def test_maui_data_source_read_waveform_different_length(init): + """BF: Stacking return arrays with different length. + + Depending on rounding issues, time and data arrays can have + different lengths. Shorten to the shorter one by cutting the longer + one from the end. + """ + faulty_dataset_str = [] + faulty_dataset_int = [] + for it in range(402): # 402 datapoints will make the error + faulty_dataset_str.append(str(it)) + faulty_dataset_int.append(it) + return_data_string = '" ' + ' '.join(faulty_dataset_str) + ' "' + + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRMD?', + 'TRMD SINGLE', + "C1:INSPECT? 'SIMPLE'", + "C1:INSPECT? 'HORIZ_OFFSET'", + "C1:INSPECT? 'HORIZ_INTERVAL'", + 'TRMD AUTO' + ], + [ + 'AUTO', + return_data_string, + "HORIZ_OFFSET : 9.8895e-06 ", + "HORIZ_INTERVAL : 5e-10 " + ], + sep="\n" + ) as osc: + # create the signal that we want to get returned + signal = np.array(faulty_dataset_int) + h_offset = 9.8895e-06 + h_interval = 5e-10 + timebase = np.arange( + h_offset, + h_offset + h_interval * (len(signal)), + h_interval + ) + + # now cut timebase to the length of the signal + timebase = timebase[0:len(signal)] + + # create return dataset + dataset_return = np.stack((timebase, signal)) + assert (osc.channel[0].read_waveform() == dataset_return).all() + + +def test_maui_data_source_read_waveform_math(init): + """Return a numpy array of a waveform for multiple sweeps.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "C1:INSPECT? 'SIMPLE'", + "C1:INSPECT? 'HORIZ_OFFSET'", + "C1:INSPECT? 'HORIZ_INTERVAL'", + ], + [ + '" 1. 2. 3. 4. "', + "HORIZ_OFFSET : 0. ", + "HORIZ_INTERVAL : 2.5 " + ], + sep="\n" + ) as osc: + wf = np.array( + [ + [0., 2.5, 5., 7.5], + [1., 2., 3., 4.] + ] + ) + assert (osc.channel[0].read_waveform(single=False) == wf).all() + + +def test_maui_data_source_read_waveform_bin_format(init): + """Raise a not implemented error.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + with pytest.raises(NotImplementedError): + osc.channel[0].read_waveform(bin_format=True) + + +def test_maui_data_source_trace(init): + """Get / Set the on/off status of a trace.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'F1:TRA?', + 'C1:TRA ON', + 'C3:TRA OFF' + ], + [ + 'ON' + ], + sep="\n" + ) as osc: + assert osc.math[0].trace + osc.channel[0].trace = True + osc.channel[2].trace = False + + +# TEST CHANNEL CLASS # + + +def test_maui_channel_init(init): + """Initialize a MAUI Channel.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + assert osc.channel[0]._idx == 1 + assert isinstance(osc.channel[0]._parent, ik.teledyne.MAUI) + + +def test_maui_channel_coupling(init): + """Get / Set MAUI Channel coupling.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'C3:CPL?', + 'C3:CPL A1M' + ], + [ + 'D50' + ], + sep="\n" + ) as osc: + assert osc.channel[2].coupling == osc.channel[2].Coupling.dc50 + osc.channel[2].coupling = osc.channel[2].Coupling.ac1M + + +def test_maui_channel_offset(init): + """Get / Set MAUI Channel offset.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'C1:OFST?', + 'C1:OFST 0.2', + 'C3:OFST 2' + ], + [ + '1' + ], + sep="\n" + ) as osc: + assert osc.channel[0].offset == u.Quantity(1, u.V) + osc.channel[0].offset = u.Quantity(200, u.mV) + osc.channel[2].offset = 2 + + +def test_maui_channel_scale(init): + """Get / Set MAUI Channel scale.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'C2:VDIV?', + 'C1:VDIV 2.0', + 'C2:VDIV 0.4' + ], + [ + '1' + ], + sep="\n" + ) as osc: + assert osc.channel[1].scale == u.Quantity(1, u.V) + osc.channel[0].scale = u.Quantity(2000, u.mV) + osc.channel[1].scale = 0.4 + + +# TEST MATH CLASS # + + +def test_maui_math_init(init): + """Initialize math channel.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + assert osc.math[0]._idx == 1 + assert isinstance(osc.math[0]._parent, ik.teledyne.MAUI) + + +def test_maui_math_clear_sweeps(init): + """Clears math channel sweeps.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'CLEAR_SWEEPS' + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].clear_sweeps() + + +# TEST MATH CLASS OPERATORS # + + +def test_maui_math_op_current_settings(init): + """Return the current settings as oscilloscope string.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'F1:DEF?' + ], + [ + 'bla bla bla' # answer unimportant + ], + sep="\n" + ) as osc: + assert osc.math[0].operator.current_setting == 'bla bla bla' + + +def test_maui_math_op_absolute(init): + """Set math channel, absolute operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'ABS(C1)'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.absolute(0) + + +def test_maui_math_op_average(init): + """Set math channel, average operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'AVG(C1)',AVERAGETYPE,SUMMED,SWEEPS,1000", + "F1:DEFINE EQN,'AVG(C2)',AVERAGETYPE,CONTINUOUS,SWEEPS,100" + + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.average(0) + osc.math[0].operator.average(1, average_type='continuous', sweeps=100) + + +def test_maui_math_op_derivative(init): + """Set math channel, derivative operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'DERI(C1)',VERSCALE,1000000.0," + "VEROFFSET,0,ENABLEAUTOSCALE,ON", + "F1:DEFINE EQN,'DERI(C3)',VERSCALE,5.0," + "VEROFFSET,1.0,ENABLEAUTOSCALE,OFF" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.derivative(0) + osc.math[0].operator.derivative( + 2, + vscale=u.Quantity(5000, u.mV / u.s), + voffset=u.Quantity(60, u.V / u.min), + autoscale=False + ) + + +def test_maui_math_op_difference(init): + """Set math channel, difference operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'C1-C3',VERSCALEVARIABLE,FALSE", + "F1:DEFINE EQN,'C1-C3',VERSCALEVARIABLE,TRUE" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.difference(0, 2) + osc.math[0].operator.difference(0, 2, vscale_variable=True) + + +def test_maui_math_op_envelope(init): + """Set math channel, envelope operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'EXTR(C1)',SWEEPS,1000,LIMITNUMSWEEPS,True", + "F1:DEFINE EQN,'EXTR(F2)',SWEEPS,10,LIMITNUMSWEEPS,False" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.envelope(0) + osc.math[0].operator.envelope(('f', 1), sweeps=10, limit_sweeps=False) + + +def test_maui_math_op_eres(init): + """Set math channel, eres operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'ERES(C1)',BITS,0.5", + "F1:DEFINE EQN,'ERES(C1)',BITS,3", + "F1:DEFINE EQN,'ERES(C1)',BITS,0.5" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.eres(0) + osc.math[0].operator.eres(0, 3) + osc.math[0].operator.eres(0, 28) + + +def test_maui_math_op_fft(init): + """Set math channel, fft operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'FFT(C1)',TYPE,powerspectrum," + "WINDOW,vonhann,SUPPRESSDC,ON", + "F1:DEFINE EQN,'FFT(C1)',TYPE,real," + "WINDOW,flattop,SUPPRESSDC,OFF", + "F1:DEFINE EQN,'FFT(C1)',TYPE,powerspectrum," + "WINDOW,vonhann,SUPPRESSDC,ON" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.fft(0) + osc.math[0].operator.fft(0, type='real', window='flattop', + suppress_dc=False) + osc.math[0].operator.fft(0, type='inv str', window='inv str', + suppress_dc=1) + + +def test_maui_math_op_floor(init): + """Set math channel, floor operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'FLOOR(C1)',SWEEPS,1000,LIMITNUMSWEEPS,True", + "F1:DEFINE EQN,'FLOOR(C1)',SWEEPS,10,LIMITNUMSWEEPS,False" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.floor(0) + osc.math[0].operator.floor(0, sweeps=10, limit_sweeps=False) + + +def test_maui_math_op_integral(init): + """Set math channel, integral operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'INTG(C1),MULTIPLIER,1,ADDER,0," + "VERSCALE,0.001,VEROFFSET,0", + "F1:DEFINE EQN,'INTG(C4),MULTIPLIER,10,ADDER,0.5," + "VERSCALE,1,VEROFFSET,0.001", + "F1:DEFINE EQN,'INTG(C4),MULTIPLIER,10,ADDER,0.5," + "VERSCALE,1,VEROFFSET,0.001" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.integral(0) + osc.math[0].operator.integral(3, multiplier=10, adder=0.5, + vscale=u.Quantity(1, u.Wb), + voffset=u.Quantity(0.001, u.Wb)) + osc.math[0].operator.integral(3, multiplier=10, adder=0.5, + vscale=1, voffset=0.001) + + +def test_maui_math_op_invert(init): + """Set math channel, invert operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'-C1'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.invert(0) + + +def test_maui_math_op_product(init): + """Set math channel, product operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'C1*C3'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.product(0, 2) + + +def test_maui_math_op_ratio(init): + """Set math channel, ratio operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'C1/C3'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.ratio(0, 2) + + +def test_maui_math_op_reciprocal(init): + """Set math channel, reciprocal operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'1/C3'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.reciprocal(2) + + +def test_maui_math_op_rescale(init): + """Set math channel, rescale operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'RESC(C3)',MULTIPLIER,1,ADDER,0", + "F1:DEFINE EQN,'RESC(C3)',MULTIPLIER,10.3,ADDER,1.3" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.rescale(2) + osc.math[0].operator.rescale(2, multiplier=10.3, adder=1.3) + + +def test_maui_math_op_sinx(init): + """Set math channel, sin(x)/x operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'SINX(C3)'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.sinx(2) + + +def test_maui_math_op_square(init): + """Set math channel, square operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'SQR(C3)'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.square(2) + + +def test_maui_math_op_square_root(init): + """Set math channel, square root operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'SQRT(C3)'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.square_root(2) + + +def test_maui_math_op_sum(init): + """Set math channel, sum operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'C3+C1'" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.sum(2, 0) + + +def test_maui_math_op_trend(init): + """Set math channel, trend operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'TREND(C1)',VERSCALE,1,CENTER,0," + "AUTOFINDSCALE,ON", + "F1:DEFINE EQN,'TREND(C2)',VERSCALE,2,CENTER,1," + "AUTOFINDSCALE,OFF", + "F1:DEFINE EQN,'TREND(C2)',VERSCALE,2,CENTER,1," + "AUTOFINDSCALE,ON" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.trend(0) + osc.math[0].operator.trend(1, vscale=u.Quantity(2, u.V), + center=u.Quantity(1, u.V), + autoscale=False) + osc.math[0].operator.trend(1, vscale=2, center=1, autoscale=True) + + +def test_maui_math_op_roof(init): + """Set math channel, roof operator.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "F1:DEFINE EQN,'ROOF(C1)',SWEEPS,1000,LIMITNUMSWEEPS,True", + "F1:DEFINE EQN,'ROOF(C1)',SWEEPS,10,LIMITNUMSWEEPS,False" + ], + [ + ], + sep="\n" + ) as osc: + osc.math[0].operator.roof(0) + osc.math[0].operator.roof(0, sweeps=10, limit_sweeps=False) + + +# TEST MEASUREMENT CLASS # + + +def test_maui_measurement_init(init): + """Initialize measurement class.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + assert osc.measurement[0]._idx == 1 + assert isinstance(osc.measurement[0]._parent, ik.teledyne.MAUI) + + +def test_maui_measurement_state(init): + """Get / Set measurement state.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "PARM?", + "PARM CUST,HISTICON" + ], + [ + "CUST,OFF" + ], + sep="\n" + ) as osc: + assert osc.measurement[0].measurement_state == \ + osc.measurement[0].State.off + osc.measurement[0].measurement_state = \ + osc.measurement[0].State.histogram_icon + + +def test_maui_measurement_statistics_error(init): + """Raise ValueError if statistics cannot be gathered.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "PAST? CUST, P1" + ], + [ + "CUST,P1,NULL,C3,AVG,UNDEF,HIGH,UNDEF,LAST,UNDEF,LOW,UNDEF," + "SIGMA,UNDEF,SWEEPS,0" + ], + sep="\n" + ) as osc: + with pytest.raises(ValueError): + print(osc.measurement[0].statistics) + + +def test_maui_measurement_statistics(init): + """Get statistics for given measurement.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "PAST? CUST, P1" + ], + [ + "CUST,P1,MEAN,C3,AVG,10,HIGH,20,LAST,30,LOW,40,SIGMA,50," + "SWEEPS,42" + ], + sep="\n" + ) as osc: + assert osc.measurement[0].statistics == (10., 40., 20., 50., 42.) + + +def test_maui_measurement_delete(init): + """Delete a given measurement.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "PADL 1" + ], + [ + ], + sep="\n" + ) as osc: + osc.measurement[0].delete() + + +def test_maui_measurement_set_parameter(init): + """Set a specific parameter.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "PACU 1,AMPL,C3" + ], + [ + ], + sep="\n" + ) as osc: + osc.measurement[0].set_parameter(osc.MeasurementParameters.amplitude, + 2) + + +def test_maui_measurement_set_invalid_parameter(init): + """Set a specific parameter.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + with pytest.raises(AttributeError): + osc.measurement[0].set_parameter('amplitude', 2) + + +# TEST CLASS PROPERTIES AND METHODS # + + +def test_maui_ref(init): + """Reference class in oscillioscope is not implemented.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + with pytest.raises(NotImplementedError): + assert osc.ref + + +def test_maui_number_channels(init): + """Set / Get number of channels.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + osc.number_channels = 42 + assert osc.number_channels == 42 + + +def test_maui_number_functions(init): + """Set / Get number of functions.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + osc.number_functions = 42 + assert osc.number_functions == 42 + + +def test_maui_number_measurements(init): + """Set / Get number of measurements.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init + ], + [ + ], + sep="\n" + ) as osc: + osc.number_measurements = 42 + assert osc.number_measurements == 42 + + +def test_maui_self_test(init): + """Runs an oscilloscope self test.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "*TST?" + ], + [ + "*TST 0" + ], + sep="\n" + ) as osc: + assert osc.self_test == "*TST 0" # Status: OK + + +def test_maui_show_id(init): + """Displays oscilloscope ID.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "*IDN?" + ], + [ + "*IDN LECROY,WAVEMASTER,WM01000,3.3.0" + ], + sep="\n" + ) as osc: + assert osc.show_id == "*IDN LECROY,WAVEMASTER,WM01000,3.3.0" + + +def test_maui_show_options(init): + """Displays oscilloscope options.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + "*OPT?" + ], + [ + "*OPT 0" + ], + sep="\n" + ) as osc: + assert osc.show_options == "*OPT 0" # no options installed + + +def test_maui_time_div(init): + """Get / Set time per division.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TDIV?', + 'TDIV 0.001' + ], + [ + '1' + ], + sep="\n" + ) as osc: + assert osc.time_div == u.Quantity(1, u.s) + osc.time_div = u.Quantity(1, u.ms) + + +def test_maui_trigger_state(init): + """Get / Set trigger state.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRMD?', + 'TRMD SINGLE' + ], + [ + 'AUTO' + ], + sep="\n" + ) as osc: + assert osc.trigger_state == osc.TriggerState.auto + osc.trigger_state = osc.TriggerState.single + + +def test_maui_trigger_delay(init): + """Get / Set trigger delay.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRDL?', + 'TRDL 0.001', + 'TRDL 1' + ], + [ + '0.001' + ], + sep="\n" + ) as osc: + assert osc.trigger_delay == u.Quantity(1, u.ms) + osc.trigger_delay = u.Quantity(1, u.ms) + osc.trigger_delay = 1 + + +def test_maui_trigger_source(init): + """Get / Set trigger source.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRIG_SELECT?', + 'TRIG_SELECT?', + 'TRIG_SELECT EDGE,SR,EX' + ], + [ + 'EDGE,SR,C1,HT,OFF', + 'EDGE,SR,C1,HT,OFF' + ], + sep="\n" + ) as osc: + assert osc.trigger_source == osc.TriggerSource.c0 + osc.trigger_source = osc.TriggerSource.ext + + +def test_maui_trigger_type(init): + """Get / Set trigger type.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRIG_SELECT?', + 'TRIG_SELECT?', + 'TRIG_SELECT EDGE,SR,C3' + ], + [ + 'RUNT,SR,C3,HT,OFF', + 'EDGE,SR,C3,HT,OFF' + ], + sep="\n" + ) as osc: + assert osc.trigger_type == osc.TriggerType.runt + osc.trigger_type = osc.TriggerType.edge + + +def test_maui_clear_sweeps(init): + """Clear the sweeps.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'CLEAR_SWEEPS' + ], + [ + ], + sep="\n" + ) as osc: + osc.clear_sweeps() + + +def test_maui_force_trigger(init): + """Force a trigger.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'ARM' + ], + [ + ], + sep="\n" + ) as osc: + osc.force_trigger() + + +def test_maui_run(init): + """Run the measurement in automatic trigger state.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'TRMD AUTO' + ], + [ + ], + sep="\n" + ) as osc: + osc.run() + + +def test_maui_stop(init): + """Stop the acquisition.""" + with expected_protocol( + ik.teledyne.MAUI, + [ + init, + 'STOP' + ], + [ + ], + sep="\n" + ) as osc: + osc.stop() + + +# STATIC FUNCTIONS # + + +def test_source_stichting_integer(): + """Source stiching when an integer is given.""" + assert ik.teledyne.maui._source(3) == "C4" + + +def test_source_stiching_tuple(): + """Source stiching when a tuple is given.""" + assert ik.teledyne.maui._source(('p', 0)) == "P1" + assert ik.teledyne.maui._source(('P', 0)) == "P1" + + +def test_source_stiching_value_error(): + """Raise a value error if anything else.""" + with pytest.raises(ValueError): + ik.teledyne.maui._source(3.14) From 8b20d6fb8aab8609fe9f3ab9e1c7850de79ea892 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 11 Aug 2020 18:20:30 -0700 Subject: [PATCH 044/108] Enhancements to SRS DG645 test suite and class (#247) * Enhancements to SRS DG645 test suite and class - Enhanced test suite of SRS DG645 - All routines have a test - Tested all new tests with actual hardware - Previously named `trigger_source` test was in fact a test for the delay set to a channel. Renamed and moved this specific test. Furthermore, reformatted the test to fit in with the rest of the tests. - An actual trigger_source test is also included now. - Some routines in SRSDG645 class contained, e.g., `level_amplitude` contained a statement to assume units. Some other routines did not. - Included `assume_units` and detailed in doc string for the following routines: > delay (part of _SRS645Channel class) > holdoff > burst_period > burst_delay - Extended test cases accordingly * Added test for initialization of `_SRSDG645Channel` Co-authored-by: Reto Trappitsch --- instruments/srs/srsdg645.py | 10 ++ instruments/tests/test_srs/test_srsdg645.py | 171 +++++++++++++++++++- 2 files changed, 174 insertions(+), 7 deletions(-) diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index f793cdcbe..56c38feb9 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -56,12 +56,15 @@ def delay(self): Formatted as a two-tuple of the reference and the delay time. For example, ``(SRSDG645.Channels.A, u.Quantity(10, "ps"))`` indicates a delay of 10 picoseconds from delay channel A. + + :units: Assume seconds if no units given. """ resp = self._ddg.query("DLAY?{}".format(int(self._chan))).split(",") return SRSDG645.Channels(int(resp[0])), u.Quantity(float(resp[1]), "s") @delay.setter def delay(self, newval): + newval = (newval[0], assume_units(newval[1], u.s)) self._ddg.sendcmd("DLAY {},{},{}".format( int(self._chan), int(newval[0].idx), @@ -331,6 +334,7 @@ def holdoff(self): @holdoff.setter def holdoff(self, newval): + newval = assume_units(newval, u.s) self.sendcmd("HOLD {}".format(newval.rescale(u.s).magnitude)) @property @@ -381,11 +385,14 @@ def burst_period(self): Gets/sets the burst period. The burst period sets the time between delay cycles during a burst. The burst period may range from 100 ns to 2000 – 10 ns in 10 ns steps. + + :units: Assume seconds if no units given. """ return u.Quantity(float(self.query("BURP?")), u.s) @burst_period.setter def burst_period(self, newval): + newval = assume_units(newval, u.s) self.sendcmd("BURP {}".format(newval.rescale(u.s).magnitude)) @property @@ -395,9 +402,12 @@ def burst_delay(self): delays the first burst pulse relative to the trigger by the burst delay. The burst delay may range from 0 ps to < 2000 s with a resolution of 5 ps. + + :units: Assume seconds if no units given. """ return u.Quantity(float(self.query("BURD?")), u.s) @burst_delay.setter def burst_delay(self, newval): + newval = assume_units(newval, u.s) self.sendcmd("BURD {}".format(newval.rescale(u.s).magnitude)) diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index 46ee9ea39..0d363039f 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -6,6 +6,7 @@ # IMPORTS #################################################################### +import pytest import instruments.units as u @@ -14,9 +15,58 @@ # TESTS ###################################################################### +# pylint: disable=protected-access + test_srsdg645_name = make_name_test(ik.srs.SRSDG645) +# CHANNELS # + + +def test_srsdg645_channel_init(): + """ + _SRSDG645Channel: Ensure correct errors are raised during + initialization if not coming from a DG class. + """ + with pytest.raises(TypeError): + ik.srs.srsdg645._SRSDG645Channel(42, 0) + + +def test_srsdg645_channel_init_channel_value(): + """ + _SRSDG645Channel: Ensure the correct channel value is used when + passing on a SRSDG645.Channels instance as the `chan` value. + """ + ddg = ik.srs.SRSDG645.open_test() # test connection + chan = ik.srs.srsdg645.SRSDG645.Channels.B # select a channel manually + assert ik.srs.srsdg645._SRSDG645Channel(ddg, chan)._chan == 3 + + +def test_srsdg645_channel_delay(): + """ + SRSDG645: Get / set delay. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "DLAY?2", + "DLAY 3,2,60.0", + "DLAY 5,4,10" + ], + [ + "0,42" + ], + ) as ddg: + ref, t = ddg.channel["A"].delay + assert ref == ddg.Channels.T0 + assert abs((t - u.Quantity(42, "s")).magnitude) < 1e5 + ddg.channel["B"].delay = (ddg.channel["A"], u.Quantity(1, "minute")) + ddg.channel["D"].delay = (ddg.channel["C"], 10) + + +# DG645 # + + def test_srsdg645_output_level(): """ SRSDG645: Checks getting/setting unitful output level. @@ -71,12 +121,115 @@ def test_srsdg645_output_polarity(): ddg.output["CD"].polarity = ddg.LevelPolarity.negative +def test_srsdg645_output_polarity_raise_type_error(): + """ + SRSDG645: Polarity setter with wrong input - raise type error. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + ], + [ + ] + ) as ddg: + with pytest.raises(TypeError): + ddg.output["AB"].polarity = 1 + + +def test_srsdg645_display(): + """ + SRSDG645: Set / get display mode. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "DISP?", + "DISP 0,0" + ], + [ + "12,3" + ] + ) as ddg: + assert ddg.display == (ddg.DisplayMode.channel_levels, + ddg.Channels.B) + ddg.display = (ddg.DisplayMode.trigger_rate, + ddg.Channels.T0) + + +def test_srsdg645_enable_adv_triggering(): + """ + SRSDG645: Set / get if advanced triggering is enabled. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "ADVT?", + "ADVT 1" + ], + [ + "0" + ] + ) as ddg: + assert not ddg.enable_adv_triggering + ddg.enable_adv_triggering = True + + +def test_srsdg645_trigger_rate(): + """ + SRSDG645: Set / get trigger rate. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "TRAT?", + "TRAT 10000", + "TRAT 1000" + ], + [ + "+1000.000000" + ] + ) as ddg: + assert ddg.trigger_rate == u.Quantity(1000, u.Hz) + ddg.trigger_rate = 10000 + ddg.trigger_rate = u.Quantity(1000, u.Hz) # unitful send + + def test_srsdg645_trigger_source(): - with expected_protocol(ik.srs.SRSDG645, "DLAY?2\nDLAY 3,2,60.0\n", "0,42\n") as ddg: - ref, t = ddg.channel["A"].delay - assert ref == ddg.Channels.T0 - assert abs((t - u.Quantity(42, "s")).magnitude) < 1e5 - ddg.channel["B"].delay = (ddg.channel["A"], u.Quantity(1, "minute")) + """ + SRSDG645: Set / get trigger source. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "TSRC?", + "TSRC 1" + ], + [ + "0" + ] + ) as ddg: + assert ddg.trigger_source == ddg.TriggerSource.internal + ddg.trigger_source = ddg.TriggerSource.external_rising + + +def test_srsdg645_holdoff(): + """ + SRSDG645: Set / get hold off. + """ + with expected_protocol( + ik.srs.SRSDG645, + [ + "HOLD?", + "HOLD 0", + "HOLD 0.01" + ], + [ + "+0.001001000000" + ] + ) as ddg: + assert ddg.holdoff == u.Quantity(1001, u.us) + ddg.holdoff = 0 + ddg.holdoff = u.Quantity(10, u.ms) # unitful hold off def test_srsdg645_enable_burst_mode(): @@ -144,7 +297,8 @@ def test_srsdg645_burst_period(): ik.srs.SRSDG645, [ "BURP?", - "BURP 13" + "BURP 13", + "BURP 0.1" ], [ "100E-9" @@ -152,6 +306,7 @@ def test_srsdg645_burst_period(): ) as ddg: unit_eq(ddg.burst_period, u.Quantity(100, "ns").rescale(u.s)) ddg.burst_period = u.Quantity(13, "s") + ddg.burst_period = 0.1 def test_srsdg645_burst_delay(): @@ -163,7 +318,8 @@ def test_srsdg645_burst_delay(): ik.srs.SRSDG645, [ "BURD?", - "BURD 42" + "BURD 42", + "BURD 0.1" ], [ "0" @@ -171,3 +327,4 @@ def test_srsdg645_burst_delay(): ) as ddg: unit_eq(ddg.burst_delay, u.Quantity(0, "s")) ddg.burst_delay = u.Quantity(42, "s") + ddg.burst_delay = 0.1 From 3786dba84dd2e37b8affa09fed70e4219f10b5f9 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 13 Aug 2020 18:20:30 -0700 Subject: [PATCH 045/108] Test Suite for all Lakeshore instruments, Bug Fix Lakeshore475 (#249) Full test suite for all three existing Lakeshore instruments written. While writing test suite, fixed 4 bugs in `lakeshore475.py`: - Two send strings were stiched together as a string and an integer, not allowed anymore. Switched to proper string formatting using an f-string. - Two TypeError messages contained the wrong text (likely a copy, paste error). This is now fixed. --- instruments/lakeshore/lakeshore475.py | 8 +- instruments/tests/test_lakeshore/__init__.py | 0 .../tests/test_lakeshore/test_lakeshore340.py | 49 ++ .../tests/test_lakeshore/test_lakeshore370.py | 62 ++ .../tests/test_lakeshore/test_lakeshore475.py | 546 ++++++++++++++++++ 5 files changed, 661 insertions(+), 4 deletions(-) create mode 100644 instruments/tests/test_lakeshore/__init__.py create mode 100644 instruments/tests/test_lakeshore/test_lakeshore340.py create mode 100644 instruments/tests/test_lakeshore/test_lakeshore370.py create mode 100644 instruments/tests/test_lakeshore/test_lakeshore475.py diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index d8eb80ec6..19282249c 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -109,7 +109,7 @@ def field_units(self): def field_units(self, newval): if isinstance(newval, u.unitquantity.UnitQuantity): if newval in LAKESHORE_FIELD_UNITS_INV: - self.sendcmd('UNIT ' + LAKESHORE_FIELD_UNITS_INV[newval]) + self.sendcmd(f"UNIT {LAKESHORE_FIELD_UNITS_INV[newval]}") else: raise ValueError('Not an acceptable Python quantities object') else: @@ -131,7 +131,7 @@ def temp_units(self): def temp_units(self, newval): if isinstance(newval, u.unitquantity.UnitQuantity): if newval in LAKESHORE_TEMP_UNITS_INV: - self.sendcmd('TUNIT ' + LAKESHORE_TEMP_UNITS_INV[newval]) + self.sendcmd(f"TUNIT {LAKESHORE_TEMP_UNITS_INV[newval]}") else: raise TypeError('Not an acceptable Python quantities object') else: @@ -314,11 +314,11 @@ def change_measurement_mode(self, mode, resolution, filter_type, "`Lakeshore475.Filter` value, got {} " "instead.".format(type(filter_type))) if not isinstance(peak_mode, Lakeshore475.PeakMode): - raise TypeError("Filter type setting must be a " + raise TypeError("Peak measurement type setting must be a " "`Lakeshore475.PeakMode` value, got {} " "instead.".format(type(peak_mode))) if not isinstance(peak_disp, Lakeshore475.PeakDisplay): - raise TypeError("Filter type setting must be a " + raise TypeError("Peak display type setting must be a " "`Lakeshore475.PeakDisplay` value, got {} " "instead.".format(type(peak_disp))) diff --git a/instruments/tests/test_lakeshore/__init__.py b/instruments/tests/test_lakeshore/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instruments/tests/test_lakeshore/test_lakeshore340.py b/instruments/tests/test_lakeshore/test_lakeshore340.py new file mode 100644 index 000000000..183e6010c --- /dev/null +++ b/instruments/tests/test_lakeshore/test_lakeshore340.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Lakeshore 340 +""" + +# IMPORTS #################################################################### + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + +# pylint: disable=protected-access + +# TEST SENSOR CLASS # + + +def test_lakeshore340_sensor_init(): + """ + Test initialization of sensor class. + """ + with expected_protocol( + ik.lakeshore.Lakeshore340, + [ + ], + [ + ], + ) as cryo: + sensor = cryo.sensor[0] + assert sensor._parent is cryo + assert sensor._idx == 1 + + +def test_lakeshore340_sensor_temperature(): + """ + Receive a unitful temperature from a sensor. + """ + with expected_protocol( + ik.lakeshore.Lakeshore340, + [ + "KRDG?1" + ], + [ + "77" + ], + ) as cryo: + assert cryo.sensor[0].temperature == u.Quantity(77, u.K) diff --git a/instruments/tests/test_lakeshore/test_lakeshore370.py b/instruments/tests/test_lakeshore/test_lakeshore370.py new file mode 100644 index 000000000..3a0b521df --- /dev/null +++ b/instruments/tests/test_lakeshore/test_lakeshore370.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Lakeshore 370 +""" + +# IMPORTS #################################################################### + +import pytest + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + +# pylint: disable=redefined-outer-name,protected-access + +# PYTEST FIXTURES FOR INITIALIZATION # + + +@pytest.fixture +def init(): + """Returns the command the instrument sends at initaliation.""" + return "IEEE 3,0" + + +# TEST SENSOR CLASS # + + +def test_lakeshore370_channel_init(init): + """ + Test initialization of channel class. + """ + with expected_protocol( + ik.lakeshore.Lakeshore370, + [ + init + ], + [ + ], + ) as lsh: + channel = lsh.channel[7] + assert channel._parent is lsh + assert channel._idx == 8 + + +def test_lakeshore370_channel_resistance(init): + """ + Receive a unitful resistance from a channel. + """ + with expected_protocol( + ik.lakeshore.Lakeshore370, + [ + init, + "RDGR? 1" + ], + [ + "100." + ], + ) as lsh: + assert lsh.channel[0].resistance == u.Quantity(100, u.ohm) diff --git a/instruments/tests/test_lakeshore/test_lakeshore475.py b/instruments/tests/test_lakeshore/test_lakeshore475.py new file mode 100644 index 000000000..178440169 --- /dev/null +++ b/instruments/tests/test_lakeshore/test_lakeshore475.py @@ -0,0 +1,546 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Lakeshore 475 Gaussmeter +""" + +# IMPORTS #################################################################### + +import pytest + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + + +# TEST LAKESHORE475 CLASS PROPERTIES # + + +def test_lakeshore475_field(): + """ + Get field from connected probe unitful. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "RDGFIELD?", + "UNIT?" + ], + [ + "200.", + "2" + ], + ) as lsh: + assert lsh.field == u.Quantity(200., u.tesla) + + +def test_lakeshore475_field_units(): + """ + Get / set field unit on device. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "UNIT?", + "UNIT 2" + ], + [ + "3" + ], + ) as lsh: + assert lsh.field_units == u.oersted + lsh.field_units = u.tesla + + +def test_lakeshore475_field_units_invalid_unit(): + """ + Raise a ValueError if an invalid unit is given. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + with pytest.raises(ValueError) as exc_info: + lsh.field_units = u.m + exc_msg = exc_info.value.args[0] + assert exc_msg == "Not an acceptable Python quantities object" + + +def test_lakeshore475_field_units_not_a_unit(): + """ + Raise a ValueError if something else than a quantity is given. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + with pytest.raises(TypeError) as exc_info: + lsh.field_units = 42 + exc_msg = exc_info.value.args[0] + assert exc_msg == "Field units must be a Python quantity" + + +def test_lakeshore475_temp_units(): + """ + Get / set temperature unit on device. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "TUNIT?", + "TUNIT 2" + ], + [ + "1" + ], + ) as lsh: + assert lsh.temp_units == u.celsius + lsh.temp_units = u.kelvin + + +def test_lakeshore475_temp_units_invalid_unit(): + """ + Raise a ValueError if an invalid unit is given. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + with pytest.raises(TypeError) as exc_info: + lsh.temp_units = u.fahrenheit + exc_msg = exc_info.value.args[0] + assert exc_msg == "Not an acceptable Python quantities object" + + +def test_lakeshore475_temp_units_not_a_unit(): + """ + Raise a ValueError if something else than a quantity is given. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + with pytest.raises(TypeError) as exc_info: + lsh.temp_units = 42 + exc_msg = exc_info.value.args[0] + assert exc_msg == "Temperature units must be a Python quantity" + + +def test_lakeshore475_field_setpoint(): + """ + Get / set field set point. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CSETP?", + "UNIT?", + "UNIT?", + "CSETP 10000.0", # send 1 tesla + "UNIT?", + "CSETP 23.0" # send 23 unitless (equals gauss) + ], + [ + "10.", + "1", + "1", + "1" + ], + ) as lsh: + assert lsh.field_setpoint == u.Quantity(10, u.gauss) + lsh.field_setpoint = u.Quantity(1., u.tesla) + lsh.field_setpoint = 23. + + +def test_lakeshore475_field_get_control_params(): + """ + Get field control parameters. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?" + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2" # teslas + ], + ) as lsh: + current_params = lsh.field_control_params + assert current_params == ( + 1.0, + 10.0, + u.Quantity(42.0, u.tesla / u.min), + u.Quantity(100.0, u.volt / u.min) + ) + + +def test_lakeshore475_field_set_control_params(): + """ + Set field control parameters, unitful and using assumed units. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "UNIT?", + "CPARAM 5.0,50.0,120.0,60.0", + "UNIT?", + "CPARAM 5.0,50.0,120.0,180.0", + "UNIT?", + "CPARAM 5.0,50.0,120.0,60.0" + ], + [ + "2", # teslas + "2", # teslas + "2" # teslas + ], + ) as lsh: + # currently set units are used + lsh.field_control_params = ( + 5.0, + 50.0, + u.Quantity(120.0, u.tesla / u.min), + u.Quantity(60.0, u.volt / u.min) + ) + # different units are used + lsh.field_control_params = ( + 5.0, + 50.0, + u.Quantity(20000.0, u.gauss / u.s), + u.Quantity(3000.0, u.mV / u.s) + ) + # no units are used + lsh.field_control_params = ( + 5.0, + 50.0, + 120.0, + 60.0 + ) + + +def test_lakeshore475_field_set_control_params_not_a_tuple(): + """ + Set field control parameters with wrong type. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + with pytest.raises(TypeError) as exc_info: + lsh.field_control_params = 42 + exc_msg = exc_info.value.args[0] + assert exc_msg == "Field control parameters must be specified as " \ + " a tuple" + + +def test_lakeshore475_p_value(): + """ + Get / set p-value. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 5.0,10.0,42.0,100.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2" + ], + ) as lsh: + assert lsh.p_value == 1.0 + lsh.p_value = 5.0 + + +def test_lakeshore475_i_value(): + """ + Get / set i-value. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,5.0,42.0,100.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2" + ], + ) as lsh: + assert lsh.i_value == 10.0 + lsh.i_value = 5.0 + + +def test_lakeshore475_ramp_rate(): + """ + Get / set ramp rate, unitful and not. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,420.0,100.0", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,420.0,100.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + "2", + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", + "2" + ], + ) as lsh: + assert lsh.ramp_rate == u.Quantity(42.0, u.tesla / u.min) + lsh.ramp_rate = u.Quantity(420.0, u.tesla / u.min) + lsh.ramp_rate = 420.0 + + +def test_lakeshore475_control_slope_limit(): + """ + Get / set slope limit, unitful and not. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,42.0,42.0", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,42.0,42.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", + "2" + ], + ) as lsh: + assert lsh.control_slope_limit == u.Quantity(100.0, u.V / u.min) + lsh.control_slope_limit = u.Quantity(42000.0, u.mV / u.min) + lsh.control_slope_limit = 42.0 + + +def test_lakeshore475_control_mode(): + """ + Get / set control mode. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "CMODE?", + "CMODE 1" + ], + [ + "0" + ], + ) as lsh: + assert not lsh.control_mode + lsh.control_mode = True + + +# TEST LAKESHORE475 CLASS METHODS # + + +def test_lakeshore475_change_measurement_mode(): + """ + Change the measurement mode with valid values and ensure properly + sent to device. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "RDGMODE 1,2,3,2,1" + ], + [ + ], + ) as lsh: + # parameters to send + mode = lsh.Mode.dc + resolution = 4 + filter_type = lsh.Filter.lowpass + peak_mode = lsh.PeakMode.pulse + peak_disp = lsh.PeakDisplay.positive + # send them + lsh.change_measurement_mode( + mode, + resolution, + filter_type, + peak_mode, + peak_disp + ) + + +def test_lakeshore475_change_measurement_mode_mismatched_type(): + """ + Ensure that mismatched input type raises a TypeError. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + # parameters to send + mode = lsh.Mode.dc + resolution = 4 + filter_type = lsh.Filter.lowpass + peak_mode = lsh.PeakMode.pulse + peak_disp = lsh.PeakDisplay.positive + # check mode + with pytest.raises(TypeError) as exc_info: + lsh.change_measurement_mode( + 42, + resolution, + filter_type, + peak_mode, + peak_disp + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == f"Mode setting must be a `Lakeshore475.Mode` " \ + f"value, got {type(42)} instead." + # check resolution + with pytest.raises(TypeError) as exc_info: + lsh.change_measurement_mode( + mode, + 3.14, + filter_type, + peak_mode, + peak_disp + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == 'Parameter "resolution" must be an integer.' + # check filter_type + with pytest.raises(TypeError) as exc_info: + lsh.change_measurement_mode( + mode, + resolution, + 42, + peak_mode, + peak_disp + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == f"Filter type setting must be a " \ + f"`Lakeshore475.Filter` value, " \ + f"got {type(42)} instead." + # check peak_mode + with pytest.raises(TypeError) as exc_info: + lsh.change_measurement_mode( + mode, + resolution, + filter_type, + 42, + peak_disp + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == f"Peak measurement type setting must be a " \ + f"`Lakeshore475.PeakMode` value, " \ + f"got {type(42)} instead." + # check peak_display + with pytest.raises(TypeError) as exc_info: + lsh.change_measurement_mode( + mode, + resolution, + filter_type, + peak_mode, + 42 + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == f"Peak display type setting must be a " \ + f"`Lakeshore475.PeakDisplay` value, " \ + f"got {type(42)} instead." + + +def test_lakeshore475_change_measurement_mode_invalid_resolution(): + """ + Ensure that mismatched input type raises a TypeError. + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + ], + [ + ], + ) as lsh: + # parameters to send + mode = lsh.Mode.dc + filter_type = lsh.Filter.lowpass + peak_mode = lsh.PeakMode.pulse + peak_disp = lsh.PeakDisplay.positive + # check resolution too low + with pytest.raises(ValueError) as exc_info: + lsh.change_measurement_mode( + mode, + 2, + filter_type, + peak_mode, + peak_disp + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == "Only 3,4,5 are valid resolutions." + # check resolution too high + with pytest.raises(ValueError) as exc_info: + lsh.change_measurement_mode( + mode, + 6, + filter_type, + peak_mode, + peak_disp + ) + exc_msg = exc_info.value.args[0] + assert exc_msg == "Only 3,4,5 are valid resolutions." From 64093e5a2d4a350c7bff03276944dc52dcb42961 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 24 Aug 2020 20:40:49 -0400 Subject: [PATCH 046/108] Tests rigolds1000 (#250) * Bug fixes and full test suite for Rigol DS1000 Created tests for all functions of Rigol DS1000. Currently contains one failing test: `test_channel_coupling`: - The test is correct and according to the manual - The issue is in the routine, bug fix needs discussion Bug fixes for `rigolds1000.py`: - In `bool_properties` calls, named arguments were not appropriately named for `inst_true` and `inst_false`. Corrected. - All bug fixes cross checked with instrument manual. - `panel_lock` bool_property might have a bug when compared with manual. Needs discussion. * Proposed fix for Coupling error Move coupling into the channel instance. This is similarly done the same way for other oscilloscopes, e.g., `Tektronix DPO70000`. * Change Rigol DS1000 Key lock command Change from "ON", "OFF" to the manual described "ENAB", "DIS". --- instruments/rigol/rigolds1000.py | 32 +- .../tests/test_rigol/test_rigolds1000.py | 346 ++++++++++++++++++ 2 files changed, 363 insertions(+), 15 deletions(-) create mode 100644 instruments/tests/test_rigol/test_rigolds1000.py diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 9885d4780..965c87362 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -37,14 +37,6 @@ class AcquisitionType(Enum): average = "AVER" peak_detect = "PEAK" - class Coupling(Enum): - """ - Enum containing valid coupling modes for the Rigol DS1000 - """ - ac = "AC" - dc = "DC" - ground = "GND" - # INNER CLASSES # class DataSource(OscilloscopeDataSource): @@ -79,6 +71,15 @@ class Channel(DataSource, OscilloscopeChannel): .. warning:: This class should NOT be manually created by the user. It is designed to be initialized by the `RigolDS1000Series` class. """ + + class Coupling(Enum): + """ + Enum containing valid coupling modes for the Rigol DS1000 + """ + ac = "AC" + dc = "DC" + ground = "GND" + def __init__(self, parent, idx): self._parent = parent self._idx = idx + 1 # Rigols are 1-based. @@ -107,21 +108,21 @@ def query(self, cmd): """ return self._parent.query(":CHAN{}:{}".format(self._idx, cmd)) - coupling = enum_property("COUP", lambda: RigolDS1000Series.Coupling) + coupling = enum_property("COUP", Coupling) - bw_limit = bool_property("BWL", "ON", "OFF") - display = bool_property("DISP", "ON", "OFF") - invert = bool_property("INV", "ON", "OFF") + bw_limit = bool_property("BWL", inst_true="ON", inst_false="OFF") + display = bool_property("DISP", inst_true="ON", inst_false="OFF") + invert = bool_property("INV", inst_true="ON", inst_false="OFF") # TODO: :CHAN:OFFset # TODO: :CHAN:PROBe # TODO: :CHAN:SCALe - filter = bool_property("FILT", "ON", "OFF") + filter = bool_property("FILT", inst_true="ON", inst_false="OFF") # TODO: :CHAN:MEMoryDepth - vernier = bool_property("VERN", "ON", "OFF") + vernier = bool_property("VERN", inst_true="ON", inst_false="OFF") # PROPERTIES # @@ -193,7 +194,8 @@ def stop(self): # # Many of the :KEY: commands are not yet implemented as methods. - panel_locked = bool_property(":KEY:LOCK", "ON", "OFF") + panel_locked = bool_property(":KEY:LOCK", inst_true="ENAB", + inst_false="DIS") def release_panel(self): # TODO: better name? diff --git a/instruments/tests/test_rigol/test_rigolds1000.py b/instruments/tests/test_rigol/test_rigolds1000.py new file mode 100644 index 000000000..5d1764a33 --- /dev/null +++ b/instruments/tests/test_rigol/test_rigolds1000.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Rigol DS1000 +""" + +# IMPORTS #################################################################### + +import numpy as np +import pytest + +import instruments as ik +from instruments.tests import expected_protocol, make_name_test + +# TESTS ###################################################################### + +# pylint: disable=protected-access + + +test_rigolds1000_name = make_name_test(ik.rigol.RigolDS1000Series) + + +# TEST CHANNEL # + + +def test_channel_initialization(): + """Ensure correct initialization of channel object.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ], + [ + ] + ) as osc: + channel = osc.channel[0] + assert channel._parent is osc + assert channel._idx == 1 + + +def test_channel_coupling(): + """Get / set channel coupling.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":CHAN1:COUP?", + ":CHAN2:COUP DC" + ], + [ + "AC" + ] + ) as osc: + assert osc.channel[0].coupling == osc.channel[0].Coupling.ac + osc.channel[1].coupling = osc.channel[1].Coupling.dc + + +def test_channel_bw_limit(): + """Get / set instrument bw limit.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":CHAN2:BWL?", + ":CHAN1:BWL ON" + ], + [ + "OFF" + ] + ) as osc: + assert not osc.channel[1].bw_limit + osc.channel[0].bw_limit = True + + +def test_channel_display(): + """Get / set instrument display.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":CHAN2:DISP?", + ":CHAN1:DISP ON" + ], + [ + "OFF" + ] + ) as osc: + assert not osc.channel[1].display + osc.channel[0].display = True + + +def test_channel_invert(): + """Get / set instrument invert.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":CHAN2:INV?", + ":CHAN1:INV ON" + ], + [ + "OFF" + ] + ) as osc: + assert not osc.channel[1].invert + osc.channel[0].invert = True + + +def test_channel_filter(): + """Get / set instrument filter.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":CHAN2:FILT?", + ":CHAN1:FILT ON" + ], + [ + "OFF" + ] + ) as osc: + assert not osc.channel[1].filter + osc.channel[0].filter = True + + +def test_channel_vernier(): + """Get / set instrument vernier.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":CHAN2:VERN?", + ":CHAN1:VERN ON" + ], + [ + "OFF" + ] + ) as osc: + assert not osc.channel[1].vernier + osc.channel[0].vernier = True + + +def test_channel_name(): + """Get channel name - DataSource property.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ], + [ + ] + ) as osc: + assert osc.channel[0].name == "CHAN1" + + +def test_channel_read_waveform(): + """Read waveform of channel object.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":WAV:DATA? CHAN2" + ], + [ + b"#210" + bytes.fromhex("00000001000200030004") + b"0" + ] + ) as osc: + np.testing.assert_array_equal( + osc.channel[1].read_waveform(), + [0, 1, 2, 3, 4] + ) + + +# TEST MATH # + + +def test_math_name(): + """Ensure correct naming of math object.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ], + [ + ] + ) as osc: + assert osc.math.name == "MATH" + + +def test_math_read_waveform(): + """Read waveform of of math object.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":WAV:DATA? MATH" + ], + [ + b"#210" + bytes.fromhex("00000001000200030004") + b"0" + ] + ) as osc: + np.testing.assert_array_equal( + osc.math.read_waveform(), + [0, 1, 2, 3, 4] + ) + + +# TEST REF DATASOURCE # + + +def test_ref_name(): + """Ensure correct naming of ref object.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ], + [ + ] + ) as osc: + assert osc.ref.name == "REF" + + +def test_ref_read_waveform_raises_error(): + """Ensure error raising when reading waveform of REF channel.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ], + [ + ] + ) as osc: + with pytest.raises(NotImplementedError): + osc.ref.read_waveform() + + +# TEST FURTHER PROPERTIES AND METHODS # + + +def test_acquire_type(): + """Get / Set acquire type.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":ACQ:TYPE?", + ":ACQ:TYPE PEAK" + ], + [ + "NORM" + ] + ) as osc: + assert osc.acquire_type == osc.AcquisitionType.normal + osc.acquire_type = osc.AcquisitionType.peak_detect + + +def test_acquire_averages(): + """Get / Set acquire averages.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":ACQ:AVER?", + ":ACQ:AVER 128" + ], + [ + "16" + ] + ) as osc: + assert osc.acquire_averages == 16 + osc.acquire_averages = 128 + + +def test_acquire_averages_bad_values(): + """Raise error when bad values encountered.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ], + [ + ] + ) as osc: + with pytest.raises(ValueError): + osc.acquire_averages = 0 + with pytest.raises(ValueError): + osc.acquire_averages = 1 + with pytest.raises(ValueError): + osc.acquire_averages = 42 + with pytest.raises(ValueError): + osc.acquire_averages = 257 + with pytest.raises(ValueError): + osc.acquire_averages = 512 + + +def test_force_trigger(): + """Force a trigger.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":FORC" + ], + [ + ] + ) as osc: + osc.force_trigger() + + +def test_run(): + """Run the instrument.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":RUN" + ], + [ + ] + ) as osc: + osc.run() + + +def test_stop(): + """Stop the instrument.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":STOP" + ], + [ + ] + ) as osc: + osc.stop() + + +def test_panel_locked(): + """Get / set the panel_locked bool property.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":KEY:LOCK?", + ":KEY:LOCK DIS" + ], + [ + "ENAB" + ] + ) as osc: + assert osc.panel_locked + osc.panel_locked = False + + +def test_release_panel(): + """Get / set the panel_locked bool property.""" + with expected_protocol( + ik.rigol.RigolDS1000Series, + [ + ":KEY:FORC" + ], + [ + ] + ) as osc: + osc.release_panel() From 4c1a67c1043de0a001057b4e9a58bc296f843cf8 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 25 Aug 2020 12:45:10 -0400 Subject: [PATCH 047/108] Tests for Yokogawa 7651 and tests + BF for Yokogawa 6370 (#251) - Full test suite for Yokogawa 7651 - Extended test suite for Yokogawa 6370 for complete coverage - Found two small bugs in Yokogawa 6370: - Routines `data` and `wavelength`, both return the data for the active trace, returned the method itself instead of the data. - These were also the two untested routines. --- .../tests/test_yokogawa/test_yokogawa7651.py | 232 ++++++++++++++++++ .../tests/test_yokogawa/test_yokogawa_6370.py | 61 +++++ instruments/yokogawa/yokogawa6370.py | 4 +- 3 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 instruments/tests/test_yokogawa/test_yokogawa7651.py diff --git a/instruments/tests/test_yokogawa/test_yokogawa7651.py b/instruments/tests/test_yokogawa/test_yokogawa7651.py new file mode 100644 index 000000000..9d49a71c5 --- /dev/null +++ b/instruments/tests/test_yokogawa/test_yokogawa7651.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the Yokogawa 7651 power supply +""" + +# IMPORTS ##################################################################### + + +import pytest + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + + +# TESTS ####################################################################### + +# pylint: disable=protected-access + +# TEST CHANNEL # + + +def test_channel_init(): + """Initialize of channel class.""" + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + ], + [ + ] + ) as yok: + assert yok.channel[0]._parent is yok + assert yok.channel[0]._name == 0 + + +def test_channel_mode(): + """Get / Set mode of the channel.""" + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + "F5;", + "E;", # trigger + "F1;", + "E;" # trigger + ], + [ + ] + ) as yok: + # query + with pytest.raises(NotImplementedError) as exc_info: + print(f"Mode is: {yok.channel[0].mode}") + exc_msg = exc_info.value.args[0] + assert exc_msg == "This instrument does not support querying the " \ + "operation mode." + + # set first current, then voltage mode + yok.channel[0].mode = yok.Mode.current + yok.channel[0].mode = yok.Mode.voltage + + +def test_channel_invalid_mode_set(): + """Set mode to invalid value.""" + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + ], + [ + ] + ) as yok: + wrong_mode = 42 + with pytest.raises(TypeError) as exc_info: + yok.channel[0].mode = wrong_mode + exc_msg = exc_info.value.args[0] + assert exc_msg == "Mode setting must be a `Yokogawa7651.Mode` " \ + "value, got {} instead.".format(type(wrong_mode)) + + +def test_channel_voltage(): + """Get / Set voltage of channel.""" + + # values to set for test + value_unitless = 5. + value_unitful = u.Quantity(500, u.mV) + + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + "F1;\nE;", # set voltage mode + f"SA{value_unitless};", + "E;", # trigger + "F1;\nE;", # set voltage mode + f"SA{value_unitful.rescale(u.volt).magnitude};", + "E;" # trigger + ], + [ + ] + ) as yok: + # query + with pytest.raises(NotImplementedError) as exc_info: + print(f"Voltage is: {yok.channel[0].voltage}") + exc_msg = exc_info.value.args[0] + assert exc_msg == "This instrument does not support querying the " \ + "output voltage setting." + + # set first current, then voltage mode + yok.channel[0].voltage = value_unitless + yok.channel[0].voltage = value_unitful + + +def test_channel_current(): + """Get / Set current of channel.""" + + # values to set for test + value_unitless = 0.8 + value_unitful = u.Quantity(50, u.mA) + + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + "F5;\nE;", # set voltage mode + f"SA{value_unitless};", + "E;", # trigger + "F5;\nE;", # set voltage mode + f"SA{value_unitful.rescale(u.A).magnitude};", + "E;" # trigger + ], + [ + ] + ) as yok: + # query + with pytest.raises(NotImplementedError) as exc_info: + print(f"Current is: {yok.channel[0].current}") + exc_msg = exc_info.value.args[0] + assert exc_msg == "This instrument does not support querying the " \ + "output current setting." + + # set first current, then current mode + yok.channel[0].current = value_unitless + yok.channel[0].current = value_unitful + + +def test_channel_output(): + """Get / Set output of channel.""" + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + "O1;", # turn output on + "E;", + "O0;", # turn output off + "E;" + ], + [ + ] + ) as yok: + # query + with pytest.raises(NotImplementedError) as exc_info: + print(f"Output is: {yok.channel[0].output}") + exc_msg = exc_info.value.args[0] + assert exc_msg == "This instrument does not support querying the " \ + "output status." + + # set first current, then current mode + yok.channel[0].output = True + yok.channel[0].output = False + + +# CLASS PROPERTIES # + + +def test_voltage(): + """Get / Set voltage of instrument.""" + + # values to set for test + value_unitless = 5. + value_unitful = u.Quantity(500, u.mV) + + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + "F1;\nE;", # set voltage mode + f"SA{value_unitless};", + "E;", # trigger + "F1;\nE;", # set voltage mode + f"SA{value_unitful.rescale(u.volt).magnitude};", + "E;" # trigger + ], + [ + ] + ) as yok: + # query + with pytest.raises(NotImplementedError) as exc_info: + print(f"Voltage is: {yok.voltage}") + exc_msg = exc_info.value.args[0] + assert exc_msg == "This instrument does not support querying the " \ + "output voltage setting." + + # set first current, then voltage mode + yok.voltage = value_unitless + yok.voltage = value_unitful + + +def test_current(): + """Get / Set current of instrument.""" + + # values to set for test + value_unitless = 0.8 + value_unitful = u.Quantity(50, u.mA) + + with expected_protocol( + ik.yokogawa.Yokogawa7651, + [ + "F5;\nE;", # set current mode + f"SA{value_unitless};", + "E;", # trigger + "F5;\nE;", # set current mode + f"SA{value_unitful.rescale(u.A).magnitude};", + "E;" # trigger + ], + [ + ] + ) as yok: + # query + with pytest.raises(NotImplementedError) as exc_info: + print(f"current is: {yok.current}") + exc_msg = exc_info.value.args[0] + assert exc_msg == "This instrument does not support querying the " \ + "output current setting." + + # set first current, then current mode + yok.current = value_unitless + yok.current = value_unitful diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index 0b382bfbb..68ced84db 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -223,6 +223,67 @@ def test_active_trace(): assert inst.active_trace == inst.Traces.G +# METHODS # + + +@given(values=st.lists(st.decimals(allow_infinity=False, allow_nan=False), + min_size=1)) +def test_data_active_trace(values): + """Get data from active trace - method.""" + values_packed = b"".join(struct.pack(" Date: Thu, 27 Aug 2020 14:59:07 -0400 Subject: [PATCH 048/108] Test suite and BF for Tektronix AWG2000 (#252) * Test suite and BF for Tektronix AWG2000 Full test suite created Bug fixes for `TekAWG2000` class: - Enum names were called instead of value (twice) in properties getter for `polarity` and `shape. Now switched to value. - `upload_waveform`: Rearranged error checking: Now, no instrument changes are sent when a `ValueError` is encountered. This is not necessarily a bug, but I think it is better this this way. - Fixed the following depreciation warning: "DeprecationWarning: tostring() is deprecated. Use tobytes() instead." * Sampling of instrument channel in test suite with pytest parametrize Replaces the previous use of hypothesis. All channels are still sampled. --- instruments/tektronix/tekawg2000.py | 16 +- .../tests/test_tektronix/test_tekawg2000.py | 331 ++++++++++++++++++ 2 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 instruments/tests/test_tektronix/test_tekawg2000.py diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index 6e08c4e9f..a9f8b9cbd 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -125,8 +125,8 @@ def polarity(self): :type: `TekAWG2000.Polarity` """ - return TekAWG2000.Polarity[self._tek.query("FG:{}:POL?".format( - self._name)).strip()] + return TekAWG2000.Polarity(self._tek.query("FG:{}:POL?".format( + self._name)).strip()) @polarity.setter def polarity(self, newval): @@ -145,8 +145,8 @@ def shape(self): :type: `TekAWG2000.Shape` """ - return TekAWG2000.Shape[self._tek.query("FG:{}:SHAP?".format( - self._name)).strip().split(',')[0]] + return TekAWG2000.Shape(self._tek.query("FG:{}:SHAP?".format( + self._name)).strip().split(',')[0]) @shape.setter def shape(self, newval): @@ -243,15 +243,15 @@ def upload_waveform(self, yzero, ymult, xincr, waveform): if not isinstance(waveform, np.ndarray): raise TypeError("waveform must be specified as a numpy array") + if np.max(np.abs(waveform)) > 1: + raise ValueError("The max value for an element in waveform is 1.") + self.sendcmd("WFMP:YZERO {}".format(yzero)) self.sendcmd("WFMP:YMULT {}".format(ymult)) self.sendcmd("WFMP:XINCR {}".format(xincr)) - if np.max(np.abs(waveform)) > 1: - raise ValueError("The max value for an element in waveform is 1.") - waveform *= (2**12 - 1) - waveform = waveform.astype(" Date: Tue, 1 Sep 2020 14:34:28 -0400 Subject: [PATCH 049/108] #248 - Fix coveralls integration (#255) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 40a3b6624..6bf23f02c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: install: - "pip install -r requirements.txt" - "pip install -r dev-requirements.txt" - - pip install python-coveralls + - pip install coveralls - pip install coverage - pip install pytest-cov before_script: From d5c1ad55650a13428833f7a259a2deb55f6d2bd7 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 1 Sep 2020 17:04:58 -0700 Subject: [PATCH 050/108] Test suite for Tektronix DPO4104 and bug fixes (#253) * Test suite for Tektronix DPO4104 and bug fixes Full coverage test suite for Tektronix DPO4104 added. Trying to make use of hypothesis where it makes sense and leave it away where it doesn't. Bug fixes for `tekdpo4104.py`: - Instead of the new value to set, the documentation was passed on in the `_parent_property` routine. This routine is only used once, however, a usage scenario is written but has likely never been tested before. The test that does what this property is supposed to do passes now with the fix. - Reading ASCII data from a data source used `map` and then directly transferred to an `ndarray`. This was fine in python 2, not anymore though. Fixed on line 114. - Reading binary data is tested now. The test however failed since the next command issued left only a termination character. The binary data is read using `binblockread` from `Instruments`. This will leave the last termination character behind. Inserted an additional reading statement to read one character to get rid of this character. Checked with the manual to ensure consistency. Manual states that this termination character should be there after the binary data block, thus, the bug fix makes sense. * Switched hypothesis random sampling over enum to pytest parametrize --- instruments/tektronix/tekdpo4104.py | 7 +- .../tests/test_tektronix/test_tekdpo4104.py | 503 ++++++++++++++++++ 2 files changed, 507 insertions(+), 3 deletions(-) create mode 100644 instruments/tests/test_tektronix/test_tekdpo4104.py diff --git a/instruments/tektronix/tekdpo4104.py b/instruments/tektronix/tekdpo4104.py index fe046470e..5a6b9ae64 100644 --- a/instruments/tektronix/tekdpo4104.py +++ b/instruments/tektronix/tekdpo4104.py @@ -34,7 +34,7 @@ def getter(self): # pylint: disable=missing-docstring def setter(self, newval): with self: # pylint: disable=protected-access - setattr(self._tek, prop_name, doc) + setattr(self._tek, prop_name, newval) return property(getter, setter, doc=doc) @@ -111,8 +111,7 @@ def read_waveform(self, bin_format=True): sleep(0.02) # Work around issue with 2.48 firmware. raw = self._tek.query("CURVE?") raw = raw.split(",") # Break up comma delimited string - raw = map(float, raw) # Convert each list element to int - raw = np.array(raw) # Convert into numpy array + raw = np.array(raw, dtype=np.float) # Convert to numpy array else: # Set encoding to signed, big-endian self._tek.sendcmd("DAT:ENC RIB") @@ -121,6 +120,8 @@ def read_waveform(self, bin_format=True): self._tek.sendcmd("CURVE?") # Read in the binary block, data width of 2 bytes. raw = self._tek.binblockread(data_width) + # Read the new line character that is sent + self._tek._file.read_raw(1) # pylint: disable=protected-access yoffs = self._tek.y_offset # Retrieve Y offset ymult = self._tek.query("WFMP:YMU?") # Retrieve Y multiplier diff --git a/instruments/tests/test_tektronix/test_tekdpo4104.py b/instruments/tests/test_tektronix/test_tekdpo4104.py new file mode 100644 index 000000000..ebfa6e2d9 --- /dev/null +++ b/instruments/tests/test_tektronix/test_tekdpo4104.py @@ -0,0 +1,503 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Tests for the Tektronix DPO 4104 oscilloscope. +""" + +# IMPORTS ##################################################################### + +from enum import Enum +import struct + +from hypothesis import ( + given, + strategies as st, +) +import numpy as np +import pytest + +import instruments as ik +from instruments.tests import expected_protocol, make_name_test + + +# TESTS ####################################################################### + +# pylint: disable=protected-access + + +test_tekdpo4104_name = make_name_test(ik.tektronix.TekDPO4104) + + +# INSTRUMENT # + + +def test_data_source(): + """Get / set data source for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "DAT:SOU CH1", # set a string + "DAT:SOU?", + "DAT:SOU REF2", # set value of an enum + "DAT:SOU?", + "DAT:SOU MATH", # set a math channel + "DAT:SOU?" + ], + [ + "CH1", + "REF2", + "MATH" + ] + ) as inst: + # Channel as string + inst.data_source = "CH1" + assert inst.data_source == \ + ik.tektronix.tekdpo4104._TekDPO4104Channel(inst, 0) + + # Reference channel as enum + class RefChannel(Enum): + """Temporary reference channel enum.""" + channel = "REF2" + + channel = RefChannel.channel.value + inst.data_source = RefChannel.channel + assert inst.data_source == \ + ik.tektronix.tekdpo4104._TekDPO4104DataSource(inst, channel) + + # Set a math channel + math_ch = inst.math + inst.data_source = math_ch + assert inst.data_source == \ + ik.tektronix.tekdpo4104._TekDPO4104DataSource(inst, + math_ch.name) + + +h_record_lengths_possible = (1000, 10000, 100000, 1000000, 10000000) + + +@pytest.mark.parametrize("aqu_length", h_record_lengths_possible) +def test_aquisition_length(aqu_length): + """Get / set acquisition length with valid values.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + f"HOR:RECO {aqu_length}", + "HOR:RECO?" + ], + [ + f"{aqu_length}" + ] + ) as inst: + inst.aquisition_length = aqu_length + assert inst.aquisition_length == aqu_length + + +def test_aquisition_running(): + """Get / set status of aquisition running.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "ACQ:STATE?", + "ACQ:STATE 0", + "ACQ:STATE?", + "ACQ:STATE 1" + ], + [ + "1", + "0" + ] + ) as inst: + assert inst.aquisition_running + inst.aquisition_running = False + assert not inst.aquisition_running + inst.aquisition_running = True + + +def test_aquisition_continuous(): + """Get / set status of aquisition continuous.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "ACQ:STOPA?", + "ACQ:STOPA SEQ", + "ACQ:STOPA?", + "ACQ:STOPA RUNST" + ], + [ + "RUNST", + "SEQ" + ] + ) as inst: + assert inst.aquisition_continuous + inst.aquisition_continuous = False + assert not inst.aquisition_continuous + inst.aquisition_continuous = True + + +@pytest.mark.parametrize("data_width", (1, 2)) +def test_data_width(data_width): + """Get / set data width with valid values.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + f"DATA:WIDTH {data_width}", + "DATA:WIDTH?", + ], + [ + f"{data_width}" + ] + ) as inst: + inst.data_width = data_width + assert inst.data_width == data_width + + +def test_data_width_out_of_range(): + """Raise Value Error if input value is out of range.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as exc_info: + inst.data_width = 42 + exc_msg = exc_info.value.args[0] + assert exc_msg == "Only one or two byte-width is supported." + + +@given(offset=st.floats(min_value=-100, max_value=100)) +def test_y_offset(offset): + """Get / set Y offset of currently selected data source.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + f"WFMP:YOF {offset}", + "WFMP:YOF?" + ], + [ + f"{offset}" + ] + ) as inst: + inst.y_offset = offset + assert inst.y_offset == offset + + +def test_force_trigger(): + """Force a trigger event to occur.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "TRIG FORCE" + ], + [ + ] + ) as inst: + inst.force_trigger() + + +# CHANNELS # + + +channels_to_try = range(4) +channels_to_try_ids = [f"CH{it}" for it in channels_to_try] + + +@pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_ids) +def test_channel_init(channel): + """Initialize a channel.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + assert inst.channel[channel]._idx == channel + 1 + + +@pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_ids) +@pytest.mark.parametrize("coupling", ik.tektronix.TekDPO4104.Coupling) +def test_channel_coupling(channel, coupling): + """Initialize a channel.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + f"CH{channel + 1}:COUPL {coupling.value}", + f"CH{channel + 1}:COUPL?" + ], + [ + f"{coupling.value}" + ] + ) as inst: + inst.channel[channel].coupling = coupling + assert inst.channel[channel].coupling == coupling + + +def test_channel_coupling_invalid_value(): + """Raise Type Error when trying to set coupling with wrong value.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + wrong_type = "DC" + with pytest.raises(TypeError) as exc_info: + inst.channel[0].coupling = wrong_type + exc_msg = exc_info.value.args[0] + assert exc_msg == f"Coupling setting must be a `TekDPO4104.Coupling`" \ + f" value, got {type(wrong_type)} instead." + + +# DATA SOURCE # + + +reference_sources_to_try = range(4) +reference_sources_to_try_ids = [f"REF{it}" for it in reference_sources_to_try] + + +@pytest.mark.parametrize("ref", reference_sources_to_try, + ids=reference_sources_to_try_ids) +def test_data_source_ref_initialize(ref): + """Initialize a ref data source.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + ref_source = inst.ref[ref] + + # test instance + assert isinstance(ref_source, + ik.tektronix.tekdpo4104._TekDPO4104DataSource) + + # test for parent + assert ref_source._tek is inst + + +def test_data_source_math_initialize(): + """Initialize a ref data source.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + math_source = inst.math + + # test instance + assert isinstance(math_source, + ik.tektronix.tekdpo4104._TekDPO4104DataSource) + + # test for parent + assert math_source._tek is inst + + +def test_data_source_name(): + """Get the name of the data source.""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + assert inst.math.name == "MATH" + + +def test_data_source_equality_not_implemented(): + """Raise NotImplemented when comparing different types""" + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + ], + [ + ] + ) as inst: + assert inst.math.__eq__(42) == NotImplemented + + +@given(values=st.lists(st.integers(min_value=-32768, max_value=32767), + min_size=1), + ymult=st.integers(min_value=1, max_value=65536), + yzero=st.floats(min_value=-100, max_value=100), + xzero=st.floats(min_value=-10, max_value=10), + xincr=st.floats(min_value=1e-6, max_value=1) + ) +def test_data_source_read_waveform_bin(values, ymult, yzero, xzero, xincr): + """Read the waveform of a data trace in bin format.""" + old_dat_source = 3 + old_dat_stop = 100 # "previous" setting + # new values + channel = 0 + data_width = 2 # use format '>h' for decoding + yoffs = 0 # already tested with hypothesis + # values packing + ptcnt = len(values) + values_packed = b"".join(struct.pack(">h", value) for value in values) + values_len = str(len(values_packed)).encode() + values_len_of_len = str(len(values_len)).encode() + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "DAT:SOU?", # old data source + f"DAT:SOU CH{channel+1}", + "DAT:STOP?", + f"DAT:STOP {10**7}", + "DAT:ENC RIB", # set encoding + "DATA:WIDTH?", # query data width + "CURVE?", # get the data (in bin format) + "WFMP:YOF?", # query yoffs + "WFMP:YMU?", # query ymult + "WFMP:YZE?", # query yzero + "WFMP:XZE?", # query x zero + "WFMP:XIN?", # retrieve x increments + "WFMP:NR_P?", # retrieve number of points + f"DAT:STOP {old_dat_stop}", + f"DAT:SOU CH{old_dat_source + 1}" # set back old data source + ], + [ + f"CH{old_dat_source+1}", + f"{old_dat_stop}", + f"{data_width}", + b"#" + values_len_of_len + values_len + values_packed, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xzero}", + f"{xincr}", + f"{ptcnt}" + ], + ) as inst: + x_read, y_read = inst.channel[channel].read_waveform() + x_calc = np.arange(ptcnt) * xincr + xzero + y_calc = ((np.array(values) - yoffs) * ymult) + yzero + np.testing.assert_equal(x_read, x_calc) + np.testing.assert_equal(y_read, y_calc) + + +@given(values=st.lists(st.integers(min_value=-32768, max_value=32767), + min_size=1), + ymult=st.integers(min_value=1, max_value=65536), + yzero=st.floats(min_value=-100, max_value=100), + xzero=st.floats(min_value=-10, max_value=10), + xincr=st.floats(min_value=1e-9, max_value=1), + ) +def test_data_source_read_waveform_ascii(values, ymult, yzero, xzero, xincr): + """Read waveform back in ASCII format.""" + old_dat_source = 3 + old_dat_stop = 100 # "previous" setting + # new values + channel = 0 + yoffs = 0 # already tested with hypothesis + # transform values to strings + values_str = ",".join([str(value) for value in values]) + # calculated values + ptcnt = len(values) + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "DAT:SOU?", # old data source + f"DAT:SOU CH{channel + 1}", + "DAT:STOP?", + f"DAT:STOP {10**7}", + "DAT:ENC ASCI", # set encoding + "CURVE?", # get the data (in bin format) + "WFMP:YOF?", + "WFMP:YMU?", # query y-offset + "WFMP:YZE?", # query y zero + "WFMP:XZE?", # query x zero + "WFMP:XIN?", # retrieve x increments + "WFMP:NR_P?", # retrieve number of points + f"DAT:STOP {old_dat_stop}", + f"DAT:SOU CH{old_dat_source + 1}" # set back old data source + ], + [ + f"CH{old_dat_source + 1}", + f"{old_dat_stop}", + f"{values_str}", + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xzero}", + f"{xincr}", + f"{ptcnt}" + ] + ) as inst: + # get the values from the instrument + x_read, y_read = inst.channel[channel].read_waveform(bin_format=False) + # manually calculate the values + raw = np.array(values_str.split(","), dtype=np.float) + y_calc = (raw - yoffs) * ymult + yzero + x_calc = np.arange(ptcnt) * xincr + xzero + # assert arrays are equal + np.testing.assert_almost_equal(x_read, x_calc) + np.testing.assert_almost_equal(y_read, y_calc) + + +@given(offset=st.floats(min_value=-100, max_value=100)) +def test_data_source_y_offset_get(offset): + """Get y-offset from parent property.""" + old_dat_source = 2 + channel = 0 + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "DAT:SOU?", # old data source + f"DAT:SOU CH{channel + 1}", + "WFMP:YOF?", + f"DAT:SOU CH{old_dat_source + 1}" # set back old data source + ], + [ + f"CH{old_dat_source + 1}", + f"{offset}" + ] + ) as inst: + assert inst.channel[channel].y_offset == offset + + +@given(offset=st.floats(min_value=-100, max_value=100)) +def test_data_source_y_offset_set(offset): + """Set y-offset from parent property.""" + old_dat_source = 2 + channel = 0 + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "DAT:SOU?", # old data source + f"DAT:SOU CH{channel + 1}", + f"WFMP:YOF {offset}", + f"DAT:SOU CH{old_dat_source + 1}" # set back old data source + ], + [ + f"CH{old_dat_source + 1}", + ] + ) as inst: + inst.channel[channel].y_offset = offset + + +def test_data_source_y_offset_set_old_data_source_same(): + """Set y-offset from parent property, old data source same. + + Test one case of setting a data source where the old data source + and the new one is the same. Use y_offset for this test. + """ + offset = 0 + old_dat_source = 0 + channel = 0 + with expected_protocol( + ik.tektronix.TekDPO4104, + [ + "DAT:SOU?", # old data source + f"WFMP:YOF {offset}", + ], + [ + f"CH{old_dat_source + 1}", + ] + ) as inst: + inst.channel[channel].y_offset = offset From f10497d3f40f7858975e0a62c9da8161cf4a064f Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 8 Sep 2020 10:00:33 -0700 Subject: [PATCH 051/108] Test suite and BF for SRS CTC-100 (#257) * Test suite and BF for SRS CTC-100 Full coverage test suite for SRS CTC-100 Minor bug fix for `srsctc100.py`: `get_log_point` method in `Channel` subclass assigned a quantity to return values without converting them to numbers. Returned were thus unitful strings. While technically not a bug, this behavior does not fit together with the rest of this class or the general returns in ik Now floats are returned. * Wrapping return values in () instead of using \ --- instruments/srs/srsctc100.py | 3 +- instruments/tests/test_srs/test_srsctc100.py | 622 +++++++++++++++++++ 2 files changed, 624 insertions(+), 1 deletion(-) create mode 100644 instruments/tests/test_srs/test_srsctc100.py diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 0fa5fa50e..a3396ef06 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -235,7 +235,8 @@ def get_log_point(self, which='next', units=None): 'getLog.xy {}, {}'.format(self._chan_name, which) ).split(',') ] - return u.Quantity(point[0], 'ms'), u.Quantity(point[1], units) + return u.Quantity(float(point[0]), 'ms'), \ + u.Quantity(float(point[1]), units) def get_log(self): """ diff --git a/instruments/tests/test_srs/test_srsctc100.py b/instruments/tests/test_srs/test_srsctc100.py new file mode 100644 index 000000000..b11670eb7 --- /dev/null +++ b/instruments/tests/test_srs/test_srsctc100.py @@ -0,0 +1,622 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the SRS CTC-100 +""" + +# IMPORTS #################################################################### + +from hypothesis import ( + given, + strategies as st, +) +import pytest +import numpy as np + +import instruments.units as u + +import instruments as ik +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + + +# pylint: disable=protected-access + + +# SETUP # + + +# Create one channel name for every possible unit for parametrized testing +ch_units = list(ik.srs.SRSCTC100._UNIT_NAMES.keys()) +ch_names = [f"CH {it}" for it in range(len(ch_units))] +ch_name_unit_dict = dict(zip(ch_names, ch_units)) + + +# string that is returned when initializing channels: +ch_names_query = "getOutput.names?" +ch_names_str = ",".join(ch_names) + + +# CHANNELS # + + +@pytest.mark.parametrize("channel", ch_names) +def test_srsctc100_channel_init(channel): + """Initialize a channel.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query + ], + [ + ch_names_str + ] + ) as inst: + with inst._error_checking_disabled(): + ch = inst.channel[channel] + assert ch._ctc is inst + assert ch._chan_name == channel + assert ch._rem_name == channel.replace(" ", "") + + +def test_srsctc100_channel_name(): + """Get / set the channel name.""" + old_name = ch_names[0] + new_name = "New channel" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{old_name.replace(' ', '')}.name = \"{new_name}\"" + ], + [ + ch_names_str + ] + ) as inst: + with inst._error_checking_disabled(): + ch = inst.channel[ch_names[0]] + # assert old name is set + assert ch.name == ch_names[0] + # set a new name + ch.name = new_name + assert ch.name == new_name + assert ch._rem_name == new_name.replace(" ", "") + + +@pytest.mark.parametrize("channel", ch_names) +def test_srsctc100_channel_get(channel): + """Query a given channel. + + Ensure proper functionality for all available channels. + """ + cmd = "COMMAND" + answ = "ANSWER" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.{cmd}?" + ], + [ + ch_names_str, + answ + ] + ) as inst: + with inst._error_checking_disabled(): + assert inst.channel[channel]._get(cmd) == answ + + +@pytest.mark.parametrize("channel", ch_names) +def test_srsctc100_channel_set(channel): + """Send a command to a given channel. + + Ensure proper functionality for all available channels. + """ + cmd = "COMMAND" + newval = "NEWVAL" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.{cmd} = \"{newval}\"" + ], + [ + ch_names_str + ] + ) as inst: + with inst._error_checking_disabled(): + inst.channel[channel]._set(cmd, newval) + + +def test_srsctc100_channel_value(): + """Get value and unit from a given channel.""" + channel = ch_names[0] + unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] + value = 42 + + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.value?", + "getOutput.units?", + ch_names_query + ], + [ + ch_names_str, + f"{value}", + ",".join(ch_units), + ch_names_str, + ] + ) as inst: + with inst._error_checking_disabled(): + assert inst.channel[channel].value == u.Quantity(value, unit) + + +def test_srsctc100_channel_units_single(): + """Get unit for one given channel.""" + channel = ch_names[0] + unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + "getOutput.units?", + ch_names_query + ], + [ + ch_names_str, + ",".join(ch_units), + ch_names_str, + ] + ) as inst: + with inst._error_checking_disabled(): + ch = inst.channel[channel] + assert ch.units == unit + + +@pytest.mark.parametrize("sensor", ik.srs.SRSCTC100.SensorType) +def test_srsctc100_channel_sensor_type(sensor): + """Get type of sensor attached to specified channel.""" + channel = ch_names[0] + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.sensor?", + ], + [ + ch_names_str, + f"{sensor.value}" + ] + ) as inst: + with inst._error_checking_disabled(): + assert inst.channel[channel].sensor_type == sensor + + +@pytest.mark.parametrize("newval", (True, False)) +def test_srsctc100_channel_stats_enabled(newval): + """Get / set enabling statistics for specified channel.""" + channel = ch_names[0] + value_inst = "On" if newval else "Off" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.stats = \"{value_inst}\"", + f"{channel.replace(' ', '')}.stats?" + ], + [ + ch_names_str, + f"{value_inst}" + ] + ) as inst: + with inst._error_checking_disabled(): + ch = inst.channel[channel] + ch.stats_enabled = newval + assert ch.stats_enabled == newval + + +@given(points=st.integers(min_value=2, max_value=6000)) +def test_srsctc100_channel_stats_points(points): + """Get / set stats points in valid range.""" + channel = ch_names[0] + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.points = \"{points}\"", + f"{channel.replace(' ', '')}.points?" + ], + [ + ch_names_str, + f"{points}" + ] + ) as inst: + with inst._error_checking_disabled(): + ch = inst.channel[channel] + ch.stats_points = points + assert ch.stats_points == points + + +def test_srsctc100_channel_average(): + """Get average measurement for given channel, unitful.""" + channel = ch_names[0] + unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] + value = 42 + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.average?", + "getOutput.units?", + ch_names_query + ], + [ + ch_names_str, + f"{value}", + ",".join(ch_units), + ch_names_str, + ] + ) as inst: + with inst._error_checking_disabled(): + assert inst.channel[channel].average == u.Quantity(value, unit) + + +def test_srsctc100_channel_std_dev(): + """Get standard deviation for given channel, unitful.""" + channel = ch_names[0] + unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] + value = 42 + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.SD?", + "getOutput.units?", + ch_names_query + ], + [ + ch_names_str, + f"{value}", + ",".join(ch_units), + ch_names_str, + ] + ) as inst: + with inst._error_checking_disabled(): + assert inst.channel[channel].std_dev == u.Quantity(value, unit) + + +@pytest.mark.parametrize("channel", ch_names) +def test_get_log_point(channel): + """Get a log point and include a unit query.""" + channel = ch_names[0] + unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_name_unit_dict[channel]] + values = (13, 42) + which = "first" + values_out = (u.Quantity(float(values[0]), u.ms), + u.Quantity(float(values[1]), unit)) + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + "getOutput.units?", + ch_names_query, + f"getLog.xy {channel}, {which}" + ], + [ + ch_names_str, + ",".join(ch_units), + ch_names_str, + f"{values[0]},{values[1]}" + ] + ) as inst: + with inst._error_checking_disabled(): + assert (inst.channel[channel].get_log_point(which=which) == + values_out) + + +def test_get_log_point_with_unit(): + """Get a log point and include a unit query.""" + channel = ch_names[0] + unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] + values = (13, 42) + which = "first" + values_out = (u.Quantity(float(values[0]), u.ms), + u.Quantity(float(values[1]), unit)) + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + f"getLog.xy {channel}, {which}" + ], + [ + ch_names_str, + f"{values[0]},{values[1]}" + ] + ) as inst: + with inst._error_checking_disabled(): + assert (inst.channel[channel].get_log_point(which=which, + units=unit) == + values_out) + + +@pytest.mark.parametrize("channel", ch_names) +def test_channel_get_log(channel): + """Get the full log of a channel. + + Leave error checking activated, because it is run at the end. + """ + # make some data + times = [0, 1, 2, 3] + values = [1.3, 2.4, 3.5, 4.6] + + # variables + units = ik.srs.SRSCTC100._UNIT_NAMES[ch_name_unit_dict[channel]] + n_points = len(values) + + # strings for error checking, sending and receiving + err_check_send = "geterror?" + err_check_reci = "0,NO ERROR" + + # stich together strings to read all the values + str_log_next_send = "\n".join([f"getLog.xy {channel}, next" for + it in range(1, n_points)]) + str_log_next_reci = "\n".join([f"{times[it]},{values[it]}" for + it in range(1, n_points)]) + + # make data to compare with + ts = u.Quantity(np.empty((n_points,)), u.ms) + temps = u.Quantity(np.empty((n_points,)), units) + for it, time in enumerate(times): + ts[it] = u.Quantity(time, u.ms) + temps[it] = u.Quantity(values[it], units) + + + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query, + err_check_send, + "getOutput.units?", + err_check_send, + ch_names_query, + err_check_send, + f"getLog.xy? {channel}", + err_check_send, + f"getLog.xy {channel}, first", # query first point + str_log_next_send, + err_check_send + ], + [ + ch_names_str, + err_check_reci, + ",".join(ch_units), + err_check_reci, + ch_names_str, + err_check_reci, + f"{n_points}", + err_check_reci, + f"{times[0]},{values[0]}", + str_log_next_reci, + err_check_reci + ] + ) as inst: + ch = inst.channel[channel] + ts_read, temps_read = ch.get_log() + # assert the data is correct + np.testing.assert_equal(ts, ts_read) + np.testing.assert_equal(temps, temps_read) + + +# INSTRUMENT # + + +def test_srsctc100_init(): + """Initialize the SRS CTC-100 instrument.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ], + [ + ] + ) as inst: + assert inst._do_errcheck + + +def test_srsctc100_channel_names(): + """Get current channel names from instrument.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ch_names_query + ], + [ + ch_names_str + ] + ) as inst: + with inst._error_checking_disabled(): + assert inst._channel_names() == ch_names + + +def test_srsctc100_channel_units_all(): + """Get units for all channels.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + "getOutput.units?", + ch_names_query + ], + [ + ",".join(ch_units), + ch_names_str + ] + ) as inst: + with inst._error_checking_disabled(): + # create a unit dictionary to compare the return to + unit_dict = dict( + (chan_name, ik.srs.SRSCTC100._UNIT_NAMES[unit_str]) + for chan_name, unit_str in zip(ch_names, ch_units) + ) + assert inst.channel_units() == unit_dict + + +def test_srsctc100_errcheck(): + """Error check - no error returned.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + "geterror?" + ], + [ + "0,NO ERROR" + ] + ) as inst: + assert inst.errcheck() == 0 + + +def test_srsctc100_errcheck_error_raised(): + """Error check - error raises.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + "geterror?" + ], + [ + "42,THE ANSWER" + ] + ) as inst: + with pytest.raises(IOError) as exc_info: + inst.errcheck() + exc_msg = exc_info.value.args[0] + assert exc_msg == "THE ANSWER" + + +def test_srsctc100_error_checking_disabled_context(): + """Context dialogue to disable error checking.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ], + [ + ] + ) as inst: + # by default, error checking enabled + with inst._error_checking_disabled(): + assert not inst._do_errcheck + + # default enabled again + assert inst._do_errcheck + + +@given(figures=st.integers(min_value=0, max_value=6)) +def test_srsctc100_display_figures(figures): + """Get / set significant figures of display.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + f"system.display.figures = {figures}", + "system.display.figures?" + ], + [ + f"{figures}" + ] + ) as inst: + with inst._error_checking_disabled(): + inst.display_figures = figures + assert inst.display_figures == figures + + +@given(figures=st.integers().filter(lambda x: x < 0 or x > 6)) +def test_srsctc100_display_figures_value_error(figures): + """Raise ValueError when setting an invalid number of figures.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ], + [ + ] + ) as inst: + with inst._error_checking_disabled(): + with pytest.raises(ValueError) as exc_info: + inst.display_figures = figures + exc_msg = exc_info.value.args[0] + assert exc_msg == "Number of display figures must be an " \ + "integer from 0 to 6, inclusive." + + +@pytest.mark.parametrize("newval", (True, False)) +def test_srsctc100_error_check_toggle(newval): + """Get / set error check bool.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + ], + [ + ] + ) as inst: + inst.error_check_toggle = newval + assert inst.error_check_toggle == newval + + +def test_srsctc100_error_check_toggle_type_error(): + """Raise type error when error check toggle set with non-bool.""" + newval = 42 + with expected_protocol( + ik.srs.SRSCTC100, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError): + inst.error_check_toggle = newval + + +def test_srsctc100_sendcmd(): + """Send a command and error check.""" + cmd = "COMMAND" + with expected_protocol( + ik.srs.SRSCTC100, + [ + cmd, + "geterror?" + ], + [ + "0,NO ERROR" + ] + ) as inst: + inst.sendcmd("COMMAND") + + +def test_srsctc100_query(): + """Send a query and error check.""" + cmd = "COMMAND" + answ = "ANSWER" + with expected_protocol( + ik.srs.SRSCTC100, + [ + cmd, + "geterror?" + ], + [ + answ, + "0,NO ERROR" + ] + ) as inst: + assert inst.query("COMMAND") == answ + + +def test_srsctc100_clear_log(): + """Clear the log.""" + with expected_protocol( + ik.srs.SRSCTC100, + [ + "System.Log.Clear yes" + ], + [ + ] + ) as inst: + with inst._error_checking_disabled(): + inst.clear_log() From e2c9bfbddd029f27df548c0831de06dc20de572b Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 8 Sep 2020 11:12:12 -0700 Subject: [PATCH 052/108] Test suite and BFs for TekDPO70000 (#256) * Test suite and BFs for TekDPO70000 **Bug Fixes**: - Two properties were called as `unitful_property` instead of `enum_property`. As the "unit", an enum was given. Seems to be a copy&paste error. - The second of these properties also had the wrong enum specified. Swapped out for correct enum. - Double checked all fixes with manual. **Test suite**: - Full, and likely overkill, test suite added (needs discussion). - One test that is intentionally skipped added for binary waveform reading (needs discussion). - Full coverage not achieved. * Remove `if self._testing` to skip `time.sleep` in tests, replace w/ mock Also assert that time.sleep is called in the main routine, since it is required by the instrument. * Change TekDPO70000 to always read termination character after waveform The loopback communicator used in testing does not flush the input, thus a termination character is stuck in the queue that does not get read. According to the manual, a `` character is always sent after transmitting the waveform. Flushing the input is now simplified and includes a `read_raw` for one character. This takes care of proper testing. Tests were adjusted accordingly. * Removed unnecessary `@pytest.mark.parametrize` statements for channels Testing of all channels is not necessary for property factories and should be shifted to their tests. Test suites still checks that `sendcmd` and `query` are working properly for all parametrized scenarios. --- instruments/tektronix/tekdpo70000.py | 17 +- .../tests/test_tektronix/test_tekdpo70000.py | 1927 +++++++++++++++++ 2 files changed, 1933 insertions(+), 11 deletions(-) create mode 100644 instruments/tests/test_tektronix/test_tekdpo70000.py diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index f9a8bfd5b..a832bd361 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -190,12 +190,8 @@ def read_waveform(self, bin_format=True): ) self._parent.sendcmd("CURV?") raw = self._parent.binblockread(n_bytes, fmt=dtype) - # Clear the queue by trying to read. - # FIXME: this is a hack-y way of doing so. - if hasattr(self._parent._file, 'flush_input'): - self._parent._file.flush_input() - else: - self._parent._file.read() + # Clear the queue by reading the end of line character + self._parent._file.read_raw(1) return self._scale_raw_data(raw) @@ -372,7 +368,7 @@ class SpectralWindow(Enum): inst_false="OFF" ) - spectral_mag = unitful_property( + spectral_mag = enum_property( "SPEC:MAG", Mag, doc=""" @@ -380,9 +376,9 @@ class SpectralWindow(Enum): """ ) - spectral_phase = unitful_property( + spectral_phase = enum_property( "SPEC:PHASE", - Mag, + Phase, doc=""" Whether the spectral phase is degrees, radians, or group delay. """ @@ -799,8 +795,7 @@ def data_source(self, newval): # Some Tek scopes require this after the DAT:SOU command, or else # they will stop responding. - if not self._testing: - time.sleep(0.02) + time.sleep(0.02) horiz_acq_duration = unitful_property( 'HOR:ACQDURATION', diff --git a/instruments/tests/test_tektronix/test_tekdpo70000.py b/instruments/tests/test_tektronix/test_tekdpo70000.py new file mode 100644 index 000000000..77a84ae19 --- /dev/null +++ b/instruments/tests/test_tektronix/test_tekdpo70000.py @@ -0,0 +1,1927 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Tests for the Tektronix DPO 70000 oscilloscope. +""" + +# IMPORTS ##################################################################### + +import struct +import time + +from hypothesis import ( + given, + strategies as st, +) +import numpy as np +import pytest + +import instruments as ik +from instruments import units as u +from instruments.tests import expected_protocol, make_name_test + + +# TESTS ####################################################################### + +# pylint: disable=too-many-lines,protected-access + + +test_tekdpo70000_name = make_name_test(ik.tektronix.TekDPO70000) + + +# STATIC METHOD # + + +@pytest.mark.parametrize("binary_format", + ik.tektronix.TekDPO70000.BinaryFormat) +@pytest.mark.parametrize("byte_order", + ik.tektronix.TekDPO70000.ByteOrder) +@pytest.mark.parametrize("n_bytes", (1, 2, 4, 8)) +def test_dtype(binary_format, byte_order, n_bytes): + """Return the formatted format name, depending on settings.""" + binary_format_dict = { + ik.tektronix.TekDPO70000.BinaryFormat.int: "i", + ik.tektronix.TekDPO70000.BinaryFormat.uint: "u", + ik.tektronix.TekDPO70000.BinaryFormat.float: "f" + } + byte_order_dict = { + ik.tektronix.TekDPO70000.ByteOrder.big_endian: ">", + ik.tektronix.TekDPO70000.ByteOrder.little_endian: "<" + } + value_expected = f"{byte_order_dict[byte_order]}" \ + f"{binary_format_dict[binary_format]}" \ + f"{n_bytes}" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ], + [ + ] + ) as inst: + assert inst._dtype(binary_format, byte_order, n_bytes) == \ + value_expected + + +# DATA SOURCE - TESTED WITH CHANNELS # + + +def test_data_source_name(): + """Query the name of a data source.""" + channel = 0 + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ], + [ + ] + ) as inst: + assert inst.channel[channel].name == f"CH{channel+1}" + + +@pytest.mark.parametrize("channel", [it for it in range(4)]) +@given(values=st.lists(st.integers(min_value=-2147483648, + max_value=2147483647), min_size=1)) +def test_data_source_read_waveform(channel, values): + """Read waveform from data source, binary format only!""" + # select one set to test for: + binary_format = ik.tektronix.TekDPO70000.BinaryFormat.int # go w/ values + byte_order = ik.tektronix.TekDPO70000.ByteOrder.big_endian + n_bytes = 4 + # get the dtype + dtype_set = ik.tektronix.TekDPO70000._dtype(binary_format, byte_order, + n_bytes) + + # pack the values + values_packed = b"".join(struct.pack(dtype_set[:-1], + value) for value in values) + values_len = str(len(values_packed)).encode() + values_len_of_len = str(len(values_len)).encode() + values = np.array(values) + # scale the values + scale = 1. + position = 0. + offset = 0. + scaled_values = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * + values.astype(float) / (2**15) - position + ) + offset + + # run through the instrument + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "DAT:SOU?", # query data source + "DAT:ENC FAS", # fastest encoding + "WFMO:BYT_N?", # get n_bytes + "WFMO:BN_F?", # outgoing binary format + "WFMO:BYT_O?", # outgoing byte order + "CURV?", # query data + f"CH{channel + 1}:SCALE?", # scale raw data + f"CH{channel + 1}:POS?", + f"CH{channel + 1}:OFFS?" + + ], + [ + f"CH{channel+1}", + f"{n_bytes}", + f"{binary_format.value}", + f"{byte_order.value}", + b"#" + values_len_of_len + values_len + values_packed, + f"{scale}", + f"{position}", + f"{offset}" + ] + ) as inst: + # query waveform + scaled_raw = inst.channel[channel].read_waveform() + np.testing.assert_equal(scaled_raw, scaled_values) + + +def test_data_source_read_waveform_with_old_data_source(): + """Read waveform from data, old data source present!""" + channel = 0 # multiple channels already tested above + # select one set to test for: + binary_format = ik.tektronix.TekDPO70000.BinaryFormat.int # go w/ values + byte_order = ik.tektronix.TekDPO70000.ByteOrder.big_endian + n_bytes = 4 + # get the dtype + dtype_set = ik.tektronix.TekDPO70000._dtype(binary_format, byte_order, + n_bytes) + + # pack the values + values = np.arange(10) + values_packed = b"".join(struct.pack(dtype_set[:-1], + value) for value in values) + values_len = str(len(values_packed)).encode() + values_len_of_len = str(len(values_len)).encode() + # scale the values + scale = 1. + position = 0. + offset = 0. + scaled_values = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * + values.astype(float) / (2**15) - position + ) + offset + + # old data source to set manually - ensure it is set back later + old_dsrc = "MATH1" + + # run through the instrument + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "DAT:SOU?", # query data source + f"DAT:SOU CH{channel + 1}", # set current data source + "DAT:ENC FAS", # fastest encoding + "WFMO:BYT_N?", # get n_bytes + "WFMO:BN_F?", # outgoing binary format + "WFMO:BYT_O?", # outgoing byte order + "CURV?", # query data + f"CH{channel + 1}:SCALE?", # scale raw data + f"CH{channel + 1}:POS?", + f"CH{channel + 1}:OFFS?", + f"DAT:SOU {old_dsrc}" + ], + [ + old_dsrc, + f"{n_bytes}", + f"{binary_format.value}", + f"{byte_order.value}", + b"#" + values_len_of_len + values_len + values_packed, + f"{scale}", + f"{position}", + f"{offset}" + ] + ) as inst: + # query waveform + scaled_raw = inst.channel[channel].read_waveform() + np.testing.assert_equal(scaled_raw, scaled_values) + + +# MATH # + + +@pytest.mark.parametrize("math", [it for it in range(4)]) +def test_math_init(math): + """Initialize a math channel.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ], + [ + ] + ) as inst: + assert inst.math[math]._parent is inst + assert inst.math[math]._idx == math + 1 + + +@pytest.mark.parametrize("math", [it for it in range(4)]) +def test_math_sendcmd(math): + """Send a command from a math channel.""" + cmd = "TEST" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math+1}:{cmd}" + ], + [ + ] + ) as inst: + inst.math[math].sendcmd(cmd) + + +@pytest.mark.parametrize("math", [it for it in range(4)]) +def test_math_query(math): + """Query from a math channel.""" + cmd = "TEST" + answ = "ANSWER" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math+1}:{cmd}" + ], + [ + answ + ] + ) as inst: + assert inst.math[math].query(cmd) == answ + + +@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", + blacklist_categories=('Cs',)))) +def test_math_define(value): + """Get / set a string operation from the Math mode.""" + math = 0 + cmd = "DEF" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math+1}:{cmd} \"{value}\"", + f"MATH{math+1}:{cmd}?" + ], + [ + f"\"{value}\"" + ] + ) as inst: + inst.math[math].define = value + assert inst.math[math].define == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.Math.FilterMode) +def test_math_filter_mode(value): + """Get / set filter mode.""" + math = 0 + cmd = "FILT:MOD" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value.value}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.math[math].filter_mode = value + assert inst.math[math].filter_mode == value + + +@given(value=st.floats(min_value=0)) +def test_math_filter_risetime(value): + """Get / set filter risetime.""" + math = 0 + cmd = "FILT:RIS" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].filter_risetime = value + inst.math[math].filter_risetime = value_unitful + assert inst.math[math].filter_risetime == pytest.approx(value_unitful) + + +@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", + blacklist_categories=('Cs',)))) +def test_math_label(value): + """Get / set a label for the math channel.""" + math = 0 + cmd = "LAB:NAM" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math+1}:{cmd} \"{value}\"", + f"MATH{math+1}:{cmd}?" + ], + [ + f"\"{value}\"" + ] + ) as inst: + inst.math[math].label = value + assert inst.math[math].label == value + + +@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.HOR_DIVS, + max_value=ik.tektronix.TekDPO70000.HOR_DIVS)) +def test_math_label_xpos(value): + """Get / set x position for label.""" + math = 0 + cmd = "LAB:XPOS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].label_xpos = value + assert inst.math[math].label_xpos == value + + +@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS)) +def test_math_label_ypos(value): + """Get / set y position for label.""" + math = 0 + cmd = "LAB:YPOS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].label_ypos = value + assert inst.math[math].label_ypos == value + + +@given(value=st.integers(min_value=0)) +def test_math_num_avg(value): + """Get / set number of averages.""" + math = 0 + cmd = "NUMAV" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].num_avg = value + assert inst.math[math].num_avg == pytest.approx(value) + + +@given(value=st.floats(min_value=0)) +def test_math_spectral_center(value): + """Get / set spectral center.""" + math = 0 + cmd = "SPEC:CENTER" + value_unitful = u.Quantity(value, u.Hz) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_center = value + inst.math[math].spectral_center = value_unitful + assert inst.math[math].spectral_center == pytest.approx(value_unitful) + + +@given(value=st.floats(allow_nan=False)) +def test_math_spectral_gatepos(value): + """Get / set gate position.""" + math = 0 + cmd = "SPEC:GATEPOS" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_gatepos = value + inst.math[math].spectral_gatepos = value_unitful + assert inst.math[math].spectral_gatepos == pytest.approx(value_unitful) + + +@given(value=st.floats(allow_nan=False)) +def test_math_spectral_gatewidth(value): + """Get / set gate width.""" + math = 0 + cmd = "SPEC:GATEWIDTH" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_gatewidth = value + inst.math[math].spectral_gatewidth = value_unitful + assert inst.math[math].spectral_gatewidth == pytest.approx(value_unitful) + + +@pytest.mark.parametrize("value", [True, False]) +def test_math_spectral_lock(value): + """Get / set spectral lock.""" + math = 0 + cmd = "SPEC:LOCK" + value_io = "ON" if value else "OFF" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value_io}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.math[math].spectral_lock = value + assert inst.math[math].spectral_lock == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.Math.Mag) +def test_math_spectral_mag(value): + """Get / set spectral magnitude scaling.""" + math = 0 + cmd = "SPEC:MAG" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value.value}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.math[math].spectral_mag = value + assert inst.math[math].spectral_mag == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.Math.Phase) +def test_math_spectral_phase(value): + """Get / set spectral phase unit.""" + math = 0 + cmd = "SPEC:PHASE" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value.value}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.math[math].spectral_phase = value + assert inst.math[math].spectral_phase == value + + +@given(value=st.floats(allow_nan=False)) +def test_math_spectral_reflevel(value): + """Get / set spectral reference level.""" + math = 0 + cmd = "SPEC:REFL" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_reflevel = value + assert inst.math[math].spectral_reflevel == value + + +@given(value=st.floats(allow_nan=False)) +def test_math_spectral_reflevel_offset(value): + """Get / set spectral reference level offset.""" + math = 0 + cmd = "SPEC:REFLEVELO" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_reflevel_offset = value + assert inst.math[math].spectral_reflevel_offset == value + + +@given(value=st.floats(min_value=0)) +def test_math_spectral_resolution_bandwidth(value): + """Get / set spectral resolution bandwidth.""" + math = 0 + cmd = "SPEC:RESB" + value_unitful = u.Quantity(value, u.Hz) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_resolution_bandwidth = value + inst.math[math].spectral_resolution_bandwidth = value_unitful + assert inst.math[math].spectral_resolution_bandwidth == \ + pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=0)) +def test_math_spectral_span(value): + """Get / set frequency span of output data vector.""" + math = 0 + cmd = "SPEC:SPAN" + value_unitful = u.Quantity(value, u.Hz) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_span = value + inst.math[math].spectral_span = value_unitful + assert inst.math[math].spectral_span == pytest.approx(value_unitful) + + +@given(value=st.floats(allow_nan=False)) +def test_math_spectral_suppress(value): + """Get / set spectral suppression value.""" + math = 0 + cmd = "SPEC:SUPP" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].spectral_suppress = value + assert inst.math[math].spectral_suppress == value + + +@pytest.mark.parametrize("value", [True, False]) +def test_math_spectral_unwrap(value): + """Get / set phase wrapping.""" + math = 0 + cmd = "SPEC:UNWR" + value_io = "ON" if value else "OFF" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value_io}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.math[math].spectral_unwrap = value + assert inst.math[math].spectral_unwrap == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.Math.SpectralWindow) +def test_math_spectral_window(value): + """Get / set spectral window.""" + math = 0 + cmd = "SPEC:WIN" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value.value}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.math[math].spectral_window = value + assert inst.math[math].spectral_window == value + + +@given(value=st.floats(min_value=0)) +def test_math_threshold(value): + """Get / set threshold of math channel.""" + math = 0 + cmd = "THRESH" + value_unitful = u.Quantity(value, u.V) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].threshhold = value + inst.math[math].threshhold = value_unitful + assert inst.math[math].threshhold == pytest.approx(value_unitful) + + +@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", + blacklist_categories=('Cs',)))) +def test_math_units(value): + """Get / set a label for the units.""" + math = 0 + cmd = "UNITS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math+1}:{cmd} \"{value}\"", + f"MATH{math+1}:{cmd}?" + ], + [ + f"\"{value}\"" + ] + ) as inst: + inst.math[math].unit_string = value + assert inst.math[math].unit_string == value + + +@pytest.mark.parametrize("value", [True, False]) +def test_math_autoscale(value): + """Get / set if autoscale is enabled.""" + math = 0 + cmd = "VERT:AUTOSC" + value_io = "ON" if value else "OFF" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value_io}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.math[math].autoscale = value + assert inst.math[math].autoscale == value + + +@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS / 2, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS / 2)) +def test_math_position(value): + """Get / set spectral vertical position from center.""" + math = 0 + cmd = "VERT:POS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].position = value + assert inst.math[math].position == value + + +@given(value=st.floats(min_value=0)) +def test_math_scale(value): + """Get / set scale in volts per division.""" + math = 0 + cmd = "VERT:SCALE" + value_unitful = u.Quantity(value, u.V) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.math[math].scale = value + inst.math[math].scale = value_unitful + assert inst.math[math].scale == pytest.approx(value_unitful) + + +@given(value=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), + min_size=1)) +def test_math_scale_raw_data(value): + """Return scaled raw data according to current settings.""" + math = 0 + scale = 1. + position = -2.3 + value = np.array(value) + expected_value = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * + value.astype(float) / (2**15) - position) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:VERT:SCALE?", + f"MATH{math + 1}:VERT:POS?" + ], + [ + f"{scale}", + f"{position}" + ] + ) as inst: + np.testing.assert_equal(inst.math[math]._scale_raw_data(value), + expected_value) + + +# CHANNEL # + + +@pytest.mark.parametrize("channel", [it for it in range(4)]) +def test_channel_init(channel): + """Initialize a channel.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ], + [ + ] + ) as inst: + assert inst.channel[channel]._parent is inst + assert inst.channel[channel]._idx == channel + 1 + + +@pytest.mark.parametrize("channel", [it for it in range(4)]) +def test_channel_sendcmd(channel): + """Send a command from a channel.""" + cmd = "TEST" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd}" + ], + [ + ] + ) as inst: + inst.channel[channel].sendcmd(cmd) + + +@pytest.mark.parametrize("channel", [it for it in range(4)]) +def test_channel_query(channel): + """Send a query from a channel.""" + cmd = "TEST" + answ = "ANSWER" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd}" + ], + [ + answ + ] + ) as inst: + assert inst.channel[channel].query(cmd) == answ + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.Channel.Coupling) +def test_channel_coupling(value): + """Get / set channel coupling.""" + channel = 0 + cmd = "COUP" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd} {value.value}", + f"CH{channel+1}:{cmd}?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.channel[channel].coupling = value + assert inst.channel[channel].coupling == value + + +@given(value=st.floats(min_value=0, max_value=30e9)) +def test_channel_bandwidth(value): + """Get / set bandwidth of a channel. + + Test unitful and unitless setting. + """ + channel = 0 + cmd = "BAN" + value_unitful = u.Quantity(value, u.Hz) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].bandwidth = value + inst.channel[channel].bandwidth = value_unitful + assert inst.channel[channel].bandwidth == pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=-25e-9, max_value=25e-9)) +def test_channel_deskew(value): + """Get / set deskew time. + + Test unitful and unitless setting. + """ + channel = 0 + cmd = "DESK" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].deskew = value + inst.channel[channel].deskew = value_unitful + assert inst.channel[channel].deskew == pytest.approx(value_unitful) + + +@pytest.mark.parametrize("value", [50, 1000000]) +def test_channel_termination(value): + """Get / set termination of channel. + + Valid values are 50 Ohm or 1 MOhm. Try setting unitful and + unitless. + """ + channel = 0 + cmd = "TERM" + value_unitful = u.Quantity(value, u.Ohm) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].termination = value + inst.channel[channel].termination = value_unitful + assert inst.channel[channel].termination == \ + pytest.approx(value_unitful) + + +@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", + blacklist_categories=('Cs',)))) +def test_channel_label(value): + """Get / set human readable label for channel.""" + channel = 0 + cmd = "LAB:NAM" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd} \"{value}\"", + f"CH{channel+1}:{cmd}?" + ], + [ + f"\"{value}\"" + ] + ) as inst: + inst.channel[channel].label = value + assert inst.channel[channel].label == value + + +@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.HOR_DIVS, + max_value=ik.tektronix.TekDPO70000.HOR_DIVS)) +def test_channel_label_xpos(value): + """Get / set x position for label.""" + channel = 0 + cmd = "LAB:XPOS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd} {value:e}", + f"CH{channel+1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].label_xpos = value + assert inst.channel[channel].label_xpos == value + + +@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS)) +def test_channel_label_ypos(value): + """Get / set y position for label.""" + channel = 0 + cmd = "LAB:YPOS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd} {value:e}", + f"CH{channel+1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].label_ypos = value + assert inst.channel[channel].label_ypos == value + + +@given(value=st.floats(allow_nan=False)) +def test_channel_offset(value): + """Get / set offset, unitful in V and unitless.""" + channel = 0 + cmd = "OFFS" + value_unitful = u.Quantity(value, u.V) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].offset = value + inst.channel[channel].offset = value_unitful + assert inst.channel[channel].offset == pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS)) +def test_channel_position(value): + """Get / set vertical position.""" + channel = 0 + cmd = "POS" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel+1}:{cmd} {value:e}", + f"CH{channel+1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].position = value + assert inst.channel[channel].position == value + + +@given(value=st.floats(min_value=0)) +def test_channel_scale(value): + """Get / set scale.""" + channel = 0 + cmd = "SCALE" + value_unitful = u.Quantity(value, u.V) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?" + ], + [ + f"{value}" + ] + ) as inst: + inst.channel[channel].scale = value + inst.channel[channel].scale = value_unitful + assert inst.channel[channel].scale == pytest.approx(value_unitful) + + +@given(value=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), + min_size=1)) +def test_channel_scale_raw_data(value): + """Return scaled raw data according to current settings.""" + channel = 0 + scale = u.Quantity(1., u.V) + position = -1. + offset = u.Quantity(0., u.V) + value = np.array(value) + expected_value = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * + value.astype(float) / (2**15) - + position) + offset + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:SCALE?", + f"CH{channel + 1}:POS?", + f"CH{channel + 1}:OFFS?" + ], + [ + f"{scale}", + f"{position}", + f"{offset}" + ] + ) as inst: + np.testing.assert_equal(inst.channel[channel]._scale_raw_data(value), + expected_value) + + +# INSTRUMENT # + + +@pytest.mark.parametrize("value", ["AUTO", "OFF"]) +def test_acquire_enhanced_enob(value): + """Get / set enhanced effective number of bits.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:ENHANCEDE {value}", + "ACQ:ENHANCEDE?" + ], + [ + f"{value}" + ] + ) as inst: + inst.acquire_enhanced_enob = value + assert inst.acquire_enhanced_enob == value + + +@pytest.mark.parametrize("value", [True, False]) +def test_acquire_enhanced_state(value): + """Get / set state of enhanced effective number of bits.""" + value_io = "1" if value else "0" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:ENHANCEDE:STATE {value_io}", + "ACQ:ENHANCEDE:STATE?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.acquire_enhanced_state = value + assert inst.acquire_enhanced_state == value + + +@pytest.mark.parametrize("value", ["AUTO", "ON", "OFF"]) +def test_acquire_interp_8bit(value): + """Get / set interpolation method of instrument.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:INTERPE {value}", + "ACQ:INTERPE?" + ], + [ + f"{value}" + ] + ) as inst: + inst.acquire_interp_8bit = value + assert inst.acquire_interp_8bit == value + + +@pytest.mark.parametrize("value", [True, False]) +def test_acquire_magnivu(value): + """Get / set MagniVu feature.""" + value_io = "ON" if value else "OFF" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:MAG {value_io}", + "ACQ:MAG?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.acquire_magnivu = value + assert inst.acquire_magnivu == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.AcquisitionMode) +def test_acquire_mode(value): + """Get / set acquisition mode.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:MOD {value.value}", + "ACQ:MOD?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.acquire_mode = value + assert inst.acquire_mode == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.AcquisitionMode) +def test_acquire_mode_actual(value): + """Get actually used acquisition mode (query only).""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "ACQ:MOD:ACT?" + ], + [ + f"{value.value}" + ] + ) as inst: + assert inst.acquire_mode_actual == value + + +@given(value=st.integers(min_value=0, max_value=2**30-1)) +def test_acquire_num_acquisitions(value): + """Get number of waveform acquisitions since start (query only).""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "ACQ:NUMAC?" + ], + [ + f"{value}" + ] + ) as inst: + assert inst.acquire_num_acquisitions == value + + +@given(value=st.integers(min_value=0)) +def test_acquire_num_avgs(value): + """Get / set number of waveform acquisitions to average.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:NUMAV {value}", + "ACQ:NUMAV?" + ], + [ + f"{value}" + ] + ) as inst: + inst.acquire_num_avgs = value + assert inst.acquire_num_avgs == value + + +@given(value=st.integers(min_value=0)) +def test_acquire_num_envelop(value): + """Get / set number of waveform acquisitions to envelope.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:NUME {value}", + "ACQ:NUME?" + ], + [ + f"{value}" + ] + ) as inst: + inst.acquire_num_envelop = value + assert inst.acquire_num_envelop == value + + +@given(value=st.integers(min_value=0)) +def test_acquire_num_frames(value): + """Get / set number of frames in FastFrame Single Sequence mode. + + Query only. + """ + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "ACQ:NUMFRAMESACQ?" + ], + [ + f"{value}" + ] + ) as inst: + assert inst.acquire_num_frames == value + + +@given(value=st.integers(min_value=5000, max_value=2147400000)) +def test_acquire_num_samples(value): + """Get / set number of acquired samples to make up waveform database.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:NUMSAM {value}", + "ACQ:NUMSAM?" + ], + [ + f"{value}" + ] + ) as inst: + inst.acquire_num_samples = value + assert inst.acquire_num_samples == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.SamplingMode) +def test_acquire_sampling_mode(value): + """Get / set sampling mode.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:SAMP {value.value}", + "ACQ:SAMP?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.acquire_sampling_mode = value + assert inst.acquire_sampling_mode == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.AcquisitionState) +def test_acquire_state(value): + """Get / set acquisition state.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:STATE {value.value}", + "ACQ:STATE?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.acquire_state = value + assert inst.acquire_state == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.StopAfter) +def test_acquire_stop_after(value): + """Get / set whether acquisition is continuous.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"ACQ:STOPA {value.value}", + "ACQ:STOPA?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.acquire_stop_after = value + assert inst.acquire_stop_after == value + + +@given(value=st.integers(min_value=0)) +def test_data_framestart(value): + """Get / set start frame for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:FRAMESTAR {value}", + "DAT:FRAMESTAR?" + ], + [ + f"{value}" + ] + ) as inst: + inst.data_framestart = value + assert inst.data_framestart == value + + +@given(value=st.integers(min_value=0)) +def test_data_framestop(value): + """Get / set stop frame for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:FRAMESTOP {value}", + "DAT:FRAMESTOP?" + ], + [ + f"{value}" + ] + ) as inst: + inst.data_framestop = value + assert inst.data_framestop == value + + +@given(value=st.integers(min_value=0)) +def test_data_start(value): + """Get / set start data point for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:STAR {value}", + "DAT:STAR?" + ], + [ + f"{value}" + ] + ) as inst: + inst.data_start = value + assert inst.data_start == value + + +@given(value=st.integers(min_value=0)) +def test_data_stop(value): + """Get / set stop data point for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:STOP {value}", + "DAT:STOP?" + ], + [ + f"{value}" + ] + ) as inst: + inst.data_stop = value + assert inst.data_stop == value + + +@pytest.mark.parametrize("value", [True, False]) +def test_data_sync_sources(value): + """Get / set if data sync sources are on or off.""" + value_io = "ON" if value else "OFF" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:SYNCSOU {value_io}", + "DAT:SYNCSOU?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.data_sync_sources = value + assert inst.data_sync_sources == value + + +valid_channel_range = [it for it in range(4)] + + +@pytest.mark.parametrize("no", valid_channel_range) +def test_data_source_channel(no): + """Get / set channel as data source.""" + channel_name = f"CH{no + 1}" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:SOU {channel_name}", + f"DAT:SOU?" + ], + [ + channel_name + ] + ) as inst: + channel = inst.channel[no] + inst.data_source = channel + assert inst.data_source == channel + + +valid_math_range = [it for it in range(4)] + + +@pytest.mark.parametrize("no", valid_math_range) +def test_data_source_math(no, mocker): + """Get / set math as data source.""" + math_name = f"MATH{no + 1}" + + # patch call to time.sleep with mock + mock_time = mocker.patch.object(time, 'sleep', return_value=None) + + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:SOU {math_name}", + f"DAT:SOU?" + ], + [ + math_name + ] + ) as inst: + math = inst.math[no] + inst.data_source = math + assert inst.data_source == math + + # assert that time.sleep has been called + mock_time.assert_called() + + +def test_data_source_ref_not_implemented_error(): + """Get / set a reference channel raises a NotImplemented error.""" + ref_name = "REF1" # example, range not important + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:SOU?" + ], + [ + ref_name + ] + ) as inst: + # getter + with pytest.raises(NotImplementedError): + print(inst.data_source) + # setter + with pytest.raises(NotImplementedError): + inst.data_source = inst.ref[0] + + +def test_data_source_not_implemented_error(): + """Get a data source that is currently not implemented.""" + ds_name = "HHG29" # example, range not important + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"DAT:SOU?" + ], + [ + ds_name + ] + ) as inst: + with pytest.raises(NotImplementedError): + print(inst.data_source) + + +def test_data_source_invalid_type(): + """Raise TypeError when a wrong type is set for data source.""" + invalid_data_source = 42 + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as exc_info: + inst.data_source = invalid_data_source + exc_msg = exc_info.value.args[0] + assert exc_msg == f"{type(invalid_data_source)} is not a valid data " \ + f"source." + + +@given(value=st.floats(min_value=0, max_value=1000)) +def test_horiz_acq_duration(value): + """Get horizontal acquisition duration (query only).""" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "HOR:ACQDURATION?" + ], + [ + f"{value}" + ] + ) as inst: + assert inst.horiz_acq_duration == pytest.approx(value_unitful) + + +@given(value=st.integers(min_value=0)) +def test_horiz_acq_length(value): + """Get horizontal acquisition length (query only).""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "HOR:ACQLENGTH?" + ], + [ + f"{value}" + ] + ) as inst: + assert inst.horiz_acq_length == value + + +@pytest.mark.parametrize("value", [True, False]) +def test_horiz_delay_mode(value): + """Get / set state of horizontal delay mode.""" + value_io = "1" if value else "0" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:DEL:MOD {value_io}", + "HOR:DEL:MOD?" + ], + [ + f"{value_io}" + ] + ) as inst: + inst.horiz_delay_mode = value + assert inst.horiz_delay_mode == value + + +@given(value=st.floats(min_value=0, max_value=100)) +def test_horiz_delay_pos(value): + """Get / set horizontal time base if delay mode is on. + + Test setting unitful and without units.""" + value_unitful = u.Quantity(value, u.percent) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:DEL:POS {value:e}", + f"HOR:DEL:POS {value:e}", + "HOR:DEL:POS?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_delay_pos = value + inst.horiz_delay_pos = value_unitful + assert inst.horiz_delay_pos == pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=0)) +def test_horiz_delay_time(value): + """Get / set horizontal delay time.""" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:DEL:TIM {value:e}", + f"HOR:DEL:TIM {value:e}", + "HOR:DEL:TIM?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_delay_time = value + inst.horiz_delay_time = value_unitful + assert inst.horiz_delay_time == pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=0)) +def test_horiz_interp_ratio(value): + """Get horizontal interpolation ratio (query only).""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "HOR:MAI:INTERPR?" + ], + [ + f"{value}" + ] + ) as inst: + assert inst.horiz_interp_ratio == value + + +@given(value=st.floats(min_value=0)) +def test_horiz_main_pos(value): + """Get / set horizontal main position. + + Test setting unitful and without units.""" + value_unitful = u.Quantity(value, u.percent) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MAI:POS {value:e}", + f"HOR:MAI:POS {value:e}", + "HOR:MAI:POS?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_main_pos = value + inst.horiz_main_pos = value_unitful + assert inst.horiz_main_pos == pytest.approx(value_unitful) + + +def test_horiz_unit(): + """Get / set horizontal unit string.""" + unit_string = "LUM" # as example in manual + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MAI:UNI \"{unit_string}\"", + "HOR:MAI:UNI?" + ], + [ + f"\"{unit_string}\"" + ] + ) as inst: + inst.horiz_unit = unit_string + assert inst.horiz_unit == unit_string + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.HorizontalMode) +def test_horiz_mode(value): + """Get / set horizontal mode.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MODE {value.value}", + "HOR:MODE?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.horiz_mode = value + assert inst.horiz_mode == value + + +@given(value=st.integers(min_value=0)) +def test_horiz_record_length_lim(value): + """Get / set horizontal record length limit.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MODE:AUTO:LIMIT {value}", + "HOR:MODE:AUTO:LIMIT?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_record_length_lim = value + assert inst.horiz_record_length_lim == value + + +@given(value=st.integers(min_value=0)) +def test_horiz_record_length(value): + """Get / set horizontal record length.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MODE:RECO {value}", + "HOR:MODE:RECO?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_record_length = value + assert inst.horiz_record_length == value + + +@given(value=st.floats(min_value=0, max_value=30e9)) +def test_horiz_sample_rate(value): + """Get / set horizontal sampling rate. + + Set with and without units.""" + value_unitful = u.Quantity(value, u.Hz) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MODE:SAMPLER {value:e}", + f"HOR:MODE:SAMPLER {value:e}", + f"HOR:MODE:SAMPLER?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_sample_rate = value_unitful + inst.horiz_sample_rate = value + assert inst.horiz_sample_rate == pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=0)) +def test_horiz_scale(value): + """Get / set horizontal scale in seconds per division. + + Set with and without units.""" + value_unitful = u.Quantity(value, u.s) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:MODE:SCA {value:e}", + f"HOR:MODE:SCA {value:e}", + f"HOR:MODE:SCA?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_scale = value_unitful + inst.horiz_scale = value + assert inst.horiz_scale == pytest.approx(value_unitful) + + +@given(value=st.floats(min_value=0)) +def test_horiz_pos(value): + """Get / set position of trigger point on the screen. + + Set with and without units. + """ + value_unitful = u.Quantity(value, u.percent) + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:POS {value:e}", + f"HOR:POS {value:e}", + f"HOR:POS?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_pos = value_unitful + inst.horiz_pos = value + assert inst.horiz_pos == pytest.approx(value_unitful) + + +@pytest.mark.parametrize("value", ["AUTO", "OFF", "ON"]) +def test_horiz_roll(value): + """Get / set roll mode status.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"HOR:ROLL {value}", + f"HOR:ROLL?" + ], + [ + f"{value}" + ] + ) as inst: + inst.horiz_roll = value + assert inst.horiz_roll == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.TriggerState) +def test_trigger_state(value): + """Get / set the trigger state.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"TRIG:STATE {value.value}", + "TRIG:STATE?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.trigger_state = value + assert inst.trigger_state == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.WaveformEncoding) +def test_outgoing_waveform_encoding(value): + """Get / set the encoding used for outgoing waveforms.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"WFMO:ENC {value.value}", + "WFMO:ENC?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.outgoing_waveform_encoding = value + assert inst.outgoing_waveform_encoding == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.BinaryFormat) +def test_outgoing_byte_format(value): + """Get / set the binary format for outgoing waveforms.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"WFMO:BN_F {value.value}", + "WFMO:BN_F?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.outgoing_binary_format = value + assert inst.outgoing_binary_format == value + + +@pytest.mark.parametrize("value", ik.tektronix.TekDPO70000.ByteOrder) +def test_outgoing_byte_order(value): + """Get / set the binary data endianness for outgoing waveforms.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"WFMO:BYT_O {value.value}", + "WFMO:BYT_O?" + ], + [ + f"{value.value}" + ] + ) as inst: + inst.outgoing_byte_order = value + assert inst.outgoing_byte_order == value + + +@pytest.mark.parametrize("value", (1, 2, 4, 8)) +def test_outgoing_n_bytes(value): + """Get / set the number of bytes sampled in waveforms binary encoding.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + f"WFMO:BYT_N {value}", + "WFMO:BYT_N?" + ], + [ + f"{value}" + ] + ) as inst: + inst.outgoing_n_bytes = value + assert inst.outgoing_n_bytes == value + + +# METHODS # + + +def test_select_fastest_encoding(): + """Sets encoding to fastest methods.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "DAT:ENC FAS" + ], + [ + ] + ) as inst: + inst.select_fastest_encoding() + + +def test_force_trigger(): + """Force a trivver event.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + "TRIG FORC" + ], + [ + ] + ) as inst: + inst.force_trigger() + + +def test_run(): + """Enables the trigger for the oscilloscope.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ":RUN" + ], + [ + ] + ) as inst: + inst.run() + + +def test_stop(): + """Disables the trigger for the oscilloscope.""" + with expected_protocol( + ik.tektronix.TekDPO70000, + [ + ":STOP" + ], + [ + ] + ) as inst: + inst.stop() From 6d1214ddf0ed2b5ad3a4932825971495e5e19a00 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 9 Sep 2020 07:50:40 -0700 Subject: [PATCH 053/108] Tests that proprety factories invoke host classes `query` and `sendcmd` methods (#259) * Extend tests for property factories Test for property factories were extended to test the following: - Setter and getter work according to the expected format. - Assert that the property getter calls its host classes `query` method - Assert that the property setter calls its host classes `sendcmd` method * Arrange imports properly --- instruments/tests/test_util_fns.py | 135 ++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 3 deletions(-) diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index 80952157a..3c0effeba 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -12,14 +12,73 @@ import instruments.units as u from instruments.util_fns import ( + assume_units, + bool_property, + convert_temperature, + enum_property, + int_property, ProxyList, - assume_units, convert_temperature, - setattr_expression + setattr_expression, + string_property, + unitful_property, + unitless_property ) + +# FIXTURES ################################################################### + + +@pytest.fixture +def mock_inst(mocker): + """Intialize a mock instrument to test property factories. + + Include a call to each property factory to be tested. The command + given to the property factory must be a valid argument returned by + query. This argument can be asserted later. Also set up are mocker + spies to assert `query` and `sendcmd` have actually been called. + + :return: Fake instrument class. + """ + class Inst: + """Mock instrument class.""" + def __init__(self): + """Set up the mocker spies and send command placeholder.""" + # spies + self.spy_query = mocker.spy(self, 'query') + self.spy_sendcmd = mocker.spy(self, 'sendcmd') + + # variable to set with send command + self._sendcmd = None + + def query(self, cmd): + """Return the command minus the ? which is sent along.""" + return f"{cmd[:-1]}" + + def sendcmd(self, cmd): + """Sets the command to `self._sendcmd`.""" + self._sendcmd = cmd + + class SomeEnum(Enum): + test = "enum" + + bool_property = bool_property("ON") # return True + + enum_property = enum_property("enum", SomeEnum) + + unitless_property = unitless_property("42") + + int_property = int_property("42") + + unitful_property = unitful_property("42", u.K) + + string_property = string_property("'STRING'") + + return Inst() + + # TEST CASES ################################################################# -# pylint: disable=protected-access,missing-docstring +# pylint: disable=protected-access,missing-docstring,redefined-outer-name def test_ProxyList_basics(): @@ -209,3 +268,73 @@ def __init__(self): a = A() setattr_expression(a, 'b[0].x', 'foo') assert a.b[0].x == 'foo' + + +def test_bool_property_sendcmd_query(mock_inst): + """Assert that bool_property calls sendcmd, query of parent class.""" + # fixture query should return "On" -> True + assert mock_inst.bool_property + mock_inst.spy_query.assert_called() + # setter + mock_inst.bool_property = True + assert mock_inst._sendcmd == "ON ON" + mock_inst.spy_sendcmd.assert_called() + + +def test_enum_property_sendcmd_query(mock_inst): + """Assert that enum_property calls sendcmd, query of parent class.""" + # test getter + assert mock_inst.enum_property == mock_inst.SomeEnum.test + mock_inst.spy_query.assert_called() + # setter + mock_inst.enum_property = mock_inst.SomeEnum.test + assert mock_inst._sendcmd == "enum enum" + mock_inst.spy_sendcmd.assert_called() + + +def test_unitless_property_sendcmd_query(mock_inst): + """Assert that unitless_property calls sendcmd, query of parent class.""" + # getter + assert mock_inst.unitless_property == 42 + mock_inst.spy_query.assert_called() + # setter + value = 13 + mock_inst.unitless_property = value + assert mock_inst._sendcmd == f"42 {value:e}" + mock_inst.spy_sendcmd.assert_called() + + +def test_int_property_sendcmd_query(mock_inst): + """Assert that int_property calls sendcmd, query of parent class.""" + # getter + assert mock_inst.int_property == 42 + mock_inst.spy_query.assert_called() + # setter + value = 13 + mock_inst.int_property = value + assert mock_inst._sendcmd == f"42 {value}" + mock_inst.spy_sendcmd.assert_called() + + +def test_unitful_property_sendcmd_query(mock_inst): + """Assert that unitful_property calls sendcmd, query of parent class.""" + # getter + assert mock_inst.unitful_property == u.Quantity(42, u.K) + mock_inst.spy_query.assert_called() + # setter + value = 13 + mock_inst.unitful_property = u.Quantity(value, u.K) + assert mock_inst._sendcmd == f"42 {value:e}" + mock_inst.spy_sendcmd.assert_called() + + +def test_string_property_sendcmd_query(mock_inst): + """Assert that string_property calls sendcmd, query of parent class.""" + # getter + assert mock_inst.string_property == "STRING" + mock_inst.spy_query.assert_called() + # setter + value = "forty-two" + mock_inst.string_property = value + assert mock_inst._sendcmd == f"'STRING' \"{value}\"" + mock_inst.spy_sendcmd.assert_called() From c9ffd6eabd8c9c5af7502a81fdc45e0959e656fe Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 10 Sep 2020 09:17:13 -0700 Subject: [PATCH 054/108] Test suite and BFs for Thorlabs PM100USB (#260) * Test suite and BFs for Thorlabs PM100USB Test suite with full coverage. **Bug fixes:** - Could not find a programmers manual on this instrument. - Replaced a `sendcmd` with a query, since a value is expected from the instrument. No read provided, thus this is likely a bug. - When creating `self._flags` while initializing a sensor, an integer was compared to a string. String now first converted to integer for comparison. * Added the second BF described before - staging mishap... --- .../test_thorlabs/test_thorlabs_pm100usb.py | 256 ++++++++++++++++++ instruments/thorlabs/pm100usb.py | 4 +- 2 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py diff --git a/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py b/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py new file mode 100644 index 000000000..63390d5bb --- /dev/null +++ b/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Thorlabs PM100USB +""" + +# IMPORTS #################################################################### + + +from hypothesis import ( + given, + strategies as st, +) +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +# pylint: disable=protected-access,redefined-outer-name + + +# FIXTURES # + + +@pytest.fixture +def init_sensor(): + """Initialize a sensor - return initialized sensor class.""" + class Sensor: + """Initialize a sensor class""" + NAME = "SENSOR" + SERIAL_NUMBER = "123456" + CALIBRATION_MESSAGE = "OK" + SENSOR_TYPE = "TEMPERATURE" + SENSOR_SUBTYPE = "KDP" + FLAGS = "256" + + def sendmsg(self): + return "SYST:SENSOR:IDN?" + + def message(self): + return ",".join([self.NAME, self.SERIAL_NUMBER, + self.CALIBRATION_MESSAGE, self.SENSOR_TYPE, + self.SENSOR_SUBTYPE, self.FLAGS]) + + return Sensor() + + +# SENSOR CLASS # + + +def test_sensor_init(init_sensor): + """Initialize a sensor object from the parent class.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + init_sensor.sendmsg() + ], + [ + init_sensor.message() + ] + ) as inst: + assert inst.sensor._parent is inst + + +def test_sensor_name(init_sensor): + """Get name of the sensor.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + init_sensor.sendmsg() + ], + [ + init_sensor.message() + ] + ) as inst: + assert inst.sensor.name == init_sensor.NAME + + +def test_sensor_serial_number(init_sensor): + """Get serial number of the sensor.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + init_sensor.sendmsg() + ], + [ + init_sensor.message() + ] + ) as inst: + assert inst.sensor.serial_number == init_sensor.SERIAL_NUMBER + + +def test_sensor_calibration_message(init_sensor): + """Get calibration message of the sensor.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + init_sensor.sendmsg() + ], + [ + init_sensor.message() + ] + ) as inst: + assert (inst.sensor.calibration_message == + init_sensor.CALIBRATION_MESSAGE) + + +def test_sensor_type(init_sensor): + """Get type of the sensor.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + init_sensor.sendmsg() + ], + [ + init_sensor.message() + ] + ) as inst: + assert inst.sensor.type == (init_sensor.SENSOR_TYPE, + init_sensor.SENSOR_SUBTYPE) + + +def test_sensor_flags(init_sensor): + """Get flags of the sensor.""" + flag_read = init_sensor.FLAGS + flags = ik.thorlabs.PM100USB._SensorFlags(**{ + e.name: bool(e & int(flag_read)) + for e in ik.thorlabs.PM100USB.SensorFlags + }) + with expected_protocol( + ik.thorlabs.PM100USB, + [ + init_sensor.sendmsg() + ], + [ + init_sensor.message() + ] + ) as inst: + assert inst.sensor.flags == flags + + +# INSTRUMENT # + + +def test_cache_units(): + """Get, set cache units bool.""" + msr_conf = ik.thorlabs.PM100USB.MeasurementConfiguration.current + with expected_protocol( + ik.thorlabs.PM100USB, + [ + "CONF?" + ], + [ + f"{msr_conf.value}" # measurement configuration temperature + ] + ) as inst: + inst.cache_units = True + assert inst._cache_units == inst._READ_UNITS[msr_conf] + inst.cache_units = False + assert not inst.cache_units + + +@pytest.mark.parametrize("msr_conf", + ik.thorlabs.PM100USB.MeasurementConfiguration) +def test_measurement_configuration(msr_conf): + """Get / set measurement configuration.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + f"CONF {msr_conf.value}", + "CONF?" + ], + [ + f"{msr_conf.value}" # measurement configuration temperature + ] + ) as inst: + inst.measurement_configuration = msr_conf + assert inst.measurement_configuration == msr_conf + + +@given(value=st.integers(min_value=1)) +def test_averaging_count(value): + """Get / set averaging count.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + f"SENS:AVER:COUN {value}", + "SENS:AVER:COUN?" + ], + [ + f"{value}" # measurement configuration temperature + ] + ) as inst: + inst.averaging_count = value + assert inst.averaging_count == value + + +@given(value=st.integers(max_value=0)) +def test_averaging_count_value_error(value): + """Raise a ValueError if the averaging count is wrong.""" + with expected_protocol( + ik.thorlabs.PM100USB, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.averaging_count = value + err_msg = err_info.value.args[0] + assert err_msg == "Must count at least one time." + + +@given(value=st.floats(min_value=0)) +def test_read(value): + """Read instrument and grab the units.""" + msr_conf = ik.thorlabs.PM100USB.MeasurementConfiguration.current + with expected_protocol( + ik.thorlabs.PM100USB, + [ + "CONF?", + "READ?" + ], + [ + f"{msr_conf.value}", # measurement configuration temperature + f"{value}" + + ] + ) as inst: + units = inst._READ_UNITS[msr_conf] # cache units is False at init + assert inst.read() == value * units + + +def test_read_cached_units(): + """Read instrument and grab the units.""" + msr_conf = ik.thorlabs.PM100USB.MeasurementConfiguration.current + value = 42 + with expected_protocol( + ik.thorlabs.PM100USB, + [ + "CONF?", + "READ?" + ], + [ + f"{msr_conf.value}", # measurement configuration temperature + f"{value}" + + ] + ) as inst: + units = inst._READ_UNITS[msr_conf] # cache units is False at init + inst.cache_units = True + assert inst.read() == value * units diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index 448aeb439..8ead8441b 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -83,7 +83,7 @@ def __init__(self, parent): self._parent = parent # Pull details about the sensor from SYST:SENSOR:IDN? - sensor_idn = parent.sendcmd("SYST:SENSOR:IDN?") + sensor_idn = parent.query("SYST:SENSOR:IDN?") ( self._name, self._serial_number, self._calibration_message, self._sensor_type, self._sensor_subtype, self._flags @@ -93,7 +93,7 @@ def __init__(self, parent): # We want flags to be a named tuple over bools. # pylint: disable=protected-access self._flags = parent._SensorFlags(**{ - e.name: bool(e & self._flags) + e.name: bool(e & int(self._flags)) for e in PM100USB.SensorFlags # pylint: disable=not-an-iterable }) From 77c6f0a9b1155f06becd4d0b1d6b574e9a62b126 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 14 Sep 2020 07:08:20 -0700 Subject: [PATCH 055/108] Test suite and BFs for Tektronix TDS5xx (#261) * Test suite and BFs for Tektronix TDS5xx Test suite with full coverage - Tests for `get_hardcopy` routine is a bit creative, but couldn't find any manual entry for what is done there. Bug fixes: - Typos - map(float, list_of_string) not allowed anymore, switched to directly transferring to `np.array` with specific `dtype`. - `flush_input` in `read_waveform` replaced with reading one character. Manual states that newline character is present after binary block. - Enums called with name instead of value -> fixed, now called properly - `get_hardcopy` routine tried to unpack read data. However, the `read` routine already decodes it using "utf-8" as the default encoding already. Thus: need to encode again before using `struct.unpack` to unpack them. - Class had `import time` and `from time import sleep` and used `sleep` and `time.sleep`. Used `import time`, which is useful to mock out sleep time. * Delete some unused setup variables I forgot to delete before * Change `get_hardcopy` to use `read_raw` and update test. Test still worked, but now we can use hypothesis to simulate binary data. --- instruments/tektronix/tektds5xx.py | 32 +- .../tests/test_tektronix/test_tktds5xx.py | 861 ++++++++++++++++++ 2 files changed, 877 insertions(+), 16 deletions(-) create mode 100644 instruments/tests/test_tektronix/test_tktds5xx.py diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index 6a2f9e818..fe744f029 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -39,7 +39,6 @@ import operator import struct import time -from time import sleep import numpy as np @@ -70,7 +69,7 @@ def __init__(self, tek, idx): def read(self): """ Gets the current measurement value of the channel, and returns a dict - of all relevent information + of all relevant information :rtype: `dict` of measurement parameters """ @@ -126,8 +125,7 @@ def read_waveform(self, bin_format=True): self._parent.sendcmd('DAT:ENC ASCI') raw = self._parent.query('CURVE?') raw = raw.split(',') # Break up comma delimited string - raw = map(float, raw) # Convert each list element to int - raw = np.array(raw) # Convert into numpy array + raw = np.array(raw, dtype=np.float) # Convert into numpy array else: # Set encoding to signed, big-endian self._parent.sendcmd('DAT:ENC RIB') @@ -137,7 +135,8 @@ def read_waveform(self, bin_format=True): raw = self._parent.binblockread(data_width) # pylint: disable=protected-access - self._parent._file.flush_input() # Flush input buffer + # read line separation character + self._parent._file.read_raw(1) # Retrieve Y offset yoffs = self._parent.query('WFMP:{}:YOF?'.format(self.name)) @@ -244,7 +243,7 @@ def scale(self): """ Gets/sets the scale setting for this channel. - :type: `TekTDS5xx.Impedance` + :type: `float` """ return float(self._parent.query("CH{}:SCA?".format(self._idx))) @@ -401,7 +400,7 @@ def sources(self): :rtype: `list` """ active = [] - channels = map(int, self.query('SEL?').split(';')[0:11]) + channels = np.array(self.query('SEL?').split(';')[0:11], dtype=np.int) for idx in range(0, 4): if channels[idx]: active.append(_TekTDS5xxChannel(self, idx)) @@ -432,7 +431,7 @@ def data_source(self): @data_source.setter def data_source(self, newval): if isinstance(newval, _TekTDS5xxDataSource): - newval = TekTDS5xx.Source[newval.name] + newval = TekTDS5xx.Source(newval.name) if not isinstance(newval, TekTDS5xx.Source): raise TypeError("Source setting must be a `TekTDS5xx.Source`" " value, got {} instead.".format(type(newval))) @@ -500,7 +499,7 @@ def trigger_coupling(self): :type: `TekTDS5xx.Coupling` """ - return TekTDS5xx.Coupling[self.query("TRIG:MAI:EDGE:COUP?")] + return TekTDS5xx.Coupling(self.query("TRIG:MAI:EDGE:COUP?")) @trigger_coupling.setter def trigger_coupling(self, newval): @@ -539,8 +538,9 @@ def trigger_source(self): @trigger_source.setter def trigger_source(self, newval): if not isinstance(newval, TekTDS5xx.Trigger): - raise TypeError("Trigger source setting must be a" - "`TekTDS5xx.source` value, got {} instead.".format(type(newval))) + raise TypeError("Trigger source setting must be a " + "`TekTDS5xx.Trigger` value, got {} " + "instead.".format(type(newval))) self.sendcmd("TRIG:MAI:EDGE:SOU {}".format(newval.value)) @@ -557,7 +557,7 @@ def clock(self): @clock.setter def clock(self, newval): if not isinstance(newval, datetime): - raise ValueError("Expected datetime.datetime" + raise ValueError("Expected datetime.datetime " "but got {} instead".format(type(newval))) self.sendcmd(newval.strftime('DATE "%Y-%m-%d";:TIME "%H:%M:%S"')) @@ -573,7 +573,7 @@ def display_clock(self): @display_clock.setter def display_clock(self, newval): if not isinstance(newval, bool): - raise ValueError("Expected bool but got" + raise ValueError("Expected bool but got " "{} instead".format(type(newval))) self.sendcmd('DISPLAY:CLOCK {}'.format(int(newval))) @@ -585,13 +585,13 @@ def get_hardcopy(self): """ self.sendcmd('HARDC:PORT GPI;HARDC:LAY PORT;:HARDC:FORM BMP') self.sendcmd('HARDC START') - sleep(1) - header = self.query("", size=54) + time.sleep(1) + header = self._file.read_raw(size=54) # Get BMP Length in kilobytes from DIB header, because file header is # bad length = reduce( operator.mul, struct.unpack('h", value) for value in values) + values_len = str(len(values_packed)).encode() + values_len_of_len = str(len(values_len)).encode() + + # calculations + y_calc = ((values_arr - yoffs) * ymult) + yzero + x_calc = np.arange(float(ptcnt)) * xincr + + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + "DAT:SOU?", + "DAT:ENC RIB", + "DATA:WIDTH?", + "CURVE?", + f"WFMP:CH{channel_no+1}:YOF?", + f"WFMP:CH{channel_no+1}:YMU?", + f"WFMP:CH{channel_no+1}:YZE?", + f"WFMP:CH{channel_no+1}:XIN?", + f"WFMP:CH{channel_no+1}:NR_P?" + + ], + [ + f"CH{channel_no+1}", + f"{data_width}", + b"#" + values_len_of_len + values_len + values_packed, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xincr}", + f"{ptcnt}" + ] + ) as inst: + channel = inst.channel[channel_no] + x_read, y_read = channel.read_waveform(bin_format=True) + np.testing.assert_equal(x_read, x_calc) + np.testing.assert_equal(y_read, y_calc) + + +@given(values=st.lists(st.floats(min_value=0), min_size=1)) +def test_data_source_read_waveform_ascii(values): + """Read waveform from data source as ASCII.""" + # constants - to not overkill it with hypothesis + channel_no = 0 + yoffs = 1. + ymult = 1. + yzero = 0.3 + xincr = 0.001 + # make values to compare with + values_str = ",".join([str(value) for value in values]) + values_arr = np.array(values) + + # calculations + ptcnt = len(values) + y_calc = ((values_arr - yoffs) * ymult) + yzero + x_calc = np.arange(float(ptcnt)) * xincr + + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + "DAT:SOU?", + "DAT:ENC ASCI", + "CURVE?", + f"WFMP:CH{channel_no+1}:YOF?", + f"WFMP:CH{channel_no+1}:YMU?", + f"WFMP:CH{channel_no+1}:YZE?", + f"WFMP:CH{channel_no+1}:XIN?", + f"WFMP:CH{channel_no+1}:NR_P?" + + ], + [ + f"CH{channel_no+1}", + values_str, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xincr}", + f"{ptcnt}" + ] + ) as inst: + channel = inst.channel[channel_no] + x_read, y_read = channel.read_waveform(bin_format=False) + np.testing.assert_equal(x_read, x_calc) + np.testing.assert_equal(y_read, y_calc) + + +# CHANNEL # + + +@pytest.mark.parametrize("channel", [it for it in range(4)]) +def test_channel_init(channel): + """Initialize a new channel.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + assert inst.channel[channel]._parent is inst + assert inst.channel[channel]._idx == channel + 1 + + +@pytest.mark.parametrize("coupl", ik.tektronix.TekTDS5xx.Coupling) +def test_channel_coupling(coupl): + """Get / set channel coupling.""" + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"CH{channel+1}:COUPL {coupl.value}", + f"CH{channel+1}:COUPL?" + ], + [ + f"{coupl.value}" + ] + ) as inst: + inst.channel[channel].coupling = coupl + assert inst.channel[channel].coupling == coupl + + +def test_channel_coupling_type_error(): + """Raise type error if channel coupling is set with wrong type.""" + wrong_type = 42 + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.channel[channel].coupling = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Coupling setting must be a `TekTDS5xx.Coupling` " \ + f"value, got {type(wrong_type)} instead." + + +@pytest.mark.parametrize("bandw", ik.tektronix.TekTDS5xx.Bandwidth) +def test_channel_bandwidth(bandw): + """Get / set channel bandwidth.""" + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"CH{channel+1}:BAND {bandw.value}", + f"CH{channel+1}:BAND?" + ], + [ + f"{bandw.value}" + ] + ) as inst: + inst.channel[channel].bandwidth = bandw + assert inst.channel[channel].bandwidth == bandw + + +def test_channel_bandwidth_type_error(): + """Raise type error if channel bandwidth is set with wrong type.""" + wrong_type = 42 + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.channel[channel].bandwidth = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Bandwidth setting must be a " \ + f"`TekTDS5xx.Bandwidth` value, got " \ + f"{type(wrong_type)} instead." + + +@pytest.mark.parametrize("imped", ik.tektronix.TekTDS5xx.Impedance) +def test_channel_impedance(imped): + """Get / set channel impedance.""" + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"CH{channel+1}:IMP {imped.value}", + f"CH{channel+1}:IMP?" + ], + [ + f"{imped.value}" + ] + ) as inst: + inst.channel[channel].impedance = imped + assert inst.channel[channel].impedance == imped + + +def test_channel_impedance_type_error(): + """Raise type error if channel impedance is set with wrong type.""" + wrong_type = 42 + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.channel[channel].impedance = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Impedance setting must be a " \ + f"`TekTDS5xx.Impedance` value, got " \ + f"{type(wrong_type)} instead." + + +@given(value=st.floats(min_value=0, exclude_min=True)) +def test_channel_probe(value): + """Get connected probe value.""" + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"CH{channel+1}:PRO?" + ], + [ + f"{value}" + ] + ) as inst: + value_expected = round(1 / value, 0) + assert inst.channel[channel].probe == value_expected + + +@given(value=st.floats(min_value=0)) +def test_channel_scale(value): + """Get / set scale setting.""" + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"CH{channel + 1}:SCA {value:.3E}", + f"CH{channel + 1}:SCA?", + f"CH{channel + 1}:SCA?" + ], + [ + f"{value}", + f"{value}" + ] + ) as inst: + inst.channel[channel].scale = value + print(f"\n>>>{value}") + assert inst.channel[channel].scale == value + + +def test_channel_scale_value_error(): + """Raise ValueError if scale was not set properly.""" + scale_set = 42 + scale_rec = 13 + channel = 0 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"CH{channel + 1}:SCA {scale_set:.3E}", + f"CH{channel + 1}:SCA?" + ], + [ + f"{scale_rec}" + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.channel[channel].scale = scale_set + err_msg = err_info.value.args[0] + assert err_msg == f"Tried to set CH{channel+1} Scale to {scale_set} " \ + f"but got {float(scale_rec)} instead" + + +# INSTRUMENT # + + +@given(states=st.lists(st.integers(min_value=0, max_value=1), min_size=11, + max_size=11)) +def test_sources(states): + """Get list of all active sources.""" + active_sources = [] + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + "SEL?" + ], + [ + ";".join([str(state) for state in states]) + ] + ) as inst: + # create active_sources + for idx in range(4): + if states[idx]: + active_sources.append( + ik.tektronix.tektds5xx._TekTDS5xxChannel(inst, idx) + ) + for idx in range(4, 7): + if states[idx]: + active_sources.append( + ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, + f"MATH{idx-3}") + ) + for idx in range(7, 11): + if states[idx]: + active_sources.append( + ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, + f"REF{idx-6}") + ) + # read active sources + active_read = inst.sources + + assert active_read == active_sources + + +@pytest.mark.parametrize("channel", [it for it in range(4)]) +def test_data_source_channel(channel): + """Get / set channel data source for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"DAT:SOU CH{channel+1}", + f"DAT:SOU CH{channel+1}", + "DAT:SOU?" + ], + [ + f"CH{channel+1}" + ] + ) as inst: + # set as Source enum + inst.data_source = ik.tektronix.TekTDS5xx.Source[f"CH{channel + 1}"] + # set as channel object + data_source = inst.channel[channel] + inst.data_source = data_source + assert inst.data_source == data_source + + +@pytest.mark.parametrize("channel", [it for it in range(3)]) +def test_data_source_math(channel): + """Get / set math data source for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"DAT:SOU MATH{channel+1}", + f"DAT:SOU MATH{channel+1}", + "DAT:SOU?" + ], + [ + f"MATH{channel+1}" + ] + ) as inst: + # set as Source enum + inst.data_source = ik.tektronix.TekTDS5xx.Source[f"Math{channel + 1}"] + # set as channel object + data_source = inst.math[channel] + inst.data_source = data_source + assert inst.data_source == data_source + + +@pytest.mark.parametrize("channel", [it for it in range(3)]) +def test_data_source_ref(channel): + """Get / set ref data source for waveform transfer.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"DAT:SOU REF{channel+1}", + f"DAT:SOU REF{channel+1}", + "DAT:SOU?" + ], + [ + f"REF{channel+1}" + ] + ) as inst: + # set as Source enum + inst.data_source = ik.tektronix.TekTDS5xx.Source[f"Ref{channel + 1}"] + # set as channel object + data_source = inst.ref[channel] + inst.data_source = data_source + assert inst.data_source == data_source + + +def test_data_source_raise_type_error(): + """Raise TypeError when setting data source with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.data_source = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Source setting must be a `TekTDS5xx.Source` " \ + f"value, got {type(wrong_type)} instead." + + +@pytest.mark.parametrize("width", (1, 2)) +def test_data_width(width): + """Get / set data width.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"DATA:WIDTH {width}", + "DATA:WIDTH?" + ], + [ + f"{width}" + ] + ) as inst: + inst.data_width = width + assert inst.data_width == width + + +@given(width=st.integers().filter(lambda x: x < 1 or x > 2)) +def test_data_width_value_error(width): + """Raise ValueError when setting a wrong data width.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.data_width = width + err_msg = err_info.value.args[0] + assert err_msg == "Only one or two byte-width is supported." + + +def test_force_trigger(): + """Raise NotImplementedError when forcing a trigger.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(NotImplementedError): + inst.force_trigger() + + +@given(value=st.floats(min_value=0)) +def test_horizontal_scale(value): + """Get / set horizontal scale.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"HOR:MAI:SCA {value:.3E}", + "HOR:MAI:SCA?", + "HOR:MAI:SCA?" + ], + [ + f"{value}", + f"{value}" + ] + ) as inst: + inst.horizontal_scale = value + assert inst.horizontal_scale == value + + +def test_horizontal_scale_value_error(): + """Raise ValueError if setting horizontal scale does not work.""" + set_value = 42 + get_value = 13 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"HOR:MAI:SCA {set_value:.3E}", + "HOR:MAI:SCA?" + ], + [ + f"{get_value}", + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.horizontal_scale = set_value + err_msg = err_info.value.args[0] + assert err_msg == f"Tried to set Horizontal Scale to {set_value} " \ + f"but got {float(get_value)} instead" + + +@given(value=st.floats(min_value=0)) +def test_trigger_level(value): + """Get / set trigger level.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"TRIG:MAI:LEV {value:.3E}", + "TRIG:MAI:LEV?", + "TRIG:MAI:LEV?" + ], + [ + f"{value}", + f"{value}" + ] + ) as inst: + inst.trigger_level = value + assert inst.trigger_level == value + + +def test_trigger_level_value_error(): + """Raise ValueError if setting trigger level does not work.""" + set_value = 42 + get_value = 13 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"TRIG:MAI:LEV {set_value:.3E}", + "TRIG:MAI:LEV?" + ], + [ + f"{get_value}" + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.trigger_level = set_value + err_msg = err_info.value.args[0] + assert err_msg == f"Tried to set trigger level to {set_value} " \ + f"but got {float(get_value)} instead" + + +@pytest.mark.parametrize("coupl", ik.tektronix.TekTDS5xx.Coupling) +def test_trigger_coupling(coupl): + """Get / set trigger coupling.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"TRIG:MAI:EDGE:COUP {coupl.value}", + "TRIG:MAI:EDGE:COUP?" + ], + [ + f"{coupl.value}" + ] + ) as inst: + inst.trigger_coupling = coupl + assert inst.trigger_coupling == coupl + + +def test_trigger_coupling_type_error(): + """Raise type error when coupling is not a `Coupling` enum.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.trigger_coupling = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Coupling setting must be a `TekTDS5xx.Coupling` " \ + f"value, got {type(wrong_type)} instead." + + +@pytest.mark.parametrize("edge", ik.tektronix.TekTDS5xx.Edge) +def test_trigger_slope(edge): + """Get / set trigger slope.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"TRIG:MAI:EDGE:SLO {edge.value}", + "TRIG:MAI:EDGE:SLO?" + ], + [ + f"{edge.value}" + ] + ) as inst: + inst.trigger_slope = edge + assert inst.trigger_slope == edge + + +def test_trigger_slope_type_error(): + """Raise type error when edge is not an `Edge` enum.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.trigger_slope = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Edge setting must be a `TekTDS5xx.Edge` " \ + f"value, got {type(wrong_type)} instead." + + +@pytest.mark.parametrize("source", ik.tektronix.TekTDS5xx.Trigger) +def test_trigger_source(source): + """Get / set trigger source.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"TRIG:MAI:EDGE:SOU {source.value}", + "TRIG:MAI:EDGE:SOU?" + ], + [ + f"{source.value}" + ] + ) as inst: + inst.trigger_source = source + assert inst.trigger_source == source + + +def test_trigger_source_type_error(): + """Raise type error when source is not an `source` enum.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.trigger_source = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Trigger source setting must be a " \ + f"`TekTDS5xx.Trigger` value, got " \ + f"{type(wrong_type)} instead." + + +@given(dt=st.datetimes(min_value=datetime(1000, 1, 1))) +def test_clock(dt): + """Get / set oscilloscope clock.""" + # create a date and time + dt_fmt_receive = '"%Y-%m-%d";"%H:%M:%S"' + dt_fmt_send = 'DATE "%Y-%m-%d";:TIME "%H:%M:%S"' + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + dt.strftime(dt_fmt_send), + "DATE?;:TIME?" + ], + [ + dt.strftime(dt_fmt_receive) + ] + ) as inst: + inst.clock = dt + assert inst.clock == dt.replace(microsecond=0) + + +def test_clock_value_error(): + """Raise ValueError when not set with datetime object.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.clock = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Expected datetime.datetime but got " \ + f"{type(wrong_type)} instead" + + +@pytest.mark.parametrize("newval", (True, False)) +def test_display_clock(newval): + """Get / set if clock is displayed on screen.""" + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + f"DISPLAY:CLOCK {int(newval)}", + "DISPLAY:CLOCK?" + ], + [ + f"{int(newval)}" + ] + ) as inst: + inst.display_clock = newval + assert inst.display_clock == newval + + +def test_display_clock_value_error(): + """Raise ValueError when display_clock is called w/o a bool.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS5xx, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.display_clock = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Expected bool but got {type(wrong_type)} instead" + + +@given(data=st.binary(min_size=1, max_size=2147483647)) +def test_get_hardcopy(mocker, data): + """Transfer data in binary from the instrument. + + Data is at least 1 byte long, then we need to add 8 for the + color table. + Fake the header of the data such that in byte 18:30 are 4 factorial + packed as ' Date: Mon, 14 Sep 2020 09:42:02 -0700 Subject: [PATCH 056/108] Tests and BFs for Keithley 195 (#262) * Tests and BFs for Keithley 195 Full coverage test suite BFs: - Transfer queried values to bytes before using `struct.unpack`. - Do subsequent comparisons on bytes as well. - Remove `if self._testing` statement to skip a sleep time. In the test, mock `time.sleep` for speedup and ensure it was called with a 2 seconds argument. * Read the status word using `read_raw` Changed the read to use `read_raw` to read the instrument. Thus, no need to re-encode the status word using `bytes`. Co-authored-by: Steven Casagrande --- instruments/keithley/keithley195.py | 14 +- .../tests/test_keithley/test_keithley195.py | 461 ++++++++++++++++++ 2 files changed, 468 insertions(+), 7 deletions(-) create mode 100644 instruments/tests/test_keithley/test_keithley195.py diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index 637fad39b..8b8d907e0 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -259,8 +259,7 @@ def measure(self, mode=None): current_mode = self.mode if mode != current_mode: self.mode = mode - if not self._testing: - time.sleep(2) # Gives the instrument a moment to settle + time.sleep(2) # Gives the instrument a moment to settle else: mode = self.mode value = self.query('') @@ -277,7 +276,8 @@ def get_status_word(self): :return: String containing setting information of the instrument :rtype: `str` """ - return self.query('U0DX') + self.sendcmd('U0DX') + return self._file.read_raw() @staticmethod def parse_status_word(statusword): # pylint: disable=too-many-locals @@ -295,7 +295,7 @@ def parse_status_word(statusword): # pylint: disable=too-many-locals :return: A parsed version of the status word as a Python dictionary :rtype: `dict` """ - if statusword[:3] != '195': + if statusword[:3] != b'195': raise ValueError('Status word starts with wrong prefix, expected ' '195, got {}'.format(statusword)) @@ -306,13 +306,13 @@ def parse_status_word(statusword): # pylint: disable=too-many-locals return {'trigger': Keithley195.TriggerMode(int(trigger)), 'mode': Keithley195.Mode(int(function)), 'range': int(input_range), - 'eoi': (eoi == '1'), + 'eoi': (eoi == b'1'), 'buffer': buf, 'rate': rate, 'srqmode': srqmode, - 'relative': (relative == '1'), + 'relative': (relative == b'1'), 'delay': delay, - 'multiplex': (multiplex == '1'), + 'multiplex': (multiplex == b'1'), 'selftest': selftest, 'dataformat': data_fmt, 'datacontrol': data_ctrl, diff --git a/instruments/tests/test_keithley/test_keithley195.py b/instruments/tests/test_keithley/test_keithley195.py new file mode 100644 index 000000000..395dd7454 --- /dev/null +++ b/instruments/tests/test_keithley/test_keithley195.py @@ -0,0 +1,461 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Keithley 195 digital multimeter. +""" + +# IMPORTS #################################################################### + +import struct +import time + +from hypothesis import ( + given, + strategies as st, +) +import pytest + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + +# TESTS ###################################################################### + + +# pylint: disable=redefined-outer-name + + +# PYTEST FIXTURES FOR INITIALIZATION # + + +@pytest.fixture +def init(): + """Returns the initialization command that is sent to instrument.""" + return "YX\nG1DX" + + +@pytest.fixture +def statusword(): + """Return a standard statusword for the status of the instrument.""" + trigger = b"1" # talk_one_shot + mode = b"2" # resistance + range = b"3" # 2kOhm in resistance mode + eoi = b"1" # disabled + buffer = b"3" # reading done, currently unused + rate = b"5" # Line cycle integration + srqmode = b"0" # disabled + relative = b"1" # relative mode is activated + delay = b"0" # no delay, currently unused + multiplex = b"0" # multiplex enabled + selftest = b"2" # self test successful, currently unused + dataformat = b"1" # Readings without prefix/suffix. + datacontrol = b"0" # Readings without prefix/suffix. + filter = b"0" # filter disabled + terminator = b"1" + + statusword_p1 = b"195 " # sends a space after 195! + statusword_p2 = struct.pack('@4c2s3c2s5c2s', trigger, mode, range, eoi, + buffer, rate, srqmode, relative, delay, + multiplex, selftest, dataformat, datacontrol, + filter, terminator) + return statusword_p1 + statusword_p2 + + +# TEST INSTRUMENT # + + +def test_keithley195_mode(init, statusword): + """Get / set the measurement mode.""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "F2DX", + "U0DX" + + ], + [ + statusword + ], + sep="\n" + ) as mul: + mul.mode = mul.Mode.resistance + assert mul.mode == mul.Mode.resistance + + +def test_keithley195_mode_string(init, statusword): + """Get / set the measurement mode using a string.""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "F2DX", + "U0DX" + + ], + [ + statusword + ], + sep="\n" + ) as mul: + mul.mode = 'resistance' + assert mul.mode == mul.Mode.resistance + + +def test_keithley195_mode_type_error(init): + """Raise type error when setting the mode with the wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley195, + [ + init + ], + [ + ], + sep="\n" + ) as mul: + with pytest.raises(TypeError) as err_info: + mul.mode = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Mode must be specified as a Keithley195.Mode " \ + f"value, got {wrong_type} instead." + + +def test_keithley195_trigger_mode(init, statusword): + """Get / set the trigger mode.""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "T1X", + "U0DX" + + ], + [ + statusword + ], + sep="\n" + ) as mul: + mul.trigger_mode = mul.TriggerMode.talk_one_shot + assert mul.trigger_mode == mul.TriggerMode.talk_one_shot + + +def test_keithley195_trigger_mode_string(init, statusword): + """Get / set the trigger using a string.""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "T1X", + "U0DX" + + ], + [ + statusword + ], + sep="\n" + ) as mul: + mul.trigger_mode = 'talk_one_shot' + assert mul.trigger_mode == mul.TriggerMode.talk_one_shot + + +def test_keithley195_trigger_mode_type_error(init): + """Raise type error when setting the trigger mode with the wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley195, + [ + init + ], + [ + ], + sep="\n" + ) as mul: + with pytest.raises(TypeError) as err_info: + mul.trigger_mode = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Drive must be specified as a " \ + f"Keithley195.TriggerMode, got {wrong_type} instead." + + +def test_keithley195_relative(init, statusword): + """Get / set the relative mode""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "Z0DX", + "Z1DX", + "U0DX" + + ], + [ + statusword + ], + sep="\n" + ) as mul: + mul.relative = False + mul.relative = True + assert mul.relative + + +def test_keithley195_relative_type_error(init): + """Raise type error when setting relative non-bool.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + ], + [ + ], + sep="\n" + ) as mul: + with pytest.raises(TypeError) as err_info: + mul.relative = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == "Relative mode must be a boolean." + + +@pytest.mark.parametrize("range", + ik.keithley.Keithley195.ValidRange.resistance.value) +def test_keithley195_input_range(init, statusword, range): + """Get / set input range. + + Set unitful and w/o units. + """ + mode = ik.keithley.Keithley195.Mode(int(statusword.decode()[5])) + index = ik.keithley.Keithley195.ValidRange[mode.name].value.index(range) + # new statusword + new_statusword = list(statusword.decode()) + new_statusword[6] = str(index + 1) + new_statusword = "".join(new_statusword) + # units + units = ik.keithley.keithley195.UNITS2[mode] + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "U0DX", + f"R{index + 1}DX", + "U0DX", + f"R{index + 1}DX", + "U0DX", # query + "U0DX" + ], + [ + statusword, + statusword, + new_statusword, + new_statusword + ], + sep="\n" + ) as mul: + mul.input_range = range + mul.input_range = u.Quantity(range, units) + assert mul.input_range == range * units + + +def test_keithley195_input_range_auto(init, statusword): + """Get / set input range auto.""" + # new statusword + new_statusword = list(statusword.decode()) + new_statusword[6] = "0" + new_statusword = "".join(new_statusword) + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "R0DX", + "U0DX" + ], + [ + new_statusword + ], + sep="\n" + ) as mul: + mul.input_range = 'Auto' + assert mul.input_range == 'auto' + + +def test_keithley195_input_range_set_wrong_string(init): + """Raise Value error if input range set w/ string other than 'auto'.""" + bad_string = 'forty-two' + with expected_protocol( + ik.keithley.Keithley195, + [ + init + ], + [ + ], + sep="\n" + ) as mul: + with pytest.raises(ValueError) as err_info: + mul.input_range = bad_string + err_msg = err_info.value.args[0] + assert err_msg == "Only \"auto\" is acceptable when specifying the " \ + "input range as a string." + + +def test_keithley195_input_range_set_wrong_range(init, statusword): + """Raise Value error if input range set w/ out of range value.""" + mode = ik.keithley.Keithley195.Mode(int(statusword.decode()[5])) + valid = ik.keithley.Keithley195.ValidRange[mode.name].value + out_of_range_value = 42 + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "U0DX" + ], + [ + statusword + ], + sep="\n" + ) as mul: + with pytest.raises(ValueError) as err_info: + mul.input_range = out_of_range_value + err_msg = err_info.value.args[0] + assert err_msg == f"Valid range settings for mode {mode} are: {valid}" + + +def test_keithley195_input_range_set_wrong_type(init, statusword): + """Raise TypeError if input range set w/ wrong type.""" + wrong_type = {"The Answer": 42} + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "U0DX" + ], + [ + statusword + ], + sep="\n" + ) as mul: + with pytest.raises(TypeError) as err_info: + mul.input_range = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Range setting must be specified as a float, " \ + f"int, or the string \"auto\", got " \ + f"{type(wrong_type)}" + + +@given(value=st.floats(allow_infinity=False, allow_nan=False)) +def test_measure_mode_is_none(init, statusword, value): + """Get a measurement in current measure mode.""" + mode = ik.keithley.Keithley195.Mode(int(statusword.decode()[5])) + units = ik.keithley.keithley195.UNITS2[mode] + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "U0DX" + ], + [ + statusword, + f"{value}" + ], + sep="\n" + ) as mul: + assert mul.measure() == value * units + + +def test_measure_mode_is_current(init, statusword): + """Get a measurement with given mode, which is already set.""" + mode = ik.keithley.Keithley195.Mode(int(statusword.decode()[5])) + units = ik.keithley.keithley195.UNITS2[mode] + value = 3.14 + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "U0DX" + ], + [ + statusword, + f"{value}" + ], + sep="\n" + ) as mul: + assert mul.measure(mode=mode) == value * units + + +def test_measure_new_mode(init, statusword, mocker): + """Get a measurement with given mode, which is already set. + + Mock time.sleep() call and assert it is called with 2 seconds. + """ + # patch call to time.sleep with mock + mock_time = mocker.patch.object(time, 'sleep', return_value=None) + + # new modes + new_mode = ik.keithley.Keithley195.Mode(0) + units = ik.keithley.keithley195.UNITS2[new_mode] + value = 3.14 + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "U0DX", + "F0DX" # send new mode + ], + [ + statusword, + f"{value}" + ], + sep="\n" + ) as mul: + assert mul.measure(mode=new_mode) == value * units + + # assert time.sleep is called with 2 second argument + mock_time.assert_called_with(2) + + +def test_parse_status_word_value_error(init): + """Raise ValueError if status word does not start with '195'.""" + wrong_statusword = "42 314" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + ], + [ + ], + sep="\n" + ) as mul: + with pytest.raises(ValueError) as err_info: + mul.parse_status_word(wrong_statusword) + err_msg = err_info.value.args[0] + assert err_msg == f"Status word starts with wrong prefix, expected " \ + f"195, got {wrong_statusword}" + + +def test_trigger(init): + """Send a trigger command.""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "X" + ], + [ + ], + sep="\n" + ) as mul: + mul.trigger() + + +def test_auto_range(init): + """Set input range to 'auto'.""" + with expected_protocol( + ik.keithley.Keithley195, + [ + init, + "R0DX", + ], + [ + ], + sep="\n" + ) as mul: + mul.auto_range() From ecdedbc239c7eeb0f91facb53a1c7ee87e8994a8 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 22 Sep 2020 20:08:11 -0700 Subject: [PATCH 057/108] Test suite update and BF for Agilent 34410a (#264) Test suite extended for full coverage. Minor bug fix: Returning `int('9.91000000E+37')` in `read_last_data` returns a `ValueError`, and is not consistent with the routine otherwise returning a `float`. Changed to returning a float. --- instruments/agilent/agilent34410a.py | 2 +- .../tests/test_agilent/test_agilent_34410a.py | 125 ++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index ea0c4a709..93c275d2b 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -162,7 +162,7 @@ def read_last_data(self): } if data == '9.91000000E+37': - return int(data) + return float(data) else: data = data.split(" ") data[0] = float(data[0]) diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 0a922fedc..9da9cd5c8 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -7,6 +7,7 @@ # IMPORTS #################################################################### import numpy as np +import pytest import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq @@ -43,6 +44,45 @@ def test_agilent34410a_data_point_count(): assert dmm.data_point_count == 215 +def test_agilent34410a_init(): + """Switch device from `idle` to `wait-for-trigger state`.""" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "INIT" + ], + [ + ] + ) as dmm: + dmm.init() + + +def test_agilent34410a_abort(): + """Abort all current measurements.""" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "ABOR" + ], + [ + ] + ) as dmm: + dmm.abort() + + +def test_agilent34410a_clear_memory(): + """Clear non-volatile memory.""" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "DATA:DEL NVMEM" + ], + [ + ] + ) as dmm: + dmm.clear_memory() + + def test_agilent34410a_r(): with expected_protocol( ik.agilent.Agilent34410a, @@ -59,6 +99,40 @@ def test_agilent34410a_r(): unit_eq(dmm.r(1), np.array([1]) * u.volt) +def test_agilent34410a_r_count_zero(): + """Read measurements with count set to zero.""" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "CONF?", + "FORM:DATA REAL,64", + "R?" + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + # pylint: disable=no-member + b"#18" + bytes.fromhex("3FF0000000000000") + ] + ) as dmm: + unit_eq(dmm.r(0), np.array([1]) * u.volt) + + +def test_agilent34410a_r_type_error(): + """Raise TypeError if count is not a integer.""" + wrong_type = "42" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "CONF?", + ], [ + "VOLT +1.000000E+01,+3.000000E-06", + ] + ) as dmm: + with pytest.raises(TypeError) as err_info: + dmm.r(wrong_type) + err_msg = err_info.value.args[0] + assert err_msg == 'Parameter "count" must be an integer' + + def test_agilent34410a_fetch(): with expected_protocol( ik.agilent.Agilent34410a, @@ -92,6 +166,42 @@ def test_agilent34410a_read_data(): unit_eq(data[1], 5.27150000E-03 * u.volt) +def test_agilent34410a_read_data_count_minus_one(): + """Read data for all data points available.""" + sample_count = 100 + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "DATA:POIN?", + "CONF?", + "FORM:DATA ASC", + f"DATA:REM? {sample_count}" + ], [ + f"{sample_count}", + "VOLT +1.000000E+01,+3.000000E-06", + "+4.27150000E-03,5.27150000E-03" + ] + ) as dmm: + data = dmm.read_data(-1) + unit_eq(data[0], 4.27150000E-03 * u.volt) + unit_eq(data[1], 5.27150000E-03 * u.volt) + + +def test_agilent34410a_read_data_type_error(): + """Raise Type error if count is not an integer.""" + wrong_type = "42" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + ], + [ + ] + ) as dmm: + with pytest.raises(TypeError) as err_info: + dmm.read_data(wrong_type) + err_msg = err_info.value.args[0] + assert err_msg == 'Parameter "sample_count" must be an integer.' + def test_agilent34410a_read_data_nvmem(): with expected_protocol( ik.agilent.Agilent34410a, @@ -118,3 +228,18 @@ def test_agilent34410a_read_last_data(): ] ) as dmm: unit_eq(dmm.read_last_data(), 1.73730000E-03 * u.volt) + + +def test_agilent34410a_read_last_data_na(): + """Return 9.91e37 if no data are available to read.""" + na_value_str = "9.91000000E+37" + with expected_protocol( + ik.agilent.Agilent34410a, + [ + "DATA:LAST?" + ], + [ + na_value_str + ] + ) as dmm: + assert dmm.read_last_data() == float(na_value_str) From 0ef885fdc450be481b11554a4d87cbc63ebaa3fe Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 22 Sep 2020 20:37:14 -0700 Subject: [PATCH 058/108] Test suite and BFs for Keithley580 (#263) * Test suite and BFs for Keithley580 Test suite with full coverage added **BFs**: - Removed inaccessible code block - Replaced a not in `enum` statement with a check of `isinstance`, now the test is compatible to the error description that is raised. - Getting the 'auto' input was previously not possible, fixed the respective property getter. - Fixed typos - Fixed status word getting such that an IOError can actually be raised now if failing to receive a status word after given number of tries - Fixed reading of bytes: same issue as in Keithley195, trying to unpack a string with `struct.unpack`. Now consistent and functional. * Read measurement and status word using `read_raw` Preferred method over using `query`, since `query` decodes the return, which then needs to be encoded again prior to unpacking with `struct`. Now, the return is read using `read_raw`, which can directly be unpacked. Note: `query('')` still sends a `:`, which is now sent using `sendcmd` prior to running `read_raw`. It also strips the last character from the return, which done here as well. Updated tests. * Status word now created according to manual, read in w/o last byte The status word, according to the manual, contains the termination character. It is unclear if this is the same as the end line end character, was implemented that way before. The test is generating a termination character different from the line end character (to keep it general). The unpacking of the status word is limited to the 13 bytes that we want to unpack. * Removed terminator from status word createn in test fixture The terminator is already added by expected protocol. Co-authored-by: Steven Casagrande --- instruments/keithley/keithley580.py | 88 +- .../tests/test_keithley/test_keithley580.py | 874 ++++++++++++++++++ 2 files changed, 919 insertions(+), 43 deletions(-) create mode 100644 instruments/tests/test_keithley/test_keithley580.py diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index 6f787f9b1..5a53b01e2 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -108,11 +108,8 @@ def polarity(self): value = self.parse_status_word(self.get_status_word())['polarity'] if value == '+': return Keithley580.Polarity.positive - elif value == '-': - return Keithley580.Polarity.negative else: - raise ValueError('Not a valid polarity returned from ' - 'instrument, got {}'.format(value)) + return Keithley580.Polarity.negative @polarity.setter def polarity(self, newval): @@ -248,7 +245,7 @@ def trigger_mode(self): def trigger_mode(self, newval): if isinstance(newval, str): newval = Keithley580.TriggerMode[newval] - if newval not in Keithley580.TriggerMode: + if not isinstance(newval, Keithley580.TriggerMode): raise TypeError('Drive must be specified as a ' 'Keithley580.TriggerMode, got {} ' 'instead.'.format(newval)) @@ -262,8 +259,11 @@ def input_range(self): :type: `~quantities.quantity.Quantity` or `str` """ - value = float(self.parse_status_word(self.get_status_word())['range']) - return value * u.ohm + value = self.parse_status_word(self.get_status_word())['range'] + if isinstance(value, str): # if range is 'auto' + return value + else: + return float(value) * u.ohm @input_range.setter def input_range(self, newval): @@ -324,7 +324,7 @@ def store_calibration_constants(self): not currently implemented. """ # self.write('L0X') - raise NotImplementedError('setCalibrationConstants not implemented') + raise NotImplementedError('storeCalibrationConstants not implemented') def get_status_word(self): """ @@ -336,13 +336,14 @@ def get_status_word(self): """ tries = 5 statusword = '' - while statusword[:3] != '580' and tries != 0: + while statusword[:3] != b'580' and tries != 0: tries -= 1 self.sendcmd('U0X') time.sleep(1) - statusword = self.query('') + self.sendcmd('') + statusword = self._file.read_raw() - if statusword is None: + if tries == 0: raise IOError('could not retrieve status word') return statusword[:-1] @@ -361,28 +362,28 @@ def parse_status_word(self, statusword): :rtype: `dict` """ - if statusword[:3] != '580': + if statusword[:3] != b'580': raise ValueError('Status word starts with wrong ' 'prefix: {}'.format(statusword)) (drive, polarity, drycircuit, operate, rng, relative, eoi, trigger, sqrondata, sqronerror, - linefreq) = struct.unpack('@8c2s2s2', statusword[3:]) - - valid = {'drive': {'0': 'pulsed', - '1': 'dc'}, - 'polarity': {'0': '+', - '1': '-'}, - 'range': {'0': 'auto', - '1': 0.2, - '2': 2, - '3': 20, - '4': 2e2, - '5': 2e3, - '6': 2e4, - '7': 2e5}, - 'linefreq': {'0': '60Hz', - '1': '50Hz'}} + linefreq) = struct.unpack('@8c2s2sc', statusword[3:16]) + + valid = {'drive': {b'0': 'pulsed', + b'1': 'dc'}, + 'polarity': {b'0': '+', + b'1': '-'}, + 'range': {b'0': 'auto', + b'1': 0.2, + b'2': 2, + b'3': 20, + b'4': 2e2, + b'5': 2e3, + b'6': 2e4, + b'7': 2e5}, + 'linefreq': {b'0': '60Hz', + b'1': '50Hz'}} try: drive = valid['drive'][drive] @@ -395,12 +396,12 @@ def parse_status_word(self, statusword): return {'drive': drive, 'polarity': polarity, - 'drycircuit': (drycircuit == '1'), - 'operate': (operate == '1'), + 'drycircuit': (drycircuit == b'1'), + 'operate': (operate == b'1'), 'range': rng, - 'relative': (relative == '1'), + 'relative': (relative == b'1'), 'eoi': eoi, - 'trigger': (trigger == '1'), + 'trigger': (trigger == b'1'), 'sqrondata': sqrondata, 'sqronerror': sqronerror, 'linefreq': linefreq, @@ -416,7 +417,8 @@ def measure(self): :rtype: `~quantities.quantity.Quantity` """ self.trigger() - return self.parse_measurement(self.query(''))['resistance'] + self.sendcmd('') + return self.parse_measurement(self._file.read_raw()[:-1])['resistance'] @staticmethod def parse_measurement(measurement): @@ -434,16 +436,16 @@ def parse_measurement(measurement): (status, polarity, drycircuit, drive, resistance) = \ struct.unpack('@4c11s', measurement) - valid = {'status': {'S': 'standby', - 'N': 'normal', - 'O': 'overflow', - 'Z': 'relative'}, - 'polarity': {'+': '+', - '-': '-'}, - 'drycircuit': {'N': False, - 'D': True}, - 'drive': {'P': 'pulsed', - 'D': 'dc'}} + valid = {'status': {b'S': 'standby', + b'N': 'normal', + b'O': 'overflow', + b'Z': 'relative'}, + 'polarity': {b'+': '+', + b'-': '-'}, + 'drycircuit': {b'N': False, + b'D': True}, + 'drive': {b'P': 'pulsed', + b'D': 'dc'}} try: status = valid['status'][status] polarity = valid['polarity'][polarity] diff --git a/instruments/tests/test_keithley/test_keithley580.py b/instruments/tests/test_keithley/test_keithley580.py new file mode 100644 index 000000000..a16fa7035 --- /dev/null +++ b/instruments/tests/test_keithley/test_keithley580.py @@ -0,0 +1,874 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Keithley 580 digital multimeter. +""" + +# IMPORTS #################################################################### + + +import struct +import time + +from hypothesis import ( + given, + strategies as st, +) +import pytest + +import instruments as ik +import instruments.units as u +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +# pylint: disable=redefined-outer-name + + +# PYTEST FIXTURES FOR INITIALIZATION # + + +@pytest.fixture +def init(): + """Returns the initialization command that is sent to instrument.""" + return "Y:X:" + + +@pytest.fixture +def create_statusword(): + """Create a function that can create a status word. + + Variables used in tests can be set manually, but useful default + values are set as well. Note: The terminator is not created, since + it is already sent by `expected_protocol`. + + :return: Method to make a status word. + :rtype: `method` + """ + def make_statusword(drive=b"1", polarity=b"0", drycircuit=b"0", + operate=b"0", rng=b"0", relative=b"0", trigger=b"1", + linefreq=b"0"): + """Create the status word.""" + # other variables + eoi = b"0" + sqrondata = b"0" + sqronerror = b"0" + + status_word = struct.pack('@8c2s2sc', drive, polarity, drycircuit, + operate, rng, relative, eoi, trigger, + sqrondata, sqronerror, linefreq) + + return b"580" + status_word + + return make_statusword + + +@pytest.fixture +def create_measurement(): + """Create a function that can create a measurement. + + Variables used in tests can be set manually, but useful default + values are set as well. + + :return: Method to make a measurement. + :rtype: `method` + """ + def make_measurement(status=b"N", polarity=b"+", drycircuit=b"D", + drive=b"P", resistance=b"42"): + """Create a measurement.""" + resistance = bytes(resistance.decode().zfill(11), "utf-8") + measurement = struct.pack('@4c11s', status, polarity, drycircuit, + drive, resistance) + + return measurement + + return make_measurement + + +@pytest.fixture(autouse=True) +def mock_time(mocker): + """Mock the time.sleep object for use. + + Use by default, such that getting status word is fast in tests. + """ + return mocker.patch.object(time, 'sleep', return_value=None) + + +# PROPERTIES # + + +@pytest.mark.parametrize("newval", ik.keithley.Keithley580.Polarity) +def test_polarity(init, create_statusword, newval): + """Get / set instrument polarity.""" + status_word = create_statusword(polarity=bytes(str(newval.value), "utf-8")) + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"P{newval.value}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.polarity = newval + assert inst.polarity == newval + + +@pytest.mark.parametrize("newval_str", [it.name for it in + ik.keithley.Keithley580.Polarity]) +def test_polarity_string(init, newval_str): + """Set polarity with a string.""" + newval = ik.keithley.Keithley580.Polarity[newval_str] + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"P{newval.value}X" + ":", + ], + [ + ], + sep="\n" + ) as inst: + inst.polarity = newval_str + + +def test_polarity_wrong_type(init): + """Raise TypeError if setting polarity with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.polarity = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Polarity must be specified as a " \ + f"Keithley580.Polarity, got {wrong_type} " \ + f"instead." + + +@pytest.mark.parametrize("newval", ik.keithley.Keithley580.Drive) +def test_drive(init, create_statusword, newval): + """Get / set instrument drive.""" + status_word = create_statusword(drive=bytes(str(newval.value), "utf-8")) + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"D{newval.value}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.drive = newval + assert inst.drive == newval + + +@pytest.mark.parametrize("newval_str", [it.name for it in + ik.keithley.Keithley580.Drive]) +def test_drive_string(init, newval_str): + """Set drive with a string.""" + newval = ik.keithley.Keithley580.Drive[newval_str] + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"D{newval.value}X" + ":", + ], + [ + ], + sep="\n" + ) as inst: + inst.drive = newval_str + + +def test_drive_wrong_type(init): + """Raise TypeError if setting drive with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.drive = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Drive must be specified as a " \ + f"Keithley580.Drive, got {wrong_type} " \ + f"instead." + + +@pytest.mark.parametrize("newval", (True, False)) +def test_dry_circuit_test(init, create_statusword, newval): + """Get / set dry circuit test.""" + status_word = create_statusword( + drycircuit=bytes(str(int(newval)), "utf-8")) + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"C{int(newval)}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.dry_circuit_test = newval + assert inst.dry_circuit_test == newval + + +def test_dry_circuit_test_wrong_type(init): + """Raise TypeError if setting dry circuit test with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.dry_circuit_test = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == "DryCircuitTest mode must be a boolean." + + +@pytest.mark.parametrize("newval", (True, False)) +def test_operate(init, create_statusword, newval): + """Get / set operate.""" + status_word = create_statusword( + operate=bytes(str(int(newval)), "utf-8")) + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"O{int(newval)}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.operate = newval + assert inst.operate == newval + + +def test_operate_wrong_type(init): + """Raise TypeError if setting operate with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.operate = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == "Operate mode must be a boolean." + + +@pytest.mark.parametrize("newval", (True, False)) +def test_relative(init, create_statusword, newval): + """Get / set relative.""" + status_word = create_statusword( + relative=bytes(str(int(newval)), "utf-8")) + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"Z{int(newval)}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.relative = newval + assert inst.relative == newval + + +def test_relative_wrong_type(init): + """Raise TypeError if setting relative with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.relative = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == "Relative mode must be a boolean." + + +def test_trigger_mode_get(init): + """Getting trigger mode raises NotImplementedError. + + Unclear why this is not implemented. + """ + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(NotImplementedError): + assert inst.trigger_mode + + +@pytest.mark.parametrize("newval", ik.keithley.Keithley580.TriggerMode) +def test_trigger_mode_set(init, newval): + """Set instrument trigger mode.""" + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"T{newval.value}X" + ":" + ], + [ + ], + sep="\n" + ) as inst: + inst.trigger_mode = newval + + +@pytest.mark.parametrize("newval", ik.keithley.Keithley580.TriggerMode) +def test_trigger_mode_set_string(init, newval): + """Set instrument trigger mode as a string.""" + newval_str = newval.name + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"T{newval.value}X" + ":" + ], + [ + ], + sep="\n" + ) as inst: + inst.trigger_mode = newval_str + + +def test_trigger_mode_set_type_error(init): + """Raise TypeError when setting trigger mode with wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.keithley.Keithley580, + [ + init + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.trigger_mode = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Drive must be specified as a " \ + f"Keithley580.TriggerMode, got " \ + f"{wrong_type} instead." + + +@pytest.mark.parametrize("newval", (2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5)) +def test_input_range_float(init, create_statusword, newval): + """Get / set input range with a float, unitful and unitless.""" + valid = ('auto', 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) + newval_unitful = newval * u.ohm + newval_index = valid.index(newval) + + status_word = create_statusword( + rng=bytes(str(newval_index), "utf-8")) + + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"R{newval_index}X" + ":", + f"R{newval_index}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.input_range = newval + inst.input_range = newval_unitful + assert inst.input_range == newval_unitful + + +def test_input_range_auto(init, create_statusword): + """Get / set input range auto.""" + newval = 'auto' + newval_index = 0 + + status_word = create_statusword( + rng=bytes(str(newval_index), "utf-8")) + + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + f"R{newval_index}X" + ":", + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + inst.input_range = newval + assert inst.input_range == newval + + +@given(newval=st.floats().filter( + lambda x: x not in (2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5))) +def test_input_range_float_value_error(init, newval): + """Raise ValueError if input range set to invalid value.""" + valid = ('auto', 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) + with expected_protocol( + ik.keithley.Keithley580, + [ + init + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.input_range = newval + err_msg = err_info.value.args[0] + assert err_msg == f"Valid range settings are: {valid}" + + +def test_input_range_auto_value_error(init): + """Raise ValueError if string set as input range is not 'auto'.""" + newval = 'automatic' + + with expected_protocol( + ik.keithley.Keithley580, + [ + init + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.input_range = newval + err_msg = err_info.value.args[0] + assert err_msg == "Only \"auto\" is acceptable when specifying the " \ + "input range as a string." + + +def test_input_range_type_error(init): + """Raise TypeError if input range is set with wrong type.""" + wrong_type = {"The Answer": 42} + + with expected_protocol( + ik.keithley.Keithley580, + [ + init + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.input_range = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Range setting must be specified as a float, " \ + f"int, or the string \"auto\", got " \ + f"{type(wrong_type)}" + + +# METHODS # + + +def test_trigger(init): + """Send a trigger to instrument.""" + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + "X:" + ], + [ + ], + sep="\n" + ) as inst: + inst.trigger() + + +def test_auto_range(init): + """Put instrument into auto range mode.""" + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + "R0X:" + ], + [ + ], + sep="\n" + ) as inst: + inst.auto_range() + + +def test_set_calibration_value(init): + """Raise NotImplementedError when trying to set calibration value.""" + value = None + with expected_protocol( + ik.keithley.Keithley580, + [ + init + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(NotImplementedError) as err_info: + inst.set_calibration_value(value) + err_msg = err_info.value.args[0] + assert err_msg == "setCalibrationValue not implemented" + + +def test_store_calibration_constants(init): + """Raise NotImplementedError when trying to store calibration constants.""" + with expected_protocol( + ik.keithley.Keithley580, + [ + init + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(NotImplementedError) as err_info: + inst.store_calibration_constants() + err_msg = err_info.value.args[0] + assert err_msg == "storeCalibrationConstants not implemented" + + +# STATUS WORD # + + +def test_get_status_word(init, create_statusword, mock_time): + """Test getting a default status word.""" + status_word = create_statusword() + + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + "U0X:", + ":" + ], + [ + status_word + b":" + ], + sep="\n" + ) as inst: + assert inst.get_status_word() == status_word + mock_time.assert_called_with(1) + + +def test_get_status_word_fails(init, mock_time): + """Raise IOError after 5 reads with bad returns.""" + wrong_status_word = b"195 12345" + + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + "U0X:", + ":", + "U0X:", + ":", + "U0X:", + ":", + "U0X:", + ":", + "U0X:", + ":" + ], + [ + wrong_status_word, + wrong_status_word, + wrong_status_word, + wrong_status_word, + wrong_status_word + ], + sep="\n" + ) as inst: + with pytest.raises(IOError) as err_info: + inst.get_status_word() + err_msg = err_info.value.args[0] + assert err_msg == "could not retrieve status word" + + mock_time.assert_called_with(1) + + +@pytest.mark.parametrize("line_frequency", (("0", "60Hz"), ("1", "50Hz"))) +def test_parse_status_word(init, create_statusword, line_frequency): + """Parse a given status word. + + Note: full range of parameters explored in individual routines. + Here, we thus just use the default status word created by the + fixture and only parametrize where other routines do not. + """ + status_word = create_statusword( + linefreq=bytes(line_frequency[0], "utf-8") + ) + # create the dictionary to compare to + expected_dict = { + "drive": "dc", + "polarity": "+", + "drycircuit": False, + "operate": False, + "range": "auto", + "relative": False, + "eoi": b"0", + "trigger": True, + "sqrondata": struct.pack('@2s', b"0"), + "sqronerror": struct.pack('@2s', b"0"), + "linefreq": line_frequency[1], + } + with expected_protocol( + ik.keithley.Keithley580, + [ + init + + ], + [ + ], + sep="\n" + ) as inst: + # add terminator to expected dict: + expected_dict["terminator"] = inst.terminator + assert inst.parse_status_word(status_word) == expected_dict + + +@given(drive=st.integers(min_value=2, max_value=9), + polarity=st.integers(min_value=2, max_value=9), + rng=st.integers(min_value=8, max_value=9), + linefreq=st.integers(min_value=2, max_value=9)) +def test_parse_status_word_invalid_values(init, create_statusword, drive, + polarity, rng, linefreq): + """Raise RuntimeError if status word contains invalid values.""" + status_word = create_statusword( + drive=bytes(str(drive), "utf-8"), + polarity=bytes(str(polarity), "utf-8"), + rng=bytes(str(rng), "utf-8"), + linefreq=bytes(str(linefreq), "utf-8") + ) + with expected_protocol( + ik.keithley.Keithley580, + [ + init + + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(RuntimeError) as err_info: + inst.parse_status_word(status_word) + err_msg = err_info.value.args[0] + assert err_msg == f"Cannot parse status word: {status_word}" + + +def test_parse_status_word_invalid_prefix(init): + """Raise ValueError if status word has invalid prefix.""" + invalid_status_word = b"314 424242" + with expected_protocol( + ik.keithley.Keithley580, + [ + init + + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.parse_status_word(invalid_status_word) + err_msg = err_info.value.args[0] + assert err_msg == f"Status word starts with wrong prefix: " \ + f"{invalid_status_word}" + + +# MEASUREMENT # + + +@given(resistance=st.floats(min_value=0.001, max_value=1000000)) +def test_measure(init, create_measurement, resistance): + """Perform a resistance measurement.""" + # cap resistance at max of 11 character with given max_value + resistance_byte = bytes(f"{resistance:.3f}", "utf-8") + measurement = create_measurement(resistance=resistance_byte) + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + "X:", # trigger + ":" + ], + [ + measurement + b":" + ], + sep="\n" + ) as inst: + read_value = inst.measure() + assert read_value.magnitude == pytest.approx(resistance) + assert read_value.units == u.ohm + + +@pytest.mark.parametrize("status", (b"S", b"N", b"O", b"Z")) +@pytest.mark.parametrize("polarity", (b"+", b"-")) +@pytest.mark.parametrize("drycircuit", (b"N", b"D")) +@pytest.mark.parametrize("drive", (b"P", b"D")) +def test_parse_measurement(init, create_measurement, status, polarity, + drycircuit, drive): + """Parse a given measurement.""" + resistance = b"42" + measurement = create_measurement( + status=status, + polarity=polarity, + drycircuit=drycircuit, + drive=drive, + resistance=resistance + ) + + # valid states + valid = {'status': {b'S': 'standby', + b'N': 'normal', + b'O': 'overflow', + b'Z': 'relative'}, + 'polarity': {b'+': '+', + b'-': '-'}, + 'drycircuit': {b'N': False, + b'D': True}, + 'drive': {b'P': 'pulsed', + b'D': 'dc'}} + + # create expected dictionary + dict_expected = { + "status": valid["status"][status], + "polarity": valid["polarity"][polarity], + "drycircuit": valid["drycircuit"][drycircuit], + "drive": valid["drive"][drive], + "resistance": float(resistance.decode()) * u.ohm + } + + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + + ], + [ + ], + sep="\n" + ) as inst: + assert inst.parse_measurement(measurement) == dict_expected + + +def test_parse_measurement_invalid(init, create_measurement): + """Raise an exception if the status contains invalid character.""" + measurement = create_measurement(status=bytes("V", "utf-8")) + + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + + ], + [ + ], + sep="\n" + ) as inst: + with pytest.raises(Exception) as exc_info: + inst.parse_measurement(measurement) + err_msg = exc_info.value.args[0] + assert err_msg == f"Cannot parse measurement: {measurement}" + + +# COMMUNICATION METHODS # + + +def test_sendcmd(init): + """Send a command to the instrument.""" + cmd = "COMMAND" + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + cmd + ":" + + ], + [ + ], + sep="\n" + ) as inst: + inst.sendcmd(cmd) + + +def test_query(init): + """Query the instrument.""" + cmd = "COMMAND" + answer = "ANSWER" + with expected_protocol( + ik.keithley.Keithley580, + [ + init, + cmd + ":" + + ], + [ + answer + ":" + ], + sep="\n" + ) as inst: + assert inst.query(cmd) == answer From a768609efbbc28249248fc33e84bf5ae32a7a8b8 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 22 Sep 2020 21:08:24 -0700 Subject: [PATCH 059/108] Full coverage tests and BFs for Thorlabs APT (#265) **Bug fixes in `thorlabsapt.py`**: - turn tuple into list before doing list operations - make sure that `expect_data_len` is always provided for `querypacket` routine. Data lengths taken from manual. - Fixed typos --- .../tests/test_thorlabs/test_thorlabs_apt.py | 428 +++++++++++++++++- instruments/thorlabs/thorlabsapt.py | 21 +- 2 files changed, 436 insertions(+), 13 deletions(-) diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py index 9ff701c0e..10bd83fd1 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_apt.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -6,11 +6,13 @@ # IMPORTS #################################################################### -# pylint: disable=unused-import +# pylint: disable=unused-import,too-many-lines import struct +import warnings +from hypothesis import given, strategies as st import pytest import instruments.units as u @@ -24,7 +26,14 @@ # pylint: disable=protected-access,unused-argument -def test_apt_hw_info(): +hw_types_setup = ( + (45, "Multi-channel controller motherboard"), + (44, "Brushless DC controller"), + (3, "Unknown type: 3")) + + +@pytest.mark.parametrize("hw_type", hw_types_setup) +def test_apt_hw_info(hw_type): with expected_protocol( ik.thorlabs.ThorLabsAPT, [ @@ -47,7 +56,7 @@ def test_apt_hw_info(): # Model number "ABC-123".encode('ascii'), # HW type - 3, + hw_type[0], # FW version, 0xa1, 0xa2, 0xa3, # Notes @@ -66,7 +75,7 @@ def test_apt_hw_info(): # Check internal representations. # NB: we shouldn't do this in some sense, but these fields # act as an API to the APT subclasses. - assert apt._hw_type == "Unknown type: 3" + assert apt._hw_type == hw_type[1] assert apt._fw_version == "a1.a2.a3" assert apt._notes == "abcdefg" assert apt._hw_version == 42 @@ -81,6 +90,34 @@ def test_apt_hw_info(): ) +def test_apt_hw_info_io_error(mocker): + inst_class = ik.thorlabs.ThorLabsAPT + + # mock querying a packet and raise an IOError + io_error_mock = mocker.Mock() + io_error_mock.side_effect = IOError + mocker.patch.object(inst_class, 'querypacket', io_error_mock) + + with expected_protocol( + inst_class, + [ + ], + [ + ], + sep="" + ) as apt: + # IOError was raised, assert that defaults are still present + assert apt._serial_number is None + assert apt._model_number is None + assert apt._hw_type is None + assert apt._fw_version is None + assert apt._notes == "" + assert apt._hw_version is None + assert apt._mod_state is None + assert apt._n_channels == 0 + assert apt._channel == () + + # FIXTURES FOR APT TEST SUITE # @@ -195,6 +232,78 @@ def init_tim101(): return stdin, stdout +@pytest.fixture +def init_ksg101(): + """Return the send, receive value to initialize a KSG101 unit.""" + stdin = ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + stdout = ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "KSG101".encode('ascii'), + # HW type + 3, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 1 + ) + ).pack() + return stdin, stdout + + +@pytest.fixture +def init_kpz001(): + """Return the send, receive value to initialize a KPZ001 unit.""" + stdin = ThorLabsPacket( + message_id=ThorLabsCommands.HW_REQ_INFO, + param1=0x00, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + stdout = ThorLabsPacket( + message_id=ThorLabsCommands.HW_GET_INFO, + dest=0x01, + source=0x50, + data=hw_info_data.pack( + # Serial number + b'\x01\x02\x03\x04', + # Model number + "KPZ101".encode('ascii'), + # HW type + 3, + # FW version, + 0xa1, 0xa2, 0xa3, + # Notes + "abcdefg".encode('ascii'), + # HW version + 42, + # Mod state + 43, + # Number of channels + 1 + ) + ).pack() + return stdin, stdout + + # pylint: disable=redefined-outer-name @@ -677,6 +786,215 @@ def test_apt_pia_enabled_type_error(init_kim101): assert apt.channel[0].enabled +# APT PIEZO STAGE (APT_PS) # + + +def test_apt_ps_max_travel_no_response(init_kpz001): + with expected_protocol( + ik.thorlabs.APTPiezoStage, + [ + init_kpz001[0], + ThorLabsPacket( # read state + message_id=ThorLabsCommands.PZ_REQ_MAXTRAVEL, + param1=0x01, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + ], + [ + init_kpz001[1] + ], + sep="" + ) as apt: + assert apt.channel[0].max_travel == NotImplemented + + +def test_apt_ps_led_intensity(init_kpz001): + """Get / set LED intensity between zero and 1.""" + led_intensity = 0.73 + with expected_protocol( + ik.thorlabs.APTPiezoStage, + [ + init_kpz001[0], + ThorLabsPacket( # set state + message_id=ThorLabsCommands.PZ_SET_TPZ_DISPSETTINGS, + param1=None, param2=None, + dest=0x50, + source=0x01, + data=struct.pack(' 0)) + for key, bit_mask in apt_mc_channel_status_bit_mask.items() + ) + + with expected_protocol( + ik.thorlabs.APTMotorController, + [ + init_kdc101[0], + ThorLabsPacket( # read position + message_id=ThorLabsCommands.MOT_REQ_STATUSUPDATE, + param1=0x01, param2=0x00, + dest=0x50, + source=0x01, + data=None + ).pack() + ], + [ + init_kdc101[1], + ThorLabsPacket( + message_id=ThorLabsCommands.MOT_GET_POSCOUNTER, + param1=None, param2=None, + dest=0x50, + source=0x01, + data=struct.pack(' self._n_channels: - self._channel = self._channel + \ + self._channel = list(self._channel) + \ list(self._channel_type(self, chan_idx) for chan_idx in range(self._n_channels, nch)) elif nch < self._n_channels: @@ -282,7 +282,9 @@ def max_travel(self): source=0x01, data=None ) - resp = self._apt.querypacket(pkt) + resp = self._apt.querypacket( + pkt, expect_data_len=4 + ) # Not all APT piezo devices support querying the maximum travel # distance. Those that do not simply ignore the PZ_REQ_MAXTRAVEL @@ -309,7 +311,9 @@ def led_intensity(self): source=0x01, data=None ) - resp = self.querypacket(pkt) + resp = self.querypacket( + pkt, expect_data_len=2 + ) # Not all APT piezo devices support querying the LED intenstiy # distance, e.g., TIM, KIM. Those that do not simply ignore the @@ -963,7 +967,7 @@ def position_control_closed(self): `True` means that the position control is closed, `False` otherwise - :tyep: `bool` + :type: `bool` """ pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.PZ_REQ_POSCONTROLMODE, @@ -1012,7 +1016,9 @@ def output_position(self): data=None ) resp = self._apt.querypacket( - pkt, expect=_cmds.ThorLabsCommands.PZ_GET_OUTPUTPOS) + pkt, expect=_cmds.ThorLabsCommands.PZ_GET_OUTPUTPOS, + expect_data_len=4 + ) # chan, pos _, pos = struct.unpack(' Date: Tue, 22 Sep 2020 21:31:48 -0700 Subject: [PATCH 060/108] Test suite and BFs for Tektronix TDS224 (#266) Test suite extended for full coverage **Bug fixes**: - Using `map` to convert a list from string to floats is Py2 legacy and not allowed anymore. Now using `numpy` conversions. - Remove `if not self._testing` statement to introduce `time.sleep`: instead mocking time.sleep in test suite now with an autouse fixture. --- instruments/tektronix/tektds224.py | 6 +- .../test_tektronix/test_tektronix_tds224.py | 160 +++++++++++++++++- 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index f5e0e985b..db5b5e3af 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -72,8 +72,7 @@ def read_waveform(self, bin_format=True): # Set the data encoding format to ASCII raw = self._tek.query("CURVE?") raw = raw.split(',') # Break up comma delimited string - raw = map(float, raw) # Convert each list element to int - raw = np.array(raw) # Convert into numpy array + raw = np.array(raw, dtype=np.float) # Convert to ndarray else: self._tek.sendcmd("DAT:ENC RIB") # Set encoding to signed, big-endian @@ -227,8 +226,7 @@ def data_source(self, newval): elif hasattr(newval, "name"): # Is a datasource with a name. newval = newval.name self.sendcmd(f"DAT:SOU {newval}") - if not self._testing: - time.sleep(0.01) # Let the instrument catch up. + time.sleep(0.01) # Let the instrument catch up. @property def data_width(self): diff --git a/instruments/tests/test_tektronix/test_tektronix_tds224.py b/instruments/tests/test_tektronix/test_tektronix_tds224.py index 7cd75a05e..52aad0e1e 100644 --- a/instruments/tests/test_tektronix/test_tektronix_tds224.py +++ b/instruments/tests/test_tektronix/test_tektronix_tds224.py @@ -6,18 +6,57 @@ # IMPORTS #################################################################### +from enum import Enum +import time + +from hypothesis import given, strategies as st import numpy as np +import pytest import instruments as ik from instruments.tests import expected_protocol, make_name_test # TESTS ###################################################################### -# pylint: disable=protected-access +# pylint: disable=protected-access,redefined-outer-name + + +# FIXTURES # + + +@pytest.fixture(autouse=True) +def mock_time(mocker): + """Mock time to replace time.sleep.""" + return mocker.patch.object(time, 'sleep', return_value=None) + test_tektds224_name = make_name_test(ik.tektronix.TekTDS224) +def test_ref_init(): + """Initialize a reference channel.""" + with expected_protocol( + ik.tektronix.TekTDS224, + [ + ], + [ + ] + ) as tek: + assert tek.ref[0]._tek is tek + + +def test_data_source_name(): + """Get name of data source.""" + with expected_protocol( + ik.tektronix.TekTDS224, + [ + ], + [ + ] + ) as tek: + assert tek.math.name == "MATH" + + def test_tektds224_data_width(): with expected_protocol( ik.tektronix.TekTDS224, @@ -32,19 +71,58 @@ def test_tektds224_data_width(): tek.data_width = 1 -def test_tektds224_data_source(): +@given(width=st.integers().filter(lambda x: x > 2 or x < 1)) +def test_tektds224_data_width_value_error(width): + """Raise value error if data_width is out of range.""" + with expected_protocol( + ik.tektronix.TekTDS224, + [ + ], + [ + ] + ) as tek: + with pytest.raises(ValueError) as err_info: + tek.data_width = width + err_msg = err_info.value.args[0] + assert err_msg == "Only one or two byte-width is supported." + + +def test_tektds224_data_source(mock_time): with expected_protocol( ik.tektronix.TekTDS224, [ + "DAT:SOU?", "DAT:SOU?", "DAT:SOU MATH" ], [ + "MATH", "CH1" ] ) as tek: + assert tek.data_source == tek.math assert tek.data_source == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) tek.data_source = tek.math + # assert that time.sleep is called + mock_time.assert_called() + + +def test_tektds224_data_source_with_enum(): + """Set data source from an enum.""" + class Channel(Enum): + """Fake class to init data_source with enum.""" + channel = "MATH" + + with expected_protocol( + ik.tektronix.TekTDS224, + [ + "DAT:SOU MATH" + ], + [ + ] + ) as tek: + tek.data_source = Channel.channel + def test_tektds224_channel(): with expected_protocol( @@ -70,6 +148,23 @@ def test_tektds224_channel_coupling(): tek.channel[1].coupling = tek.Coupling.ac +def test_tektds224_channel_coupling_type_error(): + """Raise TypeError if coupling setting is wrong type.""" + wrong_type = 42 + with expected_protocol( + ik.tektronix.TekTDS224, + [ + ], + [ + ] + ) as tek: + with pytest.raises(TypeError) as err_info: + tek.channel[1].coupling = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == f"Coupling setting must be a `TekTDS224.Coupling` " \ + f"value,got {type(wrong_type)} instead." + + def test_tektds224_data_source_read_waveform(): with expected_protocol( ik.tektronix.TekTDS224, @@ -102,3 +197,64 @@ def test_tektds224_data_source_read_waveform(): (x, y) = tek.channel[1].read_waveform() assert (x == data).all() assert (y == data).all() + + +@given(values=st.lists(st.floats(allow_infinity=False, allow_nan=False), + min_size=1)) +def test_tektds224_data_source_read_waveform_ascii(values): + """Read waveform as ASCII""" + # values + values_str = ",".join([str(value) for value in values]) + + # parameters + yoffs = 1 + ymult = 1 + yzero = 0 + xzero = 0 + xincr = 1 + ptcnt = len(values) + + with expected_protocol( + ik.tektronix.TekTDS224, + [ + "DAT:SOU?", + "DAT:SOU CH2", + "DAT:ENC ASCI", + "CURVE?", + "WFMP:CH2:YOF?", + "WFMP:CH2:YMU?", + "WFMP:CH2:YZE?", + "WFMP:XZE?", + "WFMP:XIN?", + "WFMP:CH2:NR_P?", + "DAT:SOU CH1" + ], [ + "CH1", + values_str, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xzero}", + f"{xincr}", + f"{ptcnt}" + ] + ) as tek: + x_expected = np.arange(float(ptcnt)) * float(xincr) + float(xzero) + y_expected = ((np.array(values) - float(yoffs)) * float(ymult)) + \ + float(yzero) + x_read, y_read = tek.channel[1].read_waveform(bin_format=False) + np.testing.assert_equal(x_read, x_expected) + np.testing.assert_equal(y_read, y_expected) + + +def test_force_trigger(): + """Raise NotImplementedError when trying to force a trigger.""" + with expected_protocol( + ik.tektronix.TekTDS224, + [ + ], + [ + ] + ) as tek: + with pytest.raises(NotImplementedError): + tek.force_trigger() From c930722da31093625569688887e2326d1888320e Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 14 Oct 2020 14:33:58 -0400 Subject: [PATCH 061/108] Gentec-eo Blu support and test suite (#267) * Gentec-eo Blu support and test suite Added support for gentec-eo's Blu series power meters. These power meters connect via bluetooth, the bluetooth dongle showsvup as a regular COM port. In addition,vthey can be plugged into a USB port and vshow up as a regular serial interface as well. Full test suite added as well. All routines have example doc strings. Added to docs. * Add try, finally blocks to reset temporary variables if routines error Also included tests that ensure that it actually works. --- doc/source/apiref/gentec-eo.rst | 12 + doc/source/apiref/index.rst | 1 + instruments/__init__.py | 1 + instruments/gentec_eo/__init__.py | 3 + instruments/gentec_eo/blu.py | 650 +++++++++++++++++++ instruments/tests/test_gentec_eo/__init__.py | 0 instruments/tests/test_gentec_eo/test_blu.py | 619 ++++++++++++++++++ 7 files changed, 1286 insertions(+) create mode 100644 doc/source/apiref/gentec-eo.rst create mode 100644 instruments/gentec_eo/__init__.py create mode 100644 instruments/gentec_eo/blu.py create mode 100644 instruments/tests/test_gentec_eo/__init__.py create mode 100644 instruments/tests/test_gentec_eo/test_blu.py diff --git a/doc/source/apiref/gentec-eo.rst b/doc/source/apiref/gentec-eo.rst new file mode 100644 index 000000000..b9c681068 --- /dev/null +++ b/doc/source/apiref/gentec-eo.rst @@ -0,0 +1,12 @@ +.. currentmodule:: instruments.gentec_eo + +========= +Gentec-EO +========= + +:class:`Blu` Power Meter +======================================= + +.. autoclass:: Blu + :members: + :undoc-members: \ No newline at end of file diff --git a/doc/source/apiref/index.rst b/doc/source/apiref/index.rst index e60a2eb6d..8ab9ce1b4 100644 --- a/doc/source/apiref/index.rst +++ b/doc/source/apiref/index.rst @@ -15,6 +15,7 @@ Contents: generic_scpi agilent fluke + gentec-eo glassman holzworth hp diff --git a/instruments/__init__.py b/instruments/__init__.py index 0f0640748..0b93dcc1c 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -13,6 +13,7 @@ from . import agilent from . import generic_scpi from . import fluke +from . import gentec_eo from . import glassman from . import holzworth from . import hp diff --git a/instruments/gentec_eo/__init__.py b/instruments/gentec_eo/__init__.py new file mode 100644 index 000000000..85200fda3 --- /dev/null +++ b/instruments/gentec_eo/__init__.py @@ -0,0 +1,3 @@ +"""Module containing Gentec-eo instruments.""" + +from .blu import Blu diff --git a/instruments/gentec_eo/blu.py b/instruments/gentec_eo/blu.py new file mode 100644 index 000000000..7527feb6a --- /dev/null +++ b/instruments/gentec_eo/blu.py @@ -0,0 +1,650 @@ +"""Support for Gentec-EO Blu devices.""" + + +# IMPORTS ##################################################################### + +from enum import Enum +from time import sleep + +from instruments.abstract_instruments import Instrument +import instruments.units as u +from instruments.util_fns import assume_units + +# CLASSES ##################################################################### + + +class Blu(Instrument): + """Communicate with Gentec-eo BLU power / energy meter interfaces. + + These instruments communicate via USB or via bluetooth. The + bluetooth sender / receiver that is provided with the instrument is + simply emulating a COM port. This routine cannot pair the device + with bluetooth, but once it is paired, it can communicate with the + port. Alternatively, you can plug the device into the computer using + a USB cable. + + .. warning:: If commands are issued too fast, the device will not + answer. Experimentally, a 1 ms delay should be enough to get the + device into answering mode. Keep this in mind when issuing many + commands at once. No wait time included in this class. + + .. note:: The instrument also has a possiblity to read a continuous + data stream. This is currently not implemented here since it + would have to be threaded out. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.current_value + 3.004 W + """ + + def __init__(self, filelike): + super(Blu, self).__init__(filelike) + + # use a terminator for blu, even though none required + self.terminator = "\r\n" + + # define the power mode + self._power_mode = None + + # acknowledgement message + self._ack_message = "ACK" + + def _ack_expected(self, msg=""): + """Set up acknowledgement checking.""" + return self._ack_message + + # ENUMS # + + class Scale(Enum): + """Available scales for Blu devices. + + The following list maps available scales of the Blu devices + to the respective indexes. All scales are either in watts or + joules, depending if power or energy mode is activated. + Furthermore, the maximum value that can be measured determines + the name of the scale to be set. Prefixes are given in the + `enum` class while the unit is omitted since it depends on the + mode the head is in. + """ + max1pico = "00" + max3pico = "01" + max10pico = "02" + max30pico = "03" + max100pico = "04" + max300pico = "05" + max1nano = "06" + max3nano = "07" + max10nano = "08" + max30nano = "09" + max100nano = "10" + max300nano = "11" + max1micro = "12" + max3micro = "13" + max10micro = "14" + max30micro = "15" + max100micro = "16" + max300micro = "17" + max1milli = "18" + max3milli = "19" + max10milli = "20" + max30milli = "21" + max100milli = "22" + max300milli = "23" + max1 = "24" + max3 = "25" + max10 = "26" + max30 = "27" + max100 = "28" + max300 = "29" + max1kilo = "30" + max3kilo = "31" + max10kilo = "32" + max30kilo = "33" + max100kilo = "34" + max300kilo = "35" + max1Mega = "36" + max3Mega = "37" + max10Mega = "38" + max30Mega = "39" + max100Mega = "40" + max300Mega = "41" + + # PROPERTIES # + + @property + def anticipation(self): + """Get / Set anticipation. + + This command is used to enable or disable the anticipation + processing when the device is reading from a wattmeter. The + anticipation is a software-based acceleration algorithm that + provides faster readings using the detector’s calibration. + + :return: Is anticipation enabled or not. + :rtype: bool + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.anticipation + True + >>> inst.anticipation = False + """ + return self._value_query("*GAN", tp=int) == 1 + + @anticipation.setter + def anticipation(self, newval): + sendval = 1 if newval else 0 + self.sendcmd("*ANT{}".format(sendval)) + + @property + def auto_scale(self): + """Get / Set auto scale on the device. + + :return: Status of auto scale enabled feature. + :rtype: bool + + :raises ValueError: The command was not acknowledged by the + device. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.auto_scale + True + >>> inst.auto_scale = False + """ + resp = self._value_query("*GAS", tp=int) + return resp == 1 + + @auto_scale.setter + def auto_scale(self, newval): + sendval = 1 if newval else 0 + self.sendcmd("*SAS{}".format(sendval)) + + @property + def available_scales(self): + """Get available scales from connected device. + + :return: Scales currently available on device. + :rtype: :class:`Blu.Scale` + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.available_scales + [, , + , , , + , ] + """ + # set no terminator and a 1 second timeout + _terminator = self.terminator + self.terminator = "" + _timeout = self.timeout + self.timeout = u.Quantity(1, u.s) + + try: + # get the response + resp = self._no_ack_query("*DVS").split('\r\n') + finally: + # set back terminator and 3 second timeout + self.terminator = _terminator + self.timeout = _timeout + + # prepare return + retlist = [] # init return list of enums + for line in resp: + if len(line) > 0: # account for empty lines + index = line[line.find("[")+1:line.find("]")] + retlist.append(self.Scale(index)) + return retlist + + @property + def battery_state(self): + """Get the charge state of the battery. + + :return: Charge state of battery + :rtype: u.percent + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.battery_state + array(100.) * % + """ + resp = self._no_ack_query("*QSO").rstrip() + resp = float(resp[resp.find("=")+1:len(resp)]) + return u.Quantity(resp, u.percent) + + @property + def current_value(self): + """Get the currently measured value (unitful). + + :return: Currently measured value + :rtype: u.Quantity + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.current_value + 3.004 W + """ + if self._power_mode is None: + _ = self.measure_mode # determine the power mode + sleep(0.01) + + unit = u.W if self._power_mode else u.J + return u.Quantity(float(self._no_ack_query("*CVU")), unit) + + @property + def head_type(self): + """Get the head type information. + + :return: Type of instrument head. + :rtype: str + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.head_type + 'NIG : 104552, Wattmeter, V1.95' + """ + return self._no_ack_query("*GFW") + + @property + def measure_mode(self): + """Get the current measurement mode. + + Potential return values are 'power', which inidcates power mode + in W and 'sse', indicating single shot energy mode in J. + + :return: 'power' if in power mode, 'sse' if in single shot + energy mode. + :rtype: str + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.measure_mode + 'power' + """ + resp = self._value_query("*GMD", tp=int) + if resp == 0: + self._power_mode = True + return 'power' + else: + self._power_mode = False + return 'sse' + + @property + def new_value_ready(self): + """Get status if a new value is ready. + + This command is used to check whether a new value is available + from the device. Though optional, its use is recommended when + used with single pulse operation. + + :return: Is a new value ready? + :rtype: bool + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.new_value_ready + False + """ + resp = self._no_ack_query("*NVU") + return False if resp.find("Not") > -1 else True + + @property + def scale(self): + """Get / Set measurement scale. + + The measurement scales are chosen from the the `Scale` enum + class. Scales are either in watts or joules, depending on what + state the power meter is currently in. + + .. note:: Setting a scale manually will automatically turn of + auto scale. + + :return: Scale that is currently set. + :rtype: :class:`Blu.Scale` + + :raises ValueError: The command was not acknowledged by the + device. A scale that is not available might have been + selected. Use `available_scales` to display scales that + are possible on your device. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.scale = inst.Scale.max3 + >>> inst.scale + + """ + return self.Scale(self._value_query("*GCR")) + + @scale.setter + def scale(self, newval): + self.sendcmd("*SCS{}".format(newval.value)) + + @property + def single_shot_energy_mode(self): + """ Get / Set single shot energy mode. + + :return: Is single shot energy mode turned on? + :rtype: bool + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.single_shot_energy_mode + False + >>> inst.single_shot_energy_mode = True + """ + val = self._value_query("*GSE", tp=int) == 1 + self._power_mode = False if val else True + return val + + @single_shot_energy_mode.setter + def single_shot_energy_mode(self, newval): + sendval = 1 if newval else 0 # set send value + self._power_mode = False if newval else True # set power mode + self.sendcmd("*SSE{}".format(sendval)) + + @property + def trigger_level(self): + """Get / Set trigger level when in energy mode. + + The trigger level must be between 0.001 and 0.998. + + :return: Trigger level (absolute) with respect to the currently + set scale + :rtype: float + + :raise ValueError: Trigger level out of range. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.trigger_level = 0.153 + >>> inst.trigger_level + 0.153 + + """ + level = self._no_ack_query("*GTL") + # get the percent + retval = float(level[level.find(":")+1:level.find("%")]) / 100 + return retval + + @trigger_level.setter + def trigger_level(self, newval): + if newval < 0.001 or newval > 0.99: + raise ValueError("Trigger level {} is out of range. It must be " + "between 0.001 and 0.998.".format(newval)) + + newval = newval * 100. + if newval >= 10: + newval = str(round(newval, 1)).zfill(4) + else: + newval = str(round(newval, 2)).zfill(4) + + self.sendcmd("*STL{}".format(newval)) + + @property + def usb_state(self): + """Get status if USB cable is connected. + + :return: Is a USB cable connected? + :rtype: bool + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.usb_state + True + """ + return self._value_query("*USB", tp=int) == 1 + + @property + def user_multiplier(self): + """Get / Set user multiplier. + + :return: User multiplier + :rtype: u.Quantity + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.user_multiplier = 10 + >>> inst.user_multiplier + 10.0 + """ + return self._value_query("*GUM", tp=float) + + @user_multiplier.setter + def user_multiplier(self, newval): + sendval = _format_eight(newval) # sendval: 8 characters long + self.sendcmd("*MUL{}".format(sendval)) + + @property + def user_offset(self): + """Get / Set user offset. + + The user offset can be set unitful in watts or joules and set + to the device. + + :return: User offset + :rtype: u.Quantity + + :raises ValueError: Unit not supported or value for offset is + out of range. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.user_offset = 10 + >>> inst.user_offset + array(10.) * W + """ + if self._power_mode is None: + _ = self.measure_mode # determine the power mode + sleep(0.01) + + if self._power_mode: + return assume_units(self._value_query("*GUO", tp=float), u.W) + else: + return assume_units(self._value_query("*GUO", tp=float), u.J) + + @user_offset.setter + def user_offset(self, newval): + # if unitful, try to rescale and grab magnitude + if isinstance(newval, u.Quantity): + try: + newval = newval.rescale(u.W).magnitude + except ValueError: # so it's not in W + try: + newval = newval.rescale(u.J).magnitude + except ValueError: # invalid unit + raise ValueError("Value must be given in watts, " + "joules, or unitless.") + sendval = _format_eight(newval) # sendval: 8 characters long + self.sendcmd("*OFF{}".format(sendval)) + + @property + def version(self): + """Get device information. + + :return: Version and device type + :rtype: str + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.version + 'Blu firmware Version 1.95' + """ + return self._no_ack_query("*VER") + + @property + def wavelength(self): + """Get / Set the wavelength. + + The wavelength can be set unitful. Specifying zero as a + wavelength or providing an out-of-bound value as a parameter + restores the default settings, typically 1064nm. If no units + are provided, nm are assumed. + + :return: Wavelength in nm + :rtype: u.Quantity + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.wavelength = u.Quantity(527, u.nm) + >>> inst.wavelength + array(527) * nm + """ + return u.Quantity(self._value_query("*GWL", tp=int), u.nm) + + @wavelength.setter + def wavelength(self, newval): + val = assume_units(newval, u.nm).rescale(u.nm).magnitude.round(0) + if val >= 1000000 or val < 0: # can only send 5 digits + val = 0 # out of bound anyway + val = str(int(val)).zfill(5) + self.sendcmd("*PWC{}".format(val)) + + @property + def zero_offset(self): + """Get / Set zero offset. + + Gets the status if zero offset is enabled. When set to `True`, + the device will read the current level immediately for around + three seconds and then set the baseline to the averaged value. + If activated and set to `True` again, a new value for the + baseline will be established. + + :return: Is zero offset enabled? + :rtype: bool + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.zero_offset + True + >>> inst.zero_offset = False + """ + return self._value_query("*GZO", tp=int) == 1 + + @zero_offset.setter + def zero_offset(self, newval): + if newval: + self.sendcmd("*SOU") + else: + self.sendcmd("*COU") + + # METHODS # + + def confirm_connection(self): + """Confirm a connection to the device. + + Turns of bluetooth searching by confirming a connection. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.confirm_connection() + """ + self.sendcmd("*RDY") + + def disconnect(self): + """Disconnect the device. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.disconnect() + """ + self.sendcmd("*BTD") + + def scale_down(self): + """Set scale to next lower level. + + Sets the power meter to the next lower scale. If already at + the lowest possible scale, no change will be made. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.scale_down() + """ + self.sendcmd("*SSD") + + def scale_up(self): + """Set scale to next higher level. + + Sets the power meter to the next higher scale. If already at + the highest possible scale, no change will be made. + + Example: + >>> import instruments as ik + >>> inst = ik.gentec_eo.Blu.open_serial('/dev/ttyACM0') + >>> inst.scale_up() + """ + self.sendcmd("*SSU") + + # PRIVATE METHODS # + + def _no_ack_query(self, cmd, size=-1): + """Query a value and don't expect an ACK message.""" + self._ack_message = None + try: + value = self.query(cmd, size=size) + finally: + self._ack_message = "ACK" + return value + + def _value_query(self, cmd, tp=str): + """Query one specific value and return it. + + :param cmd: Command to send to self._no_ack_query. + :type cmd: str + :param tp: Type of the value to be returned, default: str + :type tp: type + + :return: Single value of query. + :rtype: tp (selected type) + + :raises ValueError: Conversion of response into given type was + unsuccessful. + """ + resp = self._no_ack_query(cmd).rstrip() # strip \r\n + resp = resp.split(":")[1] # strip header off + resp = resp.replace(" ", "") # strip white space + if isinstance(resp, tp): + return resp + else: + return tp(resp) + + +def _format_eight(value): + """Formats a value to eight characters total. + + :param value: value to be formatted, > -1e100 and < 1e100 + :type value: int,float + + :return: Value formatted to 8 characters + :rtype: str + """ + if len(str(value)) > 8: + if value < 0: + value = "{0:.2g}".format(value).zfill(8) # make space for - + else: + value = "{0:.3g}".format(value).zfill(8) + else: + value = str(value).zfill(8) + return value diff --git a/instruments/tests/test_gentec_eo/__init__.py b/instruments/tests/test_gentec_eo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/instruments/tests/test_gentec_eo/test_blu.py b/instruments/tests/test_gentec_eo/test_blu.py new file mode 100644 index 000000000..2a8908a15 --- /dev/null +++ b/instruments/tests/test_gentec_eo/test_blu.py @@ -0,0 +1,619 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the Gentec-eo Blu +""" + +# IMPORTS #################################################################### + +from hypothesis import given, strategies as st +import pytest + +import instruments as ik +from instruments.tests import expected_protocol +import instruments.units as u + +# TESTS ###################################################################### + +# pylint: disable=protected-access + + +# TESTS FOR Blu # + + +def test_blu_initialization(): + """Initialize the device.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + ], + [ + ], + sep="\r\n", + ) as blu: + assert blu.terminator == "\r\n" + assert blu._power_mode is None + + +# TEST PROPERTIES # + + +def test_blu_anticipation(): + """Get / Set the instrument into anticipation mode.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GAN", + "*ANT0" + ], + [ + "Anticipation: 1", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.anticipation + blu.anticipation = False + + +def test_blu_auto_scale(): + """Get / Set the instrument into automatic scaling mode.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GAS", + "*SAS0" + ], + [ + "Autoscale: 1", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.auto_scale + blu.auto_scale = False + + +def test_blu_available_scales(): + """Get the available scales that are on teh blue device. + + Note that the routine tested here will temporarily overwrite the + terminator and the timeout. The function here is special in the + sense that it returns a list of parameters, all individual entries + are separated by the terminator. There is no clear end to when this + should be finished. It is assumed that 1 second is enough time to + send all the data. + """ + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*DVS" + ], + [ + "[22]: 100.0 m\r\n" + "[23]: 300.0 m\r\n" + "[24]: 1.000\r\n" + "[25]: 3.000\r\n" + "[26]: 10.00\r\n" + "[27]: 30.00\r\n" + "[28]: 100.0\r\n" + ], + sep="", + ) as blu: + ret_scale = [ + blu.Scale.max100milli, + blu.Scale.max300milli, + blu.Scale.max1, + blu.Scale.max3, + blu.Scale.max10, + blu.Scale.max30, + blu.Scale.max100 + ] + assert blu.available_scales == ret_scale + + +def test_blu_available_scales_error(): + """Ensure that temporary variables are reset if read errors. + + Return a `bogus` value, which is not an available scale, and ensure + that the temporary variables are reset afterwards. This specific + case raises a ValueError. + """ + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*DVS" + ], + [ + "bogus" + ], + sep="", + ) as blu: + _terminator = blu.terminator + _timeout = blu.timeout + with pytest.raises(ValueError): + _ = blu.available_scales + assert blu.terminator == _terminator + assert blu.timeout == _timeout + + +def test_blu_battery_state(): + """Get the battery state of the instrument in percent.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*QSO" + ], + [ + "98" + ], + sep="\r\n", + ) as blu: + assert blu.battery_state == u.Quantity(98, u.percent) + + +def test_blu_current_value_watts(): + """Get the current value in Watt mode.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GMD", + "*CVU" + ], + [ + "Mode: 0", + "42" + ], + sep="\r\n", + ) as blu: + assert blu.current_value == u.Quantity(42, u.W) + + +def test_blu_current_value_joules(): + """Get the current value in Watt mode.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GMD", + "*CVU" + ], + [ + "Mode: 2", + "42" + ], + sep="\r\n", + ) as blu: + assert blu.current_value == u.Quantity(42, u.J) + + +def test_blu_head_type(): + """Get information on the connected power meter head. + + Here, an example head is returned. + """ + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GFW" + ], + [ + "NIG : 104552, Wattmeter, V1.95" + ], + sep="\r\n", + ) as blu: + example_head = "NIG : 104552, Wattmeter, V1.95" + assert blu.head_type == example_head + + +def test_blu_measure_mode(): + """Get the measure mode the head is in. + + This routine is also run when a unitful response is returned from + another routine and the measurement mode has not been determined + before. + """ + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GMD", + "*GMD" + ], + [ + "Mode: 0", + "Mode: 2" + ], + sep="\r\n", + ) as blu: + # power mode + assert blu.measure_mode == "power" + assert blu._power_mode + + # single shot energy mode (J) + assert blu.measure_mode == "sse" + assert not blu._power_mode + + +def test_blu_new_value_ready(): + """Query if a new value is ready for reading.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*NVU", + "*NVU" + ], + [ + "New Data Not Available", + "New Data Available" + ], + sep="\r\n", + ) as blu: + assert not blu.new_value_ready + assert blu.new_value_ready + + +@pytest.mark.parametrize("scale", ik.gentec_eo.Blu.Scale) +def test_blu_scale(scale): + """Get / set the instrument scale manually.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + f"*SCS{scale.value}", + "*GCR" + ], + [ + "ACK", + f"Range: {scale.value}" + ], + sep="\r\n", + ) as blu: + blu.scale = scale + assert blu.scale == scale + + +def test_blu_single_shot_energy_mode(): + """Get / set the single shot energy mode.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GSE", + "*SSE1" + ], + [ + "SSE: 0", + "ACK" + ], + sep="\r\n", + ) as blu: + assert not blu.single_shot_energy_mode + assert blu._power_mode + blu.single_shot_energy_mode = True + assert not blu._power_mode + + +def test_blu_trigger_level(): + """Get / set the trigger level.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GTL", + "*STL53.4", + "*STL01.2", + "*STL1.23" + ], + [ + "Trigger level: 15.4% (4.6 Watts) of max power: 30 Watts", + "ACK", + "ACK", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.trigger_level == 0.154 + blu.trigger_level = 0.534 + blu.trigger_level = 0.012 + blu.trigger_level = 0.0123 + + +def test_blu_trigger_level_invalid_value(): + """Raise error when trigger level value set is out of bound.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + ], + [ + ], + sep="\r\n", + ) as blu: + with pytest.raises(ValueError): + blu.trigger_level = -0.3 + with pytest.raises(ValueError): + blu.trigger_level = 1.1 + + +def test_blu_usb_state(): + """Get the status if USB cable is plugged in.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*USB" + ], + [ + "USB: 1" + ], + sep="\r\n", + ) as blu: + assert blu.usb_state + + +def test_blu_user_multiplier(): + """Get / set user multiplier.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GUM", + "*MUL435.6666" + ], + [ + "User Multiplier: 3.3000000e+01", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.user_multiplier == 33. + blu.user_multiplier = 435.6666 + + +def test_blu_user_offset_watts(): + """Get / set user offset in watts.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GMD", # get power mode + "*GUO", + "*OFF000042.0" + + ], + [ + "Mode: 0", # power mode watts + "User Offset : 1.500e-3", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.user_offset == u.Quantity(1.5, u.mW) + blu.user_offset = u.Quantity(42.0, u.W) + + +def test_blu_user_offset_joules(): + """Get / set user offset in joules.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GMD", # get power mode + "*GUO", + "*OFF000042.0" + + ], + [ + "Mode: 2", # power mode watts + "User Offset : 1.500e-3", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.user_offset == u.Quantity(0.0015, u.J) + blu.user_offset = u.Quantity(42.0, u.J) + + +def test_blu_user_offset_unitless(): + """Set user offset unitless.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*OFF000042.0" + + ], + [ + "ACK" + ], + sep="\r\n", + ) as blu: + blu.user_offset = 42.0 + + +def test_blu_user_offset_unit_error(): + """Raise ValueError if unit is invalid.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + ], + [ + ], + sep="\r\n", + ) as blu: + with pytest.raises(ValueError): + blu.user_offset = u.Quantity(42, u.mm) + + +def test_blu_version(): + """Query version of device.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*VER" + ], + [ + "Blu firmware Version 1.95" + ], + sep="\r\n", + ) as blu: + version = "Blu firmware Version 1.95" + assert blu.version == version + + +def test_blu_wavelength(): + """Get / set the wavelength.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GWL", + "*PWC00527", + "*PWC00527" + ], + [ + "PWC: 1064", + "ACK", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.wavelength == u.Quantity(1064, u.nm) + blu.wavelength = u.Quantity(0.527, u.um) + blu.wavelength = 527 + + +def test_blu_wavelength_out_of_bound(): + """Get / set the wavelength when value is out of bound.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*PWC00000", + "*PWC00000" + ], + [ + "ACK", + "ACK" + ], + sep="\r\n", + ) as blu: + blu.wavelength = u.Quantity(1000, u.um) + blu.wavelength = -3 + + +def test_blu_zero_offset(): + """Get / set the zero offset.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*GZO", + "*SOU", + "*COU" + ], + [ + "Zero: 1", + "ACK", + "ACK" + ], + sep="\r\n", + ) as blu: + assert blu.zero_offset + blu.zero_offset = True + blu.zero_offset = False + + +# TEST METHODS # + + +def test_blu_confirm_connection(): + """Confirm a bluetooth connection.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*RDY" + ], + [ + "ACK" + ], + sep="\r\n", + ) as blu: + blu.confirm_connection() + + +def test_blu_disconnect(): + """Disconnect bluetooth connection.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*BTD" + ], + [ + "ACK" + ], + sep="\r\n", + ) as blu: + blu.disconnect() + + +def test_blu_scale_down(): + """Set the scale one level lower.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*SSD" + ], + [ + "ACK" + ], + sep="\r\n", + ) as blu: + blu.scale_down() + + +def test_blu_scale_up(): + """Set the scale one level higher.""" + with expected_protocol( + ik.gentec_eo.Blu, + [ + "*SSU" + ], + [ + "ACK" + ], + sep="\r\n", + ) as blu: + blu.scale_up() + + +def test_no_ack_query_error(mocker): + """Ensure temporary variables reset if `_no_ack_query` errors. + + Mocking query here in order to raise an error on query. + """ + with expected_protocol( + ik.gentec_eo.Blu, + [ + ], + [ + ], + sep="\r\n", + ) as blu: + # mock query w/ IOError + io_error_mock = mocker.Mock() + io_error_mock.side_effect = IOError + mocker.patch.object(blu, 'query', io_error_mock) + # do the query + with pytest.raises(IOError): + _ = blu._no_ack_query("QUERY") + assert blu._ack_message == "ACK" + + +# NON-Blu ROUTINES # + + +def test_format_eight_type(): + """Ensure type returned is string.""" + assert isinstance(ik.gentec_eo.blu._format_eight(3.), str) + + +@given(value=st.floats(min_value=-1e100, max_value=1e100, + exclude_min=True, exclude_max=True)) +def test_format_eight_length_values(value): + """Ensure format eight routine works. + + This is a helper routine for the blu device to cut any number to + eight characters. Make sure this is the case with various numbers + and that it is correct to 1% with given number. + """ + value_read = ik.gentec_eo.blu._format_eight(value) + assert value == pytest.approx(float(value_read), abs(value) / 100.) + assert len(value_read) == 8 From f1a7d2024cbd83eb2778db44d934749d90b623f8 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 14 Oct 2020 16:05:03 -0400 Subject: [PATCH 062/108] Improved testing and enhancements of Newport Agilis support (#268) Test suite: - Extended to fully cover `agilis.py` - Time is now mocked out for all tests to speed things up Enhancements: - `@property` did not always return the same type. Corrected for multiple routines and tested with hardware. - Also adjusted doc strings to represent these changes. - Change `from time import sleep` to `import time` for mocking --- instruments/newport/agilis.py | 37 +++-- instruments/tests/test_newport/test_agilis.py | 148 ++++++++++++++++-- 2 files changed, 154 insertions(+), 31 deletions(-) diff --git a/instruments/newport/agilis.py b/instruments/newport/agilis.py index b3e954a3b..900563caf 100644 --- a/instruments/newport/agilis.py +++ b/instruments/newport/agilis.py @@ -29,7 +29,7 @@ # IMPORTS ##################################################################### -from time import sleep +import time from enum import IntEnum @@ -97,12 +97,13 @@ def jog(self): 3 — Positive direction, 1700 steps/s at max. step amplitude. 4 — Positive direction, 666 steps/s at defined step amplitude. - If the jog mode is queried it is returend as a string. + :return: Jog motion set + :rtype: `int` """ resp = self._cont.ag_query("{} JA?".format( int(self._ax) )) - return resp + return int(resp.split("JA")[1]) @jog.setter def jog(self, mode): @@ -133,12 +134,13 @@ def number_of_steps(self): different positions even though a TP command may return the same result. - Returns xTPnn where x is the axis queried and nn are the steps. + :return: Number of steps + :rtype: int """ resp = self._cont.ag_query("{} TP".format( int(self._ax) )) - return resp + return int(resp.split("TP")[1]) @property def move_relative(self): @@ -152,7 +154,7 @@ def move_relative(self): resp = self._cont.ag_query("{} PR?".format( int(self._ax) )) - return resp + return int(resp.split("PR")[1]) @move_relative.setter def move_relative(self, steps): @@ -184,7 +186,7 @@ def move_to_limit(self): resp = self._cont.ag_query("{} MA?".format( int(self._ax) )) - return resp + return int(resp.split("MA")[1]) @move_to_limit.setter def move_to_limit(self, mode): @@ -210,9 +212,10 @@ def step_amplitude(self): provide a tuple or list of two values (one positive, one negative), which will set both values. Valid values are between -50 and 50, except for 0. - If queried, returns a tuple of first the negative, then the positive - step amplitude response in the format xSUnn where x is the axis and - nn the step amplitude + + :return: Tuple of first negative, then positive step amplitude + response. + :rtype: (`int`, `int`) """ resp_neg = self._cont.ag_query("{} SU-?".format( int(self._ax) @@ -220,7 +223,7 @@ def step_amplitude(self): resp_pos = self._cont.ag_query("{} SU+?".format( int(self._ax) )) - return resp_neg, resp_pos + return int(resp_neg.split("SU")[1]), int(resp_pos.split("SU")[1]) @step_amplitude.setter def step_amplitude(self, nns): @@ -251,14 +254,14 @@ def step_delay(self): delay of 2 seconds between pulses. By default, after reset, the value is 0. Setter: value must be integer between 0 and 200000 included - If queried, command returns the currently set step delay as a string - in the format xDLnn where x is the axis number and nn the step delay - value. + + :return: Step delay + :rtype: `int` """ resp = self._cont.ag_query("{} DL?".format( int(self._ax) )) - return resp + return int(resp.split("DL")[1]) @step_delay.setter def step_delay(self, nn): @@ -503,7 +506,7 @@ def ag_sendcmd(self, cmd): Sends the command, then sleeps """ self.sendcmd(cmd) - sleep(self._sleep_time) + time.sleep(self._sleep_time) def ag_query(self, cmd, size=-1): """ @@ -518,7 +521,7 @@ def ag_query(self, cmd, size=-1): resp = "Query timed out." # sleep - sleep(self._sleep_time) + time.sleep(self._sleep_time) return resp diff --git a/instruments/tests/test_newport/test_agilis.py b/instruments/tests/test_newport/test_agilis.py index 50c59da6e..2116c070d 100644 --- a/instruments/tests/test_newport/test_agilis.py +++ b/instruments/tests/test_newport/test_agilis.py @@ -6,6 +6,8 @@ # IMPORTS ##################################################################### +import time + import pytest import instruments as ik @@ -13,6 +15,19 @@ # TESTS ####################################################################### + +# pylint: disable=protected-access + + +# FIXTURES # + + +@pytest.fixture(autouse=True) +def mock_time(mocker): + """Mock `time.sleep` for and set to zero as autouse fixture.""" + return mocker.patch.object(time, 'sleep', return_value=None) + + # CONTROLLER TESTS # @@ -36,6 +51,21 @@ def test_aguc2_enable_remote_mode(): assert agl.enable_remote_mode is False +def test_aguc2_error_previous_command_no_error(): + """Test return of an error value (`No Error`) from previous command.""" + with expected_protocol( + ik.newport.AGUC2, + [ + "TE" + ], + [ + "TE0" + ], + sep="\r\n" + ) as agl: + assert agl.error_previous_command == "No error" + + def test_aguc2_error_previous_command(): """ Check the call error of previous command routine. Note that the test will @@ -158,13 +188,70 @@ def test_aguc2_ag_query(): assert agl.ag_query("VE") == "AG-UC2 v2.2.1" +def test_aguc2_ag_query_io_error(mocker): + """Respond with `Query timed out.` if IOError occurs.""" + # mock the query to raise an IOError + io_error_mock = mocker.Mock() + io_error_mock.side_effect = IOError + mocker.patch.object(ik.newport.AGUC2, 'query', io_error_mock) + + with expected_protocol( + ik.newport.AGUC2, + [ + ], + [ + ], + sep="\r\n" + ) as agl: + assert agl.ag_query("VE") == "Query timed out." + + # AXIS TESTS # -def test_aguc2_axis_am_i_still(): - """ - Check if a given axis is still or not. - """ +@pytest.mark.parametrize("axis", ik.newport.AGUC2.Axes) +def test_aguc2_axis_init_enum(axis): + """Initialize an axis externally with an enum.""" + with expected_protocol( + ik.newport.AGUC2, + [ + ], + [ + ], + sep="\r\n" + ) as agl: + ax = ik.newport.agilis._Axis(agl, axis) + assert ax._ax == axis.value + + +def test_aguc2_axis_init_wrong_type(): + """Raise TypeError when not initialized from AGUC2 parent class.""" + with pytest.raises(TypeError) as err_info: + ik.newport.agilis._Axis(42, ik.newport.AGUC2.Axes.X) + err_msg = err_info.value.args[0] + assert err_msg == "Don't do that." + + +@pytest.mark.parametrize("axis", ik.newport.AGUC2.Axes) +@pytest.mark.parametrize("still", (True, False)) +def test_aguc2_axis_am_i_still(axis, still): + """Check if axis is still or not.""" + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + f"{axis.value} TS", + ], + [ + f"{axis.value}TS {int(not still)}" + ], + sep="\r\n" + ) as agl: + assert agl.axis[axis].am_i_still() == still + + +def test_aguc2_axis_am_i_still_io_error(): + """Raise IOError if max retries achieved.""" with expected_protocol( ik.newport.AGUC2, [ @@ -184,10 +271,27 @@ def test_aguc2_axis_am_i_still(): agl.axis["Y"].am_i_still(max_retries=3) +@pytest.mark.parametrize("axis", ik.newport.AGUC2.Axes) +def test_aguc2_axis_axis_status_not_moving(axis): + """Check status of axis and return axis not moving.""" + with expected_protocol( + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + f"{axis.value} TS", + ], + [ + f"{axis.value}TS0" + ], + sep="\r\n" + ) as agl: + assert agl.axis[axis].axis_status == "Ready (not moving)." + + def test_aguc2_axis_axis_status(): """ Check the status of the axis. Note that the test will return - "Status code not valid." since no instrument is connected. + "Status code query failed." since no instrument is connected. """ with expected_protocol( ik.newport.AGUC2, @@ -205,22 +309,26 @@ def test_aguc2_axis_axis_status(): def test_aguc2_axis_jog(): - """ - Check the jog function. - """ + """Get / set jog function.""" with expected_protocol( ik.newport.AGUC2, [ "MR", # initialize remote mode "1 JA 3", + "1 JA?", "2 JA -4", + "2 JA?" ], [ + "1JA3", + "2JA-4" ], sep="\r\n" ) as agl: agl.axis["X"].jog = 3 + assert agl.axis["X"].jog == 3 agl.axis["Y"].jog = -4 + assert agl.axis["Y"].jog == -4 with pytest.raises(ValueError): agl.axis["X"].jog = -5 with pytest.raises(ValueError): @@ -242,7 +350,7 @@ def test_aguc2_axis_number_of_steps(): ], sep="\r\n" ) as agl: - assert agl.axis["X"].number_of_steps == "1TP0" + assert agl.axis["X"].number_of_steps == 0 def test_aguc2_axis_move_relative(): @@ -254,14 +362,20 @@ def test_aguc2_axis_move_relative(): [ "MR", # initialize remote mode "1 PR 1000", - "2 PR -340" + "1 PR?", + "2 PR -340", + "2 PR?" ], [ + "1PR1000", + "2PR-340" ], sep="\r\n" ) as agl: agl.axis["X"].move_relative = 1000 + assert agl.axis["X"].move_relative == 1000 agl.axis["Y"].move_relative = -340 + assert agl.axis["Y"].move_relative == -340 with pytest.raises(ValueError): agl.axis["X"].move_relative = 2147483648 with pytest.raises(ValueError): @@ -277,13 +391,16 @@ def test_aguc2_axis_move_to_limit(): ik.newport.AGUC2, [ "MR", # initialize remote mode - "2 MA 3" + "2 MA 3", + "2 MA?" ], [ + "2MA42" ], sep="\r\n" ) as agl: agl.axis["Y"].move_to_limit = 3 + assert agl.axis["Y"].move_to_limit == 42 with pytest.raises(ValueError): agl.axis["Y"].move_to_limit = -5 with pytest.raises(ValueError): @@ -301,7 +418,9 @@ def test_aguc2_axis_step_amplitude(): "1 SU-?", "1 SU+?", "1 SU -35", - "1 SU 47" + "1 SU 47", + "1 SU -23", + "1 SU 13" ], [ @@ -309,9 +428,10 @@ def test_aguc2_axis_step_amplitude(): ], sep="\r\n" ) as agl: - assert agl.axis["X"].step_amplitude == ("1SU-35", "1SU+35") + assert agl.axis["X"].step_amplitude == (-35, 35) agl.axis["X"].step_amplitude = -35 agl.axis["X"].step_amplitude = 47 + agl.axis["X"].step_amplitude = (-23, 13) with pytest.raises(ValueError): agl.axis["X"].step_amplitude = 0 with pytest.raises(ValueError): @@ -337,7 +457,7 @@ def test_aguc2_axis_step_delay(): ], sep="\r\n" ) as agl: - assert agl.axis["Y"].step_delay == "2DL0" + assert agl.axis["Y"].step_delay == 0 agl.axis["X"].step_delay = 1000 agl.axis["X"].step_delay = 200 with pytest.raises(ValueError): From 2fb496687200297a5cfc51a75372eb8990bec4c7 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 14 Oct 2020 16:15:26 -0400 Subject: [PATCH 063/108] Tests and BF for Fluke3000 (#269) Full coverage test suite. Changes & BF for `fluke3000.py`: - Instead of returning an `AttributeError` it is now raised (as for other methods already too). - Fixed a typo --- instruments/fluke/fluke3000.py | 4 +- .../tests/test_fluke/test_fluke3000.py | 260 ++++++++++++++++++ 2 files changed, 262 insertions(+), 2 deletions(-) diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index 25f3437f6..7721c7586 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -193,7 +193,7 @@ def input_range(self): :rtype: `str` """ - return AttributeError('The `Fluke3000` FC is an autoranging only multimeter') + raise AttributeError('The `Fluke3000` FC is an autoranging only multimeter') # METHODS # @@ -216,7 +216,7 @@ def connect(self): def scan(self): """ - Search for available modules and reformatturns a dictionary + Search for available modules and reformat. Returns a dictionary of the modules found and their port ID. """ # Loop over possible channels, store device locations diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index 3abdca8ba..052f537f0 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -6,6 +6,7 @@ # IMPORTS #################################################################### +import pytest import instruments.units as u @@ -14,6 +15,10 @@ # TESTS ###################################################################### + +# pylint: disable=protected-access + + # Empty initialization sequence (scan function) that does not uncover # any available Fluke 3000 FC device. none_sequence = [ @@ -61,6 +66,31 @@ ] +# Default initialization sequence (scan function) that binds a multimeter +# to port 1. Adopted from `init_sequence` and `init_response`, thus +# counting does not contain 4. +init_sequence_mm_only = [ + "rfebd 01 0", # 1 + "rfgus 01", # 2 + "rfebd 02 0", # 3 + "rfebd 03 0", # 5 + "rfebd 04 0", # 6 + "rfebd 05 0", # 7 + "rfebd 06 0" # 8 +] +init_response_mm_only = [ + "CR:Ack=0:RFEBD", # 1.1 + "ME:R:S#=01:DCC=012:PH=64", # 1.2 + "CR:Ack=0:RFGUS", # 2.1 + "ME:R:S#=01:DCC=004:PH=46333030304643", # 2.2 + "CR:Ack=2", # 3 + "CR:Ack=2", # 5 + "CR:Ack=2", # 6 + "CR:Ack=2", # 7 + "CR:Ack=2" # 8 +] + + def test_mode(): with expected_protocol( ik.fluke.Fluke3000, @@ -80,6 +110,70 @@ def test_mode(): assert inst.mode == inst.Mode.voltage_dc +def test_mode_key_error(): + """Raise KeyError if the Module is not available.""" + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + # kill positions to trigger error + inst.positions = {} + with pytest.raises(KeyError) as err_info: + _ = inst.mode + err_msg = err_info.value.args[0] + assert err_msg == "No `Fluke3000` FC multimeter is bound" + + +def test_trigger_mode_attribute_error(): + """Raise AttributeError since trigger mode not supported.""" + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + with pytest.raises(AttributeError) as err_info: + _ = inst.trigger_mode + err_msg = err_info.value.args[0] + assert err_msg == "The `Fluke3000` only supports single trigger when " \ + "queried" + + +def test_relative_attribute_error(): + """Raise AttributeError since relative measurement mode not supported.""" + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + with pytest.raises(AttributeError) as err_info: + _ = inst.relative + err_msg = err_info.value.args[0] + assert err_msg == "The `Fluke3000` FC does not support relative " \ + "measurements" + + +def test_input_range_attribute_error(): + """ + Raise AttributeError since instrument is an auto ranging only + multimeter. + """ + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + with pytest.raises(AttributeError) as err_info: + _ = inst.input_range + err_msg = err_info.value.args[0] + assert err_msg == "The `Fluke3000` FC is an autoranging only " \ + "multimeter" + + def test_connect(): with expected_protocol( ik.fluke.Fluke3000, @@ -108,6 +202,20 @@ def test_connect(): assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 +def test_connect_no_modules_available(): + """Raise ValueError if no modules are avilable.""" + with pytest.raises(ValueError) as err_info: + with expected_protocol( + ik.fluke.Fluke3000, + none_sequence, + none_response, + "\r" + ) as inst: + _ = inst + err_msg = err_info.value.args[0] + assert err_msg == "No `Fluke3000` modules available" + + def test_scan(): with expected_protocol( ik.fluke.Fluke3000, @@ -119,6 +227,24 @@ def test_scan(): assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 +def test_scan_module_not_implemented(): + """Raise NotImplementedError if a module with wrong ID is found.""" + # modify response to contain unknown module + module_id = 42 + mod_response = list(init_response) + mod_response[3] = f"ME:R:S#=01:DCC=004:PH={module_id}" # new module id + with pytest.raises(NotImplementedError) as err_info: + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + mod_response, + "\r" + ) as inst: + _ = inst + err_msg = err_info.value.args[0] + assert err_msg == f"Module ID {module_id} not implemented" + + def test_reset(): with expected_protocol( ik.fluke.Fluke3000, @@ -140,6 +266,26 @@ def test_reset(): inst.reset() +def test_flush(mocker): + """Test flushing the reads, which raises an OSError here. + + Mocking `read()` to generate the error. + """ + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + # mock read to raise OSError + os_error_mock = mocker.Mock() + os_error_mock.side_effect = OSError + read_mock = mocker.patch.object(inst, 'read', os_error_mock) + # now flush + inst.flush() + read_mock.assert_called() + + def test_measure(): with expected_protocol( ik.fluke.Fluke3000, @@ -161,3 +307,117 @@ def test_measure(): ) as inst: assert inst.measure(inst.Mode.voltage_dc) == 0.509 * u.volt assert inst.measure(inst.Mode.temperature) == -25.3 * u.celsius + + +def test_measure_invalid_mode(): + """Raise ValueError if measurement mode is not supported.""" + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + wrong_mode = 42 + with pytest.raises(ValueError) as err_info: + inst.measure(wrong_mode) + err_msg = err_info.value.args[0] + assert err_msg == f"Mode {wrong_mode} is not supported" + + +def test_measure_no_module_with_mode(): + """ + Raise ValueError if not sensor that supports the requested mode is + connected. + """ + mode_not_available = ik.fluke.Fluke3000.Mode.temperature + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence_mm_only, + init_response_mm_only, + "\r" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.measure(mode=mode_not_available) + err_msg = err_info.value.args[0] + assert err_msg == f"Device necessary to measure {mode_not_available} " \ + f"is not available" + + +def test_measure_inconsistent_answer(mocker): + """Measurement test with inconsistent answer. + + The first time around in this measurement an inconsistent answer is + returend. This would usually call a `flush` routine, which reads + until no more terminators are found. Here, `flush` is mocked out + such that the `expected_protocol` can actually be used. + """ + mode_issue = 42 # expect 02, answer something different - unexpected + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence + + [ + # bad query + "rfemd 01 1", # 1 + "rfemd 01 2", # 2 + "rfemd 01 2", # 2 + # try again + "rfemd 01 1", # 1 + "rfemd 01 2", # 2 + ], + init_response + + [ + # bad response + "CR:Ack=0:RFEMD", # 1.1 + f"ME:R:S#=01:DCC=010:PH=FD010006{mode_issue}0C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + "CR:Ack=0:RFEMD", # 2 + # "", # something to flush + # try again + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + ], + "\r" + ) as inst: + # mock out flush + flush_mock = mocker.patch.object(inst, 'flush', return_value=None) + assert inst.measure(inst.Mode.voltage_dc) == 0.509 * u.volt + # assert that flush was called once + flush_mock.assert_called_once() + + +def test_parse_ph_not_in_result(): + """Raise ValueError if 'PH' is not in `result`.""" + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + mode = inst.Mode.temperature + bad_result = "42" + with pytest.raises(ValueError) as err_info: + inst._parse(bad_result, mode) + err_msg = err_info.value.args[0] + assert err_msg == "Cannot parse a string that does not contain a " \ + "return value" + + +def test_parse_wrong_mode(): + """Raise ValueError if multimeter not in the right mode.""" + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + mode_requested = inst.Mode.temperature + result = "ME:R:S#=01:DCC=010:PH=FD010006020C0600" + mode_result = inst.Mode(result.split("PH=")[-1][8:10]) + with pytest.raises(ValueError) as err_info: + inst._parse(result, mode_requested) + err_msg = err_info.value.args[0] + assert err_msg == f"Mode {mode_requested.name} was requested but " \ + f"the Fluke 3000FC Multimeter is in mode " \ + f"{mode_result.name} instead. Could not read the " \ + f"requested quantity." From 9f83b003873eca8731d1a627f2e18b14d790abcd Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 14 Oct 2020 17:43:45 -0400 Subject: [PATCH 064/108] Adjust `pytest.approx` statement such that Keithley580 tests pass (#271) - Make approx statement with `rel=1e-6` argument, now passes - This should be the default according to the docs though... --- instruments/tests/test_keithley/test_keithley580.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/tests/test_keithley/test_keithley580.py b/instruments/tests/test_keithley/test_keithley580.py index a16fa7035..e8bda7c54 100644 --- a/instruments/tests/test_keithley/test_keithley580.py +++ b/instruments/tests/test_keithley/test_keithley580.py @@ -761,7 +761,7 @@ def test_measure(init, create_measurement, resistance): sep="\n" ) as inst: read_value = inst.measure() - assert read_value.magnitude == pytest.approx(resistance) + assert read_value.magnitude == pytest.approx(resistance, rel=1e-6) assert read_value.units == u.ohm From e09bd866e8d706fcc63688a58108afa4152da495 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Wed, 14 Oct 2020 18:44:24 -0400 Subject: [PATCH 065/108] Update pytest to version 6.1.1 (#272) --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 1f8c048cc..a5adf1678 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,5 @@ mock -pytest +pytest==6.1.1 pytest-mock hypothesis==4.28.2 pylint==2.4.4 From ee2fd497da8f9ea2ae02525d07f850476901c3d6 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 14 Oct 2020 18:54:34 -0400 Subject: [PATCH 066/108] Test suite and BF for Newport's `error.py` (#270) Simple test suite with full coverage. Time stamps are only tested for existence. Further tests is probably not required, however, if wanted we might want to use `pytest-freezegun` and include that into the `dev-requirements.txt` in order to freeze timestamps. (Maybe should include that just for the awesome name). Bug fix: The timedelta creation for the timestamp made absolutely no sense and resulted in an error, even when just executing it by itself. Creating a timestamp that is as close to the one given if not specific `datetime` is given. Co-authored-by: Steven Casagrande --- instruments/newport/errors.py | 3 +- instruments/tests/test_newport/test_errors.py | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 instruments/tests/test_newport/test_errors.py diff --git a/instruments/newport/errors.py b/instruments/newport/errors.py index 7deb8cf48..c031f3feb 100644 --- a/instruments/newport/errors.py +++ b/instruments/newport/errors.py @@ -103,8 +103,7 @@ def __init__(self, errcode=None, timestamp=None): if timestamp is None: self._timestamp = datetime.datetime.now() - NewportError.start_time else: - self._timestamp = datetime.timedelta( - seconds=(timestamp * 400E-6)) + self._timestamp = datetime.datetime.now() - timestamp if errcode is not None: # Break the error code into an axis number diff --git a/instruments/tests/test_newport/test_errors.py b/instruments/tests/test_newport/test_errors.py new file mode 100644 index 000000000..23652d689 --- /dev/null +++ b/instruments/tests/test_newport/test_errors.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for NewportError class +""" + +# IMPORTS #################################################################### + + +import datetime + +from instruments.newport.errors import NewportError + + +# TESTS ###################################################################### + + +# pylint: disable=protected-access + + +def test_init_none(): + """Initialized with both arguments as `None`.""" + cls = NewportError() + assert isinstance(cls._timestamp, datetime.timedelta) + assert cls._errcode is None + assert cls._axis is None + + +def test_init_with_timestamp(): + """Initialized with a time stamp.""" + timestamp = datetime.datetime.now() + cls = NewportError(timestamp=timestamp) + assert isinstance(cls._timestamp, datetime.timedelta) + + +def test_init_with_error_code(): + """Initialize with non-axis specific error code.""" + err_code = 7 # parameter out of range + cls = NewportError(errcode=err_code) + assert cls._axis is None + assert cls._errcode == 7 + + +def test_init_with_error_code_axis(): + """Initialize with axis-specific error code.""" + err_code = 313 # ax 3 not enabled + cls = NewportError(errcode=err_code) + assert cls._axis == 3 + assert cls._errcode == 13 + + +def test_get_message(): + """Get the message for a given error code.""" + err_code = "7" + cls = NewportError() + assert cls.get_message(err_code) == cls.messageDict[err_code] + + +def test_timestamp(): + """Get the timestamp for a given error.""" + cls = NewportError() + assert cls.timestamp == cls._timestamp + + +def test_errcode(): + """Get the error code reported by device.""" + cls = NewportError(errcode=7) + assert cls.errcode == cls._errcode + + +def test_axis(): + """Get axis for given error code.""" + cls = NewportError(errcode=313) + assert cls.axis == cls._axis From d5c148d8de1d9bd075ff39e9431f8662f5515760 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 15 Oct 2020 11:17:42 -0400 Subject: [PATCH 067/108] Rename visa to pyvisa (#275) Switch `import visa` and dependent statements to `import pyvisa`. Takes care of depreciation warning to avoid further conflicts with other package. See issue #273 --- .../abstract_instruments/comm/visa_communicator.py | 10 +++++----- instruments/abstract_instruments/instrument.py | 10 +++++----- instruments/tests/test_base_instrument.py | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 9700cf134..36aea3742 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -10,7 +10,7 @@ import io -import visa +import pyvisa from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -29,13 +29,13 @@ class VisaCommunicator(io.IOBase, AbstractCommunicator): def __init__(self, conn): super(VisaCommunicator, self).__init__(self) - if visa is None: + if pyvisa is None: raise ImportError("PyVISA required for accessing VISA instruments.") - version = int(visa.__version__.replace(".", "").ljust(3, "0")) + version = int(pyvisa.__version__.replace(".", "").ljust(3, "0")) # pylint: disable=no-member - if (version < 160 and isinstance(conn, visa.Instrument)) or \ - (version >= 160 and isinstance(conn, visa.Resource)): + if (version < 160 and isinstance(conn, pyvisa.Instrument)) or \ + (version >= 160 and isinstance(conn, pyvisa.Resource)): self._conn = conn self._terminator = "\n" else: diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 0a8f846a8..ac0a96da0 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -15,7 +15,7 @@ from serial import SerialException from serial.tools.list_ports import comports import numpy as np -import visa +import pyvisa import usb import usb.core import usb.util @@ -581,16 +581,16 @@ def open_visa(cls, resource_name): .. _PyVISA: http://pyvisa.sourceforge.net/ """ - if visa is None: + if pyvisa is None: raise ImportError("PyVISA is required for loading VISA " "instruments.") - version = list(map(int, visa.__version__.split("."))) + version = list(map(int, pyvisa.__version__.split("."))) while len(version) < 3: version += [0] if version[0] >= 1 and version[1] >= 6: - ins = visa.ResourceManager().open_resource(resource_name) + ins = pyvisa.ResourceManager().open_resource(resource_name) else: - ins = visa.instrument(resource_name) #pylint: disable=no-member + ins = pyvisa.instrument(resource_name) #pylint: disable=no-member return cls(VisaCommunicator(ins)) @classmethod diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 84c65e5a5..0629f6c76 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -272,14 +272,14 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): ) -@mock.patch("instruments.abstract_instruments.instrument.visa", new=None) +@mock.patch("instruments.abstract_instruments.instrument.pyvisa", new=None) def test_instrument_open_visa_import_error(): with pytest.raises(ImportError): _ = ik.Instrument.open_visa("abc123") @mock.patch("instruments.abstract_instruments.instrument.VisaCommunicator") -@mock.patch("instruments.abstract_instruments.instrument.visa") +@mock.patch("instruments.abstract_instruments.instrument.pyvisa") def test_instrument_open_visa_new_version(mock_visa, mock_visa_comm): mock_visa_comm.return_value.__class__ = VisaCommunicator mock_visa.__version__ = "1.8" @@ -294,7 +294,7 @@ def test_instrument_open_visa_new_version(mock_visa, mock_visa_comm): @mock.patch("instruments.abstract_instruments.instrument.VisaCommunicator") -@mock.patch("instruments.abstract_instruments.instrument.visa") +@mock.patch("instruments.abstract_instruments.instrument.pyvisa") def test_instrument_open_visa_old_version(mock_visa, mock_visa_comm): mock_visa_comm.return_value.__class__ = VisaCommunicator mock_visa.__version__ = "1.5" From 760deb36b0723c1400777e9b9d7595be6f801977 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 15 Oct 2020 11:52:45 -0400 Subject: [PATCH 068/108] Tests and BFs for Thorlabs `_abstract` and `_packets` helper classes (#274) Test suites for both helper classes with full coverage. Bug fixes for `_abstract.py`: - Re-stated `if timeout` to `if timeout is not None` to avoid ambivalence if a timeout of zero is specified (as done in the test to speed things up). By default, `timeout` is set to `None`. Bug fixes for `_packets.py`: - Removed inaccessible code. - `__str__` had several errors in it and couldn't print when given variables were `None` (which some of them always have to be) - `parameters` setter previously set `message_id` instead of parameters. --- .../tests/test_thorlabs/test_abstract.py | 186 +++++++++++ .../tests/test_thorlabs/test_packets.py | 294 ++++++++++++++++++ instruments/thorlabs/_abstract.py | 2 +- instruments/thorlabs/_packets.py | 17 +- 4 files changed, 491 insertions(+), 8 deletions(-) create mode 100644 instruments/tests/test_thorlabs/test_abstract.py create mode 100644 instruments/tests/test_thorlabs/test_packets.py diff --git a/instruments/tests/test_thorlabs/test_abstract.py b/instruments/tests/test_thorlabs/test_abstract.py new file mode 100644 index 000000000..c48cf84dc --- /dev/null +++ b/instruments/tests/test_thorlabs/test_abstract.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract ThorlabsInstrument class. +""" + +# IMPORTS #################################################################### + + +import struct +import time + +import pytest + +import instruments as ik +from instruments.thorlabs import _packets +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +example_packet = _packets.ThorLabsPacket( + message_id=0x0000, + param1=0x00, + param2=0x00, + dest=0x50, + source=0x01, + data=None +) + + +example_packet_with_data = _packets.ThorLabsPacket( + message_id=0x0000, + param1=None, + param2=None, + dest=0x50, + source=0x01, + data=struct.pack(' Date: Mon, 19 Oct 2020 10:09:21 -0400 Subject: [PATCH 069/108] Replace quantities with pint (#243) * First pass replacing quantities with pint * Update u.quantity.Quantity references * Replace Kelvin -> kelvin * Update to use dev build of pint * Remove unused imports * Fix lakeshore tests * Update newer tests to use pint calls * Fix remaining tests and drivers * Remove quantities from dependencies * Update typing Quantity references to pint * Remove dangling quantities imports * Update more docstring references to quantities * Update docs * Update new tests for pint * Add tests for new lines in Lakeshore475.py * Add tests for mc1.py * Fix Phasematrix cBm * Update Keithley 195 & 580 tests for pint * Fix gentec tests for pint * Enable users to import instruments.units * Update units to use pint application registry * Update flaky keithley580 test * Update pint to use latest pypi version * Update examples * Revert docstring import changes * Revert import changes in ipynb files * Remove pint import in lakeshore475.py --- doc/examples/ex_qubitekkcc.py | 29 ++-- doc/examples/ex_qubitekkcc_simple.py | 18 +-- doc/examples/ex_tekdpo70000.ipynb | 2 +- doc/examples/ex_thorlabstc200.py | 10 +- doc/examples/srs_DG645.py | 2 +- doc/source/apiref/other.rst | 9 +- doc/source/conf.py | 2 +- doc/source/devguide/code_style.rst | 2 +- doc/source/devguide/util_fns.rst | 10 +- doc/source/intro.rst | 4 +- instruments/__init__.py | 5 +- .../comm/gpib_communicator.py | 12 +- .../comm/serial_communicator.py | 6 +- .../comm/socket_communicator.py | 6 +- .../comm/usbtmc_communicator.py | 6 +- .../comm/visa_communicator.py | 4 +- .../comm/vxi11_communicator.py | 2 +- .../abstract_instruments/electrometer.py | 2 +- .../function_generator.py | 30 ++-- .../abstract_instruments/multimeter.py | 2 +- .../optical_spectrum_analyzer.py | 6 +- .../abstract_instruments/power_supply.py | 8 +- .../signal_generator/channel.py | 6 +- instruments/agilent/agilent33220a.py | 8 +- instruments/agilent/agilent34410a.py | 14 +- instruments/config.py | 2 +- instruments/fluke/fluke3000.py | 8 +- .../generic_scpi/scpi_function_generator.py | 8 +- instruments/generic_scpi/scpi_instrument.py | 6 +- instruments/generic_scpi/scpi_multimeter.py | 10 +- instruments/gentec_eo/blu.py | 19 ++- instruments/glassman/glassmanfr.py | 18 +-- instruments/holzworth/holzworth_hs9000.py | 13 +- instruments/hp/hp3456a.py | 22 +-- instruments/hp/hp6624a.py | 24 +-- instruments/hp/hp6632b.py | 12 +- instruments/hp/hp6652a.py | 12 +- instruments/hp/hpe3631a.py | 22 +-- instruments/keithley/keithley195.py | 11 +- instruments/keithley/keithley2182.py | 12 +- instruments/keithley/keithley485.py | 10 +- instruments/keithley/keithley580.py | 10 +- instruments/keithley/keithley6220.py | 4 +- instruments/keithley/keithley6514.py | 6 +- instruments/lakeshore/lakeshore340.py | 6 +- instruments/lakeshore/lakeshore370.py | 4 +- instruments/lakeshore/lakeshore475.py | 72 +++++---- instruments/minghe/mhs5200a.py | 16 +- instruments/newport/newportesp301.py | 151 +++++++++--------- instruments/ondax/lm.py | 48 +++--- instruments/oxford/oxforditc503.py | 6 +- .../phasematrix/phasematrix_fsw0020.py | 16 +- instruments/picowatt/picowattavs47.py | 4 +- instruments/qubitekk/cc1.py | 18 +-- instruments/qubitekk/mc1.py | 32 ++-- instruments/srs/srs345.py | 8 +- instruments/srs/srs830.py | 18 +-- instruments/srs/srsctc100.py | 16 +- instruments/srs/srsdg645.py | 22 +-- instruments/tektronix/tekawg2000.py | 20 +-- instruments/tektronix/tekdpo70000.py | 2 +- instruments/tektronix/tektds224.py | 2 +- instruments/teledyne/maui.py | 24 +-- instruments/tests/__init__.py | 11 +- .../test_function_generator.py | 2 +- .../tests/test_agilent/test_agilent_33220a.py | 8 +- .../tests/test_agilent/test_agilent_34410a.py | 2 +- instruments/tests/test_comm/test_gpibusb.py | 2 +- instruments/tests/test_comm/test_serial.py | 2 +- instruments/tests/test_comm/test_socket.py | 2 +- instruments/tests/test_comm/test_usbtmc.py | 2 +- instruments/tests/test_config.py | 2 +- .../tests/test_fluke/test_fluke3000.py | 4 +- .../test_scpi_function_generator.py | 2 +- .../test_generic_scpi/test_scpi_multimeter.py | 4 +- instruments/tests/test_gentec_eo/test_blu.py | 4 +- .../tests/test_glassman/test_glassmanfr.py | 2 +- .../test_holzworth/test_holzworth_hs9000.py | 13 +- instruments/tests/test_hp/test_hp3456a.py | 15 +- instruments/tests/test_hp/test_hp6624a.py | 2 +- instruments/tests/test_hp/test_hp6632b.py | 2 +- instruments/tests/test_hp/test_hpe3631a.py | 2 +- .../tests/test_keithley/test_keithley195.py | 2 +- .../tests/test_keithley/test_keithley2182.py | 16 +- .../tests/test_keithley/test_keithley485.py | 6 +- .../tests/test_keithley/test_keithley580.py | 4 +- .../tests/test_keithley/test_keithley6220.py | 2 +- .../tests/test_keithley/test_keithley6514.py | 2 +- .../tests/test_lakeshore/test_lakeshore340.py | 2 +- .../tests/test_lakeshore/test_lakeshore370.py | 2 +- .../tests/test_lakeshore/test_lakeshore475.py | 66 ++++++-- .../tests/test_minghe/test_minghe_mhs5200a.py | 8 +- instruments/tests/test_ondax/test_lm.py | 47 +++--- .../tests/test_oxford/test_oxforditc503.py | 5 +- .../test_phasematrix_fsw0020.py | 13 +- .../test_picowatt/test_picowatt_avs47.py | 2 +- .../test_bounded_unitful_property.py | 2 +- .../test_unitful_property.py | 5 +- .../test_unitless_property.py | 2 +- .../tests/test_qubitekk/test_qubitekk_cc1.py | 2 +- .../tests/test_qubitekk/test_qubitekk_mc1.py | 58 ++++++- instruments/tests/test_split_str.py | 2 +- instruments/tests/test_srs/test_srs345.py | 2 +- instruments/tests/test_srs/test_srs830.py | 2 +- instruments/tests/test_srs/test_srsctc100.py | 7 +- instruments/tests/test_srs/test_srsdg645.py | 9 +- .../tests/test_tektronix/test_tekawg2000.py | 8 +- .../tests/test_tektronix/test_tekdpo70000.py | 55 +++---- instruments/tests/test_teledyne/test_maui.py | 2 +- .../tests/test_thorlabs/test_thorlabs_apt.py | 2 +- .../test_thorlabs/test_thorlabs_lcc25.py | 2 +- .../tests/test_thorlabs/test_thorlabs_sc10.py | 2 +- .../test_thorlabs/test_thorlabs_tc200.py | 22 +-- .../test_toptica/test_toptica_topmode.py | 2 +- instruments/tests/test_util_fns.py | 44 +---- .../tests/test_yokogawa/test_yokogawa7651.py | 10 +- .../tests/test_yokogawa/test_yokogawa_6370.py | 2 +- instruments/thorlabs/_abstract.py | 4 +- instruments/thorlabs/lcc25.py | 30 ++-- instruments/thorlabs/pm100usb.py | 4 +- instruments/thorlabs/sc10.py | 10 +- instruments/thorlabs/tc200.py | 36 ++--- instruments/thorlabs/thorlabsapt.py | 44 ++--- instruments/toptica/topmode.py | 6 +- instruments/units.py | 45 +----- instruments/util_fns.py | 46 ++---- instruments/yokogawa/yokogawa6370.py | 2 +- instruments/yokogawa/yokogawa7651.py | 22 +-- requirements.txt | 2 +- setup.py | 4 +- 130 files changed, 820 insertions(+), 856 deletions(-) diff --git a/doc/examples/ex_qubitekkcc.py b/doc/examples/ex_qubitekkcc.py index be4448243..ac6036821 100644 --- a/doc/examples/ex_qubitekkcc.py +++ b/doc/examples/ex_qubitekkcc.py @@ -2,24 +2,25 @@ from sys import platform as _platform import instruments as ik -from quantities import second, nanosecond +import instruments.units as u def main(): - cc = ik.qubitekk.CC1.open_serial(vid=1027, pid=24577, baud=19200, timeout=10) - cc1.dwell_time = 1.0 * second - print cc1.dwell_time - cc1.delay = 0.0 * nanosecond - print cc1.delay - cc1.window = 3.0 * nanosecond - print cc1.window + cc1 = ik.qubitekk.CC1.open_serial(vid=1027, pid=24577, baud=19200, timeout=10) + cc1.dwell_time = 1.0 * u.s + print(cc1.dwell_time) + cc1.delay = 0.0 * u.ns + print(cc1.delay) + cc1.window = 3.0 * u.ns + print(cc1.window) cc1.trigger = ik.qubitekk.TriggerModeInt.start_stop - print cc1.trigger - print "Fetching Counts" - print cc1.channel[0].count - print cc1.channel[1].count - print cc1.channel[2].count - print "Fetched Counts" + print(cc1.trigger) + print("Fetching Counts") + print(cc1.channel[0].count) + print(cc1.channel[1].count) + print(cc1.channel[2].count) + print("Fetched Counts") + if __name__ == "__main__": while True: diff --git a/doc/examples/ex_qubitekkcc_simple.py b/doc/examples/ex_qubitekkcc_simple.py index e792cbe7e..b42d4e140 100644 --- a/doc/examples/ex_qubitekkcc_simple.py +++ b/doc/examples/ex_qubitekkcc_simple.py @@ -4,7 +4,7 @@ from sys import platform as _platform import instruments as ik -import quantities +import instruments.units as u if __name__ == "__main__": @@ -15,13 +15,13 @@ else: cc = ik.qubitekk.CC1.open_serial('COM8', 19200, timeout=1) - print "Initializing Coincidence Counter" - cc.dwell_time = 1.0*quantities.s - cc.delay = 0.0*quantities.ns - cc.window = 3.0*quantities.ns + print("Initializing Coincidence Counter") + cc.dwell_time = 1.0*u.s + cc.delay = 0.0*u.ns + cc.window = 3.0*u.ns cc.trigger = cc.TriggerMode.start_stop - print "ch1 counts: "+str(cc.channel[0].count) - print "ch2 counts: "+str(cc.channel[1].count) - print "counts counts: "+str(cc.channel[2].count) + print(f"ch1 counts: {str(cc.channel[0].count)}") + print(f"ch2 counts: {str(cc.channel[1].count)}") + print(f"counts counts: {str(cc.channel[2].count)}") - print "Finished Initializing Coincidence Counter" \ No newline at end of file + print("Finished Initializing Coincidence Counter") \ No newline at end of file diff --git a/doc/examples/ex_tekdpo70000.ipynb b/doc/examples/ex_tekdpo70000.ipynb index 8954a36d6..fc0c5faec 100644 --- a/doc/examples/ex_tekdpo70000.ipynb +++ b/doc/examples/ex_tekdpo70000.ipynb @@ -152,7 +152,7 @@ "cell_type": "code", "collapsed": false, "input": [ - "_.rescale('GHz')" + "_.to('GHz')" ], "language": "python", "metadata": {}, diff --git a/doc/examples/ex_thorlabstc200.py b/doc/examples/ex_thorlabstc200.py index b38f6ec27..c872526a1 100644 --- a/doc/examples/ex_thorlabstc200.py +++ b/doc/examples/ex_thorlabstc200.py @@ -1,10 +1,10 @@ #Thorlabs Temperature Controller example import instruments as ik -import quantities +import instruments.units as u tc = ik.thorlabs.TC200.open_serial('/dev/tc200', 115200) -tc.temperature_set = 70*quantities.degF +tc.temperature_set = 70*u.degF print("The current temperature is: ", tc.temperature) tc.mode = tc.Mode.normal @@ -22,7 +22,7 @@ tc.d = 2 print("The current d gain is: ", tc.d) -tc.degrees = quantities.degF +tc.degrees = u.degF print("The current degrees settings is: ", tc.degrees) tc.sensor = tc.Sensor.ptc100 @@ -31,10 +31,10 @@ tc.beta = 3900 print("The current beta settings is: ", tc.beta) -tc.max_temperature = 150*quantities.degC +tc.max_temperature = 150*u.degC print("The current max temperature setting is: ", tc.max_temperature) -tc.max_power = 1000*quantities.mW +tc.max_power = 1000*u.mW print("The current max power setting is: ", tc.max_power) diff --git a/doc/examples/srs_DG645.py b/doc/examples/srs_DG645.py index 4ab5c54d5..d3a434c48 100644 --- a/doc/examples/srs_DG645.py +++ b/doc/examples/srs_DG645.py @@ -15,7 +15,7 @@ # -# We start by importing the `srs` package from within the main `instruments` package, along with the `quantities` package +# We start by importing the `srs` package from within the main `instruments` package, along with the `instruments.units` package # that is used to track physical quantities. # diff --git a/doc/source/apiref/other.rst b/doc/source/apiref/other.rst index f5163df47..eee2c172d 100644 --- a/doc/source/apiref/other.rst +++ b/doc/source/apiref/other.rst @@ -29,11 +29,4 @@ Units ----- Units are identified to the Phase Matrix FSW-0020 using the -`~quantities.Quantity` class implemented by the `quantities` package. To support -the FSW-0020, we provide several additional unit quantities, listed here. - -.. autodata:: mHz - -.. autodata:: dBm - -.. autodata:: cBm +`~pint.Quantity` class implemented by the `pint` package. diff --git a/doc/source/conf.py b/doc/source/conf.py index 6002ac35d..c49ad4d71 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -248,7 +248,7 @@ 'http://docs.python.org/': None, 'numpy': ('http://docs.scipy.org/doc/numpy', None), 'serial': ('http://pyserial.sourceforge.net/', None), - 'quantities': ('http://pythonhosted.org/quantities/', None), + 'pint': ('https://pint.readthedocs.io/en/stable/', None), } autodoc_member_order = 'groupwise' diff --git a/doc/source/devguide/code_style.rst b/doc/source/devguide/code_style.rst index 5d50d4b5e..fa435abad 100644 --- a/doc/source/devguide/code_style.rst +++ b/doc/source/devguide/code_style.rst @@ -10,7 +10,7 @@ Data Types Numeric Data ------------ -When appropriate, use :class:`quantities.Quantity` objects to track units. +When appropriate, use :class:`pint.Quantity` objects to track units. If this is not possible or appropriate, use a bare `float` for scalars and `np.ndarray` for array-valued data. diff --git a/doc/source/devguide/util_fns.rst b/doc/source/devguide/util_fns.rst index e316538ac..74a1378be 100644 --- a/doc/source/devguide/util_fns.rst +++ b/doc/source/devguide/util_fns.rst @@ -75,12 +75,12 @@ These properties, when implemented in your class, might look like this:: voltage = unitful_property( "VOLT", u.volt, - valid_range=(0*quantities.volt, 10*quantities.volt) + valid_range=(0*u.volt, 10*u.volt) doc=""" Gets/sets the output voltage. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -136,11 +136,11 @@ the `~instruments.thorlabs.TC200` class:: doc=""" Gets the actual temperature of the sensor - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units degrees C. - :type: `~quantities.quantity.Quantity` or `int` + :type: `~pint.Quantity` or `int` :return: the temperature (in degrees C) - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 42f62ed43..faab65f4d 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -29,7 +29,7 @@ $ pip install -r requirements.txt - NumPy - `PySerial`_ -- `quantities`_ +- `pint`_ - `python-vxi11`_ - `PyUSB`_ (version 1.0a or higher, required for raw USB support) - `python-usbtmc`_ @@ -40,7 +40,7 @@ Optional Dependencies ~~~~~~~~~~~~~~~~~~~~~ .. _PySerial: http://pyserial.sourceforge.net/ -.. _quantities: http://pythonhosted.org/quantities/ +.. _pint: https://pint.readthedocs.io/en/stable/ .. _ruamel.yaml: http://yaml.readthedocs.io .. _PyUSB: http://sourceforge.net/apps/trac/pyusb/ .. _PyVISA: http://pyvisa.sourceforge.net/ diff --git a/instruments/__init__.py b/instruments/__init__.py index 0b93dcc1c..36cf43f9b 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -6,6 +6,8 @@ # IMPORTS #################################################################### +__all__ = ["units"] + from . import abstract_instruments from .abstract_instruments import Instrument @@ -33,7 +35,8 @@ from . import toptica from . import yokogawa -from . import units +from .units import ureg as units + from .config import load_instruments # VERSION METADATA ########################################################### diff --git a/instruments/abstract_instruments/comm/gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py index 58229927e..2f2886da8 100644 --- a/instruments/abstract_instruments/comm/gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -12,7 +12,7 @@ import io import time -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -98,7 +98,7 @@ def timeout(self): Gets/sets the timeeout of both the GPIB bus and the connection channel between the PC and the GPIB adapter. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: As specified, or assumed to be of units ``seconds`` """ return self._timeout @@ -107,13 +107,13 @@ def timeout(self): def timeout(self, newval): newval = assume_units(newval, u.second) if self._model == GPIBCommunicator.Model.gi and self._version <= 4: - newval = newval.rescale(u.second) + newval = newval.to(u.second) self._file.sendcmd('+t:{}'.format(int(newval.magnitude))) else: - newval = newval.rescale(u.millisecond) + newval = newval.to(u.millisecond) self._file.sendcmd("++read_tmo_ms {}".format(int(newval.magnitude))) - self._file.timeout = newval.rescale(u.second) - self._timeout = newval.rescale(u.second) + self._file.timeout = newval.to(u.second) + self._timeout = newval.to(u.second) @property def terminator(self): diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index d456bb5a6..ea48ab647 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -11,7 +11,7 @@ import io import serial -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -80,14 +80,14 @@ def timeout(self): """ Gets/sets the communication timeout of the serial comm channel. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: As specified or assumed to be of units ``seconds`` """ return self._conn.timeout * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, u.second).rescale(u.second).magnitude + newval = assume_units(newval, u.second).to(u.second).magnitude self._conn.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index e3f082ba3..ad31855c7 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -11,7 +11,7 @@ import io import socket -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units @@ -70,14 +70,14 @@ def timeout(self): """ Gets/sets the connection timeout of the socket comm channel. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: As specified or assumed to be of units ``seconds`` """ return self._conn.gettimeout() * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, u.second).rescale(u.second).magnitude + newval = assume_units(newval, u.second).to(u.second).magnitude self._conn.settimeout(newval) # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 9e2e6c1ad..e50b6beee 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -14,7 +14,7 @@ from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units -import instruments.units as u +from instruments.units import ureg as u # CLASSES ##################################################################### @@ -67,14 +67,14 @@ def timeout(self): """ Gets/sets the communication timeout of the usbtmc comm channel. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: As specified or assumed to be of units ``seconds`` """ return self._filelike.timeout * u.second @timeout.setter def timeout(self, newval): - newval = assume_units(newval, u.second).rescale(u.s).magnitude + newval = assume_units(newval, u.second).to(u.s).magnitude self._filelike.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 36aea3742..d32cbb5b8 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -14,7 +14,7 @@ from instruments.abstract_instruments.comm import AbstractCommunicator from instruments.util_fns import assume_units -import instruments.units as u +from instruments.units import ureg as u # CLASSES ##################################################################### @@ -86,7 +86,7 @@ def timeout(self): @timeout.setter def timeout(self, newval): - newval = assume_units(newval, u.second).rescale(u.second).magnitude + newval = assume_units(newval, u.second).to(u.second).magnitude self._conn.timeout = newval # FILE-LIKE METHODS # diff --git a/instruments/abstract_instruments/comm/vxi11_communicator.py b/instruments/abstract_instruments/comm/vxi11_communicator.py index b5f9efa2f..c997668d2 100644 --- a/instruments/abstract_instruments/comm/vxi11_communicator.py +++ b/instruments/abstract_instruments/comm/vxi11_communicator.py @@ -89,7 +89,7 @@ def timeout(self): """ Gets/sets the communication timeout of the vxi11 comm channel. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: As specified or assumed to be of units ``seconds`` """ return self._inst.timeout diff --git a/instruments/abstract_instruments/electrometer.py b/instruments/abstract_instruments/electrometer.py index 26aa251b8..8eaaa5e54 100644 --- a/instruments/abstract_instruments/electrometer.py +++ b/instruments/abstract_instruments/electrometer.py @@ -47,7 +47,7 @@ def unit(self): Gets/sets the measurement mode for the electrometer. This is an abstract method. - :type: `~quantities.UnitQuantity` + :type: `~pint.Unit` """ @property diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index 46d4563de..8d179a9da 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -10,8 +10,10 @@ import abc from enum import Enum +from pint.errors import DimensionalityError + from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units, ProxyList # CLASSES ##################################################################### @@ -57,7 +59,7 @@ def frequency(self): Gets/sets the the output frequency of the function generator. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ if self._parent._channel_count == 1: return self._parent.frequency @@ -97,7 +99,7 @@ def offset(self): Gets/sets the output offset voltage of the function generator. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ if self._parent._channel_count == 1: return self._parent.offset @@ -117,7 +119,7 @@ def phase(self): Gets/sets the output phase of the function generator. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ if self._parent._channel_count == 1: return self._parent.phase @@ -151,14 +153,14 @@ def amplitude(self): If set with units of :math:`\\text{dBm}`, then no voltage mode can be passed. - If set with units of :math:`\\text{V}` as a `~quantities.Quantity` or a + If set with units of :math:`\\text{V}` as a `~pint.Quantity` or a `float` without a voltage mode, then the voltage mode is assumed to be peak-to-peak. :units: As specified, or assumed to be :math:`\\text{V}` if not specified. - :type: Either a `tuple` of a `~quantities.Quantity` and a - `FunctionGenerator.VoltageMode`, or a `~quantities.Quantity` + :type: Either a `tuple` of a `~pint.Quantity` and a + `FunctionGenerator.VoltageMode`, or a `~pint.Quantity` if no voltage mode applies. """ mag, units = self._get_amplitude_() @@ -173,10 +175,10 @@ def amplitude(self, newval): # Try and rescale to dBm... if it succeeds, set the magnitude # and units accordingly, otherwise handle as a voltage. try: - newval_dbm = newval.rescale(u.dBm) + newval_dbm = newval.to(u.dBm) mag = float(newval_dbm.magnitude) units = self._parent.VoltageMode.dBm - except (AttributeError, ValueError): + except (AttributeError, ValueError, DimensionalityError): # OK, we have volts. Now, do we have a tuple? If not, assume Vpp. if not isinstance(newval, tuple): mag = newval @@ -185,7 +187,7 @@ def amplitude(self, newval): mag, units = newval # Finally, convert the magnitude out to a float. - mag = float(assume_units(mag, u.V).rescale(u.V).magnitude) + mag = float(assume_units(mag, u.V).to(u.V).magnitude) self._set_amplitude_(mag, units) @@ -237,7 +239,7 @@ def amplitude(self): Gets/sets the output amplitude of the first channel of the function generator - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self.channel[0].amplitude @@ -257,7 +259,7 @@ def frequency(self): Gets/sets the the output frequency of the function generator. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ if self._channel_count > 1: return self.channel[0].frequency @@ -297,7 +299,7 @@ def offset(self): Gets/sets the output offset voltage of the function generator. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ if self._channel_count > 1: return self.channel[0].offset @@ -317,7 +319,7 @@ def phase(self): Gets/sets the output phase of the function generator. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ if self._channel_count > 1: return self.channel[0].phase diff --git a/instruments/abstract_instruments/multimeter.py b/instruments/abstract_instruments/multimeter.py index a75acec13..b46f67d5b 100644 --- a/instruments/abstract_instruments/multimeter.py +++ b/instruments/abstract_instruments/multimeter.py @@ -77,7 +77,7 @@ def input_range(self): Gets/sets the current input range setting of the multimeter. This is an abstract method. - :type: `~quantities.quantity.Quantity` or `~enum.Enum` + :type: `~pint.Quantity` or `~enum.Enum` """ @input_range.setter diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py index 6bd4e64d1..b8e32c93d 100644 --- a/instruments/abstract_instruments/optical_spectrum_analyzer.py +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -79,7 +79,7 @@ def start_wl(self): Gets/sets the the start wavelength of the OSA. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ raise NotImplementedError @@ -95,7 +95,7 @@ def stop_wl(self): Gets/sets the the stop wavelength of the OSA. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ raise NotImplementedError @@ -111,7 +111,7 @@ def bandwidth(self): Gets/sets the the bandwidth of the OSA. This is an abstract property. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ raise NotImplementedError diff --git a/instruments/abstract_instruments/power_supply.py b/instruments/abstract_instruments/power_supply.py index f1c309ad7..8c7af11e7 100644 --- a/instruments/abstract_instruments/power_supply.py +++ b/instruments/abstract_instruments/power_supply.py @@ -46,7 +46,7 @@ def voltage(self): Gets/sets the output voltage for the power supply channel. This is an abstract method. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @voltage.setter @@ -61,7 +61,7 @@ def current(self): Gets/sets the output current for the power supply channel. This is an abstract method. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @current.setter @@ -116,7 +116,7 @@ def voltage(self): Gets/sets the output voltage for all channel on the power supply. This is an abstract method. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @voltage.setter @@ -131,7 +131,7 @@ def current(self): Gets/sets the output current for all channel on the power supply. This is an abstract method. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @current.setter diff --git a/instruments/abstract_instruments/signal_generator/channel.py b/instruments/abstract_instruments/signal_generator/channel.py index c65b38937..a4838d63b 100644 --- a/instruments/abstract_instruments/signal_generator/channel.py +++ b/instruments/abstract_instruments/signal_generator/channel.py @@ -29,7 +29,7 @@ def frequency(self): """ Gets/sets the output frequency of the signal generator channel - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @frequency.setter @@ -43,7 +43,7 @@ def power(self): """ Gets/sets the output power of the signal generator channel - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @power.setter @@ -57,7 +57,7 @@ def phase(self): """ Gets/sets the output phase of the signal generator channel - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ @phase.setter diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 68179d393..37a6324d7 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -10,7 +10,7 @@ from instruments.generic_scpi import SCPIFunctionGenerator -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( enum_property, int_property, bool_property, assume_units ) @@ -156,9 +156,9 @@ def load_resistance(self): function allows the instrument to compensate of the voltage divider and accurately report the voltage across the attached load. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units :math:`\\Omega` (ohm). - :type: `~quantities.quantity.Quantity` or `Agilent33220a.LoadResistance` + :type: `~pint.Quantity` or `Agilent33220a.LoadResistance` """ value = self.query("OUTP:LOAD?") try: @@ -171,7 +171,7 @@ def load_resistance(self, newval): if isinstance(newval, self.LoadResistance): newval = newval.value else: - newval = assume_units(newval, u.ohm).rescale(u.ohm).magnitude + newval = assume_units(newval, u.ohm).to(u.ohm).magnitude if (newval < 0) or (newval > 10000): raise ValueError( "Load resistance must be between 0 and 10,000") diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 93c275d2b..2f395c56e 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### from instruments.generic_scpi import SCPIMultimeter -import instruments.units as u +from instruments.units import ureg as u # CLASSES ##################################################################### @@ -78,7 +78,7 @@ def r(self, count): :param int count: Number of samples to take. - :rtype: `~quantities.quantity.Quantity` with `numpy.array` + :rtype: `~pint.Quantity` with `numpy.array` """ mode = self.mode units = UNITS[mode] @@ -107,7 +107,7 @@ def fetch(self): recommended to transfer a large number of data points using this method. - :rtype: `list` of `~quantities.quantity.Quantity` elements + :rtype: `list` of `~pint.Quantity` elements """ units = UNITS[self.mode] return list(map(float, self.query('FETC?').split(','))) * units @@ -123,7 +123,7 @@ def read_data(self, sample_count): output buffer. If set to -1, all points in memory will be transfered. - :rtype: `list` of `~quantities.quantity.Quantity` elements + :rtype: `list` of `~pint.Quantity` elements """ if not isinstance(sample_count, int): raise TypeError('Parameter "sample_count" must be an integer.') @@ -139,7 +139,7 @@ def read_data_nvmem(self): """ Returns all readings in non-volatile memory (NVMEM). - :rtype: `list` of `~quantities.quantity.Quantity` elements + :rtype: `list` of `~pint.Quantity` elements """ units = UNITS[self.mode] data = list(map(float, self.query('DATA:DATA? NVMEM').split(','))) @@ -153,7 +153,7 @@ def read_last_data(self): returned. :units: As specified by the data returned by the instrument. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ data = self.query('DATA:LAST?') unit_map = { @@ -179,7 +179,7 @@ def read_meter(self): This is similar to calling `~Agilent34410a.init` and then immediately following `~Agilent34410a.fetch`. - :rtype: `~quantities.Quantity` + :rtype: `~pint.Quantity` """ mode = self.mode units = UNITS[mode] diff --git a/instruments/config.py b/instruments/config.py index d477974e8..493d16b97 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -21,7 +21,7 @@ # the import-error check should be re-enabled. import ruamel_yaml as yaml # pylint: disable=import-error -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import setattr_expression, split_unit_str # FUNCTIONS ################################################################### diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index 7721c7586..be191cabc 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -37,7 +37,7 @@ from enum import Enum from instruments.abstract_instruments import Multimeter -import instruments.units as u +from instruments.units import ureg as u # CLASSES ##################################################################### @@ -307,7 +307,7 @@ def measure(self, mode): :type mode: `Fluke3000.Mode` :return: A measurement from the multimeter. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ # Check that the mode is supported @@ -349,7 +349,7 @@ def measure(self, mode): # Return with the appropriate units units = UNITS[mode] - return value * units + return u.Quantity(value, units) def _get_module(self, mode): """Gets the module associated with this measurement mode. @@ -472,7 +472,7 @@ def _parse_factor(data): Fluke3000.Mode.current_ac: u.amp, Fluke3000.Mode.current_dc: u.amp, Fluke3000.Mode.frequency: u.hertz, - Fluke3000.Mode.temperature: u.celsius, + Fluke3000.Mode.temperature: u.degC, Fluke3000.Mode.resistance: u.ohm, Fluke3000.Mode.capacitance: u.farad } diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 669d11831..5d200231c 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import FunctionGenerator from instruments.generic_scpi import SCPIInstrument @@ -79,7 +79,7 @@ def _set_amplitude_(self, magnitude, units): Gets/sets the output frequency. :units: As specified, or assumed to be :math:`\\text{Hz}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -101,9 +101,9 @@ def _set_amplitude_(self, magnitude, units): Set value should be within correct bounds of instrument. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units volts. - :type: `~quantities.quantity.Quantity` with units volts. + :type: `~pint.Quantity` with units volts. """ ) diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index 42c9b7c31..c7b77572b 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -9,7 +9,7 @@ from enum import IntEnum from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units # CLASSES ##################################################################### @@ -144,7 +144,7 @@ def line_frequency(self): :return: The power line frequency :units: Hertz - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return u.Quantity( float(self.query("SYST:LFR?")), @@ -154,7 +154,7 @@ def line_frequency(self): @line_frequency.setter def line_frequency(self, newval): self.sendcmd("SYST:LFR {}".format( - assume_units(newval, "Hz").rescale("Hz").magnitude + assume_units(newval, "Hz").to("Hz").magnitude )) # ERROR QUEUE HANDLING ## diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index da6f9eb8e..83e32525f 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -9,7 +9,7 @@ from enum import Enum -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import Multimeter from instruments.generic_scpi import SCPIInstrument @@ -179,7 +179,7 @@ def input_range(self): >>> dmm.input_range = 1 * u.millivolt :units: As appropriate for the current mode setting. - :type: `~quantities.Quantity`, or `~SCPIMultimeter.InputRange` + :type: `~pint.Quantity`, or `~SCPIMultimeter.InputRange` """ value = self.query('CONF?') mode = self.Mode(self._mode_parse(value)) @@ -197,7 +197,7 @@ def input_range(self, newval): if isinstance(newval, self.InputRange): newval = newval.value else: - newval = assume_units(newval, units).rescale(units).magnitude + newval = assume_units(newval, units).to(units).magnitude self.sendcmd("CONF:{} {}".format(mode.value, newval)) @property @@ -322,7 +322,7 @@ def sample_count(self, newval): receiving a trigger event before starting the measurement. :units: As specified, or assumed to be of units seconds otherwise. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ ) @@ -354,7 +354,7 @@ def sample_count(self, newval): `~SCPIMultimeter.trigger_delay` property. :units: As specified, or assumed to be of units seconds otherwise. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ ) diff --git a/instruments/gentec_eo/blu.py b/instruments/gentec_eo/blu.py index 7527feb6a..bfd8241e0 100644 --- a/instruments/gentec_eo/blu.py +++ b/instruments/gentec_eo/blu.py @@ -7,7 +7,7 @@ from time import sleep from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units # CLASSES ##################################################################### @@ -462,14 +462,13 @@ def user_offset(self): def user_offset(self, newval): # if unitful, try to rescale and grab magnitude if isinstance(newval, u.Quantity): - try: - newval = newval.rescale(u.W).magnitude - except ValueError: # so it's not in W - try: - newval = newval.rescale(u.J).magnitude - except ValueError: # invalid unit - raise ValueError("Value must be given in watts, " - "joules, or unitless.") + if newval.is_compatible_with(u.W): + newval = newval.to(u.W).magnitude + elif newval.is_compatible_with(u.J): + newval = newval.to(u.J).magnitude + else: + raise ValueError("Value must be given in watts, " + "joules, or unitless.") sendval = _format_eight(newval) # sendval: 8 characters long self.sendcmd("*OFF{}".format(sendval)) @@ -512,7 +511,7 @@ def wavelength(self): @wavelength.setter def wavelength(self, newval): - val = assume_units(newval, u.nm).rescale(u.nm).magnitude.round(0) + val = round(assume_units(newval, u.nm).to(u.nm).magnitude) if val >= 1000000 or val < 0: # can only send 5 digits val = 0 # out of bound anyway val = str(int(val)).zfill(5) diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 3ea060ede..7e343b219 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -39,7 +39,7 @@ PowerSupply, PowerSupplyChannel ) -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units # CLASSES ##################################################################### @@ -155,7 +155,7 @@ def voltage(self): Gets/sets the output voltage setting. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ return self.polarity*self._voltage @@ -169,7 +169,7 @@ def current(self): Gets/sets the output current setting. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ return self.polarity*self._current @@ -183,7 +183,7 @@ def voltage_sense(self): Gets the output voltage as measured by the sense wires. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self.get_status()["voltage"] @@ -193,7 +193,7 @@ def current_sense(self): Gets/sets the output current as measured by the sense wires. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self.get_status()["current"] @@ -339,7 +339,7 @@ def set_status(self, voltage=None, current=None, output=None, reset=False): # If the voltage is not specified, keep it as is voltage = assume_units(voltage, u.volt) if voltage is not None else self.voltage - ratio = float(voltage.rescale(u.volt)/self.voltage_max.rescale(u.volt)) + ratio = float(voltage.to(u.volt)/self.voltage_max.to(u.volt)) voltage_int = int(round(value_max*ratio)) self._voltage = self.voltage_max*float(voltage_int)/value_max assert 0. * u.volt <= self._voltage <= self.voltage_max @@ -347,7 +347,7 @@ def set_status(self, voltage=None, current=None, output=None, reset=False): # If the current is not specified, keep it as is current = assume_units(current, u.amp) if current is not None else self.current - ratio = float(current.rescale(u.amp)/self.current_max.rescale(u.amp)) + ratio = float(current.to(u.amp)/self.current_max.to(u.amp)) current_int = int(round(value_max*ratio)) self._current = self.current_max*float(current_int)/value_max assert 0. * u.amp <= self._current <= self.current_max @@ -408,7 +408,7 @@ def _parse_voltage(self, word): :param word: Byte string to be parsed :type: `bytes` - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ value = int(word.decode('utf-8'), 16) value_max = int(0x3ff) @@ -422,7 +422,7 @@ def _parse_current(self, word): :param word: Byte string to be parsed :type: `bytes` - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ value = int(word.decode("utf-8"), 16) value_max = int(0x3ff) diff --git a/instruments/holzworth/holzworth_hs9000.py b/instruments/holzworth/holzworth_hs9000.py index 06cca8344..9beef39ab 100644 --- a/instruments/holzworth/holzworth_hs9000.py +++ b/instruments/holzworth/holzworth_hs9000.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.signal_generator import ( SignalGenerator, @@ -16,7 +16,6 @@ from instruments.util_fns import ( ProxyList, split_unit_str, bounded_unitful_property, bool_property ) -from instruments.units import dBm # CLASSES ##################################################################### @@ -121,7 +120,7 @@ def temperature(self): Gets the current temperature of the specified channel. :units: As specified by the instrument. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ val, units = split_unit_str(self.query("TEMP?")) units = "deg{}".format(units) @@ -142,13 +141,13 @@ def temperature(self): >>> print(hs.channel[0].frequency_min) >>> print(hs.channel[0].frequency_max) - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` :units: As specified or assumed to be of units GHz """ ) power, power_min, power_max = bounded_unitful_property( "PWR", - units=dBm, + units=u.dBm, doc=""" Gets/sets the output power of the specified channel. When setting, values are bounded between what is returned by `power_min` @@ -161,7 +160,7 @@ def temperature(self): >>> print(hs.channel[0].power_min) >>> print(hs.channel[0].power_max) - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` :units: `instruments.units.dBm` """ ) @@ -180,7 +179,7 @@ def temperature(self): >>> print(hs.channel[0].phase_min) >>> print(hs.channel[0].phase_max) - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` :units: As specified or assumed to be of units degrees """ ) diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index f628451be..0de05f76b 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -36,7 +36,7 @@ from enum import Enum, IntEnum from instruments.abstract_instruments import Multimeter -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units, bool_property, enum_property # CLASSES ##################################################################### @@ -297,7 +297,7 @@ def delay(self): @delay.setter def delay(self, value): - delay = assume_units(value, u.s).rescale(u.s).magnitude + delay = assume_units(value, u.s).to(u.s).magnitude self._register_write(HP3456a.Register.delay, delay) @property @@ -412,11 +412,11 @@ def z(self, value): def input_range(self): """Set the input range to be used. - The `HP3456a` has separate ranges for `~quantities.ohm` and for - `~quantities.volt`. The range value sent to the instrument depends on + The `HP3456a` has separate ranges for `ohm` and for + `volt`. The range value sent to the instrument depends on the unit set on the input range value. `auto` selects auto ranging. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ raise NotImplementedError @@ -429,18 +429,18 @@ def input_range(self, value): raise ValueError("Only 'auto' is acceptable when specifying " "the input range as a string.") - elif isinstance(value, u.quantity.Quantity): + elif isinstance(value, u.Quantity): if value.units == u.volt: valid = HP3456a.ValidRange.voltage.value - value = value.rescale(u.volt) + value = value.to(u.volt) elif value.units == u.ohm: valid = HP3456a.ValidRange.resistance.value - value = value.rescale(u.ohm) + value = value.to(u.ohm) else: raise ValueError("Value {} not quantity.volt or quantity.ohm" "".format(value)) - value = float(value) + value = float(value.magnitude) if value not in valid: raise ValueError("Value {} outside valid ranges " "{}".format(value, valid)) @@ -508,7 +508,7 @@ def fetch(self, mode=None): :type mode: `HP3456a.Mode` :return: A series of measurements from the multimeter. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ if mode is not None: units = UNITS[mode] @@ -542,7 +542,7 @@ def measure(self, mode=None): :type mode: `HP3456a.Mode` :return: A measurement from the multimeter. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ if mode is not None: diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index 638074ef2..cdcbc3c2a 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -12,7 +12,7 @@ PowerSupply, PowerSupplyChannel ) -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList, unitful_property, bool_property # CLASSES ##################################################################### @@ -116,7 +116,7 @@ def mode(self, newval): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -132,7 +132,7 @@ def mode(self, newval): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -145,7 +145,7 @@ def mode(self, newval): specified channel. :units: :math:`\\text{V}` (volts) - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -158,7 +158,7 @@ def mode(self, newval): the specified channel. :units: :math:`\\text{A}` (amps) - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -173,7 +173,7 @@ def mode(self, newval): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -246,9 +246,9 @@ def voltage(self): """ Gets/sets the voltage for all four channels. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :type: `list` of `~quantities.quantity.Quantity` with units Volt + :type: `list` of `~pint.Quantity` with units Volt """ return [ self.channel[i].voltage for i in range(self.channel_count) @@ -272,9 +272,9 @@ def current(self): """ Gets/sets the current for all four channels. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Amps. - :type: `list` of `~quantities.quantity.Quantity` with units Amp + :type: `list` of `~pint.Quantity` with units Amp """ return [ self.channel[i].current for i in range(self.channel_count) @@ -299,7 +299,7 @@ def voltage_sense(self): Gets the actual voltage as measured by the sense wires for all channels. :units: :math:`\\text{V}` (volts) - :rtype: `tuple` of `~quantities.quantity.Quantity` + :rtype: `tuple` of `~pint.Quantity` """ return ( self.channel[i].voltage_sense for i in range(self.channel_count) @@ -311,7 +311,7 @@ def current_sense(self): Gets the actual current as measured by the instrument for all channels. :units: :math:`\\text{A}` (amps) - :rtype: `tuple` of `~quantities.quantity.Quantity` + :rtype: `tuple` of `~pint.Quantity` """ return ( self.channel[i].current_sense for i in range(self.channel_count) diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index 958911685..2fd662eb2 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -36,7 +36,7 @@ from instruments.generic_scpi.scpi_instrument import SCPIInstrument from instruments.hp.hp6652a import HP6652a -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import (unitful_property, unitless_property, bool_property, enum_property, int_property) @@ -262,7 +262,7 @@ class SenseWindow(Enum): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -275,7 +275,7 @@ class SenseWindow(Enum): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -304,7 +304,7 @@ class SenseWindow(Enum): range increases the low current measurement sensitivity and accuracy. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -381,7 +381,7 @@ class SenseWindow(Enum): seconds, the interval will be rounded to the nearest 15.6 us increment. :units: As specified, or assumed to be :math:`\\text{s}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -405,7 +405,7 @@ class SenseWindow(Enum): current protection, but not overvoltage protection. :units: As specified, or assumed to be :math:`\\text{s}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index f1175dc4d..09470dfff 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -9,7 +9,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import (PowerSupply, PowerSupplyChannel) from instruments.util_fns import unitful_property, bool_property @@ -69,7 +69,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -82,7 +82,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -94,7 +94,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): Gets the actual output voltage as measured by the sense wires. :units: :math:`\\text{V}` (volts) - :rtype: `~quantities.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -106,7 +106,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): Gets the actual output current as measured by the sense wires. :units: :math:`\\text{A}` (amps) - :rtype: `~quantities.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -119,7 +119,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): Note there is no bounds checking on the value specified. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index a3e6e6a5b..d5cfdbe5d 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -35,7 +35,7 @@ import time -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import ( PowerSupply, @@ -159,10 +159,10 @@ def voltage(self): Gets/sets the output voltage of the source. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ raw = self.query("SOUR:VOLT?") - return u.Quantity(*split_unit_str(raw, u.volt)).rescale(u.volt) + return u.Quantity(*split_unit_str(raw, u.volt)).to(u.volt) @voltage.setter def voltage(self, newval): @@ -170,7 +170,7 @@ def voltage(self, newval): Gets/sets the output voltage of the source. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ min_value, max_value = self.voltage_range if newval < min_value: @@ -183,7 +183,7 @@ def voltage(self, newval): # Rescale to the correct unit before printing. This will also # catch bad units. - strval = "{:e}".format(assume_units(newval, u.volt).rescale(u.volt).item()) + strval = "{:e}".format(assume_units(newval, u.volt).to(u.volt).magnitude) self.sendcmd('SOUR:VOLT {}'.format(strval)) @property @@ -192,7 +192,7 @@ def voltage_min(self): Gets the minimum voltage for the current channel. :units: :math:`\\text{V}`. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self.voltage_range[0] @@ -202,7 +202,7 @@ def voltage_max(self): Gets the maximum voltage for the current channel. :units: :math:`\\text{V}`. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self.voltage_range[1] @@ -217,7 +217,7 @@ def voltage_range(self): order the values as MAX can be negative. :units: :math:`\\text{V}`. - :type: array of `~quantities.Quantity` + :type: array of `~pint.Quantity` """ value = u.Quantity(*split_unit_str(self.query("SOUR:VOLT? MAX"), u.volt)) if value < 0.: @@ -233,7 +233,7 @@ def voltage_range(self): Gets/sets the output current of the source. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -245,7 +245,7 @@ def voltage_range(self): Gets the actual output voltage as measured by the sense wires. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ ) @@ -257,7 +257,7 @@ def voltage_range(self): Gets the actual output current as measured by the sense wires. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ ) diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index 8b8d907e0..a0ba2e3fc 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -11,9 +11,8 @@ import struct from enum import Enum, IntEnum -import instruments.units as u - from instruments.abstract_instruments import Multimeter +from instruments.units import ureg as u # CLASSES ##################################################################### @@ -188,7 +187,7 @@ def input_range(self): All modes will also accept the string ``auto`` which will set the 195 into auto ranging mode. - :rtype: `~quantities.quantity.Quantity` or `str` + :rtype: `~pint.Quantity` or `str` """ index = self.parse_status_word(self.get_status_word())['range'] if index == 0: @@ -208,8 +207,8 @@ def input_range(self, newval): else: raise ValueError('Only "auto" is acceptable when specifying ' 'the input range as a string.') - if isinstance(newval, u.quantity.Quantity): - newval = float(newval) + if isinstance(newval, u.Quantity): + newval = float(newval.magnitude) mode = self.mode valid = Keithley195.ValidRange[mode.name].value @@ -253,7 +252,7 @@ def measure(self, mode=None): :type mode: `Keithley195.Mode` :return: A measurement from the multimeter. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ if mode is not None: current_mode = self.mode diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index e43a9a23e..c9d7afd5d 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### from enum import Enum -import instruments.units as u +from instruments.units import ureg as u from instruments.generic_scpi import SCPIMultimeter from instruments.abstract_instruments import Multimeter @@ -90,7 +90,7 @@ def measure(self, mode=None): :param mode: Mode that the measurement will be performed in :type mode: Keithley2182.Mode :return: The value of the measurement - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ if mode is not None: # self.mode = mode @@ -98,7 +98,7 @@ def measure(self, mode=None): self._parent.sendcmd('SENS:CHAN {}'.format(self._idx)) value = float(self._parent.query('SENS:DATA:FRES?')) unit = self._parent.units - return value * unit + return u.Quantity(value, unit) # ENUMS # @@ -182,7 +182,7 @@ def units(self): """ Gets the current measurement units of the instrument. - :rtype: `~quantities.unitquantity.UnitQuantity` + :rtype: `~pint.Unit` """ mode = self.channel[0].mode if mode == Keithley2182.Mode.voltage_dc: @@ -212,7 +212,7 @@ def fetch(self): recommended to transfer a large number of data points using GPIB. :return: Measurement readings from the instrument output buffer. - :rtype: `list` of `~quantities.quantity.Quantity` elements + :rtype: `list` of `~pint.Quantity` elements """ return list(map(float, self.query("FETC?").split(","))) * self.units @@ -225,7 +225,7 @@ def measure(self, mode=None): :type: `Keithley2182.Mode` :return: Returns a single shot measurement of the specified mode. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` :units: Volts, Celsius, Kelvin, or Fahrenheit """ if mode is None: diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index 365677341..a45e359ed 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -37,7 +37,7 @@ from enum import Enum from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u # CLASSES ##################################################################### @@ -182,7 +182,7 @@ def input_range(self): Gets/sets the range (R) of the Keithley 485 input terminals. The valid ranges are one of ``{auto|2e-9|2e-8|2e-7|2e-6|2e-5|2e-4|2e-3}`` - :type: `~quantities.quantity.Quantity` or `str` + :type: `~pint.Quantity` or `str` """ value = self.get_status()["range"] if isinstance(value, str): @@ -200,7 +200,7 @@ def input_range(self, newval): else: raise ValueError("Only `auto` is acceptable when specifying " "the range as a string.") - if isinstance(newval, u.quantity.Quantity): + if isinstance(newval, u.Quantity): newval = float(newval) if isinstance(newval, (float, int)): @@ -393,7 +393,7 @@ def measure(self): """ Perform a current measurement with the Keithley 485. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ return self._parse_measurement(self.query("X")) @@ -406,7 +406,7 @@ def _parse_measurement(self, measurement): :param measurement: String to be unpacked and parsed :type: `str` - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ (status, function, base, current) = \ unpack("@1c2s1c10s", bytes(measurement, "utf-8")) diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index 5a53b01e2..82d9292cc 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -38,7 +38,7 @@ from enum import IntEnum -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import Instrument @@ -257,7 +257,7 @@ def input_range(self): Gets/sets the range of the Keithley 580 input terminals. The valid ranges are one of ``{AUTO|2e-1|2|20|200|2000|2e4|2e5}`` - :type: `~quantities.quantity.Quantity` or `str` + :type: `~pint.Quantity` or `str` """ value = self.parse_status_word(self.get_status_word())['range'] if isinstance(value, str): # if range is 'auto' @@ -276,8 +276,8 @@ def input_range(self, newval): else: raise ValueError('Only "auto" is acceptable when specifying ' 'the input range as a string.') - if isinstance(newval, u.quantity.Quantity): - newval = float(newval) + if isinstance(newval, u.Quantity): + newval = float(newval.magnitude) if isinstance(newval, (float, int)): if newval in valid: @@ -414,7 +414,7 @@ def measure(self): The usual mode parameter is ignored for the Keithley 580 as the only valid mode is resistance. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ self.trigger() self.sendcmd('') diff --git a/instruments/keithley/keithley6220.py b/instruments/keithley/keithley6220.py index cb7497f04..ee5f68931 100644 --- a/instruments/keithley/keithley6220.py +++ b/instruments/keithley/keithley6220.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import PowerSupply from instruments.generic_scpi import SCPIInstrument @@ -73,7 +73,7 @@ def voltage(self, newval): -105mA and +105mA. :units: As specified, or assumed to be :math:`\\text{A}` otherwise. - :type: `float` or `~quantities.Quantity` + :type: `float` or `~pint.Quantity` """ ) diff --git a/instruments/keithley/keithley6514.py b/instruments/keithley/keithley6514.py index ec97f6b2d..911be3067 100644 --- a/instruments/keithley/keithley6514.py +++ b/instruments/keithley/keithley6514.py @@ -10,7 +10,7 @@ from instruments.abstract_instruments import Electrometer from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import bool_property, enum_property # CLASSES ##################################################################### @@ -175,7 +175,7 @@ def input_range(self): """ Gets/sets the upper limit of the current range. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ # pylint: disable=no-member mode = self.mode @@ -186,7 +186,7 @@ def input_range(self): def input_range(self, newval): # pylint: disable=no-member mode = self.mode - val = newval.rescale(self._MODE_UNITS[mode]).item() + val = newval.to(self._MODE_UNITS[mode]).magnitude if val not in self._valid_range(mode).value: raise ValueError( 'Unexpected range limit for currently selected mode.') diff --git a/instruments/lakeshore/lakeshore340.py b/instruments/lakeshore/lakeshore340.py index 03b2cf190..a5c6200c4 100644 --- a/instruments/lakeshore/lakeshore340.py +++ b/instruments/lakeshore/lakeshore340.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -50,10 +50,10 @@ def temperature(self): Gets the temperature of the specified sensor. :units: Kelvin - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ value = self._parent.query('KRDG?{}'.format(self._idx)) - return u.Quantity(float(value), u.Kelvin) + return u.Quantity(float(value), u.kelvin) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore370.py b/instruments/lakeshore/lakeshore370.py index e19bc2981..8871201ad 100644 --- a/instruments/lakeshore/lakeshore370.py +++ b/instruments/lakeshore/lakeshore370.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -54,7 +54,7 @@ def resistance(self): Gets the resistance of the specified sensor. :units: Ohm - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ value = self._parent.query('RDGR? {}'.format(self._idx)) return u.Quantity(float(value), u.ohm) diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 19282249c..d2f97c59d 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -9,7 +9,7 @@ from enum import IntEnum from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units, bool_property # CONSTANTS ################################################################### @@ -18,7 +18,7 @@ 1: u.gauss, 2: u.tesla, 3: u.oersted, - 4: u.CompoundUnit('A/m') + 4: u.amp / u.meter } LAKESHORE_TEMP_UNITS = { @@ -89,7 +89,7 @@ def field(self): """ Read field from connected probe. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return float(self.query('RDGFIELD?')) * self.field_units @@ -100,14 +100,14 @@ def field_units(self): Acceptable units are Gauss, Tesla, Oersted, and Amp/meter. - :type: `~quantities.unitquantity.UnitQuantity` + :type: `~pint.Unit` """ value = int(self.query('UNIT?')) return LAKESHORE_FIELD_UNITS[value] @field_units.setter def field_units(self, newval): - if isinstance(newval, u.unitquantity.UnitQuantity): + if isinstance(newval, u.Unit): if newval in LAKESHORE_FIELD_UNITS_INV: self.sendcmd(f"UNIT {LAKESHORE_FIELD_UNITS_INV[newval]}") else: @@ -122,14 +122,14 @@ def temp_units(self): Acceptable units are celcius and kelvin. - :type: `~quantities.unitquantity.UnitQuantity` + :type: `~pint.Unit` """ value = int(self.query('TUNIT?')) return LAKESHORE_TEMP_UNITS[value] @temp_units.setter def temp_units(self, newval): - if isinstance(newval, u.unitquantity.UnitQuantity): + if isinstance(newval, u.Unit): if newval in LAKESHORE_TEMP_UNITS_INV: self.sendcmd(f"TUNIT {LAKESHORE_TEMP_UNITS_INV[newval]}") else: @@ -142,9 +142,9 @@ def field_setpoint(self): """ Gets/sets the final setpoint of the field control ramp. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Gauss. - :type: `~quantities.quantity.Quantity` with units Gauss + :type: `~pint.Quantity` with units Gauss """ value = self.query('CSETP?').strip() units = self.field_units @@ -152,9 +152,15 @@ def field_setpoint(self): @field_setpoint.setter def field_setpoint(self, newval): - units = self.field_units - newval = float(assume_units(newval, u.gauss).rescale(units).magnitude) - self.sendcmd('CSETP {}'.format(newval)) + expected_units = self.field_units + newval = assume_units(newval, u.gauss) + + if newval.units != expected_units: + raise ValueError(f"Field setpoint must be specified in the same units " + f"that the field units are currently set to. Attempts units of " + f"{newval.units}, currently expecting {expected_units}.") + + self.sendcmd('CSETP {}'.format(newval.magnitude)) @property def field_control_params(self): @@ -162,7 +168,7 @@ def field_control_params(self): Gets/sets the parameters associated with the field control ramp. These are (in this order) the P, I, ramp rate, and control slope limit. - :type: `tuple` of 2 `float` and 2 `~quantities.quantity.Quantity` + :type: `tuple` of 2 `float` and 2 `~pint.Quantity` """ params = self.query('CPARAM?').strip().split(',') params = [float(x) for x in params] @@ -175,23 +181,21 @@ def field_control_params(self, newval): if not isinstance(newval, tuple): raise TypeError('Field control parameters must be specified as ' ' a tuple') - newval = list(newval) - newval[0] = float(newval[0]) - newval[1] = float(newval[1]) + p, i, ramp_rate, control_slope_lim = newval + + expected_units = self.field_units / u.minute + + ramp_rate = assume_units(ramp_rate, expected_units) + if ramp_rate.units != expected_units: + raise ValueError(f"Field control params ramp rate must be specified in the same units " + f"that the field units are currently set to, per minute. Attempts units of " + f"{ramp_rate.units}, currently expecting {expected_units}.") + ramp_rate = float(ramp_rate.magnitude) - unit = self.field_units / u.minute - newval[2] = float( - assume_units(newval[2], unit).rescale(unit).magnitude) unit = u.volt / u.minute - newval[3] = float( - assume_units(newval[3], unit).rescale(unit).magnitude) - - self.sendcmd('CPARAM {},{},{},{}'.format( - newval[0], - newval[1], - newval[2], - newval[3], - )) + control_slope_lim = float(assume_units(control_slope_lim, unit).to(unit).magnitude) + + self.sendcmd(f"CPARAM {p},{i},{ramp_rate},{control_slope_lim}") @property def p_value(self): @@ -230,16 +234,16 @@ def ramp_rate(self): """ Gets/sets the ramp rate value for the field control ramp. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current field units / minute. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return self.field_control_params[2] @ramp_rate.setter def ramp_rate(self, newval): unit = self.field_units / u.minute - newval = float(assume_units(newval, unit).rescale(unit).magnitude) + newval = float(assume_units(newval, unit).to(unit).magnitude) values = list(self.field_control_params) values[2] = newval self.field_control_params = tuple(values) @@ -249,16 +253,16 @@ def control_slope_limit(self): """ Gets/sets the I value for the field control ramp. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units volt / minute. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return self.field_control_params[3] @control_slope_limit.setter def control_slope_limit(self, newval): unit = u.volt / u.minute - newval = float(assume_units(newval, unit).rescale(unit).magnitude) + newval = float(assume_units(newval, unit).to(unit).magnitude) values = list(self.field_control_params) values[3] = newval self.field_control_params = tuple(values) diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py index 8d737b3a1..50673932c 100644 --- a/instruments/minghe/mhs5200a.py +++ b/instruments/minghe/mhs5200a.py @@ -11,7 +11,7 @@ from enum import Enum from instruments.abstract_instruments import FunctionGenerator -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList, assume_units # CLASSES ##################################################################### @@ -66,7 +66,7 @@ def _get_amplitude_(self): def _set_amplitude_(self, magnitude, units): if units == self._mhs.VoltageMode.peak_to_peak or \ units == self._mhs.VoltageMode.rms: - magnitude = assume_units(magnitude, "V").rescale(u.V).magnitude + magnitude = assume_units(magnitude, "V").to(u.V).magnitude elif units == self._mhs.VoltageMode.dBm: raise NotImplementedError("Decibel units are not supported.") magnitude *= 100 @@ -112,9 +112,9 @@ def frequency(self): """ Gets/Sets the frequency of this channel. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units hertz. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ query = ":r{0}f".format(self._chan) response = self._mhs.query(query) @@ -123,7 +123,7 @@ def frequency(self): @frequency.setter def frequency(self, new_val): - new_val = assume_units(new_val, u.Hz).rescale(u.Hz).\ + new_val = assume_units(new_val, u.Hz).to(u.Hz).\ magnitude*100.0 query = ":s{0}f{1}".format(self._chan, int(new_val)) self._mhs.sendcmd(query) @@ -153,9 +153,9 @@ def phase(self): """ Gets/Sets the phase of this channel. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of degrees. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ # need to convert query = ":r{0}p".format(self._chan) @@ -164,7 +164,7 @@ def phase(self): @phase.setter def phase(self, new_val): - new_val = assume_units(new_val, u.deg).rescale("deg").magnitude + new_val = assume_units(new_val, u.deg).to("deg").magnitude query = ":s{0}p{1}".format(self._chan, int(new_val)) self._mhs.sendcmd(query) diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index 47a93d2d5..db06be510 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -17,7 +17,7 @@ from instruments.abstract_instruments import Instrument from instruments.newport.errors import NewportError -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units, ProxyList # ENUMS ####################################################################### @@ -299,7 +299,8 @@ class NewportESP301Axis: returned by `NewportESP301.axis`. """ # quantities micro inch - micro_inch = u.UnitQuantity('micro-inch', u.inch / 1e6, symbol='uin') + # micro_inch = u.UnitQuantity('micro-inch', u.inch / 1e6, symbol='uin') + micro_inch = u.uinch # Some more work might need to be done here to make # the encoder_step and motor_step functional @@ -393,9 +394,9 @@ def acceleration(self): """ Gets/sets the axis acceleration - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport unit - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( @@ -407,7 +408,7 @@ def acceleration(self): def acceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (u.s**2)).rescale( + newval = float(assume_units(newval, self._units / (u.s**2)).to( self._units / (u.s**2)).magnitude) self._newport_cmd("AC", target=self.axis_id, params=[newval]) @@ -416,9 +417,9 @@ def deceleration(self): """ Gets/sets the axis deceleration - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s^2}` - :type: `~quantities.Quantity` or float + :type: `~pint.Quantity` or float """ return assume_units( float(self._newport_cmd("AG?", target=self.axis_id)), @@ -429,7 +430,7 @@ def deceleration(self): def deceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (u.s**2)).rescale( + newval = float(assume_units(newval, self._units / (u.s**2)).to( self._units / (u.s**2)).magnitude) self._newport_cmd("AG", target=self.axis_id, params=[newval]) @@ -438,9 +439,9 @@ def estop_deceleration(self): """ Gets/sets the axis estop deceleration - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s^2}` - :type: `~quantities.Quantity` or float + :type: `~pint.Quantity` or float """ return assume_units( float(self._newport_cmd("AE?", target=self.axis_id)), @@ -449,7 +450,7 @@ def estop_deceleration(self): @estop_deceleration.setter def estop_deceleration(self, decel): - decel = float(assume_units(decel, self._units / (u.s**2)).rescale( + decel = float(assume_units(decel, self._units / (u.s**2)).to( self._units / (u.s**2)).magnitude) self._newport_cmd("AE", target=self.axis_id, params=[decel]) @@ -458,9 +459,9 @@ def jerk(self): """ Gets/sets the jerk rate for the controller - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport unit - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( @@ -470,7 +471,7 @@ def jerk(self): @jerk.setter def jerk(self, jerk): - jerk = float(assume_units(jerk, self._units / (u.s**3)).rescale( + jerk = float(assume_units(jerk, self._units / (u.s**3)).to( self._units / (u.s**3)).magnitude) self._newport_cmd("JK", target=self.axis_id, params=[jerk]) @@ -479,9 +480,9 @@ def velocity(self): """ Gets/sets the axis velocity - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("VA?", target=self.axis_id)), @@ -490,7 +491,7 @@ def velocity(self): @velocity.setter def velocity(self, velocity): - velocity = float(assume_units(velocity, self._units / (u.s)).rescale( + velocity = float(assume_units(velocity, self._units / (u.s)).to( self._units / u.s).magnitude) self._newport_cmd("VA", target=self.axis_id, params=[velocity]) @@ -499,9 +500,9 @@ def max_velocity(self): """ Gets/sets the axis maximum velocity - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("VU?", target=self.axis_id)), @@ -512,7 +513,7 @@ def max_velocity(self): def max_velocity(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / u.s).rescale( + newval = float(assume_units(newval, self._units / u.s).to( self._units / u.s).magnitude) self._newport_cmd("VU", target=self.axis_id, params=[newval]) @@ -521,9 +522,9 @@ def max_base_velocity(self): """ Gets/sets the maximum base velocity for stepper motors - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("VB?", target=self.axis_id)), @@ -534,7 +535,7 @@ def max_base_velocity(self): def max_base_velocity(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / u.s).rescale( + newval = float(assume_units(newval, self._units / u.s).to( self._units / u.s).magnitude) self._newport_cmd("VB", target=self.axis_id, params=[newval]) @@ -543,9 +544,9 @@ def jog_high_velocity(self): """ Gets/sets the axis jog high velocity - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("JH?", target=self.axis_id)), @@ -559,7 +560,7 @@ def jog_high_velocity(self, newval): newval = float(assume_units( newval, self._units / u.s - ).rescale(self._units / u.s).magnitude) + ).to(self._units / u.s).magnitude) self._newport_cmd("JH", target=self.axis_id, params=[newval]) @property @@ -567,9 +568,9 @@ def jog_low_velocity(self): """ Gets/sets the axis jog low velocity - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("JW?", target=self.axis_id)), @@ -583,7 +584,7 @@ def jog_low_velocity(self, newval): newval = float(assume_units( newval, self._units / u.s - ).rescale(self._units / u.s).magnitude) + ).to(self._units / u.s).magnitude) self._newport_cmd("JW", target=self.axis_id, params=[newval]) @property @@ -591,9 +592,9 @@ def homing_velocity(self): """ Gets/sets the axis homing velocity - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("OH?", target=self.axis_id)), @@ -607,7 +608,7 @@ def homing_velocity(self, newval): newval = float(assume_units( newval, self._units / u.s - ).rescale(self._units / u.s).magnitude) + ).to(self._units / u.s).magnitude) self._newport_cmd("OH", target=self.axis_id, params=[newval]) @property @@ -615,9 +616,9 @@ def max_acceleration(self): """ Gets/sets the axis max acceleration - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s^2}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("AU?", target=self.axis_id)), @@ -628,7 +629,7 @@ def max_acceleration(self): def max_acceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (u.s**2)).rescale( + newval = float(assume_units(newval, self._units / (u.s**2)).to( self._units / (u.s**2)).magnitude) self._newport_cmd("AU", target=self.axis_id, params=[newval]) @@ -638,15 +639,15 @@ def max_deceleration(self): Gets/sets the axis max decceleration. Max deaceleration is always the same as acceleration. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\frac{unit}{s^2}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return self.max_acceleration @max_deceleration.setter def max_deceleration(self, decel): - decel = float(assume_units(decel, self._units / (u.s**2)).rescale( + decel = float(assume_units(decel, self._units / (u.s**2)).to( self._units / (u.s**2)).magnitude) self.max_acceleration = decel @@ -655,9 +656,9 @@ def position(self): """ Gets real position on axis in units - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport unit - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("TP?", target=self.axis_id)), @@ -669,9 +670,9 @@ def desired_position(self): """ Gets desired position on axis in units - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport unit - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("DP?", target=self.axis_id)), @@ -683,9 +684,9 @@ def desired_velocity(self): """ Gets the axis desired velocity in unit/s - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport unit/s - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("DP?", target=self.axis_id)), @@ -698,9 +699,9 @@ def home(self): Gets/sets the axis home position. Default should be 0 as that sets current position as home - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport unit - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("DH?", target=self.axis_id)), @@ -711,7 +712,7 @@ def home(self): def home(self, newval=0): if newval is None: return - newval = float(assume_units(newval, self._units).rescale( + newval = float(assume_units(newval, self._units).to( self._units).magnitude) self._newport_cmd("DH", target=self.axis_id, params=[newval]) @@ -720,7 +721,7 @@ def units(self): """ Get the units that all commands are in reference to. - :type: `~quantities.Quantity` with units corresponding to + :type: `~pint.Quantity` with units corresponding to units of axis connected or int which corresponds to Newport unit number """ @@ -745,7 +746,7 @@ def encoder_resolution(self): per step. Encoder functionality must be enabled. :units: The number of units per encoder step - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( @@ -757,7 +758,7 @@ def encoder_resolution(self): def encoder_resolution(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units).rescale( + newval = float(assume_units(newval, self._units).to( self._units).magnitude) self._newport_cmd("SU", target=self.axis_id, params=[newval]) @@ -768,7 +769,7 @@ def full_step_resolution(self): units per step. Encoder functionality must be enabled. :units: The number of units per encoder step - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( @@ -783,7 +784,7 @@ def full_step_resolution(self, newval): newval = float(assume_units( newval, self._units - ).rescale(self._units).magnitude) + ).to(self._units).magnitude) self._newport_cmd("FR", target=self.axis_id, params=[newval]) @property @@ -792,7 +793,7 @@ def left_limit(self): Gets/sets the axis left travel limit :units: The limit in units - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("SL?", target=self.axis_id)), @@ -801,7 +802,7 @@ def left_limit(self): @left_limit.setter def left_limit(self, limit): - limit = float(assume_units(limit, self._units).rescale( + limit = float(assume_units(limit, self._units).to( self._units).magnitude) self._newport_cmd("SL", target=self.axis_id, params=[limit]) @@ -811,7 +812,7 @@ def right_limit(self): Gets/sets the axis right travel limit :units: units - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("SR?", target=self.axis_id)), @@ -820,7 +821,7 @@ def right_limit(self): @right_limit.setter def right_limit(self, limit): - limit = float(assume_units(limit, self._units).rescale( + limit = float(assume_units(limit, self._units).to( self._units).magnitude) self._newport_cmd("SR", target=self.axis_id, params=[limit]) @@ -830,7 +831,7 @@ def error_threshold(self): Gets/sets the axis error threshold :units: units - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("FE?", target=self.axis_id)), @@ -844,7 +845,7 @@ def error_threshold(self, newval): newval = float(assume_units( newval, self._units - ).rescale(self._units).magnitude) + ).to(self._units).magnitude) self._newport_cmd("FE", target=self.axis_id, params=[newval]) @property @@ -852,9 +853,9 @@ def current(self): """ Gets/sets the axis current (amps) - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\text{A}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("QI?", target=self.axis_id)), @@ -865,7 +866,7 @@ def current(self): def current(self, newval): if newval is None: return - current = float(assume_units(newval, u.A).rescale( + current = float(assume_units(newval, u.A).to( u.A).magnitude) self._newport_cmd("QI", target=self.axis_id, params=[current]) @@ -874,9 +875,9 @@ def voltage(self): """ Gets/sets the axis voltage - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of current newport :math:`\\text{V}` - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` """ return assume_units( float(self._newport_cmd("QV?", target=self.axis_id)), @@ -887,7 +888,7 @@ def voltage(self): def voltage(self, newval): if newval is None: return - voltage = float(assume_units(newval, u.V).rescale( + voltage = float(assume_units(newval, u.V).to( u.V).magnitude) self._newport_cmd("QV", target=self.axis_id, params=[voltage]) @@ -1101,7 +1102,7 @@ def search_for_home( def move(self, position, absolute=True, wait=False, block=False): """ :param position: Position to set move to along this axis. - :type position: `float` or :class:`~quantities.Quantity` + :type position: `float` or :class:`~pint.Quantity` :param bool absolute: If `True`, the position ``pos`` is interpreted as relative to the zero-point of the encoder. If `False`, ``pos`` is interpreted as relative to the current @@ -1110,7 +1111,7 @@ def move(self, position, absolute=True, wait=False, block=False): commands until movement is finished :param bool block: If True, will block code until movement is finished """ - position = float(assume_units(position, self._units).rescale( + position = float(assume_units(position, self._units).to( self._units).magnitude) if absolute: self._newport_cmd("PA", params=[position], target=self.axis_id) @@ -1164,9 +1165,9 @@ def wait_for_position(self, position): :param position: Position to wait for on axis - :type position: float or :class:`~quantities.Quantity` + :type position: float or :class:`~pint.Quantity` """ - position = float(assume_units(position, self._units).rescale( + position = float(assume_units(position, self._units).to( self._units).magnitude) self._newport_cmd( "WP", target=self.axis_id, params=[position]) @@ -1187,9 +1188,9 @@ def wait_for_motion(self, poll_interval=0.01, max_wait=None): # In programming mode, the "WS" command should be # sent instead, and the two parameters to this method should # be ignored. - poll_interval = float(assume_units(poll_interval, u.s).rescale( + poll_interval = float(assume_units(poll_interval, u.s).to( u.s).magnitude) - max_wait = float(assume_units(max_wait, u.s).rescale( + max_wait = float(assume_units(max_wait, u.s).to( u.s).magnitude) tic = time() while True: @@ -1277,11 +1278,11 @@ def setup_axis(self, **kwargs): 'configuration') if 'reduce_motor_torque_time' in kwargs and 'reduce_motor_torque_percentage' in kwargs: motor_time = kwargs['reduce_motor_torque_time'] - motor_time = int(assume_units(motor_time, u.ms).rescale(u.ms).magnitude) + motor_time = int(assume_units(motor_time, u.ms).to(u.ms).magnitude) if motor_time < 0 or motor_time > 60000: raise ValueError("Time must be between 0 and 60000 ms") percentage = kwargs['reduce_motor_torque_percentage'] - percentage = int(assume_units(percentage, u.percent).rescale( + percentage = int(assume_units(percentage, u.percent).to( u.percent).magnitude) if percentage < 0 or percentage > 100: raise ValueError("Time must be between 0 and 60000 ms") @@ -1325,7 +1326,7 @@ def read_setup(self): 'trajectory' 'hardware_limit_configuration' - :rtype: dict of `quantities.Quantity`, float and int + :rtype: dict of `pint.Quantity`, float and int """ config = dict() @@ -1393,9 +1394,9 @@ def _get_pq_unit(num): def _get_unit_num(self, quantity): """ Gets the integer label used by the Newport ESP 301 corresponding to a - given `~quantities.Quantity`. + given `~pint.Quantity`. - :param quantities.Quantity quantity: Units to return a label for. + :param pint.Quantity quantity: Units to return a label for. :return int: """ diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index 85853f0cf..0d976082d 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -11,7 +11,7 @@ from enum import IntEnum -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import Instrument from instruments.util_fns import convert_temperature, assume_units @@ -80,7 +80,7 @@ def target(self): :return: Current ACC of the Laser :units: mA - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = float(self._parent.query("rstli?")) return response*u.mA @@ -169,7 +169,7 @@ def target(self): :return: the target laser power :units: mW - :type: `~quantities.Quantities` + :type: `~pint.Quantity` """ response = self._parent.query("rslp?") return float(response)*u.mW @@ -258,16 +258,16 @@ def on_time(self): >>> laser.modulation.on_time = 1 * u.ms :return: The TTL modulation on time - :units: As specified (if a `~quantities.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units milliseconds. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self._parent.query("stsont?") return float(response)*u.ms @on_time.setter def on_time(self, newval): - newval = assume_units(newval, u.ms).rescale(u.ms).magnitude + newval = assume_units(newval, u.ms).to(u.ms).magnitude self._parent.sendcmd("stsont:"+str(newval)) @property @@ -286,16 +286,16 @@ def off_time(self): >>> laser.modulation.on_time = 1 * u.ms :return: The TTL modulation off time. - :units: As specified (if a `~quantities.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units milliseconds. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self._parent.query("stsofft?") return float(response)*u.ms @off_time.setter def off_time(self, newval): - newval = assume_units(newval, u.ms).rescale(u.ms).magnitude + newval = assume_units(newval, u.ms).to(u.ms).magnitude self._parent.sendcmd("stsofft:"+str(newval)) @property @@ -353,7 +353,7 @@ def current(self): >>> print(laser.tec.current) :units: mA - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self._parent.query("rti?") return float(response)*u.mA @@ -372,10 +372,10 @@ def target(self): >>> print(laser.tec.target) :units: Degrees Celcius - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self._parent.query("rstt?") - return float(response)*u.degC + return u.Quantity(float(response), u.degC) @property def enabled(self): @@ -427,16 +427,16 @@ def current(self): """ Gets/sets the laser diode current, in mA. - :units: As specified (if a `~quantities.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units mA. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self.query("rli?") return float(response)*u.mA @current.setter def current(self, newval): - newval = assume_units(newval, u.mA).rescale(u.mA).magnitude + newval = assume_units(newval, u.mA).to(u.mA).magnitude self.sendcmd("slc:"+str(newval)) @property @@ -445,16 +445,16 @@ def maximum_current(self): Get/Set the maximum laser diode current in mA. If the current is set over the limit, the laser will shut down. - :units: As specified (if a `~quantities.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units mA. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self.query("rlcm?") return float(response)*u.mA @maximum_current.setter def maximum_current(self, newval): - newval = assume_units(newval, u.mA).rescale('mA').magnitude + newval = assume_units(newval, u.mA).to('mA').magnitude self.sendcmd("smlc:" + str(newval)) @property @@ -462,16 +462,16 @@ def power(self): """ Get/Set the laser's optical power in mW. - :units: As specified (if a `~quantities.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units mW. - :rtype: `~quantities.Quantity` + :rtype: `~pint.Quantity` """ response = self.query("rlp?") return float(response)*u.mW @power.setter def power(self, newval): - newval = assume_units(newval, u.mW).rescale(u.mW).magnitude + newval = assume_units(newval, u.mW).to(u.mW).magnitude self.sendcmd("slp:"+str(newval)) @property @@ -499,12 +499,12 @@ def temperature(self): """ Gets/sets laser diode temperature. - :units: As specified (if a `~quantities.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units degrees celcius. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ response = self.query("rtt?") - return float(response)*u.degC + return u.Quantity(float(response), u.degC) @temperature.setter def temperature(self, newval): diff --git a/instruments/oxford/oxforditc503.py b/instruments/oxford/oxforditc503.py index 5772498fd..07fd06fa7 100644 --- a/instruments/oxford/oxforditc503.py +++ b/instruments/oxford/oxforditc503.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -54,10 +54,10 @@ def temperature(self): Read the temperature of the attached probe to the specified channel. :units: Kelvin - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ value = float(self._parent.query('R{}'.format(self._idx))[1:]) - return u.Quantity(value, u.Kelvin) + return u.Quantity(value, u.kelvin) # PROPERTIES # diff --git a/instruments/phasematrix/phasematrix_fsw0020.py b/instruments/phasematrix/phasematrix_fsw0020.py index 215d375f8..48df12f65 100644 --- a/instruments/phasematrix/phasematrix_fsw0020.py +++ b/instruments/phasematrix/phasematrix_fsw0020.py @@ -7,11 +7,9 @@ # IMPORTS ##################################################################### -from quantities import GHz - from instruments.abstract_instruments.signal_generator import SingleChannelSG +from instruments.units import ureg as u from instruments.util_fns import assume_units -from instruments.units import dBm, cBm, mHz # CLASSES ##################################################################### @@ -47,16 +45,16 @@ def frequency(self): If units are not specified, the frequency is assumed to be in gigahertz (GHz). - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: frequency, assumed to be GHz """ - return (int(self.query('04.'), 16) * mHz).rescale(GHz) + return (int(self.query('04.'), 16) * u.mHz).to(u.GHz) @frequency.setter def frequency(self, newval): # Rescale the input to millihertz as demanded by the signal # generator, then convert to an integer. - newval = int(assume_units(newval, GHz).rescale(mHz).magnitude) + newval = int(assume_units(newval, u.GHz).to(u.mHz).magnitude) # Write the integer to the serial port in ASCII-encoded # uppercase-hexadecimal format, with padding to 12 nybbles. @@ -71,10 +69,10 @@ def power(self): If units are not specified, the power is assumed to be in decibel-milliwatts (dBm). - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: log-power, assumed to be dBm """ - return (int(self.query('0D.'), 16) * cBm).rescale(dBm) + return u.Quantity((int(self.query('0D.'), 16)), u.cBm).to(u.dBm) @power.setter def power(self, newval): @@ -83,7 +81,7 @@ def power(self, newval): # The Phase Matrix unit speaks in units of centibel-milliwats, # so convert and take the integer part. - newval = int(assume_units(newval, dBm).rescale(cBm).magnitude) + newval = int(assume_units(newval, u.dBm).to(u.cBm).magnitude) # Command code 0x03, parameter length 2 bytes (4 nybbles) self.sendcmd('03{:04X}.'.format(newval)) diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index 4bc37eed9..4bfa34e72 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -9,7 +9,7 @@ from enum import IntEnum from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import (enum_property, bool_property, int_property, ProxyList) @@ -55,7 +55,7 @@ def resistance(self): reading is up to date by first sending the "ADC" command. :units: :math:`\\Omega` (ohms) - :rtype: `~quantities.Quantity` + :rtype: `~pint.Quantity` """ # First make sure the mux is on the correct channel if self._parent.mux_channel != self._idx: diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index ff7f83d9b..d4f065e7d 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -11,7 +11,7 @@ from enum import Enum from instruments.generic_scpi.scpi_instrument import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( ProxyList, assume_units, split_unit_str ) @@ -221,15 +221,15 @@ def window(self): """ Gets/sets the length of the coincidence window between the two signals. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units nanoseconds. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return u.Quantity(*split_unit_str(self.query("WIND?"), "ns")) @window.setter def window(self, new_val): - new_val_mag = int(assume_units(new_val, u.ns).rescale(u.ns).magnitude) + new_val_mag = int(assume_units(new_val, u.ns).to(u.ns).magnitude) if new_val_mag < 0 or new_val_mag > 7: raise ValueError("Window is out of range.") # window must be an integer! @@ -242,14 +242,14 @@ def delay(self): When setting, ``N`` may be ``0, 2, 4, 6, 8, 10, 12, or 14ns``. - :rtype: quantities.ns + :rtype: `~pint.Quantity` :return: the delay value """ return u.Quantity(*split_unit_str(self.query("DELA?"), "ns")) @delay.setter def delay(self, new_val): - new_val = assume_units(new_val, u.ns).rescale(u.ns) + new_val = assume_units(new_val, u.ns).to(u.ns) if new_val < 0*u.ns or new_val > 14*u.ns: raise ValueError("New delay value is out of bounds.") if new_val.magnitude % 2 != 0: @@ -262,9 +262,9 @@ def dwell_time(self): Gets/sets the length of time before a clear signal is sent to the counters. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units seconds. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ # the older versions of the firmware erroneously report the units of the # dwell time as being seconds rather than ms @@ -276,7 +276,7 @@ def dwell_time(self): @dwell_time.setter def dwell_time(self, new_val): - new_val_mag = assume_units(new_val, u.s).rescale(u.s).magnitude + new_val_mag = assume_units(new_val, u.s).to(u.s).magnitude if new_val_mag < 0: raise ValueError("Dwell time cannot be negative.") self.sendcmd(":DWEL {}".format(new_val_mag)) diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index 029f8f91c..c08e05b92 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -11,7 +11,7 @@ from enum import Enum from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( int_property, enum_property, unitful_property, assume_units ) @@ -50,13 +50,13 @@ def increment(self): Gets/sets the stepping increment value of the motor controller :units: As specified, or assumed to be of units milliseconds - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self._increment @increment.setter def increment(self, newval): - self._increment = assume_units(newval, u.ms).rescale(u.ms) + self._increment = assume_units(newval, u.ms).to(u.ms) @property def lower_limit(self): @@ -64,13 +64,13 @@ def lower_limit(self): Gets/sets the stepping lower limit value of the motor controller :units: As specified, or assumed to be of units milliseconds - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self._lower_limit @lower_limit.setter def lower_limit(self, newval): - self._lower_limit = assume_units(newval, u.ms).rescale(u.ms) + self._lower_limit = assume_units(newval, u.ms).to(u.ms) @property def upper_limit(self): @@ -78,13 +78,13 @@ def upper_limit(self): Gets/sets the stepping upper limit value of the motor controller :units: As specified, or assumed to be of units milliseconds - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return self._upper_limit @upper_limit.setter def upper_limit(self, newval): - self._upper_limit = assume_units(newval, u.ms).rescale(u.ms) + self._upper_limit = assume_units(newval, u.ms).to(u.ms) direction = unitful_property( command="DIRE", @@ -92,7 +92,7 @@ def upper_limit(self, newval): Get the internal direction variable, which is a function of how far the motor needs to go. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: milliseconds """, units=u.ms, @@ -105,7 +105,7 @@ def upper_limit(self, newval): Gets/Sets the amount of force required to overcome static inertia. Must be between 0 and 100 milliseconds. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: milliseconds """, format_code='{:.0f}', @@ -122,7 +122,7 @@ def internal_position(self): the positive direction minus the number of milliseconds that voltage has been applied to the motor in the negative direction. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: milliseconds """ response = int(self.query("POSI?"))*self.step_size @@ -133,7 +133,7 @@ def internal_position(self): doc=""" Get the estimated motor position, in millimeters. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: millimeters """, units=u.mm, @@ -159,7 +159,7 @@ def internal_position(self): Gets/Sets the number of milliseconds per step. Must be between 1 and 100 milliseconds. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: milliseconds """, format_code='{:.0f}', @@ -204,7 +204,7 @@ def move_timeout(self): Get the motor's timeout value, which indicates the number of milliseconds before the motor can start moving again. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: milliseconds """ response = int(self.query("TIME?")) @@ -240,12 +240,12 @@ def move(self, new_position): the number of motor steps. It varies between motors. :param new_position: the location - :type new_position: `~quantities.Quantity` + :type new_position: `~pint.Quantity` """ + new_position = assume_units(new_position, u.ms).to(u.ms) if self.lower_limit <= new_position <= self.upper_limit: - new_position = assume_units(new_position, u.ms).rescale(u.ms) clock_cycles = new_position/self.step_size - cmd = ":MOVE "+str(int(clock_cycles)) + cmd = f":MOVE {int(clock_cycles)}" self.sendcmd(cmd) else: raise ValueError("Location out of range") diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index 0be746929..406f044cc 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -9,7 +9,7 @@ from enum import IntEnum -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import FunctionGenerator from instruments.generic_scpi import SCPIInstrument @@ -82,7 +82,7 @@ class Function(IntEnum): Gets/sets the output frequency. :units: As specified, or assumed to be :math:`\\text{Hz}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -104,7 +104,7 @@ class Function(IntEnum): Gets/sets the offset voltage for the output waveform. :units: As specified, or assumed to be :math:`\\text{V}` otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) @@ -116,6 +116,6 @@ class Function(IntEnum): :units: As specified, or assumed to be degrees (:math:`{}^{\\circ}`) otherwise. - :type: `float` or `~quantities.quantity.Quantity` + :type: `float` or `~pint.Quantity` """ ) diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index 1f80a28c4..7fba17032 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -20,7 +20,7 @@ LoopbackCommunicator ) from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( bool_property, bounded_unitful_property, enum_property, unitful_property ) @@ -140,9 +140,9 @@ class Mode(Enum): doc=""" Gets/sets the lock-in amplifier reference frequency. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Hertz. - :type: `~quantities.Quantity` with units Hertz. + :type: `~pint.Quantity` with units Hertz. """ ) @@ -155,9 +155,9 @@ class Mode(Enum): Set value should be -360deg <= newval < +730deg. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units degrees. - :type: `~quantities.Quantity` with units degrees. + :type: `~pint.Quantity` with units degrees. """ ) @@ -170,9 +170,9 @@ class Mode(Enum): Set value should be 0.004 <= newval <= 5.000 - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units volts. Value should be specified as peak-to-peak. - :type: `~quantities.Quantity` with units volts peak-to-peak. + :type: `~pint.Quantity` with units volts peak-to-peak. """ ) @@ -206,7 +206,7 @@ def sample_rate(self): Acceptable set values are :math:`2^n` where :math:`n \in \{-4...+9\}` or the string `trigger`. - :type: `~quantities.Quantity` with units Hertz. + :type: `~pint.Quantity` with units Hertz. """ value = int(self.query('SRAT?')) if value == 14: @@ -317,7 +317,7 @@ def init(self, sample_rate, buffer_mode): :param sample_rate: The desired sampling rate. Acceptable set values are :math:`2^n` where :math:`n \in \{-4...+9\}` in units Hertz or the string `trigger`. - :type sample_rate: `~quantities.Quantity` or `str` + :type sample_rate: `~pint.Quantity` or `str` :param `SRS830.BufferMode` buffer_mode: This sets the behaviour of the instrument when the data storage buffer is full. Setting to diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index a3396ef06..5dd47b068 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -12,7 +12,7 @@ import numpy as np from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -116,7 +116,7 @@ def value(self): kind of sensor and/or channel you have specified. Units can be one of ``celsius``, ``watt``, ``volt``, ``ohm``, or ``dimensionless``. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ # WARNING: Queries all units all the time. # TODO: Make an OutputChannel that subclasses this class, @@ -134,7 +134,7 @@ def units(self): Units can be one of ``celsius``, ``watt``, ``volt``, ``ohm``, or ``dimensionless``. - :type: `~quantities.UnitQuantity` + :type: `~pint.Unit` """ # FIXME: does not respect "chan.d/dt" property. return self._ctc.channel_units()[self._chan_name] @@ -190,7 +190,7 @@ def average(self): Gets the average measurement for the specified channel as determined by the statistics gathering. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return u.Quantity( float(self._get('average')), @@ -203,7 +203,7 @@ def std_dev(self): Gets the standard deviation for the specified channel as determined by the statistics gathering. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ return u.Quantity( float(self._get('SD')), @@ -222,9 +222,9 @@ def get_log_point(self, which='next', units=None): :param units: Units to attach to the returned data point. If left with the value of `None` then the instrument will be queried for the current units setting. - :type units: `~quantities.UnitQuantity` + :type units: `~pint.Unit` :return: The log data point with units - :rtype: `~quantities.Quantity` + :rtype: `~pint.Quantity` """ if units is None: units = self.units @@ -245,7 +245,7 @@ def get_log(self): :return: Tuple of all the log data points. First value is time, second is the measurement value. - :rtype: Tuple of 2x `~quantities.Quantity`, each comprised of + :rtype: Tuple of 2x `~pint.Quantity`, each comprised of a numpy array (`numpy.dnarray`). """ # Remember the current units. diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index 56c38feb9..c8e938874 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -10,7 +10,7 @@ from instruments.abstract_instruments.comm import GPIBCommunicator from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units, ProxyList # CLASSES ##################################################################### @@ -68,7 +68,7 @@ def delay(self, newval): self._ddg.sendcmd("DLAY {},{},{}".format( int(self._chan), int(newval[0].idx), - newval[1].rescale("s").magnitude + newval[1].to("s").magnitude )) @@ -205,7 +205,7 @@ def level_amplitude(self): """ Amplitude (in voltage) of the output level for this output. - :type: `float` or :class:`~quantities.Quantity` + :type: `float` or :class:`~pint.Quantity` :units: As specified, or :math:`\\text{V}` by default. """ return u.Quantity( @@ -223,7 +223,7 @@ def level_offset(self): """ Amplitude offset (in voltage) of the output level for this output. - :type: `float` or :class:`~quantities.Quantity` + :type: `float` or :class:`~pint.Quantity` :units: As specified, or :math:`\\text{V}` by default. """ return u.Quantity( @@ -299,7 +299,7 @@ def trigger_rate(self): """ Gets/sets the rate of the internal trigger. - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` :units: As passed or Hz if not specified. """ return u.Quantity(float(self.query("TRAT?")), u.Hz) @@ -307,7 +307,7 @@ def trigger_rate(self): @trigger_rate.setter def trigger_rate(self, newval): newval = assume_units(newval, u.Hz) - self.sendcmd("TRAT {}".format(newval.rescale(u.Hz).magnitude)) + self.sendcmd("TRAT {}".format(newval.to(u.Hz).magnitude)) @property def trigger_source(self): @@ -327,7 +327,7 @@ def holdoff(self): """ Gets/sets the trigger holdoff time. - :type: `~quantities.Quantity` or `float` + :type: `~pint.Quantity` or `float` :units: As passed, or s if not specified. """ return u.Quantity(float(self.query("HOLD?")), u.s) @@ -335,7 +335,7 @@ def holdoff(self): @holdoff.setter def holdoff(self, newval): newval = assume_units(newval, u.s) - self.sendcmd("HOLD {}".format(newval.rescale(u.s).magnitude)) + self.sendcmd("HOLD {}".format(newval.to(u.s).magnitude)) @property def enable_burst_mode(self): @@ -392,8 +392,8 @@ def burst_period(self): @burst_period.setter def burst_period(self, newval): - newval = assume_units(newval, u.s) - self.sendcmd("BURP {}".format(newval.rescale(u.s).magnitude)) + newval = assume_units(newval, u.sec) + self.sendcmd("BURP {}".format(newval.to(u.sec).magnitude)) @property def burst_delay(self): @@ -410,4 +410,4 @@ def burst_delay(self): @burst_delay.setter def burst_delay(self, newval): newval = assume_units(newval, u.s) - self.sendcmd("BURD {}".format(newval.rescale(u.s).magnitude)) + self.sendcmd("BURD {}".format(newval.to(u.sec).magnitude)) diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index a9f8b9cbd..9a541d32a 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -11,7 +11,7 @@ import numpy as np from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units, ProxyList # CLASSES ##################################################################### @@ -60,9 +60,9 @@ def amplitude(self): """ Gets/sets the amplitude of the specified channel. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :type: `~quantities.Quantity` with units Volts peak-to-peak. + :type: `~pint.Quantity` with units Volts peak-to-peak. """ return u.Quantity( float(self._tek.query("FG:{}:AMPL?".format(self._name)).strip()), @@ -73,7 +73,7 @@ def amplitude(self): def amplitude(self, newval): self._tek.sendcmd("FG:{}:AMPL {}".format( self._name, - assume_units(newval, u.V).rescale(u.V).magnitude + assume_units(newval, u.V).to(u.V).magnitude )) @property @@ -81,9 +81,9 @@ def offset(self): """ Gets/sets the offset of the specified channel. - :units: As specified (if a `~quantities.Quantity`) or assumed to be + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :type: `~quantities.Quantity` with units Volts. + :type: `~pint.Quantity` with units Volts. """ return u.Quantity( float(self._tek.query("FG:{}:OFFS?".format(self._name)).strip()), @@ -94,7 +94,7 @@ def offset(self): def offset(self, newval): self._tek.sendcmd("FG:{}:OFFS {}".format( self._name, - assume_units(newval, u.V).rescale(u.V).magnitude + assume_units(newval, u.V).to(u.V).magnitude )) @property @@ -103,9 +103,9 @@ def frequency(self): Gets/sets the frequency of the specified channel when using the built-in function generator. - ::units: As specified (if a `~quantities.Quantity`) or assumed to be + ::units: As specified (if a `~pint.Quantity`) or assumed to be of units Hertz. - :type: `~quantities.Quantity` with units Hertz. + :type: `~pint.Quantity` with units Hertz. """ return u.Quantity( float(self._tek.query("FG:FREQ?").strip()), @@ -115,7 +115,7 @@ def frequency(self): @frequency.setter def frequency(self, newval): self._tek.sendcmd("FG:FREQ {}HZ".format( - assume_units(newval, u.Hz).rescale(u.Hz).magnitude + assume_units(newval, u.Hz).to(u.Hz).magnitude )) @property diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index a832bd361..2340266ff 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -14,7 +14,7 @@ Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource ) from instruments.generic_scpi import SCPIInstrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( enum_property, string_property, int_property, unitful_property, unitless_property, bool_property, ProxyList diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index db5b5e3af..560c100dc 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -19,7 +19,7 @@ ) from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList -import instruments.units as u +from instruments.units import ureg as u # CLASSES ##################################################################### diff --git a/instruments/teledyne/maui.py b/instruments/teledyne/maui.py index 4a8517189..6087a823a 100644 --- a/instruments/teledyne/maui.py +++ b/instruments/teledyne/maui.py @@ -21,7 +21,7 @@ from instruments.abstract_instruments import ( Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource ) -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( assume_units, enum_property, bool_property, ProxyList ) @@ -370,7 +370,7 @@ def offset(self): @offset.setter def offset(self, newval): - newval = assume_units(newval, 'V').rescale(u.V).magnitude + newval = assume_units(newval, 'V').to(u.V).magnitude self.sendcmd('OFST {}'.format(newval)) @property @@ -391,7 +391,7 @@ def scale(self): @scale.setter def scale(self, newval): - newval = assume_units(newval, 'V').rescale(u.V).magnitude + newval = assume_units(newval, 'V').to(u.V).magnitude self.sendcmd('VDIV {}'.format(newval)) # METHODS # @@ -527,11 +527,11 @@ def derivative(self, src, vscale=1e6, voffset=0, """ src_str = _source(src) - vscale = assume_units(vscale, u.V/u.s).rescale( + vscale = assume_units(vscale, u.V/u.s).to( u.V/u.s ).magnitude - voffset = assume_units(voffset, u.V/u.s).rescale( + voffset = assume_units(voffset, u.V/u.s).to( u.V/u.s ).magnitude @@ -673,11 +673,11 @@ def integral(self, src, multiplier=1, adder=0, vscale=1e-3, """ src_str = _source(src) - vscale = assume_units(vscale, u.Wb).rescale( + vscale = assume_units(vscale, u.Wb).to( u.Wb ).magnitude - voffset = assume_units(voffset, u.Wb).rescale( + voffset = assume_units(voffset, u.Wb).to( u.Wb ).magnitude @@ -754,7 +754,7 @@ def rescale(self, src, multiplier=1, adder=0): """ src_str = _source(src) - adder = assume_units(adder, u.V).rescale( + adder = assume_units(adder, u.V).to( u.V ).magnitude @@ -819,11 +819,11 @@ def trend(self, src, vscale=1, center=0, autoscale=True): """ src_str = _source(src) - vscale = assume_units(vscale, u.V).rescale( + vscale = assume_units(vscale, u.V).to( u.V ).magnitude - center = assume_units(center, u.V).rescale( + center = assume_units(center, u.V).to( u.V ).magnitude @@ -1237,7 +1237,7 @@ def time_div(self): @time_div.setter def time_div(self, newval): - newval = assume_units(newval, 's').rescale(u.s).magnitude + newval = assume_units(newval, 's').to(u.s).magnitude self.sendcmd('TDIV {}'.format(newval)) # TRIGGER PROPERTIES @@ -1276,7 +1276,7 @@ def trigger_delay(self): @trigger_delay.setter def trigger_delay(self, newval): - newval = assume_units(newval, 's').rescale(u.s).magnitude + newval = assume_units(newval, 's').to(u.s).magnitude self.sendcmd('TRDL {}'.format(newval)) @property diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index 5cbc3c6be..ea5380993 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -14,6 +14,8 @@ from io import BytesIO from unittest import mock +import pytest + # FUNCTIONS ################################################################## @@ -96,16 +98,13 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n", repeat=1): # """Only read {} bytes out of {}""".format(current, end) -def unit_eq(a, b, msg=None, thresh=1e-5): +def unit_eq(a, b): """ Asserts that two unitful quantites ``a`` and ``b`` are equal up to a small numerical threshold. """ - assert abs((a - b).magnitude) <= thresh, "{} - {} = {}.{}".format( - a, b, a - b, - "\n" + msg if msg is not None else "" - ) - assert a.units == b.units, "{} and {} have different units".format(a, b) + assert a.magnitude == pytest.approx(b.magnitude) + assert a.units == b.units, f"{a} and {b} have different units" def make_name_test(ins_class, name_cmd="*IDN?"): diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index 16aa42029..edf9a37a5 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index 0c7e05078..e9de49e9c 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -7,7 +7,7 @@ # IMPORTS #################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, make_name_test @@ -155,14 +155,14 @@ def test_agilent33220a_load_resistance(): [ "OUTP:LOAD?", "OUTP:LOAD?", - "OUTP:LOAD 100.0", + "OUTP:LOAD 100", "OUTP:LOAD MAX" ], [ "50", "INF" ] ) as fg: - assert fg.load_resistance == 50 * u.Ohm + assert fg.load_resistance == 50 * u.ohm assert fg.load_resistance == fg.LoadResistance.high_impedance - fg.load_resistance = 100 * u.Ohm + fg.load_resistance = 100 * u.ohm fg.load_resistance = fg.LoadResistance.maximum diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 9da9cd5c8..40af2567b 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -11,7 +11,7 @@ import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq -import instruments.units as u +from instruments.units import ureg as u # TESTS ###################################################################### diff --git a/instruments/tests/test_comm/test_gpibusb.py b/instruments/tests/test_comm/test_gpibusb.py index 5731eee40..d5b6455d2 100644 --- a/instruments/tests/test_comm/test_gpibusb.py +++ b/instruments/tests/test_comm/test_gpibusb.py @@ -9,7 +9,7 @@ import pytest import serial -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.comm import GPIBCommunicator, SerialCommunicator from instruments.tests import unit_eq diff --git a/instruments/tests/test_comm/test_serial.py b/instruments/tests/test_comm/test_serial.py index 1f1502e0c..a87523cbf 100644 --- a/instruments/tests/test_comm/test_serial.py +++ b/instruments/tests/test_comm/test_serial.py @@ -9,7 +9,7 @@ import pytest import serial -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.comm import SerialCommunicator from instruments.tests import unit_eq diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index 67328425f..81c1861cd 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -10,7 +10,7 @@ import socket import pytest -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments.comm import SocketCommunicator from instruments.tests import unit_eq diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index b17747a97..2729faa7d 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -13,7 +13,7 @@ from instruments.abstract_instruments.comm import USBTMCCommunicator from instruments.tests import unit_eq -import instruments.units as u +from instruments.units import ureg as u from .. import mock # TEST CASES ################################################################# diff --git a/instruments/tests/test_config.py b/instruments/tests/test_config.py index e3ba05110..b1a9c7f29 100644 --- a/instruments/tests/test_config.py +++ b/instruments/tests/test_config.py @@ -9,7 +9,7 @@ from io import StringIO -import instruments.units as u +from instruments.units import ureg as u from instruments import Instrument from instruments.config import ( diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index 052f537f0..0910df493 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol @@ -306,7 +306,7 @@ def test_measure(): "\r" ) as inst: assert inst.measure(inst.Mode.voltage_dc) == 0.509 * u.volt - assert inst.measure(inst.Mode.temperature) == -25.3 * u.celsius + assert inst.measure(inst.Mode.temperature) == u.Quantity(-25.3, u.degC) def test_measure_invalid_mode(): diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index e0feb8919..bde3c557f 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -7,7 +7,7 @@ # IMPORTS #################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, make_name_test diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index a38faff14..bf9eab0ad 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -7,7 +7,7 @@ # IMPORTS #################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq @@ -54,7 +54,7 @@ def test_scpi_multimeter_input_range(): "CONF?", # 3.1 "CONF:FRES MIN", # 3.2 "CONF?", # 4.1 - "CONF:CURR:DC 1.0" # 4.2 + "CONF:CURR:DC 1" # 4.2 ], [ "CURR:AC +1.000000E+01,+3.000000E-06", # 1 "CURR:AC AUTO,+3.000000E-06", # 2 diff --git a/instruments/tests/test_gentec_eo/test_blu.py b/instruments/tests/test_gentec_eo/test_blu.py index 2a8908a15..f8c777f19 100644 --- a/instruments/tests/test_gentec_eo/test_blu.py +++ b/instruments/tests/test_gentec_eo/test_blu.py @@ -11,7 +11,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ###################################################################### @@ -395,7 +395,7 @@ def test_blu_user_offset_joules(): ], [ - "Mode: 2", # power mode watts + "Mode: 2", # power mode joules "User Offset : 1.500e-3", "ACK" ], diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index 222a609d2..470718e83 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -8,7 +8,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ###################################################################### diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index c29a78ae1..870184c75 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -7,11 +7,10 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol -from instruments.units import dBm from .. import mock # TEST CLASSES ################################################################ @@ -118,7 +117,7 @@ def test_channel_temperature(): sep="\n" ) as hs: channel = hs.channel[0] - assert channel.temperature == 10 * u.degC + assert channel.temperature == u.Quantity(10, u.degC) def test_channel_frequency_getter(): @@ -182,9 +181,9 @@ def test_channel_power_getter(): sep="\n" ) as hs: channel = hs.channel[0] - assert channel.power == 0 * dBm - assert channel.power_min == -100 * dBm - assert channel.power_max == 20 * dBm + assert channel.power == u.Quantity(0, u.dBm) + assert channel.power_min == u.Quantity(-100, u.dBm) + assert channel.power_max == u.Quantity(20, u.dBm) def test_channel_power_setter(): @@ -204,7 +203,7 @@ def test_channel_power_setter(): sep="\n" ) as hs: channel = hs.channel[0] - channel.power = 0 * dBm + channel.power = u.Quantity(0, u.dBm) def test_channel_phase_getter(): diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index 0578b9749..c1bd86f2c 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -7,12 +7,11 @@ # IMPORTS ##################################################################### -import numpy as np import pytest import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ####################################################################### @@ -180,15 +179,9 @@ def test_hp3456a_fetch(): sep="\r" ) as dmm: v = dmm.fetch(dmm.Mode.resistance_2wire) - np.testing.assert_array_equal( - v, [0.1055, 0.1043, 0.1005, 0.1014] * u.ohm - ) - assert v[0].units == u.ohm + assert v == [0.1055 * u.ohm, 0.1043 * u.ohm, 0.1005 * u.ohm, 0.1014 * u.ohm] v = dmm.fetch() - np.testing.assert_array_equal( - v, [0.1055, 0.1043, 0.1005, 0.1014] * u.ohm - ) - assert isinstance(v[0], float) + assert v == [0.1055, 0.1043, 0.1005, 0.1014] def test_hp3456a_variance(): @@ -239,7 +232,7 @@ def test_hp3456a_delay(): [ "HO0T4SO1", "RED", - "W1.0STD" + "W1STD" ], [ "-000.0000E+0" ], diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 5e2372cb9..74ba00d0e 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -11,7 +11,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u from .. import mock # TESTS ####################################################################### diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index e2986a488..6f24c4cee 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, make_name_test, unit_eq diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index 64060aabb..d6e1e73af 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol diff --git a/instruments/tests/test_keithley/test_keithley195.py b/instruments/tests/test_keithley/test_keithley195.py index 395dd7454..927f1d8f8 100644 --- a/instruments/tests/test_keithley/test_keithley195.py +++ b/instruments/tests/test_keithley/test_keithley195.py @@ -16,8 +16,8 @@ import pytest import instruments as ik -import instruments.units as u from instruments.tests import expected_protocol +from instruments.units import ureg as u # TESTS ###################################################################### diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 3fbdd88a6..5894f9ad8 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -12,7 +12,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ####################################################################### @@ -69,7 +69,7 @@ def test_channel_measure_temperature(): ] ) as inst: channel = inst.channel[0] - assert channel.measure() == 1.234 * u.celsius + assert channel.measure() == u.Quantity(1.234, u.degC) def test_channel_measure_unknown_temperature_units(): @@ -118,15 +118,9 @@ def test_units(): "VOLT" ] ) as inst: - units = str(inst.units.units).split()[1] - assert units == "degC" - - units = str(inst.units.units).split()[1] - assert units == "degF" - - units = str(inst.units.units).split()[1] - assert units == "K" - + assert inst.units == u.degC + assert inst.units == u.degF + assert inst.units == u.kelvin assert inst.units == u.volt diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index eea8636b0..7228c26e8 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -7,7 +7,7 @@ # IMPORTS #################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol @@ -148,5 +148,5 @@ def test_measure(): "NDCL-9.0000E+0" ] ) as inst: - assert inst.measure() == 1.2345 * u.nanoamp - assert inst.measure() == 1. * u.nanoamp + assert 1.2345 * u.nanoamp == inst.measure() + assert 1 * u.nanoamp == inst.measure() diff --git a/instruments/tests/test_keithley/test_keithley580.py b/instruments/tests/test_keithley/test_keithley580.py index e8bda7c54..ed5d0c4c8 100644 --- a/instruments/tests/test_keithley/test_keithley580.py +++ b/instruments/tests/test_keithley/test_keithley580.py @@ -17,8 +17,8 @@ import pytest import instruments as ik -import instruments.units as u from instruments.tests import expected_protocol +from instruments.units import ureg as u # TESTS ###################################################################### @@ -761,7 +761,7 @@ def test_measure(init, create_measurement, resistance): sep="\n" ) as inst: read_value = inst.measure() - assert read_value.magnitude == pytest.approx(resistance, rel=1e-6) + assert read_value.magnitude == pytest.approx(resistance, rel=1e-5) assert read_value.units == u.ohm diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index 1bf486e90..a2e5cc70d 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index fc093df2d..ca997c991 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -11,7 +11,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ####################################################################### diff --git a/instruments/tests/test_lakeshore/test_lakeshore340.py b/instruments/tests/test_lakeshore/test_lakeshore340.py index 183e6010c..6c20143f4 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore340.py +++ b/instruments/tests/test_lakeshore/test_lakeshore340.py @@ -7,7 +7,7 @@ # IMPORTS #################################################################### import instruments as ik -import instruments.units as u +from instruments.units import ureg as u from instruments.tests import expected_protocol # TESTS ###################################################################### diff --git a/instruments/tests/test_lakeshore/test_lakeshore370.py b/instruments/tests/test_lakeshore/test_lakeshore370.py index 3a0b521df..aaa2a4d99 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore370.py +++ b/instruments/tests/test_lakeshore/test_lakeshore370.py @@ -9,7 +9,7 @@ import pytest import instruments as ik -import instruments.units as u +from instruments.units import ureg as u from instruments.tests import expected_protocol # TESTS ###################################################################### diff --git a/instruments/tests/test_lakeshore/test_lakeshore475.py b/instruments/tests/test_lakeshore/test_lakeshore475.py index 178440169..c02039fb7 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore475.py +++ b/instruments/tests/test_lakeshore/test_lakeshore475.py @@ -9,7 +9,7 @@ import pytest import instruments as ik -import instruments.units as u +from instruments.units import ureg as u from instruments.tests import expected_protocol # TESTS ###################################################################### @@ -150,22 +150,42 @@ def test_lakeshore475_field_setpoint(): "CSETP?", "UNIT?", "UNIT?", - "CSETP 10000.0", # send 1 tesla + "CSETP 1.0", # send 1 tesla "UNIT?", "CSETP 23.0" # send 23 unitless (equals gauss) ], [ "10.", "1", - "1", + "2", "1" ], ) as lsh: assert lsh.field_setpoint == u.Quantity(10, u.gauss) + lsh.field_setpoint = u.Quantity(1., u.tesla) lsh.field_setpoint = 23. +def test_lakeshore475_field_setpoint_wrong_units(): + """ + Setting the field setpoint with the wrong units + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "UNIT?", + ], + [ + "1" + ], + ) as lsh: + with pytest.raises(ValueError) as exc_info: + lsh.field_setpoint = u.Quantity(1., u.tesla) + exc_msg = exc_info.value.args[0] + assert "Field setpoint must be specified in the same units" in exc_msg + + def test_lakeshore475_field_get_control_params(): """ Get field control parameters. @@ -200,12 +220,9 @@ def test_lakeshore475_field_set_control_params(): "UNIT?", "CPARAM 5.0,50.0,120.0,60.0", "UNIT?", - "CPARAM 5.0,50.0,120.0,180.0", - "UNIT?", "CPARAM 5.0,50.0,120.0,60.0" ], [ - "2", # teslas "2", # teslas "2" # teslas ], @@ -217,13 +234,6 @@ def test_lakeshore475_field_set_control_params(): u.Quantity(120.0, u.tesla / u.min), u.Quantity(60.0, u.volt / u.min) ) - # different units are used - lsh.field_control_params = ( - 5.0, - 50.0, - u.Quantity(20000.0, u.gauss / u.s), - u.Quantity(3000.0, u.mV / u.s) - ) # no units are used lsh.field_control_params = ( 5.0, @@ -239,10 +249,8 @@ def test_lakeshore475_field_set_control_params_not_a_tuple(): """ with expected_protocol( ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + [], + [], ) as lsh: with pytest.raises(TypeError) as exc_info: lsh.field_control_params = 42 @@ -251,6 +259,30 @@ def test_lakeshore475_field_set_control_params_not_a_tuple(): " a tuple" +def test_lakeshore475_field_set_control_params_wrong_units(): + """ + Set field control parameters with the wrong units + """ + with expected_protocol( + ik.lakeshore.Lakeshore475, + [ + "UNIT?", + ], + [ + "1", # gauss + ], + ) as lsh: + with pytest.raises(ValueError) as exc_info: + lsh.field_control_params = ( + 5.0, + 50.0, + u.Quantity(120.0, u.tesla / u.min), + u.Quantity(60.0, u.volt / u.min) + ) + exc_msg = exc_info.value.args[0] + assert "Field control params ramp rate must be specified in the same units" in exc_msg + + def test_lakeshore475_p_value(): """ Get / set p-value. diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index 28996d8f0..6c289e656 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol @@ -48,7 +48,7 @@ def test_mhs_amplitude_dbm_notimplemented(): sep="\r\n" ) as mhs: with pytest.raises(NotImplementedError): - mhs.channel[0].amplitude = 6.6*ik.units.dBm + mhs.channel[0].amplitude = u.Quantity(6.6, u.dBm) def test_mhs_duty_cycle(): @@ -164,8 +164,8 @@ def test_mhs_phase(): ], sep="\r\n" ) as mhs: - assert mhs.channel[0].phase == 120 - assert mhs.channel[1].phase == 0 + assert mhs.channel[0].phase == 120 * u.degree + assert mhs.channel[1].phase == 0 * u.degree mhs.channel[0].phase = 60 mhs.channel[1].phase = 180 diff --git a/instruments/tests/test_ondax/test_lm.py b/instruments/tests/test_ondax/test_lm.py index 74c3b8ab7..6e3edef90 100644 --- a/instruments/tests/test_ondax/test_lm.py +++ b/instruments/tests/test_ondax/test_lm.py @@ -9,10 +9,9 @@ import pytest -import quantities - from instruments import ondax from instruments.tests import expected_protocol +from instruments.units import ureg as u # TESTS ####################################################################### @@ -28,7 +27,7 @@ def test_acc_target(): ], sep="\r" ) as lm: - assert lm.acc.target == 100 * quantities.mA + assert lm.acc.target == 100 * u.mA def test_acc_enable(): @@ -111,7 +110,7 @@ def test_apc_target(): ], sep="\r" ) as lm: - assert lm.apc.target == 100 * quantities.mW + assert lm.apc.target == 100 * u.mW def test_apc_enable(): @@ -188,7 +187,7 @@ def test_modulation_on_time(): ondax.LM, [ "stsont?", - "stsont:20.0" + "stsont:20" ], [ "10", @@ -196,8 +195,8 @@ def test_modulation_on_time(): ], sep="\r" ) as lm: - assert lm.modulation.on_time == 10 * quantities.ms - lm.modulation.on_time = 20 * quantities.ms + assert lm.modulation.on_time == 10 * u.ms + lm.modulation.on_time = 20 * u.ms def test_modulation_off_time(): @@ -205,7 +204,7 @@ def test_modulation_off_time(): ondax.LM, [ "stsofft?", - "stsofft:20.0" + "stsofft:20" ], [ "10", @@ -213,8 +212,8 @@ def test_modulation_off_time(): ], sep="\r" ) as lm: - assert lm.modulation.off_time == 10 * quantities.ms - lm.modulation.off_time = 20 * quantities.ms + assert lm.modulation.off_time == 10 * u.ms + lm.modulation.off_time = 20 * u.ms def test_modulation_enabled(): @@ -269,7 +268,7 @@ def test_tec_current(): ], sep="\r" ) as lm: - assert lm.tec.current == 100 * quantities.mA + assert lm.tec.current == 100 * u.mA def test_tec_target(): @@ -283,7 +282,7 @@ def test_tec_target(): ], sep="\r" ) as lm: - assert lm.tec.target == 22 * quantities.degC + assert lm.tec.target == u.Quantity(22, u.degC) def test_tec_enable(): @@ -346,7 +345,7 @@ def test_current(): ondax.LM, [ "rli?", - "slc:100.0" + "slc:100" ], [ "120", @@ -354,8 +353,8 @@ def test_current(): ], sep="\r" ) as lm: - assert lm.current == 120 * quantities.mA - lm.current = 100 * quantities.mA + assert lm.current == 120 * u.mA + lm.current = 100 * u.mA def test_maximum_current(): @@ -363,7 +362,7 @@ def test_maximum_current(): ondax.LM, [ "rlcm?", - "smlc:100.0" + "smlc:100" ], [ "120", @@ -371,8 +370,8 @@ def test_maximum_current(): ], sep="\r" ) as lm: - assert lm.maximum_current == 120 * quantities.mA - lm.maximum_current = 100 * quantities.mA + assert lm.maximum_current == 120 * u.mA + lm.maximum_current = 100 * u.mA def test_power(): @@ -380,7 +379,7 @@ def test_power(): ondax.LM, [ "rlp?", - "slp:100.0" + "slp:100" ], [ "120", @@ -388,8 +387,8 @@ def test_power(): ], sep="\r" ) as lm: - assert lm.power == 120 * quantities.mW - lm.power = 100 * quantities.mW + assert lm.power == 120 * u.mW + lm.power = 100 * u.mW def test_serial_number(): @@ -425,7 +424,7 @@ def test_temperature(): ondax.LM, [ "rtt?", - "stt:40.0" + "stt:40" ], [ "35", @@ -433,8 +432,8 @@ def test_temperature(): ], sep="\r" ) as lm: - assert lm.temperature == 35 * quantities.degC - lm.temperature = 40 * quantities.degC + assert lm.temperature == u.Quantity(35, u.degC) + lm.temperature = u.Quantity(40, u.degC) def test_enable(): diff --git a/instruments/tests/test_oxford/test_oxforditc503.py b/instruments/tests/test_oxford/test_oxforditc503.py index ca18f0bea..0986152cb 100644 --- a/instruments/tests/test_oxford/test_oxforditc503.py +++ b/instruments/tests/test_oxford/test_oxforditc503.py @@ -7,10 +7,9 @@ # IMPORTS ##################################################################### -import instruments.units as u - import instruments as ik from instruments.tests import expected_protocol +from instruments.units import ureg as u # TESTS ####################################################################### @@ -41,4 +40,4 @@ def test_sensor_temperature(): sep="\r" ) as inst: sensor = inst.sensor[0] - assert sensor.temperature == 123 * u.kelvin + assert sensor.temperature == u.Quantity(123, u.kelvin) diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index ec400ecc8..6c5487a6c 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -7,11 +7,10 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol -from instruments.units import mHz, dBm, cBm # TESTS ####################################################################### @@ -32,13 +31,13 @@ def test_frequency(): ik.phasematrix.PhaseMatrixFSW0020, [ "04.", - "0C{:012X}.".format(int((10 * u.GHz).rescale(mHz).magnitude)) + "0C{:012X}.".format(int((10 * u.GHz).to(u.mHz).magnitude)) ], [ "00E8D4A51000" ] ) as inst: - assert inst.frequency == 1 * u.GHz + assert inst.frequency == 1.0000000000000002 * u.GHz inst.frequency = 10 * u.GHz @@ -47,14 +46,14 @@ def test_power(): ik.phasematrix.PhaseMatrixFSW0020, [ "0D.", - "03{:04X}.".format(int((10 * dBm).rescale(cBm).magnitude)) + "03{:04X}.".format(int(u.Quantity(10, u.dBm).to(u.cBm).magnitude)) ], [ "-064" ] ) as inst: - assert inst.power == -10 * dBm - inst.power = 10 * dBm + assert inst.power == u.Quantity(-10, u.dBm) + inst.power = u.Quantity(10, u.dBm) def test_blanking(): diff --git a/instruments/tests/test_picowatt/test_picowatt_avs47.py b/instruments/tests/test_picowatt/test_picowatt_avs47.py index 1b4d97885..c67bca372 100644 --- a/instruments/tests/test_picowatt/test_picowatt_avs47.py +++ b/instruments/tests/test_picowatt/test_picowatt_avs47.py @@ -7,7 +7,7 @@ # IMPORTS ##################################################################### -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index 9d3b0b4ce..d082041c0 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import bounded_unitful_property from . import MockInstrument diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index de61b1435..aedc31a79 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -8,9 +8,10 @@ import pytest -import instruments.units as u +import pint from instruments.util_fns import unitful_property +from instruments.units import ureg as u from . import MockInstrument # TEST CASES ################################################################# @@ -62,7 +63,7 @@ class UnitfulMock(MockInstrument): def test_unitful_property_wrong_units(): - with pytest.raises(ValueError): + with pytest.raises(pint.errors.DimensionalityError): class UnitfulMock(MockInstrument): unitful_property = unitful_property('MOCK', u.hertz) diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index ba932b6b8..bbf7f67d7 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import unitless_property from . import MockInstrument diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index 795cbb95b..3503fd850 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, unit_eq diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index 6fd22358f..7eb19b53a 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -9,7 +9,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol @@ -17,6 +17,36 @@ # TESTS ###################################################################### +def test_mc1_increment(): + with expected_protocol( + ik.qubitekk.MC1, + [], [], sep="\r" + ) as mc: + assert mc.increment == 1 * u.ms + mc.increment = 3 * u.ms + assert mc.increment == 3 * u.ms + + +def test_mc1_lower_limit(): + with expected_protocol( + ik.qubitekk.MC1, + [], [], sep="\r" + ) as mc: + assert mc.lower_limit == -300 * u.ms + mc.lower_limit = -400 * u.ms + assert mc.lower_limit == -400 * u.ms + + +def test_mc1_upper_limit(): + with expected_protocol( + ik.qubitekk.MC1, + [], [], sep="\r" + ) as mc: + assert mc.upper_limit == 300 * u.ms + mc.upper_limit = 400 * u.ms + assert mc.upper_limit == 400 * u.ms + + def test_mc1_setting(): with expected_protocol( ik.qubitekk.MC1, @@ -75,7 +105,7 @@ def test_mc1_direction(): ], sep="\r" ) as mc: - assert mc.direction == -100 + assert mc.direction == -100 * u.ms def test_mc1_firmware(): @@ -92,6 +122,20 @@ def test_mc1_firmware(): assert mc.firmware == (1, 0, 1) +def test_mc1_firmware_no_patch_info(): + with expected_protocol( + ik.qubitekk.MC1, + [ + "FIRM?" + ], + [ + "1.0" + ], + sep="\r" + ) as mc: + assert mc.firmware == (1, 0, 0) + + def test_mc1_inertia(): with expected_protocol( ik.qubitekk.MC1, @@ -103,7 +147,7 @@ def test_mc1_inertia(): ], sep="\r" ) as mc: - assert mc.inertia == 20 + assert mc.inertia == 20 * u.ms def test_mc1_step(): @@ -201,10 +245,10 @@ def test_mc1_move(): def test_mc1_move_value_error(): - with pytest.raises(ValueError), expected_protocol( + with pytest.raises(ValueError) as exc_info, expected_protocol( ik.qubitekk.MC1, - [":MOVE -1000"], - [""], - sep="\r" + [], [], sep="\r" ) as mc: mc.move(-1000) + exc_msg = exc_info.value.args[0] + assert exc_msg == "Location out of range" diff --git a/instruments/tests/test_split_str.py b/instruments/tests/test_split_str.py index 85ff9a58d..963b5c49c 100644 --- a/instruments/tests/test_split_str.py +++ b/instruments/tests/test_split_str.py @@ -9,7 +9,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( split_unit_str ) diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index b428f0f6a..3c74d178e 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -11,7 +11,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ####################################################################### diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index 4bb2b99bf..8d9729e07 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -12,7 +12,7 @@ import instruments as ik from instruments.tests import expected_protocol -import instruments.units as u +from instruments.units import ureg as u # TESTS ####################################################################### diff --git a/instruments/tests/test_srs/test_srsctc100.py b/instruments/tests/test_srs/test_srsctc100.py index b11670eb7..3b9a59ffc 100644 --- a/instruments/tests/test_srs/test_srsctc100.py +++ b/instruments/tests/test_srs/test_srsctc100.py @@ -13,10 +13,9 @@ import pytest import numpy as np -import instruments.units as u - import instruments as ik from instruments.tests import expected_protocol +from instruments.units import ureg as u # TESTS ###################################################################### @@ -406,8 +405,8 @@ def test_channel_get_log(channel): ch = inst.channel[channel] ts_read, temps_read = ch.get_log() # assert the data is correct - np.testing.assert_equal(ts, ts_read) - np.testing.assert_equal(temps, temps_read) + np.testing.assert_array_equal(ts, ts_read) + np.testing.assert_array_equal(temps, temps_read) # INSTRUMENT # diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index 0d363039f..a235e1f0a 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -8,9 +8,8 @@ import pytest -import instruments.units as u - import instruments as ik +from instruments.units import ureg as u from instruments.tests import expected_protocol, make_name_test, unit_eq # TESTS ###################################################################### @@ -50,7 +49,7 @@ def test_srsdg645_channel_delay(): ik.srs.SRSDG645, [ "DLAY?2", - "DLAY 3,2,60.0", + "DLAY 3,2,60", "DLAY 5,4,10" ], [ @@ -227,7 +226,7 @@ def test_srsdg645_holdoff(): "+0.001001000000" ] ) as ddg: - assert ddg.holdoff == u.Quantity(1001, u.us) + assert u.Quantity(1001, u.us) == ddg.holdoff ddg.holdoff = 0 ddg.holdoff = u.Quantity(10, u.ms) # unitful hold off @@ -304,7 +303,7 @@ def test_srsdg645_burst_period(): "100E-9" ] ) as ddg: - unit_eq(ddg.burst_period, u.Quantity(100, "ns").rescale(u.s)) + unit_eq(ddg.burst_period, u.Quantity(100, "ns").to(u.sec)) ddg.burst_period = u.Quantity(13, "s") ddg.burst_period = 0.1 diff --git a/instruments/tests/test_tektronix/test_tekawg2000.py b/instruments/tests/test_tektronix/test_tekawg2000.py index 2c3d0a68c..4ef7da62c 100644 --- a/instruments/tests/test_tektronix/test_tekawg2000.py +++ b/instruments/tests/test_tektronix/test_tekawg2000.py @@ -13,7 +13,7 @@ import numpy as np import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, make_name_test @@ -74,7 +74,7 @@ def test_channel_amplitude(channel, val_read, val_unitless, val_millivolt): [ f"FG:CH{channel+1}:AMPL?", f"FG:CH{channel+1}:AMPL {val_unitless}", - f"FG:CH{channel+1}:AMPL {val_unitful.rescale(u.V).magnitude}" + f"FG:CH{channel+1}:AMPL {val_unitful.to(u.V).magnitude}" ], [ f"{val_read.magnitude}" @@ -98,7 +98,7 @@ def test_channel_offset(channel, val_read, val_unitless, val_millivolt): [ f"FG:CH{channel+1}:OFFS?", f"FG:CH{channel+1}:OFFS {val_unitless}", - f"FG:CH{channel+1}:OFFS {val_unitful.rescale(u.V).magnitude}" + f"FG:CH{channel+1}:OFFS {val_unitful.to(u.V).magnitude}" ], [ f"{val_read.magnitude}" @@ -122,7 +122,7 @@ def test_channel_frequency(channel, val_read, val_unitless, val_kilohertz): [ f"FG:FREQ?", f"FG:FREQ {val_unitless}HZ", - f"FG:FREQ {val_unitful.rescale(u.Hz).magnitude}HZ" + f"FG:FREQ {val_unitful.to(u.Hz).magnitude}HZ" ], [ f"{val_read.magnitude}" diff --git a/instruments/tests/test_tektronix/test_tekdpo70000.py b/instruments/tests/test_tektronix/test_tekdpo70000.py index 77a84ae19..2bfcb26cc 100644 --- a/instruments/tests/test_tektronix/test_tekdpo70000.py +++ b/instruments/tests/test_tektronix/test_tekdpo70000.py @@ -17,8 +17,12 @@ import pytest import instruments as ik -from instruments import units as u -from instruments.tests import expected_protocol, make_name_test +from instruments.tests import ( + expected_protocol, + make_name_test, + unit_eq, +) +from instruments.units import ureg as u # TESTS ####################################################################### @@ -303,7 +307,7 @@ def test_math_filter_risetime(value): ) as inst: inst.math[math].filter_risetime = value inst.math[math].filter_risetime = value_unitful - assert inst.math[math].filter_risetime == pytest.approx(value_unitful) + unit_eq(inst.math[math].filter_risetime, value_unitful) @given(value=st.text(alphabet=st.characters(blacklist_characters="\n", @@ -404,7 +408,7 @@ def test_math_spectral_center(value): ) as inst: inst.math[math].spectral_center = value inst.math[math].spectral_center = value_unitful - assert inst.math[math].spectral_center == pytest.approx(value_unitful) + unit_eq(inst.math[math].spectral_center, value_unitful) @given(value=st.floats(allow_nan=False)) @@ -426,7 +430,7 @@ def test_math_spectral_gatepos(value): ) as inst: inst.math[math].spectral_gatepos = value inst.math[math].spectral_gatepos = value_unitful - assert inst.math[math].spectral_gatepos == pytest.approx(value_unitful) + unit_eq(inst.math[math].spectral_gatepos, value_unitful) @given(value=st.floats(allow_nan=False)) @@ -448,7 +452,7 @@ def test_math_spectral_gatewidth(value): ) as inst: inst.math[math].spectral_gatewidth = value inst.math[math].spectral_gatewidth = value_unitful - assert inst.math[math].spectral_gatewidth == pytest.approx(value_unitful) + unit_eq(inst.math[math].spectral_gatewidth, value_unitful) @pytest.mark.parametrize("value", [True, False]) @@ -566,8 +570,7 @@ def test_math_spectral_resolution_bandwidth(value): ) as inst: inst.math[math].spectral_resolution_bandwidth = value inst.math[math].spectral_resolution_bandwidth = value_unitful - assert inst.math[math].spectral_resolution_bandwidth == \ - pytest.approx(value_unitful) + unit_eq(inst.math[math].spectral_resolution_bandwidth, value_unitful) @given(value=st.floats(min_value=0)) @@ -589,7 +592,7 @@ def test_math_spectral_span(value): ) as inst: inst.math[math].spectral_span = value inst.math[math].spectral_span = value_unitful - assert inst.math[math].spectral_span == pytest.approx(value_unitful) + unit_eq(inst.math[math].spectral_span, value_unitful) @given(value=st.floats(allow_nan=False)) @@ -669,7 +672,7 @@ def test_math_threshold(value): ) as inst: inst.math[math].threshhold = value inst.math[math].threshhold = value_unitful - assert inst.math[math].threshhold == pytest.approx(value_unitful) + unit_eq(inst.math[math].threshhold, value_unitful) @given(value=st.text(alphabet=st.characters(blacklist_characters="\n", @@ -751,7 +754,7 @@ def test_math_scale(value): ) as inst: inst.math[math].scale = value inst.math[math].scale = value_unitful - assert inst.math[math].scale == pytest.approx(value_unitful) + unit_eq(inst.math[math].scale, value_unitful) @given(value=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), @@ -869,7 +872,7 @@ def test_channel_bandwidth(value): ) as inst: inst.channel[channel].bandwidth = value inst.channel[channel].bandwidth = value_unitful - assert inst.channel[channel].bandwidth == pytest.approx(value_unitful) + unit_eq(inst.channel[channel].bandwidth, value_unitful) @given(value=st.floats(min_value=-25e-9, max_value=25e-9)) @@ -894,7 +897,7 @@ def test_channel_deskew(value): ) as inst: inst.channel[channel].deskew = value inst.channel[channel].deskew = value_unitful - assert inst.channel[channel].deskew == pytest.approx(value_unitful) + unit_eq(inst.channel[channel].deskew, value_unitful) @pytest.mark.parametrize("value", [50, 1000000]) @@ -906,7 +909,7 @@ def test_channel_termination(value): """ channel = 0 cmd = "TERM" - value_unitful = u.Quantity(value, u.Ohm) + value_unitful = u.Quantity(value, u.ohm) with expected_protocol( ik.tektronix.TekDPO70000, [ @@ -920,8 +923,7 @@ def test_channel_termination(value): ) as inst: inst.channel[channel].termination = value inst.channel[channel].termination = value_unitful - assert inst.channel[channel].termination == \ - pytest.approx(value_unitful) + unit_eq(inst.channel[channel].termination, value_unitful) @given(value=st.text(alphabet=st.characters(blacklist_characters="\n", @@ -1003,7 +1005,7 @@ def test_channel_offset(value): ) as inst: inst.channel[channel].offset = value inst.channel[channel].offset = value_unitful - assert inst.channel[channel].offset == pytest.approx(value_unitful) + unit_eq(inst.channel[channel].offset, value_unitful) @given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, @@ -1045,7 +1047,7 @@ def test_channel_scale(value): ) as inst: inst.channel[channel].scale = value inst.channel[channel].scale = value_unitful - assert inst.channel[channel].scale == pytest.approx(value_unitful) + unit_eq(inst.channel[channel].scale, value_unitful) @given(value=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), @@ -1073,8 +1075,7 @@ def test_channel_scale_raw_data(value): f"{offset}" ] ) as inst: - np.testing.assert_equal(inst.channel[channel]._scale_raw_data(value), - expected_value) + np.testing.assert_array_equal(inst.channel[channel]._scale_raw_data(value), expected_value) # INSTRUMENT # @@ -1520,7 +1521,7 @@ def test_horiz_acq_duration(value): f"{value}" ] ) as inst: - assert inst.horiz_acq_duration == pytest.approx(value_unitful) + unit_eq(inst.horiz_acq_duration, value_unitful) @given(value=st.integers(min_value=0)) @@ -1575,7 +1576,7 @@ def test_horiz_delay_pos(value): ) as inst: inst.horiz_delay_pos = value inst.horiz_delay_pos = value_unitful - assert inst.horiz_delay_pos == pytest.approx(value_unitful) + unit_eq(inst.horiz_delay_pos, value_unitful) @given(value=st.floats(min_value=0)) @@ -1595,7 +1596,7 @@ def test_horiz_delay_time(value): ) as inst: inst.horiz_delay_time = value inst.horiz_delay_time = value_unitful - assert inst.horiz_delay_time == pytest.approx(value_unitful) + unit_eq(inst.horiz_delay_time, value_unitful) @given(value=st.floats(min_value=0)) @@ -1632,7 +1633,7 @@ def test_horiz_main_pos(value): ) as inst: inst.horiz_main_pos = value inst.horiz_main_pos = value_unitful - assert inst.horiz_main_pos == pytest.approx(value_unitful) + unit_eq(inst.horiz_main_pos, value_unitful) def test_horiz_unit(): @@ -1722,7 +1723,7 @@ def test_horiz_sample_rate(value): ) as inst: inst.horiz_sample_rate = value_unitful inst.horiz_sample_rate = value - assert inst.horiz_sample_rate == pytest.approx(value_unitful) + unit_eq(inst.horiz_sample_rate, value_unitful) @given(value=st.floats(min_value=0)) @@ -1744,7 +1745,7 @@ def test_horiz_scale(value): ) as inst: inst.horiz_scale = value_unitful inst.horiz_scale = value - assert inst.horiz_scale == pytest.approx(value_unitful) + unit_eq(inst.horiz_scale, value_unitful) @given(value=st.floats(min_value=0)) @@ -1767,7 +1768,7 @@ def test_horiz_pos(value): ) as inst: inst.horiz_pos = value_unitful inst.horiz_pos = value - assert inst.horiz_pos == pytest.approx(value_unitful) + unit_eq(inst.horiz_pos, value_unitful) @pytest.mark.parametrize("value", ["AUTO", "OFF", "ON"]) diff --git a/instruments/tests/test_teledyne/test_maui.py b/instruments/tests/test_teledyne/test_maui.py index 28bcec77c..a538d914c 100644 --- a/instruments/tests/test_teledyne/test_maui.py +++ b/instruments/tests/test_teledyne/test_maui.py @@ -10,7 +10,7 @@ import pytest import instruments as ik -import instruments.units as u +from instruments.units import ureg as u from instruments.tests import expected_protocol # TESTS ###################################################################### diff --git a/instruments/tests/test_thorlabs/test_thorlabs_apt.py b/instruments/tests/test_thorlabs/test_thorlabs_apt.py index 10bd83fd1..73f481254 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_apt.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_apt.py @@ -14,7 +14,7 @@ from hypothesis import given, strategies as st import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.thorlabs._packets import ThorLabsPacket, hw_info_data diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index 62a52f0e8..e18d3ad61 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, unit_eq diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 4293b5ac1..14e239fb8 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -8,7 +8,7 @@ import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol, unit_eq diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index 370f62cd1..3ecff30a9 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -9,7 +9,7 @@ from enum import IntEnum import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol @@ -141,7 +141,7 @@ def test_tc200_temperature(): ], sep="\r" ) as tc: - assert tc.temperature == 30.0 * u.degC + assert tc.temperature == u.Quantity(30.0, u.degC) def test_tc200_temperature_set(): @@ -150,20 +150,20 @@ def test_tc200_temperature_set(): [ "tset?", "tmax?", - "tset=40.0" + "tset=40" ], [ "tset?", "30 C", "> tmax?", "250", - "> tset=40.0", + "> tset=40", "> " ], sep="\r" ) as tc: - assert tc.temperature_set == 30.0 * u.degC - tc.temperature_set = 40 * u.degC + assert tc.temperature_set == u.Quantity(30.0, u.degC) + tc.temperature_set = u.Quantity(40, u.degC) def test_tc200_temperature_range(): @@ -179,7 +179,7 @@ def test_tc200_temperature_range(): ], sep="\r" ) as tc: - tc.temperature_set = 50 * u.degC + tc.temperature_set = u.Quantity(50, u.degC) def test_tc200_pid(): @@ -378,8 +378,8 @@ def test_tc200_degrees(): ], sep="\r" ) as tc: - assert str(tc.degrees).split(" ")[1] == "K" - assert str(tc.degrees).split(" ")[1] == "degC" + assert tc.degrees == u.degK + assert tc.degrees == u.degC assert tc.degrees == u.degF tc.degrees = u.degC @@ -550,8 +550,8 @@ def test_tc200_max_temperature(): ], sep="\r" ) as tc: - assert tc.max_temperature == 200.0 * u.degC - tc.max_temperature = 180 * u.degC + assert tc.max_temperature == u.Quantity(200.0, u.degC) + tc.max_temperature = u.Quantity(180, u.degC) def test_tc200_temp_min(): diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index 41a045ebf..9636ed1f9 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -8,7 +8,7 @@ from datetime import datetime import pytest -import instruments.units as u +from instruments.units import ureg as u import instruments as ik diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index 3c0effeba..aa48457c0 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -8,13 +8,13 @@ from enum import Enum +import pint import pytest -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( assume_units, bool_property, - convert_temperature, enum_property, int_property, ProxyList, @@ -185,47 +185,15 @@ def test_assume_units_correct(): m = u.Quantity(1, 'm') # Check that unitful quantities are kept unitful. - assert assume_units(m, 'mm').rescale('mm').magnitude == 1000 + assert assume_units(m, 'mm').to('mm').magnitude == 1000 # Check that raw scalars are made unitful. - assert assume_units(1, 'm').rescale('mm').magnitude == 1000 - - -def test_temperature_conversion(): - blo = 70.0 * u.degF - out = convert_temperature(blo, u.degC) - assert out.magnitude == 21.11111111111111 - out = convert_temperature(blo, u.degK) - assert out.magnitude == 294.2055555555555 - out = convert_temperature(blo, u.degF) - assert out.magnitude == 70.0 - - blo = 20.0 * u.degC - out = convert_temperature(blo, u.degF) - assert out.magnitude == 68 - out = convert_temperature(blo, u.degC) - assert out.magnitude == 20.0 - out = convert_temperature(blo, u.degK) - assert out.magnitude == 293.15 - - blo = 270 * u.degK - out = convert_temperature(blo, u.degC) - assert out.magnitude == -3.1499999999999773 - out = convert_temperature(blo, u.degF) - assert out.magnitude == 141.94736842105263 - out = convert_temperature(blo, u.K) - assert out.magnitude == 270 - - -def test_temperater_conversion_failure(): - with pytest.raises(ValueError): - blo = 70.0 * u.degF - convert_temperature(blo, u.V) + assert assume_units(1, 'm').to('mm').magnitude == 1000 def test_assume_units_failures(): - with pytest.raises(ValueError): - assume_units(1, 'm').rescale('s') + with pytest.raises(pint.errors.DimensionalityError): + assume_units(1, 'm').to('s') def test_setattr_expression_simple(): class A: diff --git a/instruments/tests/test_yokogawa/test_yokogawa7651.py b/instruments/tests/test_yokogawa/test_yokogawa7651.py index 9d49a71c5..19211844a 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa7651.py +++ b/instruments/tests/test_yokogawa/test_yokogawa7651.py @@ -10,7 +10,7 @@ import pytest import instruments as ik -import instruments.units as u +from instruments.units import ureg as u from instruments.tests import expected_protocol @@ -90,7 +90,7 @@ def test_channel_voltage(): f"SA{value_unitless};", "E;", # trigger "F1;\nE;", # set voltage mode - f"SA{value_unitful.rescale(u.volt).magnitude};", + f"SA{value_unitful.to(u.volt).magnitude};", "E;" # trigger ], [ @@ -122,7 +122,7 @@ def test_channel_current(): f"SA{value_unitless};", "E;", # trigger "F5;\nE;", # set voltage mode - f"SA{value_unitful.rescale(u.A).magnitude};", + f"SA{value_unitful.to(u.A).magnitude};", "E;" # trigger ], [ @@ -182,7 +182,7 @@ def test_voltage(): f"SA{value_unitless};", "E;", # trigger "F1;\nE;", # set voltage mode - f"SA{value_unitful.rescale(u.volt).magnitude};", + f"SA{value_unitful.to(u.volt).magnitude};", "E;" # trigger ], [ @@ -214,7 +214,7 @@ def test_current(): f"SA{value_unitless};", "E;", # trigger "F5;\nE;", # set current mode - f"SA{value_unitful.rescale(u.A).magnitude};", + f"SA{value_unitful.to(u.A).magnitude};", "E;" # trigger ], [ diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index 68ced84db..7029f75f5 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -14,7 +14,7 @@ strategies as st, ) import numpy as np -import instruments.units as u +from instruments.units import ureg as u import instruments as ik from instruments.tests import expected_protocol diff --git a/instruments/thorlabs/_abstract.py b/instruments/thorlabs/_abstract.py index f66a453e8..c67caee5e 100644 --- a/instruments/thorlabs/_abstract.py +++ b/instruments/thorlabs/_abstract.py @@ -9,7 +9,7 @@ import time -from quantities import second +from instruments.units import ureg as u from instruments.thorlabs import _packets from instruments.abstract_instruments.instrument import Instrument @@ -73,7 +73,7 @@ def querypacket(self, packet, expect=None, timeout=None, expect_data_len=None): t_start = time.time() if timeout is not None: - timeout = assume_units(timeout, second).rescale('second').magnitude + timeout = assume_units(timeout, u.second).to('second').magnitude while True: self._file.write_raw(packet.pack()) diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index 84a7d5b3c..593b77a2f 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -13,7 +13,7 @@ from instruments.thorlabs.thorlabs_utils import check_cmd from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import enum_property, bool_property, unitful_property # CLASSES ##################################################################### @@ -70,9 +70,9 @@ def name(self): Gets/sets the frequency at which the LCC oscillates between the two voltages. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Hertz. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -141,9 +141,9 @@ def name(self): doc=""" Gets/sets the voltage value for output 1. - :units: As specified (if a `~quantities.quantity.Quantity`) or + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -156,9 +156,9 @@ def name(self): doc=""" Gets/sets the voltage value for output 2. - :units: As specified (if a `~quantities.quantity.Quantity`) or + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -171,9 +171,9 @@ def name(self): doc=""" Gets/sets the minimum voltage value for the test mode. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -187,9 +187,9 @@ def name(self): Gets/sets the maximum voltage value for the test mode. If the maximum voltage is less than the minimum voltage, nothing happens. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -202,9 +202,9 @@ def name(self): doc=""" Gets/sets the dwell time for voltages for the test mode. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units milliseconds. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -217,9 +217,9 @@ def name(self): doc=""" Gets/sets the voltage increment for voltages for the test mode. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index 8ead8441b..eaa18ebdc 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -12,7 +12,7 @@ from enum import Enum, IntEnum -import instruments.units as u +from instruments.units import ureg as u from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import enum_property @@ -229,7 +229,7 @@ def read(self, size=-1, encoding='utf-8'): of ``-1`` reads until a termination character is found. :units: As specified by :attr:`~PM100USB.measurement_configuration`. - :rtype: :class:`~quantities.Quantity` + :rtype: :class:`~pint.Quantity` """ # Get the current configuration to find out the units we need to # attach. diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index 0aa82656f..d248925b7 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -12,7 +12,7 @@ from instruments.abstract_instruments import Instrument from instruments.thorlabs.thorlabs_utils import check_cmd -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( bool_property, enum_property, int_property, unitful_property ) @@ -136,9 +136,9 @@ def name(self): doc=""" Gets/sets the amount of time that the shutter is open, in ms - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units milliseconds. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ ) @@ -151,9 +151,9 @@ def name(self): doc=""" Gets/sets the amount of time that the shutter is closed, in ms - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units milliseconds. - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ ) diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index d76a5107c..82db3f20b 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -11,7 +11,7 @@ from enum import IntEnum, Enum from instruments.abstract_instruments import Instrument -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ( convert_temperature, enum_property, unitful_property, int_property ) @@ -154,11 +154,11 @@ def status(self): doc=""" Gets the actual temperature of the sensor - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units degrees C. - :type: `~quantities.quantity.Quantity` or `int` + :type: `~pint.Quantity` or `int` :return: the temperature (in degrees C) - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -167,14 +167,14 @@ def status(self): units=u.degC, format_code="{:.1f}", set_fmt="{}={}", - valid_range=(20*u.degC, 205*u.degC), + valid_range=(u.Quantity(20, u.degC), u.Quantity(205, u.degC)), doc=""" Gets/sets the maximum temperature :return: the maximum temperature (in deg C) :units: As specified or assumed to be degree Celsius. Returns with units degC. - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ ) @@ -183,23 +183,23 @@ def temperature_set(self): """ Gets/sets the actual temperature of the sensor - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units degrees C. - :type: `~quantities.quantity.Quantity` or `int` + :type: `~pint.Quantity` or `int` :return: the temperature (in degrees C) - :rtype: `~quantities.quantity.Quantity` + :rtype: `~pint.Quantity` """ response = self.query("tset?").replace( " C", "").replace(" F", "").replace(" K", "") - return float(response) * u.degC + return u.Quantity(float(response), u.degC) @temperature_set.setter def temperature_set(self, newval): # the set temperature is always in celsius - newval = convert_temperature(newval, u.degC).magnitude - if newval < 20.0 or newval > self.max_temperature: + newval = convert_temperature(newval, u.degC) + if newval < u.Quantity(20.0, u.degC) or newval > self.max_temperature: raise ValueError("Temperature set is out of range.") - out_query = "tset={}".format(newval) + out_query = "tset={}".format(newval.magnitude) self.sendcmd(out_query) @property @@ -281,7 +281,7 @@ def degrees(self): Gets/sets the units of the temperature measurement. :return: The temperature units (degC/F/K) the TC200 is measuring in - :type: `~quantities.unitquantity.UnitTemperature` + :type: `~pint.Unit` """ response = self.status if (response >> 4) % 2 and (response >> 5) % 2: @@ -293,11 +293,11 @@ def degrees(self): @degrees.setter def degrees(self, newval): - if newval is u.degC: + if newval == u.degC: self.sendcmd("unit=c") - elif newval is u.degF: + elif newval == u.degF: self.sendcmd("unit=f") - elif newval is u.degK: + elif newval == u.degK: self.sendcmd("unit=k") else: raise TypeError("Invalid temperature type") @@ -342,6 +342,6 @@ def degrees(self, newval): :return: The maximum power :units: Watts (linear units) - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ ) diff --git a/instruments/thorlabs/thorlabsapt.py b/instruments/thorlabs/thorlabsapt.py index 5fdc5e99b..8544960c2 100644 --- a/instruments/thorlabs/thorlabsapt.py +++ b/instruments/thorlabs/thorlabsapt.py @@ -14,7 +14,7 @@ import warnings from instruments.thorlabs import _abstract, _packets, _cmds -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import assume_units # LOGGING ##################################################################### @@ -271,7 +271,7 @@ def max_travel(self): """ Gets the maximum travel for the specified piezo channel. - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: Nanometers """ pkt = _packets.ThorLabsPacket( @@ -459,9 +459,9 @@ def drive_op_parameters(self, params): "length 3.") # ensure units - volt = int(assume_units(params[0], u.V).rescale(u.V).magnitude) - rate = int(assume_units(params[1], 1/u.s).rescale(1/u.s).magnitude) - accl = int(assume_units(params[2], 1/u.s**2).rescale( + volt = int(assume_units(params[0], u.V).to(u.V).magnitude) + rate = int(assume_units(params[1], 1/u.s).to(1/u.s).magnitude) + accl = int(assume_units(params[2], 1/u.s**2).to( 1/u.s**2 ).magnitude) @@ -668,8 +668,8 @@ def jog_parameters(self, params): mode = int(params[0]) steps_fwd = int(params[1]) steps_bkw = int(params[2]) - rate = int(assume_units(params[3], 1/u.s).rescale(1/u.s).magnitude) - accl = int(assume_units(params[4], 1/u.s**2).rescale( + rate = int(assume_units(params[3], 1/u.s).to(1/u.s).magnitude) + accl = int(assume_units(params[4], 1/u.s**2).to( 1/u.s**2 ).magnitude) @@ -1123,20 +1123,20 @@ class MotorChannel(ThorLabsAPT.APTChannel): # Note that for these drivers, the scale factors are identical # for position, velcoity and acceleration. This is not true for # all drivers! - 'DRV001': (u.Quantity(51200, 'ct/mm'),) * 3, - 'DRV013': (u.Quantity(25600, 'ct/mm'),) * 3, - 'DRV014': (u.Quantity(25600, 'ct/mm'),) * 3, - 'DRV113': (u.Quantity(20480, 'ct/mm'),) * 3, - 'DRV114': (u.Quantity(20480, 'ct/mm'),) * 3, - 'FW103': (u.Quantity(25600 / 360, 'ct/deg'),) * 3, - 'NR360': (u.Quantity(25600 / 5.4546, 'ct/deg'),) * 3 + 'DRV001': (u.Quantity(51200, 'count/mm'),) * 3, + 'DRV013': (u.Quantity(25600, 'count/mm'),) * 3, + 'DRV014': (u.Quantity(25600, 'count/mm'),) * 3, + 'DRV113': (u.Quantity(20480, 'count/mm'),) * 3, + 'DRV114': (u.Quantity(20480, 'count/mm'),) * 3, + 'FW103': (u.Quantity(25600 / 360, 'count/deg'),) * 3, + 'NR360': (u.Quantity(25600 / 5.4546, 'count/deg'),) * 3 }, re.compile('TDC001|KDC101'): { - 'MTS25-Z8': (1 / u.Quantity(34304, 'mm/ct'), NotImplemented, NotImplemented), - 'MTS50-Z8': (1 / u.Quantity(34304, 'mm/ct'), NotImplemented, NotImplemented), + 'MTS25-Z8': (1 / u.Quantity(34304, 'mm/count'), NotImplemented, NotImplemented), + 'MTS50-Z8': (1 / u.Quantity(34304, 'mm/count'), NotImplemented, NotImplemented), # TODO: Z8xx and Z6xx models. Need to add regex support to motor models, too. - 'PRM1-Z8': (u.Quantity(1919.64, 'ct/deg'), NotImplemented, NotImplemented), + 'PRM1-Z8': (u.Quantity(1919.64, 'count/deg'), NotImplemented, NotImplemented), } } @@ -1165,7 +1165,7 @@ def motion_timeout(self): Gets/sets the motor channel motion timeout. :units: Seconds - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return self._motion_timeout @@ -1268,7 +1268,7 @@ def position(self): """ Gets the current position of the specified motor channel - :type: `~quantities.Quantity` + :type: `~pint.Quantity` """ pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.MOT_REQ_POSCOUNTER, @@ -1291,7 +1291,7 @@ def position_encoder(self): """ Gets the position of the encoder of the specified motor channel - :type: `~quantities.Quantity` + :type: `~pint.Quantity` :units: Encoder ``counts`` """ pkt = _packets.ThorLabsPacket( @@ -1335,7 +1335,7 @@ def move(self, pos, absolute=True): :param pos: The position to move to. Provided value will be converted to encoder counts. - :type pos: `~quantities.Quantity` + :type pos: `~pint.Quantity` :units pos: As specified, or assumed to of units encoder counts :param bool absolute: Specify if the position is a relative or @@ -1372,7 +1372,7 @@ def move(self, pos, absolute=True): scaled_pos = (pos * self.scale_factors[0]) # Force a unit error. try: - pos_ec = int(scaled_pos.rescale(u.counts).magnitude) + pos_ec = int(scaled_pos.to(u.counts).magnitude) except: raise ValueError("Provided units are not compatible " "with current motor scale factor.") diff --git a/instruments/toptica/topmode.py b/instruments/toptica/topmode.py index 46cacd81f..9dc034244 100644 --- a/instruments/toptica/topmode.py +++ b/instruments/toptica/topmode.py @@ -13,7 +13,7 @@ from instruments.abstract_instruments import Instrument from instruments.toptica.toptica_utils import convert_toptica_boolean as ctbool from instruments.toptica.toptica_utils import convert_toptica_datetime as ctdate -import instruments.units as u +from instruments.units import ureg as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -102,7 +102,7 @@ def wavelength(self): :return: The wavelength of the specified laser :units: Nanometers (nm) - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return float(self.parent.reference(self.name + ":wavelength")) * u.nm @@ -156,7 +156,7 @@ def on_time(self): :return: The 'on time' value for the specified laser :units: Seconds (s) - :type: `~quantities.quantity.Quantity` + :type: `~pint.Quantity` """ return float(self.parent.reference(self.name + ":ontime")) * u.s diff --git a/instruments/units.py b/instruments/units.py index 7aba67008..954976fa6 100644 --- a/instruments/units.py +++ b/instruments/units.py @@ -6,47 +6,10 @@ # IMPORTS ##################################################################### -# pylint: disable=unused-wildcard-import, wildcard-import - - -from quantities import * -from quantities.unitquantity import IrreducibleUnit +import pint # UNITS ####################################################################### -# IRREDUCIBLE UNITS # - - -class UnitLogPower(IrreducibleUnit): - """ - Base unit class for log-power units. The primary example of this - is `dBm`. - """ - _primary_order = 80 # Something large smaller than 99. - -# SPECIFIC UNITS # - -# Define basic unit of log-power, the dBm. - -#: Decibel-milliwatts, a basic unit of logarithmic power. -dBm = UnitLogPower('decibel-milliwatt', symbol='dBm') - -# The Phase Matrix signal generators communicate in units of millihertz (mHz) -# and centibel-milliwatts (cBm). We define those units here to make conversions -# easier later on. - -# TODO: move custom units out to another module. - -mHz = UnitQuantity( - 'millihertz', - milli * Hz, - symbol='mHz', - doc=""" - `~quantities.UnitQuantity` representing millihertz, the native unit of the - Phase Matrix FSW-0020. - """ -) - -#: Centibel-milliwatts, the native log-power unit supported by the -#: Phase Matrix FSW-0020. -cBm = UnitLogPower('centibel-milliwatt', dBm / 10, symbol='cBm') +ureg = pint.get_application_registry() +ureg.define("percent = []") +ureg.define("centibelmilliwatt = 1e-3 watt; logbase: 10; logfactor: 100 = cBm") diff --git a/instruments/util_fns.py b/instruments/util_fns.py index 7911ff088..3681ed590 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -10,7 +10,7 @@ import re from enum import Enum, IntEnum -import instruments.units as u +from instruments.units import ureg as u # CONSTANTS ################################################################### @@ -23,7 +23,7 @@ def assume_units(value, units): """ If units are not provided for ``value`` (that is, if it is a raw - `float`), then returns a `~quantities.Quantity` with magnitude + `float`), then returns a `~pint.Quantity` with magnitude given by ``value`` and units given by ``units``. :param value: A value that may or may not be unitful. @@ -70,42 +70,18 @@ def setattr_expression(target, name_expr, value): def convert_temperature(temperature, base): """ - Convert the temperature to the specified base. This is needed because - the package `quantities` does not differentiate between ``degC`` and - ``degK``. + Obsolete with the transition to Pint from Quantities. :param temperature: A quantity with units of Kelvin, Celsius, or Fahrenheit - :type temperature: `quantities.Quantity` + :type temperature: `pint.Quantity` :param base: A temperature unit to convert to - :type base: `unitquantity.UnitTemperature` + :type base: `pint.Quantity` :return: The converted temperature - :rtype: `quantities.Quantity` + :rtype: `pint.Quantity` """ - # quantities reports equivalence between degC and degK, so a string - # comparison is needed newval = assume_units(temperature, u.degC) - if newval.units == u.degF and str(base).split(" ")[1] == 'degC': - return_val = ((newval.magnitude - 32.0) * 5.0 / 9.0) * base - elif str(newval.units).split(" ")[1] == 'K' and str(base).split(" ")[1] == 'degC': - return_val = (newval.magnitude - 273.15) * base - elif str(newval.units).split(" ")[1] == 'K' and base == u.degF: - return_val = (newval.magnitude / 1.8 - 459 / 57) * base - elif str(newval.units).split(" ")[1] == 'degC' and base == u.degF: - return_val = (newval.magnitude * 9.0 / 5.0 + 32.0) * base - elif newval.units == u.degF and str(base).split(" ")[1] == 'K': - return_val = ((newval.magnitude + 459.57) * 5.0 / 9.0) * base - elif str(newval.units).split(" ")[1] == 'degC' and str(base).split(" ")[1] == 'K': - return_val = (newval.magnitude + 273.15) * base - elif str(newval.units).split(" ")[1] == 'degC' and str(base).split(" ")[1] == 'degC': - return_val = newval - elif newval.units == u.degF and base == u.degF: - return_val = newval - elif str(newval.units).split(" ")[1] == 'K' and str(base).split(" ")[1] == 'K': - return_val = newval - else: - raise ValueError(f"Unable to convert {str(newval.units)} to {str(base)}") - return return_val + return newval.to(base) def split_unit_str(s, default_units=u.dimensionless, lookup=None): @@ -468,7 +444,7 @@ def _out_decor_fcn(val): def _getter(self): raw = _in_decor_fcn(self.query("{}?".format(command))) - return u.Quantity(*split_unit_str(raw, units)).rescale(units) + return u.Quantity(*split_unit_str(raw, units)).to(units) def _setter(self, newval): min_value, max_value = valid_range @@ -487,7 +463,7 @@ def _setter(self, newval): # Rescale to the correct unit before printing. This will also # catch bad units. strval = format_code.format( - assume_units(newval, units).rescale(units).item()) + assume_units(newval, units).to(units).magnitude) self.sendcmd(set_fmt.format( command if set_cmd is None else set_cmd, _out_decor_fcn(strval) @@ -545,13 +521,13 @@ def _min_getter(self): if valid_range[0] == "query": return u.Quantity(*split_unit_str(self.query(min_fmt_str.format(command)), units)) - return assume_units(valid_range[0], units).rescale(units) + return assume_units(valid_range[0], units).to(units) def _max_getter(self): if valid_range[1] == "query": return u.Quantity(*split_unit_str(self.query(max_fmt_str.format(command)), units)) - return assume_units(valid_range[1], units).rescale(units) + return assume_units(valid_range[1], units).to(units) new_range = ( None if valid_range[0] is None else _min_getter, diff --git a/instruments/yokogawa/yokogawa6370.py b/instruments/yokogawa/yokogawa6370.py index 5920bbbdd..5f8944c64 100644 --- a/instruments/yokogawa/yokogawa6370.py +++ b/instruments/yokogawa/yokogawa6370.py @@ -9,7 +9,7 @@ from enum import IntEnum, Enum -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import ( OpticalSpectrumAnalyzer, diff --git a/instruments/yokogawa/yokogawa7651.py b/instruments/yokogawa/yokogawa7651.py index f0ec10aed..e75ffd6d4 100644 --- a/instruments/yokogawa/yokogawa7651.py +++ b/instruments/yokogawa/yokogawa7651.py @@ -9,7 +9,7 @@ from enum import IntEnum -import instruments.units as u +from instruments.units import ureg as u from instruments.abstract_instruments import ( PowerSupply, @@ -82,16 +82,16 @@ def voltage(self): Querying the voltage is not supported by this instrument. - :units: As specified (if a `~quantities.quantity.Quantity`) or + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :type: `~quantities.quantity.Quantity` with units Volt + :type: `~pint.Quantity` with units Volt """ raise NotImplementedError('This instrument does not support ' 'querying the output voltage setting.') @voltage.setter def voltage(self, newval): - newval = assume_units(newval, u.volt).rescale(u.volt).magnitude + newval = assume_units(newval, u.volt).to(u.volt).magnitude self.mode = self._parent.Mode.voltage self._parent.sendcmd('SA{};'.format(newval)) self._parent.trigger() @@ -104,16 +104,16 @@ def current(self): Querying the current is not supported by this instrument. - :units: As specified (if a `~quantities.quantity.Quantity`) or + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Amps. - :type: `~quantities.quantity.Quantity` with units Amp + :type: `~pint.Quantity` with units Amp """ raise NotImplementedError('This instrument does not support ' 'querying the output current setting.') @current.setter def current(self, newval): - newval = assume_units(newval, u.amp).rescale(u.amp).magnitude + newval = assume_units(newval, u.amp).to(u.amp).magnitude self.mode = self._parent.Mode.current self._parent.sendcmd('SA{};'.format(newval)) self._parent.trigger() @@ -175,9 +175,9 @@ def voltage(self): Querying the voltage is not supported by this instrument. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :type: `~quantities.quantity.Quantity` with units Volt + :type: `~pint.Quantity` with units Volt """ raise NotImplementedError('This instrument does not support querying ' 'the output voltage setting.') @@ -193,9 +193,9 @@ def current(self): Querying the current is not supported by this instrument. - :units: As specified (if a `~quantities.quantity.Quantity`) or assumed + :units: As specified (if a `~pint.Quantity`) or assumed to be of units Amps. - :type: `~quantities.quantity.Quantity` with units Amp + :type: `~pint.Quantity` with units Amp """ raise NotImplementedError('This instrument does not support querying ' 'the output current setting.') diff --git a/requirements.txt b/requirements.txt index ccc81b3f7..52ddda77b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ numpy pyserial pyvisa>=1.9 -quantities>=0.12.1 python-vxi11>=0.8 pyusb python-usbtmc ruamel.yaml~=0.15.37 +pint>=0.16.1 diff --git a/setup.py b/setup.py index 6bfadef89..c9febed31 100644 --- a/setup.py +++ b/setup.py @@ -36,11 +36,11 @@ "numpy", "pyserial>=3.3", "pyvisa>=1.9", - "quantities>=0.12.1", "python-vxi11>=0.8", "python-usbtmc", "pyusb", - "ruamel.yaml~=0.15.37" + "ruamel.yaml~=0.15.37", + "pint>=0.16.1" ] From b23edc33beff955c5e54dd584fb4acd1c7b2b96c Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 2 Nov 2020 21:26:18 -0500 Subject: [PATCH 070/108] BF and full coverage test suite for NewportESP301 (#280) Bug fixes: - Typos - Import changes for mocking in tests - Setter shouldn't be called with a default argument for argument - Change `u.Quantity` call to `u.Unit` call - Remove error call that doesn't seem to make sense - Check for `max_wait is not None` in `wait_for_motion` - Add keywords to docstring in `setup_axis` - Correct copy / paste error in error message in `setup_axis` Test suite uses a lot of mocking in order to not go through the crazy calls that permanently happen to the routine. This should be okay since the mocked out routines are thoroughly tested separately. --- instruments/newport/newportesp301.py | 44 +- .../tests/test_newport/test_newportesp301.py | 2754 ++++++++++++++++- 2 files changed, 2774 insertions(+), 24 deletions(-) diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index db06be510..7c833f934 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -13,7 +13,7 @@ from contextlib import contextmanager from enum import IntEnum from functools import reduce -from time import time, sleep +import time from instruments.abstract_instruments import Instrument from instruments.newport.errors import NewportError @@ -689,7 +689,7 @@ def desired_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("DP?", target=self.axis_id)), + float(self._newport_cmd("DV?", target=self.axis_id)), self._units / u.s ) @@ -709,7 +709,7 @@ def home(self): ) @home.setter - def home(self, newval=0): + def home(self, newval): if newval is None: return newval = float(assume_units(newval, self._units).to( @@ -721,9 +721,8 @@ def units(self): """ Get the units that all commands are in reference to. - :type: `~pint.Quantity` with units corresponding to - units of axis connected or int which corresponds to Newport - unit number + :type: `~pint.Unit` corresponding to units of axis connected or + int which corresponds to Newport unit number """ self._units = self._get_pq_unit(self._get_units()) return self._units @@ -734,7 +733,7 @@ def units(self, newval): return if isinstance(newval, int): self._units = self._get_pq_unit(NewportESP301Units(int(newval))) - elif isinstance(newval, u.Quantity): + elif isinstance(newval, u.Unit): self._units = newval newval = self._get_unit_num(newval) self._set_units(newval) @@ -1121,7 +1120,7 @@ def move(self, position, absolute=True, wait=False, block=False): if wait: self.wait_for_position(position) if block: - sleep(0.003) + time.sleep(0.003) mot = self.is_motion_done while not mot: mot = self.is_motion_done @@ -1154,10 +1153,7 @@ def stop_motion(self): """ Stop all motion on axis. With programmed deceleration rate """ - try: - self._newport_cmd("ST", target=self.axis_id) - except NewportError as e: - raise NewportError(e) + self._newport_cmd("ST", target=self.axis_id) def wait_for_position(self, position): """ @@ -1190,15 +1186,16 @@ def wait_for_motion(self, poll_interval=0.01, max_wait=None): # be ignored. poll_interval = float(assume_units(poll_interval, u.s).to( u.s).magnitude) - max_wait = float(assume_units(max_wait, u.s).to( - u.s).magnitude) - tic = time() + if max_wait is not None: + max_wait = float(assume_units(max_wait, u.s).to( + u.s).magnitude) + tic = time.time() while True: if self.is_motion_done: return else: - if max_wait is None or (time() - tic) < max_wait: - sleep(poll_interval) + if max_wait is None or (time.time() - tic) < max_wait: + time.sleep(poll_interval) else: raise IOError("Timed out waiting for motion to finish.") @@ -1230,19 +1227,24 @@ def setup_axis(self, **kwargs): * 'jog_low_velocity' = jog low speed (U/s) * 'max_acceleration' = maximum acceleration (U/s^2) * 'acceleration' = acceleration (U/s^2) + * 'velocity' = velocity (U/s) * 'deceleration' = set deceleration (U/s^2) * 'error_threshold' = set error threshold (U) + * 'estop_deceleration' = estop deceleration (U/s^2) + * 'jerk' = jerk rate (U/s^3) * 'proportional_gain' = PID proportional gain (optional) * 'derivative_gain' = PID derivative gain (optional) - * 'interal_gain' = PID internal gain (optional) + * 'integral_gain' = PID internal gain (optional) * 'integral_saturation_gain' = PID integral saturation (optional) * 'trajectory' = trajectory mode (optional) * 'position_display_resolution' (U per step) * 'feedback_configuration' * 'full_step_resolution' = (U per step) * 'home' = (U) - * 'acceleration_feed_forward' = bewtween 0 to 2e9 - * 'reduce_motor_torque' = (time(ms),percentage) + * 'acceleration_feed_forward' = between 0 to 2e9 + * 'microstep_factor' = axis microstep factor + * 'reduce_motor_torque_time' = time (ms) between 0 and 60000, + * 'reduce_motor_torque_percentage' = percentage between 0 and 100 """ self.motor_type = kwargs.get('motor_type') @@ -1285,7 +1287,7 @@ def setup_axis(self, **kwargs): percentage = int(assume_units(percentage, u.percent).to( u.percent).magnitude) if percentage < 0 or percentage > 100: - raise ValueError("Time must be between 0 and 60000 ms") + raise ValueError(r"Percentage must be between 0 and 100%") self._newport_cmd( "QR", target=self._axis_id, params=[motor_time, percentage]) diff --git a/instruments/tests/test_newport/test_newportesp301.py b/instruments/tests/test_newport/test_newportesp301.py index 1484ba029..3343cc46f 100644 --- a/instruments/tests/test_newport/test_newportesp301.py +++ b/instruments/tests/test_newport/test_newportesp301.py @@ -6,18 +6,47 @@ # IMPORTS ##################################################################### +import time + +from hypothesis import given, strategies as st +import pytest import instruments as ik +from instruments.units import ureg as u from instruments.tests import expected_protocol # TESTS ####################################################################### -def test_axis_returns_axis_class(): +# pylint: disable=protected-access,too-many-lines + + +# INSTRUMENT # + + +def test_init(): + """Initialize a Newport ESP301 instrument.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + assert inst._execute_immediately + assert inst._command_list == [] + assert inst._bulk_query_resp == "" + assert inst.terminator == "\r" + + +@given(ax=st.integers(min_value=0, max_value=99)) +def test_axis_returns_axis_class(ax): + """Return axis class with given axis number.""" with expected_protocol( ik.newport.NewportESP301, [ - "1SN?", + f"{ax+1}SN?", "TB?" # error check query ], [ @@ -25,6 +54,2725 @@ def test_axis_returns_axis_class(): "0,0,0" ], sep="\r" + ) as inst: + axis = inst.axis[ax] + assert isinstance(axis, ik.newport.NewportESP301Axis) + + +def test_newport_cmd(mocker): + """Send a low level command to some randomly chosen target. + + Execute command immediately (default), but no error check. + """ + target = "TARG" + cmd = "COMMAND" + params = (1, 2, 3) + # stitch together raw command to send + raw_cmd = f"{target}{cmd}{','.join(map(str, params))}" + with expected_protocol( + ik.newport.NewportESP301, + [ + raw_cmd + ], + [ + ], + sep="\r" + ) as inst: + execute_spy = mocker.spy(inst, '_execute_cmd') + resp = inst._newport_cmd(cmd, params=params, target=target, + errcheck=False) + assert resp is None + execute_spy.assert_called_with(raw_cmd, False) + + +def test_newport_cmd_add_to_list(): + """Send a low level command to some randomly chosen target. + + Do not execute, just add command to list. + """ + target = "TARG" + cmd = "COMMAND" + params = (1, 2, 3) + # stitch together raw command to send + raw_cmd = f"{target}{cmd}{','.join(map(str, params))}" + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + inst._execute_immediately = False + resp = inst._newport_cmd(cmd, params=params, target=target) + assert resp is None + assert inst._command_list == [raw_cmd] + + +def test_newport_cmd_with_axis(): + """Send a low level command for a given axis.""" + ax = 42 + cmd = "COMMAND" + params = (1, 2, 3) + # stitch together raw command to send + raw_cmd = f"{ax+1}{cmd}{','.join(map(str, params))}" + + with expected_protocol( + ik.newport.NewportESP301, + [ + f"{ax+1}SN?", + "TB?", # error check query + raw_cmd + ], + [ + "1", + "0,0,0" + ], + sep="\r" + ) as inst: + axis = inst.axis[ax] + resp = inst._newport_cmd(cmd, params=params, target=axis, + errcheck=False) + assert resp is None + + +def test_execute_cmd_query(): + """Execute a query.""" + query = "QUERY?" + response = "RESPONSE" + + with expected_protocol( + ik.newport.NewportESP301, + [ + query, + "TB?" + ], + [ + response, + "0,0,0" # no error + ], + sep="\r" + ) as inst: + assert inst._execute_cmd(query) == response + + +def test_execute_cmd_query_error(): + """Raise an error while sending a command to the instrument. + + Only check for the context of the specific error message, since + timestamp is not frozen. + """ + cmd = "COMMAND" + with expected_protocol( + ik.newport.NewportESP301, + [ + cmd, + "TB?" + ], + [ + "13,0,0" # no error + ], + sep="\r" + ) as inst: + with pytest.raises(ik.newport.errors.NewportError) as err_info: + inst._execute_cmd(cmd) + err_msg = err_info.value.args[0] + assert "GROUP NUMBER MISSING" in err_msg + + +def test_home(mocker): + """Search for home. + + Mock `_newport_cmd`, this routine is already tested. Just assert + that it is called with correct arguments. + """ + axis = "ax" + params = 1, 2, 3 + errcheck = False + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + mock_cmd = mocker.patch.object(inst, '_newport_cmd') + inst._home(axis, params, errcheck) + mock_cmd.assert_called_with("OR", target=axis, params=[params], + errcheck=errcheck) + + +@pytest.mark.parametrize("search_mode", ik.newport.NewportESP301HomeSearchMode) +def test_search_for_home(mocker, search_mode): + """Search for home with specific method. + + Mock `_home` routine (already tested) and just assert that called + with the correct arguments. + """ + axis = 3 + errcheck = True + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + mock_cmd = mocker.patch.object(inst, '_home') + inst.search_for_home(axis, search_mode, errcheck) + mock_cmd.assert_called_with(axis=axis, search_mode=search_mode, + errcheck=errcheck) + + +def test_reset(mocker): + """Reset the device. + + Mock `_newport_cmd`, this routine is already tested. Just assert + that it is called with correct arguments. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + mock_cmd = mocker.patch.object(inst, '_newport_cmd') + inst.reset() + mock_cmd.assert_called_with("RS", errcheck=False) + + +@given(prg_id=st.integers(min_value=1, max_value=100)) +def test_define_program(mocker, prg_id): + """Define an empty program. + + Mock out the `_newport_cmd` routine. Already tested and not + required. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + mock_cmd = mocker.patch.object(inst, '_newport_cmd') + with inst.define_program(prg_id): + pass + calls = ( + mocker.call("XX", target=prg_id), + mocker.call("EP", target=prg_id), + mocker.call("QP") + ) + mock_cmd.has_calls(calls) + + +@given(prg_id=st.integers().filter(lambda x: x < 1 or x > 100)) +def test_define_program_value_error(prg_id): + """Raise ValueError when defining program with invalid ID.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + with pytest.raises(ValueError) as err_info: + with inst.define_program(prg_id): + pass + err_msg = err_info.value.args[0] + assert err_msg == "Invalid program ID. Must be an integer from 1 to " \ + "100 (inclusive)." + + +@pytest.mark.parametrize("errcheck", (True, False)) +def test_execute_bulk_command(mocker, errcheck): + """Execute bulk commands. + + Mock out the `_execute_cmd` call and simply assert that calls are + in correct order. + + We will just do three move commands, one with steps of 1, 10, and + 11. + """ + ax = 0 + move_commands_sent = '1PA1.0 ; 1PA10.0 ; ; 1PA11.0 ; ' + resp = "Response" + with expected_protocol( + ik.newport.NewportESP301, + [ + f"{ax+1}SN?", + "TB?", # error check query + ], + [ + "1", + "0,0,0" + ], + sep="\r" + ) as inst: + axis = inst.axis[ax] + mock_exec = mocker.patch.object(inst, '_execute_cmd', + return_value=resp) + with inst.execute_bulk_command(errcheck=errcheck): + assert not inst._execute_immediately + # some move commands + axis.move(1.) + axis.move(10.) + axis.move(11.) + mock_exec.assert_called_with(move_commands_sent, errcheck) + assert inst._bulk_query_resp == resp + assert inst._command_list == [] + assert inst._execute_immediately + + +@given(prg_id=st.integers(min_value=1, max_value=100)) +def test_run_program(mocker, prg_id): + """Run a program. + + Mock out the `_newport_cmd` routine. Already tested and not + required. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + mock_cmd = mocker.patch.object(inst, '_newport_cmd') + inst.run_program(prg_id) + mock_cmd.assert_called_with("EX", target=prg_id) + + +@given(prg_id=st.integers().filter(lambda x: x < 1 or x > 100)) +def test_run_program_value_error(prg_id): + """Raise ValueError when defining program with invalid ID.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ], + [ + ], + sep="\r" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.run_program(prg_id) + err_msg = err_info.value.args[0] + assert err_msg == "Invalid program ID. Must be an integer from 1 to " \ + "100 (inclusive)." + + +# AXIS # + + +# commands to send, return when initializing axis zero +ax_init = "1SN?\rTB?", "1\r0,0,0" + + +def test_axis_init(): + """Initialize a new axis.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + assert axis._controller == inst + assert axis._axis_id == 1 + assert axis._units == u.Quantity(1, u.count) + + +def test_axis_init_type_error(): + """Raise TypeError when axis initialized from wrong parent.""" + with pytest.raises(TypeError) as err_info: + _ = ik.newport.newportesp301.NewportESP301Axis(42, 0) + err_msg = err_info.value.args[0] + assert err_msg == "Axis must be controlled by a Newport ESP-301 motor " \ + "controller." + + +def test_axis_units_of(mocker): + """Context manager with reset of units after usage. + + Mock out the getting and setting the units. These two routines are + tested separately, thus only assert that the correct calls are + issued. + """ + get_unit = ik.newport.newportesp301.NewportESP301Units.millimeter + set_unit = ik.newport.newportesp301.NewportESP301Units.inches + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_get = mocker.patch.object(axis, '_get_units', return_value=get_unit) + mock_set = mocker.patch.object(axis, '_set_units', return_value=None) + with axis._units_of(set_unit): + mock_get.assert_called() + mock_set.assert_called_with(set_unit) + mock_set.assert_called_with(get_unit) + + +def test_axis_get_units(mocker): + """Get units from the axis. + + Mock out the command sending and receiving. + """ + resp = "2" + unit = ik.newport.newportesp301.NewportESP301Units(int(resp)) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value=resp) + assert unit == axis._get_units() + mock_cmd.assert_called_with("SN?", target=1) + + +def test_axis_set_units(mocker): + """Set units for a given axis. + + Mock out the actual command sending for simplicity, but assert it + has been called. + """ + unit = ik.newport.newportesp301.NewportESP301Units.radian # just pick one + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value=None) + assert axis._set_units(unit) is None + mock_cmd.assert_called_with("SN", target=1, params=[int(unit)]) + + +def test_axis_id(): + """Get axis ID.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + assert axis.axis_id == 1 + + +@pytest.mark.parametrize("resp", ("0", "1")) +def test_axis_is_motion_done(mocker, resp): + """Get if motion is done. + + Mock out the command sending, as above. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value=resp) + assert axis.is_motion_done is bool(int(resp)) + mock_cmd.assert_called_with("MD?", target=1) + + +def test_axis_acceleration(mocker): + """Set / get axis acceleration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.acceleration = value + mock_cmd.assert_called_with("AC", target=1, params=[float(value)]) + assert axis.acceleration == u.Quantity(value, axis._units / u.s**2) + mock_cmd.assert_called_with("AC?", target=1) + + +def test_axis_acceleration_none(): + """Set axis acceleration with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.acceleration = None + + +def test_axis_deceleration(mocker): + """Set / get axis deceleration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.deceleration = value + mock_cmd.assert_called_with("AG", target=1, params=[float(value)]) + assert axis.deceleration == u.Quantity(value, axis._units / u.s**2) + mock_cmd.assert_called_with("AG?", target=1) + + +def test_axis_deceleration_none(): + """Set axis deceleration with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.deceleration = None + + +def test_axis_estop_deceleration(mocker): + """Set / get axis estop deceleration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.estop_deceleration = value + mock_cmd.assert_called_with("AE", target=1, params=[float(value)]) + assert axis.estop_deceleration == u.Quantity(value, + axis._units / u.s**2) + mock_cmd.assert_called_with("AE?", target=1) + + +def test_axis_jerk(mocker): + """Set / get axis jerk rate. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.jerk = value + mock_cmd.assert_called_with("JK", target=1, params=[float(value)]) + assert axis.jerk == u.Quantity(value, axis._units / u.s**3) + mock_cmd.assert_called_with("JK?", target=1) + + +def test_axis_velocity(mocker): + """Set / get axis velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.velocity = value + mock_cmd.assert_called_with("VA", target=1, params=[float(value)]) + assert axis.velocity == u.Quantity(value, axis._units / u.s) + mock_cmd.assert_called_with("VA?", target=1) + +def test_axis_max_velocity(mocker): + """Set / get axis maximum velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.max_velocity = value + mock_cmd.assert_called_with("VU", target=1, params=[float(value)]) + assert axis.max_velocity == u.Quantity(value, axis._units / u.s) + mock_cmd.assert_called_with("VU?", target=1) + + +def test_axis_max_velocity_none(): + """Set axis maximum velocity with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.max_velocity = None + + +def test_axis_max_base_velocity(mocker): + """Set / get axis maximum base velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.max_base_velocity = value + mock_cmd.assert_called_with("VB", target=1, params=[float(value)]) + assert axis.max_base_velocity == u.Quantity(value, axis._units / u.s) + mock_cmd.assert_called_with("VB?", target=1) + + +def test_axis_max_base_velocity_none(): + """Set axis maximum base velocity with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.max_base_velocity = None + + +def test_axis_jog_high_velocity(mocker): + """Set / get axis jog high velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.jog_high_velocity = value + mock_cmd.assert_called_with("JH", target=1, params=[float(value)]) + assert axis.jog_high_velocity == u.Quantity(value, axis._units / u.s) + mock_cmd.assert_called_with("JH?", target=1) + + +def test_axis_jog_high_velocity_none(): + """Set axis jog high velocity with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.jog_high_velocity = None + + +def test_axis_jog_low_velocity(mocker): + """Set / get axis jog low velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.jog_low_velocity = value + mock_cmd.assert_called_with("JW", target=1, params=[float(value)]) + assert axis.jog_low_velocity == u.Quantity(value, axis._units / u.s) + mock_cmd.assert_called_with("JW?", target=1) + + +def test_axis_jog_low_velocity_none(): + """Set axis jog low velocity with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.jog_low_velocity = None + + +def test_axis_homing_velocity(mocker): + """Set / get axis homing velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.homing_velocity = value + mock_cmd.assert_called_with("OH", target=1, params=[float(value)]) + assert axis.homing_velocity == u.Quantity(value, axis._units / u.s) + mock_cmd.assert_called_with("OH?", target=1) + + +def test_axis_homing_velocity_none(): + """Set axis homing velocity with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.homing_velocity = None + + +def test_axis_max_acceleration(mocker): + """Set / get axis maximum acceleration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.max_acceleration = value + mock_cmd.assert_called_with("AU", target=1, params=[float(value)]) + assert axis.max_acceleration == u.Quantity(value, axis._units / u.s**2) + mock_cmd.assert_called_with("AU?", target=1) + + +def test_axis_max_acceleration_none(): + """Set axis maximum acceleration with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.max_acceleration = None + + +def test_axis_max_deceleration(mocker): + """Set / get axis maximum deceleration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.max_deceleration = value + mock_cmd.assert_called_with("AU", target=1, params=[float(value)]) + assert axis.max_deceleration == u.Quantity(value, axis._units / u.s**2) + mock_cmd.assert_called_with("AU?", target=1) + + +def test_axis_position(mocker): + """Get axis position. + + Mock out `_newport_cmd` since tested elsewhere. + """ + retval = "42" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=retval) + assert axis.position == u.Quantity(float(retval), axis._units) + mock_cmd.assert_called_with("TP?", target=1) + + +def test_axis_desired_position(mocker): + """Get axis desired position. + + Mock out `_newport_cmd` since tested elsewhere. + """ + retval = "42" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=retval) + assert axis.desired_position == u.Quantity(float(retval), axis._units) + mock_cmd.assert_called_with("DP?", target=1) + + +def test_axis_desired_velocity(mocker): + """Get axis desired velocity. + + Mock out `_newport_cmd` since tested elsewhere. + """ + retval = "42" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=retval) + assert axis.desired_velocity == u.Quantity(float(retval), + axis._units / u.s) + mock_cmd.assert_called_with("DV?", target=1) + + +def test_axis_home(mocker): + """Set / get axis home position. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.home = value + mock_cmd.assert_called_with("DH", target=1, params=[float(value)]) + assert axis.home == u.Quantity(value, axis._units) + mock_cmd.assert_called_with("DH?", target=1) + + +def test_axis_home_none(): + """Set axis home with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.home = None + + +def test_axis_units(mocker): + """Get / set units. + + Mock out `_newport_cmd` since tested elsewhere. Returns u.counts + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value="0") + assert axis.units == u.counts + mock_cmd.reset_mock() + # set units with None + axis.units = None + mock_cmd.assert_not_called() + # set units with um as number (num 3) + axis.units = 3 + assert axis._units == u.um + mock_cmd.assert_called_with("SN", target=1, params=[3]) + # set units with millimeters as quantity (num 2) + axis.units = u.mm + assert axis._units == u.mm + mock_cmd.assert_called_with("SN", target=1, params=[2]) + + +def test_axis_encoder_resolution(mocker): + """Set / get axis encoder resolution. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.encoder_resolution = value + mock_cmd.assert_called_with("SU", target=1, params=[float(value)]) + assert axis.encoder_resolution == u.Quantity(value, axis._units) + mock_cmd.assert_called_with("SU?", target=1) + + +def test_axis_encoder_resolution_none(): + """Set axis encoder resolution with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.encoder_resolution = None + + +def test_axis_full_step_resolution(mocker): + """Set / get axis full step resolution. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.full_step_resolution = value + mock_cmd.assert_called_with("FR", target=1, params=[float(value)]) + assert axis.full_step_resolution == u.Quantity(value, axis._units) + mock_cmd.assert_called_with("FR?", target=1) + + +def test_axis_full_step_resolution_none(): + """Set axis full step resolution with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.full_step_resolution = None + + +def test_axis_left_limit(mocker): + """Set / get axis left limit. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.left_limit = value + mock_cmd.assert_called_with("SL", target=1, params=[float(value)]) + assert axis.left_limit == u.Quantity(value, axis._units) + mock_cmd.assert_called_with("SL?", target=1) + + +def test_axis_right_limit(mocker): + """Set / get axis right limit. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.right_limit = value + mock_cmd.assert_called_with("SR", target=1, params=[float(value)]) + assert axis.right_limit == u.Quantity(value, axis._units) + mock_cmd.assert_called_with("SR?", target=1) + + +def test_axis_error_threshold(mocker): + """Set / get axis error threshold. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.error_threshold = value + mock_cmd.assert_called_with("FE", target=1, params=[float(value)]) + assert axis.error_threshold == u.Quantity(value, axis._units) + mock_cmd.assert_called_with("FE?", target=1) + + +def test_axis_error_threshold_none(): + """Set axis error threshold with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.error_threshold = None + + +def test_axis_current(mocker): + """Set / get axis current. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.current = value + mock_cmd.assert_called_with("QI", target=1, params=[float(value)]) + assert axis.current == u.Quantity(value, u.A) + mock_cmd.assert_called_with("QI?", target=1) + + +def test_axis_current_none(): + """Set axis current with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.current = None + + +def test_axis_voltage(mocker): + """Set / get axis voltage. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.voltage = value + mock_cmd.assert_called_with("QV", target=1, params=[float(value)]) + assert axis.voltage == u.Quantity(value, u.V) + mock_cmd.assert_called_with("QV?", target=1) + + +def test_axis_voltage_none(): + """Set axis voltage with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.voltage = None + + +def test_axis_motor_type(mocker): + """Set / get axis motor type. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 1 # DC Servo + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.motor_type = value + mock_cmd.assert_called_with("QM", target=1, params=[float(value)]) + assert axis.motor_type == value + mock_cmd.assert_called_with("QM?", target=1) + + +def test_axis_motor_type_none(): + """Set axis motor type with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.motor_type = None + + +def test_axis_feedback_configuration(mocker): + """Set / get axis feedback configuration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value_ret = 'A13\r\n' # 2 additional characters that will be cancelled + value = int(value_ret[:-2], 16) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value_ret) + axis.feedback_configuration = value + mock_cmd.assert_called_with("ZB", target=1, params=[float(value)]) + assert axis.feedback_configuration == value + mock_cmd.assert_called_with("ZB?", target=1) + + +def test_axis_feedback_configuration_none(): + """Set axis feedback configuration with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.feedback_configuration = None + + +def test_axis_position_display_resolution(mocker): + """Set / get axis position display resolution. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.position_display_resolution = value + mock_cmd.assert_called_with("FP", target=1, params=[float(value)]) + assert axis.position_display_resolution == value + mock_cmd.assert_called_with("FP?", target=1) + + +def test_axis_position_display_resolution_none(): + """Set axis position display resolution with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.position_display_resolution = None + + +def test_axis_trajectory(mocker): + """Set / get axis trajectory. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.trajectory = value + mock_cmd.assert_called_with("TJ", target=1, params=[float(value)]) + assert axis.trajectory == value + mock_cmd.assert_called_with("TJ?", target=1) + + +def test_axis_trajectory_none(): + """Set axis trajectory with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.trajectory = None + + +def test_axis_microstep_factor(mocker): + """Set / get axis microstep factor. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.microstep_factor = value + mock_cmd.assert_called_with("QS", target=1, params=[float(value)]) + assert axis.microstep_factor == value + mock_cmd.assert_called_with("QS?", target=1) + + +def test_axis_microstep_factor_none(): + """Set axis microstep factor with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.microstep_factor = None + + +@given(fct=st.integers().filter(lambda x: x < 1 or x > 250)) +def test_axis_microstep_factor_out_of_range(fct): + """Raise ValueError when microstep factor is out of range.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError) as err_info: + axis.microstep_factor = fct + err_msg = err_info.value.args[0] + assert err_msg == "Microstep factor must be between 1 and 250" + + +def test_axis_hardware_limit_configuration(mocker): + """Set / get axis hardware limit configuration. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value_ret = '42\r\n' # add two characters to delete later + value = int(value_ret[:-2]) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value_ret) + axis.hardware_limit_configuration = value + mock_cmd.assert_called_with("ZH", target=1, params=[float(value)]) + assert axis.hardware_limit_configuration == value + mock_cmd.assert_called_with("ZH?", target=1) + + +def test_axis_hardware_limit_configuration_none(): + """Set axis hardware limit configuration with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.hardware_limit_configuration = None + + +def test_axis_acceleration_feed_forward(mocker): + """Set / get axis acceleration feed forward. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + axis.acceleration_feed_forward = value + mock_cmd.assert_called_with("AF", target=1, params=[float(value)]) + assert axis.acceleration_feed_forward == value + mock_cmd.assert_called_with("AF?", target=1) + + +def test_axis_acceleration_feed_forward_none(): + """Set axis acceleration feed forward with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.acceleration_feed_forward = None + + +def test_axis_proportional_gain(mocker): + """Set / get axis proportional gain. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value_ret = '42\r' + value = float(value_ret[:-1]) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value_ret) + axis.proportional_gain = value + mock_cmd.assert_called_with("KP", target=1, params=[float(value)]) + assert axis.proportional_gain == float(value) + mock_cmd.assert_called_with("KP?", target=1) + + +def test_axis_proportional_gain_none(): + """Set axis proportional gain with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.proportional_gain = None + + +def test_axis_derivative_gain(mocker): + """Set / get axis derivative gain. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value_ret = '42' + value = float(value_ret) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value_ret) + axis.derivative_gain = value + mock_cmd.assert_called_with("KD", target=1, params=[float(value)]) + assert axis.derivative_gain == float(value) + mock_cmd.assert_called_with("KD?", target=1) + + +def test_axis_derivative_gain_none(): + """Set axis derivative gain with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.derivative_gain = None + + +def test_axis_integral_gain(mocker): + """Set / get axis integral gain. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value_ret = '42' + value = float(value_ret) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value_ret) + axis.integral_gain = value + mock_cmd.assert_called_with("KI", target=1, params=[float(value)]) + assert axis.integral_gain == float(value) + mock_cmd.assert_called_with("KI?", target=1) + + +def test_axis_integral_gain_none(): + """Set axis integral gain with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.integral_gain = None + + +def test_axis_integral_saturation_gain(mocker): + """Set / get axis integral saturation gain. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value_ret = '42' + value = float(value_ret) + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value_ret) + axis.integral_saturation_gain = value + mock_cmd.assert_called_with("KS", target=1, params=[float(value)]) + assert axis.integral_saturation_gain == float(value) + mock_cmd.assert_called_with("KS?", target=1) + + +def test_axis_integral_saturation_gain_none(): + """Set axis integral saturation gain with `None` does nothing.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + axis.integral_saturation_gain = None + + +def test_axis_encoder_position(mocker): + """Get encoder position. + + Mock out the getting and setting the units. These two routines are + tested separately, thus only assert that the correct calls are + issued. + Also mock out `_newport_cmd`. + """ + value = 42 + get_unit = ik.newport.newportesp301.NewportESP301Units.millimeter + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_get = mocker.patch.object(axis, '_get_units', + return_value=get_unit) + mock_set = mocker.patch.object(axis, '_set_units', return_value=None) + mock_cmd = mocker.patch.object(axis, '_newport_cmd', + return_value=value) + assert axis.encoder_position == u.Quantity(value, u.count) + mock_get.assert_called() + mock_set.assert_called_with(get_unit) + mock_cmd.assert_called_with("TP?", target=1) + + +# AXIS METHODS # + + +@pytest.mark.parametrize("mode", + ik.newport.newportesp301.NewportESP301HomeSearchMode) +def test_axis_search_for_home(mocker, mode): + """Search for home. + + Mock out `search_for_home` of controller since already tested. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_search = mocker.patch.object(axis._controller, 'search_for_home') + axis.search_for_home(search_mode=mode.value) + mock_search.assert_called_with(axis=1, search_mode=mode.value) + + +def test_axis_move_absolute(mocker): + """Make an absolute move (default) on the axis. + + No wait, no block. + Mock out `_newport_cmd` since tested elsewhere. + """ + position = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.move(position) + mock_cmd.assert_called_with("PA", params=[position], target=1) + + +def test_axis_move_relative_wait(mocker): + """Make an relative move on the axis and wait. + + Do a wait but no block. + Mock out `_newport_cmd` since tested elsewhere. + """ + position = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.move(position, absolute=False, wait=True) + calls = ( + mocker.call("PR", params=[position], target=1), + mocker.call("WP", target=1, params=[float(position)]) + ) + mock_cmd.assert_has_calls(calls) + + +def test_axis_move_relative_wait_block(mocker): + """Make an relative move on the axis and wait. + + Do a wait and lock, go once into while loop. + Mock out `_newport_cmd`, `time.sleep`, and `is_motion_done` since + tested elsewhere. + """ + position = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd.side_effect = [None, None, False, True] + axis.move(position, absolute=False, wait=True, block=True) + calls = ( + mocker.call("PR", params=[position], target=1), + mocker.call("WP", target=1, params=[float(position)]), + mocker.call("MD?", target=1), + mocker.call("MD?", target=1) + ) + mock_cmd.assert_has_calls(calls) + + +def test_axis_move_to_hardware_limit(mocker): + """Move to hardware limit. + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.move_to_hardware_limit() + mock_cmd.assert_called_with("MT", target=1) + + +def test_axis_move_indefinitely(mocker): + """Move indefinitely + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.move_indefinitely() + mock_cmd.assert_called_with("MV", target=1) + + +def test_axis_abort_motion(mocker): + """Abort motion. + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.abort_motion() + mock_cmd.assert_called_with("AB", target=1) + + +def test_axis_wait_for_stop(mocker): + """Wait for stop. + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.wait_for_stop() + mock_cmd.assert_called_with("WS", target=1) + + +def test_axis_stop_motion(mocker): + """Stop motion. + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.stop_motion() + mock_cmd.assert_called_with("ST", target=1) + + +def test_axis_wait_for_position(mocker): + """Wait for position. + + Mock out `_newport_cmd` since tested elsewhere. + """ + value = 42 + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.wait_for_position(value) + mock_cmd.assert_called_with("WP", target=1, params=[float(value)]) + + +def test_axis_wait_for_motion_max_wait_zero(mocker): + """Wait for motion to finish. + + Motion is not stopped (mock that part) but maximum wait time is + zero. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mocker.patch.object(axis, '_newport_cmd', return_value="0") + + with pytest.raises(IOError) as err_info: + axis.wait_for_motion(max_wait=0.) + err_msg = err_info.value.args[0] + assert err_msg == "Timed out waiting for motion to finish." + + +def test_axis_wait_for_motion_max_wait_some_time(mocker): + """Wait for motion to finish. + + Motion is stopped after several queries that first return `False`. + Mocking `time.time`, `time.sleep`, and `_newport_cmd`. Using + generators to create the appropriate times.. + """ + interval = 42. + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + # patch time and sleep + mock_time = mocker.patch.object(time, 'time', return_value=None) + mock_time.side_effect = [0.0, 0.0, 0.1] + mock_sleep = mocker.patch.object(time, 'sleep', return_value=None) + # get axis + axis = inst.axis[0] + # patch status + mock_status = mocker.patch.object(axis, '_newport_cmd', + return_value=None) + mock_status.side_effect = ["0", "0", "1"] + assert axis.wait_for_motion(poll_interval=interval) is None + # make sure the routine has called sleep + mock_sleep.assert_called_with(interval) + + +def test_axis_enable(mocker): + """Enable axis. + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.enable() + mock_cmd.assert_called_with("MO", target=1) + + +def test_axis_disable(mocker): + """Disable axis. + + Mock out `_newport_cmd` since tested elsewhere. + """ + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + axis.disable() + mock_cmd.assert_called_with("MF", target=1) + + +def test_axis_setup_axis(mocker): + """Set up non-newport motor. + + Mock out `_newport_cmd` since tested elsewhere. + """ + motor_type = 2 # stepper motor + current = 1 + voltage = 2 + units = ik.newport.newportesp301.NewportESP301Units.radian + encoder_resolution = 3. + max_velocity = 4 + max_base_velocity = 5 + homing_velocity = 6 + jog_high_velocity = 7 + jog_low_velocity = 8 + max_acceleration = 9 + acceleration = 10 + velocity = 11 + deceleration = 12 + estop_deceleration = 13 + jerk = 14 + error_threshold = 15 + proportional_gain = 16 + derivative_gain = 17 + integral_gain = 18 + integral_saturation_gain = 19 + trajectory = 20 + position_display_resolution = 21 + feedback_configuration = 22 + full_step_resolution = 23 + home = 24 + microstep_factor = 25 + acceleration_feed_forward = 26 + hardware_limit_configuration = 27 + + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mocker.patch.object(axis, 'read_setup', return_value=True) + ax_setup = axis.setup_axis( + motor_type=motor_type, + current=current, + voltage=voltage, + units=units, + encoder_resolution=encoder_resolution, + max_velocity=max_velocity, + max_base_velocity=max_base_velocity, + homing_velocity=homing_velocity, + jog_high_velocity=jog_high_velocity, + jog_low_velocity=jog_low_velocity, + max_acceleration=max_acceleration, + acceleration=acceleration, + velocity=velocity, + deceleration=deceleration, + estop_deceleration=estop_deceleration, + jerk=jerk, + error_threshold=error_threshold, + proportional_gain=proportional_gain, + derivative_gain=derivative_gain, + integral_gain=integral_gain, + integral_saturation_gain=integral_saturation_gain, + trajectory=trajectory, + position_display_resolution=position_display_resolution, + feedback_configuration=feedback_configuration, + full_step_resolution=full_step_resolution, + home=home, + microstep_factor=microstep_factor, + acceleration_feed_forward=acceleration_feed_forward, + hardware_limit_configuration=hardware_limit_configuration, + ) + assert ax_setup + + # assert mandatory calls in any order + calls_params = ( + mocker.call("QM", target=1, params=[int(motor_type)]), + mocker.call("ZB", target=1, params=[int(feedback_configuration)]), + mocker.call("FR", target=1, params=[float(full_step_resolution)]), + mocker.call("FP", target=1, + params=[int(position_display_resolution)]), + mocker.call("QI", target=1, params=[float(current)]), + mocker.call("QV", target=1, params=[float(voltage)]), + mocker.call("SN", target=1, params=[units.value]), + mocker.call("SU", target=1, params=[float(encoder_resolution)]), + mocker.call("AU", target=1, params=[float(max_acceleration)]), + mocker.call("VU", target=1, params=[float(max_velocity)]), + mocker.call("VB", target=1, params=[float(max_base_velocity)]), + mocker.call("OH", target=1, params=[float(homing_velocity)]), + mocker.call("JH", target=1, params=[float(jog_high_velocity)]), + mocker.call("JW", target=1, params=[float(jog_low_velocity)]), + mocker.call("AC", target=1, params=[float(acceleration)]), + mocker.call("VA", target=1, params=[float(velocity)]), + mocker.call("AG", target=1, params=[float(deceleration)]), + mocker.call("AE", target=1, params=[float(estop_deceleration)]), + mocker.call("JK", target=1, params=[float(jerk)]), + mocker.call("FE", target=1, params=[float(error_threshold)]), + mocker.call("KP", target=1, params=[float(proportional_gain)]), + mocker.call("KD", target=1, params=[float(derivative_gain)]), + mocker.call("KI", target=1, params=[float(integral_gain)]), + mocker.call("KS", target=1, + params=[float(integral_saturation_gain)]), + mocker.call("DH", target=1, params=[float(home)]), + mocker.call("QS", target=1, params=[float(microstep_factor)]), + mocker.call("AF", target=1, + params=[float(acceleration_feed_forward)]), + mocker.call("TJ", target=1, params=[int(trajectory)]), + mocker.call("ZH", target=1, + params=[int(hardware_limit_configuration)]), + ) + mock_cmd.assert_has_calls(calls_params, any_order=True) + + # assert final calls - in order + calls_final = ( + mocker.call("UF", target=1), + mocker.call("QD", target=1), + mocker.call("SM") + ) + mock_cmd.assert_has_calls(calls_final) + mock_cmd.assert_called_with("SM") + + +def test_axis_setup_axis_torque(mocker): + """Set up non-newport motor with torque specifications. + + Mock out `_newport_cmd` since tested elsewhere. + """ + motor_type = 2 # stepper motor + current = 1 + voltage = 2 + units = ik.newport.newportesp301.NewportESP301Units.radian + encoder_resolution = 3. + max_velocity = 4 + max_base_velocity = 5 + homing_velocity = 6 + jog_high_velocity = 7 + jog_low_velocity = 8 + max_acceleration = 9 + acceleration = 10 + velocity = 11 + deceleration = 12 + estop_deceleration = 13 + jerk = 14 + error_threshold = 15 + proportional_gain = 16 + derivative_gain = 17 + integral_gain = 18 + integral_saturation_gain = 19 + trajectory = 20 + position_display_resolution = 21 + feedback_configuration = 22 + full_step_resolution = 23 + home = 24 + microstep_factor = 25 + acceleration_feed_forward = 26 + hardware_limit_configuration = 27 + # special configs + rmt_time = 42 + rmt_perc = 13 + + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mocker.patch.object(axis, 'read_setup', return_value=True) + axis.setup_axis( + motor_type=motor_type, + current=current, + voltage=voltage, + units=units, + encoder_resolution=encoder_resolution, + max_velocity=max_velocity, + max_base_velocity=max_base_velocity, + homing_velocity=homing_velocity, + jog_high_velocity=jog_high_velocity, + jog_low_velocity=jog_low_velocity, + max_acceleration=max_acceleration, + acceleration=acceleration, + velocity=velocity, + deceleration=deceleration, + estop_deceleration=estop_deceleration, + jerk=jerk, + error_threshold=error_threshold, + proportional_gain=proportional_gain, + derivative_gain=derivative_gain, + integral_gain=integral_gain, + integral_saturation_gain=integral_saturation_gain, + trajectory=trajectory, + position_display_resolution=position_display_resolution, + feedback_configuration=feedback_configuration, + full_step_resolution=full_step_resolution, + home=home, + microstep_factor=microstep_factor, + acceleration_feed_forward=acceleration_feed_forward, + hardware_limit_configuration=hardware_limit_configuration, + reduce_motor_torque_time=rmt_time, + reduce_motor_torque_percentage=rmt_perc + ) + # ensure the torque settings are set + call_torque = ( + mocker.call("QR", target=1, params=[rmt_time, rmt_perc]), + ) + + mock_cmd.assert_has_calls(call_torque) + + +@given(rmt_time=st.integers().filter(lambda x: x < 0 or x > 60000)) +def test_axis_setup_axis_torque_time_out_of_range(mocker, rmt_time): + """Raise ValueError when time is out of range. + + Mock out `_newport_cmd` since tested elsewhere. + """ + motor_type = 2 # stepper motor + current = 1 + voltage = 2 + units = ik.newport.newportesp301.NewportESP301Units.radian + encoder_resolution = 3. + max_velocity = 4 + max_base_velocity = 5 + homing_velocity = 6 + jog_high_velocity = 7 + jog_low_velocity = 8 + max_acceleration = 9 + acceleration = 10 + velocity = 11 + deceleration = 12 + estop_deceleration = 13 + jerk = 14 + error_threshold = 15 + proportional_gain = 16 + derivative_gain = 17 + integral_gain = 18 + integral_saturation_gain = 19 + trajectory = 20 + position_display_resolution = 21 + feedback_configuration = 22 + full_step_resolution = 23 + home = 24 + microstep_factor = 25 + acceleration_feed_forward = 26 + hardware_limit_configuration = 27 + # special configs + rmt_perc = 13 + + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mocker.patch.object(axis, '_newport_cmd') + mocker.patch.object(axis, 'read_setup', return_value=True) + with pytest.raises(ValueError) as err_info: + axis.setup_axis( + motor_type=motor_type, + current=current, + voltage=voltage, + units=units, + encoder_resolution=encoder_resolution, + max_velocity=max_velocity, + max_base_velocity=max_base_velocity, + homing_velocity=homing_velocity, + jog_high_velocity=jog_high_velocity, + jog_low_velocity=jog_low_velocity, + max_acceleration=max_acceleration, + acceleration=acceleration, + velocity=velocity, + deceleration=deceleration, + estop_deceleration=estop_deceleration, + jerk=jerk, + error_threshold=error_threshold, + proportional_gain=proportional_gain, + derivative_gain=derivative_gain, + integral_gain=integral_gain, + integral_saturation_gain=integral_saturation_gain, + trajectory=trajectory, + position_display_resolution=position_display_resolution, + feedback_configuration=feedback_configuration, + full_step_resolution=full_step_resolution, + home=home, + microstep_factor=microstep_factor, + acceleration_feed_forward=acceleration_feed_forward, + hardware_limit_configuration=hardware_limit_configuration, + reduce_motor_torque_time=rmt_time, + reduce_motor_torque_percentage=rmt_perc + ) + err_msg = err_info.value.args[0] + assert err_msg == "Time must be between 0 and 60000 ms" + + +@given(rmt_perc=st.integers().filter(lambda x: x < 0 or x > 100)) +def test_axis_setup_axis_torque_percentage_out_of_range(mocker, rmt_perc): + """Raise ValueError when time is out of range. + + Mock out `_newport_cmd` since tested elsewhere. + """ + motor_type = 2 # stepper motor + current = 1 + voltage = 2 + units = ik.newport.newportesp301.NewportESP301Units.radian + encoder_resolution = 3. + max_velocity = 4 + max_base_velocity = 5 + homing_velocity = 6 + jog_high_velocity = 7 + jog_low_velocity = 8 + max_acceleration = 9 + acceleration = 10 + velocity = 11 + deceleration = 12 + estop_deceleration = 13 + jerk = 14 + error_threshold = 15 + proportional_gain = 16 + derivative_gain = 17 + integral_gain = 18 + integral_saturation_gain = 19 + trajectory = 20 + position_display_resolution = 21 + feedback_configuration = 22 + full_step_resolution = 23 + home = 24 + microstep_factor = 25 + acceleration_feed_forward = 26 + hardware_limit_configuration = 27 + # special configs + rmt_time = 42 + + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mocker.patch.object(axis, '_newport_cmd') + mocker.patch.object(axis, 'read_setup', return_value=True) + with pytest.raises(ValueError) as err_info: + axis.setup_axis( + motor_type=motor_type, + current=current, + voltage=voltage, + units=units, + encoder_resolution=encoder_resolution, + max_velocity=max_velocity, + max_base_velocity=max_base_velocity, + homing_velocity=homing_velocity, + jog_high_velocity=jog_high_velocity, + jog_low_velocity=jog_low_velocity, + max_acceleration=max_acceleration, + acceleration=acceleration, + velocity=velocity, + deceleration=deceleration, + estop_deceleration=estop_deceleration, + jerk=jerk, + error_threshold=error_threshold, + proportional_gain=proportional_gain, + derivative_gain=derivative_gain, + integral_gain=integral_gain, + integral_saturation_gain=integral_saturation_gain, + trajectory=trajectory, + position_display_resolution=position_display_resolution, + feedback_configuration=feedback_configuration, + full_step_resolution=full_step_resolution, + home=home, + microstep_factor=microstep_factor, + acceleration_feed_forward=acceleration_feed_forward, + hardware_limit_configuration=hardware_limit_configuration, + reduce_motor_torque_time=rmt_time, + reduce_motor_torque_percentage=rmt_perc + ) + err_msg = err_info.value.args[0] + assert err_msg == r"Percentage must be between 0 and 100%" + + +def test_axis_read_setup(mocker): + """Read the axis setup and return it. + + Mock out `_newport_cmd` since tested elsewhere. + """ + config = { + 'units': u.mm, + 'motor_type': ik.newport.newportesp301.NewportESP301MotorType.dc_servo, + 'feedback_configuration': 1, # last 2 removed at return + 'full_step_resolution': u.Quantity(2.0, u.mm), + 'position_display_resolution': 3, + 'current': u.Quantity(4.0, u.A), + 'max_velocity': u.Quantity(5.0, u.mm / u.s), + 'encoder_resolution': u.Quantity(6.0, u.mm), + 'acceleration': u.Quantity(7.0, u.mm / u.s**2), + 'deceleration': u.Quantity(8.0, u.mm / u.s**2), + 'velocity': u.Quantity(9.0, u.mm / u.s), + 'max_acceleration': u.Quantity(10.0, u.mm / u.s**2.), + 'homing_velocity': u.Quantity(11.0, u.mm / u.s), + 'jog_high_velocity': u.Quantity(12.0, u.mm / u.s), + 'jog_low_velocity': u.Quantity(13.0, u.mm / u.s), + 'estop_deceleration': u.Quantity(14.0, u.mm / u.s**2.), + 'jerk': u.Quantity(14.0, u.mm / u.s**3.), + 'proportional_gain': 15.0, # last 1 removed at return + 'derivative_gain': 16.0, + 'integral_gain': 17.0, + 'integral_saturation_gain': 18.0, + 'home': u.Quantity(19.0, u.mm), + 'microstep_factor': 20, + 'acceleration_feed_forward': 21.0, + 'trajectory': 22, + 'hardware_limit_configuration': 23 # last 2 removed + } + + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd.side_effect = [ + ik.newport.newportesp301.NewportESP301Units.millimeter.value, + config['motor_type'].value, + f"{config['feedback_configuration']}**", # 2 extra + config['full_step_resolution'].magnitude, + config['position_display_resolution'], + config['current'].magnitude, + config['max_velocity'].magnitude, + config['encoder_resolution'].magnitude, + config['acceleration'].magnitude, + config['deceleration'].magnitude, + config['velocity'].magnitude, + config['max_acceleration'].magnitude, + config['homing_velocity'].magnitude, + config['jog_high_velocity'].magnitude, + config['jog_low_velocity'].magnitude, + config['estop_deceleration'].magnitude, + config['jerk'].magnitude, + f"{config['proportional_gain']}*", # 1 extra + config['derivative_gain'], + config['integral_gain'], + config['integral_saturation_gain'], + config['home'].magnitude, + config['microstep_factor'], + config['acceleration_feed_forward'], + config['trajectory'], + f"{config['hardware_limit_configuration']}**" + ] + assert axis.read_setup() == config + + +def test_axis_get_status(mocker): + """Get an axis status. + + Mock out `_newport_cmd` since tested elsewhere. + """ + status = { + "units": u.mm, + "position": u.Quantity(1.0, u.mm), + "desired_position": u.Quantity(2.0, u.mm), + "desired_velocity": u.Quantity(3.0, u.mm / u.s), + "is_motion_done": True + } + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd.side_effect = [ + "2", + status["position"].magnitude, + status["desired_position"].magnitude, + status["desired_velocity"].magnitude, + "1" + ] + assert axis.get_status() == status + + +@pytest.mark.parametrize("num", ik.newport.NewportESP301Axis._unit_dict) +def test_axis_get_pq_unit(num): + """Get units for specified axis.""" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + assert axis._get_pq_unit(num) == axis._unit_dict[num] + + +@pytest.mark.parametrize("num", ik.newport.NewportESP301Axis._unit_dict) +def test_axis_get_unit_num(num): + """Get unit number from dictionary. + + Skip number 1, since u.count appears twice in dictionary! + """ + if num == 1: + num = 0 # u.count twice + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + quant = axis._unit_dict[num] + print(quant) + assert axis._get_unit_num(quant) == num + + +def test_axis_get_unit_num_invalid_unit(): + """Raise KeyError if unit not valid.""" + invalid_unit = u.ly + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" + ) as inst: + axis = inst.axis[0] + with pytest.raises(KeyError) as err_info: + axis._get_unit_num(invalid_unit) + err_msg = err_info.value.args[0] + assert err_msg == f"{invalid_unit} is not a valid unit for Newport " \ + f"Axis" + + +def test_axis_newport_cmd(mocker): + """Send command to parent class. + + Mock out parent classes `_newport_cmd` and assert call. + """ + cmd = 123 + some_keyword = "keyword" + with expected_protocol( + ik.newport.NewportESP301, + [ + ax_init[0] + ], + [ + ax_init[1] + ], + sep="\r" ) as inst: axis = inst.axis[0] - assert isinstance(axis, ik.newport.NewportESP301Axis) is True + mock_cmd = mocker.patch.object(axis._controller, '_newport_cmd') + axis._newport_cmd(cmd, some_keyword=some_keyword) + mock_cmd.assert_called_with(cmd, some_keyword=some_keyword) From cf8c81a5fe045d013f8f07647dd5825bde85dfa1 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 3 Nov 2020 10:16:03 -0500 Subject: [PATCH 071/108] Full coverage for Agilent 33220a tests (#281) Added a couple of tests for error messages and edge cases. --- .../tests/test_agilent/test_agilent_33220a.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index e9de49e9c..1c519e833 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -6,6 +6,8 @@ # IMPORTS #################################################################### +from hypothesis import given, strategies as st +import pytest from instruments.units import ureg as u @@ -166,3 +168,32 @@ def test_agilent33220a_load_resistance(): assert fg.load_resistance == fg.LoadResistance.high_impedance fg.load_resistance = 100 * u.ohm fg.load_resistance = fg.LoadResistance.maximum + + +@given(value=st.floats().filter(lambda x: x < 0 or x > 10000)) +def test_agilent33220a_load_resistance_value_invalid(value): + """Raise ValueError when resistance value loaded is out of range.""" + with expected_protocol( + ik.agilent.Agilent33220a, + [ + ], [ + ] + ) as fg: + with pytest.raises(ValueError) as err_info: + fg.load_resistance = value + err_msg = err_info.value.args[0] + assert err_msg == "Load resistance must be between 0 and 10,000" + + +def test_phase_not_implemented_error(): + """Raise a NotImplementedError when getting / setting the phase.""" + with expected_protocol( + ik.agilent.Agilent33220a, + [ + ], [ + ] + ) as fg: + with pytest.raises(NotImplementedError): + _ = fg.phase() + with pytest.raises(NotImplementedError): + fg.phase = 42 From f2da132e2cbe4a82ac88dd761cc3ad797cde1cbc Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 3 Nov 2020 11:18:03 -0500 Subject: [PATCH 072/108] GlassmanFR Tests and Typo fixes (#282) * GlassmanFR Tests and Typo fixes Completed tests for Glassman FR for full coverage. Fixed a few typos in the main routine. * Remove lint --- instruments/glassman/glassmanfr.py | 8 +- .../tests/test_glassman/test_glassmanfr.py | 135 +++++++++++++++++- 2 files changed, 137 insertions(+), 6 deletions(-) diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 7e343b219..4a4005a4b 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -110,13 +110,13 @@ class ResponseCode(Enum): """ Enum containing the possible reponse codes returned by the instrument. """ - #: A set command expects an acknoledge response (`A`) + #: A set command expects an acknowledge response (`A`) S = "A" #: A query command expects a response packet (`R`) Q = "R" #: A version query expects a different response packet (`B`) V = "B" - #: A configure command expects an acknoledge response (`A`) + #: A configure command expects an acknowledge response (`A`) C = "A" class ErrorCode(Enum): @@ -228,13 +228,13 @@ def output(self): @output.setter def output(self, newval): if not isinstance(newval, bool): - raise TypeError("Ouput status mode must be a boolean.") + raise TypeError("Output status mode must be a boolean.") self.set_status(output=newval) @property def fault(self): """ - Gets/sets the output status. + Gets the output status. Returns True if the instrument has a fault. diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index 470718e83..a99d84734 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -6,6 +6,8 @@ # IMPORTS #################################################################### +import pytest + import instruments as ik from instruments.tests import expected_protocol from instruments.units import ureg as u @@ -13,6 +15,8 @@ # TESTS ###################################################################### +# pylint: disable=protected-access + def set_defaults(inst): """ Sets default values for the voltage and current range of the Glassman FR @@ -140,6 +144,38 @@ def test_output(): assert inst.output +def test_output_type_error(): + """Raise TypeError when setting output w non-boolean value.""" + with expected_protocol( + ik.glassman.GlassmanFR, + [ + ], + [ + ], + "\r" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.output = 42 + err_msg = err_info.value.args[0] + assert err_msg == "Output status mode must be a boolean." + + +@pytest.mark.parametrize("value", [0, 2]) +def test_fault(value): + """Get the instrument status: True if fault.""" + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q51" + ], + [ + f"R000000000{value}004{value}", + ], + "\r" + ) as inst: + assert inst.fault == bool(value) + + def test_version(): with expected_protocol( ik.glassman.GlassmanFR, @@ -173,6 +209,22 @@ def test_device_timeout(): assert not inst.device_timeout +def test_device_timeout_type_error(): + """Raise TypeError if device timeout mode not set with boolean.""" + with expected_protocol( + ik.glassman.GlassmanFR, + [ + ], + [ + ], + "\r" + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.device_timeout = 42 + err_msg = err_info.value.args[0] + assert err_msg == "Device timeout mode must be a boolean." + + def test_sendcmd(): with expected_protocol( ik.glassman.GlassmanFR, @@ -186,17 +238,79 @@ def test_sendcmd(): def test_query(): + """Query the instrument.""" + response = "R123ABC5C" + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q123ABCAD" + ], + [ + response + ], + "\r" + ) as inst: + assert inst.query("Q123ABC") == response[1:-2] + + +def test_query_invalid_response_code(): + """Raise ValueError when query receives an invalid response code.""" + response = "A123ABC5C" + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q123ABCAD" + ], + [ + response + ], + "\r" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.query("Q123ABC") + err_msg = err_info.value.args[0] + assert err_msg == f"Invalid response code: {response}" + + +def test_query_invalid_checksum(): + """Raise ValueError if query returns with invalid checksum.""" + response = "R123ABC5A" + with expected_protocol( + ik.glassman.GlassmanFR, + [ + "\x01Q123ABCAD" + ], + [ + response + ], + "\r" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.query("Q123ABC") + err_msg = err_info.value.args[0] + assert err_msg == f"Invalid checksum: {response}" + + +@pytest.mark.parametrize("err", ik.glassman.GlassmanFR.ErrorCode) +def test_query_error(err): + """Raise ValueError if query returns with error.""" + err_code = err.value + check_sum = ord(err_code) % 256 + response = f"E{err_code}{format(check_sum, '02X')}" with expected_protocol( ik.glassman.GlassmanFR, [ "\x01Q123ABCAD" ], [ - "R123ABC5C" + response ], "\r" ) as inst: - inst.query("Q123ABC") + with pytest.raises(ValueError) as err_info: + inst.query("Q123ABC") + err_msg = err_info.value.args[0] + assert err_msg == f"Instrument responded with error: {err.name}" def test_reset(): @@ -231,3 +345,20 @@ def test_set_status(): assert inst.output assert inst.voltage == 10 * u.kilovolt assert inst.current == 1.2 * u.milliamp + + +def test_parse_invalid_response(): + """Raise a RunTime error if response cannot be parsed.""" + response = "000000000X00" # invalid monitors + with expected_protocol( + ik.glassman.GlassmanFR, + [ + ], + [ + ], + "\r" + ) as inst: + with pytest.raises(RuntimeError) as err_info: + inst._parse_response(response) + err_msg = err_info.value.args[0] + assert err_msg == f"Cannot parse response packet: {response}" From 9865a69130b0e8eaffb7e808fb14a7e61aedc381 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Wed, 4 Nov 2020 21:57:28 -0500 Subject: [PATCH 073/108] Full coverage tests for Minghe MHS5200a (#283) --- .../tests/test_minghe/test_minghe_mhs5200a.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index 6c289e656..27d5516c8 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -207,3 +207,31 @@ def test_mhs_serial_number(): sep="\r\n" ) as mhs: assert mhs.serial_number == "5225A1" + + +def test_mhs_get_amplitude(): + """Raise NotImplementedError when trying to get amplitude""" + with expected_protocol( + ik.minghe.MHS5200, + [ + ], + [ + ], + sep="\r\n" + ) as mhs: + with pytest.raises(NotImplementedError): + mhs._get_amplitude_() + + +def test_mhs_set_amplitude(): + """Raise NotImplementedError when trying to set amplitude""" + with expected_protocol( + ik.minghe.MHS5200, + [ + ], + [ + ], + sep="\r\n" + ) as mhs: + with pytest.raises(NotImplementedError): + mhs._set_amplitude_(1, 2) From c739ca07d7ac1f58ab4c28f617198526a93ffa2b Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 5 Nov 2020 17:47:48 -0500 Subject: [PATCH 074/108] Tests and some BFs for HP devices (#284) Full cov test suites for: - HP3456a - HP6624a - HP6632b - HP6652a - HPe3631a Bug fix: - HPe3631a: Raise an error that was previously returned Changes: - HP3456a: Remove `if self._testing` statement to not do `time.sleep` during tests. Instead use an automatically used pytest fixture to mock out `time.sleep`. --- instruments/hp/hp3456a.py | 6 +- instruments/hp/hpe3631a.py | 2 +- instruments/tests/test_hp/test_hp3456a.py | 9 +++ instruments/tests/test_hp/test_hp6624a.py | 15 +++++ instruments/tests/test_hp/test_hp6632b.py | 40 ++++++++++++++ instruments/tests/test_hp/test_hp6652a.py | 16 ++++++ instruments/tests/test_hp/test_hpe3631a.py | 64 +++++++++++++++++++--- 7 files changed, 139 insertions(+), 13 deletions(-) diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index 0de05f76b..976b09782 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -574,8 +574,7 @@ def _register_read(self, name): "HP3456a.Register, got {} " "instead.".format(name)) self.sendcmd("RE{}".format(name.value)) - if not self._testing: # pragma: no cover - time.sleep(.1) + time.sleep(.1) return float(self.query("", size=-1)) def _register_write(self, name, value): @@ -601,8 +600,7 @@ def _register_write(self, name, value): ]: raise ValueError("register {} is read only".format(name)) self.sendcmd("W{}ST{}".format(value, name.value)) - if not self._testing: # pragma: no cover - time.sleep(.1) + time.sleep(.1) def trigger(self): """ diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index d5cfdbe5d..a7ac30132 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -141,7 +141,7 @@ def mode(self): is lower than I. If the load is smaller than V/I, the set current I acts as a current limiter and the voltage is lower than V. """ - return AttributeError("The `HPe3631a` sets its mode automatically") + raise AttributeError("The `HPe3631a` sets its mode automatically") channelid = int_property( "INST:NSEL", diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index c1bd86f2c..f4fed1098 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -6,6 +6,7 @@ # IMPORTS ##################################################################### +import time import pytest @@ -18,6 +19,12 @@ # pylint: disable=protected-access +@pytest.fixture(autouse=True) +def time_mock(mocker): + """Mock out time to speed up.""" + return mocker.patch.object(time, 'sleep', return_value=None) + + def test_hp3456a_trigger_mode(): with expected_protocol( ik.hp.HP3456a, @@ -338,6 +345,8 @@ def test_hp3456a_input_range(): ) as dmm: dmm.input_range = 10 ** -1 * u.volt dmm.input_range = 1e3 * u.ohm + with pytest.raises(NotImplementedError): + _ = dmm.input_range def test_hp3456a_input_range_invalid_str(): diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 74ba00d0e..7cc40f67e 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -57,6 +57,21 @@ def test_channel_query(): assert value == "FOO" +def test_mode(): + """Raise NotImplementedError when mode is called.""" + with expected_protocol( + ik.hp.HP6624a, + [], + [], + sep="\n" + ) as hp: + channel = hp.channel[0] + with pytest.raises(NotImplementedError): + _ = channel.mode + with pytest.raises(NotImplementedError): + channel.mode = 42 + + def test_channel_voltage(): with expected_protocol( ik.hp.HP6624a, diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index 6f24c4cee..6d845af74 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -6,6 +6,7 @@ # IMPORTS ##################################################################### +import pytest from instruments.units import ureg as u @@ -326,6 +327,45 @@ def test_hp6632b_abort_output_trigger(): psu.abort_output_trigger() +def test_line_frequency(): + """Raise NotImplemented error when called.""" + with expected_protocol( + ik.hp.HP6632b, + [], + [] + ) as psu: + with pytest.raises(NotImplementedError): + psu.line_frequency = 42 + with pytest.raises(NotImplementedError): + _ = psu.line_frequency + + +def test_display_brightness(): + """Raise NotImplemented error when called.""" + with expected_protocol( + ik.hp.HP6632b, + [], + [] + ) as psu: + with pytest.raises(NotImplementedError): + psu.display_brightness = 42 + with pytest.raises(NotImplementedError): + _ = psu.display_brightness + + +def test_display_contrast(): + """Raise NotImplemented error when called.""" + with expected_protocol( + ik.hp.HP6632b, + [], + [] + ) as psu: + with pytest.raises(NotImplementedError): + psu.display_contrast = 42 + with pytest.raises(NotImplementedError): + _ = psu.display_contrast + + def test_hp6632b_check_error_queue(): with expected_protocol( ik.hp.HP6632b, diff --git a/instruments/tests/test_hp/test_hp6652a.py b/instruments/tests/test_hp/test_hp6652a.py index 713fba3b5..925fc14ec 100644 --- a/instruments/tests/test_hp/test_hp6652a.py +++ b/instruments/tests/test_hp/test_hp6652a.py @@ -6,6 +6,7 @@ # IMPORTS ##################################################################### +import pytest import instruments as ik from instruments.tests import expected_protocol @@ -27,6 +28,21 @@ def test_name(): assert hp.name == "FOO BAR" +def test_mode(): + """Raise NotImplementedError when called.""" + with expected_protocol( + ik.hp.HP6652a, + [ + ], + [ + ], + sep="\n" + ) as hp: + with pytest.raises(NotImplementedError): + _ = hp.mode + with pytest.raises(NotImplementedError): + hp.mode = 42 + def test_reset(): with expected_protocol( ik.hp.HP6652a, diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index d6e1e73af..62fff59c1 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -6,6 +6,9 @@ # IMPORTS ##################################################################### +import time + +import pytest from instruments.units import ureg as u @@ -15,6 +18,12 @@ # TESTS ####################################################################### +@pytest.fixture(autouse=True) +def time_mock(mocker): + """Mock out time such that the tests go faster.""" + return mocker.patch.object(time, 'sleep', return_value=None) + + def test_channel(): with expected_protocol( ik.hp.HPe3631a, @@ -34,6 +43,7 @@ def test_channel(): assert inst.channelid == 1 assert inst.channel[2] == inst assert inst.channelid == 2 + assert inst.channel.__len__() == len([1, 2, 3]) # len of valild set def test_channelid(): @@ -55,6 +65,22 @@ def test_channelid(): assert inst.channelid == 2 +def test_mode(): + """Raise AttributeError since instrument sets mode automatically.""" + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM" + ], + [ + ] + ) as inst: + with pytest.raises(AttributeError) as err_info: + _ = inst.mode() + err_msg = err_info.value.args[0] + assert err_msg == "The `HPe3631a` sets its mode automatically" + + def test_voltage(): with expected_protocol( ik.hp.HPe3631a, @@ -81,14 +107,36 @@ def test_voltage(): assert inst.voltage_max == 6.0 * u.volt inst.voltage = 3.0 * u.volt assert inst.voltage == 3.0 * u.volt - try: - inst.voltage = -1.0 * u.volt - except ValueError: - pass - try: - inst.voltage = 7.0 * u.volt - except ValueError: - pass + with pytest.raises(ValueError) as err_info: + newval = -1.0 * u.volt + inst.voltage = newval + err_msg = err_info.value.args[0] + assert err_msg == f"Voltage quantity is too low. Got {newval}, " \ + f"minimum value is {0.}" + with pytest.raises(ValueError) as err_info: + newval = 7.0 * u.volt + inst.voltage = newval + err_msg = err_info.value.args[0] + assert err_msg == f"Voltage quantity is too high. Got {newval}, " \ + f"maximum value is {u.Quantity(6.0, u.V)}" + + +def test_voltage_range_negative(): + """Get voltage max if negative.""" + max_volts = -6. + with expected_protocol( + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:VOLT? MAX" # 1 + ], + [ + f"{max_volts}", # 1 + ] + ) as inst: + expected_value = u.Quantity(max_volts, u.V), 0. + received_value = inst.voltage_range + assert expected_value == received_value def test_current(): From e3f27644e388bfc9c052cfdedc54c1aca4f4763f Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 12 Nov 2020 23:39:53 -0500 Subject: [PATCH 075/108] Test suite and BF for Keithley 485 (#285) Test suite now includes full coverage. Bug fixes: - Quantities error corrected - If statement for error raising corrected, previous comparison was impossible to achieve - Error catching in `_parse_measurement`. Errors were previously raised in a `try`, `except` statement, however, the wrong error messages would be displayed if these errors in fact got raised --- instruments/keithley/keithley485.py | 18 +- .../tests/test_keithley/test_keithley485.py | 221 ++++++++++++++++++ 2 files changed, 233 insertions(+), 6 deletions(-) diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index a45e359ed..ba7473ed8 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -201,7 +201,7 @@ def input_range(self, newval): raise ValueError("Only `auto` is acceptable when specifying " "the range as a string.") if isinstance(newval, u.Quantity): - newval = float(newval) + newval = float(newval.magnitude) if isinstance(newval, (float, int)): if newval in valid: @@ -334,7 +334,7 @@ def _get_status_word(self): statusword = self.query("U0X") tries -= 1 - if statusword is None: + if tries == 0: raise IOError("Could not retrieve status word") return statusword[:-1] @@ -413,10 +413,16 @@ def _parse_measurement(self, measurement): try: status = self.Status(status) - if status != self.Status.normal: - raise ValueError("Instrument not in normal mode: {}".format(status.name)) - if function != b"DC": - raise ValueError("Instrument not returning DC function: {}".format(function)) + except ValueError: + raise ValueError(f"Invalid status word in measurement: {status}") + + if status != self.Status.normal: + raise ValueError("Instrument not in normal mode: {}".format(status.name)) + + if function != b"DC": + raise ValueError("Instrument not returning DC function: {}".format(function)) + + try: current = float(current) * u.amp if base == b"A" else 10 ** (float(current)) * u.amp except: raise Exception("Cannot parse measurement: {}".format(measurement)) diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index 7228c26e8..6bade5797 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -6,6 +6,7 @@ # IMPORTS #################################################################### +import pytest from instruments.units import ureg as u @@ -15,6 +16,8 @@ # TESTS ###################################################################### +# pylint: disable=protected-access + def test_zero_check(): with expected_protocol( ik.keithley.Keithley485, @@ -29,6 +32,10 @@ def test_zero_check(): inst.zero_check = False inst.zero_check = True assert inst.zero_check + with pytest.raises(TypeError) as err_info: + inst.zero_check = 42 + err_msg = err_info.value.args[0] + assert err_msg == "Zero Check mode must be a boolean." def test_log(): @@ -45,6 +52,10 @@ def test_log(): inst.log = False inst.log = True assert inst.log + with pytest.raises(TypeError) as err_info: + inst.log = 42 + err_msg = err_info.value.args[0] + assert err_msg == "Log mode must be a boolean." def test_input_range(): @@ -77,6 +88,10 @@ def test_relative(): inst.relative = False inst.relative = True assert inst.relative + with pytest.raises(TypeError) as err_info: + inst.relative = 42 + err_msg = err_info.value.args[0] + assert err_msg == "Relative mode must be a boolean." def test_eoi_mode(): @@ -93,6 +108,10 @@ def test_eoi_mode(): inst.eoi_mode = True inst.eoi_mode = False assert not inst.eoi_mode + with pytest.raises(TypeError) as err_info: + inst.eoi_mode = 42 + err_msg = err_info.value.args[0] + assert err_msg == "EOI mode must be a boolean." def test_trigger_mode(): @@ -109,6 +128,12 @@ def test_trigger_mode(): inst.trigger_mode = "continuous_ontalk" inst.trigger_mode = "oneshot_onx" assert inst.trigger_mode == "oneshot_onx" + with pytest.raises(TypeError) as err_info: + newval = 42 + inst.trigger_mode = newval + err_msg = err_info.value.args[0] + assert err_msg == f"Drive must be specified as a " \ + f"Keithley485.TriggerMode, got {newval} instead." def test_auto_range(): @@ -125,6 +150,82 @@ def test_auto_range(): assert inst.input_range == "auto" +@pytest.mark.parametrize("newval", (2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3)) +def test_input_range_value(newval): + """Set input range with a given value from list.""" + valid = ("auto", 2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3) + with expected_protocol( + ik.keithley.Keithley485, + [ + f"R{valid.index(newval)}X" + ], [ + ] + ) as inst: + inst.input_range = newval + + +def test_input_range_quantity(): + """Set input range with a given value from list.""" + valid = ("auto", 2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3) + newval = 2e-9 + quant = u.Quantity(newval, u.A) + with expected_protocol( + ik.keithley.Keithley485, + [ + f"R{valid.index(newval)}X" + ], [ + ] + ) as inst: + inst.input_range = quant + + +def test_input_range_invalid_value(): + """Raise ValueError if invalid value is given.""" + valid = ("auto", 2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3) + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.input_range = 42 + err_msg = err_info.value.args[0] + assert err_msg == f"Valid range settings are: {valid}" + + +def test_input_range_invalid_type(): + """Raise TypeError if invalid type is given.""" + invalid_type = [42] + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(TypeError) as err_info: + inst.input_range = invalid_type + err_msg = err_info.value.args[0] + assert err_msg == f"Range setting must be specified as a float, " \ + f"int, or the string `auto`, got " \ + f"{type(invalid_type)}" + + +def test_input_range_invalid_string(): + """Raise ValueError if input range set with invalid string.""" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.input_range = "2e-9" + err_msg = err_info.value.args[0] + assert err_msg == "Only `auto` is acceptable when specifying the " \ + "range as a string." + + def test_get_status(): with expected_protocol( ik.keithley.Keithley485, @@ -150,3 +251,123 @@ def test_measure(): ) as inst: assert 1.2345 * u.nanoamp == inst.measure() assert 1 * u.nanoamp == inst.measure() + + +def test_get_status_word_fails(): + """Raise IOError if status word query fails > 5 times.""" + with expected_protocol( + ik.keithley.Keithley485, + [ + "U0X", + "U0X", + "U0X", + "U0X", + "U0X" + ], [ + "", + "", + "", + "", + "" + ] + ) as inst: + with pytest.raises(IOError) as err_info: + inst._get_status_word() + err_msg = err_info.value.args[0] + assert err_msg == "Could not retrieve status word" + + +def test_parse_status_word_wrong_prefix(): + """Raise ValueError if statusword has wrong prefix.""" + wrong_statusword = "wrong statusword" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst._parse_status_word(wrong_statusword) + err_msg = err_info.value.args[0] + assert err_msg == f"Status word starts with wrong prefix: " \ + f"{wrong_statusword}" + + +def test_parse_status_word_cannot_parse(): + """Raise RuntimeError if statusword cannot be parsed.""" + bad_statusword = "485FFFFFFFFFF" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(RuntimeError) as err_info: + inst._parse_status_word(bad_statusword) + err_msg = err_info.value.args[0] + assert err_msg == f"Cannot parse status word: {bad_statusword}" + + +def test_parse_measurement_invalid_status(): + """Raise ValueError if invalild status encountered.""" + status = "L" + bad_measurement = f"{status}DCA+1.2345E-9" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst._parse_measurement(bad_measurement) + err_msg = err_info.value.args[0] + assert err_msg == f"Invalid status word in measurement: " \ + f"{bytes(status, 'utf-8')}" + + +def test_parse_measurement_bad_status(): + """Raise ValueError if non-normal status encountered.""" + status = ik.keithley.Keithley485.Status.overflow + bad_measurement = f"{status.value.decode('utf-8')}DCA+1.2345E-9" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst._parse_measurement(bad_measurement) + err_msg = err_info.value.args[0] + assert err_msg == f"Instrument not in normal mode: {status.name}" + + +def test_parse_measurement_bad_function(): + """Raise ValueError if non-normal function encountered.""" + function = "XX" + bad_measurement = f"N{function}A+1.2345E-9" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst._parse_measurement(bad_measurement) + err_msg = err_info.value.args[0] + assert err_msg == f"Instrument not returning DC function: " \ + f"{bytes(function, 'utf-8')}" + + +def test_parse_measurement_bad_measurement(): + """Raise ValueError if non-normal function encountered.""" + bad_measurement = f"NDCA+1.23X5E-9" + with expected_protocol( + ik.keithley.Keithley485, + [ + ], [ + ] + ) as inst: + with pytest.raises(Exception) as err_info: + inst._parse_measurement(bad_measurement) + err_msg = err_info.value.args[0] + assert err_msg == f"Cannot parse measurement: {bad_measurement}" From 076e09f16c1fe61a1e6c0d5add96a9044a7dd685 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Sat, 14 Nov 2020 16:09:28 -0500 Subject: [PATCH 076/108] Full coverage for Keithley 2182 & 6220 (#286) Added and extended existing tests for these two instruments. --- .../tests/test_keithley/test_keithley2182.py | 79 +++++++++++++++++++ .../tests/test_keithley/test_keithley6220.py | 23 ++++++ 2 files changed, 102 insertions(+) diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 5894f9ad8..d92c85a24 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -34,6 +34,70 @@ def test_channel_mode(): ) as inst: channel = inst.channel[0] assert channel.mode == inst.Mode.voltage_dc + with pytest.raises(NotImplementedError): + channel.mode = 42 + + +def test_channel_trigger_mode(): + """Raise NotImplementedError when getting / setting trigger mode.""" + with expected_protocol( + ik.keithley.Keithley2182, + [ + ], + [ + ] + ) as inst: + channel = inst.channel[0] + with pytest.raises(NotImplementedError): + _ = channel.trigger_mode + with pytest.raises(NotImplementedError): + channel.trigger_mode = 42 + + +def test_channel_relative(): + """Raise NotImplementedError when getting / setting relative.""" + with expected_protocol( + ik.keithley.Keithley2182, + [ + ], + [ + ] + ) as inst: + channel = inst.channel[0] + with pytest.raises(NotImplementedError): + _ = channel.relative + with pytest.raises(NotImplementedError): + channel.relative = 42 + + +def test_channel_input_range(): + """Raise NotImplementedError when getting / setting input range.""" + with expected_protocol( + ik.keithley.Keithley2182, + [ + ], + [ + ] + ) as inst: + channel = inst.channel[0] + with pytest.raises(NotImplementedError): + _ = channel.input_range + with pytest.raises(NotImplementedError): + channel.input_range = 42 + + +def test_channel_measure_mode_not_none(): + """Raise NotImplementedError measuring with non-None mode.""" + with expected_protocol( + ik.keithley.Keithley2182, + [ + ], + [ + ] + ) as inst: + channel = inst.channel[0] + with pytest.raises(NotImplementedError): + channel.measure(mode="Some Mode") def test_channel_measure_voltage(): @@ -225,3 +289,18 @@ def test_relative_set_wrong_type(): [] ) as inst: inst.relative = "derp" + + +def test_input_range(): + """Raise NotImplementedError when getting / setting input range.""" + with expected_protocol( + ik.keithley.Keithley2182, + [ + ], + [ + ] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.input_range + with pytest.raises(NotImplementedError): + inst.input_range = 42 diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index a2e5cc70d..d68b79597 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -7,6 +7,8 @@ # IMPORTS ##################################################################### +import pytest + from instruments.units import ureg as u import instruments as ik @@ -20,6 +22,27 @@ def test_channel(): assert inst.channel[0] == inst +def test_voltage(): + """Raise NotImplementedError when getting / setting voltage.""" + with expected_protocol( + ik.keithley.Keithley6220, + [ + ], + [ + ] + ) as inst: + with pytest.raises(NotImplementedError) as err_info: + _ = inst.voltage + err_msg = err_info.value.args[0] + assert err_msg == "The Keithley 6220 does not support voltage " \ + "settings." + with pytest.raises(NotImplementedError) as err_info: + inst.voltage = 42 + err_msg = err_info.value.args[0] + assert err_msg == "The Keithley 6220 does not support voltage " \ + "settings." + + def test_current(): with expected_protocol( ik.keithley.Keithley6220, From 424348baed7fc857f7156e70fab3425b82126a97 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 15 Nov 2020 15:57:42 -0500 Subject: [PATCH 077/108] Add support for CPython 3.9 (#277) * Add support for CPython 3.9 * Add py39 to tox envlist * Bypass pylint on py39 --- .travis.yml | 5 +++-- README.rst | 2 +- setup.py | 1 + tox.ini | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bf23f02c..99b13b253 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "3.6" - "3.7" - "3.8" + - "3.9" install: - "pip install -r requirements.txt" - "pip install -r dev-requirements.txt" @@ -24,7 +25,7 @@ before_script: - pylint --version script: - pytest --cov=instruments - - pylint --disable=I,R instruments + - if [[ $TRAVIS_PYTHON_VERSION != 3.9 ]]; then pylint --disable=I,R instruments; fi after_success: - coveralls deploy: @@ -34,4 +35,4 @@ deploy: distributions: "sdist bdist_wheel" on: tags: true - condition: "$TRAVIS_PYTHON_VERSION == 3.8" + condition: "$TRAVIS_PYTHON_VERSION == 3.9" diff --git a/README.rst b/README.rst index 396316947..10b888dbb 100644 --- a/README.rst +++ b/README.rst @@ -111,7 +111,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 3.6, 3.7, and 3.8 are supported. Should you encounter +At this time, Python 3.6, 3.7, 3.8, and 3.9 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. diff --git a/setup.py b/setup.py index c9febed31..d04d8ab0a 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Operating System :: OS Independent", "License :: OSI Approved :: GNU Affero General Public License v3", "Intended Audience :: Science/Research", diff --git a/tox.ini b/tox.ini index d1bd79109..2125710e9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38 +envlist = py36,py37,py38,py39 [testenv] deps = -rdev-requirements.txt commands = pytest From 515d62e9a47b89573452c823a7b3402ed44b42eb Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 16 Nov 2020 18:33:33 -0500 Subject: [PATCH 078/108] Tests and BF for Qubitek CC1 (#290) Full coverage test suite BF: - The `try` statement in the property getter for `count` did not work as expected and was exempted from coverage. Removed the exemption and corrected the statement. There was a `FIXME` comment attached to it, should be fixed now. --- instruments/qubitekk/cc1.py | 13 ++++-- .../tests/test_qubitekk/test_qubitekk_cc1.py | 41 ++++++++++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index d4f065e7d..bc7dc7570 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -116,18 +116,23 @@ def count(self): :rtype: `int` """ count = self._cc1.query("COUN:{0}?".format(self._chan)) - # FIXME: Does this property actually work? The try block seems - # wrong. + tries = 5 try: count = int(count) - except ValueError: # pragma: no cover + except ValueError: count = None - while count is None: + while count is None and tries > 0: # try to read again try: count = int(self._cc1.read(-1)) except ValueError: count = None + tries -= 1 + + if tries == 0: + raise IOError(f"Could not read the count of channel " + f"{self._chan}.") + self._count = count return self._count diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index 3503fd850..9f9a963db 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -6,7 +6,7 @@ # IMPORTS #################################################################### - +from io import BytesIO import pytest from instruments.units import ureg as u @@ -17,6 +17,19 @@ # TESTS ###################################################################### +def test_init_os_error(mocker): + """Initialize with acknowledgements already turned off. + + This raises an OSError in the read which must pass without an issue. + """ + stdout = BytesIO(":ACKN OF\nFIRM?\n".encode("utf-8")) + stdin = BytesIO("Firmware v2.010\n".encode("utf-8")) + mock_read = mocker.patch.object(ik.qubitekk.CC1, 'read') + mock_read.side_effect = OSError + _ = ik.qubitekk.CC1.open_test(stdin, stdout) + mock_read.assert_called_with(-1) + + def test_cc1_count(): with expected_protocol( ik.qubitekk.CC1, @@ -35,6 +48,32 @@ def test_cc1_count(): assert cc.channel[0].count == 20.0 +def test_cc1_count_valule_error(): + with expected_protocol( + ik.qubitekk.CC1, + [ + ":ACKN OF", + "FIRM?", + "COUN:C1?" + ], + [ + "", + "Firmware v2.010", + "bad_count", + "try1" + "try2" + "try3" + "try4" + "try5" + ], + sep="\n" + ) as cc: + with pytest.raises(IOError) as err_info: + _ = cc.channel[0].count + err_msg = err_info.value.args[0] + assert err_msg == "Could not read the count of channel C1." + + def test_cc1_window(): with expected_protocol( ik.qubitekk.CC1, From cb1e4ec99903656f20052d3d2ee71e23bc8d8eab Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 16 Nov 2020 22:41:46 -0500 Subject: [PATCH 079/108] Full coverage for test suites of FSW 0020 (#291) Adding tests for functions that are not implemented to get to full coverage --- .../test_phasematrix_fsw0020.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index 6c5487a6c..d683b09c3 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -7,6 +7,8 @@ # IMPORTS ##################################################################### +import pytest + from instruments.units import ureg as u import instruments as ik @@ -56,6 +58,21 @@ def test_power(): inst.power = u.Quantity(10, u.dBm) +def test_phase(): + """Raise NotImplementedError when phase is set / got.""" + with expected_protocol( + ik.phasematrix.PhaseMatrixFSW0020, + [ + ], + [ + ] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.phase + with pytest.raises(NotImplementedError): + inst.phase = 42 + + def test_blanking(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, @@ -67,6 +84,8 @@ def test_blanking(): ) as inst: inst.blanking = True inst.blanking = False + with pytest.raises(NotImplementedError): + _ = inst.blanking def test_ref_output(): @@ -80,6 +99,8 @@ def test_ref_output(): ) as inst: inst.ref_output = True inst.ref_output = False + with pytest.raises(NotImplementedError): + _ = inst.ref_output def test_output(): @@ -93,6 +114,8 @@ def test_output(): ) as inst: inst.output = True inst.output = False + with pytest.raises(NotImplementedError): + _ = inst.output def test_pulse_modulation(): @@ -106,6 +129,8 @@ def test_pulse_modulation(): ) as inst: inst.pulse_modulation = True inst.pulse_modulation = False + with pytest.raises(NotImplementedError): + _ = inst.pulse_modulation def test_am_modulation(): @@ -119,3 +144,5 @@ def test_am_modulation(): ) as inst: inst.am_modulation = True inst.am_modulation = False + with pytest.raises(NotImplementedError): + _ = inst.am_modulation From 107cd0f9961b558cf2a9a1680d3d09b67a665122 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 16 Nov 2020 22:44:35 -0500 Subject: [PATCH 080/108] Test for full coverage of Fluke3000 (#292) * Full coverage for test suites of FSW 0020 Adding tests for functions that are not implemented to get to full coverage * Final test for full coverage of Fluke 3000 --- instruments/tests/test_fluke/test_fluke3000.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index 0910df493..b064a0410 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -421,3 +421,20 @@ def test_parse_wrong_mode(): f"the Fluke 3000FC Multimeter is in mode " \ f"{mode_result.name} instead. Could not read the " \ f"requested quantity." + + +def test_parse_factor_wrong_code(): + """Raise ValueError if code not in prefixes.""" + data = "00000012" + byte = format(int(data[6:8], 16), "08b") + code = int(byte[1:4], 2) + with expected_protocol( + ik.fluke.Fluke3000, + init_sequence, + init_response, + "\r" + ) as inst: + with pytest.raises(ValueError) as err_info: + inst._parse_factor(data) + err_msg = err_info.value.args[0] + assert err_msg == f"Metric prefix not recognized: {code}" From 5b2fba7139e5a68e35aab893e90645f37f7ce806 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Mon, 16 Nov 2020 23:14:22 -0500 Subject: [PATCH 081/108] Use tox in travis (#289) * Use tox in travis * Remove old `python` block * Use all available cores for linting * Use all available cores for pytest --- .travis.yml | 46 ++++++++++++++++++++++++++++------------------ tox.ini | 18 +++++++++++++++--- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 99b13b253..bd3712bbf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,41 @@ dist: xenial sudo: false language: python -python: - - "3.6" - - "3.7" - - "3.8" - - "3.9" +stages: +- prechecks +- tests-cpython +jobs: + include: + - stage: prechecks + python: 3.6 + env: TOXENV=pylint + - stage: tests-cpython + python: 3.6 + env: TOXENV=py36 + - python: 3.7 + env: TOXENV=py37 + - python: 3.8 + env: TOXENV=py38 + - python: 3.9 + env: TOXENV=py39 +before_install: +- python --version +- uname -a +- lsb_release -a install: - - "pip install -r requirements.txt" - - "pip install -r dev-requirements.txt" - - pip install coveralls - - pip install coverage - - pip install pytest-cov + - pip install -U setuptools + - pip install tox coverage coveralls before_script: # We use before_script to report version and path information in a way # that can be easily hidden by Travis' log folding. Moreover, a nonzero # exit code from this block kills the entire job, meaning that if we can't # even sensibly get version information, we correctly abort. - - which python - - python --version - - which pytest - - pytest --version - - which pylint - - pylint --version + - virtualenv --version + - pip --version + - tox --version + - coverage --version script: - - pytest --cov=instruments - - if [[ $TRAVIS_PYTHON_VERSION != 3.9 ]]; then pylint --disable=I,R instruments; fi + - tox after_success: - coveralls deploy: diff --git a/tox.ini b/tox.ini index 2125710e9..de0e7cb21 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,17 @@ [tox] -envlist = py36,py37,py38,py39 +envlist = py36,py37,py38,py39,pylint + [testenv] -deps = -rdev-requirements.txt -commands = pytest +deps = + -rdev-requirements.txt + pytest-cov + pytest-xdist + coverage +commands = pytest -n auto --cov=instruments {toxinidir}/instruments/tests/ {posargs:} + +[testenv:pylint] +basepython = python3 +deps = + -rdev-requirements.txt +commands = + pylint -j 0 --disable=I,R {toxinidir}/instruments From ad07c61f3bb4fd40add652abf1e2ae13e1bfe0da Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 17 Nov 2020 17:31:22 -0500 Subject: [PATCH 082/108] Tests for SRS DG645 and 830 (#293) * Tests for SRS DG645 and 830 - Full coverage test suite for SRS instruments - Removed `# pragma: no cover` statements and tested these lines - Removed `if not self._testing` to invoke a `time.sleep`, which is now simply mocked out in the test using an automatically used fixture. * Improve readability Changed repetitive list entries -> list multiplication --- instruments/srs/srs830.py | 11 ++- instruments/tests/test_srs/test_srs830.py | 98 +++++++++++++++++++++ instruments/tests/test_srs/test_srsdg645.py | 11 ++- 3 files changed, 113 insertions(+), 7 deletions(-) diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index 7fba17032..d1b5f2a65 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -47,7 +47,7 @@ class SRS830(SCPIInstrument): >>> data = srs.take_measurement(1, 10) # 1Hz sample rate, 10 samples total """ - def __init__(self, filelike, outx_mode=None): # pragma: no cover + def __init__(self, filelike, outx_mode=None): """ Class initialization method. @@ -70,7 +70,7 @@ def __init__(self, filelike, outx_mode=None): # pragma: no cover pass else: warnings.warn("OUTX command has not been set. Instrument " - "behavour is unknown.", UserWarning) + "behaviour is unknown.", UserWarning) # ENUMS # class FreqSource(IntEnum): @@ -251,7 +251,7 @@ def num_data_points(self): while not resp and i < 10: resp = self.query('SPTS?').strip() i += 1 - if not resp: # pragma: no cover + if not resp: raise IOError( "Expected integer response from instrument, got {}".format( repr(resp)) @@ -363,8 +363,7 @@ def take_measurement(self, sample_rate, num_samples): self.init(sample_rate, SRS830.BufferMode['one_shot']) self.start_data_transfer() - if not self._testing: - time.sleep(sample_time + 0.1) + time.sleep(sample_time + 0.1) self.pause() @@ -374,7 +373,7 @@ def take_measurement(self, sample_rate, num_samples): # in future versions. try: self.num_data_points - except IOError: # pragma: no cover + except IOError: pass ch1 = self.read_data_buffer('ch1') diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index 8d9729e07..ec168e02b 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -6,17 +6,63 @@ # IMPORTS ##################################################################### +import time import numpy as np import pytest +import serial import instruments as ik +from instruments.abstract_instruments.comm import GPIBCommunicator, \ + LoopbackCommunicator, SerialCommunicator, USBCommunicator from instruments.tests import expected_protocol from instruments.units import ureg as u # TESTS ####################################################################### +@pytest.fixture(autouse=True) +def time_mock(mocker): + """Mock out sleep such that test runs fast.""" + return mocker.patch.object(time, 'sleep', return_value=None) + + +@pytest.mark.parametrize("mode", (1, 2)) +def test_init_mode_given(mocker, mode): + """Test initialization with a given mode.""" + comm = LoopbackCommunicator() + send_spy = mocker.spy(comm, 'sendcmd') + ik.srs.SRS830(comm, outx_mode=mode) + send_spy.assert_called_with(f"OUTX {mode}") + + +def test_init_mode_gpibcomm(mocker): + """Test initialization with GPIBCommunicator""" + mock_gpib = mocker.MagicMock() + comm = GPIBCommunicator(mock_gpib, 1) + mock_send = mocker.patch.object(comm, 'sendcmd') + ik.srs.SRS830(comm) + mock_send.assert_called_with("OUTX 1") + + +def test_init_mode_serial_comm(mocker): + """Test initialization with SerialCommunicator""" + comm = SerialCommunicator(serial.Serial()) + mock_send = mocker.patch.object(comm, 'sendcmd') + ik.srs.SRS830(comm) + mock_send.assert_called_with("OUTX 2") + + +def test_init_mode_invalid(): + """Test initialization with invalild communicator.""" + comm = USBCommunicator(None) + with pytest.warns(UserWarning) as wrn_info: + ik.srs.SRS830(comm) + wrn_msg = wrn_info[0].message.args[0] + assert wrn_msg == "OUTX command has not been set. Instrument behaviour " \ + "is unknown." + + def test_frequency_source(): with expected_protocol( ik.srs.SRS830, @@ -164,6 +210,21 @@ def test_num_data_points(): assert inst.num_data_points == 5 +def test_num_data_points_no_answer(): + """Raise IOError after no answer 10 times.""" + answer = "" + with expected_protocol( + ik.srs.SRS830, + ["SPTS?"] * 10, + [answer] * 10 + ) as inst: + with pytest.raises(IOError) as err_info: + _ = inst.num_data_points + err_msg = err_info.value.args[0] + assert err_msg == f"Expected integer response from instrument, got " \ + f"{repr(answer)}" + + def test_data_transfer(): with expected_protocol( ik.srs.SRS830, @@ -267,6 +328,43 @@ def test_take_measurement(): np.testing.assert_array_equal(resp, [[1.234, 5.678], [0.456, 5.321]]) +def test_take_measurement_num_dat_points_fails(): + """Simulate the failure of num_data_points. + + This is the way it is currently implemented. + """ + with expected_protocol( + ik.srs.SRS830, + [ + "REST", + "SRAT 4", + "SEND 0", + "FAST 2", + "STRD", + "PAUS" + ] + + [ + "SPTS?" + ] * 11 + + [ + "TRCA?1,0,2", + "SPTS?", + "TRCA?2,0,2" + ], + [ + "", + ] * 10 + + [ + "2", + "1.234,5.678", + "2", + "0.456,5.321" + ] + ) as inst: + resp = inst.take_measurement(sample_rate=1, num_samples=2) + np.testing.assert_array_equal(resp, [[1.234, 5.678], [0.456, 5.321]]) + + def test_take_measurement_invalid_num_samples(): with pytest.raises(ValueError), expected_protocol( ik.srs.SRS830, diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index a235e1f0a..a652c006e 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -9,12 +9,13 @@ import pytest import instruments as ik +from instruments.abstract_instruments.comm import GPIBCommunicator from instruments.units import ureg as u from instruments.tests import expected_protocol, make_name_test, unit_eq # TESTS ###################################################################### -# pylint: disable=protected-access +# pylint: disable=no-member,protected-access test_srsdg645_name = make_name_test(ik.srs.SRSDG645) @@ -66,6 +67,14 @@ def test_srsdg645_channel_delay(): # DG645 # +def test_srsdg645_init_gpib(mocker): + """Initialize SRSDG645 with GPIB Communicator.""" + mock_gpib = mocker.MagicMock() + comm = GPIBCommunicator(mock_gpib, 1) + ik.srs.SRSDG645(comm) + assert comm.strip == 2 + + def test_srsdg645_output_level(): """ SRSDG645: Checks getting/setting unitful output level. From c3655f72a5d838f273c9ac3a8e29137184310bd5 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 21 Nov 2020 11:23:29 -0500 Subject: [PATCH 083/108] Remove numpy as hard requirement (#279) * Move numpy to be an optional requirement * Make numpy optional for Instrument.binblockread * Make numpy optional for instruments, tests pass with numpy installed * Tests pass with and without numpy installed * Fix linting * Remove numpy from INSTALL_REQUIRES * Add numpy variants to tox * Update tox * Refactor try except blocks for importing numpy * Add py39-numpy toxenv to travis * add test for new awg2000 lines * Update docstrings and return types * Add and update tests * Update README.rst --- .travis.yml | 2 + README.rst | 6 +- instruments/__init__.py | 3 +- .../abstract_instruments/instrument.py | 7 +- instruments/agilent/agilent34410a.py | 31 +++- instruments/hp/hp6624a.py | 20 +-- instruments/keithley/keithley2182.py | 16 +- instruments/optional_dep_finder.py | 13 ++ instruments/srs/srs830.py | 20 +-- instruments/srs/srsctc100.py | 20 ++- instruments/tektronix/tekawg2000.py | 11 +- instruments/tektronix/tekdpo4104.py | 19 ++- instruments/tektronix/tekdpo70000.py | 41 +++-- instruments/tektronix/tektds224.py | 24 +-- instruments/tektronix/tektds5xx.py | 33 ++-- instruments/teledyne/maui.py | 30 ++-- instruments/tests/__init__.py | 18 +++ .../tests/test_agilent/test_agilent_34410a.py | 28 +++- instruments/tests/test_base_instrument.py | 16 +- instruments/tests/test_comm/test_usbtmc.py | 6 +- instruments/tests/test_hp/test_hp6624a.py | 18 ++- .../tests/test_keithley/test_keithley2182.py | 16 +- .../tests/test_rigol/test_rigolds1000.py | 24 +-- instruments/tests/test_srs/test_srs345.py | 11 +- instruments/tests/test_srs/test_srs830.py | 41 +++-- instruments/tests/test_srs/test_srsctc100.py | 22 ++- .../tests/test_tektronix/test_tekawg2000.py | 22 ++- .../tests/test_tektronix/test_tekdpo4104.py | 36 +++-- .../tests/test_tektronix/test_tekdpo70000.py | 95 +++++++----- .../test_tektronix/test_tektronix_tds224.py | 29 ++-- .../tests/test_tektronix/test_tktds5xx.py | 40 +++-- instruments/tests/test_teledyne/test_maui.py | 83 ++++++---- instruments/tests/test_test_utils.py | 144 ++++++++++++++++++ .../tests/test_yokogawa/test_yokogawa_6370.py | 30 ++-- instruments/yokogawa/yokogawa6370.py | 4 +- requirements.txt | 1 - setup.py | 14 +- tox.ini | 3 +- 38 files changed, 715 insertions(+), 282 deletions(-) create mode 100644 instruments/optional_dep_finder.py create mode 100644 instruments/tests/test_test_utils.py diff --git a/.travis.yml b/.travis.yml index bd3712bbf..c860dbc25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,8 @@ jobs: env: TOXENV=py38 - python: 3.9 env: TOXENV=py39 + - python: 3.9 + env: TOXENV=py39-numpy before_install: - python --version - uname -a diff --git a/README.rst b/README.rst index 10b888dbb..5ebab1153 100644 --- a/README.rst +++ b/README.rst @@ -131,10 +131,8 @@ existing classes which are similar to your work to learn more about the structure of this project. To run the tests against all supported version of Python, you will need to -have the binary for each installed, as well as any requirements needed to -install ``numpy`` under each Python version. On Debian/Ubuntu systems this means -you will need to install the ``python-dev`` package for each version of Python -supported (``python3.8-dev``, etc). +have the binary for each installed. The easiest way to accomplish this is +to use the tool `pyenv_`. With the required system packages installed, all tests can be run with ``tox``: diff --git a/instruments/__init__.py b/instruments/__init__.py index 36cf43f9b..a7cd417b4 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -35,9 +35,8 @@ from . import toptica from . import yokogawa -from .units import ureg as units - from .config import load_instruments +from .units import ureg as units # VERSION METADATA ########################################################### # In keeping with PEP-396, we define a version number of the form diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index ac0a96da0..1ca869d5d 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -10,11 +10,11 @@ import os import collections import socket +import struct import urllib.parse as parse from serial import SerialException from serial.tools.list_ports import comports -import numpy as np import pyvisa import usb import usb.core @@ -25,6 +25,7 @@ LoopbackCommunicator, GPIBCommunicator, AbstractCommunicator, USBTMCCommunicator, VXI11Communicator, serial_manager ) +from instruments.optional_dep_finder import numpy from instruments.errors import AcknowledgementError, PromptError # CONSTANTS ################################################################### @@ -285,7 +286,9 @@ def binblockread(self, data_width, fmt=None): raise IOError("Did not read in the required number of bytes" "during binblock read. Got {}, expected " "{}".format(len(data), num_of_bytes)) - return np.frombuffer(data, dtype=fmt) + if numpy: + return numpy.frombuffer(data, dtype=fmt) + return struct.unpack(f"{fmt[0]}{int(len(data)/data_width)}{fmt[-1]}", data) # CLASS METHODS # diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 2f395c56e..01f75d257 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -7,6 +7,7 @@ # IMPORTS ##################################################################### from instruments.generic_scpi import SCPIMultimeter +from instruments.optional_dep_finder import numpy from instruments.units import ureg as u # CLASSES ##################################################################### @@ -78,7 +79,8 @@ def r(self, count): :param int count: Number of samples to take. - :rtype: `~pint.Quantity` with `numpy.array` + :rtype: `tuple`[`~pint.Quantity`, ...] + or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ mode = self.mode units = UNITS[mode] @@ -91,7 +93,9 @@ def r(self, count): self.sendcmd('FORM:DATA REAL,64') self.sendcmd(msg) data = self.binblockread(8, fmt=">d") - return data * units + if numpy: + return data * units + return tuple(val * units for val in data) # DATA READING METHODS # @@ -107,10 +111,14 @@ def fetch(self): recommended to transfer a large number of data points using this method. - :rtype: `list` of `~pint.Quantity` elements + :rtype: `tuple`[`~pint.Quantity`, ...] + or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ units = UNITS[self.mode] - return list(map(float, self.query('FETC?').split(','))) * units + data = list(map(float, self.query('FETC?').split(','))) + if numpy: + return data * units + return tuple(val * units for val in data) def read_data(self, sample_count): """ @@ -123,7 +131,8 @@ def read_data(self, sample_count): output buffer. If set to -1, all points in memory will be transfered. - :rtype: `list` of `~pint.Quantity` elements + :rtype: `tuple`[`~pint.Quantity`, ...] + or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ if not isinstance(sample_count, int): raise TypeError('Parameter "sample_count" must be an integer.') @@ -133,17 +142,23 @@ def read_data(self, sample_count): units = UNITS[self.mode] self.sendcmd('FORM:DATA ASC') data = self.query('DATA:REM? {}'.format(sample_count)).split(',') - return list(map(float, data)) * units + data = list(map(float, data)) + if numpy: + return data * units + return tuple(val * units for val in data) def read_data_nvmem(self): """ Returns all readings in non-volatile memory (NVMEM). - :rtype: `list` of `~pint.Quantity` elements + :rtype: `tuple`[`~pint.Quantity`, ...] + or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ units = UNITS[self.mode] data = list(map(float, self.query('DATA:DATA? NVMEM').split(','))) - return data * units + if numpy: + return data * units + return tuple(val * units for val in data) def read_last_data(self): """ diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index cdcbc3c2a..9f65fb44d 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -248,11 +248,11 @@ def voltage(self): :units: As specified (if a `~pint.Quantity`) or assumed to be of units Volts. - :type: `list` of `~pint.Quantity` with units Volt + :type: `tuple`[`~pint.Quantity`, ...] with units Volt """ - return [ + return tuple([ self.channel[i].voltage for i in range(self.channel_count) - ] + ]) @voltage.setter def voltage(self, newval): @@ -274,11 +274,11 @@ def current(self): :units: As specified (if a `~pint.Quantity`) or assumed to be of units Amps. - :type: `list` of `~pint.Quantity` with units Amp + :type: `tuple`[`~pint.Quantity`, ...] with units Amp """ - return [ + return tuple([ self.channel[i].current for i in range(self.channel_count) - ] + ]) @current.setter def current(self, newval): @@ -299,9 +299,9 @@ def voltage_sense(self): Gets the actual voltage as measured by the sense wires for all channels. :units: :math:`\\text{V}` (volts) - :rtype: `tuple` of `~pint.Quantity` + :rtype: `tuple`[`~pint.Quantity`, ...] """ - return ( + return tuple( self.channel[i].voltage_sense for i in range(self.channel_count) ) @@ -311,9 +311,9 @@ def current_sense(self): Gets the actual current as measured by the instrument for all channels. :units: :math:`\\text{A}` (amps) - :rtype: `tuple` of `~pint.Quantity` + :rtype: `tuple`[`~pint.Quantity`, ...] """ - return ( + return tuple( self.channel[i].current_sense for i in range(self.channel_count) ) diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index c9d7afd5d..9cc1d543f 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -7,10 +7,11 @@ # IMPORTS ##################################################################### from enum import Enum -from instruments.units import ureg as u -from instruments.generic_scpi import SCPIMultimeter from instruments.abstract_instruments import Multimeter +from instruments.generic_scpi import SCPIMultimeter +from instruments.optional_dep_finder import numpy +from instruments.units import ureg as u from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -26,7 +27,7 @@ class Keithley2182(SCPIMultimeter): >>> import instruments as ik >>> meter = ik.keithley.Keithley2182.open_gpibusb("/dev/ttyUSB0", 10) - >>> print meter.measure(meter.Mode.voltage_dc) + >>> print(meter.measure(meter.Mode.voltage_dc)) """ @@ -212,9 +213,14 @@ def fetch(self): recommended to transfer a large number of data points using GPIB. :return: Measurement readings from the instrument output buffer. - :rtype: `list` of `~pint.Quantity` elements + :rtype: `tuple`[`~pint.Quantity`, ...] + or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ - return list(map(float, self.query("FETC?").split(","))) * self.units + data = list(map(float, self.query("FETC?").split(","))) + unit = self.units + if numpy: + return data * unit + return tuple(d * unit for d in data) def measure(self, mode=None): """ diff --git a/instruments/optional_dep_finder.py b/instruments/optional_dep_finder.py new file mode 100644 index 000000000..f73ac0e6d --- /dev/null +++ b/instruments/optional_dep_finder.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +""" +Small module to obtain handles to optional dependencies +""" + +# pylint: disable=unused-import +try: + import numpy + _numpy_installed = True +except ImportError: + numpy = None + _numpy_installed = False diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index d1b5f2a65..f9b76556a 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -12,14 +12,13 @@ import warnings from enum import Enum, IntEnum -import numpy as np - from instruments.abstract_instruments.comm import ( GPIBCommunicator, SerialCommunicator, LoopbackCommunicator ) from instruments.generic_scpi import SCPIInstrument +from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import ( bool_property, bounded_unitful_property, enum_property, unitful_property @@ -353,7 +352,8 @@ def take_measurement(self, sample_rate, num_samples): :param `int` num_samples: Number of samples to take. - :rtype: `list` + :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] + or if numpy is installed, `numpy.array`[`numpy.array`, `numpy.array`] """ if num_samples > 16383: raise ValueError('Number of samples cannot exceed 16383.') @@ -379,7 +379,9 @@ def take_measurement(self, sample_rate, num_samples): ch1 = self.read_data_buffer('ch1') ch2 = self.read_data_buffer('ch2') - return np.array([ch1, ch2]) + if numpy: + return numpy.array([ch1, ch2]) + return ch1, ch2 # OTHER METHODS # @@ -500,7 +502,7 @@ def read_data_buffer(self, channel): given by {CH1|CH2}. :type channel: `SRS830.Mode` or `str` - :rtype: `list` + :rtype: `tuple`[`float`, ...] or if numpy is installed, `numpy.array` """ if isinstance(channel, str): channel = channel.lower() @@ -516,10 +518,10 @@ def read_data_buffer(self, channel): # Query device for entire buffer, returning in ASCII, then # converting to a list of floats before returning to the # calling method - return np.fromstring( - self.query('TRCA?{},0,{}'.format(channel, N)).strip(), - sep=',' - ) + data = self.query('TRCA?{},0,{}'.format(channel, N)).strip() + if numpy: + return numpy.fromstring(data, sep=',') + return tuple(map(float, data.split(","))) def clear_data_buffer(self): """ diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 5dd47b068..65b45e58f 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -9,9 +9,8 @@ from contextlib import contextmanager from enum import Enum -import numpy as np - from instruments.generic_scpi import SCPIInstrument +from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import ProxyList @@ -245,8 +244,9 @@ def get_log(self): :return: Tuple of all the log data points. First value is time, second is the measurement value. - :rtype: Tuple of 2x `~pint.Quantity`, each comprised of - a numpy array (`numpy.dnarray`). + :rtype: If numpy is installed, tuple of 2x `~pint.Quantity`, + each comprised of a numpy array (`numpy.dnarray`). + Else, `tuple`[`tuple`[`~pint.Quantity`, ...], `tuple`[`~pint.Quantity`, ...]] """ # Remember the current units. units = self.units @@ -257,8 +257,12 @@ def get_log(self): # Make an empty quantity that size for the times and for the channel # values. - ts = u.Quantity(np.empty((n_points,)), 'ms') - temps = u.Quantity(np.empty((n_points,)), units) + if numpy: + ts = u.Quantity(numpy.empty((n_points,)), u.ms) + temps = u.Quantity(numpy.empty((n_points,)), units) + else: + ts = [u.Quantity(0, u.ms)] * n_points + temps = [u.Quantity(0, units)] * n_points # Reset the position to the first point, then save it. # pylint: disable=protected-access @@ -271,6 +275,10 @@ def get_log(self): if self._ctc.error_check_toggle: self._ctc.errcheck() + if not numpy: + ts = tuple(ts) + temps = tuple(temps) + return ts, temps # PRIVATE METHODS ## diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index 9a541d32a..ccf7cb7b7 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -8,9 +8,8 @@ from enum import Enum -import numpy as np - from instruments.generic_scpi import SCPIInstrument +from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import assume_units, ProxyList @@ -231,6 +230,10 @@ def upload_waveform(self, yzero, ymult, xincr, waveform): that all absolute values contained within the array should not exceed 1. """ + if numpy is None: + raise ImportError("Missing optional dependency numpy, which is required" + "for uploading waveforms.") + if not isinstance(yzero, float) and not isinstance(yzero, int): raise TypeError("yzero must be specified as a float or int") @@ -240,10 +243,10 @@ def upload_waveform(self, yzero, ymult, xincr, waveform): if not isinstance(xincr, float) and not isinstance(xincr, int): raise TypeError("xincr must be specified as a float or int") - if not isinstance(waveform, np.ndarray): + if not isinstance(waveform, numpy.ndarray): raise TypeError("waveform must be specified as a numpy array") - if np.max(np.abs(waveform)) > 1: + if numpy.max(numpy.abs(waveform)) > 1: raise ValueError("The max value for an element in waveform is 1.") self.sendcmd("WFMP:YZERO {}".format(yzero)) diff --git a/instruments/tektronix/tekdpo4104.py b/instruments/tektronix/tekdpo4104.py index 5a6b9ae64..3be867bf6 100644 --- a/instruments/tektronix/tekdpo4104.py +++ b/instruments/tektronix/tekdpo4104.py @@ -10,13 +10,12 @@ from time import sleep from enum import Enum -import numpy as np - from instruments.abstract_instruments import ( OscilloscopeChannel, OscilloscopeDataSource, Oscilloscope, ) +from instruments.optional_dep_finder import numpy from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList @@ -96,6 +95,8 @@ def read_waveform(self, bin_format=True): :param bool bin_format: If `True`, data is transfered in a binary format. Otherwise, data is transferred in ASCII. + :rtype: `tuple`[`tuple`[`~pint.Quantity`, ...], `tuple`[`~pint.Quantity`, ...]] + or if numpy is installed, `tuple` of two `~pint.Quantity` with `numpy.array` data """ # Set the acquisition channel @@ -111,7 +112,10 @@ def read_waveform(self, bin_format=True): sleep(0.02) # Work around issue with 2.48 firmware. raw = self._tek.query("CURVE?") raw = raw.split(",") # Break up comma delimited string - raw = np.array(raw, dtype=np.float) # Convert to numpy array + if numpy: + raw = numpy.array(raw, dtype=numpy.float) # Convert to numpy array + else: + raw = map(float, raw) else: # Set encoding to signed, big-endian self._tek.sendcmd("DAT:ENC RIB") @@ -127,14 +131,17 @@ def read_waveform(self, bin_format=True): ymult = self._tek.query("WFMP:YMU?") # Retrieve Y multiplier yzero = self._tek.query("WFMP:YZE?") # Retrieve Y zero - y = ((raw - yoffs) * float(ymult)) + float(yzero) - xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr # Retrieve number of data points ptcnt = self._tek.query("WFMP:NR_P?") - x = np.arange(float(ptcnt)) * float(xincr) + float(xzero) + if numpy: + x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) + y = ((raw - yoffs) * float(ymult)) + float(yzero) + else: + x = tuple([float(val) * float(xincr) + float(xzero) for val in range(int(ptcnt))]) + y = tuple(((x - yoffs) * float(ymult)) + float(yzero) for x in raw) self._tek.sendcmd("DAT:STOP {}".format(old_dat_stop)) diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index 2340266ff..c7d06f312 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -14,6 +14,7 @@ Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource ) from instruments.generic_scpi import SCPIInstrument +from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import ( enum_property, string_property, int_property, unitful_property, @@ -22,6 +23,8 @@ # CLASSES ##################################################################### +# pylint: disable=too-many-lines + class TekDPO70000(SCPIInstrument, Oscilloscope): @@ -148,11 +151,11 @@ def _dtype(binary_format, byte_order, n_bytes): return "{}{}{}".format({ TekDPO70000.ByteOrder.big_endian: ">", TekDPO70000.ByteOrder.little_endian: "<" - }[byte_order], { + }[byte_order], (n_bytes if n_bytes is not None else ""), { TekDPO70000.BinaryFormat.int: "i", TekDPO70000.BinaryFormat.uint: "u", TekDPO70000.BinaryFormat.float: "f" - }[binary_format], n_bytes) + }[binary_format]) # CLASSES # @@ -186,7 +189,7 @@ def read_waveform(self, bin_format=True): dtype = self._parent._dtype( self._parent.outgoing_binary_format, self._parent.outgoing_byte_order, - n_bytes + n_bytes=None ) self._parent.sendcmd("CURV?") raw = self._parent.binblockread(n_bytes, fmt=dtype) @@ -478,10 +481,18 @@ class SpectralWindow(Enum): def _scale_raw_data(self, data): # TODO: incorperate the unit_string somehow - return self.scale * ( - (TekDPO70000.VERT_DIVS / 2) * - data.astype(float) / (2**15) - self.position + if numpy: + return self.scale * ( + (TekDPO70000.VERT_DIVS / 2) * data.astype(float) / (2**15) - self.position + ) + + scale = self.scale + position = self.position + rval = tuple( + scale * ((TekDPO70000.VERT_DIVS / 2) * d / (2 ** 15) - position) + for d in map(float, data) ) + return rval class Channel(DataSource, OscilloscopeChannel): @@ -614,10 +625,20 @@ class Coupling(Enum): ) def _scale_raw_data(self, data): - return self.scale * ( - (TekDPO70000.VERT_DIVS / 2) * - data.astype(float) / (2**15) - self.position - ) + self.offset + scale = self.scale + position = self.position + offset = self.offset + + if numpy: + return scale * ( + (TekDPO70000.VERT_DIVS / 2) * + data.astype(float) / (2**15) - position + ) + offset + + return tuple( + scale * ((TekDPO70000.VERT_DIVS / 2) * d / (2 ** 15) - position) + offset + for d in map(float, data) + ) # PROPERTIES ## diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index 560c100dc..9e30efe4c 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -10,14 +10,13 @@ from enum import Enum -import numpy as np - from instruments.abstract_instruments import ( OscilloscopeChannel, OscilloscopeDataSource, Oscilloscope, ) from instruments.generic_scpi import SCPIInstrument +from instruments.optional_dep_finder import numpy from instruments.util_fns import ProxyList from instruments.units import ureg as u @@ -63,7 +62,8 @@ def read_waveform(self, bin_format=True): :param bool bin_format: If `True`, data is transfered in a binary format. Otherwise, data is transferred in ASCII. - :rtype: two item `tuple` of `numpy.ndarray` + :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] + or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] """ with self: @@ -72,7 +72,10 @@ def read_waveform(self, bin_format=True): # Set the data encoding format to ASCII raw = self._tek.query("CURVE?") raw = raw.split(',') # Break up comma delimited string - raw = np.array(raw, dtype=np.float) # Convert to ndarray + if numpy: + raw = numpy.array(raw, dtype=numpy.float) # Convert to ndarray + else: + raw = tuple(map(float, raw)) else: self._tek.sendcmd("DAT:ENC RIB") # Set encoding to signed, big-endian @@ -89,15 +92,16 @@ def read_waveform(self, bin_format=True): ymult = self._tek.query(f"WFMP:{self.name}:YMU?") # Retrieve Y multiply yzero = self._tek.query(f"WFMP:{self.name}:YZE?") # Retrieve Y zero - y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) - xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr - ptcnt = self._tek.query(f"WFMP:{self.name}:NR_P?") # Retrieve number - # of data - # points + ptcnt = self._tek.query(f"WFMP:{self.name}:NR_P?") # Retrieve number of data points - x = np.arange(float(ptcnt)) * float(xincr) + float(xzero) + if numpy: + x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) + y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) + else: + x = tuple(float(val) * float(xincr) + float(xzero) for val in range(int(ptcnt))) + y = tuple(((x - float(yoffs)) * float(ymult)) + float(yzero) for x in raw) return x, y diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index fe744f029..96da27d59 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -40,7 +40,6 @@ import struct import time -import numpy as np from instruments.abstract_instruments import ( OscilloscopeChannel, @@ -48,6 +47,7 @@ Oscilloscope, ) from instruments.generic_scpi import SCPIInstrument +from instruments.optional_dep_finder import numpy from instruments.util_fns import ProxyList # CLASSES ##################################################################### @@ -116,7 +116,8 @@ def read_waveform(self, bin_format=True): :param bool bin_format: If `True`, data is transfered in a binary format. Otherwise, data is transferred in ASCII. - :rtype: two item `tuple` of `numpy.ndarray` + :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] + or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] """ with self: @@ -125,7 +126,10 @@ def read_waveform(self, bin_format=True): self._parent.sendcmd('DAT:ENC ASCI') raw = self._parent.query('CURVE?') raw = raw.split(',') # Break up comma delimited string - raw = np.array(raw, dtype=np.float) # Convert into numpy array + if numpy: + raw = numpy.array(raw, dtype=numpy.float) # Convert to numpy array + else: + raw = map(float, raw) else: # Set encoding to signed, big-endian self._parent.sendcmd('DAT:ENC RIB') @@ -139,22 +143,25 @@ def read_waveform(self, bin_format=True): self._parent._file.read_raw(1) # Retrieve Y offset - yoffs = self._parent.query('WFMP:{}:YOF?'.format(self.name)) + yoffs = float(self._parent.query('WFMP:{}:YOF?'.format(self.name))) # Retrieve Y multiply - ymult = self._parent.query('WFMP:{}:YMU?'.format(self.name)) + ymult = float(self._parent.query('WFMP:{}:YMU?'.format(self.name))) # Retrieve Y zero - yzero = self._parent.query('WFMP:{}:YZE?'.format(self.name)) - - y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) + yzero = float(self._parent.query('WFMP:{}:YZE?'.format(self.name))) # Retrieve X incr - xincr = self._parent.query('WFMP:{}:XIN?'.format(self.name)) + xincr = float(self._parent.query('WFMP:{}:XIN?'.format(self.name))) # Retrieve number of data points - ptcnt = self._parent.query('WFMP:{}:NR_P?'.format(self.name)) + ptcnt = int(self._parent.query('WFMP:{}:NR_P?'.format(self.name))) - x = np.arange(float(ptcnt)) * float(xincr) + if numpy: + x = numpy.arange(float(ptcnt)) * float(xincr) + y = ((raw - yoffs) * float(ymult)) + float(yzero) + else: + x = tuple([float(val) * float(xincr) for val in range(ptcnt)]) + y = tuple(((x - yoffs) * float(ymult)) + float(yzero) for x in raw) - return (x, y) + return x, y class _TekTDS5xxChannel(_TekTDS5xxDataSource, OscilloscopeChannel): @@ -400,7 +407,7 @@ def sources(self): :rtype: `list` """ active = [] - channels = np.array(self.query('SEL?').split(';')[0:11], dtype=np.int) + channels = list(map(int, self.query('SEL?').split(';')[0:11])) for idx in range(0, 4): if channels[idx]: active.append(_TekTDS5xxChannel(self, idx)) diff --git a/instruments/teledyne/maui.py b/instruments/teledyne/maui.py index 6087a823a..a93aac8c4 100644 --- a/instruments/teledyne/maui.py +++ b/instruments/teledyne/maui.py @@ -16,11 +16,11 @@ # IMPORTS ##################################################################### from enum import Enum -import numpy as np from instruments.abstract_instruments import ( Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource ) +from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import ( assume_units, enum_property, bool_property, ProxyList @@ -230,7 +230,8 @@ def read_waveform(self, bin_format=False, single=True): :return: Data (time, signal) where time is in seconds and signal in V - :rtype: ndarray + :rtype: `tuple`[`tuple`[`~pint.Quantity`, ...], `tuple`[`~pint.Quantity`, ...]] + or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] :raises NotImplementedError: Bin format was chosen, but it is not implemented. @@ -265,19 +266,25 @@ def read_waveform(self, bin_format=False, single=True): self._parent.trigger_state = trig_state # format the string to appropriate data - dat_val = np.array(retval.replace('"', '').split(), - dtype=np.float) + retval = retval.replace('"', '').split() + if numpy: + dat_val = numpy.array(retval, dtype=numpy.float) # Convert to ndarray + else: + dat_val = tuple(map(float, retval)) # format horizontal data into floats horiz_off = float(horiz_off.replace('"', '').split(':')[1]) horiz_int = float(horiz_int.replace('"', '').split(':')[1]) # create time base - dat_time = np.arange( - horiz_off, - horiz_off + horiz_int * (len(dat_val)), - horiz_int - ) + if numpy: + dat_time = numpy.arange( + horiz_off, + horiz_off + horiz_int * (len(dat_val)), + horiz_int + ) + else: + dat_time = tuple(val * horiz_int + horiz_off for val in range(len(dat_val))) # fix length bug, sometimes dat_time is longer than dat_signal if len(dat_time) > len(dat_val): @@ -285,7 +292,10 @@ def read_waveform(self, bin_format=False, single=True): else: # in case the opposite is the case dat_val = dat_val[0:len(dat_time)] - return np.stack((dat_time, dat_val)) + if numpy: + return numpy.stack((dat_time, dat_val)) + else: + return dat_time, dat_val trace = bool_property( command="TRA", diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index ea5380993..534960fc6 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -16,6 +16,9 @@ import pytest +from instruments.optional_dep_finder import numpy +from instruments.units import ureg as u + # FUNCTIONS ################################################################## @@ -116,3 +119,18 @@ def test(): with expected_protocol(ins_class, name_cmd + "\n", "NAME\n") as ins: assert ins.name == "NAME" return test + + +def iterable_eq(a, b): + """ + Asserts that the contents of two iterables are the same. + """ + if numpy and (isinstance(a, numpy.ndarray) or isinstance(b, numpy.ndarray)): + # pylint: disable=unidiomatic-typecheck + assert type(a) == type(b), f"Expected two numpy arrays, got {type(a)}, {type(b)}" + assert len(a) == len(b), f"Length of iterables is not the same, got {len(a)} and {len(b)}" + assert (a == b).all() + elif isinstance(a, u.Quantity) and isinstance(b, u.Quantity): + unit_eq(a, b) + else: + assert a == b diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 40af2567b..547a5cab8 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -6,11 +6,16 @@ # IMPORTS #################################################################### -import numpy as np import pytest import instruments as ik -from instruments.tests import expected_protocol, make_name_test, unit_eq +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, + make_name_test, + unit_eq +) from instruments.units import ureg as u # TESTS ###################################################################### @@ -96,7 +101,11 @@ def test_agilent34410a_r(): b"#18" + bytes.fromhex("3FF0000000000000") ] ) as dmm: - unit_eq(dmm.r(1), np.array([1]) * u.volt) + expected = (u.Quantity(1, u.volt),) + if numpy: + expected = numpy.array([1]) * u.volt + actual = dmm.r(1) + iterable_eq(actual, expected) def test_agilent34410a_r_count_zero(): @@ -113,7 +122,11 @@ def test_agilent34410a_r_count_zero(): b"#18" + bytes.fromhex("3FF0000000000000") ] ) as dmm: - unit_eq(dmm.r(0), np.array([1]) * u.volt) + expected = (u.Quantity(1, u.volt),) + if numpy: + expected = numpy.array([1]) * u.volt + actual = dmm.r(0) + iterable_eq(actual, expected) def test_agilent34410a_r_type_error(): @@ -145,8 +158,10 @@ def test_agilent34410a_fetch(): ] ) as dmm: data = dmm.fetch() - unit_eq(data[0], 4.27150000E-03 * u.volt) - unit_eq(data[1], 5.27150000E-03 * u.volt) + expected = (4.27150000E-03 * u.volt, 5.27150000E-03 * u.volt) + if numpy: + expected = (4.27150000E-03, 5.27150000E-03) * u.volt + iterable_eq(data, expected) def test_agilent34410a_read_data(): @@ -202,6 +217,7 @@ def test_agilent34410a_read_data_type_error(): err_msg = err_info.value.args[0] assert err_msg == 'Parameter "sample_count" must be an integer.' + def test_agilent34410a_read_data_nvmem(): with expected_protocol( ik.agilent.Agilent34410a, diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 0629f6c76..d13915928 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -13,9 +13,9 @@ from serial.tools.list_ports_common import ListPortInfo import pytest -import numpy as np import instruments as ik +from instruments.optional_dep_finder import numpy from instruments.tests import expected_protocol # pylint: disable=unused-import from instruments.abstract_instruments.comm import ( @@ -24,6 +24,7 @@ USBTMCCommunicator, VXI11Communicator, serial_manager, SerialCommunicator ) from instruments.errors import AcknowledgementError, PromptError +from instruments.tests import iterable_eq from . import mock @@ -43,7 +44,11 @@ def test_instrument_binblockread(): ], sep="\n" ) as inst: - np.testing.assert_array_equal(inst.binblockread(2), [0, 1, 2, 3, 4]) + actual_data = inst.binblockread(2) + expected = (0, 1, 2, 3, 4) + if numpy: + expected = numpy.array(expected) + iterable_eq(actual_data, expected) def test_instrument_binblockread_two_reads(): @@ -53,11 +58,14 @@ def test_instrument_binblockread_two_reads(): side_effect=[b"#", b"2", b"10", data[:6], data[6:]] ) - np.testing.assert_array_equal(inst.binblockread(2), [0, 1, 2, 3, 4]) + expected = (0, 1, 2, 3, 4) + if numpy: + expected = numpy.array((0, 1, 2, 3, 4)) + iterable_eq(inst.binblockread(2), expected) calls_expected = [1, 1, 2, 10, 4] calls_actual = [call[0][0] for call in inst._file.read_raw.call_args_list] - np.testing.assert_array_equal(calls_expected, calls_actual) + iterable_eq(calls_actual, calls_expected) def test_instrument_binblockread_too_many_reads(): diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index 2729faa7d..2b3c9ab5d 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -9,8 +9,6 @@ import pytest -from numpy import array - from instruments.abstract_instruments.comm import USBTMCCommunicator from instruments.tests import unit_eq from instruments.units import ureg as u @@ -73,10 +71,10 @@ def test_usbtmccomm_timeout(mock_usbtmc): timeout.assert_called_with() comm.timeout = 10 - timeout.assert_called_with(array(10.0)) + timeout.assert_called_with(10.0) comm.timeout = 1000 * u.millisecond - timeout.assert_called_with(array(1.0)) + timeout.assert_called_with(1.0) @mock.patch(patch_path) diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 7cc40f67e..262071828 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -6,11 +6,13 @@ # IMPORTS ##################################################################### - import pytest import instruments as ik -from instruments.tests import expected_protocol +from instruments.tests import ( + expected_protocol, + iterable_eq, +) from instruments.units import ureg as u from .. import mock @@ -215,7 +217,8 @@ def test_all_voltage(): ], sep="\n" ) as hp: - assert sorted(hp.voltage) == sorted((2, 3, 4, 5) * u.V) + expected = (2 * u.V, 3 * u.V, 4 * u.V, 5 * u.V) + iterable_eq(hp.voltage, expected) hp.voltage = 5 * u.V hp.voltage = (1 * u.V, 2 * u.V, 3 * u.V, 4 * u.V) @@ -257,7 +260,8 @@ def test_all_current(): ], sep="\n" ) as hp: - assert sorted(hp.current) == sorted((2, 3, 4, 5) * u.A) + expected = (2 * u.A, 3 * u.A, 4 * u.A, 5 * u.A) + iterable_eq(hp.current, expected) hp.current = 5 * u.A hp.current = (1 * u.A, 2 * u.A, 3 * u.A, 4 * u.A) @@ -289,7 +293,8 @@ def test_all_voltage_sense(): ], sep="\n" ) as hp: - assert sorted(hp.voltage_sense) == sorted((2, 3, 4, 5) * u.V) + expected = (2 * u.V, 3 * u.V, 4 * u.V, 5 * u.V) + iterable_eq(hp.voltage_sense, expected) def test_all_current_sense(): @@ -309,7 +314,8 @@ def test_all_current_sense(): ], sep="\n" ) as hp: - assert sorted(hp.current_sense) == sorted((2, 3, 4, 5) * u.A) + expected = (2 * u.A, 3 * u.A, 4 * u.A, 5 * u.A) + iterable_eq(hp.current_sense, expected) def test_clear(): diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index d92c85a24..86d6042c9 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -7,11 +7,14 @@ # IMPORTS ##################################################################### -import numpy as np import pytest import instruments as ik -from instruments.tests import expected_protocol +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, +) from instruments.units import ureg as u # TESTS ####################################################################### @@ -200,9 +203,12 @@ def test_fetch(): "VOLT", ] ) as inst: - np.testing.assert_array_equal( - inst.fetch(), [1.234, 1, 5.678] * u.volt - ) + data = inst.fetch() + vals = [1.234, 1, 5.678] + expected_data = tuple(v * u.volt for v in vals) + if numpy: + expected_data = vals * u.volt + iterable_eq(data, expected_data) def test_measure(): diff --git a/instruments/tests/test_rigol/test_rigolds1000.py b/instruments/tests/test_rigol/test_rigolds1000.py index 5d1764a33..e43ab19b2 100644 --- a/instruments/tests/test_rigol/test_rigolds1000.py +++ b/instruments/tests/test_rigol/test_rigolds1000.py @@ -6,11 +6,15 @@ # IMPORTS #################################################################### -import numpy as np import pytest import instruments as ik -from instruments.tests import expected_protocol, make_name_test +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, + make_name_test, +) # TESTS ###################################################################### @@ -156,10 +160,10 @@ def test_channel_read_waveform(): b"#210" + bytes.fromhex("00000001000200030004") + b"0" ] ) as osc: - np.testing.assert_array_equal( - osc.channel[1].read_waveform(), - [0, 1, 2, 3, 4] - ) + expected = (0, 1, 2, 3, 4) + if numpy: + expected = numpy.array(expected) + iterable_eq(osc.channel[1].read_waveform(), expected) # TEST MATH # @@ -188,10 +192,10 @@ def test_math_read_waveform(): b"#210" + bytes.fromhex("00000001000200030004") + b"0" ] ) as osc: - np.testing.assert_array_equal( - osc.math.read_waveform(), - [0, 1, 2, 3, 4] - ) + expected = (0, 1, 2, 3, 4) + if numpy: + expected = numpy.array(expected) + iterable_eq(osc.math.read_waveform(), expected) # TEST REF DATASOURCE # diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index 3c74d178e..9a5985f6f 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -7,10 +7,11 @@ # IMPORTS ##################################################################### -import numpy as np - import instruments as ik -from instruments.tests import expected_protocol +from instruments.tests import ( + expected_protocol, + iterable_eq, +) from instruments.units import ureg as u # TESTS ####################################################################### @@ -28,9 +29,7 @@ def test_amplitude(): "1.234VP", ] ) as inst: - np.testing.assert_array_equal( - inst.amplitude, (1.234 * u.V, inst.VoltageMode.peak_to_peak) - ) + iterable_eq(inst.amplitude, (1.234 * u.V, inst.VoltageMode.peak_to_peak)) inst.amplitude = 0.1 * u.V inst.amplitude = (0.1 * u.V, inst.VoltageMode.rms) diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index ec168e02b..6ac052788 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -8,14 +8,21 @@ import time -import numpy as np import pytest import serial import instruments as ik -from instruments.abstract_instruments.comm import GPIBCommunicator, \ - LoopbackCommunicator, SerialCommunicator, USBCommunicator -from instruments.tests import expected_protocol +from instruments.abstract_instruments.comm import ( + GPIBCommunicator, + LoopbackCommunicator, + SerialCommunicator, + USBCommunicator +) +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, +) from instruments.units import ureg as u # TESTS ####################################################################### @@ -325,7 +332,10 @@ def test_take_measurement(): ] ) as inst: resp = inst.take_measurement(sample_rate=1, num_samples=2) - np.testing.assert_array_equal(resp, [[1.234, 5.678], [0.456, 5.321]]) + expected = ((1.234, 5.678), (0.456, 5.321)) + if numpy: + expected = numpy.array(expected) + iterable_eq(resp, expected) def test_take_measurement_num_dat_points_fails(): @@ -362,7 +372,10 @@ def test_take_measurement_num_dat_points_fails(): ] ) as inst: resp = inst.take_measurement(sample_rate=1, num_samples=2) - np.testing.assert_array_equal(resp, [[1.234, 5.678], [0.456, 5.321]]) + expected = ((1.234, 5.678), (0.456, 5.321)) + if numpy: + expected = numpy.array(expected) + iterable_eq(resp, expected) def test_take_measurement_invalid_num_samples(): @@ -475,7 +488,7 @@ def test_data_snap(): ) as inst: data = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.y) expected = [1.234, 9.876] - np.testing.assert_array_equal(data, expected) + iterable_eq(data, expected) def test_data_snap_mode_as_str(): @@ -490,7 +503,7 @@ def test_data_snap_mode_as_str(): ) as inst: data = inst.data_snap(mode1='x', mode2='y') expected = [1.234, 9.876] - np.testing.assert_array_equal(data, expected) + iterable_eq(data, expected) def test_data_snap_invalid_snap_mode1(): @@ -533,8 +546,10 @@ def test_read_data_buffer(): ] ) as inst: data = inst.read_data_buffer(channel=inst.Mode.ch1) - expected = [1.234, 9.876] - np.testing.assert_array_equal(data, expected) + expected = (1.234, 9.876) + if numpy: + expected = numpy.array(expected) + iterable_eq(data, expected) def test_read_data_buffer_mode_as_str(): @@ -550,8 +565,10 @@ def test_read_data_buffer_mode_as_str(): ] ) as inst: data = inst.read_data_buffer(channel="ch1") - expected = [1.234, 9.876] - np.testing.assert_array_equal(data, expected) + expected = (1.234, 9.876) + if numpy: + expected = numpy.array(expected) + iterable_eq(data, expected) def test_read_data_buffer_invalid_mode(): diff --git a/instruments/tests/test_srs/test_srsctc100.py b/instruments/tests/test_srs/test_srsctc100.py index 3b9a59ffc..53fb14f5e 100644 --- a/instruments/tests/test_srs/test_srsctc100.py +++ b/instruments/tests/test_srs/test_srsctc100.py @@ -11,10 +11,13 @@ strategies as st, ) import pytest -import numpy as np import instruments as ik -from instruments.tests import expected_protocol +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, +) from instruments.units import ureg as u # TESTS ###################################################################### @@ -366,12 +369,19 @@ def test_channel_get_log(channel): it in range(1, n_points)]) # make data to compare with - ts = u.Quantity(np.empty((n_points,)), u.ms) - temps = u.Quantity(np.empty((n_points,)), units) + if numpy: + ts = u.Quantity(numpy.empty((n_points,)), u.ms) + temps = u.Quantity(numpy.empty((n_points,)), units) + else: + ts = [u.Quantity(0, u.ms)] * n_points + temps = [u.Quantity(0, units)] * n_points for it, time in enumerate(times): ts[it] = u.Quantity(time, u.ms) temps[it] = u.Quantity(values[it], units) + if not numpy: + ts = tuple(ts) + temps = tuple(temps) with expected_protocol( ik.srs.SRSCTC100, @@ -405,8 +415,8 @@ def test_channel_get_log(channel): ch = inst.channel[channel] ts_read, temps_read = ch.get_log() # assert the data is correct - np.testing.assert_array_equal(ts, ts_read) - np.testing.assert_array_equal(temps, temps_read) + iterable_eq(ts, ts_read) + iterable_eq(temps, temps_read) # INSTRUMENT # diff --git a/instruments/tests/test_tektronix/test_tekawg2000.py b/instruments/tests/test_tektronix/test_tekawg2000.py index 4ef7da62c..5c39a96da 100644 --- a/instruments/tests/test_tektronix/test_tekawg2000.py +++ b/instruments/tests/test_tektronix/test_tekawg2000.py @@ -10,12 +10,12 @@ given, strategies as st, ) -import numpy as np import pytest -from instruments.units import ureg as u import instruments as ik +from instruments.optional_dep_finder import numpy from instruments.tests import expected_protocol, make_name_test +from instruments.units import ureg as u # TESTS ####################################################################### @@ -241,6 +241,7 @@ def test_waveform_name_type_mismatch(): assert exc_msg == "Waveform name must be specified as a string." +@pytest.mark.skipif(numpy is None, reason="Numpy required for this test") @given(yzero=st.floats(min_value=-5, max_value=5), ymult=st.floats(min_value=0.00001), xincr=st.floats(min_value=5e-8, max_value=1e-1), @@ -248,7 +249,7 @@ def test_waveform_name_type_mismatch(): def test_upload_waveform(yzero, ymult, xincr, waveform): """Upload a waveform from the PC to the instrument.""" # prep waveform - waveform = np.array(waveform) + waveform = numpy.array(waveform) waveform_send = waveform * (2**12 - 1) waveform_send = waveform_send.astype("h", value) for value in values) values_len = str(len(values_packed)).encode() values_len_of_len = str(len(values_len)).encode() # calculations - y_calc = ((values_arr - yoffs) * ymult) + yzero - x_calc = np.arange(float(ptcnt)) * xincr + if numpy: + x_calc = numpy.arange(float(ptcnt)) * xincr + y_calc = ((values_arr - yoffs) * ymult) + yzero + else: + x_calc = tuple([float(val) * float(xincr) for val in range(ptcnt)]) + y_calc = tuple(((val - yoffs) * float(ymult)) + float(yzero) for val in values) with expected_protocol( ik.tektronix.TekTDS5xx, @@ -160,8 +170,8 @@ def test_data_source_read_waveform_binary(values): ) as inst: channel = inst.channel[channel_no] x_read, y_read = channel.read_waveform(bin_format=True) - np.testing.assert_equal(x_read, x_calc) - np.testing.assert_equal(y_read, y_calc) + iterable_eq(x_read, x_calc) + iterable_eq(y_read, y_calc) @given(values=st.lists(st.floats(min_value=0), min_size=1)) @@ -175,12 +185,18 @@ def test_data_source_read_waveform_ascii(values): xincr = 0.001 # make values to compare with values_str = ",".join([str(value) for value in values]) - values_arr = np.array(values) + values_arr = values + if numpy: + values_arr = numpy.array(values) # calculations ptcnt = len(values) - y_calc = ((values_arr - yoffs) * ymult) + yzero - x_calc = np.arange(float(ptcnt)) * xincr + if numpy: + x_calc = numpy.arange(float(ptcnt)) * xincr + y_calc = ((values_arr - yoffs) * ymult) + yzero + else: + x_calc = tuple([float(val) * float(xincr) for val in range(ptcnt)]) + y_calc = tuple(((val - yoffs) * float(ymult)) + float(yzero) for val in values) with expected_protocol( ik.tektronix.TekTDS5xx, @@ -207,8 +223,8 @@ def test_data_source_read_waveform_ascii(values): ) as inst: channel = inst.channel[channel_no] x_read, y_read = channel.read_waveform(bin_format=False) - np.testing.assert_equal(x_read, x_calc) - np.testing.assert_equal(y_read, y_calc) + iterable_eq(x_read, x_calc) + iterable_eq(y_read, y_calc) # CHANNEL # diff --git a/instruments/tests/test_teledyne/test_maui.py b/instruments/tests/test_teledyne/test_maui.py index a538d914c..30482a62b 100644 --- a/instruments/tests/test_teledyne/test_maui.py +++ b/instruments/tests/test_teledyne/test_maui.py @@ -6,12 +6,15 @@ # IMPORTS #################################################################### -import numpy as np import pytest import instruments as ik +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, +) from instruments.units import ureg as u -from instruments.tests import expected_protocol # TESTS ###################################################################### @@ -90,13 +93,20 @@ def test_maui_data_source_read_waveform(init): ], sep="\n" ) as osc: - wf = np.array( - [ - [0., 2.5, 5., 7.5], - [1., 2., 3., 4.] - ] - ) - assert (osc.channel[0].read_waveform() == wf).all() + if numpy: + expected_wf = numpy.array( + [ + [0., 2.5, 5., 7.5], + [1., 2., 3., 4.] + ] + ) + else: + expected_wf = ( + (0., 2.5, 5., 7.5), + (1., 2., 3., 4.) + ) + actual_wf = osc.channel[0].read_waveform() + iterable_eq(actual_wf, expected_wf) def test_maui_data_source_read_waveform_different_length(init): @@ -132,22 +142,30 @@ def test_maui_data_source_read_waveform_different_length(init): ], sep="\n" ) as osc: - # create the signal that we want to get returned - signal = np.array(faulty_dataset_int) h_offset = 9.8895e-06 h_interval = 5e-10 - timebase = np.arange( - h_offset, - h_offset + h_interval * (len(signal)), - h_interval - ) - # now cut timebase to the length of the signal - timebase = timebase[0:len(signal)] - - # create return dataset - dataset_return = np.stack((timebase, signal)) - assert (osc.channel[0].read_waveform() == dataset_return).all() + if numpy: + # create the signal that we want to get returned + signal = numpy.array(faulty_dataset_int) + timebase = numpy.arange( + h_offset, + h_offset + h_interval * (len(signal)), + h_interval + ) + + # now cut timebase to the length of the signal + timebase = timebase[0:len(signal)] + # create return dataset + dataset_return = numpy.stack((timebase, signal)) + else: + signal = tuple(faulty_dataset_int) + timebase = tuple([float(val) * h_interval + h_offset for val in range(len(signal))]) + timebase = timebase[0:len(signal)] + dataset_return = timebase, signal + + actual_wf = osc.channel[0].read_waveform() + iterable_eq(actual_wf, dataset_return) def test_maui_data_source_read_waveform_math(init): @@ -167,13 +185,20 @@ def test_maui_data_source_read_waveform_math(init): ], sep="\n" ) as osc: - wf = np.array( - [ - [0., 2.5, 5., 7.5], - [1., 2., 3., 4.] - ] - ) - assert (osc.channel[0].read_waveform(single=False) == wf).all() + if numpy: + expected_wf = numpy.array( + [ + [0., 2.5, 5., 7.5], + [1., 2., 3., 4.] + ] + ) + else: + expected_wf = ( + (0., 2.5, 5., 7.5), + (1., 2., 3., 4.) + ) + actual_wf = osc.channel[0].read_waveform(single=False) + iterable_eq(actual_wf, expected_wf) def test_maui_data_source_read_waveform_bin_format(init): diff --git a/instruments/tests/test_test_utils.py b/instruments/tests/test_test_utils.py new file mode 100644 index 000000000..e6abf4343 --- /dev/null +++ b/instruments/tests/test_test_utils.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- + +""" +Module containing tests for test-related utility functions +""" + +from hypothesis import ( + given, + strategies as st, +) +import pytest + +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + iterable_eq +) +from instruments.units import ureg as u + + +# TESTS ####################################################################### + +@given(a=st.lists(st.floats())) +def test_iterable_eq_passes_basic(a): + """ + Test that two identical lists and tuples always pass + """ + b = a[:] + iterable_eq(a, b) + iterable_eq(tuple(a), tuple(b)) + + +@pytest.mark.parametrize("b", [(0, 1, 2), (0,)]) +def test_iterable_eq_fails(b): + """ + Test failure on equal and mismatched lengths + """ + a = (1, 2, 3) + with pytest.raises(AssertionError): + iterable_eq(a, b) + + +def test_iterable_eq_fails_type_mismatch(): + """ + Test failure on type mismatch + """ + a = [1, 2, 3] + with pytest.raises(AssertionError): + iterable_eq(a, tuple(a)) + + +def test_iterable_eq_passes_sequence_quantity(): + """ + Test passes on equal sequences with unitful values + """ + a = (1*u.V, 2*u.A) + iterable_eq(a, a[:]) + + +def test_iterable_eq_fails_sequence_quantity(): + """ + Test failure on unitful sequences with wrong units, and wrong magnitudes + """ + a = (1*u.V, 2*u.A) + b = (1*u.A, 2*u.A) # Different units + with pytest.raises(AssertionError): + iterable_eq(a, b) + + b = (1 * u.V, 3 * u.A) # Different magnitude + with pytest.raises(AssertionError): + iterable_eq(a, b) + + +def test_iterable_eq_passes_singular_quantity(): + """ + Test passes on singular unitful quantity + """ + iterable_eq(1*u.V, 1*u.V) + + +def test_iterable_eq_fails_singular_quantity(): + """ + Test failure on singular unitful quantity with wrong units + """ + a = 1*u.V + b = 1*u.A + with pytest.raises(AssertionError): + iterable_eq(a, b) + + +@pytest.mark.skipif(numpy is None, reason="Only run if numpy installed") +def test_iterable_eq_passes_two_numpy_array(): + """ + Test pases for two identical numpy arrays + """ + a = numpy.array([1, 2, 3]) + iterable_eq(a, a.copy()) + + +@pytest.mark.skipif(numpy is None, reason="Only run if numpy installed") +def test_iterable_eq_fails_one_numpy_array_equal_values(): + """ + Test failure for one is numpy array, other is not + """ + a = numpy.array([1, 2, 3]) + b = (1, 2, 3) + with pytest.raises(AssertionError): + iterable_eq(a, b) + + +@pytest.mark.skipif(numpy is None, reason="Only run if numpy installed") +@pytest.mark.parametrize("b", [(1, 6, 3), (1, 2)]) +def test_iterable_eq_fails_one_numpy_array(b): + """ + Test that different value and different length + comparisons against numpy arrays fail + """ + a = numpy.array([1, 2, 3]) + with pytest.raises(AssertionError): + iterable_eq(a, b) + + +@pytest.mark.skipif(numpy is None, reason="Only run if numpy installed") +@pytest.mark.parametrize("b", [(1, 6, 3), (1, 2)]) +def test_iterable_eq_fails_two_numpy_array(b): + """ + Test that different value and different length + comparisons against numpy arrays fail + """ + a = numpy.array([1, 2, 3]) + b = numpy.array(b) + with pytest.raises(AssertionError): + iterable_eq(a, b) + + +@pytest.mark.skipif(numpy is None, reason="Only run if numpy installed") +def test_iterable_eq_passes_two_numpy_array_quantities(): + """ + Test that two unitful quantities with numpy array data + will equal data will pass + """ + values = [1, 2, 3] + a = numpy.array(values) * u.V + b = numpy.array(a) * u.V + iterable_eq(a, b) diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index 7029f75f5..e0f4de355 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -13,11 +13,14 @@ given, strategies as st, ) -import numpy as np -from instruments.units import ureg as u import instruments as ik -from instruments.tests import expected_protocol +from instruments.optional_dep_finder import numpy +from instruments.tests import ( + expected_protocol, + iterable_eq, +) +from instruments.units import ureg as u # TESTS ####################################################################### @@ -39,7 +42,7 @@ def test_init(): pass -@given(values=st.lists(st.decimals(allow_infinity=False, allow_nan=False), min_size=1), +@given(values=st.lists(st.floats(allow_infinity=False, allow_nan=False), min_size=1), channel=st.sampled_from(ik.yokogawa.Yokogawa6370.Traces)) def test_channel_data(values, channel): values_packed = b"".join(struct.pack("=1.9 python-vxi11>=0.8 diff --git a/setup.py b/setup.py index d04d8ab0a..8c8a9b1b0 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ "Topic :: Software Development :: Libraries" ] INSTALL_REQUIRES = [ - "numpy", "pyserial>=3.3", "pyvisa>=1.9", "python-vxi11>=0.8", @@ -43,6 +42,13 @@ "ruamel.yaml~=0.15.37", "pint>=0.16.1" ] +TEST_REQUIRES = [ + 'pytest >= 6.1.1', + 'hypothesis' +] +EXTRA_REQUIRES = { + "numpy": ["numpy"] +} # HELPER FUNCTONS ############################################################ @@ -88,10 +94,8 @@ def find_meta(meta): author_email=find_meta("email"), packages=PACKAGES, install_requires=INSTALL_REQUIRES, - tests_require=[ - 'pytest >= 2.9.1', - 'hypothesis' - ], + tests_require=TEST_REQUIRES, + extras_require=EXTRA_REQUIRES, description=find_meta("description"), long_description=long_description, long_description_content_type="text/x-rst", diff --git a/tox.ini b/tox.ini index de0e7cb21..d9baee855 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38,py39,pylint +envlist = py{36,37,38,39},py{36,37,38,39}-numpy,pylint [testenv] deps = @@ -7,6 +7,7 @@ deps = pytest-cov pytest-xdist coverage + numpy: numpy commands = pytest -n auto --cov=instruments {toxinidir}/instruments/tests/ {posargs:} [testenv:pylint] From 8e2cb61d88ae107b97abe2013a30411d6447e21d Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 21 Nov 2020 18:37:39 -0500 Subject: [PATCH 084/108] Fix sphinx build (#294) --- doc/source/apiref/index.rst | 1 - doc/source/apiref/other.rst | 32 -------------------------------- instruments/newport/__init__.py | 2 +- instruments/srs/__init__.py | 2 +- 4 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 doc/source/apiref/other.rst diff --git a/doc/source/apiref/index.rst b/doc/source/apiref/index.rst index 8ab9ce1b4..3c76fba15 100644 --- a/doc/source/apiref/index.rst +++ b/doc/source/apiref/index.rst @@ -24,7 +24,6 @@ Contents: minghe newport ondax - other oxford phasematrix picowatt diff --git a/doc/source/apiref/other.rst b/doc/source/apiref/other.rst deleted file mode 100644 index eee2c172d..000000000 --- a/doc/source/apiref/other.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. - TODO: put documentation license header here. - -.. currentmodule:: instruments.other - -================= -Other Instruments -================= - -:class:`NewportESP301` -====================== - -.. autoclass:: NewportESP301 - :members: - :undoc-members: - -.. autoclass:: NewportESP301Axis - :members: - :undoc-members: - -:class:`PhaseMatrixFSW0020` -=========================== - -.. autoclass:: PhaseMatrixFSW0020 - :members: - :undoc-members: - -Units ------ - -Units are identified to the Phase Matrix FSW-0020 using the -`~pint.Quantity` class implemented by the `pint` package. diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index cfc97f125..14a02ca1e 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -4,7 +4,7 @@ Module containing Newport instruments """ -from .agilis import AGUC2 +from .agilis import _Axis, AGUC2 from .errors import NewportError from .newportesp301 import ( diff --git a/instruments/srs/__init__.py b/instruments/srs/__init__.py index c3572d505..689c483f4 100644 --- a/instruments/srs/__init__.py +++ b/instruments/srs/__init__.py @@ -7,5 +7,5 @@ from .srs345 import SRS345 from .srs830 import SRS830 -from .srsdg645 import SRSDG645 +from .srsdg645 import _SRSDG645Channel, SRSDG645 from .srsctc100 import SRSCTC100 From 5d465958d7f5c6c4f86c6770591d31a64d9e5662 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sun, 22 Nov 2020 18:59:34 -0500 Subject: [PATCH 085/108] Switch to using pyproject.toml and setup.cfg (#295) * Switch to using pyproject.toml and setup.cfg * Add rtd.txt for readthedocs --- doc/source/devguide/index.rst | 4 -- doc/source/intro.rst | 28 --------- pyproject.toml | 3 + requirements.txt | 7 --- rtd.txt | 1 + setup.cfg | 45 +++++++++++++++ setup.py | 103 +--------------------------------- 7 files changed, 52 insertions(+), 139 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt create mode 100644 rtd.txt diff --git a/doc/source/devguide/index.rst b/doc/source/devguide/index.rst index f730eaf3c..9a0e2dac6 100644 --- a/doc/source/devguide/index.rst +++ b/doc/source/devguide/index.rst @@ -33,10 +33,6 @@ provided ``dev-requirements.txt``:: $ pip install -r dev-requirements.txt -- mock -- pytest -- pylint - Optional Development Dependencies --------------------------------- diff --git a/doc/source/intro.rst b/doc/source/intro.rst index faab65f4d..b0a8dbd39 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -19,34 +19,6 @@ Dependencies Most of the required and optional dependencies can be obtained using ``pip``. -Required Dependencies -~~~~~~~~~~~~~~~~~~~~~ - -Using ``pip``, these requirements can be obtained automatically by using the -provided ``requirements.txt``:: - -$ pip install -r requirements.txt - -- NumPy -- `PySerial`_ -- `pint`_ -- `python-vxi11`_ -- `PyUSB`_ (version 1.0a or higher, required for raw USB support) -- `python-usbtmc`_ -- `ruamel.yaml`_ (required for configuration file support) -- `PyVISA`_ (required for accessing instruments via VISA library) - -Optional Dependencies -~~~~~~~~~~~~~~~~~~~~~ - -.. _PySerial: http://pyserial.sourceforge.net/ -.. _pint: https://pint.readthedocs.io/en/stable/ -.. _ruamel.yaml: http://yaml.readthedocs.io -.. _PyUSB: http://sourceforge.net/apps/trac/pyusb/ -.. _PyVISA: http://pyvisa.sourceforge.net/ -.. _python-usbtmc: https://pypi.python.org/pypi/python-usbtmc -.. _python-vxi11: https://pypi.python.org/pypi/python-vxi11 - Getting Started =============== diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..121a39f5f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools >= 40.6.0", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 57aa2f919..000000000 --- a/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -pyserial -pyvisa>=1.9 -python-vxi11>=0.8 -pyusb -python-usbtmc -ruamel.yaml~=0.15.37 -pint>=0.16.1 diff --git a/rtd.txt b/rtd.txt new file mode 100644 index 000000000..d6e1198b1 --- /dev/null +++ b/rtd.txt @@ -0,0 +1 @@ +-e . diff --git a/setup.cfg b/setup.cfg index 5e4090017..1171e021f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,47 @@ +[metadata] +name = instruments +version = attr: instruments.__version__ +description = attr: instruments.__description__ +author = attr: instruments.__author__ +author_email = attr: instruments.__email__ +url = attr: instruments.__uri__ +long_description = file: README.rst +long_description_content_type = text/x-rst +license = AGPLv3 +classifiers = + Development Status :: 4 - Beta + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Operating System :: OS Independent + License :: OSI Approved :: GNU Affero General Public License v3 + Intended Audience :: Science/Research + Intended Audience :: Manufacturing + Topic :: Scientific/Engineering + Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator + Topic :: Software Development :: Libraries + +[options] +include_package_data = True +packages = find: +install_requires = + pyserial>=3.3 + pyvisa>=1.9 + python-vxi11>=0.8 + python-usbtmc + pyusb + ruamel.yaml~=0.15.37 + pint>=0.16.1 + +[options.extras_require] +numpy = numpy + +[options.packages.find] +exclude = + instruments.tests + [wheel] universal = 1 diff --git a/setup.py b/setup.py index 8c8a9b1b0..bac24a43d 100644 --- a/setup.py +++ b/setup.py @@ -1,103 +1,6 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Python setup.py file for the InstrumentKit project -""" -# IMPORTS #################################################################### +import setuptools -import codecs -import os -import re - -from setuptools import setup, find_packages - -# SETUP VALUES ############################################################### - -NAME = "instruments" -PACKAGES = find_packages() -META_PATH = os.path.join("instruments", "__init__.py") -CLASSIFIERS = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Operating System :: OS Independent", - "License :: OSI Approved :: GNU Affero General Public License v3", - "Intended Audience :: Science/Research", - "Intended Audience :: Manufacturing", - "Topic :: Scientific/Engineering", - "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator", - "Topic :: Software Development :: Libraries" -] -INSTALL_REQUIRES = [ - "pyserial>=3.3", - "pyvisa>=1.9", - "python-vxi11>=0.8", - "python-usbtmc", - "pyusb", - "ruamel.yaml~=0.15.37", - "pint>=0.16.1" -] -TEST_REQUIRES = [ - 'pytest >= 6.1.1', - 'hypothesis' -] -EXTRA_REQUIRES = { - "numpy": ["numpy"] -} - - -# HELPER FUNCTONS ############################################################ - -HERE = os.path.abspath(os.path.dirname(__file__)) - - -def read(*parts): - """ - Build an absolute path from *parts* and and return the contents of the - resulting file. Assume UTF-8 encoding. - """ - with codecs.open(os.path.join(HERE, *parts), "rb", "utf-8") as f: - return f.read() - - -META_FILE = read(META_PATH) - - -def find_meta(meta): - """ - Extract __*meta*__ from META_FILE. - """ - meta_match = re.search( - r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta), - META_FILE, re.M - ) - if meta_match: - return meta_match.group(1) - raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta)) - - -# MAIN ####################################################################### - -with open("README.rst", "r") as fh: - long_description = fh.read() - -setup( - name=find_meta("title"), - version=find_meta("version"), - url=find_meta("uri"), - author=find_meta("author"), - author_email=find_meta("email"), - packages=PACKAGES, - install_requires=INSTALL_REQUIRES, - tests_require=TEST_REQUIRES, - extras_require=EXTRA_REQUIRES, - description=find_meta("description"), - long_description=long_description, - long_description_content_type="text/x-rst", - classifiers=CLASSIFIERS -) +if __name__ == "__main__": + setuptools.setup() From 3ddd6fad8db74de04def8a92efcb1b2802be9ae7 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Mon, 7 Dec 2020 23:21:34 -0500 Subject: [PATCH 086/108] Remove support for new pyserial 3.5 until we fix it (#299) --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 1171e021f..c57783792 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ classifiers = include_package_data = True packages = find: install_requires = - pyserial>=3.3 + pyserial>=3.3,<3.5 pyvisa>=1.9 python-vxi11>=0.8 python-usbtmc From b5ea5d965ee5cfbab4e19c678d42d4a6def5d2dc Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 7 Dec 2020 23:46:13 -0500 Subject: [PATCH 087/108] Tests and typo fix for Generic SCPI Instruments (#297) - All three instruments now with full coverage - Added tests to SCPI Function Generator - Added tests to SCPI Multimeter - Created tests for SCPI Instrument - Corrected a typo / copy paste error: Contrast error message returned description for brightness instead of contrast. --- instruments/generic_scpi/scpi_instrument.py | 2 +- .../test_scpi_function_generator.py | 15 + .../test_generic_scpi/test_scpi_instrument.py | 275 ++++++++++++++++++ .../test_generic_scpi/test_scpi_multimeter.py | 96 ++++++ 4 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 instruments/tests/test_generic_scpi/test_scpi_instrument.py diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index c7b77572b..1d73bdb2d 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -287,6 +287,6 @@ def display_contrast(self): @display_contrast.setter def display_contrast(self, newval): if newval < 0 or newval > 1: - raise ValueError("Display brightness must be a number between 0" + raise ValueError("Display contrast must be a number between 0" " and 1.") self.sendcmd("DISP:CONT {}".format(newval)) diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index bde3c557f..50d19ae78 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -6,6 +6,7 @@ # IMPORTS #################################################################### +import pytest from instruments.units import ureg as u @@ -94,3 +95,17 @@ def test_scpi_func_gen_offset(): assert fg.channel[0].offset == 12.34 * u.V fg.channel[0].offset = 0.4321 * u.V + + +def test_scpi_func_gen_phase(): + """Raise NotImplementedError when set / get phase.""" + with expected_protocol( + ik.generic_scpi.SCPIFunctionGenerator, + [ + ], [ + ], + ) as fg: + with pytest.raises(NotImplementedError): + _ = fg.phase + with pytest.raises(NotImplementedError): + fg.phase = 42 diff --git a/instruments/tests/test_generic_scpi/test_scpi_instrument.py b/instruments/tests/test_generic_scpi/test_scpi_instrument.py new file mode 100644 index 000000000..6e18c0d2f --- /dev/null +++ b/instruments/tests/test_generic_scpi/test_scpi_instrument.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for generic SCPI instruments +""" + +# IMPORTS #################################################################### + +from hypothesis import given, strategies as st +import pytest + +from instruments.units import ureg as u + +import instruments as ik +from instruments.tests import expected_protocol, make_name_test, unit_eq + +# TESTS ###################################################################### + + +test_scpi_multimeter_name = make_name_test(ik.generic_scpi.SCPIInstrument) + + +def test_scpi_instrument_scpi_version(): + """Get name of instrument.""" + retval = "12345" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "SYST:VERS?" + ], + [ + f"{retval}" + ] + ) as inst: + assert inst.scpi_version == retval + + +@pytest.mark.parametrize("retval", ("0", "1")) +def test_scpi_instrument_op_complete(retval): + """Check if operation is completed.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*OPC?" + ], + [ + f"{retval}" + ] + ) as inst: + assert inst.op_complete == bool(int(retval)) + + +@pytest.mark.parametrize("retval", ("off", "0", 0, False)) +def test_scpi_instrument_power_on_status_off(retval): + """Get / set power on status for instrument to on.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*PSC 0", + "*PSC?" + ], + [ + "0" + ] + ) as inst: + inst.power_on_status = retval + assert not inst.power_on_status + + +@pytest.mark.parametrize("retval", ("on", "1", 1, True)) +def test_scpi_instrument_power_on_status_on(retval): + """Get / set power on status for instrument to on.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*PSC 1", + "*PSC?" + ], + [ + "1" + ] + ) as inst: + inst.power_on_status = retval + assert inst.power_on_status + + +def test_scpi_instrument_power_on_status_value_error(): + """Raise ValueError if power on status set with invalid value.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError): + inst.power_on_status = 42 + + +def test_scpi_instrument_self_test_ok(): + """Check if self test returns okay.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*TST?", + "*TST?" + ], + [ + "0", # ok + "not ok" + ] + ) as inst: + assert inst.self_test_ok + assert not inst.self_test_ok + + +def test_scpi_instrument_reset(): + """Reset the instrument.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*RST" + ], + [ + ] + ) as inst: + inst.reset() + + +def test_scpi_instrument_clear(): + """Clear the instrument.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*CLS" + ], + [ + ] + ) as inst: + inst.clear() + + +def test_scpi_instrument_trigger(): + """Trigger the instrument.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*TRG" + ], + [ + ] + ) as inst: + inst.trigger() + + +def test_scpi_instrument_wait_to_continue(): + """Wait to continue the instrument.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + "*WAI" + ], + [ + ] + ) as inst: + inst.wait_to_continue() + + +def test_scpi_instrument_line_frequency(): + """Get / set line frequency.""" + freq_hz = 100 + freq_mhz = u.Quantity(100000, u.mHz) + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + f"SYST:LFR {freq_hz}", + "SYST:LFR?", + f"SYST:LFR {freq_mhz.to('Hz').magnitude}", + ], + [ + f"{freq_hz}", + ] + ) as inst: + inst.line_frequency = freq_hz + unit_eq(inst.line_frequency, freq_hz * u.hertz) + # send a value as mHz + inst.line_frequency = freq_mhz + + +def test_scpi_instrument_check_error_queue(): + """Check and clear error queue.""" + ErrorCodes = ik.generic_scpi.SCPIInstrument.ErrorCodes + err1 = ErrorCodes.no_error # is skipped + err2 = ErrorCodes.invalid_separator + err3 = 13 # invalid error number + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + f"SYST:ERR:CODE:ALL?" + ], + [ + f"{err1.value},{err2.value},{err3}", + ] + ) as inst: + assert inst.check_error_queue() == [err2, err3] + + +@given(val=st.floats(min_value=0, max_value=1)) +def test_scpi_instrument_display_brightness(val): + """Get / set display brightness.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + f"DISP:BRIG {val}", + f"DISP:BRIG?" + ], + [ + f"{val}", + ] + ) as inst: + inst.display_brightness = val + assert inst.display_brightness == val + + +@given(val=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x < 0 or x > 1)) +def test_scpi_instrument_display_brightness_invalid_value(val): + """Raise ValueError if display brightness set with invalid value.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.display_brightness = val + err_msg = err_info.value.args[0] + assert err_msg == "Display brightness must be a number between 0 " \ + "and 1." + + +@given(val=st.floats(min_value=0, max_value=1)) +def test_scpi_instrument_display_contrast(val): + """Get / set display contrast.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + f"DISP:CONT {val}", + f"DISP:CONT?" + ], + [ + f"{val}", + ] + ) as inst: + inst.display_contrast = val + assert inst.display_contrast == val + + +@given(val=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x < 0 or x > 1)) +def test_scpi_instrument_display_contrast_invalid_value(val): + """Raise ValueError if display contrast set with invalid value.""" + with expected_protocol( + ik.generic_scpi.SCPIInstrument, + [ + ], + [ + ] + ) as inst: + with pytest.raises(ValueError) as err_info: + inst.display_contrast = val + err_msg = err_info.value.args[0] + assert err_msg == "Display contrast must be a number between 0 " \ + "and 1." diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index bf9eab0ad..329d9b7de 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -6,6 +6,7 @@ # IMPORTS #################################################################### +import pytest from instruments.units import ureg as u @@ -91,6 +92,24 @@ def test_scpi_multimeter_resolution(): dmm.resolution = 3e-06 +def test_scpi_multimeter_resolution_type_error(): + """Raise TypeError if resolution value has the wrong type.""" + with expected_protocol( + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?" + ], [ + "VOLT +1.000000E+01,+3.000000E-06" + ] + ) as dmm: + wrong_type = "42" + with pytest.raises(TypeError) as err_info: + dmm.resolution = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == ("Resolution must be specified as an int, float, " + "or SCPIMultimeter.Resolution value.") + + def test_scpi_multimeter_trigger_count(): with expected_protocol( ik.generic_scpi.SCPIMultimeter, @@ -110,6 +129,22 @@ def test_scpi_multimeter_trigger_count(): dmm.trigger_count = 10 +def test_scpi_multimeter_trigger_count_type_error(): + """Raise TypeError if trigger count value has the wrong type.""" + with expected_protocol( + ik.generic_scpi.SCPIMultimeter, + [ + ], [ + ] + ) as dmm: + wrong_type = "42" + with pytest.raises(TypeError) as err_info: + dmm.trigger_count = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == ("Trigger count must be specified as an int " + "or SCPIMultimeter.TriggerCount value.") + + def test_scpi_multimeter_sample_count(): with expected_protocol( ik.generic_scpi.SCPIMultimeter, @@ -129,6 +164,21 @@ def test_scpi_multimeter_sample_count(): dmm.sample_count = 10 +def test_scpi_multimeter_sample_count_type_error(): + """Raise TypeError if sample count is of invalid type.""" + with expected_protocol( + ik.generic_scpi.SCPIMultimeter, + [ + ], [ + ] + ) as dmm: + wrong_type = "42" + with pytest.raises(TypeError) as err_info: + dmm.sample_count = wrong_type + err_msg = err_info.value.args[0] + assert err_msg == ("Sample count must be specified as an int " + "or SCPIMultimeter.SampleCount value.") + def test_scpi_multimeter_trigger_delay(): with expected_protocol( ik.generic_scpi.SCPIMultimeter, @@ -171,6 +221,20 @@ def test_scpi_multimeter_sample_timer(): dmm.sample_timer = 1000 * u.millisecond +def test_scpi_multimeter_relative_not_implemented(): + """Raise NotImplementedError when set / get relative.""" + with expected_protocol( + ik.generic_scpi.SCPIMultimeter, + [ + ], [ + ] + ) as dmm: + with pytest.raises(NotImplementedError): + _ = dmm.relative + with pytest.raises(NotImplementedError): + dmm.relative = 42 + + def test_scpi_multimeter_measure(): with expected_protocol( ik.generic_scpi.SCPIMultimeter, @@ -181,3 +245,35 @@ def test_scpi_multimeter_measure(): ] ) as dmm: unit_eq(dmm.measure(dmm.Mode.voltage_dc), 4.2345e-03 * u.volt) + + +def test_scpi_multimeter_measure_mode_none(): + """Read current mode if not specified, test with volt, DC mode.""" + with expected_protocol( + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", + "MEAS:VOLT:DC?", + ], [ + "VOLT:DC", + "+4.23450000E-03", + ] + + ) as dmm: + unit_eq(dmm.measure(), 4.2345e-03 * u.volt) + + +def test_scpi_multimeter_measure_invalid_mode(): + """Raise TypeError if mode is not of type SCPIMultimeter.Mode.""" + with expected_protocol( + ik.generic_scpi.SCPIMultimeter, + [ + ], [ + ] + ) as dmm: + wrong_type = 42 + with pytest.raises(TypeError) as err_info: + dmm.measure(mode=wrong_type) + err_msg = err_info.value.args[0] + assert err_msg == f"Mode must be specified as a SCPIMultimeter.Mode " \ + f"value, got {type(wrong_type)} instead." From 940fcab5fce7e1e2cfd54ea24dd7a34c60d7e775 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 8 Dec 2020 23:43:29 -0500 Subject: [PATCH 088/108] Fix issue with pyserial3.5 and remove pinned requirement pyserial<3.5 (#300) - Adjusted the mocker that faked serial ports for appropriate initialization. --- instruments/tests/test_base_instrument.py | 12 ++++-------- setup.cfg | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index d13915928..34a540a82 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -141,17 +141,15 @@ def fake_comports(): """ Generate a fake list of comports to compare against. """ - fake_device = ListPortInfo() + fake_device = ListPortInfo(device='COM1') fake_device.vid = 0 fake_device.pid = 1000 fake_device.serial_number = 'a1' - fake_device.device = 'COM1' - fake_device2 = ListPortInfo() + fake_device2 = ListPortInfo(device='COM2') fake_device2.vid = 1 fake_device2.pid = 1010 fake_device2.serial_number = 'c0' - fake_device2.device = 'COM2' return [fake_device, fake_device2] @@ -189,17 +187,15 @@ def test_instrument_open_serial_by_usb_ids_and_serial_number(mock_serial_manager @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_multiple_matches(_, mock_comports): with pytest.raises(serial.SerialException): - fake_device = ListPortInfo() + fake_device = ListPortInfo(device='COM1') fake_device.vid = 0 fake_device.pid = 1000 fake_device.serial_number = 'a1' - fake_device.device = 'COM1' - fake_device2 = ListPortInfo() + fake_device2 = ListPortInfo(device='COM2') fake_device2.vid = 0 fake_device2.pid = 1000 fake_device2.serial_number = 'b2' - fake_device2.device = 'COM2' mock_comports.return_value = [fake_device, fake_device2] diff --git a/setup.cfg b/setup.cfg index c57783792..1171e021f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ classifiers = include_package_data = True packages = find: install_requires = - pyserial>=3.3,<3.5 + pyserial>=3.3 pyvisa>=1.9 python-vxi11>=0.8 python-usbtmc From 3409b908d83ac0ecb74be35cc9f12466a2c62f74 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Sat, 12 Dec 2020 14:12:06 -0500 Subject: [PATCH 089/108] Full test suite and fixes for meta-instruments in abstract instruments (#301) Included instruments with full tests now: - Electrometer - Function Generator - Multimeter - Optical spectrum analyzer - Oscilloscope - Power supply Full test suites using pytest fixtures to patch the metaclasses such that they become accessible, see PEP 3119. Fixed `@abc.abstractproperty` decorators in optical spectrum analyzer and oscilloscope, since this decorator is deprecated since py3.3. --- .../optical_spectrum_analyzer.py | 3 +- .../abstract_instruments/oscilloscope.py | 12 +- .../test_abstract_inst/test_electrometer.py | 111 +++++++++++++++ .../test_function_generator.py | 56 ++++++++ .../test_abstract_inst/test_multimeter.py | 78 +++++++++++ .../test_optical_spectrum_analyzer.py | 113 +++++++++++++++ .../test_abstract_inst/test_oscilloscope.py | 130 ++++++++++++++++++ .../test_abstract_inst/test_power_supply.py | 99 +++++++++++++ 8 files changed, 597 insertions(+), 5 deletions(-) create mode 100644 instruments/tests/test_abstract_inst/test_electrometer.py create mode 100644 instruments/tests/test_abstract_inst/test_multimeter.py create mode 100644 instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py create mode 100644 instruments/tests/test_abstract_inst/test_oscilloscope.py create mode 100644 instruments/tests/test_abstract_inst/test_power_supply.py diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py index b8e32c93d..7d8a371cd 100644 --- a/instruments/abstract_instruments/optical_spectrum_analyzer.py +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -63,7 +63,8 @@ class OpticalSpectrumAnalyzer(Instrument, metaclass=abc.ABCMeta): # PROPERTIES # - @abc.abstractproperty + @property + @abc.abstractmethod def channel(self): """ Gets an iterator or list for easy Pythonic access to the various diff --git a/instruments/abstract_instruments/oscilloscope.py b/instruments/abstract_instruments/oscilloscope.py index 4d544a2c8..76fb62788 100644 --- a/instruments/abstract_instruments/oscilloscope.py +++ b/instruments/abstract_instruments/oscilloscope.py @@ -52,7 +52,8 @@ def __eq__(self, other): # PROPERTIES # - @abc.abstractproperty + @property + @abc.abstractmethod def name(self): """ Gets the name of the channel. This is an abstract property. @@ -116,7 +117,8 @@ class Oscilloscope(Instrument, metaclass=abc.ABCMeta): # PROPERTIES # - @abc.abstractproperty + @property + @abc.abstractmethod def channel(self): """ Gets an iterator or list for easy Pythonic access to the various @@ -125,7 +127,8 @@ def channel(self): """ raise NotImplementedError - @abc.abstractproperty + @property + @abc.abstractmethod def ref(self): """ Gets an iterator or list for easy Pythonic access to the various @@ -134,7 +137,8 @@ def ref(self): """ raise NotImplementedError - @abc.abstractproperty + @property + @abc.abstractmethod def math(self): """ Gets an iterator or list for easy Pythonic access to the various diff --git a/instruments/tests/test_abstract_inst/test_electrometer.py b/instruments/tests/test_abstract_inst/test_electrometer.py new file mode 100644 index 000000000..878f20112 --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_electrometer.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract electrometer class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def em(monkeypatch): + """Patch and return electrometer class for direct access of metaclass.""" + inst = ik.abstract_instruments.Electrometer + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +def test_electrometer_mode(em): + """Get / set mode to ensure the abstract property exists.""" + with expected_protocol( + em, + [], + [] + ) as inst: + _ = inst.mode + inst.mode = 42 + + +def test_electrometer_unit(em): + """Get unit to ensure the abstract property exists.""" + with expected_protocol( + em, + [], + [] + ) as inst: + _ = inst.unit + + +def test_electrometer_trigger_mode(em): + """Get / set trigger mode to ensure the abstract property exists.""" + with expected_protocol( + em, + [], + [] + ) as inst: + _ = inst.trigger_mode + inst.trigger_mode = 42 + + +def test_electrometer_input_range(em): + """Get / set input range to ensure the abstract property exists.""" + with expected_protocol( + em, + [], + [] + ) as inst: + _ = inst.input_range + inst.input_range = 42 + + +def test_electrometer_zero_check(em): + """Get / set zero check to ensure the abstract property exists.""" + with expected_protocol( + em, + [], + [] + ) as inst: + _ = inst.zero_check + inst.zero_check = 42 + + +def test_electrometer_zero_correct(em): + """Get / set zero correct to ensure the abstract property exists.""" + with expected_protocol( + em, + [], + [] + ) as inst: + _ = inst.zero_correct + inst.zero_correct = 42 + + +def test_electrometer_fetch(em): + """Raise NotImplementedError for fetch method.""" + with expected_protocol( + em, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + inst.fetch() + + +def test_electrometer_read_measurements(em): + """Raise NotImplementedError for read_measurements method.""" + with expected_protocol( + em, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + inst.read_measurements() diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index edf9a37a5..5c7cd3f1c 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -11,6 +11,7 @@ from instruments.units import ureg as u import instruments as ik +from instruments.tests import expected_protocol, unit_eq # TESTS ###################################################################### @@ -170,3 +171,58 @@ def test_func_gen_one_channel_passes_thru_call_setter(fg, mocker): mock_property.assert_called_once_with(1) mock_method.assert_called_once_with(magnitude=1, units=fg.VoltageMode.peak_to_peak) + + +def test_func_gen_channel_set_amplitude_dbm(mocker): + """Get amplitude of channel when units are in dBm.""" + with expected_protocol( + ik.abstract_instruments.FunctionGenerator, + [ + ], [ + ] + ) as inst: + value = 3.14 + # mock out the _get_amplitude of parent to return value in dBm + mocker.patch.object( + inst, '_get_amplitude_', return_value=( + value, + ik.abstract_instruments.FunctionGenerator.VoltageMode.dBm + ) + ) + + channel = inst.channel[0] + unit_eq(channel.amplitude, u.Quantity(value, u.dBm)) + + +def test_func_gen_channel_sendcmd(mocker): + """Send a command via parent class function.""" + with expected_protocol( + ik.abstract_instruments.FunctionGenerator, + [ + ], [ + ] + ) as inst: + cmd = "COMMAND" + # mock out parent's send command + mock_sendcmd = mocker.patch.object(inst, 'sendcmd') + channel = inst.channel[0] + channel.sendcmd(cmd) + mock_sendcmd.assert_called_with(cmd) + + +def test_func_gen__channel_sendcmd(mocker): + """Send a query via parent class function.""" + with expected_protocol( + ik.abstract_instruments.FunctionGenerator, + [ + ], [ + ] + ) as inst: + cmd = "QUERY" + size = 13 + retval = "ANSWER" + # mock out parent's query command + mock_query = mocker.patch.object(inst, 'query', return_value=retval) + channel = inst.channel[0] + assert channel.query(cmd, size=size) == retval + mock_query.assert_called_with(cmd, size) diff --git a/instruments/tests/test_abstract_inst/test_multimeter.py b/instruments/tests/test_abstract_inst/test_multimeter.py new file mode 100644 index 000000000..77b2190b5 --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_multimeter.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract multimeter class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def mul(monkeypatch): + """Patch and return Multimeter class for access.""" + inst = ik.abstract_instruments.Multimeter + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +def test_multimeter_mode(mul): + """Get / set mode: ensure existence.""" + with expected_protocol( + mul, + [], + [] + ) as inst: + _ = inst.mode + inst.mode = 42 + + +def test_multimeter_trigger_mode(mul): + """Get / set trigger mode: ensure existence.""" + with expected_protocol( + mul, + [], + [] + ) as inst: + _ = inst.trigger_mode + inst.trigger_mode = 42 + + +def test_multimeter_relative(mul): + """Get / set relative: ensure existence.""" + with expected_protocol( + mul, + [], + [] + ) as inst: + _ = inst.relative + inst.relative = 42 + + +def test_multimeter_input_range(mul): + """Get / set input range: ensure existence.""" + with expected_protocol( + mul, + [], + [] + ) as inst: + _ = inst.input_range + inst.input_range = 42 + + +def test_multimeter_measure(mul): + """Measure: ensure existence.""" + with expected_protocol( + mul, + [], + [] + ) as inst: + inst.measure('mode') diff --git a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py new file mode 100644 index 000000000..62c6de53f --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract optical spectrum analyzer class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def osa(monkeypatch): + """Patch and return Optical Spectrum Analyzer class for access.""" + inst = ik.abstract_instruments.OpticalSpectrumAnalyzer + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +@pytest.fixture +def osc(monkeypatch): + """Patch and return OSAChannel class for access.""" + inst = ik.abstract_instruments.OSAChannel + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +# OPTICAL SPECTRUM ANALYZER CLASS # + + +def test_osa_channel(osa): + """Get channel: ensure existence.""" + with expected_protocol( + osa, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.channel + + +def test_osa_start_wl(osa): + """Get / set start wavelength: ensure existence.""" + with expected_protocol( + osa, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.start_wl + with pytest.raises(NotImplementedError): + inst.start_wl = 42 + + +def test_osa_stop_wl(osa): + """Get / set stop wavelength: ensure existence.""" + with expected_protocol( + osa, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.stop_wl + with pytest.raises(NotImplementedError): + inst.stop_wl = 42 + + +def test_osa_bandwidth(osa): + """Get / set bandwidth: ensure existence.""" + with expected_protocol( + osa, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.bandwidth + with pytest.raises(NotImplementedError): + inst.bandwidth = 42 + + +def test_osa_start_sweep(osa): + """Start sweep: ensure existence.""" + with expected_protocol( + osa, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + inst.start_sweep() + + +# OSAChannel # + + +def test_osa_channel_wavelength(osc): + """Channel wavelength method: ensure existence.""" + inst = osc() + with pytest.raises(NotImplementedError): + inst.wavelength() + + +def test_osa_channel_data(osc): + """Channel data method: ensure existence.""" + inst = osc() + with pytest.raises(NotImplementedError): + inst.data() diff --git a/instruments/tests/test_abstract_inst/test_oscilloscope.py b/instruments/tests/test_abstract_inst/test_oscilloscope.py new file mode 100644 index 000000000..27a0bdd4b --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_oscilloscope.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract oscilloscope class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def osc(monkeypatch): + """Patch and return Oscilloscope class for access.""" + inst = ik.abstract_instruments.Oscilloscope + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +@pytest.fixture +def osc_ch(monkeypatch): + """Patch and return OscilloscopeChannel class for access.""" + inst = ik.abstract_instruments.OscilloscopeChannel + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +@pytest.fixture +def osc_ds(monkeypatch): + """Patch and return OscilloscopeDataSource class for access.""" + inst = ik.abstract_instruments.OscilloscopeDataSource + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +# OSCILLOSCOPE # + + +def test_oscilloscope_channel(osc): + """Get channel: ensure existence.""" + with expected_protocol( + osc, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.channel + + +def test_oscilloscope_ref(osc): + """Get ref: ensure existence.""" + with expected_protocol( + osc, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.ref + + +def test_oscilloscope_math(osc): + """Get math: ensure existence.""" + with expected_protocol( + osc, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.math + + +def test_oscilloscope_force_trigger(osc): + """Force a trigger: ensure existence.""" + with expected_protocol( + osc, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + inst.force_trigger() + + +# OSCILLOSCOPE CHANNEL # + + +def test_oscilloscope_channel_coupling(osc_ch): + """Get / set channel coupling: ensure existence.""" + inst = osc_ch() + with pytest.raises(NotImplementedError): + _ = inst.coupling + with pytest.raises(NotImplementedError): + inst.coupling = 42 + + +# OSCILLOSCOPE DATA SOURCE # + + +def test_oscilloscope_data_source_init(osc_ds): + """Initialize Oscilloscope Data Source.""" + parent = 'parent' + name = 'name' + inst = osc_ds(parent, name) + assert inst._parent == parent + assert inst._name == name + assert inst._old_dsrc is None + + +def test_oscilloscope_data_source_name(osc_ds): + """Get data source name: ensure existence.""" + parent = 'parent' + name = 'name' + inst = osc_ds(parent, name) + with pytest.raises(NotImplementedError): + _ = inst.name + + +def test_oscilloscope_data_source_read_waveform(osc_ds): + """Read data source waveform: ensure existence.""" + parent = 'parent' + name = 'name' + inst = osc_ds(parent, name) + with pytest.raises(NotImplementedError): + inst.read_waveform() diff --git a/instruments/tests/test_abstract_inst/test_power_supply.py b/instruments/tests/test_abstract_inst/test_power_supply.py new file mode 100644 index 000000000..528b85796 --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_power_supply.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract power supply class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def ps(monkeypatch): + """Patch and return Power Supply class for access.""" + inst = ik.abstract_instruments.PowerSupply + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +@pytest.fixture +def ps_ch(monkeypatch): + """Patch and return Power Supply Channel class for access.""" + inst = ik.abstract_instruments.PowerSupplyChannel + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +# POWER SUPPLY # + + +def test_power_supply_channel(ps): + """Get channel: ensure existence.""" + with expected_protocol( + ps, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.channel + + +def test_power_supply_voltage(ps): + """Get / set voltage: ensure existence.""" + with expected_protocol( + ps, + [], + [] + ) as inst: + _ = inst.voltage + inst.voltage = 42 + + +def test_power_supply_current(ps): + """Get / set current: ensure existence.""" + with expected_protocol( + ps, + [], + [] + ) as inst: + _ = inst.current + inst.current = 42 + + +# POWER SUPPLY CHANNEL # + + +def test_power_supply_channel_mode(ps_ch): + """Get / set channel mode: ensure existence.""" + inst = ps_ch() + _ = inst.mode + inst.mode = 42 + + +def test_power_supply_channel_voltage(ps_ch): + """Get / set channel voltage: ensure existence.""" + inst = ps_ch() + _ = inst.voltage + inst.voltage = 42 + + +def test_power_supply_channel_current(ps_ch): + """Get / set channel current: ensure existence.""" + inst = ps_ch() + _ = inst.current + inst.current = 42 + + +def test_power_supply_channel_output(ps_ch): + """Get / set channel output: ensure existence.""" + inst = ps_ch() + _ = inst.output + inst.output = 42 From 6cb4ec20371364d39715f448be843bc57779c0d7 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Fri, 18 Dec 2020 05:47:15 +0100 Subject: [PATCH 090/108] add space to promt (#303) --- .../tests/test_thorlabs/test_thorlabs_sc10.py | 46 +++++++++---------- instruments/thorlabs/sc10.py | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 14e239fb8..ffe3df288 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -25,7 +25,7 @@ def test_sc10_name(): [ "id?", "bloopbloop", - ">" + "> " ], sep="\r" ) as sc: @@ -42,8 +42,8 @@ def test_sc10_enable(): [ "ens?", "0", - ">ens=1", - ">" + "> ens=1", + "> " ], sep="\r" ) as sc: @@ -71,8 +71,8 @@ def test_sc10_repeat(): [ "rep?", "20", - ">rep=10", - ">" + "> rep=10", + "> " ], sep="\r" ) as sc: @@ -100,8 +100,8 @@ def test_sc10_mode(): [ "mode?", "1", - ">mode=2", - ">" + "> mode=2", + "> " ], sep="\r" ) as sc: @@ -129,8 +129,8 @@ def test_sc10_trigger(): [ "trig?", "0", - ">trig=1", - ">" + "> trig=1", + "> " ], sep="\r" ) as sc: @@ -148,8 +148,8 @@ def test_sc10_out_trigger(): [ "xto?", "0", - ">xto=1", - ">" + "> xto=1", + "> " ], sep="\r" ) as sc: @@ -167,8 +167,8 @@ def test_sc10_open_time(): [ "open?", "20", - ">open=10", - ">" + "> open=10", + "> " ], sep="\r" ) as sc: @@ -186,8 +186,8 @@ def test_sc10_shut_time(): [ "shut?", "20", - ">shut=10", - ">" + "> shut=10", + "> " ], sep="\r" ) as sc: @@ -205,8 +205,8 @@ def test_sc10_baud_rate(): [ "baud?", "0", - ">baud=1", - ">" + "> baud=1", + "> " ], sep="\r" ) as sc: @@ -233,7 +233,7 @@ def test_sc10_closed(): [ "closed?", "1", - ">" + "> " ], sep="\r" ) as sc: @@ -249,7 +249,7 @@ def test_sc10_interlock(): [ "interlock?", "1", - ">" + "> " ], sep="\r" ) as sc: @@ -265,7 +265,7 @@ def test_sc10_default(): [ "default", "1", - ">" + "> " ], sep="\r" ) as sc: @@ -281,7 +281,7 @@ def test_sc10_save(): [ "savp", "1", - ">" + "> " ], sep="\r" ) as sc: @@ -297,7 +297,7 @@ def test_sc10_save_mode(): [ "save", "1", - ">" + "> " ], sep="\r" ) as sc: @@ -313,7 +313,7 @@ def test_sc10_restore(): [ "resp", "1", - ">" + "> " ], sep="\r" ) as sc: diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index d248925b7..5f2f93962 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -31,7 +31,7 @@ class SC10(Instrument): def __init__(self, filelike): super(SC10, self).__init__(filelike) self.terminator = '\r' - self.prompt = '>' + self.prompt = '> ' def _ack_expected(self, msg=""): return msg From ed741359d6a01c8c95a32dfbc734c3a75f8a3f67 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Fri, 18 Dec 2020 23:59:42 -0500 Subject: [PATCH 091/108] Create test suite for abstract signal generator instruments (#302) - Full coverage - Adopt a pytest fixture to directly access abstract classes - Test properties / methods for existence --- .../test_signal_generator/test_channel.py | 51 +++++++++++++++++++ .../test_signal_generator.py | 35 +++++++++++++ .../test_single_channel_sg.py | 34 +++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py create mode 100644 instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py create mode 100644 instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py new file mode 100644 index 000000000..72bd6aeb2 --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract signal generator channel class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik + + +# TESTS ###################################################################### + + +@pytest.fixture +def sgc(monkeypatch): + """Patch and return SGChannel for direct access of metaclass.""" + inst = ik.abstract_instruments.signal_generator.SGChannel + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +def test_sg_channel_frequency(sgc): + """Get / set frequency: Ensure existence.""" + inst = sgc() + _ = inst.frequency + inst.frequency = 42 + + +def test_sg_channel_power(sgc): + """Get / set power: Ensure existence.""" + inst = sgc() + _ = inst.power + inst.power = 42 + + +def test_sg_channel_phase(sgc): + """Get / set phase: Ensure existence.""" + inst = sgc() + _ = inst.phase + inst.phase = 42 + + +def test_sg_channel_output(sgc): + """Get / set output: Ensure existence.""" + inst = sgc() + _ = inst.output + inst.output = 4 diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py new file mode 100644 index 000000000..cf0154d39 --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract signal generator class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def sg(monkeypatch): + """Patch and return signal generator for direct access of metaclass.""" + inst = ik.abstract_instruments.signal_generator.SignalGenerator + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +def test_signal_generator_channel(sg): + """Get channel: Ensure existence.""" + with expected_protocol( + sg, + [], + [] + ) as inst: + with pytest.raises(NotImplementedError): + _ = inst.channel diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py new file mode 100644 index 000000000..15234025d --- /dev/null +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Module containing tests for the abstract signal generator class +""" + +# IMPORTS #################################################################### + + +import pytest + +import instruments as ik +from instruments.tests import expected_protocol + + +# TESTS ###################################################################### + + +@pytest.fixture +def scsg(monkeypatch): + """Patch and return signal generator for direct access of metaclass.""" + inst = ik.abstract_instruments.signal_generator.SingleChannelSG + monkeypatch.setattr(inst, '__abstractmethods__', set()) + return inst + + +def test_signal_generator_channel(scsg): + """Get channel: Ensure existence.""" + with expected_protocol( + scsg, + [], + [] + ) as inst: + assert inst.channel[0] == inst From 7d9accea12eb3c3c4db7b2d8bc75d4dedaf520cf Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Mon, 5 Jul 2021 10:09:46 -0400 Subject: [PATCH 092/108] BFs for visa_communicator and tests (#308) * BFs for visa_communicator and tests - read_raw() now uses read_raw() of pyvisa and not read() - Not implemented functions now raise NotImplementedError - Removed testing if pyvisa is present * Changed description at start of tests to correct title --- dev-requirements.txt | 1 + .../comm/visa_communicator.py | 9 +- .../tests/test_comm/test_visa_communicator.py | 179 ++++++++++++++++++ 3 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 instruments/tests/test_comm/test_visa_communicator.py diff --git a/dev-requirements.txt b/dev-requirements.txt index a5adf1678..58edc2bb1 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,3 +3,4 @@ pytest==6.1.1 pytest-mock hypothesis==4.28.2 pylint==2.4.4 +pyvisa-sim diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index d32cbb5b8..154fde4a4 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -29,9 +29,6 @@ class VisaCommunicator(io.IOBase, AbstractCommunicator): def __init__(self, conn): super(VisaCommunicator, self).__init__(self) - if pyvisa is None: - raise ImportError("PyVISA required for accessing VISA instruments.") - version = int(pyvisa.__version__.replace(".", "").ljust(3, "0")) # pylint: disable=no-member if (version < 160 and isinstance(conn, pyvisa.Instrument)) or \ @@ -118,7 +115,7 @@ def read_raw(self, size=-1): elif size == -1: # Read the whole contents, appending the buffer we've already read. - msg = self._buf + self._conn.read() + msg = self._buf + self._conn.read_raw() # Reset the contents of the buffer. self._buf = bytearray() else: @@ -136,10 +133,10 @@ def write_raw(self, msg): self._conn.write_raw(msg) def seek(self, offset): # pylint: disable=unused-argument,no-self-use - return NotImplemented + raise NotImplementedError def tell(self): # pylint: disable=no-self-use - return NotImplemented + raise NotImplementedError def flush_input(self): """ diff --git a/instruments/tests/test_comm/test_visa_communicator.py b/instruments/tests/test_comm/test_visa_communicator.py new file mode 100644 index 000000000..2bccd7f44 --- /dev/null +++ b/instruments/tests/test_comm/test_visa_communicator.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the VISA communication layer +""" + +# IMPORTS #################################################################### + + +import pytest +import pyvisa + +from instruments.units import ureg as u +from instruments.abstract_instruments.comm import VisaCommunicator + + +# TEST CASES ################################################################# + +# pylint: disable=protected-access,redefined-outer-name + +# create a visa instrument +@pytest.fixture() +def visa_inst(): + """Create a default visa-sim instrument and return it.""" + inst = pyvisa.ResourceManager("@sim").open_resource("ASRL1::INSTR") + return inst + + +def test_visacomm_init(visa_inst): + """Initialize visa communicator.""" + comm = VisaCommunicator(visa_inst) + assert comm._conn == visa_inst + assert comm._terminator == "\n" + assert comm._buf == bytearray() + + +def test_visacomm_init_wrong_type(): + """Raise TypeError if not a VISA instrument.""" + with pytest.raises(TypeError) as err: + VisaCommunicator(42) + err_msg = err.value.args[0] + assert err_msg == "VisaCommunicator must wrap a VISA Instrument." + + +def test_visacomm_address(visa_inst): + """Get / Set instrument address.""" + comm = VisaCommunicator(visa_inst) + assert comm.address == visa_inst.resource_name + with pytest.raises(NotImplementedError) as err: + comm.address = "new address" + err_msg = err.value.args[0] + assert err_msg == ( + "Changing addresses of a VISA Instrument is not supported." + ) + + +def test_visacomm_terminator(visa_inst): + """Get / Set terminator.""" + comm = VisaCommunicator(visa_inst) + comm.terminator = ("\r") + assert comm.terminator == "\r" + + +def test_visacomm_terminator_not_string(visa_inst): + """Raise TypeError if terminator is set with non-string character.""" + comm = VisaCommunicator(visa_inst) + with pytest.raises(TypeError) as err: + comm.terminator = 42 + err_msg = err.value.args[0] + assert err_msg == ( + "Terminator for VisaCommunicator must be specified as a single " + "character string." + ) + + +def test_visacomm_terminator_not_single_char(visa_inst): + """Raise ValueError if terminator longer than one character.""" + comm = VisaCommunicator(visa_inst) + with pytest.raises(ValueError) as err: + comm.terminator = "\r\n" + err_msg = err.value.args[0] + assert err_msg == ( + "Terminator for VisaCommunicator must only be 1 character long." + ) + + +def test_visacomm_timeout(visa_inst): + """Set / Get timeout of VISA communicator.""" + comm = VisaCommunicator(visa_inst) + comm.timeout = 3 + assert comm.timeout == u.Quantity(3, u.s) + comm.timeout = u.Quantity(40000, u.ms) + assert comm.timeout == u.Quantity(40, u.s) + + +def test_visacomm_close(visa_inst, mocker): + """Raise an IOError if comms cannot be closed.""" + io_error_mock = mocker.Mock() + io_error_mock.side_effect = IOError + mock_close = mocker.patch.object(visa_inst, 'close', io_error_mock) + comm = VisaCommunicator(visa_inst) + comm.close() + mock_close.assert_called() # but error will just pass! + + +def test_visacomm_read_raw(visa_inst, mocker): + """Read raw data from instrument without size specification.""" + comm = VisaCommunicator(visa_inst) + mock_read_raw = mocker.patch.object( + visa_inst, 'read_raw', return_value=b'asdf' + ) + comm.read_raw() + mock_read_raw.assert_called() + assert comm._buf == bytearray() + + +def test_visacomm_read_raw_size(visa_inst, mocker): + """Read raw data from instrument with size specification.""" + comm = VisaCommunicator(visa_inst) + size = 3 + mock_read_bytes = mocker.patch.object( + visa_inst, 'read_bytes', return_value=b'123' + ) + ret_val = comm.read_raw(size=size) + assert ret_val == b'123' + mock_read_bytes.assert_called() + assert comm._buf == bytearray() + + +def test_visacomm_read_raw_wrong_size(visa_inst): + """Raise ValueError if size is invalid.""" + comm = VisaCommunicator(visa_inst) + with pytest.raises(ValueError) as err: + comm.read_raw(size=-3) + err_msg = err.value.args[0] + assert err_msg == ( + "Must read a positive value of characters, or -1 for all characters." + ) + + +def test_visacomm_write_raw(visa_inst, mocker): + """Write raw message to instrument.""" + mock_write = mocker.patch.object(visa_inst, 'write_raw') + comm = VisaCommunicator(visa_inst) + msg = b'12345' + comm.write_raw(msg) + mock_write.assert_called_with(msg) + + +def test_visacomm_seek_not_implemented(visa_inst): + """Raise NotImplementedError when calling seek.""" + comm = VisaCommunicator(visa_inst) + with pytest.raises(NotImplementedError): + comm.seek(42) + + +def test_visacomm_tell_not_implemented(visa_inst): + """Raise NotImplementedError when calling tell.""" + comm = VisaCommunicator(visa_inst) + with pytest.raises(NotImplementedError): + comm.tell() + + +def test_visacomm_sendcmd(visa_inst, mocker): + """Write to device.""" + mock_write = mocker.patch.object(VisaCommunicator, 'write') + comm = VisaCommunicator(visa_inst) + msg = 'asdf' + comm._sendcmd(msg) + mock_write.assert_called_with(msg + comm.terminator) + + +def test_visacomm_query(visa_inst, mocker): + """Query device.""" + mock_query = mocker.patch.object(visa_inst, 'query') + comm = VisaCommunicator(visa_inst) + msg = 'asdf' + comm._query(msg) + mock_query.assert_called_with(msg + comm.terminator) From 7d506983f5433e4c6246bb839085c51fb734c76d Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 13 Jul 2021 00:03:30 -0400 Subject: [PATCH 093/108] Add tests for full coverage of instrument.py. Remove legacy checks. (#309) * Add tests for full coverage of instrument.py. Remove legacy checks. - Added tests for `open_usb` and `open_gpibethernet` - Removed checks for modules `usb` and `pyvisa` not present since they are now by default part of IK * Require pyusb >= 1.0 --- .../abstract_instruments/instrument.py | 7 -- instruments/tests/test_base_instrument.py | 83 ++++++++++++++++++- setup.cfg | 2 +- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 1ca869d5d..a8b0ef2a1 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -584,9 +584,6 @@ def open_visa(cls, resource_name): .. _PyVISA: http://pyvisa.sourceforge.net/ """ - if pyvisa is None: - raise ImportError("PyVISA is required for loading VISA " - "instruments.") version = list(map(int, pyvisa.__version__.split("."))) while len(version) < 3: version += [0] @@ -671,10 +668,6 @@ def open_usb(cls, vid, pid): :return: Object representing the connected instrument. """ # pylint: disable=no-member - if usb is None: - raise ImportError("USB support not imported. Do you have PyUSB " - "version 1.0 or later?") - dev = usb.core.find(idVendor=vid, idProduct=pid) if dev is None: raise IOError("No such device found.") diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 34a540a82..3862b5142 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -10,6 +10,7 @@ import socket import io import serial +import usb.core from serial.tools.list_ports_common import ListPortInfo import pytest @@ -276,10 +277,21 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): ) -@mock.patch("instruments.abstract_instruments.instrument.pyvisa", new=None) -def test_instrument_open_visa_import_error(): - with pytest.raises(ImportError): - _ = ik.Instrument.open_visa("abc123") +@mock.patch("instruments.abstract_instruments.instrument.GPIBCommunicator") +@mock.patch("instruments.abstract_instruments.instrument.socket") +def test_instrument_open_gpibethernet(mock_socket_manager, mock_gpib_comm): + mock_gpib_comm.return_value.__class__ = GPIBCommunicator + + host = "192.168.1.13" + port = 1818 + + inst = ik.Instrument.open_gpibethernet( + host, port, gpib_address=1, model="pl" + ) + + mock_socket_manager.socket.assert_called() + mock_socket_manager.socket().connect.assert_called_with((host, port)) + assert isinstance(inst._file, GPIBCommunicator) is True @mock.patch("instruments.abstract_instruments.instrument.VisaCommunicator") @@ -334,6 +346,69 @@ def test_instrument_open_vxi11(mock_vxi11_comm): mock_vxi11_comm.assert_called_with("string", 1, key1="value") +@mock.patch("instruments.abstract_instruments.instrument.usb") +def test_instrument_open_usb(mock_usb): + """Open USB device.""" + # mock some behavior + mock_usb.core.find.return_value.__class__ = usb.core.Device # dev + mock_usb.core.find().get_active_configuration.return_value.__class__ = ( + usb.core.Configuration + ) + + # shortcuts for asserting calls + dev = mock_usb.core.find() + cfg = dev.get_active_configuration() + interface_number = cfg[(0, 0)].bInterfaceNumber + alternate_setting = mock_usb.control.get_interface( + dev, cfg[(0, 0)].bInterfaceNumber + ) + + # call instrument + inst = ik.Instrument.open_usb("0x1000", 0x1000) + + # assert calls according to manual + dev.set_configuration.assert_called() # check default configuration + dev.get_active_configuration.assert_called() # get active configuration + mock_usb.control.get_interface.assert_called_with(dev, interface_number) + mock_usb.util.find_descriptor.assert_any_call( + cfg, + bInterfaceNumber=interface_number, + bAlternateSetting=alternate_setting + ) + # check the first argument of the `ep =` call + assert mock_usb.util.find_descriptor.call_args_list[1][0][0] == ( + mock_usb.util.find_descriptor( + cfg, + bInterfaceNumber=interface_number, + bAlternateSetting=alternate_setting + ) + ) + + # assert instrument of correct class + assert isinstance(inst._file, USBCommunicator) + + +@mock.patch("instruments.abstract_instruments.instrument.usb") +def test_instrument_open_usb_no_device(mock_usb): + """Open USB, no device found.""" + mock_usb.core.find.return_value = None # mock no instrument found + with pytest.raises(IOError) as err: + _ = ik.Instrument.open_usb(0x1000, 0x1000) + err_msg = err.value.args[0] + assert err_msg == "No such device found." + + +@mock.patch("instruments.abstract_instruments.instrument.usb") +def test_instrument_open_usb_ep_none(mock_usb): + """Raise IOError if endpoint matching returns None.""" + mock_usb.util.find_descriptor.return_value = None + + with pytest.raises(IOError) as err: + _ = ik.Instrument.open_usb(0x1000, 0x1000) + err_msg = err.value.args[0] + assert err_msg == "USB descriptor not found." + + @mock.patch("instruments.abstract_instruments.instrument.USBTMCCommunicator") def test_instrument_open_usbtmc(mock_usbtmc_comm): mock_usbtmc_comm.return_value.__class__ = USBTMCCommunicator diff --git a/setup.cfg b/setup.cfg index 1171e021f..7b0dcf4d7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ install_requires = pyvisa>=1.9 python-vxi11>=0.8 python-usbtmc - pyusb + pyusb>=1.0 ruamel.yaml~=0.15.37 pint>=0.16.1 From 6d216bd7f8e9ec7918762fe5fb7a306d5bd0eb1f Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Thu, 28 Oct 2021 18:25:57 -0700 Subject: [PATCH 094/108] Bug fix for `lcc25.py`, add a space after prompt ">" (#311) --- .../test_thorlabs/test_thorlabs_lcc25.py | 64 +++++++++---------- instruments/thorlabs/lcc25.py | 2 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index e18d3ad61..7e9e272ef 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -25,7 +25,7 @@ def test_lcc25_name(): [ "*idn?", "bloopbloop", - ">" + "> " ], sep="\r" ) as lcc: @@ -43,8 +43,8 @@ def test_lcc25_frequency(): [ "freq?", "20", - ">freq=10.0", - ">" + "> freq=10.0", + "> " ], sep="\r" ) as lcc: @@ -60,7 +60,7 @@ def test_lcc25_frequency_lowlimit(): ], [ "freq=0.0", - ">" + "> " ], sep="\r" ) as lcc: @@ -75,7 +75,7 @@ def test_lcc25_frequency_highlimit(): ], [ "freq=160.0", - ">" + "> " ], sep="\r" ) as lcc: @@ -92,8 +92,8 @@ def test_lcc25_mode(): [ "mode?", "2", - ">mode=1", - ">" + "> mode=1", + "> " ], sep="\r" ) as lcc: @@ -120,8 +120,8 @@ def test_lcc25_enable(): [ "enable?", "0", - ">enable=1", - ">" + "> enable=1", + "> " ], sep="\r" ) as lcc: @@ -148,8 +148,8 @@ def test_lcc25_extern(): [ "extern?", "0", - ">extern=1", - ">" + "> extern=1", + "> " ], sep="\r" ) as lcc: @@ -176,8 +176,8 @@ def test_lcc25_remote(): [ "remote?", "0", - ">remote=1", - ">" + "> remote=1", + "> " ], sep="\r" ) as lcc: @@ -204,8 +204,8 @@ def test_lcc25_voltage1(): [ "volt1?", "20", - ">volt1=10.0", - ">" + "> volt1=10.0", + "> " ], sep="\r" ) as lcc: @@ -229,8 +229,8 @@ def test_lcc25_voltage2(): [ "volt2?", "20", - ">volt2=10.0", - ">" + "> volt2=10.0", + "> " ], sep="\r" ) as lcc: @@ -248,8 +248,8 @@ def test_lcc25_minvoltage(): [ "min?", "20", - ">min=10.0", - ">" + "> min=10.0", + "> " ], sep="\r" ) as lcc: @@ -267,8 +267,8 @@ def test_lcc25_maxvoltage(): [ "max?", "20", - ">max=10.0", - ">" + "> max=10.0", + "> " ], sep="\r" ) as lcc: @@ -286,8 +286,8 @@ def test_lcc25_dwell(): [ "dwell?", "20", - ">dwell=10", - ">" + "> dwell=10", + "> " ], sep="\r" ) as lcc: @@ -303,7 +303,7 @@ def test_lcc25_dwell_positive(): ], [ "dwell=-10", - ">" + "> " ], sep="\r" ) as lcc: @@ -320,8 +320,8 @@ def test_lcc25_increment(): [ "increment?", "20", - ">increment=10.0", - ">" + "> increment=10.0", + "> " ], sep="\r" ) as lcc: @@ -337,7 +337,7 @@ def test_lcc25_increment_positive(): ], [ "increment=-10", - ">" + "> " ], sep="\r" ) as lcc: @@ -353,7 +353,7 @@ def test_lcc25_default(): [ "default", "1", - ">" + "> " ], sep="\r" ) as lcc: @@ -369,7 +369,7 @@ def test_lcc25_save(): [ "save", "1", - ">" + "> " ], sep="\r" ) as lcc: @@ -385,7 +385,7 @@ def test_lcc25_set_settings(): [ "set=2", "1", - ">" + "> " ], sep="\r" ) as lcc: @@ -411,7 +411,7 @@ def test_lcc25_get_settings(): [ "get=2", "1", - ">" + "> " ], sep="\r" ) as lcc: @@ -437,7 +437,7 @@ def test_lcc25_test_mode(): [ "test", "1", - ">" + "> " ], sep="\r" ) as lcc: diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index 593b77a2f..45463527d 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -33,7 +33,7 @@ class LCC25(Instrument): def __init__(self, filelike): super(LCC25, self).__init__(filelike) self.terminator = "\r" - self.prompt = ">" + self.prompt = "> " def _ack_expected(self, msg=""): return msg From 73eff1c1044003ba1465402edc386d9286f74f5a Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 18 Jan 2022 22:05:07 -0500 Subject: [PATCH 095/108] Newport picomotor controller 8742 (#312) * Support for Newport Picomotor Controller 8742 Full functionality for this controller in single-controller and multi-controller mode (main / secondaries via RS-485) is implemented. Only single-controller mode is tested with hardware at the moment. Full test suite however includes tests for multi-controller modes. Routines were documented in docstring and also in Sphinx documentation. Note: Control via USB is currently not functional, since InstrumentKit has some issues with the real USB communications class. Needs separate PR. * Added a `read_raw` routine to `instrument.py` Reading raw data from the instrument is required for the picomotor control class, however, had so far to be done by directly accessing a private argument of `instrument.py` (not pretty). * Replaced extensive hypothesis filtering with parametrized tests This type of filtering has previously resulted in health check failures with hypothesis since too many values were filtered out. All these tests do is ensure that the correct error is raised. * Remove print statement --- doc/source/apiref/newport.rst | 6 + .../abstract_instruments/instrument.py | 12 + instruments/newport/__init__.py | 2 + instruments/newport/newport_pmc8742.py | 1161 +++++++++++++++++ .../test_newport/test_newport_pmc8742.py | 858 ++++++++++++ 5 files changed, 2039 insertions(+) create mode 100644 instruments/newport/newport_pmc8742.py create mode 100644 instruments/tests/test_newport/test_newport_pmc8742.py diff --git a/doc/source/apiref/newport.rst b/doc/source/apiref/newport.rst index ec5fc1631..55bc8a364 100644 --- a/doc/source/apiref/newport.rst +++ b/doc/source/apiref/newport.rst @@ -40,3 +40,9 @@ Newport :members: :undoc-members: +:class:`PicoMotorController8742` +================================ + +.. autoclass:: PicoMotorController8742 + :members: + :undoc-members: diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index a8b0ef2a1..57c258143 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -144,6 +144,18 @@ def read(self, size=-1, encoding="utf-8"): """ return self._file.read(size, encoding) + def read_raw(self, size=-1): + """ + Read the raw last line. + + :param int size: Number of bytes to be read. Default is read until + termination character is found. + :return: The result of the read as returned by the + connected instrument. + :rtype: `str` + """ + return self._file.read_raw(size) + # PROPERTIES # @property diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index 14a02ca1e..45e751b55 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -10,3 +10,5 @@ from .newportesp301 import ( NewportESP301, NewportESP301Axis, NewportESP301HomeSearchMode ) + +from .newport_pmc8742 import PicoMotorController8742 diff --git a/instruments/newport/newport_pmc8742.py b/instruments/newport/newport_pmc8742.py new file mode 100644 index 000000000..fb4290bee --- /dev/null +++ b/instruments/newport/newport_pmc8742.py @@ -0,0 +1,1161 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Provides support for the Newport Pico Motor Controller 8742 + +Note that the class is currently only tested with one controller connected, +however, a main controller / secondary controller setup has also been +implemented already. Commands are as described in the Picomotor manual. + +If a connection via TCP/IP is opened, the standard port that these devices +listen to is 23. + +If you have only one controller connected, everything should work out of +the box. Please only use axiss 0 through 3. + +If you have multiple controllers connected (up to 31), you need to set the +addresses of each controller. This can be done with this this class. See, +e.g., routines for `controller_address`, `scan_controller`, and `scan`. +Also make sure that you set `multiple_controllers` to `True`. This is +used for internal handling of the class only and does not communicate with +the instruments. +If you run with multiple controllers, the axiss are as following: +Ch 0 - 3 -> Motors 1 - 4 on controller with address 1 +Ch 4 - 7 -> Motors 1 - 4 on controller with address 2 +Ch i - i+4 -> Motors 1 - 4 on controller with address i / 4 + 1 (with i%4 = 0) + +All network commands only work with the main controller (this should make +sense). + +If in multiple controller mode, you can always send controller specific +commands by sending them to one individual axis of that controller. +Any axis works! +""" + +# IMPORTS # + +from enum import IntEnum + +from instruments.abstract_instruments import Instrument +from instruments.units import ureg as u +from instruments.util_fns import assume_units, ProxyList + +# pylint: disable=too-many-lines + + +class PicoMotorController8742(Instrument): + """Newport Picomotor Controller 8742 Communications Class + + Use this class to communicate with the picomotor controller 8742. + Single-controller and multi-controller setup can be used. + + Device can be talked to via TCP/IP or over USB. + FixMe: InstrumentKit currently does not communicate correctly via USB! + + Example for TCP/IP controller in single controller mode: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> motor1 = inst.axis[0] + >>> motor1.move_relative = 100 + + Example for communications via USB: + >>> import instruments as ik + >>> pid = 0x4000 + >>> vid = 0x104d + >>> ik.newport.PicoMotorController8742.open_usb(pid=pid, vid=vid) + >>> motor3 = inst.axis[2] + >>> motor3.move_absolute = -200 + + Example for multicontrollers with controller addresses 1 and 2: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.multiple_controllers = True + >>> contr1mot1 = inst.axis[0] + >>> contr2mot1 = inst.axis[4] + >>> contr1mot1.move_absolute = 200 + >>> contr2mot1.move_relative = -212 + """ + + def __init__(self, filelike): + """Initialize the PicoMotorController class.""" + super(PicoMotorController8742, self).__init__(filelike) + + # terminator + self.terminator = "\r\n" + + # setup + self._multiple_controllers = False + + # INNER CLASSES # + + class Axis: + """PicoMotorController8742 Axis class for individual motors.""" + def __init__(self, parent, idx): + """Initialize the axis with the parent and the number. + + :raises IndexError: Axis accessed looks like a main / secondary + setup, but the flag for `multiple_controllers` is not set + appropriately. See introduction. + """ + if not isinstance(parent, PicoMotorController8742): + raise TypeError("Don't do that.") + + if idx > 3 and not parent.multiple_controllers: + raise IndexError("You requested an axis that is only " + "available in multi controller mode, " + "however, have not enabled it. See " + "`multi_controllers` routine.") + + # set controller + self._parent = parent + self._idx = idx % 4 + 1 + + # set _address: + if self._parent.multiple_controllers: + self._address = f"{idx // 4 + 1}>" + else: + self._address = "" + + # ENUMS # + + class MotorType(IntEnum): + """IntEnum Class containing valid MotorTypes + + Use this enum to set the motor type. You can select that no or an + unkown motor are connected. See also `motor_check` command to set + these values per controller automatically. + """ + none = 0 + unknown = 1 + tiny = 2 + standard = 3 + + # PROPERTIES # + + @property + def acceleration(self): + """Get / set acceleration of axis in steps / sec^2. + + Valid values are between 1 and 200,000 (steps) 1 / sec^2 with the + default as 100,000 (steps) 1 / sec^2. If quantity is not unitful, + it is assumed that 1 / sec^2 is chosen. + + :return: Acceleration in 1 / sec^2 + :rtype: u.Quantity(int) + + :raises ValueError: Limit is out of bound. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.acceleration = u.Quantity(500, 1/u.s**-2) + """ + return assume_units(int(self.query("AC?")), u.s**-2) + + @acceleration.setter + def acceleration(self, value): + value = int(assume_units(value, u.s**-2).to(u.s**-2).magnitude) + if not 1 <= value <= 200000: + raise ValueError(f"Acceleration must be between 1 and " + f"200,000 s^-2 but is {value}.") + self.sendcmd(f"AC{value}") + + @property + def home_position(self): + """Get / set home position + + The home position of the device is used, e.g., when moving + to a specific position instead of a relative move. Valid values + are between -2147483648 and 2147483647. + + :return: Home position. + :rtype: int + + :raises ValueError: Set value is out of range. + + Example: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.home_position = 444 + """ + return int(self.query("DH?")) + + @home_position.setter + def home_position(self, value): + if not -2147483648 <= value <= 2147483647: + raise ValueError(f"Home position must be between -2147483648 " + f"and 2147483647, but is {value}.") + self.sendcmd(f"DH{int(value)}") + + @property + def is_stopped(self): + """Get if an axis is stopped (not moving). + + :return: Is the axis stopped? + :rtype: bool + + Example: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.is_stopped + True + """ + return bool(int(self.query("MD?"))) + + @property + def motor_type(self): + """Set / get the type of motor connected to the axis. + + Use a `MotorType` IntEnum to set this motor type. + + :return: Motor type set. + :rtype: MotorType + + :raises TypeError: Set motor type is not of type `MotorType`. + + Example: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.motor_type = ax.MotorType.tiny + """ + retval = int(self.query("QM?")) + return self.MotorType(retval) + + @motor_type.setter + def motor_type(self, value): + if not isinstance(value, self.MotorType): + raise TypeError(f"Set motor type must be of type `MotorType` " + f"but is of type {type(value)}.") + self.sendcmd(f"QM{value.value}") + + @property + def move_absolute(self): + """Get / set the absolute target position of a motor. + + Set with absolute position in steps. Valid values between + -2147483648 and +2147483647. + See also: `home_position`. + + :return: Absolute motion target position. + :rtype: int + + :raises ValueError: Requested position out of range. + + Example: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.move_absolute = 100 + """ + return int(self.query("PA?")) + + @move_absolute.setter + def move_absolute(self, value): + if not -2147483648 <= value <= 2147483647: + raise ValueError(f"Set position must be between -2147483648 " + f"and 2147483647, but is {value}.") + self.sendcmd(f"PA{int(value)}") + + @property + def move_relative(self): + """Get / set the relative target position of a motor. + + Set with relative motion in steps. Valid values between + -2147483648 and +2147483647. + See also: `home_position`. + + :return: Relative motion target position. + :rtype: int + + :raises ValueError: Requested motion out of range. + + Example: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.move_relative = 100 + """ + return int(self.query("PR?")) + + @move_relative.setter + def move_relative(self, value): + if not -2147483648 <= value <= 2147483647: + raise ValueError(f"Set motion must be between -2147483648 " + f"and 2147483647, but is {value}.") + self.sendcmd(f"PR{int(value)}") + + @property + def position(self): + """Queries current, actual position of motor. + + Positions are with respect to the home position. + + :return: Current position in steps. + :rtype: int + + Example: + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.position + 123 + """ + return int(self.query("TP?")) + + @property + def velocity(self): + """Get / set velocty of the connected motor (unitful). + + Velocity is given in (steps) per second (1/s). + If a `MotorType.tiny` motor is connected, the maximum velocity + allowed is 1750 /s, otherwise 2000 /s. + If no units are given, 1/s are assumed. + + :return: Velocity in 1/s + :rtype: u.Quantity(int) + + :raises ValueError: Set value is out of the allowed range. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.velocity = u.Quantity(500, 1/u.s) + """ + retval = int(self.query("VA?")) + return u.Quantity(retval, 1/u.s) + + @velocity.setter + def velocity(self, value): + if self.motor_type == self.MotorType.tiny: + max_velocity = 1750 + else: + max_velocity = 2000 + + value = int(assume_units(value, 1 / u.s).to(1 / u.s).magnitude) + if not 0 < value <= max_velocity: + raise ValueError(f"The maximum allowed velocity for the set " + f"motor is {max_velocity}. The requested " + f"velocity of {value} is out of range.") + self.sendcmd(f"VA{value}") + + # METHODS # + + def move_indefinite(self, direction): + """Move the motor indefinitely in the specific direction. + + To stop motion, issue `stop_motion` or `abort_motion` command. + Direction is defined as a string of either "+" or "-". + + :param direction: Direction in which to move the motor, "+" or "-" + :type direction: str + + Example: + >>> from time import sleep + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.move_indefinite("+") + >>> sleep(1) # wait a second + >>> ax.stop() + """ + if direction in ["+", "-"]: + self.sendcmd(f"MV{direction}") + + def stop(self): + """Stops the specific axis if in motion. + + Example: + >>> from time import sleep + >>> import instruments as ik + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + >>> ax.move_indefinite("+") + >>> sleep(1) # wait a second + >>> ax.stop() + """ + self.sendcmd("ST") + + # CONTROLLER SPECIFIC PROPERTIES # + + @property + def controller_address(self): + """Get / set the controller address. + + Valid address values are between 1 and 31. For setting up multiple + instruments, see `multiple_controllers`. + + :return: Address of this device if secondary, otherwise `None` + :rtype: int + """ + retval = int(self.query("SA?", axs=False)) + return retval + + @controller_address.setter + def controller_address(self, newval): + self.sendcmd(f"SA{int(newval)}", axs=False) + + @property + def controller_configuration(self): + """Get / set configuration of some of the controller’s features. + + Configuration is given as a bit mask. If changed, please save + the settings afterwards if you would like to do so. See + `save_settings`. + + The bitmask to be set can be either given as a number, or as a + string of the mask itself. The following values are equivalent: + 3, 0b11, "11" + + Bit 0: + Value 0: Perform auto motor detection. Check and set motor + type automatically when commanded to move. + Value 1: Do not perform auto motor detection on move. + Bit 1: + Value 0: Do not scan for motors connected to controllers upon + reboot (Performs ‘MC’ command upon power-up, reset or + reboot). + Value 1: Scan for motors connected to controller upon power-up + or reset. + + :return: Bitmask of the controller configuration. + :rtype: str, binary configuration + """ + return self.query('ZZ?', axs=False) + + @controller_configuration.setter + def controller_configuration(self, value): + if isinstance(value, str): + self.sendcmd(f"ZZ{value}", axs=False) + else: + self.sendcmd(f"ZZ{str(bin(value))[2:]}", axs=False) + + @property + def error_code(self): + """Get error code only. + + Error code0 means no error detected. + + :return: Error code. + :rtype: int + """ + return int(self.query("TE?", axs=False)) + + @property + def error_code_and_message(self): + """Get error code and message. + + :return: Error code, error message + :rtype: int, str + """ + retval = self.query("TB?", axs=False) + err_code, err_msg = retval.split(",") + err_code = int(err_code) + err_msg = err_msg.strip() + return err_code, err_msg + + @property + def firmware_version(self): + """Get the controller firmware version.""" + return self.query("VE?", axs=False) + + @property + def name(self): + """Get the name of the controller.""" + return self.query("*IDN?", axs=False) + + # CONTROLLER SPECIFIC METHODS # + + def abort_motion(self): + """Instantaneously stops any motion in progress.""" + self.sendcmd("AB", axs=False) + + def motor_check(self): + """Check what motors are connected and set parameters. + + Use the save command `save_settings` if you want to save the + configuration to the non-volatile memory. + """ + self.sendcmd("MC", axs=False) + + def purge(self): + """Purge the non-volatile memory of the controller. + + Perform a hard reset and reset all the saved variables. The + following variables are reset to factory settings: + 1. Hostname + 2. IP Mode + 3. IP Address + 4. Subnet mask address + 5. Gateway address + 6. Configuration register + 7. Motor type + 8. Desired Velocity + 9. Desired Acceleration + """ + self.sendcmd("XX", axs=False) + + def recall_parameters(self, value=0): + """Recall parameter set. + + This command restores the controller working parameters from values + saved in its non-volatile memory. It is useful when, for example, + the user has been exploring and changing parameters (e.g., velocity) + but then chooses to reload from previously stored, qualified + settings. Note that “*RCL 0” command just restores the working + parameters to factory default settings. It does not change the + settings saved in EEPROM. + + :param value: 0 -> Recall factory default, + 1 -> Recall last saved settings + :type int: + """ + self.sendcmd(f"*RCL{1 if value else 0}", axs=False) + + def reset(self): + """Reset the controller. + + Perform a soft reset. Saved variables are not deleted! For a + hard reset, see the `purge` command. + + ..note:: It might take up to 30 seconds to re-establish + communications via TCP/IP + """ + self.sendcmd("*RST", axs=False) + + def save_settings(self): + """Save user settings. + + This command saves the controller settings in its non-volatile memory. + The controller restores or reloads these settings to working registers + automatically after system reset or it reboots. The purge + command is used to clear non-volatile memory and restore to factory + settings. Note that the SM saves parameters for all motors. + + Saves the following variables: + 1. Controller address + 2. Hostname + 3. IP Mode + 4. IP Address + 5. Subnet mask address + 6. Gateway address + 7. Configuration register + 8. Motor type + 9. Desired Velocity + 10. Desired Acceleration + """ + self.sendcmd("SM", axs=False) + + # SEND AND QUERY # + + def sendcmd(self, cmd, axs=True): + """Send a command to an axis object. + + :param cmd: Command + :type cmd: str + :param axs: Send axis address along? Not used for controller + commands. Defaults to `True` + :type axs: bool + """ + if axs: + command = f"{self._address}{self._idx}{cmd}" + else: + command = f"{self._address}{cmd}" + self._parent.sendcmd(command) + + def query(self, cmd, size=-1, axs=True): + """Query for an axis object. + + :param cmd: Command + :type cmd: str + :param size: bytes to read, defaults to "until terminator" (-1) + :type size: int + :param axs: Send axis address along? Not used for controller + commands. Defaults to `True` + :type axs: bool + + :raises IOError: The wrong axis answered. + """ + if axs: + command = f"{self._address}{self._idx}{cmd}" + else: + command = f"{self._address}{cmd}" + + retval = self._parent.query(command, size=size) + + if retval[:len(self._address)] != self._address: + raise IOError(f"Expected to hear back from secondary " + f"controller {self._address}, instead " + f"controller {retval[:len(self._address)]} " + f"answered.") + + return retval[len(self._address):] + + @property + def axis(self): + """Return an axis object. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> ax = inst.axis[0] + """ + return ProxyList(self, self.Axis, range(31 * 4)) + + @property + def controller_address(self): + """Get / set the controller address. + + Valid address values are between 1 and 31. For setting up multiple + instruments, see `multiple_controllers`. + + :return: Address of this device if secondary, otherwise `None` + :rtype: int + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.controller_address = 13 + """ + return self.axis[0].controller_address + + @controller_address.setter + def controller_address(self, newval): + self.axis[0].controller_address = newval + + @property + def controller_configuration(self): + """Get / set configuration of some of the controller’s features. + + Configuration is given as a bit mask. If changed, please save + the settings afterwards if you would like to do so. See + `save_settings`. + + Bit 0: + Value 0: Perform auto motor detection. Check and set motor + type automatically when commanded to move. + Value 1: Do not perform auto motor detection on move. + Bit 1: + Value 0: Do not scan for motors connected to controllers upon + reboot (Performs ‘MC’ command upon power-up, reset or + reboot). + Value 1: Scan for motors connected to controller upon power-up + or reset. + + :return: Bitmask of the controller configuration. + :rtype: str + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.controller_configuration = "11" + """ + return self.axis[0].controller_configuration + + @controller_configuration.setter + def controller_configuration(self, value): + self.axis[0].controller_configuration = value + + @property + def dhcp_mode(self): + """Get / set if device is in DHCP mode. + + If not in DHCP mode, a static IP address, gateway, and netmask + must be set. + + :return: Status if DHCP mode is enabled + :rtype: `bool` + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.dhcp_mode = True + """ + return bool(self.query("IPMODE?")) + + @dhcp_mode.setter + def dhcp_mode(self, newval): + nn = 1 if newval else 0 + self.sendcmd(f"IPMODE{nn}") + + @property + def error_code(self): + """Get error code only. + + Error code0 means no error detected. + + :return: Error code. + :rtype: int + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.error_code + 0 + """ + return self.axis[0].error_code + + @property + def error_code_and_message(self): + """Get error code and message. + + :return: Error code, error message + :rtype: int, str + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.error_code + (0, 'NO ERROR DETECTED') + """ + return self.axis[0].error_code_and_message + + @property + def firmware_version(self): + """Get the controller firmware version. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.firmware_version + '8742 Version 2.2 08/01/13' + """ + return self.axis[0].firmware_version + + @property + def gateway(self): + """Get / set the gateway of the instrument. + + :return: Gateway address + :rtype: str + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.gateway = "192.168.1.1" + """ + return self.query("GATEWAY?") + + @gateway.setter + def gateway(self, value): + self.sendcmd(f"GATEWAY {value}") + + @property + def hostname(self): + """Get / set the hostname of the instrument. + + :return: Hostname + :rtype: `str` + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.hostname = "asdf" + """ + return self.query("HOSTNAME?") + + @hostname.setter + def hostname(self, value): + self.sendcmd(f"HOSTNAME {value}") + + @property + def ip_address(self): + """Get / set the IP address of the instrument. + + :return: IP address + :rtype: `str` + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.ip_address = "192.168.1.2" + """ + return self.query("IPADDR?") + + @ip_address.setter + def ip_address(self, value): + self.sendcmd(f"IPADDR {value}") + + @property + def mac_address(self): + """Get the MAC address of the instrument. + + :return: MAC address + :rtype: `str` + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.mac_address + '5827809, 8087' + """ + return self.query("MACADDR?") + + @property + def multiple_controllers(self): + """Get / set if multiple controllers are used. + + By default, this is initialized as `False`. Set to `True` if you + have a main controller / secondary controller via RS-485 network + set up. + + Instrument commands will always be sent to main controller. + Axis specific commands will be set to the axis chosen, see + `axis` description. + + :return: Status if multiple controllers are activated + :rtype: bool + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.multiple_controllers = True + """ + return self._multiple_controllers + + @multiple_controllers.setter + def multiple_controllers(self, newval): + self._multiple_controllers = True if newval else False + + @property + def name(self): + """Get the name of the controller. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.name + 'New_Focus 8742 v2.2 08/01/13 13991' + """ + return self.axis[0].name + + @property + def netmask(self): + """Get / set the Netmask of the instrument. + + :return: Netmask + :rtype: `str` + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.netmask = "255.255.255.0" + """ + return self.query("NETMASK?") + + @netmask.setter + def netmask(self, value): + self.sendcmd(f"NETMASK {value}") + + @property + def scan_controllers(self): + """RS-485 controller address map query of all controllers. + + 32 bit string that represents the following: + Bit: Value: (True: 1, False: 0) + 0 Address conflict? + 1: Controller with address 1 exists? + ... + 31: Controller with address 31 exists + + Bits 1—31 are one-to-one mapped to controller addresses 1—31. The + bit value is set to 1 only when there are no conflicts with that + address. For example, if the master controller determines that there + are unique controllers at addresses 1,2, and 7 and more than one + controller at address 23, this query will return 135. The binary + representation of 135 is 10000111. Bit #0 = 1 implies that the scan + found at lease one address conflict during last scan. Bit #1,2, 7 = 1 + implies that the scan found controllers with addresses 1,2, and 7 + that do not conflict with any other controller. + + :return: Binary representation of controller configuration bitmask. + :rtype: str + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.scan_controllers + "10000111" + """ + return self.query("SC?") + + @property + def scan_done(self): + """Queries if a controller scan is done or not. + + :return: Controller scan done? + :rtype: bool + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.scan_done + True + """ + return bool(int(self.query("SD?"))) + + # METHODS # + + def abort_motion(self): + """Instantaneously stop any motion in progress. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.abort_motion() + """ + self.axis[0].abort_motion() + + def motor_check(self): + """Check what motors are connected and set parameters. + + Use the save command `save_settings` if you want to save the + configuration to the non-volatile memory. + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.motor_check() + """ + self.axis[0].motor_check() + + def scan(self, value=2): + """Initialize and set controller addresses automatically. + + Scans the RS-485 network for connected controllers and set the + addresses automatically. Three possible scan modes can be + selected: + Mode 0: + Primary controller scans the network but does not resolve + any address conflicts. + Mode 1: + Primary controller scans the network and resolves address + conflicts, if any. This option preserves the non-conflicting + addresses and reassigns the conflicting addresses starting + with the lowest available address. + Mode 2 (default): + Primary controller reassigns the addresses of all + controllers on the network in a sequential order starting + with master controller set to address 1. + + See also: `scan_controllers` property. + + :param value: Scan mode. + :type: int + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.scan(2) + """ + self.sendcmd(f"SC{value}") + + def purge(self): + """Purge the non-volatile memory of the controller. + + Perform a hard reset and reset all the saved variables. The + following variables are reset to factory settings: + 1. Hostname + 2. IP Mode + 3. IP Address + 4. Subnet mask address + 5. Gateway address + 6. Configuration register + 7. Motor type + 8. Desired Velocity + 9. Desired Acceleration + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.purge() + """ + self.axis[0].purge() + + def recall_parameters(self, value=0): + """Recall parameter set. + + This command restores the controller working parameters from values + saved in its non-volatile memory. It is useful when, for example, + the user has been exploring and changing parameters (e.g., velocity) + but then chooses to reload from previously stored, qualified + settings. Note that “*RCL 0” command just restores the working + parameters to factory default settings. It does not change the + settings saved in EEPROM. + + :param value: 0 -> Recall factory default, + 1 -> Recall last saved settings + :type value: int + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.recall_parameters(1) + """ + self.axis[0].recall_parameters(value) + + def reset(self): + """Reset the controller. + + Perform a soft reset. Saved variables are not deleted! For a + hard reset, see the `purge` command. + + ..note:: It might take up to 30 seconds to re-establish + communications via TCP/IP + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.reset() + """ + self.axis[0].reset() + + def save_settings(self): + """Save user settings. + + This command saves the controller settings in its non-volatile memory. + The controller restores or reloads these settings to working registers + automatically after system reset or it reboots. The purge + command is used to clear non-volatile memory and restore to factory + settings. Note that the SM saves parameters for all motors. + + Saves the following variables: + 1. Controller address + 2. Hostname + 3. IP Mode + 4. IP Address + 5. Subnet mask address + 6. Gateway address + 7. Configuration register + 8. Motor type + 9. Desired Velocity + 10. Desired Acceleration + + Example: + >>> import instruments as ik + >>> import instruments.units as u + >>> ip = "192.168.1.2" + >>> port = 23 # this is the default port + >>> inst = ik.newport.PicoMotorController8742.open_tcpip(ip, port) + >>> inst.save_settings() + """ + self.axis[0].save_settings() + + # QUERY # + + def query(self, cmd, size=-1): + """Query's the device and returns ASCII string. + + Must be queried as a raw string with terminator line ending. This is + currently not implemented in instrument and therefore must be called + directly from file. + + Sometimes, the instrument sends an undecodable 6 byte header along + (usually for the first query). We'll catch it with a try statement. + The 6 byte header was also remarked in this matlab script: + https://github.com/cnanders/matlab-newfocus-model-8742 + """ + self.sendcmd(cmd) + retval = self.read_raw(size=size) + try: + retval = retval.decode("utf-8") + except UnicodeDecodeError: + retval = retval[6:].decode("utf-8") + + return retval diff --git a/instruments/tests/test_newport/test_newport_pmc8742.py b/instruments/tests/test_newport/test_newport_pmc8742.py new file mode 100644 index 000000000..f808190ef --- /dev/null +++ b/instruments/tests/test_newport/test_newport_pmc8742.py @@ -0,0 +1,858 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Tests for the Newport Picomotor Controller 8742. +""" + +# IMPORTS ##################################################################### + +from hypothesis import given, strategies as st +import pytest + +import instruments as ik +from instruments.units import ureg as u +from instruments.tests import expected_protocol + +# pylint: disable=protected-access + + +# INSTRUMENT # + + +def test_init(): + """Initialize a new Picomotor PMC8742 instrument.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + assert inst.terminator == "\r\n" + assert not inst.multiple_controllers + + +def test_controller_address(): + """Set and get controller address.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "SA2", + "SA?" + ], + [ + "2" + ], + sep="\r\n" + ) as inst: + inst.controller_address = 2 + assert inst.controller_address == 2 + + +def test_controller_configuration(): + """Set and get controller configuration.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "ZZ11", + "ZZ11", + "ZZ11", + "ZZ?" + ], + [ + "11" + ], + sep="\r\n" + ) as inst: + inst.controller_configuration = 3 + inst.controller_configuration = 0b11 + inst.controller_configuration = "11" + assert inst.controller_configuration == "11" + + +def test_dhcp_mode(): + """Set and get DHCP mode.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "IPMODE0", + "IPMODE1", + "IPMODE?" + ], + [ + "1" + ], + sep="\r\n" + ) as inst: + inst.dhcp_mode = False + inst.dhcp_mode = True + assert inst.dhcp_mode + + +def test_error_code(): + """Get error code.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "TE?" + ], + [ + "0" + ], + sep="\r\n" + ) as inst: + assert inst.error_code == 0 + + +def test_error_code_and_message(): + """Get error code and message as tuple.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "TB?" + ], + [ + "0, NO ERROR DETECTED" + ], + sep="\r\n" + ) as inst: + err_expected = (0, "NO ERROR DETECTED") + err_received = inst.error_code_and_message + assert err_received == err_expected + assert isinstance(err_received, tuple) + + +def test_firmware_version(): + """Get firmware version.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "VE?" + ], + [ + "0123456789" + ], + sep="\r\n" + ) as inst: + assert inst.firmware_version == "0123456789" + + +def test_gateway(): + """Set / get gateway.""" + ip_addr = "192.168.1.1" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"GATEWAY {ip_addr}", + "GATEWAY?" + ], + [ + f"{ip_addr}" + ], + sep="\r\n" + ) as inst: + inst.gateway = ip_addr + assert inst.gateway == ip_addr + + +def test_hostname(): + """Set / get hostname.""" + host = "192.168.1.1" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"HOSTNAME {host}", + "HOSTNAME?" + ], + [ + f"{host}" + ], + sep="\r\n" + ) as inst: + inst.hostname = host + assert inst.hostname == host + + +def test_ip_address(): + """Set / get ip address.""" + ip_addr = "192.168.1.1" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"IPADDR {ip_addr}", + "IPADDR?" + ], + [ + f"{ip_addr}" + ], + sep="\r\n" + ) as inst: + inst.ip_address = ip_addr + assert inst.ip_address == ip_addr + + +def test_mac_address(): + """Set / get mac address.""" + mac_addr = "5827809, 8087" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "MACADDR?" + ], + [ + f"{mac_addr}" + ], + sep="\r\n" + ) as inst: + assert inst.mac_address == mac_addr + + +def test_name(): + """Get name of the current instrument.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "*IDN?" + ], + [ + "NAME" + ], + sep="\r\n" + ) as inst: + assert inst.name == "NAME" + + +def test_netmask(): + """Set / get netmask.""" + ip_addr = "192.168.1.1" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"NETMASK {ip_addr}", + "NETMASK?" + ], + [ + f"{ip_addr}" + ], + sep="\r\n" + ) as inst: + inst.netmask = ip_addr + assert inst.netmask == ip_addr + + +def test_scan_controller(): + """Scan connected controllers.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "SC?" + ], + [ + "11" + ], + sep="\r\n" + ) as inst: + assert inst.scan_controllers == "11" + + +def test_scan_done(): + """Query if a controller scan is completed.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "SD?", + "SD?" + ], + [ + "1", + "0" + ], + sep="\r\n" + ) as inst: + assert inst.scan_done + assert not inst.scan_done + + +def test_abort_motion(): + """Abort all motion.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "AB" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.abort_motion() + + +def test_motor_check(): + """Check the connected motors.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "MC" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.motor_check() + + +@pytest.mark.parametrize("mode", [0, 1, 2]) +def test_scan(mode): + """Scan address configuration of motors for default and other modes.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "SC2", + f"SC{mode}" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.scan() + inst.scan(mode) + + +def test_purge(): + """Purge the memory.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "XX" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.purge() + + +@pytest.mark.parametrize("mode", [0, 1]) +def test_recall_parameters(mode): + """Recall parameters, by default the factory set values.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "*RCL0", + f"*RCL{mode}" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.recall_parameters() + inst.recall_parameters(mode) + + +def test_reset(): + """Soft reset of the controller.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "*RST" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.reset() + + +def test_save_settings(): + """Save settings of the controller.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "SM" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.save_settings() + + +def test_query_bad_header(): + """Ensure stripping of bad header if present, see comment in query.""" + retval = b"\xff\xfd\x03\xff\xfb\x01192.168.2.161" + val_expected = "192.168.2.161" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "IPADDR?" + ], + [ + retval + ], + sep="\r\n" + ) as inst: + assert inst.ip_address == val_expected + + +# AXIS SPECIFIC COMMANDS - CONTROLLER COMMANDS PER AXIS TESTED ABOVE # + + +@given(ax=st.integers(min_value=0, max_value=3)) +def test_axis_returns(ax): + """Return axis with given axis number testing all valid axes.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[ax] + assert isinstance(axis, ik.newport.PicoMotorController8742.Axis) + assert axis._parent == inst + assert axis._idx == ax + 1 + assert axis._address == "" + + +def test_axis_returns_type_error(): + """Raise TypeError if parent class is not PicoMotorController8742.""" + with pytest.raises(TypeError): + _ = ik.newport.PicoMotorController8742.Axis(0, 0) + + +@given(ax=st.integers(min_value=4)) +def test_axis_return_index_error(ax): + """Raise IndexError if axis out of bounds and in one controller mode.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + with pytest.raises(IndexError): + _ = inst.axis[ax] + + +@given(val=st.integers(min_value=1, max_value=200000)) +def test_axis_acceleration(val): + """Set / get axis acceleration unitful and without units.""" + val_unit = u.Quantity(val, u.s**-2) + val_unit_other = val_unit.to(u.min**-2) + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1AC{val}", + f"1AC{val}", + "1AC?" + ], + [ + f"{val}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.acceleration = val + axis.acceleration = val_unit_other + assert axis.acceleration == val_unit + + +@given(val=st.integers().filter(lambda x: not 1 <= x <= 200000)) +def test_axis_acceleration_value_error(val): + """Raise ValueError if acceleration out of range.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError): + axis.acceleration = val + + +@given(val=st.integers(min_value=-2147483648, max_value=2147483647)) +def test_axis_home_position(val): + """Set / get axis home position.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1DH{val}", + "1DH?" + ], + [ + f"{val}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.home_position = val + assert axis.home_position == val + + +@pytest.mark.parametrize("val", [-2147483649, 2147483648]) +def test_axis_home_position_value_error(val): + """Raise ValueError if home position out of range.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError): + axis.home_position = val + + +@pytest.mark.parametrize("val", ["0", "1"]) +def test_axis_is_stopped(val): + """Query if axis is stopped.""" + exp_result = True if val == "1" else False + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "1MD?" + ], + [ + f"{val}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + assert axis.is_stopped == exp_result + + +@pytest.mark.parametrize( + "val", ik.newport.PicoMotorController8742.Axis.MotorType +) +def test_axis_motor_type(val): + """Set / get motor type.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1QM{val.value}", + "1QM?" + ], + [ + f"{val.value}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.motor_type = val + assert axis.motor_type == val + + +def test_axis_motor_type_wrong_type(): + """Raise TypeError if not appropriate motor type.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(TypeError): + axis.motor_type = 2 + + +@given(val=st.integers(min_value=-2147483648, max_value=2147483647)) +def test_axis_move_absolute(val): + """Set / get axis move absolute.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1PA{val}", + "1PA?" + ], + [ + f"{val}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.move_absolute = val + assert axis.move_absolute == val + + +@pytest.mark.parametrize("val", [-2147483649, 2147483648]) +def test_axis_move_absolute_value_error(val): + """Raise ValueError if move absolute out of range.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError): + axis.move_absolute = val + + +@given(val=st.integers(min_value=-2147483648, max_value=2147483647)) +def test_axis_move_relative(val): + """Set / get axis move relative.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1PR{val}", + "1PR?" + ], + [ + f"{val}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.move_relative = val + assert axis.move_relative == val + + +@pytest.mark.parametrize("val", [-2147483649, 2147483648]) +def test_axis_move_relative_value_error(val): + """Raise ValueError if move relative out of range.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError): + axis.move_relative = val + + +def test_axis_position(): + """Query position of an axis.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "1TP?" + ], + [ + "42" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + assert axis.position == 42 + + +@given(val=st.integers(min_value=1, max_value=2000)) +def test_axis_velocity(val): + """Set / get axis velocity, unitful and unitless.""" + val_unit = u.Quantity(val, 1 / u.s) + val_unit_other = val_unit.to(1 / u.hour) + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1QM?", + f"1VA{val}", + f"1QM?", + f"1VA{val}", + "1VA?" + ], + [ + "3", + "3", + f"{val}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.velocity = val + axis.velocity = val_unit_other + assert axis.velocity == val_unit + + +@given(val=st.integers().filter(lambda x: not 1 <= x <= 2000)) +@pytest.mark.parametrize("motor", [0, 1, 3]) +def test_axis_velocity_value_error_regular(val, motor): + """Raise ValueError if velocity is out of range for non-tiny motor.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "1QM?" + ], + [ + f"{motor}" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError): + axis.velocity = val + + +@given(val=st.integers().filter(lambda x: not 1 <= x <= 1750)) +def test_axis_velocity_value_error_tiny(val): + """Raise ValueError if velocity is out of range for tiny motor.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + "1QM?" + ], + [ + "2" + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + with pytest.raises(ValueError): + axis.velocity = val + + +@pytest.mark.parametrize("direction", ["+", "-"]) +def test_axis_move_indefinite(direction): + """Move axis indefinitely.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1MV{direction}" + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.move_indefinite(direction) + + +def test_axis_stop(): + """Stop axis.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1ST" + ], + [ + ], + sep="\r\n" + ) as inst: + axis = inst.axis[0] + axis.stop() + + +# SOME ADDITIONAL TESTS FOR MAIN / SECONDARY CONTROLLER SETUP # + + +def test_multi_controllers(): + """Enable and disable multiple controllers.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + inst.multiple_controllers = True + assert inst.multiple_controllers + inst.multiple_controllers = False + assert not inst.multiple_controllers + + +@given(ax=st.integers(min_value=0, max_value=31*4-1)) +def test_axis_return_multi(ax): + """Return axis properly for multi-controller setup.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + inst.multiple_controllers = True + axis = inst.axis[ax] + assert isinstance(axis, ik.newport.PicoMotorController8742.Axis) + assert axis._parent == inst + assert axis._idx == ax % 4 + 1 + assert axis._address == f"{ax // 4 + 1}>" + + +@given(ax=st.integers(min_value=124)) +def test_axis_return_multi_index_error(ax): + """Raise IndexError if axis out of bounds and in multi controller mode.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + ], + [ + ], + sep="\r\n" + ) as inst: + inst.multiple_controllers = True + with pytest.raises(IndexError): + _ = inst.axis[ax] + + +@given(ax=st.integers(min_value=0, max_value=31*4-1)) +def test_axis_sendcmd_multi(ax): + """Send correct command in multiple axis mode.""" + address = ax // 4 + 1 + axis = ax % 4 + 1 + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"{address}>{axis}CMD" + ], + [ + ], + sep="\r\n" + ) as inst: + inst.multiple_controllers = True + axis = inst.axis[ax] + axis.sendcmd("CMD") + + +@given(ax=st.integers(min_value=0, max_value=31*4-1)) +def test_axis_query_multi(ax): + """Query command in multiple axis mode and strip address routing.""" + address = ax // 4 + 1 + axis = ax % 4 + 1 + answer_expected = f"{axis}ANSWER" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"{address}>{axis}CMD" + ], + [ + f"{address}>{answer_expected}" + ], + sep="\r\n" + ) as inst: + inst.multiple_controllers = True + axis = inst.axis[ax] + assert axis.query("CMD") == answer_expected + + +def test_axis_query_multi_io_error(): + """Raise IOError if query response from wrong controller.""" + with expected_protocol( + ik.newport.PicoMotorController8742, + [ + f"1>1CMD" + ], + [ + f"4>1ANSWER" + ], + sep="\r\n" + ) as inst: + inst.multiple_controllers = True + axis = inst.axis[0] + with pytest.raises(IOError): + axis.query("CMD") From 4c3433a56149f81f2a072102d40c39bb18210bcc Mon Sep 17 00:00:00 2001 From: Xavier Audier Date: Wed, 19 Jan 2022 17:59:02 -0500 Subject: [PATCH 096/108] Newport error codes x00 to x09 fixed (#314) The error codes corresponding to errors x00 to x09 are not associated to their corresponding axis due to improper matching of the error dictionary for single digit errors. Using :02d format we force a two digit string which matches the corresponding error correctly. This does not affect other error codes. Co-authored-by: Steven Casagrande --- instruments/newport/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instruments/newport/errors.py b/instruments/newport/errors.py index c031f3feb..6ae80c8fe 100644 --- a/instruments/newport/errors.py +++ b/instruments/newport/errors.py @@ -119,7 +119,7 @@ def __init__(self, errcode=None, timestamp=None): self._timestamp) super(NewportError, self).__init__(error) else: - error_message = self.get_message('x{0}'.format(self._errcode)) + error_message = self.get_message('x{0:02d}'.format(self._errcode)) error = "Newport Error: {0}. Axis: {1}. " \ "Error Message: {2}. " \ "At time : {3}".format(str(self._errcode), From f4021f8331067c79cfd5d354c941e98eb8554a3f Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 20 Jan 2022 17:04:43 -0500 Subject: [PATCH 097/108] Migrate to GitHub actions (#304) * First attempt at github actions * Testing * Cleaning * Update on event list * Testing * Rename "finish" to "coverage" (#315) * Add testing on pull_request * Update testing workflow trigger * Add newline at end of deploy (#316) Co-authored-by: Steven Casagrande Co-authored-by: Reto Trappitsch --- .coveragerc | 3 ++ .github/workflows/deploy.yml | 26 +++++++++++++++ .github/workflows/test.yml | 63 ++++++++++++++++++++++++++++++++++++ .travis.yml | 50 ---------------------------- 4 files changed, 92 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.coveragerc b/.coveragerc index 426ca9de8..650e7e628 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,3 +9,6 @@ exclude_lines = if not self._testing: raise NotImplementedError raise AssertionError + +[run] +relative_files = True diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..253beffb7 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,26 @@ +name: Upload Python Package + +on: + release: + types: [published] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip virtualenv + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..ddc5c5c8b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,63 @@ +name: Testing + +on: + push: + branches: [ $default-branch ] + pull_request: + +jobs: + static-checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: pip install --upgrade pip setuptools virtualenv tox + - name: Test with tox + run: tox -e pylint + test: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - python-version: 3.6 + TOXENV: "py36" + - python-version: 3.7 + TOXENV: "py37" + - python-version: 3.8 + TOXENV: "py38" + - python-version: 3.9 + TOXENV: "py39" + - python-version: 3.9 + TOXENV: "py39-numpy" + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: pip install --upgrade pip setuptools wheel virtualenv tox + - name: Test with tox + env: + TOXENV: ${{ matrix.TOXENV }} + run: tox + - name: Submit to coveralls + uses: AndreMiras/coveralls-python-action@develop + with: + parallel: true + flag-name: Unit Test + + coverage: + needs: test + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c860dbc25..000000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ -dist: xenial -sudo: false -language: python -stages: -- prechecks -- tests-cpython -jobs: - include: - - stage: prechecks - python: 3.6 - env: TOXENV=pylint - - stage: tests-cpython - python: 3.6 - env: TOXENV=py36 - - python: 3.7 - env: TOXENV=py37 - - python: 3.8 - env: TOXENV=py38 - - python: 3.9 - env: TOXENV=py39 - - python: 3.9 - env: TOXENV=py39-numpy -before_install: -- python --version -- uname -a -- lsb_release -a -install: - - pip install -U setuptools - - pip install tox coverage coveralls -before_script: - # We use before_script to report version and path information in a way - # that can be easily hidden by Travis' log folding. Moreover, a nonzero - # exit code from this block kills the entire job, meaning that if we can't - # even sensibly get version information, we correctly abort. - - virtualenv --version - - pip --version - - tox --version - - coverage --version -script: - - tox -after_success: - - coveralls -deploy: - provider: pypi - user: ${PYPI_USERNAME} - password: ${PYPI_PASSWORD} - distributions: "sdist bdist_wheel" - on: - tags: true - condition: "$TRAVIS_PYTHON_VERSION == 3.9" From ef934667d2ec5e4f988f94a56d5e786fd5314676 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Sat, 22 Jan 2022 11:39:24 -0500 Subject: [PATCH 098/108] Establish read and write capabilities for USBCommunicator (#313) * Support for Newport Picomotor Controller 8742 Full functionality for this controller in single-controller and multi-controller mode (main / secondaries via RS-485) is implemented. Only single-controller mode is tested with hardware at the moment. Full test suite however includes tests for multi-controller modes. Routines were documented in docstring and also in Sphinx documentation. Note: Control via USB is currently not functional, since InstrumentKit has some issues with the real USB communications class. Needs separate PR. * Added a `read_raw` routine to `instrument.py` Reading raw data from the instrument is required for the picomotor control class, however, had so far to be done by directly accessing a private argument of `instrument.py` (not pretty). * Fix writing to USB device, change PMC to 1 termination character Writing works, but has issues with the terminator. need to add it at least once manually... The IK way of doing things is currently not the same as the tutorial? Reading always and only returns 0... might need to switch the control? * Revert changes to newport_pmc8742 - termination character change * Rewrote instrument.py and usb communicator to work with Newport PMC8742 Tests are currently failing, but that's okay, these are tests that simply test for the old / not-working usb communicator initalization. SRS830 test is failing because USBCommunicator is used to test some behavior that USBCommunicator is the wrong one. Anyway, probably better to just mock something here in this failing test. * Adjust USBCommunicator such that compatible with other instruments * Fixed failing tests in existing test suite * Full testing for USBCommunicator, some BFs * USB Communicator bug fix and test suite... now for real... * Use wMaxPacketSize for the size of the package that is read by default --- .../comm/usb_communicator.py | 110 +++++--- .../abstract_instruments/instrument.py | 29 +-- instruments/tests/test_base_instrument.py | 54 +--- .../tests/test_comm/test_usb_communicator.py | 236 ++++++++++++++++++ instruments/tests/test_srs/test_srs830.py | 6 +- 5 files changed, 333 insertions(+), 102 deletions(-) create mode 100644 instruments/tests/test_comm/test_usb_communicator.py diff --git a/instruments/abstract_instruments/comm/usb_communicator.py b/instruments/abstract_instruments/comm/usb_communicator.py index 27770ef27..c6e83d4f5 100644 --- a/instruments/abstract_instruments/comm/usb_communicator.py +++ b/instruments/abstract_instruments/comm/usb_communicator.py @@ -10,7 +10,12 @@ import io +import usb.core +import usb.util + from instruments.abstract_instruments.comm import AbstractCommunicator +from instruments.units import ureg as u +from instruments.util_fns import assume_units # CLASSES ##################################################################### @@ -24,16 +29,53 @@ class USBCommunicator(io.IOBase, AbstractCommunicator): communicators such as `FileCommunicator` (usbtmc on Linux), `VisaCommunicator`, or `USBTMCCommunicator`. - .. warning:: The operational status of this communicator is unknown, - and it is suggested that it is not relied on. + .. warning:: The operational status of this communicator is poorly tested. """ - def __init__(self, conn): + def __init__(self, dev): super(USBCommunicator, self).__init__(self) - # TODO: Check to make sure this is a USB connection - self._conn = conn + if not isinstance(dev, usb.core.Device): + raise TypeError("USBCommunicator must wrap a usb.core.Device object.") + + # follow (mostly) pyusb tutorial + + # set the active configuration. With no arguments, the first + # configuration will be the active one + dev.set_configuration() + + # get an endpoint instance + cfg = dev.get_active_configuration() + intf = cfg[(0, 0)] + + # initialize in and out endpoints + ep_out = usb.util.find_descriptor( + intf, + # match the first OUT endpoint + custom_match= \ + lambda e: \ + usb.util.endpoint_direction(e.bEndpointAddress) == \ + usb.util.ENDPOINT_OUT + ) + + ep_in = usb.util.find_descriptor( + intf, + # match the first OUT endpoint + custom_match= \ + lambda e: \ + usb.util.endpoint_direction(e.bEndpointAddress) == \ + usb.util.ENDPOINT_IN + ) + + if (ep_in or ep_out) is None: + raise IOError("USB endpoint not found.") + + # read the maximum package size from the ENDPOINT_IN + self._max_packet_size = ep_in.wMaxPacketSize + + self._dev = dev + self._ep_in = ep_in + self._ep_out = ep_out self._terminator = "\n" - # PROPERTIES # @property @@ -57,19 +99,23 @@ def terminator(self): def terminator(self, newval): if not isinstance(newval, str): raise TypeError("Terminator for USBCommunicator must be specified " - "as a single character string.") - if len(newval) > 1: - raise ValueError("Terminator for USBCommunicator must only be 1 " - "character long.") + "as a character string.") self._terminator = newval @property def timeout(self): - raise NotImplementedError + """ + Gets/sets the communication timeout of the USB communicator. + + :type: `~pint.Quantity` + :units: As specified or assumed to be of units ``seconds`` + """ + return assume_units(self._dev.default_timeout, u.ms).to(u.second) @timeout.setter def timeout(self, newval): - raise NotImplementedError + newval = assume_units(newval, u.second).to(u.ms).magnitude + self._dev.default_timeout = newval # FILE-LIKE METHODS # @@ -77,40 +123,48 @@ def close(self): """ Shutdown and close the USB connection """ - try: - self._conn.shutdown() - finally: - self._conn.close() + self._dev.reset() + usb.util.dispose_resources(self._dev) def read_raw(self, size=-1): - raise NotImplementedError + """Read raw string back from device and return. - def read(self, size=-1, encoding="utf-8"): - raise NotImplementedError + String returned is most likely shorter than the size requested. Will + terminate by itself. + Read size of -1 will be transformed into 1000 bytes. - def write_raw(self, msg): + :param size: Size to read in bytes + :type size: int """ - Write bytes to the raw usb connection object. + if size == -1: + size = self._max_packet_size + term = self._terminator.encode("utf-8") + read_val = bytes(self._ep_in.read(size)) + if term not in read_val: + raise IOError(f"Did not find the terminator in the returned string. " + f"Total size of {size} might not be enough.") + return read_val.rstrip(term) + + def write_raw(self, msg): + """Write bytes to the raw usb connection object. :param bytes msg: Bytes to be sent to the instrument over the usb connection. """ - self._conn.write(msg) + self._ep_out.write(msg) def seek(self, offset): # pylint: disable=unused-argument,no-self-use - return NotImplemented + raise NotImplementedError def tell(self): # pylint: disable=no-self-use - return NotImplemented + raise NotImplementedError def flush_input(self): """ Instruct the communicator to flush the input buffer, discarding the entirety of its contents. - - Not implemented for usb communicator """ - raise NotImplementedError + self._ep_in.read(self._max_packet_size) # METHODS # @@ -124,7 +178,7 @@ def _sendcmd(self, msg): :param str msg: The command message to send to the instrument """ msg += self._terminator - self._conn.sendall(msg) + self.write(msg) def _query(self, msg, size=-1): """ diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 57c258143..6727e7493 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -16,9 +16,7 @@ from serial import SerialException from serial.tools.list_ports import comports import pyvisa -import usb import usb.core -import usb.util from instruments.abstract_instruments.comm import ( SocketCommunicator, USBCommunicator, VisaCommunicator, FileCommunicator, @@ -674,39 +672,16 @@ def open_usb(cls, vid, pid): method. :param str vid: Vendor ID of the USB device to open. - :param int pid: Product ID of the USB device to open. + :param str pid: Product ID of the USB device to open. :rtype: `Instrument` :return: Object representing the connected instrument. """ - # pylint: disable=no-member dev = usb.core.find(idVendor=vid, idProduct=pid) if dev is None: raise IOError("No such device found.") - # Use the default configuration offered by the device. - dev.set_configuration() - - # Copied from the tutorial at: - # http://pyusb.sourceforge.net/docs/1.0/tutorial.html - cfg = dev.get_active_configuration() - interface_number = cfg[(0, 0)].bInterfaceNumber - alternate_setting = usb.control.get_interface(dev, interface_number) - intf = usb.util.find_descriptor( - cfg, bInterfaceNumber=interface_number, - bAlternateSetting=alternate_setting - ) - - ep = usb.util.find_descriptor( - intf, - custom_match=lambda e: - usb.util.endpoint_direction(e.bEndpointAddress) == - usb.util.ENDPOINT_OUT - ) - if ep is None: - raise IOError("USB descriptor not found.") - - return cls(USBCommunicator(ep)) + return cls(USBCommunicator(dev)) @classmethod def open_file(cls, filename): diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 3862b5142..6102d8877 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -346,46 +346,23 @@ def test_instrument_open_vxi11(mock_vxi11_comm): mock_vxi11_comm.assert_called_with("string", 1, key1="value") +@mock.patch("instruments.abstract_instruments.instrument.USBCommunicator") @mock.patch("instruments.abstract_instruments.instrument.usb") -def test_instrument_open_usb(mock_usb): +def test_instrument_open_usb(mock_usb, mock_usb_comm): """Open USB device.""" - # mock some behavior - mock_usb.core.find.return_value.__class__ = usb.core.Device # dev - mock_usb.core.find().get_active_configuration.return_value.__class__ = ( - usb.core.Configuration - ) + mock_usb.core.find.return_value.__class__ = usb.core.Device + mock_usb_comm.return_value.__class__ = USBCommunicator - # shortcuts for asserting calls - dev = mock_usb.core.find() - cfg = dev.get_active_configuration() - interface_number = cfg[(0, 0)].bInterfaceNumber - alternate_setting = mock_usb.control.get_interface( - dev, cfg[(0, 0)].bInterfaceNumber - ) + # fake instrument + vid = "0x1000" + pid = "0x1000" + dev = mock_usb.core.find(idVendor=vid, idProduct=pid) # call instrument - inst = ik.Instrument.open_usb("0x1000", 0x1000) - - # assert calls according to manual - dev.set_configuration.assert_called() # check default configuration - dev.get_active_configuration.assert_called() # get active configuration - mock_usb.control.get_interface.assert_called_with(dev, interface_number) - mock_usb.util.find_descriptor.assert_any_call( - cfg, - bInterfaceNumber=interface_number, - bAlternateSetting=alternate_setting - ) - # check the first argument of the `ep =` call - assert mock_usb.util.find_descriptor.call_args_list[1][0][0] == ( - mock_usb.util.find_descriptor( - cfg, - bInterfaceNumber=interface_number, - bAlternateSetting=alternate_setting - ) - ) + inst = ik.Instrument.open_usb(vid, pid) - # assert instrument of correct class assert isinstance(inst._file, USBCommunicator) + mock_usb_comm.assert_called_with(dev) @mock.patch("instruments.abstract_instruments.instrument.usb") @@ -398,17 +375,6 @@ def test_instrument_open_usb_no_device(mock_usb): assert err_msg == "No such device found." -@mock.patch("instruments.abstract_instruments.instrument.usb") -def test_instrument_open_usb_ep_none(mock_usb): - """Raise IOError if endpoint matching returns None.""" - mock_usb.util.find_descriptor.return_value = None - - with pytest.raises(IOError) as err: - _ = ik.Instrument.open_usb(0x1000, 0x1000) - err_msg = err.value.args[0] - assert err_msg == "USB descriptor not found." - - @mock.patch("instruments.abstract_instruments.instrument.USBTMCCommunicator") def test_instrument_open_usbtmc(mock_usbtmc_comm): mock_usbtmc_comm.return_value.__class__ = USBTMCCommunicator diff --git a/instruments/tests/test_comm/test_usb_communicator.py b/instruments/tests/test_comm/test_usb_communicator.py new file mode 100644 index 000000000..2e2eb3591 --- /dev/null +++ b/instruments/tests/test_comm/test_usb_communicator.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the USB communicator. +""" + +# IMPORTS #################################################################### + +from hypothesis import given, strategies as st +import pytest + +import usb.core +import usb.util + +from instruments.abstract_instruments.comm import USBCommunicator +from instruments.units import ureg as u +from .. import mock + +# TEST CASES ################################################################# + +# pylint: disable=protected-access,unused-argument, redefined-outer-name + +patch_util = "instruments.abstract_instruments.comm.usb_communicator.usb.util" + + +@pytest.fixture() +def dev(): + """Return a usb core device for initialization.""" + dev = mock.MagicMock() + dev.__class__ = usb.core.Device + return dev + + +@pytest.fixture() +@mock.patch(patch_util) +def inst(patch_util, dev): + """Return a USB Communicator instrument.""" + return USBCommunicator(dev) + + +@mock.patch(patch_util) +def test_init(usb_util, dev): + """Initialize usb communicator.""" + # mock some behavior of the device required for initializing + dev.find.return_value.__class__ = usb.core.Device # dev + # shortcuts for asserting calls + cfg = dev.get_active_configuration() + interface_number = cfg[(0, 0)].bInterfaceNumber + _ = dev.control.get_interface( + dev, cfg[(0, 0)].bInterfaceNumber + ) + + inst = USBCommunicator(dev) + + # # assert calls according to manual + dev.set_configuration.assert_called() # check default configuration + dev.get_active_configuration.assert_called() # get active configuration + dev.control.get_interface.assert_called_with(dev, interface_number) + usb_util.find_descriptor.assert_has_calls(cfg) + + assert isinstance(inst, USBCommunicator) + + assert inst._dev == dev + + +def test_init_wrong_type(): + """Raise TypeError if initialized with wrong device.""" + with pytest.raises(TypeError) as err: + _ = USBCommunicator(42) + err_msg = err.value.args[0] + assert err_msg == "USBCommunicator must wrap a usb.core.Device object." + + +def test_init_no_endpoints(dev): + """Initialize usb communicator without endpoints.""" + # mock some behavior of the device required for initializing + dev.find.return_value.__class__ = usb.core.Device # dev + + with pytest.raises(IOError) as err: + _ = USBCommunicator(dev) + err_msg = err.value.args[0] + assert err_msg == "USB endpoint not found." + + +def test_address(inst): + """Address of device can not be read, nor written.""" + with pytest.raises(NotImplementedError): + _ = inst.address + + with pytest.raises(ValueError) as err: + inst.address = 42 + + msg = err.value.args[0] + assert msg == "Unable to change USB target address." + + +def test_terminator(inst): + """Get / set terminator of instrument.""" + assert inst.terminator == "\n" + inst.terminator = "\r\n" + assert inst.terminator == "\r\n" + + +def test_terminator_wrong_type(inst): + """Raise TypeError when setting bad terminator.""" + with pytest.raises(TypeError) as err: + inst.terminator = 42 + msg = err.value.args[0] + assert msg == "Terminator for USBCommunicator must be specified as a " \ + "character string." + + +@given(val=st.integers(min_value=1)) +def test_timeout_get(val, inst): + """Get a timeout from device (ms) and turn into s.""" + # mock timeout value of device + inst._dev.default_timeout = val + + ret_val = inst.timeout + assert ret_val == u.Quantity(val, u.ms).to(u.s) + + +def test_timeout_set_unitless(inst): + """Set a timeout value from device unitless (s).""" + val = 1000 + inst.timeout = val + set_val = inst._dev.default_timeout + exp_val = 1000 * val + assert set_val == exp_val + + +def test_timeout_set_minutes(inst): + """Set a timeout value from device in minutes.""" + val = 10 + val_to_set = u.Quantity(val, u.min) + inst.timeout = val_to_set + set_val = inst._dev.default_timeout + exp_val = 1000 * 60 * val + assert set_val == exp_val + + +@mock.patch(patch_util) +def test_close(usb_util, inst): + """Close the connection, release instrument.""" + inst.close() + inst._dev.reset.assert_called() + usb_util.dispose_resources.assert_called_with(inst._dev) + + +def test_read_raw(inst): + """Read raw information from instrument.""" + msg = b"message\n" + msg_exp = b"message" + + inst._ep_in.read.return_value = msg + + assert inst.read_raw() == msg_exp + + +def test_read_raw_size(inst): + """If size is -1, read 1000 bytes.""" + msg = b"message\n" + inst._ep_in.read.return_value = msg + + # set max package size + max_size = 256 + inst._max_packet_size = max_size + + _ = inst.read_raw(size=-1) + inst._ep_in.read.assert_called_with(max_size) + + +def test_read_raw_termination_char_not_found(inst): + """Raise IOError if termination character not found.""" + msg = b"message" + inst._ep_in.read.return_value = msg + default_read_size = 1000 + + inst._max_packet_size = default_read_size + + with pytest.raises(IOError) as err: + _ = inst.read_raw() + err_msg = err.value.args[0] + assert err_msg == f"Did not find the terminator in the returned " \ + f"string. Total size of {default_read_size} might " \ + f"not be enough." + + +def test_write_raw(inst): + """Write a message to the instrument.""" + msg = b"message\n" + inst.write_raw(msg) + inst._ep_out.write.assert_called_with(msg) + + +def test_seek(inst): + """Raise NotImplementedError if `seek` is called.""" + with pytest.raises(NotImplementedError): + inst.seek(42) + + +def test_tell(inst): + """Raise NotImplementedError if `tell` is called.""" + with pytest.raises(NotImplementedError): + inst.tell() + + +def test_flush_input(inst): + """Flush the input out by trying to read until no more available.""" + inst._ep_in.read.side_effect = [b"message\n", usb.core.USBTimeoutError] + inst.flush_input() + inst._ep_in.read.assert_called() + + +def test_sendcmd(inst): + """Send a command.""" + msg = "msg" + msg_to_send = f"msg{inst._terminator}" + + inst.write = mock.MagicMock() + + inst._sendcmd(msg) + inst.write.assert_called_with(msg_to_send) + + +def test_query(inst): + """Query the instrument.""" + msg = "msg" + size = 1000 + + inst.sendcmd = mock.MagicMock() + inst.read = mock.MagicMock() + + inst._query(msg, size=size) + inst.sendcmd.assert_called_with(msg) + inst.read.assert_called_with(size) diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index 6ac052788..c317df577 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -13,10 +13,10 @@ import instruments as ik from instruments.abstract_instruments.comm import ( + FileCommunicator, GPIBCommunicator, LoopbackCommunicator, SerialCommunicator, - USBCommunicator ) from instruments.optional_dep_finder import numpy from instruments.tests import ( @@ -61,8 +61,8 @@ def test_init_mode_serial_comm(mocker): def test_init_mode_invalid(): - """Test initialization with invalild communicator.""" - comm = USBCommunicator(None) + """Test initialization with invalid communicator.""" + comm = FileCommunicator(None) with pytest.warns(UserWarning) as wrn_info: ik.srs.SRS830(comm) wrn_msg = wrn_info[0].message.args[0] From ae5ed18ca5bd864f9cdcdedb84cd352b067e7f31 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 22 Jan 2022 12:43:44 -0500 Subject: [PATCH 099/108] Add pre-commit (#317) * Add pre-commit * Update CI static checks * Remove pylint * Update doc * Add six, because pyvisa-sim isn't well-defined * Update README.rst Co-authored-by: Steven Casagrande --- .github/workflows/test.yml | 6 +- .pre-commit-config.yaml | 14 + README.rst | 36 +- dev-requirements.txt | 2 +- .../ex_keithley6514-checkpoint.ipynb | 2 +- doc/examples/ex_generic_scpi.ipynb | 2 +- doc/examples/ex_generic_scpi.py | 9 +- doc/examples/ex_hp3456.py | 3 +- doc/examples/ex_keithley195.ipynb | 2 +- doc/examples/ex_keithley195.py | 7 +- doc/examples/ex_keithley6514.ipynb | 2 +- doc/examples/ex_oscilloscope_waveform.ipynb | 2 +- doc/examples/ex_oscilloscope_waveform.py | 13 +- doc/examples/ex_qubitekk_gui.py | 118 +- doc/examples/ex_qubitekkcc_simple.py | 12 +- doc/examples/ex_tekdpo70000.ipynb | 2 +- doc/examples/ex_thorlabslcc.py | 11 +- doc/examples/ex_thorlabssc10.py | 22 +- doc/examples/ex_thorlabstc200.py | 13 +- doc/examples/ex_topticatopmode.py | 31 +- doc/examples/example2.py | 26 +- doc/examples/minghe/ex_minghe_mhs5200.py | 9 +- doc/examples/qubitekk/ex_qubitekk_mc1.py | 14 +- doc/examples/srs_DG645.ipynb | 2 +- doc/examples/srs_DG645.py | 6 +- doc/source/acknowledgements.rst | 3 +- doc/source/apiref/agilent.rst | 6 +- doc/source/apiref/fluke.rst | 4 +- doc/source/apiref/generic_scpi.rst | 7 +- doc/source/apiref/gentec-eo.rst | 2 +- doc/source/apiref/glassman.rst | 4 +- doc/source/apiref/holzworth.rst | 3 +- doc/source/apiref/index.rst | 4 +- doc/source/apiref/instrument.rst | 7 +- doc/source/apiref/keithley.rst | 8 +- doc/source/apiref/lakeshore.rst | 7 +- doc/source/apiref/minghe.rst | 6 +- doc/source/apiref/newport.rst | 8 +- doc/source/apiref/ondax.rst | 5 +- doc/source/apiref/oxford.rst | 6 +- doc/source/apiref/phasematrix.rst | 5 +- doc/source/apiref/picowatt.rst | 5 +- doc/source/apiref/qubitekk.rst | 6 +- doc/source/apiref/rigol.rst | 7 +- doc/source/apiref/srs.rst | 8 +- doc/source/apiref/tektronix.rst | 10 +- doc/source/apiref/teledyne.rst | 4 +- doc/source/apiref/thorlabs.rst | 4 +- doc/source/apiref/toptica.rst | 4 +- doc/source/apiref/yokogawa.rst | 5 +- doc/source/conf.py | 159 +- doc/source/devguide/design_philosophy.rst | 1 - doc/source/devguide/index.rst | 15 +- doc/source/devguide/util_fns.rst | 1 - doc/source/index.rst | 3 +- doc/source/intro.rst | 5 +- .../comm/abstract_comm.py | 6 +- .../comm/file_communicator.py | 24 +- .../comm/gpib_communicator.py | 37 +- .../comm/loopback_communicator.py | 10 +- .../comm/serial_communicator.py | 27 +- .../comm/serial_manager.py | 13 +- .../comm/socket_communicator.py | 24 +- .../comm/usb_communicator.py | 25 +- .../comm/usbtmc_communicator.py | 6 +- .../comm/visa_communicator.py | 26 +- .../comm/vxi11_communicator.py | 19 +- .../function_generator.py | 9 +- .../abstract_instruments/instrument.py | 142 +- .../signal_generator/single_channel_sg.py | 2 +- instruments/agilent/agilent33220a.py | 24 +- instruments/agilent/agilent34410a.py | 47 +- instruments/config.py | 23 +- instruments/fluke/fluke3000.py | 84 +- .../generic_scpi/scpi_function_generator.py | 18 +- instruments/generic_scpi/scpi_instrument.py | 42 +- instruments/generic_scpi/scpi_multimeter.py | 87 +- instruments/gentec_eo/blu.py | 28 +- instruments/glassman/glassmanfr.py | 76 +- instruments/holzworth/holzworth_hs9000.py | 25 +- instruments/hp/hp3456a.py | 83 +- instruments/hp/hp6624a.py | 66 +- instruments/hp/hp6632b.py | 81 +- instruments/hp/hp6652a.py | 26 +- instruments/hp/hpe3631a.py | 37 +- instruments/keithley/keithley195.py | 155 +- instruments/keithley/keithley2182.py | 29 +- instruments/keithley/keithley485.py | 104 +- instruments/keithley/keithley580.py | 254 +-- instruments/keithley/keithley6220.py | 14 +- instruments/keithley/keithley6514.py | 100 +- instruments/lakeshore/lakeshore340.py | 2 +- instruments/lakeshore/lakeshore370.py | 4 +- instruments/lakeshore/lakeshore475.py | 124 +- instruments/minghe/mhs5200a.py | 39 +- instruments/named_struct.py | 130 +- instruments/newport/__init__.py | 4 +- instruments/newport/agilis.py | 141 +- instruments/newport/errors.py | 174 +- instruments/newport/newport_pmc8742.py | 72 +- instruments/newport/newportesp301.py | 420 +++-- instruments/ondax/lm.py | 63 +- instruments/optional_dep_finder.py | 1 + instruments/oxford/oxforditc503.py | 4 +- .../phasematrix/phasematrix_fsw0020.py | 20 +- instruments/picowatt/picowattavs47.py | 14 +- instruments/qubitekk/cc1.py | 41 +- instruments/qubitekk/mc1.py | 43 +- instruments/rigol/rigolds1000.py | 22 +- instruments/srs/srs345.py | 25 +- instruments/srs/srs830.py | 141 +- instruments/srs/srsctc100.py | 103 +- instruments/srs/srsdg645.py | 37 +- instruments/tektronix/tekawg2000.py | 69 +- instruments/tektronix/tekdpo4104.py | 27 +- instruments/tektronix/tekdpo70000.py | 370 ++-- instruments/tektronix/tektds224.py | 33 +- instruments/tektronix/tektds5xx.py | 173 +- instruments/teledyne/maui.py | 364 ++-- instruments/tests/__init__.py | 29 +- .../test_abstract_inst/test_electrometer.py | 50 +- .../test_function_generator.py | 92 +- .../test_abstract_inst/test_multimeter.py | 34 +- .../test_optical_spectrum_analyzer.py | 34 +- .../test_abstract_inst/test_oscilloscope.py | 42 +- .../test_abstract_inst/test_power_supply.py | 22 +- .../test_signal_generator/test_channel.py | 2 +- .../test_signal_generator.py | 8 +- .../test_single_channel_sg.py | 8 +- .../tests/test_agilent/test_agilent_33220a.py | 137 +- .../tests/test_agilent/test_agilent_34410a.py | 209 +-- instruments/tests/test_base_instrument.py | 124 +- instruments/tests/test_comm/test_gpibusb.py | 16 +- instruments/tests/test_comm/test_loopback.py | 4 +- instruments/tests/test_comm/test_serial.py | 2 +- instruments/tests/test_comm/test_socket.py | 2 +- .../tests/test_comm/test_usb_communicator.py | 18 +- .../tests/test_comm/test_visa_communicator.py | 34 +- instruments/tests/test_config.py | 44 +- .../tests/test_fluke/test_fluke3000.py | 325 ++-- .../test_scpi_function_generator.py | 66 +- .../test_generic_scpi/test_scpi_instrument.py | 180 +- .../test_generic_scpi/test_scpi_multimeter.py | 244 ++- instruments/tests/test_gentec_eo/test_blu.py | 433 ++--- .../tests/test_glassman/test_glassmanfr.py | 217 +-- .../test_holzworth/test_holzworth_hs9000.py | 191 +- instruments/tests/test_hp/test_hp3456a.py | 393 ++--- instruments/tests/test_hp/test_hp6624a.py | 247 +-- instruments/tests/test_hp/test_hp6632b.py | 238 +-- instruments/tests/test_hp/test_hp6652a.py | 42 +- instruments/tests/test_hp/test_hpe3631a.py | 166 +- .../tests/test_keithley/test_keithley195.py | 309 +--- .../tests/test_keithley/test_keithley2182.py | 237 +-- .../tests/test_keithley/test_keithley485.py | 210 +-- .../tests/test_keithley/test_keithley580.py | 627 +++---- .../tests/test_keithley/test_keithley6220.py | 35 +- .../tests/test_keithley/test_keithley6514.py | 152 +- .../tests/test_lakeshore/test_lakeshore340.py | 18 +- .../tests/test_lakeshore/test_lakeshore370.py | 20 +- .../tests/test_lakeshore/test_lakeshore475.py | 472 ++--- .../tests/test_minghe/test_minghe_mhs5200a.py | 187 +- instruments/tests/test_named_struct.py | 37 +- instruments/tests/test_newport/test_agilis.py | 360 ++-- .../test_newport/test_newport_pmc8742.py | 484 ++---- .../tests/test_newport/test_newportesp301.py | 1405 ++++----------- instruments/tests/test_ondax/test_lm.py | 356 +--- .../tests/test_oxford/test_oxforditc503.py | 19 +- .../test_phasematrix_fsw0020.py | 83 +- .../test_picowatt/test_picowatt_avs47.py | 108 +- .../test_bool_property.py | 39 +- .../test_bounded_unitful_property.py | 71 +- .../test_enum_property.py | 115 +- .../test_int_property.py | 39 +- .../test_property_factories/test_rproperty.py | 12 +- .../test_string_property.py | 36 +- .../test_unitful_property.py | 110 +- .../test_unitless_property.py | 37 +- .../tests/test_qubitekk/test_qubitekk_cc1.py | 432 ++--- .../tests/test_qubitekk/test_qubitekk_mc1.py | 169 +- .../tests/test_rigol/test_rigolds1000.py | 185 +- instruments/tests/test_split_str.py | 9 +- instruments/tests/test_srs/test_srs345.py | 75 +- instruments/tests/test_srs/test_srs830.py | 472 ++--- instruments/tests/test_srs/test_srsctc100.py | 435 ++--- instruments/tests/test_srs/test_srsdg645.py | 197 +-- .../tests/test_tektronix/test_tekawg2000.py | 237 +-- .../tests/test_tektronix/test_tekdpo4104.py | 393 ++--- .../tests/test_tektronix/test_tekdpo70000.py | 1349 +++++--------- .../test_tektronix/test_tektronix_tds224.py | 209 +-- .../tests/test_tektronix/test_tktds5xx.py | 602 +++---- instruments/tests/test_teledyne/test_maui.py | 876 +++------- instruments/tests/test_test_utils.py | 17 +- .../tests/test_thorlabs/test_abstract.py | 99 +- .../tests/test_thorlabs/test_packets.py | 81 +- .../tests/test_thorlabs/test_thorlabs_apt.py | 1545 ++++++++--------- .../test_thorlabs/test_thorlabs_lcc25.py | 331 +--- .../test_thorlabs/test_thorlabs_pm100usb.py | 145 +- .../tests/test_thorlabs/test_thorlabs_sc10.py | 216 +-- .../test_thorlabs/test_thorlabs_tc200.py | 437 +---- .../test_toptica/test_toptica_topmode.py | 706 +++----- instruments/tests/test_util_fns.py | 66 +- .../tests/test_yokogawa/test_yokogawa7651.py | 167 +- .../tests/test_yokogawa/test_yokogawa_6370.py | 248 ++- instruments/thorlabs/__init__.py | 2 +- instruments/thorlabs/_abstract.py | 20 +- instruments/thorlabs/_cmds.py | 1 + instruments/thorlabs/_packets.py | 79 +- instruments/thorlabs/lcc25.py | 23 +- instruments/thorlabs/pm100usb.py | 55 +- instruments/thorlabs/sc10.py | 28 +- instruments/thorlabs/tc200.py | 43 +- instruments/thorlabs/thorlabs_utils.py | 6 +- instruments/thorlabs/thorlabsapt.py | 494 +++--- instruments/toptica/topmode.py | 28 +- instruments/toptica/toptica_utils.py | 6 +- instruments/util_fns.py | 246 ++- instruments/yokogawa/yokogawa6370.py | 31 +- instruments/yokogawa/yokogawa7651.py | 51 +- matlab/open_instrument.m | 8 +- tox.ini | 5 +- 220 files changed, 9515 insertions(+), 15661 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ddc5c5c8b..cecdff0f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,9 +15,9 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install --upgrade pip setuptools virtualenv tox - - name: Test with tox - run: tox -e pylint + run: pip install --upgrade pre-commit + - name: Run static checks via pre-commit + run: pre-commit run --all test: runs-on: ubuntu-latest strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..45a805c27 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: no-commit-to-branch + args: [--branch, master] + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements + - repo: https://github.com/psf/black + rev: 21.12b0 + hooks: + - id: black diff --git a/README.rst b/README.rst index 5ebab1153..4ac0cd1c6 100644 --- a/README.rst +++ b/README.rst @@ -141,8 +141,42 @@ With the required system packages installed, all tests can be run with ``tox``: $ pip install tox $ tox +Pre-commit +---------- + +A variety of static code checks are managed and executed via the tool +`pre-commit_`. This only needs to be setup once +and then it'll manage everything for you. + +.. code-block:: console + + $ pip install pre-commit + $ pre-commit install + +Afterwards, when you go to make a git commit, all the plugins (as specified +by the configuration file `.pre-commit-config.yaml`) will be executed against +the files that have changed. If any plugins make changes to the files, the +commit will abort, allowing you to add those changes to your changeset and +try to commit again. This tool will gate CI, so be sure to let them run +and pass! + +You can also run all the hooks against all the files by directly calling +pre-commit, or though the `tox` environment: + +.. code-block:: console + + $ pre-commit run --all + +or + +.. code-block:: console + + $ tox -e precommit + +See the `pre-commit` documentation for more information. + License ------- All code in this repository is released under the AGPL-v3 license. Please see -the ``license`` folder for more information. \ No newline at end of file +the ``license`` folder for more information. diff --git a/dev-requirements.txt b/dev-requirements.txt index 58edc2bb1..bd8644a31 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,5 +2,5 @@ mock pytest==6.1.1 pytest-mock hypothesis==4.28.2 -pylint==2.4.4 pyvisa-sim +six diff --git a/doc/examples/.ipynb_checkpoints/ex_keithley6514-checkpoint.ipynb b/doc/examples/.ipynb_checkpoints/ex_keithley6514-checkpoint.ipynb index 57d28106c..504959281 100644 --- a/doc/examples/.ipynb_checkpoints/ex_keithley6514-checkpoint.ipynb +++ b/doc/examples/.ipynb_checkpoints/ex_keithley6514-checkpoint.ipynb @@ -168,4 +168,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/ex_generic_scpi.ipynb b/doc/examples/ex_generic_scpi.ipynb index fbb694469..7e5e1f3e5 100644 --- a/doc/examples/ex_generic_scpi.ipynb +++ b/doc/examples/ex_generic_scpi.ipynb @@ -97,4 +97,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/ex_generic_scpi.py b/doc/examples/ex_generic_scpi.py index f973fe46a..e5abe1af4 100644 --- a/doc/examples/ex_generic_scpi.py +++ b/doc/examples/ex_generic_scpi.py @@ -11,7 +11,7 @@ # -# In this example, we will demonstrate how to connect to a generic SCPI +# In this example, we will demonstrate how to connect to a generic SCPI # instrument and query its identification information. # @@ -24,7 +24,7 @@ # -# Next, we open our connection to the instrument. Here we use the generic +# Next, we open our connection to the instrument. Here we use the generic # SCPIInstrument class and open the connection using the Galvant Industries' # GPIBUSB adapter. Our connection is made to the virtual serial port located at # /dev/ttyUSB0 and GPIB address 1 @@ -35,7 +35,7 @@ # -inst = ik.generic_scpi.SCPIInstrument.open_gpibusb('/dev/ttyUSB0', 1) +inst = ik.generic_scpi.SCPIInstrument.open_gpibusb("/dev/ttyUSB0", 1) # @@ -44,5 +44,4 @@ # -print inst.name - +print(inst.name) diff --git a/doc/examples/ex_hp3456.py b/doc/examples/ex_hp3456.py index 558e8dd9a..17e784528 100644 --- a/doc/examples/ex_hp3456.py +++ b/doc/examples/ex_hp3456.py @@ -6,7 +6,7 @@ import instruments as ik import instruments.units as u -dmm = ik.hp.HP3456a.open_gpibusb('/dev/ttyUSB0', 22) +dmm = ik.hp.HP3456a.open_gpibusb("/dev/ttyUSB0", 22) logging.basicConfig(level=logging.DEBUG) dmm._file.debug = True dmm.trigger_mode = dmm.TriggerMode.hold @@ -72,4 +72,3 @@ print(dmm.measure(dmm.Mode.dcv)) dmm.autozero = 0 print(dmm.measure(dmm.Mode.dcv)) - diff --git a/doc/examples/ex_keithley195.ipynb b/doc/examples/ex_keithley195.ipynb index 804fff576..87ee0dcaa 100644 --- a/doc/examples/ex_keithley195.ipynb +++ b/doc/examples/ex_keithley195.ipynb @@ -89,4 +89,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/ex_keithley195.py b/doc/examples/ex_keithley195.py index 82546ce02..de66f9908 100644 --- a/doc/examples/ex_keithley195.py +++ b/doc/examples/ex_keithley195.py @@ -24,14 +24,14 @@ # -# Next, we open our connection to the instrument. Here we use the +# Next, we open our connection to the instrument. Here we use the # Keithley195 class and open the connection using Galvant Industries' # GPIBUSB adapter. Our connection is made to the virtual serial port located at # /dev/ttyUSB0 and GPIB address 16. # -dmm = ik.keithley.Keithley195.open_gpibusb('/dev/ttyUSB0', 1) +dmm = ik.keithley.Keithley195.open_gpibusb("/dev/ttyUSB0", 1) # @@ -39,5 +39,4 @@ # -print dmm.measure() - +print(dmm.measure()) diff --git a/doc/examples/ex_keithley6514.ipynb b/doc/examples/ex_keithley6514.ipynb index 6dc47376f..4d6b6397d 100644 --- a/doc/examples/ex_keithley6514.ipynb +++ b/doc/examples/ex_keithley6514.ipynb @@ -312,4 +312,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/ex_oscilloscope_waveform.ipynb b/doc/examples/ex_oscilloscope_waveform.ipynb index ca45d0622..c28b4d35c 100644 --- a/doc/examples/ex_oscilloscope_waveform.ipynb +++ b/doc/examples/ex_oscilloscope_waveform.ipynb @@ -118,4 +118,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/ex_oscilloscope_waveform.py b/doc/examples/ex_oscilloscope_waveform.py index 3269c7586..871e7a005 100644 --- a/doc/examples/ex_oscilloscope_waveform.py +++ b/doc/examples/ex_oscilloscope_waveform.py @@ -11,12 +11,12 @@ # -# In this example, we will demonstrate how to connect to a Tektronix DPO 4104 +# In this example, we will demonstrate how to connect to a Tektronix DPO 4104 # oscilloscope and transfer the waveform from channel 1 into memory. # -# We start by importing the InstrumentKit and numpy packages. In this example +# We start by importing the InstrumentKit and numpy packages. In this example # we require numpy because the waveforms will be returned as numpy arrays. # @@ -36,18 +36,18 @@ # -tek = ik.tektronix.TekTDS224.open_tcpip('192.168.0.2', 8080) +tek = ik.tektronix.TekTDS224.open_tcpip("192.168.0.2", 8080) # -# Now that we are connected to the instrument, we can transfer the waveform +# Now that we are connected to the instrument, we can transfer the waveform # from the oscilloscope. Note that Python channel[0] specifies the physical # channel 1. This is due to Python's zero-based numbering vs Tektronix's # one-based numbering. # -[x,y] = tek.channel[0].read_waveform() +[x, y] = tek.channel[0].read_waveform() # @@ -56,5 +56,4 @@ # -print np.mean(y) - +print(np.mean(y)) diff --git a/doc/examples/ex_qubitekk_gui.py b/doc/examples/ex_qubitekk_gui.py index 2ee971b70..d768f4196 100644 --- a/doc/examples/ex_qubitekk_gui.py +++ b/doc/examples/ex_qubitekk_gui.py @@ -1,12 +1,14 @@ #!/usr/bin/python # Qubitekk Coincidence Counter example import matplotlib -matplotlib.use('TkAgg') + +matplotlib.use("TkAgg") from matplotlib.figure import Figure from numpy import arange, sin, pi from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg + # implement the default mpl key bindings from matplotlib.backend_bases import key_press_handler from sys import platform as _platform @@ -35,19 +37,19 @@ def getvalues(i): chan2counts.set("Overflow") if cc.channel[2].count < 0: coinc_counts.set("Overflow") - t.append(i*time_diff) + t.append(i * time_diff) i += 1 # plot values - p1, = a.plot(t, coincvals, color="r", linewidth=2.0) - p2, = a.plot(t, chan1vals, color="b", linewidth=2.0) - p3, = a.plot(t, chan2vals, color="g", linewidth=2.0) + (p1,) = a.plot(t, coincvals, color="r", linewidth=2.0) + (p2,) = a.plot(t, chan1vals, color="b", linewidth=2.0) + (p3,) = a.plot(t, chan2vals, color="g", linewidth=2.0) a.legend([p1, p2, p3], ["Coincidences", "Channel 1", "Channel 2"]) - a.set_xlabel('Time (s)') - a.set_ylabel('Counts (Hz)') + a.set_xlabel("Time (s)") + a.set_ylabel("Counts (Hz)") canvas.show() # get the values again in the specified amount of time - root.after(int(time_diff*1000),getvalues,i) + root.after(int(time_diff * 1000), getvalues, i) def gate_enable(): @@ -83,6 +85,7 @@ def reset(*args): trigger_enabled.set(cc.count_enable) gate_enabled.set(cc.gate_enable) + if __name__ == "__main__": cc = ik.qubitekk.CC1.open_serial(vid=1027, pid=24577, baud=19200, timeout=10) print(cc.firmware) @@ -140,7 +143,7 @@ def reset(*args): # set up the plotting area f = Figure(figsize=(10, 8), dpi=100) - a = f.add_subplot(111, axisbg='black') + a = f.add_subplot(111, axisbg="black") t = [] coincvals = [] @@ -152,48 +155,79 @@ def reset(*args): canvas.get_tk_widget().grid(column=3, row=1, rowspan=11, sticky=tk.W) # label initialization - dwell_time_entry = tk.Entry(mainframe, width=7, textvariable=dwell_time, font="Verdana 20") + dwell_time_entry = tk.Entry( + mainframe, width=7, textvariable=dwell_time, font="Verdana 20" + ) dwell_time_entry.grid(column=2, row=2, sticky=(tk.W, tk.E)) window_entry = tk.Entry(mainframe, width=7, textvariable=window, font="Verdana 20") window_entry.grid(column=2, row=3, sticky=(tk.W, tk.E)) - tk.Label(mainframe, text="Dwell Time:", font="Verdana 20").grid(column=1, row=2, sticky=tk.W) - tk.Label(mainframe, text="Window size:", font="Verdana 20").grid(column=1, row=3, sticky=tk.W) - - tk.Checkbutton(mainframe, font="Verdana 20", variable=gate_enabled, command=gate_enable).grid(column=2, row=4) - tk.Label(mainframe, text="Gate Enable: ", font="Verdana 20").grid(column=1, row=4, sticky=tk.W) - - tk.Checkbutton(mainframe, font="Verdana 20", variable=subtract_enabled, command=subtract_enable).grid(column=2, row=5) - tk.Label(mainframe, text="Subtract Accidentals: ", font="Verdana 20").grid(column=1, row=5, sticky=tk.W) - - tk.Checkbutton(mainframe, font="Verdana 20", variable=trigger_enabled, command=trigger_enable).grid(column=2, row=6) - tk.Label(mainframe, text="Continuous Trigger: ", font="Verdana 20").grid(column=1, row=6, sticky=tk.W) - - tk.Label(mainframe, text="Channel 1: ", font="Verdana 20").grid(column=1, row=7, sticky=tk.W) - tk.Label(mainframe, text="Channel 2: ", font="Verdana 20").grid(column=1, row=8, sticky=tk.W) - tk.Label(mainframe, text="Coincidences: ", font="Verdana 20").grid(column=1, row=9, sticky=tk.W) - - tk.Label(mainframe, textvariable=chan1counts, font="Verdana 34", fg="white", bg="black").grid(column=2, row=7, - sticky=tk.W) - tk.Label(mainframe, textvariable=chan2counts, font="Verdana 34", fg="white", bg="black").grid(column=2, row=8, - sticky=tk.W) - tk.Label(mainframe, textvariable=coinc_counts, font="Verdana 34", fg="white", bg="black").grid(column=2, row=9, - sticky=tk.W) - - tk.Button(mainframe, text="Reset", font="Verdana 24", command=reset).grid(column=1, row=10, sticky=tk.W) - - tk.Button(mainframe, text="Clear Counts", font="Verdana 24", command=clear_counts).grid(column=2, row=10, - sticky=tk.W) - - tk.Label(mainframe, text="Firmware Version: " + str(cc.firmware), - font="Verdana 20").grid(column=1, row=11, columnspan=2, sticky=tk.W) + tk.Label(mainframe, text="Dwell Time:", font="Verdana 20").grid( + column=1, row=2, sticky=tk.W + ) + tk.Label(mainframe, text="Window size:", font="Verdana 20").grid( + column=1, row=3, sticky=tk.W + ) + + tk.Checkbutton( + mainframe, font="Verdana 20", variable=gate_enabled, command=gate_enable + ).grid(column=2, row=4) + tk.Label(mainframe, text="Gate Enable: ", font="Verdana 20").grid( + column=1, row=4, sticky=tk.W + ) + + tk.Checkbutton( + mainframe, font="Verdana 20", variable=subtract_enabled, command=subtract_enable + ).grid(column=2, row=5) + tk.Label(mainframe, text="Subtract Accidentals: ", font="Verdana 20").grid( + column=1, row=5, sticky=tk.W + ) + + tk.Checkbutton( + mainframe, font="Verdana 20", variable=trigger_enabled, command=trigger_enable + ).grid(column=2, row=6) + tk.Label(mainframe, text="Continuous Trigger: ", font="Verdana 20").grid( + column=1, row=6, sticky=tk.W + ) + + tk.Label(mainframe, text="Channel 1: ", font="Verdana 20").grid( + column=1, row=7, sticky=tk.W + ) + tk.Label(mainframe, text="Channel 2: ", font="Verdana 20").grid( + column=1, row=8, sticky=tk.W + ) + tk.Label(mainframe, text="Coincidences: ", font="Verdana 20").grid( + column=1, row=9, sticky=tk.W + ) + + tk.Label( + mainframe, textvariable=chan1counts, font="Verdana 34", fg="white", bg="black" + ).grid(column=2, row=7, sticky=tk.W) + tk.Label( + mainframe, textvariable=chan2counts, font="Verdana 34", fg="white", bg="black" + ).grid(column=2, row=8, sticky=tk.W) + tk.Label( + mainframe, textvariable=coinc_counts, font="Verdana 34", fg="white", bg="black" + ).grid(column=2, row=9, sticky=tk.W) + + tk.Button(mainframe, text="Reset", font="Verdana 24", command=reset).grid( + column=1, row=10, sticky=tk.W + ) + + tk.Button( + mainframe, text="Clear Counts", font="Verdana 24", command=clear_counts + ).grid(column=2, row=10, sticky=tk.W) + + tk.Label( + mainframe, text="Firmware Version: " + str(cc.firmware), font="Verdana 20" + ).grid(column=1, row=11, columnspan=2, sticky=tk.W) for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) # when the enter key is pressed, send the current values in the entries to the dwelltime and window to the # coincidence counter - root.bind('',parse) + root.bind("", parse) # in 100 milliseconds, get the counts values off of the coincidence counter - root.after(int(time_diff*1000), getvalues, i) + root.after(int(time_diff * 1000), getvalues, i) # start the GUI root.mainloop() diff --git a/doc/examples/ex_qubitekkcc_simple.py b/doc/examples/ex_qubitekkcc_simple.py index b42d4e140..e347aa21a 100644 --- a/doc/examples/ex_qubitekkcc_simple.py +++ b/doc/examples/ex_qubitekkcc_simple.py @@ -11,17 +11,17 @@ # open connection to coincidence counter. If you are using Windows, this will be a com port. On linux, it will show # up in /dev/ttyusb if _platform == "linux" or _platform == "linux2": - cc = ik.qubitekk.CC1.open_serial('/dev/ttyUSB0', 19200, timeout=1) + cc = ik.qubitekk.CC1.open_serial("/dev/ttyUSB0", 19200, timeout=1) else: - cc = ik.qubitekk.CC1.open_serial('COM8', 19200, timeout=1) + cc = ik.qubitekk.CC1.open_serial("COM8", 19200, timeout=1) print("Initializing Coincidence Counter") - cc.dwell_time = 1.0*u.s - cc.delay = 0.0*u.ns - cc.window = 3.0*u.ns + cc.dwell_time = 1.0 * u.s + cc.delay = 0.0 * u.ns + cc.window = 3.0 * u.ns cc.trigger = cc.TriggerMode.start_stop print(f"ch1 counts: {str(cc.channel[0].count)}") print(f"ch2 counts: {str(cc.channel[1].count)}") print(f"counts counts: {str(cc.channel[2].count)}") - print("Finished Initializing Coincidence Counter") \ No newline at end of file + print("Finished Initializing Coincidence Counter") diff --git a/doc/examples/ex_tekdpo70000.ipynb b/doc/examples/ex_tekdpo70000.ipynb index fc0c5faec..d4d18a78a 100644 --- a/doc/examples/ex_tekdpo70000.ipynb +++ b/doc/examples/ex_tekdpo70000.ipynb @@ -322,4 +322,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/ex_thorlabslcc.py b/doc/examples/ex_thorlabslcc.py index 6aa2376a8..f3814097f 100644 --- a/doc/examples/ex_thorlabslcc.py +++ b/doc/examples/ex_thorlabslcc.py @@ -1,10 +1,11 @@ -#Thorlabs Liquid Crystal Controller example +# Thorlabs Liquid Crystal Controller example import instruments as ik -lcc = ik.thorlabs.LCC25.open_serial('COM10', 115200,timeout=1) -#put model in voltage1 setting: +lcc = ik.thorlabs.LCC25.open_serial("COM10", 115200, timeout=1) + +# put model in voltage1 setting: lcc.mode = llc.Mode.voltage1 -print("The current frequency is: ",lcc.frequency) -print("The current voltage is: ",lcc.voltage1) +print("The current frequency is: ", lcc.frequency) +print("The current voltage is: ", lcc.voltage1) diff --git a/doc/examples/ex_thorlabssc10.py b/doc/examples/ex_thorlabssc10.py index aaa79a678..ab6ae1ece 100644 --- a/doc/examples/ex_thorlabssc10.py +++ b/doc/examples/ex_thorlabssc10.py @@ -1,32 +1,32 @@ -#Thorlabs Shutter Controller example +# Thorlabs Shutter Controller example import instruments as ik -#if the baud mode is set to 1, then the baud rate is 115200 -#otherwise, the baud rate is 9600 -sc = ik.thorlabs.SC10.open_serial('COM9', 9600,timeout=1) + +# if the baud mode is set to 1, then the baud rate is 115200 +# otherwise, the baud rate is 9600 +sc = ik.thorlabs.SC10.open_serial("COM9", 9600, timeout=1) print("It is a: ", sc.name) print("Setting shutter open time to 10 ms") sc.open_time = 10 -print("The shutter open time is: ",sc.open_time) +print("The shutter open time is: ", sc.open_time) print("Setting shutter open time to 50 ms") sc.open_time = 50 -print("The shutter open time is: ",sc.open_time) +print("The shutter open time is: ", sc.open_time) print("Setting shutter close time to 10 ms") sc.open_time = 10 -print("The shutter close time is: ",sc.open_time) +print("The shutter close time is: ", sc.open_time) print("Setting shutter close time to 50 ms") sc.open_time = 50 -print("The shutter close time is: ",sc.open_time) +print("The shutter close time is: ", sc.open_time) print("Setting repeat count to 4") sc.repeat = 4 -print("The repeat count is: ",sc.repeat) +print("The repeat count is: ", sc.repeat) print("Setting repeat count to 8") sc.repeat = 8 -print("The repeat count is: ",sc.repeat) +print("The repeat count is: ", sc.repeat) print("setting mode to auto") sc.mode = sc.Mode.auto - diff --git a/doc/examples/ex_thorlabstc200.py b/doc/examples/ex_thorlabstc200.py index c872526a1..eabaac943 100644 --- a/doc/examples/ex_thorlabstc200.py +++ b/doc/examples/ex_thorlabstc200.py @@ -1,10 +1,11 @@ -#Thorlabs Temperature Controller example +# Thorlabs Temperature Controller example import instruments as ik import instruments.units as u -tc = ik.thorlabs.TC200.open_serial('/dev/tc200', 115200) -tc.temperature_set = 70*u.degF +tc = ik.thorlabs.TC200.open_serial("/dev/tc200", 115200) + +tc.temperature_set = 70 * u.degF print("The current temperature is: ", tc.temperature) tc.mode = tc.Mode.normal @@ -31,10 +32,8 @@ tc.beta = 3900 print("The current beta settings is: ", tc.beta) -tc.max_temperature = 150*u.degC +tc.max_temperature = 150 * u.degC print("The current max temperature setting is: ", tc.max_temperature) -tc.max_power = 1000*u.mW +tc.max_power = 1000 * u.mW print("The current max power setting is: ", tc.max_power) - - diff --git a/doc/examples/ex_topticatopmode.py b/doc/examples/ex_topticatopmode.py index 4db019846..6c8410db4 100644 --- a/doc/examples/ex_topticatopmode.py +++ b/doc/examples/ex_topticatopmode.py @@ -7,10 +7,11 @@ import instruments as ik import instruments.units as u from platform import system -if system() == 'Windows': - tm = ik.toptica.TopMode.open_serial('COM17', 115200) + +if system() == "Windows": + tm = ik.toptica.TopMode.open_serial("COM17", 115200) else: - tm = ik.toptica.TopMode.open_serial('/dev/ttyACM0', 115200) + tm = ik.toptica.TopMode.open_serial("/dev/ttyACM0", 115200) print("The top mode's firmware is: ", tm.firmware) print("The top mode's serial number is: ", tm.serial_number) @@ -28,30 +29,18 @@ print("The laser1's enable state is: ", tm.laser[0].enable) print("The laser1's up time is: ", tm.laser[0].on_time) print("The laser1's charm state is: ", tm.laser[0].charm_status) -print("The laser1's temperature controller state is: ", - tm.laser[0].temperature_control_status) -print("The laser1's current controller state is: ", - tm.laser[0].current_control_status) +print( + "The laser1's temperature controller state is: ", + tm.laser[0].temperature_control_status, +) +print("The laser1's current controller state is: ", tm.laser[0].current_control_status) print("The laser1's tec state is: ", tm.laser[0].tec_status) print("The laser1's intensity is: ", tm.laser[0].intensity) print("The laser1's mode hop state is: ", tm.laser[0].mode_hop) print("The laser1's correction status is: ", tm.laser[0].correction_status) print("The laser1's lock start time is: ", tm.laser[0].lock_start) print("The laser1's first mode hop time is: ", tm.laser[0].first_mode_hop_time) -print("The laser1's latest mode hop time is: ", - tm.laser[0].latest_mode_hop_time) +print("The laser1's latest mode hop time is: ", tm.laser[0].latest_mode_hop_time) print("The current emission state is: ", tm.enable) tm.laser[0].enable = True - - - - - - - - - - - - diff --git a/doc/examples/example2.py b/doc/examples/example2.py index 228bcad88..ba54978fe 100644 --- a/doc/examples/example2.py +++ b/doc/examples/example2.py @@ -2,23 +2,23 @@ # Filename: example2.py # Example 1: -# - Import required packages -# - Create object for our Tek TDS 224 -# - Transfer the waveform from the oscilloscope on channel 1 using binary block reading -# - Calculate the FFT of the transfered waveform -# - Graph resultant data +# - Import required packages +# - Create object for our Tek TDS 224 +# - Transfer the waveform from the oscilloscope on channel 1 using binary block reading +# - Calculate the FFT of the transfered waveform +# - Graph resultant data from instruments import * import numpy as np import matplotlib.pyplot as plt -tek = Tektds224('/dev/ttyUSB0',1,30) +tek = Tektds224("/dev/ttyUSB0", 1, 30) -[x,y] = tek.readWaveform('CH1','BINARY') -freq = np.fft.fft(y) # Calculate FFT -timestep = float( tek.query('WFMP:XIN?') ) # Query the timestep between data points -freqx = np.fft.fftfreq(freq.size,timestep) # Compute the x-axis for the FFT data -plt.plot(freqx,abs(freq)) # Plot the data using matplotlib -plt.ylim(0,500) # Adjust the vertical scale -plt.show() # Show the graph +[x, y] = tek.readWaveform("CH1", "BINARY") +freq = np.fft.fft(y) # Calculate FFT +timestep = float(tek.query("WFMP:XIN?")) # Query the timestep between data points +freqx = np.fft.fftfreq(freq.size, timestep) # Compute the x-axis for the FFT data +plt.plot(freqx, abs(freq)) # Plot the data using matplotlib +plt.ylim(0, 500) # Adjust the vertical scale +plt.show() # Show the graph diff --git a/doc/examples/minghe/ex_minghe_mhs5200.py b/doc/examples/minghe/ex_minghe_mhs5200.py index 9c3880d58..7b4ef4fc2 100644 --- a/doc/examples/minghe/ex_minghe_mhs5200.py +++ b/doc/examples/minghe/ex_minghe_mhs5200.py @@ -4,25 +4,24 @@ mhs = MHS5200.open_serial(vid=6790, pid=29987, baud=57600) print(mhs.serial_number) -mhs.channel[0].frequency = 3000000*u.Hz +mhs.channel[0].frequency = 3000000 * u.Hz print(mhs.channel[0].frequency) mhs.channel[0].function = MHS5200.Function.sawtooth_down print(mhs.channel[0].function) -mhs.channel[0].amplitude = 9.0*u.V +mhs.channel[0].amplitude = 9.0 * u.V print(mhs.channel[0].amplitude) mhs.channel[0].offset = -0.5 print(mhs.channel[0].offset) mhs.channel[0].phase = 90 print(mhs.channel[0].phase) -mhs.channel[1].frequency = 2000000*u.Hz +mhs.channel[1].frequency = 2000000 * u.Hz print(mhs.channel[1].frequency) mhs.channel[1].function = MHS5200.Function.square print(mhs.channel[1].function) -mhs.channel[1].amplitude = 2.0*u.V +mhs.channel[1].amplitude = 2.0 * u.V print(mhs.channel[1].amplitude) mhs.channel[1].offset = 0.0 print(mhs.channel[1].offset) mhs.channel[1].phase = 15 print(mhs.channel[1].phase) - diff --git a/doc/examples/qubitekk/ex_qubitekk_mc1.py b/doc/examples/qubitekk/ex_qubitekk_mc1.py index f30792de0..4e37ff0ad 100644 --- a/doc/examples/qubitekk/ex_qubitekk_mc1.py +++ b/doc/examples/qubitekk/ex_qubitekk_mc1.py @@ -9,8 +9,8 @@ if __name__ == "__main__": mc1 = MC1.open_serial(vid=1027, pid=24577, baud=9600, timeout=1) - mc1.step_size = 25*u.ms - mc1.inertia = 10*u.ms + mc1.step_size = 25 * u.ms + mc1.inertia = 10 * u.ms print("step size:", mc1.step_size) print("inertial force: ", mc1.inertia) @@ -20,18 +20,18 @@ mc1.center() while mc1.is_centering(): - print(str(mc1.metric_position)+" "+str(mc1.direction)) + print(str(mc1.metric_position) + " " + str(mc1.direction)) pass print("Stage Centered") # for the motor in the mechanical delay line, the travel is limited from # the full range of travel. Here's how to set the limits. - mc1.lower_limit = -260*u.ms - mc1.upper_limit = 300*u.ms - mc1.increment = 5*u.ms + mc1.lower_limit = -260 * u.ms + mc1.upper_limit = 300 * u.ms + mc1.increment = 5 * u.ms x_pos = mc1.lower_limit while x_pos <= mc1.upper_limit: - print(str(mc1.metric_position)+" "+str(mc1.direction)) + print(str(mc1.metric_position) + " " + str(mc1.direction)) mc1.move(x_pos) while mc1.move_timeout > 0: sleep(0.5) diff --git a/doc/examples/srs_DG645.ipynb b/doc/examples/srs_DG645.ipynb index 92a348725..0df90152c 100644 --- a/doc/examples/srs_DG645.ipynb +++ b/doc/examples/srs_DG645.ipynb @@ -130,4 +130,4 @@ "metadata": {} } ] -} \ No newline at end of file +} diff --git a/doc/examples/srs_DG645.py b/doc/examples/srs_DG645.py index d3a434c48..efacbba0f 100644 --- a/doc/examples/srs_DG645.py +++ b/doc/examples/srs_DG645.py @@ -30,7 +30,7 @@ # -ddg = SRSDG645.open_gpibusb('/dev/ttyUSB0', 15) +ddg = SRSDG645.open_gpibusb("/dev/ttyUSB0", 15) # @@ -38,8 +38,6 @@ # -ddg.channel[ddg.Channels.A].delay = (ddg.Channels.B, u.Quantity(10, 'us')) +ddg.channel[ddg.Channels.A].delay = (ddg.Channels.B, u.Quantity(10, "us")) # - - diff --git a/doc/source/acknowledgements.rst b/doc/source/acknowledgements.rst index 8b5b1ac70..f2418eadb 100644 --- a/doc/source/acknowledgements.rst +++ b/doc/source/acknowledgements.rst @@ -10,10 +10,9 @@ First off, I'd like to give special thanks to cgranade for his help with pretty much every step along the way. I would be hard pressed to find something that he had nothing to do with. -- ihincks for the fantastic property factories (used throughout all classes) and for the Tektronix DPO70000 series class. +- ihincks for the fantastic property factories (used throughout all classes) and for the Tektronix DPO70000 series class. - dijkstrw for contributing several classes (HP6632b, HP3456a, Keithley 580) as well as plenty of general IK testing. - CatherineH for the Qubitekk CC1, Thorlabs LCC25, SC10, and TC200 classes - silverchris for the TekTDS5xx class - wil-langford for the HP6652a class - whitewhim2718 for the Newport ESP 301 - diff --git a/doc/source/apiref/agilent.rst b/doc/source/apiref/agilent.rst index 12022f81f..1623124a9 100644 --- a/doc/source/apiref/agilent.rst +++ b/doc/source/apiref/agilent.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.agilent - + ======= Agilent ======= @@ -13,7 +13,7 @@ Agilent .. autoclass:: Agilent33220a :members: :undoc-members: - + :class:`Agilent34410a` Digital Multimeter ========================================= diff --git a/doc/source/apiref/fluke.rst b/doc/source/apiref/fluke.rst index 06a728900..2c4ece773 100644 --- a/doc/source/apiref/fluke.rst +++ b/doc/source/apiref/fluke.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.fluke - + ===== Fluke ===== diff --git a/doc/source/apiref/generic_scpi.rst b/doc/source/apiref/generic_scpi.rst index 4755a117b..b183c2663 100644 --- a/doc/source/apiref/generic_scpi.rst +++ b/doc/source/apiref/generic_scpi.rst @@ -1,9 +1,9 @@ .. TODO: put documentation license header here. - + .. _apiref-generic_scpi: .. currentmodule:: instruments.generic_scpi - + ======================== Generic SCPI Instruments ======================== @@ -14,7 +14,7 @@ Generic SCPI Instruments .. autoclass:: SCPIInstrument :members: :undoc-members: - + :class:`SCPIMultimeter` - Generic multimeter using SCPI commands ================================================================ @@ -28,4 +28,3 @@ Generic SCPI Instruments .. autoclass:: SCPIFunctionGenerator :members: :undoc-members: - diff --git a/doc/source/apiref/gentec-eo.rst b/doc/source/apiref/gentec-eo.rst index b9c681068..aefd52c53 100644 --- a/doc/source/apiref/gentec-eo.rst +++ b/doc/source/apiref/gentec-eo.rst @@ -9,4 +9,4 @@ Gentec-EO .. autoclass:: Blu :members: - :undoc-members: \ No newline at end of file + :undoc-members: diff --git a/doc/source/apiref/glassman.rst b/doc/source/apiref/glassman.rst index acf1204f7..4b83e3a62 100644 --- a/doc/source/apiref/glassman.rst +++ b/doc/source/apiref/glassman.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.glassman - + ======== Glassman ======== diff --git a/doc/source/apiref/holzworth.rst b/doc/source/apiref/holzworth.rst index 1d806c646..227e41449 100644 --- a/doc/source/apiref/holzworth.rst +++ b/doc/source/apiref/holzworth.rst @@ -1,6 +1,6 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.holzworth ========= @@ -13,4 +13,3 @@ Holzworth .. autoclass:: HS9000 :members: :undoc-members: - diff --git a/doc/source/apiref/index.rst b/doc/source/apiref/index.rst index 3c76fba15..45b401f7b 100644 --- a/doc/source/apiref/index.rst +++ b/doc/source/apiref/index.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. _apiref: - + InstrumentKit API Reference =========================== diff --git a/doc/source/apiref/instrument.rst b/doc/source/apiref/instrument.rst index 5b8b9bc52..be2f360f4 100644 --- a/doc/source/apiref/instrument.rst +++ b/doc/source/apiref/instrument.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments - + ======================= Instrument Base Classes ======================= @@ -13,7 +13,7 @@ Instrument Base Classes .. autoclass:: Instrument :members: :undoc-members: - + :class:`Multimeter` - Abstract class for multimeter instruments =============================================================== @@ -48,4 +48,3 @@ Instrument Base Classes .. autoclass:: instruments.abstract_instruments.signal_generator.SGChannel :members: :undoc-members: - diff --git a/doc/source/apiref/keithley.rst b/doc/source/apiref/keithley.rst index 8a974c2c3..5e1ed4e73 100644 --- a/doc/source/apiref/keithley.rst +++ b/doc/source/apiref/keithley.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.keithley - + ======== Keithley ======== @@ -20,7 +20,7 @@ Keithley .. autoclass:: Keithley485 :members: :undoc-members: - + :class:`Keithley580` Microohm Meter =================================== @@ -49,4 +49,4 @@ Keithley .. autoclass:: Keithley6514 :members: :undoc-members: -.. _Keithley 6514: http://www.tunl.duke.edu/documents/public/electronics/Keithley/keithley-6514-electrometer-manual.pdf \ No newline at end of file +.. _Keithley 6514: http://www.tunl.duke.edu/documents/public/electronics/Keithley/keithley-6514-electrometer-manual.pdf diff --git a/doc/source/apiref/lakeshore.rst b/doc/source/apiref/lakeshore.rst index f147dc579..a16bf29c1 100644 --- a/doc/source/apiref/lakeshore.rst +++ b/doc/source/apiref/lakeshore.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.lakeshore - + ========= Lakeshore ========= @@ -13,7 +13,7 @@ Lakeshore .. autoclass:: Lakeshore340 :members: :undoc-members: - + :class:`Lakeshore370` AC Resistance Bridge ========================================== @@ -27,4 +27,3 @@ Lakeshore .. autoclass:: Lakeshore475 :members: :undoc-members: - diff --git a/doc/source/apiref/minghe.rst b/doc/source/apiref/minghe.rst index 6a1763039..9ad8f87c9 100644 --- a/doc/source/apiref/minghe.rst +++ b/doc/source/apiref/minghe.rst @@ -1,12 +1,12 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.minghe - + ====== Minghe ====== - + :class:`MHS5200` Function Generator =================================== diff --git a/doc/source/apiref/newport.rst b/doc/source/apiref/newport.rst index 55bc8a364..cecdef3f5 100644 --- a/doc/source/apiref/newport.rst +++ b/doc/source/apiref/newport.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.newport - + ======= Newport ======= @@ -24,11 +24,11 @@ Newport .. autoclass:: NewportESP301 :members: :undoc-members: - + .. autoclass:: NewportESP301Axis :members: :undoc-members: - + .. autoclass:: NewportESP301HomeSearchMode :members: :undoc-members: diff --git a/doc/source/apiref/ondax.rst b/doc/source/apiref/ondax.rst index 3320c2993..adc7c08a3 100644 --- a/doc/source/apiref/ondax.rst +++ b/doc/source/apiref/ondax.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.ondax - + ===== Ondax ===== @@ -13,4 +13,3 @@ Ondax .. autoclass:: LM :members: :undoc-members: - diff --git a/doc/source/apiref/oxford.rst b/doc/source/apiref/oxford.rst index ca1c120e8..3c978d930 100644 --- a/doc/source/apiref/oxford.rst +++ b/doc/source/apiref/oxford.rst @@ -1,12 +1,12 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.oxford - + ====== Oxford ====== - + :class:`OxfordITC503` Temperature Controller ============================================ diff --git a/doc/source/apiref/phasematrix.rst b/doc/source/apiref/phasematrix.rst index 0843420b1..364f783eb 100644 --- a/doc/source/apiref/phasematrix.rst +++ b/doc/source/apiref/phasematrix.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.phasematrix - + =========== PhaseMatrix =========== @@ -13,4 +13,3 @@ PhaseMatrix .. autoclass:: PhaseMatrixFSW0020 :members: :undoc-members: - diff --git a/doc/source/apiref/picowatt.rst b/doc/source/apiref/picowatt.rst index 32ea43674..f6c8c6a94 100644 --- a/doc/source/apiref/picowatt.rst +++ b/doc/source/apiref/picowatt.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.picowatt - + ======== Picowatt ======== @@ -13,4 +13,3 @@ Picowatt .. autoclass:: PicowattAVS47 :members: :undoc-members: - diff --git a/doc/source/apiref/qubitekk.rst b/doc/source/apiref/qubitekk.rst index b86dc6253..8f80571f2 100644 --- a/doc/source/apiref/qubitekk.rst +++ b/doc/source/apiref/qubitekk.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.qubitekk - + ======== Qubitekk ======== @@ -13,7 +13,7 @@ Qubitekk .. autoclass:: CC1 :members: :undoc-members: - + :class:`MC1` Motor Controller ============================= diff --git a/doc/source/apiref/rigol.rst b/doc/source/apiref/rigol.rst index 7e585d521..0feac2703 100644 --- a/doc/source/apiref/rigol.rst +++ b/doc/source/apiref/rigol.rst @@ -1,16 +1,15 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.rigol - + ===== Rigol ===== - + :class:`RigolDS1000Series` Oscilloscope ======================================= .. autoclass:: RigolDS1000Series :members: :undoc-members: - diff --git a/doc/source/apiref/srs.rst b/doc/source/apiref/srs.rst index 95fc6f6a2..9b6a4e697 100644 --- a/doc/source/apiref/srs.rst +++ b/doc/source/apiref/srs.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.srs - + ========================= Stanford Research Systems ========================= @@ -13,7 +13,7 @@ Stanford Research Systems .. autoclass:: SRS345 :members: :undoc-members: - + :class:`SRS830` Lock-In Amplifier ================================= @@ -34,7 +34,7 @@ Stanford Research Systems .. autoclass:: SRSDG645 :members: :undoc-members: - + .. autoclass:: _SRSDG645Channel :members: :undoc-members: diff --git a/doc/source/apiref/tektronix.rst b/doc/source/apiref/tektronix.rst index a14cbda5c..5d2201ec8 100644 --- a/doc/source/apiref/tektronix.rst +++ b/doc/source/apiref/tektronix.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.tektronix - + ========= Tektronix ========= @@ -13,18 +13,18 @@ Tektronix .. autoclass:: TekAWG2000 :members: :undoc-members: - + :class:`TekDPO4104` Oscilloscope ================================ .. autoclass:: TekDPO4104 :members: :undoc-members: - + .. autoclass:: _TekDPO4104DataSource :members: :undoc-members: - + .. autoclass:: _TekDPO4104Channel :members: :undoc-members: diff --git a/doc/source/apiref/teledyne.rst b/doc/source/apiref/teledyne.rst index fe0bfc6a1..55082019b 100644 --- a/doc/source/apiref/teledyne.rst +++ b/doc/source/apiref/teledyne.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.teledyne - + =============== Teledyne-LeCroy =============== diff --git a/doc/source/apiref/thorlabs.rst b/doc/source/apiref/thorlabs.rst index d50520b4b..bcae1c402 100644 --- a/doc/source/apiref/thorlabs.rst +++ b/doc/source/apiref/thorlabs.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.thorlabs - + ======== ThorLabs ======== diff --git a/doc/source/apiref/toptica.rst b/doc/source/apiref/toptica.rst index 91afaec0a..3ba100e4c 100644 --- a/doc/source/apiref/toptica.rst +++ b/doc/source/apiref/toptica.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.toptica - + ======= Toptica ======= diff --git a/doc/source/apiref/yokogawa.rst b/doc/source/apiref/yokogawa.rst index 7dcd32589..0fa66ac93 100644 --- a/doc/source/apiref/yokogawa.rst +++ b/doc/source/apiref/yokogawa.rst @@ -1,8 +1,8 @@ .. TODO: put documentation license header here. - + .. currentmodule:: instruments.yokogawa - + ======== Yokogawa ======== @@ -20,4 +20,3 @@ Yokogawa .. autoclass:: Yokogawa7651 :members: :undoc-members: - diff --git a/doc/source/conf.py b/doc/source/conf.py index c49ad4d71..616e90b70 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -17,32 +17,39 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../')) +sys.path.insert(0, os.path.abspath("../../")) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode'] +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", +] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'InstrumentKit Library' -copyright = u'2013-2020, Steven Casagrande' +project = u"InstrumentKit Library" +copyright = u"2013-2020, Steven Casagrande" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -55,158 +62,161 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. -default_role = 'obj' +default_role = "obj" # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'InstrumentKitLibrarydoc' +htmlhelp_basename = "InstrumentKitLibrarydoc" # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'InstrumentKitLibrary.tex', u'InstrumentKit Library Documentation', - u'Steven Casagrande', 'manual'), + ( + "index", + "InstrumentKitLibrary.tex", + u"InstrumentKit Library Documentation", + u"Steven Casagrande", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -214,12 +224,17 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'instrumentkitlibrary', u'InstrumentKit Library Documentation', - [u'Steven Casagrande'], 1) + ( + "index", + "instrumentkitlibrary", + u"InstrumentKit Library Documentation", + [u"Steven Casagrande"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -228,27 +243,33 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'InstrumentKitLibrary', u'InstrumentKit Library Documentation', - u'Steven Casagrande', 'InstrumentKitLibrary', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "InstrumentKitLibrary", + u"InstrumentKit Library Documentation", + u"Steven Casagrande", + "InstrumentKitLibrary", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - 'http://docs.python.org/': None, - 'numpy': ('http://docs.scipy.org/doc/numpy', None), - 'serial': ('http://pyserial.sourceforge.net/', None), - 'pint': ('https://pint.readthedocs.io/en/stable/', None), + "http://docs.python.org/": None, + "numpy": ("http://docs.scipy.org/doc/numpy", None), + "serial": ("http://pyserial.sourceforge.net/", None), + "pint": ("https://pint.readthedocs.io/en/stable/", None), } -autodoc_member_order = 'groupwise' +autodoc_member_order = "groupwise" diff --git a/doc/source/devguide/design_philosophy.rst b/doc/source/devguide/design_philosophy.rst index 11b2a9157..4cf935ccc 100644 --- a/doc/source/devguide/design_philosophy.rst +++ b/doc/source/devguide/design_philosophy.rst @@ -57,4 +57,3 @@ dimensionality of values to be sent to the device without regards for what the instrument expects; the unit conversions will be handled by InstrumentKit in a way that ensures that the expectations of the instrument are properly met, irrespective of what the user knows. - diff --git a/doc/source/devguide/index.rst b/doc/source/devguide/index.rst index 9a0e2dac6..c1bcee52b 100644 --- a/doc/source/devguide/index.rst +++ b/doc/source/devguide/index.rst @@ -22,8 +22,19 @@ Getting Started To get started with development for InstrumentKit, a few additional supporting packages must be installed. The core development packages can be found in the supporting requirements file named ``dev-requirements.txt``. These will -allow you to run the tests and check that all your code changes follow our -linting rules (through `pylint`). +allow you to run the tests. + +This repo also contains a series of static code checks that are managed +via `pre-commit`. This tool, once setup, will manage running all of these +checks prior to each commit on your local machine.:: + +$ pip install pre-commit +$ pre-commit install + +These checks are also run in CI, and must pass in order to generate +a passing build. It is suggested that you install the git hooks, but +they can be run manually on all files. See the `pre-commit` homepage +for more information. Required Development Dependencies --------------------------------- diff --git a/doc/source/devguide/util_fns.rst b/doc/source/devguide/util_fns.rst index 74a1378be..afc81283a 100644 --- a/doc/source/devguide/util_fns.rst +++ b/doc/source/devguide/util_fns.rst @@ -194,4 +194,3 @@ C-style structures for serializing and deserializing data. .. autoclass:: instruments.named_struct.Field .. autoclass:: instruments.named_struct.Padding - diff --git a/doc/source/index.rst b/doc/source/index.rst index 443e0d8f9..f8e884c38 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,6 +1,6 @@ .. TODO: put documentation license header here. - + Welcome to InstrumentKit Library's documentation! ================================================= @@ -22,4 +22,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/doc/source/intro.rst b/doc/source/intro.rst index b0a8dbd39..b9bce4f85 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -1,6 +1,6 @@ .. TODO: put documentation license header here. - + ============ Introduction ============ @@ -111,8 +111,7 @@ For instance, to add a Tektronix DPO 4104 oscilloscope with world-writable permissions, add the following to rules.d:: ATTRS{idVendor}=="0699", ATTRS{idProduct}=="0401", SYMLINK+="tekdpo4104", MODE="0666" - + .. warning:: This configuration causes the USB device to be world-writable. Do not do this on a multi-user system with untrusted users. - diff --git a/instruments/abstract_instruments/comm/abstract_comm.py b/instruments/abstract_instruments/comm/abstract_comm.py index 8915f4791..ede919828 100644 --- a/instruments/abstract_instruments/comm/abstract_comm.py +++ b/instruments/abstract_instruments/comm/abstract_comm.py @@ -39,9 +39,9 @@ def __init__(self, *args, **kwargs): # pylint: disable=unused-argument # FORMATTING METHODS # def __repr__(self): - return "<{} object at 0x{:X} "\ - "connected to {}>".format( - type(self).__name__, id(self), repr(self.address)) + return "<{} object at 0x{:X} " "connected to {}>".format( + type(self).__name__, id(self), repr(self.address) + ) # CONCRETE PROPERTIES # diff --git a/instruments/abstract_instruments/comm/file_communicator.py b/instruments/abstract_instruments/comm/file_communicator.py index 060b66ba9..27c425e6e 100644 --- a/instruments/abstract_instruments/comm/file_communicator.py +++ b/instruments/abstract_instruments/comm/file_communicator.py @@ -37,7 +37,7 @@ class FileCommunicator(io.IOBase, AbstractCommunicator): def __init__(self, filelike): super(FileCommunicator, self).__init__(self) if isinstance(filelike, str): # pragma: no cover - filelike = open(filelike, 'rb+') + filelike = open(filelike, "rb+") self._filelike = filelike self._terminator = "\n" @@ -53,15 +53,16 @@ def address(self): :type: `str` """ - if hasattr(self._filelike, 'name'): + if hasattr(self._filelike, "name"): return self._filelike.name return None @address.setter def address(self, newval): - raise NotImplementedError("Changing addresses of a file communicator" - " is not yet supported.") + raise NotImplementedError( + "Changing addresses of a file communicator" " is not yet supported." + ) @property def terminator(self): @@ -77,8 +78,10 @@ def terminator(self, newval): if isinstance(newval, bytes): newval = newval.decode("utf-8") if not isinstance(newval, str) or len(newval) > 1: - raise TypeError("Terminator for socket communicator must be " - "specified as a single character string.") + raise TypeError( + "Terminator for socket communicator must be " + "specified as a single character string." + ) self._terminator = newval @property @@ -115,10 +118,10 @@ def read_raw(self, size=-1): return self._filelike.read(size) elif size == -1: result = bytes() - c = b'' + c = b"" while c != self._terminator.encode("utf-8"): c = self._filelike.read(1) - if c == b'': + if c == b"": break if c != self._terminator.encode("utf-8"): result += c @@ -204,7 +207,7 @@ def _query(self, msg, size=-1): break resp += nextchar if nextchar.endswith(self._terminator.encode("utf-8")): - resp = resp[:-len(self._terminator)] + resp = resp[: -len(self._terminator)] break except IOError as ex: if ex.errno == errno.ETIMEDOUT: @@ -221,5 +224,6 @@ def _query(self, msg, size=-1): "providing the device file is unable to communicate with " "the instrument. Consider restarting the instrument.".format( self.address - )) + ) + ) return resp.decode("utf-8") diff --git a/instruments/abstract_instruments/comm/gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py index 2f2886da8..73d96a2d0 100644 --- a/instruments/abstract_instruments/comm/gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -56,6 +56,7 @@ class Model(Enum): """ Enum containing the supported GPIB controller models """ + #: Galvant Industries gi = "gi" #: Prologix, LLC @@ -108,7 +109,7 @@ def timeout(self, newval): newval = assume_units(newval, u.second) if self._model == GPIBCommunicator.Model.gi and self._version <= 4: newval = newval.to(u.second) - self._file.sendcmd('+t:{}'.format(int(newval.magnitude))) + self._file.sendcmd("+t:{}".format(int(newval.magnitude))) else: newval = newval.to(u.millisecond) self._file.sendcmd("++read_tmo_ms {}".format(int(newval.magnitude))) @@ -129,7 +130,7 @@ def terminator(self): if not self._eoi: return self._terminator - return 'eoi' + return "eoi" @terminator.setter def terminator(self, newval): @@ -139,7 +140,7 @@ def terminator(self, newval): newval = newval.lower() if self._model == GPIBCommunicator.Model.gi and self._version <= 4: - if newval == 'eoi': + if newval == "eoi": self.eoi = True elif not isinstance(newval, int): if len(newval) == 1: @@ -147,14 +148,18 @@ def terminator(self, newval): self.eoi = False self.eos = newval else: - raise TypeError('GPIB termination must be integer 0-255 ' - 'represending decimal value of ASCII ' - 'termination character or a string' - 'containing "eoi".') + raise TypeError( + "GPIB termination must be integer 0-255 " + "represending decimal value of ASCII " + "termination character or a string" + 'containing "eoi".' + ) elif (newval < 0) or (newval > 255): - raise ValueError('GPIB termination must be integer 0-255 ' - 'represending decimal value of ASCII ' - 'termination character.') + raise ValueError( + "GPIB termination must be integer 0-255 " + "represending decimal value of ASCII " + "termination character." + ) else: self.eoi = False self.eos = newval @@ -194,9 +199,9 @@ def eoi(self, newval): raise TypeError("EOI status must be specified as a boolean") self._eoi = newval if self._model == GPIBCommunicator.Model.gi and self._version <= 4: - self._file.sendcmd("+eoi:{}".format('1' if newval else '0')) + self._file.sendcmd("+eoi:{}".format("1" if newval else "0")) else: - self._file.sendcmd("++eoi {}".format('1' if newval else '0')) + self._file.sendcmd("++eoi {}".format("1" if newval else "0")) @property def eos(self): @@ -318,7 +323,7 @@ def _sendcmd(self, msg): """ sleep_time = 0.01 - if msg == '': + if msg == "": return if self._model == GPIBCommunicator.Model.gi: self._file.sendcmd("+a:{0}".format(str(self._gpib_address))) @@ -357,8 +362,8 @@ def _query(self, msg, size=-1): :rtype: `str` """ self.sendcmd(msg) - if self._model == GPIBCommunicator.Model.gi and '?' not in msg: - self._file.sendcmd('+read') + if self._model == GPIBCommunicator.Model.gi and "?" not in msg: + self._file.sendcmd("+read") if self._model == GPIBCommunicator.Model.pl: - self._file.sendcmd('++read') + self._file.sendcmd("++read") return self._file.read(size).strip() diff --git a/instruments/abstract_instruments/comm/loopback_communicator.py b/instruments/abstract_instruments/comm/loopback_communicator.py index 3f89ff441..339c439da 100644 --- a/instruments/abstract_instruments/comm/loopback_communicator.py +++ b/instruments/abstract_instruments/comm/loopback_communicator.py @@ -62,8 +62,10 @@ def terminator(self, newval): if isinstance(newval, bytes): newval = newval.decode("utf-8") if not isinstance(newval, str): - raise TypeError("Terminator for loopback communicator must be " - "specified as a byte or unicode string.") + raise TypeError( + "Terminator for loopback communicator must be " + "specified as a byte or unicode string." + ) self._terminator = newval @property @@ -107,10 +109,10 @@ def read_raw(self, size=-1): if self._terminator: while result.endswith(self._terminator.encode("utf-8")) is False: c = self._stdin.read(1) - if c == b'': + if c == b"": break result += c - return result[:-len(self._terminator)] + return result[: -len(self._terminator)] return self._stdin.read(-1) elif size >= 0: diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index ea48ab647..1d1b28aa0 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -34,8 +34,7 @@ def __init__(self, conn): self._terminator = "\n" self._debug = False else: - raise TypeError("SerialCommunicator must wrap a serial.Serial " - "object.") + raise TypeError("SerialCommunicator must wrap a serial.Serial " "object.") # PROPERTIES # @@ -71,8 +70,10 @@ def terminator(self, newval): if isinstance(newval, bytes): newval = newval.decode("utf-8") if not isinstance(newval, str): - raise TypeError("Terminator for serial communicator must be " - "specified as a byte or unicode string.") + raise TypeError( + "Terminator for serial communicator must be " + "specified as a byte or unicode string." + ) self._terminator = newval @property @@ -118,18 +119,16 @@ def read_raw(self, size=-1): # On the other hand, if terminator is nonempty, we can check # that the tail end of the buffer matches it. c = None - term = self._terminator.encode('utf-8') if self._terminator else None - while not ( - result.endswith(term) - if term is not None else - c == b'' - ): + term = self._terminator.encode("utf-8") if self._terminator else None + while not (result.endswith(term) if term is not None else c == b""): c = self._conn.read(1) - if c == b'' and term is not None: - raise IOError("Serial connection timed out before reading " - "a termination character.") + if c == b"" and term is not None: + raise IOError( + "Serial connection timed out before reading " + "a termination character." + ) result += c - return result[:-len(term)] if term is not None else result + return result[: -len(term)] if term is not None else result else: raise ValueError("Must read a positive value of characters.") diff --git a/instruments/abstract_instruments/comm/serial_manager.py b/instruments/abstract_instruments/comm/serial_manager.py index 3c995cbe0..33c23f59a 100644 --- a/instruments/abstract_instruments/comm/serial_manager.py +++ b/instruments/abstract_instruments/comm/serial_manager.py @@ -52,15 +52,14 @@ def new_serial_connection(port, baud=460800, timeout=3, write_timeout=3): :rtype: `SerialCommunicator` """ if not isinstance(port, str): - raise TypeError('Serial port must be specified as a string.') + raise TypeError("Serial port must be specified as a string.") if port not in serialObjDict or serialObjDict[port] is None: - conn = SerialCommunicator(serial.Serial( - port, - baudrate=baud, - timeout=timeout, - writeTimeout=write_timeout - )) + conn = SerialCommunicator( + serial.Serial( + port, baudrate=baud, timeout=timeout, writeTimeout=write_timeout + ) + ) serialObjDict[port] = conn # pylint: disable=protected-access if not serialObjDict[port]._conn.isOpen(): diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index ad31855c7..3356d027c 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -35,9 +35,11 @@ def __init__(self, conn): self._conn = conn self._terminator = "\n" else: - raise TypeError("SocketCommunicator must wrap a " - ":class:`socket.socket` object, instead got " - "{}".format(type(conn))) + raise TypeError( + "SocketCommunicator must wrap a " + ":class:`socket.socket` object, instead got " + "{}".format(type(conn)) + ) # PROPERTIES # @@ -61,8 +63,10 @@ def terminator(self, newval): if isinstance(newval, bytes): newval = newval.decode("utf-8") if not isinstance(newval, str): - raise TypeError("Terminator for socket communicator must be " - "specified as a byte or unicode string.") + raise TypeError( + "Terminator for socket communicator must be " + "specified as a byte or unicode string." + ) self._terminator = newval @property @@ -106,11 +110,13 @@ def read_raw(self, size=-1): result = bytes() while result.endswith(self._terminator.encode("utf-8")) is False: c = self._conn.recv(1) - if c == b'': - raise IOError("Socket connection timed out before reading " - "a termination character.") + if c == b"": + raise IOError( + "Socket connection timed out before reading " + "a termination character." + ) result += c - return result[:-len(self._terminator)] + return result[: -len(self._terminator)] else: raise ValueError("Must read a positive value of characters.") diff --git a/instruments/abstract_instruments/comm/usb_communicator.py b/instruments/abstract_instruments/comm/usb_communicator.py index c6e83d4f5..b0cc1096e 100644 --- a/instruments/abstract_instruments/comm/usb_communicator.py +++ b/instruments/abstract_instruments/comm/usb_communicator.py @@ -51,19 +51,15 @@ def __init__(self, dev): ep_out = usb.util.find_descriptor( intf, # match the first OUT endpoint - custom_match= \ - lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == \ - usb.util.ENDPOINT_OUT + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) + == usb.util.ENDPOINT_OUT, ) ep_in = usb.util.find_descriptor( intf, # match the first OUT endpoint - custom_match= \ - lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == \ - usb.util.ENDPOINT_IN + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) + == usb.util.ENDPOINT_IN, ) if (ep_in or ep_out) is None: @@ -76,6 +72,7 @@ def __init__(self, dev): self._ep_in = ep_in self._ep_out = ep_out self._terminator = "\n" + # PROPERTIES # @property @@ -98,8 +95,10 @@ def terminator(self): @terminator.setter def terminator(self, newval): if not isinstance(newval, str): - raise TypeError("Terminator for USBCommunicator must be specified " - "as a character string.") + raise TypeError( + "Terminator for USBCommunicator must be specified " + "as a character string." + ) self._terminator = newval @property @@ -141,8 +140,10 @@ def read_raw(self, size=-1): term = self._terminator.encode("utf-8") read_val = bytes(self._ep_in.read(size)) if term not in read_val: - raise IOError(f"Did not find the terminator in the returned string. " - f"Total size of {size} might not be enough.") + raise IOError( + f"Did not find the terminator in the returned string. " + f"Total size of {size} might not be enough." + ) return read_val.rstrip(term) def write_raw(self, msg): diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index e50b6beee..23039ea6b 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -57,8 +57,10 @@ def terminator(self, newval): if isinstance(newval, bytes): newval = newval.decode("utf-8") if not isinstance(newval, str) or len(newval) > 1: - raise TypeError("Terminator for loopback communicator must be " - "specified as a single character string.") + raise TypeError( + "Terminator for loopback communicator must be " + "specified as a single character string." + ) self._terminator = newval self._filelike.term_char = ord(newval) diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 154fde4a4..3f89b82be 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -31,8 +31,9 @@ def __init__(self, conn): version = int(pyvisa.__version__.replace(".", "").ljust(3, "0")) # pylint: disable=no-member - if (version < 160 and isinstance(conn, pyvisa.Instrument)) or \ - (version >= 160 and isinstance(conn, pyvisa.Resource)): + if (version < 160 and isinstance(conn, pyvisa.Instrument)) or ( + version >= 160 and isinstance(conn, pyvisa.Resource) + ): self._conn = conn self._terminator = "\n" else: @@ -55,8 +56,9 @@ def address(self): @address.setter def address(self, newval): - raise NotImplementedError("Changing addresses of a VISA Instrument " - "is not supported.") + raise NotImplementedError( + "Changing addresses of a VISA Instrument " "is not supported." + ) @property def terminator(self): @@ -70,11 +72,14 @@ def terminator(self): @terminator.setter def terminator(self, newval): if not isinstance(newval, str): - raise TypeError("Terminator for VisaCommunicator must be specified " - "as a single character string.") + raise TypeError( + "Terminator for VisaCommunicator must be specified " + "as a single character string." + ) if len(newval) > 1: - raise ValueError("Terminator for VisaCommunicator must only be 1 " - "character long.") + raise ValueError( + "Terminator for VisaCommunicator must only be 1 " "character long." + ) self._terminator = newval @property @@ -119,8 +124,9 @@ def read_raw(self, size=-1): # Reset the contents of the buffer. self._buf = bytearray() else: - raise ValueError("Must read a positive value of characters, or " - "-1 for all characters.") + raise ValueError( + "Must read a positive value of characters, or " "-1 for all characters." + ) return msg def write_raw(self, msg): diff --git a/instruments/abstract_instruments/comm/vxi11_communicator.py b/instruments/abstract_instruments/comm/vxi11_communicator.py index c997668d2..6cd093140 100644 --- a/instruments/abstract_instruments/comm/vxi11_communicator.py +++ b/instruments/abstract_instruments/comm/vxi11_communicator.py @@ -42,8 +42,9 @@ class VXI11Communicator(io.IOBase, AbstractCommunicator): def __init__(self, *args, **kwargs): super(VXI11Communicator, self).__init__(self) if vxi11 is None: - raise ImportError("Package python-vxi11 is required for XVI11 " - "connected instruments.") + raise ImportError( + "Package python-vxi11 is required for XVI11 " "connected instruments." + ) AbstractCommunicator.__init__(self) self._inst = vxi11.Instrument(*args, **kwargs) @@ -76,12 +77,16 @@ def terminator(self, newval): if isinstance(newval, bytes): newval = newval.decode("utf-8") if not isinstance(newval, str): - raise TypeError("Terminator for VXI11 communicator must be " - "specified as a byte or unicode string.") + raise TypeError( + "Terminator for VXI11 communicator must be " + "specified as a byte or unicode string." + ) if len(newval) > 1: - logger.warning("VXI11 instruments only support termination" - "characters of length 1. The first character" - "specified will be used.") + logger.warning( + "VXI11 instruments only support termination" + "characters of length 1. The first character" + "specified will be used." + ) self._inst.term_char = newval @property diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index 8d179a9da..54d400daa 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -47,6 +47,7 @@ class Channel(metaclass=abc.ABCMeta): abstract methods. Instruments with 1 channel have their concrete implementations at the parent instrument level. """ + def __init__(self, parent, name): self._parent = parent self._name = name @@ -203,14 +204,16 @@ class VoltageMode(Enum): """ Enum containing valid voltage modes for many function generators """ - peak_to_peak = 'VPP' - rms = 'VRMS' - dBm = 'DBM' + + peak_to_peak = "VPP" + rms = "VRMS" + dBm = "DBM" class Function(Enum): """ Enum containg valid output function modes for many function generators """ + sinusoid = "SIN" square = "SQU" triangle = "TRI" diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 6727e7493..2966c165a 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -19,9 +19,16 @@ import usb.core from instruments.abstract_instruments.comm import ( - SocketCommunicator, USBCommunicator, VisaCommunicator, FileCommunicator, - LoopbackCommunicator, GPIBCommunicator, AbstractCommunicator, - USBTMCCommunicator, VXI11Communicator, serial_manager + SocketCommunicator, + USBCommunicator, + VisaCommunicator, + FileCommunicator, + LoopbackCommunicator, + GPIBCommunicator, + AbstractCommunicator, + USBTMCCommunicator, + VXI11Communicator, + serial_manager, ) from instruments.optional_dep_finder import numpy from instruments.errors import AcknowledgementError, PromptError @@ -29,11 +36,7 @@ # CONSTANTS ################################################################### _DEFAULT_FORMATS = collections.defaultdict(lambda: ">b") -_DEFAULT_FORMATS.update({ - 1: ">b", - 2: ">h", - 4: ">i" -}) +_DEFAULT_FORMATS.update({1: ">b", 2: ">h", 4: ">i"}) # CLASSES ##################################################################### @@ -52,9 +55,11 @@ def __init__(self, filelike): if isinstance(filelike, AbstractCommunicator): self._file = filelike else: - raise TypeError("Instrument must be initialized with a filelike " - "object that is a subclass of " - "AbstractCommunicator.") + raise TypeError( + "Instrument must be initialized with a filelike " + "object that is a subclass of " + "AbstractCommunicator." + ) # Record if we're using the Loopback Communicator and put class in # testing mode so we can disable sleeps in class implementations self._testing = isinstance(self._file, LoopbackCommunicator) @@ -75,7 +80,9 @@ def sendcmd(self, cmd): be sent. """ self._file.sendcmd(str(cmd)) - ack_expected_list = self._ack_expected(cmd) # pylint: disable=assignment-from-none + ack_expected_list = self._ack_expected( + cmd + ) # pylint: disable=assignment-from-none if not isinstance(ack_expected_list, (list, tuple)): ack_expected_list = [ack_expected_list] for ack_expected in ack_expected_list: @@ -107,7 +114,9 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - ack_expected_list = self._ack_expected(cmd) # pylint: disable=assignment-from-none + ack_expected_list = self._ack_expected( + cmd + ) # pylint: disable=assignment-from-none if not isinstance(ack_expected_list, (list, tuple)): ack_expected_list = [ack_expected_list] @@ -248,7 +257,7 @@ def write(self, msg): self._file.write(msg) def binblockread(self, data_width, fmt=None): - """" + """ " Read a binary data block from attached instrument. This requires that the instrument respond in a particular manner as EOL terminators naturally can not be used in binary transfers. @@ -267,9 +276,11 @@ def binblockread(self, data_width, fmt=None): # This needs to be a # symbol for valid binary block symbol = self._file.read_raw(1) if symbol != b"#": # Check to make sure block is valid - raise IOError("Not a valid binary block start. Binary blocks " - "require the first character to be #, instead got " - "{}".format(symbol)) + raise IOError( + "Not a valid binary block start. Binary blocks " + "require the first character to be #, instead got " + "{}".format(symbol) + ) else: # Read in the num of digits for next part digits = int(self._file.read_raw(1)) @@ -293,18 +304,28 @@ def binblockread(self, data_width, fmt=None): if old_len == len(data): tries -= 1 if tries == 0: - raise IOError("Did not read in the required number of bytes" - "during binblock read. Got {}, expected " - "{}".format(len(data), num_of_bytes)) + raise IOError( + "Did not read in the required number of bytes" + "during binblock read. Got {}, expected " + "{}".format(len(data), num_of_bytes) + ) if numpy: return numpy.frombuffer(data, dtype=fmt) return struct.unpack(f"{fmt[0]}{int(len(data)/data_width)}{fmt[-1]}", data) # CLASS METHODS # - URI_SCHEMES = ["serial", "tcpip", "gpib+usb", - "gpib+serial", "visa", "file", "usbtmc", "vxi11", - "test"] + URI_SCHEMES = [ + "serial", + "tcpip", + "gpib+usb", + "gpib+serial", + "visa", + "file", + "usbtmc", + "vxi11", + "test", + ] @classmethod def open_from_uri(cls, uri): @@ -370,26 +391,20 @@ def open_from_uri(cls, uri): else: kwargs["baud"] = 115200 - return cls.open_serial( - dev_name, - **kwargs) + return cls.open_serial(dev_name, **kwargs) elif parsed_uri.scheme == "tcpip": # Ex: tcpip://192.168.0.10:4100 host, port = parsed_uri.netloc.split(":") port = int(port) return cls.open_tcpip(host, port, **kwargs) - elif parsed_uri.scheme == "gpib+usb" \ - or parsed_uri.scheme == "gpib+serial": + elif parsed_uri.scheme == "gpib+usb" or parsed_uri.scheme == "gpib+serial": # Ex: gpib+usb://COM3/15 # scheme="gpib+usb", netloc="COM3", path="/15" # Make a new device path by joining the netloc (if any) # with all but the last segment of the path. uri_head, uri_tail = os.path.split(parsed_uri.path) dev_path = os.path.join(parsed_uri.netloc, uri_head) - return cls.open_gpibusb( - dev_path, - int(uri_tail), - **kwargs) + return cls.open_gpibusb(dev_path, int(uri_tail), **kwargs) elif parsed_uri.scheme == "visa": # Ex: visa://USB::{VID}::{PID}::{SERIAL}::0::INSTR # where {VID}, {PID} and {SERIAL} are to be replaced with @@ -401,10 +416,9 @@ def open_from_uri(cls, uri): # Ex: usbtmc can take URIs exactly like visa://. return cls.open_usbtmc(parsed_uri.netloc, **kwargs) elif parsed_uri.scheme == "file": - return cls.open_file(os.path.join( - parsed_uri.netloc, - parsed_uri.path - ), **kwargs) + return cls.open_file( + os.path.join(parsed_uri.netloc, parsed_uri.path), **kwargs + ) elif parsed_uri.scheme == "vxi11": # Examples: # vxi11://192.168.1.104 @@ -413,8 +427,7 @@ def open_from_uri(cls, uri): elif parsed_uri.scheme == "test": return cls.open_test(**kwargs) else: - raise NotImplementedError("Invalid scheme or not yet " - "implemented.") + raise NotImplementedError("Invalid scheme or not yet " "implemented.") @classmethod def open_tcpip(cls, host, port): @@ -437,8 +450,16 @@ def open_tcpip(cls, host, port): # pylint: disable=too-many-arguments @classmethod - def open_serial(cls, port=None, baud=9600, vid=None, pid=None, - serial_number=None, timeout=3, write_timeout=3): + def open_serial( + cls, + port=None, + baud=9600, + vid=None, + pid=None, + serial_number=None, + timeout=3, + write_timeout=3, + ): """ Opens an instrument, connecting via a physical or emulated serial port. Note that many instruments which connect via USB are exposed to the @@ -472,14 +493,18 @@ def open_serial(cls, port=None, baud=9600, vid=None, pid=None, `~serial.Serial` for description of `port`, baud rates and timeouts. """ if port is None and vid is None: - raise ValueError("One of port, or the USB VID/PID pair, must be " - "specified when ") + raise ValueError( + "One of port, or the USB VID/PID pair, must be " "specified when " + ) if port is not None and vid is not None: - raise ValueError("Cannot specify both a specific port, and a USB" - "VID/PID pair.") + raise ValueError( + "Cannot specify both a specific port, and a USB" "VID/PID pair." + ) if (vid is not None and pid is None) or (pid is not None and vid is None): - raise ValueError("Both VID and PID must be specified when opening" - "a serial connection via a USB VID/PID pair.") + raise ValueError( + "Both VID and PID must be specified when opening" + "a serial connection via a USB VID/PID pair." + ) if port is None: match_count = 0 @@ -498,26 +523,26 @@ def open_serial(cls, port=None, baud=9600, vid=None, pid=None, # If we found more than 1 vid/pid device, but no serial number, # raise an exception due to ambiguity if match_count > 1: - raise SerialException("Found more than one matching serial " - "port from VID/PID pair") + raise SerialException( + "Found more than one matching serial " "port from VID/PID pair" + ) # if the port is still None after that, raise an error. if port is None and vid is not None: - err_msg = "Could not find a port with the attributes vid: {vid}, " \ - "pid: {pid}, serial number: {serial_number}" + err_msg = ( + "Could not find a port with the attributes vid: {vid}, " + "pid: {pid}, serial number: {serial_number}" + ) raise ValueError( err_msg.format( vid=vid, pid=pid, - serial_number="any" if serial_number is None else serial_number + serial_number="any" if serial_number is None else serial_number, ) ) ser = serial_manager.new_serial_connection( - port, - baud=baud, - timeout=timeout, - write_timeout=write_timeout + port, baud=baud, timeout=timeout, write_timeout=write_timeout ) return cls(ser) @@ -549,10 +574,7 @@ def open_gpibusb(cls, port, gpib_address, timeout=3, write_timeout=3, model="gi" .. _Galvant Industries GPIB-USB adapter: galvant.ca/#!/store/gpibusb """ ser = serial_manager.new_serial_connection( - port, - baud=460800, - timeout=timeout, - write_timeout=write_timeout + port, baud=460800, timeout=timeout, write_timeout=write_timeout ) return cls(GPIBCommunicator(ser, gpib_address, model)) @@ -600,7 +622,7 @@ def open_visa(cls, resource_name): if version[0] >= 1 and version[1] >= 6: ins = pyvisa.ResourceManager().open_resource(resource_name) else: - ins = pyvisa.instrument(resource_name) #pylint: disable=no-member + ins = pyvisa.instrument(resource_name) # pylint: disable=no-member return cls(VisaCommunicator(ins)) @classmethod diff --git a/instruments/abstract_instruments/signal_generator/single_channel_sg.py b/instruments/abstract_instruments/signal_generator/single_channel_sg.py index d7fdba629..0c9588d53 100644 --- a/instruments/abstract_instruments/signal_generator/single_channel_sg.py +++ b/instruments/abstract_instruments/signal_generator/single_channel_sg.py @@ -35,4 +35,4 @@ class SingleChannelSG(SignalGenerator, SGChannel): @property def channel(self): - return self, + return (self,) diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index 37a6324d7..d46a14f53 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -12,12 +12,16 @@ from instruments.generic_scpi import SCPIFunctionGenerator from instruments.units import ureg as u from instruments.util_fns import ( - enum_property, int_property, bool_property, assume_units + enum_property, + int_property, + bool_property, + assume_units, ) # CLASSES ##################################################################### + class Agilent33220a(SCPIFunctionGenerator): """ @@ -46,6 +50,7 @@ class Function(Enum): """ Enum containing valid functions for the Agilent/Keysight 33220a """ + sinusoid = "SIN" square = "SQU" ramp = "RAMP" @@ -59,6 +64,7 @@ class LoadResistance(Enum): """ Enum containing valid load resistance for the Agilent/Keysight 33220a """ + minimum = "MIN" maximum = "MAX" high_impedance = "INF" @@ -69,6 +75,7 @@ class OutputPolarity(Enum): Enum containg valid output polarity modes for the Agilent/Keysight 33220a """ + normal = "NORM" inverted = "INV" @@ -82,7 +89,7 @@ class OutputPolarity(Enum): :type: `Agilent33220a.Function` """, - set_fmt="{}:{}" + set_fmt="{}:{}", ) duty_cycle = int_property( @@ -95,7 +102,7 @@ class OutputPolarity(Enum): :type: `int` """, - valid_set=range(101) + valid_set=range(101), ) ramp_symmetry = int_property( @@ -108,7 +115,7 @@ class OutputPolarity(Enum): :type: `int` """, - valid_set=range(101) + valid_set=range(101), ) output = bool_property( @@ -122,7 +129,7 @@ class OutputPolarity(Enum): the output being off. :type: `bool` - """ + """, ) output_sync = bool_property( @@ -133,7 +140,7 @@ class OutputPolarity(Enum): Gets/sets the enabled status of the front panel sync connector. :type: `bool` - """ + """, ) output_polarity = enum_property( @@ -143,7 +150,7 @@ class OutputPolarity(Enum): Gets/sets the polarity of the waveform relative to the offset voltage. :type: `~Agilent33220a.OutputPolarity` - """ + """, ) @property @@ -173,8 +180,7 @@ def load_resistance(self, newval): else: newval = assume_units(newval, u.ohm).to(u.ohm).magnitude if (newval < 0) or (newval > 10000): - raise ValueError( - "Load resistance must be between 0 and 10,000") + raise ValueError("Load resistance must be between 0 and 10,000") self.sendcmd("OUTP:LOAD {}".format(newval)) @property diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index 01f75d257..e06b313ec 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -41,7 +41,7 @@ def data_point_count(self): :rtype: `int` """ - return int(self.query('DATA:POIN?')) + return int(self.query("DATA:POIN?")) # STATE MANAGEMENT METHODS # @@ -54,13 +54,13 @@ def init(self): Note that this command will also clear the previous set of readings from memory. """ - self.sendcmd('INIT') + self.sendcmd("INIT") def abort(self): """ Abort all measurements currently in progress. """ - self.sendcmd('ABOR') + self.sendcmd("ABOR") # MEMORY MANAGEMENT METHODS # @@ -68,7 +68,7 @@ def clear_memory(self): """ Clears the non-volatile memory of the Agilent 34410a. """ - self.sendcmd('DATA:DEL NVMEM') + self.sendcmd("DATA:DEL NVMEM") def r(self, count): """ @@ -87,10 +87,10 @@ def r(self, count): if not isinstance(count, int): raise TypeError('Parameter "count" must be an integer') if count == 0: - msg = 'R?' + msg = "R?" else: - msg = 'R? ' + str(count) - self.sendcmd('FORM:DATA REAL,64') + msg = "R? " + str(count) + self.sendcmd("FORM:DATA REAL,64") self.sendcmd(msg) data = self.binblockread(8, fmt=">d") if numpy: @@ -115,7 +115,7 @@ def fetch(self): or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ units = UNITS[self.mode] - data = list(map(float, self.query('FETC?').split(','))) + data = list(map(float, self.query("FETC?").split(","))) if numpy: return data * units return tuple(val * units for val in data) @@ -140,8 +140,8 @@ def read_data(self, sample_count): if sample_count == -1: sample_count = self.data_point_count units = UNITS[self.mode] - self.sendcmd('FORM:DATA ASC') - data = self.query('DATA:REM? {}'.format(sample_count)).split(',') + self.sendcmd("FORM:DATA ASC") + data = self.query("DATA:REM? {}".format(sample_count)).split(",") data = list(map(float, data)) if numpy: return data * units @@ -155,7 +155,7 @@ def read_data_nvmem(self): or if numpy is installed, `~pint.Quantity` with `numpy.array` data """ units = UNITS[self.mode] - data = list(map(float, self.query('DATA:DATA? NVMEM').split(','))) + data = list(map(float, self.query("DATA:DATA? NVMEM").split(","))) if numpy: return data * units return tuple(val * units for val in data) @@ -170,13 +170,13 @@ def read_last_data(self): :units: As specified by the data returned by the instrument. :rtype: `~pint.Quantity` """ - data = self.query('DATA:LAST?') + data = self.query("DATA:LAST?") unit_map = { "VDC": "V", "VAC": "V", } - if data == '9.91000000E+37': + if data == "9.91000000E+37": return float(data) else: data = data.split(" ") @@ -198,21 +198,22 @@ def read_meter(self): """ mode = self.mode units = UNITS[mode] - return float(self.query('READ?')) * units + return float(self.query("READ?")) * units + # UNITS ####################################################################### UNITS = { Agilent34410a.Mode.capacitance: u.farad, - Agilent34410a.Mode.voltage_dc: u.volt, - Agilent34410a.Mode.voltage_ac: u.volt, - Agilent34410a.Mode.diode: u.volt, - Agilent34410a.Mode.current_ac: u.amp, - Agilent34410a.Mode.current_dc: u.amp, - Agilent34410a.Mode.resistance: u.ohm, + Agilent34410a.Mode.voltage_dc: u.volt, + Agilent34410a.Mode.voltage_ac: u.volt, + Agilent34410a.Mode.diode: u.volt, + Agilent34410a.Mode.current_ac: u.amp, + Agilent34410a.Mode.current_dc: u.amp, + Agilent34410a.Mode.resistance: u.ohm, Agilent34410a.Mode.fourpt_resistance: u.ohm, - Agilent34410a.Mode.frequency: u.hertz, - Agilent34410a.Mode.period: u.second, + Agilent34410a.Mode.frequency: u.hertz, + Agilent34410a.Mode.period: u.second, Agilent34410a.Mode.temperature: u.kelvin, - Agilent34410a.Mode.continuity: 1, + Agilent34410a.Mode.continuity: 1, } diff --git a/instruments/config.py b/instruments/config.py index 493d16b97..048e1eeb1 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -55,6 +55,7 @@ def walk_dict(d, path): # Otherwise, resolve that segment and recurse. return walk_dict(d[path[0]], path[1:]) + def quantity_constructor(loader, node): """ Constructs a `u.Quantity` instance from a PyYAML @@ -64,9 +65,11 @@ def quantity_constructor(loader, node): value = loader.construct_scalar(node) return u.Quantity(*split_unit_str(value)) + # We avoid having to register !Q every time by doing as soon as the # relevant constructor is defined. -yaml.add_constructor(u'!Q', quantity_constructor) +yaml.add_constructor(u"!Q", quantity_constructor) + def load_instruments(conf_file_name, conf_path="/"): """ @@ -138,11 +141,12 @@ def load_instruments(conf_file_name, conf_path="/"): """ if yaml is None: - raise ImportError("Could not import ruamel.yaml, which is required " - "for this function.") + raise ImportError( + "Could not import ruamel.yaml, which is required " "for this function." + ) if isinstance(conf_file_name, str): - with open(conf_file_name, 'r') as f: + with open(conf_file_name, "r") as f: conf_dict = yaml.load(f, Loader=yaml.Loader) else: conf_dict = yaml.load(conf_file_name, Loader=yaml.Loader) @@ -154,16 +158,19 @@ def load_instruments(conf_file_name, conf_path="/"): try: inst_dict[name] = value["class"].open_from_uri(value["uri"]) - if 'attrs' in value: + if "attrs" in value: # We have some attrs we can set on the newly created instrument. - for attr_name, attr_value in value['attrs'].items(): + for attr_name, attr_value in value["attrs"].items(): setattr_expression(inst_dict[name], attr_name, attr_value) except IOError as ex: # FIXME: need to subclass Warning so that repeated warnings # aren't ignored. - warnings.warn("Exception occured loading device with URI " - "{}:\n\t{}.".format(value["uri"], ex), RuntimeWarning) + warnings.warn( + "Exception occured loading device with URI " + "{}:\n\t{}.".format(value["uri"], ex), + RuntimeWarning, + ) inst_dict[name] = None return inst_dict diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index be191cabc..5d7a1da25 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -111,6 +111,7 @@ class Module(Enum): """ Enum containing the supported modules serial numbers. """ + #: Multimeter m3000 = 46333030304643 #: Temperature module @@ -120,6 +121,7 @@ class Mode(Enum): """ Enum containing the supported mode codes. """ + #: AC Voltage voltage_ac = "01" #: DC Voltage @@ -171,7 +173,9 @@ def trigger_mode(self): :rtype: `str` """ - raise AttributeError("The `Fluke3000` only supports single trigger when queried") + raise AttributeError( + "The `Fluke3000` only supports single trigger when queried" + ) @property def relative(self): @@ -182,7 +186,9 @@ def relative(self): :rtype: `bool` """ - raise AttributeError("The `Fluke3000` FC does not support relative measurements") + raise AttributeError( + "The `Fluke3000` FC does not support relative measurements" + ) @property def input_range(self): @@ -193,7 +199,7 @@ def input_range(self): :rtype: `str` """ - raise AttributeError('The `Fluke3000` FC is an autoranging only multimeter') + raise AttributeError("The `Fluke3000` FC is an autoranging only multimeter") # METHODS # @@ -202,14 +208,16 @@ def connect(self): Connect to available modules and returns a dictionary of the modules found and their port ID. """ - self.scan() # Look for connected devices + self.scan() # Look for connected devices if not self.positions: - self.reset() # Reset the PC3000 dongle - timeout = self.timeout # Store default timeout - self.timeout = 30 * u.second # PC 3000 can take a while to bind with wireless devices - self.query_lines("rfdis", 3) # Discover available modules and bind them - self.timeout = timeout # Restore default timeout - self.scan() # Look for connected devices + self.reset() # Reset the PC3000 dongle + timeout = self.timeout # Store default timeout + self.timeout = ( + 30 * u.second + ) # PC 3000 can take a while to bind with wireless devices + self.query_lines("rfdis", 3) # Discover available modules and bind them + self.timeout = timeout # Restore default timeout + self.scan() # Look for connected devices if not self.positions: raise ValueError("No `Fluke3000` modules available") @@ -244,8 +252,8 @@ def reset(self): """ Resets the device and unbinds all modules. """ - self.query_lines("ri", 3) # Resets the device - self.query_lines("rfsm 1", 2) # Turns comms on + self.query_lines("ri", 3) # Resets the device + self.query_lines("rfsm 1", 2) # Turns comms on def read_lines(self, nlines=1): """ @@ -292,7 +300,7 @@ def flush(self): timeout = self.timeout self.timeout = 0.1 * u.second init_time = time.time() - while time.time() - init_time < 1.: + while time.time() - init_time < 1.0: try: self.read() except OSError: @@ -320,10 +328,10 @@ def measure(self, mode): raise ValueError(f"Device necessary to measure {mode} is not available") # Query the module - value = '' + value = "" port_id = self.positions[module] init_time = time.time() - while time.time() - init_time < 3.: + while time.time() - init_time < 3.0: # Read out if mode == self.Mode.temperature: # The temperature module supports single readout @@ -382,15 +390,19 @@ def _parse(self, result, mode): """ # Check that a value is contained if "PH" not in result: - raise ValueError("Cannot parse a string that does not contain a return value") + raise ValueError( + "Cannot parse a string that does not contain a return value" + ) # Isolate the data string from the output - data = result.split('PH=')[-1] + data = result.split("PH=")[-1] # Check that the multimeter is in the right mode (fifth byte) if self._parse_mode(data) != mode.value: - error = (f"Mode {mode.name} was requested but the Fluke 3000FC Multimeter is in " - f"mode {self.Mode(data[8:10]).name} instead. Could not read the requested quantity.") + error = ( + f"Mode {mode.name} was requested but the Fluke 3000FC Multimeter is in " + f"mode {self.Mode(data[8:10]).name} instead. Could not read the requested quantity." + ) raise ValueError(error) # Extract the value from the first two bytes @@ -400,7 +412,7 @@ def _parse(self, result, mode): scale = self._parse_factor(data) # Combine and return - return scale*value + return scale * value @staticmethod def _parse_mode(data): @@ -430,7 +442,7 @@ def _parse_value(data): """ # The second dual hex byte is the most significant byte - return int(data[2:4]+data[:2], 16) + return int(data[2:4] + data[:2], 16) @staticmethod def _parse_factor(data): @@ -457,33 +469,33 @@ def _parse_factor(data): prefix = PREFIXES[code] # The sixth and seventh bit encode the decimal place - scale = 10**(-int(byte[5:7], 2)) + scale = 10 ** (-int(byte[5:7], 2)) # Return the combination - return sign*prefix*scale + return sign * prefix * scale # UNITS ####################################################################### UNITS = { None: 1, - Fluke3000.Mode.voltage_ac: u.volt, - Fluke3000.Mode.voltage_dc: u.volt, - Fluke3000.Mode.current_ac: u.amp, - Fluke3000.Mode.current_dc: u.amp, - Fluke3000.Mode.frequency: u.hertz, + Fluke3000.Mode.voltage_ac: u.volt, + Fluke3000.Mode.voltage_dc: u.volt, + Fluke3000.Mode.current_ac: u.amp, + Fluke3000.Mode.current_dc: u.amp, + Fluke3000.Mode.frequency: u.hertz, Fluke3000.Mode.temperature: u.degC, - Fluke3000.Mode.resistance: u.ohm, - Fluke3000.Mode.capacitance: u.farad + Fluke3000.Mode.resistance: u.ohm, + Fluke3000.Mode.capacitance: u.farad, } # METRIC PREFIXES ############################################################# PREFIXES = { - 0: 1e0, # None - 2: 1e6, # Mega - 3: 1e3, # Kilo - 4: 1e-3, # milli - 5: 1e-6, # micro - 6: 1e-9 # nano + 0: 1e0, # None + 2: 1e6, # Mega + 3: 1e3, # Kilo + 4: 1e-3, # milli + 5: 1e-6, # micro + 6: 1e-9, # nano } diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index 5d200231c..fd6020164 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -34,12 +34,11 @@ class SCPIFunctionGenerator(FunctionGenerator, SCPIInstrument): _UNIT_MNEMONICS = { FunctionGenerator.VoltageMode.peak_to_peak: "VPP", - FunctionGenerator.VoltageMode.rms: "VRMS", - FunctionGenerator.VoltageMode.dBm: "DBM", + FunctionGenerator.VoltageMode.rms: "VRMS", + FunctionGenerator.VoltageMode.dBm: "DBM", } - _MNEMONIC_UNITS = dict((mnem, unit) - for unit, mnem in _UNIT_MNEMONICS.items()) + _MNEMONIC_UNITS = dict((mnem, unit) for unit, mnem in _UNIT_MNEMONICS.items()) # FunctionGenerator CONTRACT # @@ -53,10 +52,7 @@ def _get_amplitude_(self): """ units = self.query("VOLT:UNIT?").strip() - return ( - float(self.query("VOLT?").strip()), - self._MNEMONIC_UNITS[units] - ) + return (float(self.query("VOLT?").strip()), self._MNEMONIC_UNITS[units]) def _set_amplitude_(self, magnitude, units): """ @@ -80,7 +76,7 @@ def _set_amplitude_(self, magnitude, units): :units: As specified, or assumed to be :math:`\\text{Hz}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) function = enum_property( @@ -90,7 +86,7 @@ def _set_amplitude_(self, magnitude, units): Gets/sets the output function of the function generator :type: `SCPIFunctionGenerator.Function` - """ + """, ) offset = unitful_property( @@ -104,7 +100,7 @@ def _set_amplitude_(self, magnitude, units): :units: As specified (if a `~pint.Quantity`) or assumed to be of units volts. :type: `~pint.Quantity` with units volts. - """ + """, ) @property diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index 1d73bdb2d..26377adcc 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -42,7 +42,7 @@ def name(self): :rtype: `str` """ - return self.query('*IDN?') + return self.query("*IDN?") @property def scpi_version(self): @@ -60,7 +60,7 @@ def op_complete(self): :rtype: `bool` """ - result = self.query('*OPC?') + result = self.query("*OPC?") return bool(int(result)) @property @@ -70,19 +70,19 @@ def power_on_status(self): :type: `bool` """ - result = self.query('*PSC?') + result = self.query("*PSC?") return bool(int(result)) @power_on_status.setter def power_on_status(self, newval): - on = ['on', '1', 1, True] - off = ['off', '0', 0, False] + on = ["on", "1", 1, True] + off = ["off", "0", 0, False] if isinstance(newval, str): newval = newval.lower() if newval in on: - self.sendcmd('*PSC 1') + self.sendcmd("*PSC 1") elif newval in off: - self.sendcmd('*PSC 0') + self.sendcmd("*PSC 0") else: raise ValueError @@ -94,7 +94,7 @@ def self_test_ok(self): :rtype: `bool` """ - result = self.query('*TST?') + result = self.query("*TST?") try: result = int(result) return result == 0 @@ -108,14 +108,14 @@ def reset(self): Reset instrument. On many instruments this is a factory reset and will revert all settings to default. """ - self.sendcmd('*RST') + self.sendcmd("*RST") def clear(self): """ Clear instrument. Consult manual for specifics related to that instrument. """ - self.sendcmd('*CLS') + self.sendcmd("*CLS") def trigger(self): """ @@ -126,14 +126,14 @@ def trigger(self): This software trigger usually performs the same action as a hardware trigger to your instrument. """ - self.sendcmd('*TRG') + self.sendcmd("*TRG") def wait_to_continue(self): """ Instruct the instrument to wait until it has completed all received commands before continuing. """ - self.sendcmd('*WAI') + self.sendcmd("*WAI") # SYSTEM COMMANDS ## @@ -146,16 +146,13 @@ def line_frequency(self): :units: Hertz :type: `~pint.Quantity` """ - return u.Quantity( - float(self.query("SYST:LFR?")), - "Hz" - ) + return u.Quantity(float(self.query("SYST:LFR?")), "Hz") @line_frequency.setter def line_frequency(self, newval): - self.sendcmd("SYST:LFR {}".format( - assume_units(newval, "Hz").to("Hz").magnitude - )) + self.sendcmd( + "SYST:LFR {}".format(assume_units(newval, "Hz").to("Hz").magnitude) + ) # ERROR QUEUE HANDLING ## # NOTE: This functionality is still quite incomplete, and could be fleshed @@ -170,6 +167,7 @@ class ErrorCodes(IntEnum): Enumeration describing error codes as defined by SCPI 1999.0. Error codes that are equal to 0 mod 100 are defined to be *generic*. """ + # NOTE: this class may be overriden by subclasses, since the only access # to this enumeration from within SCPIInstrument is by "self," # not by explicit name. Thus, if an instrument supports additional @@ -270,8 +268,7 @@ def display_brightness(self): @display_brightness.setter def display_brightness(self, newval): if newval < 0 or newval > 1: - raise ValueError("Display brightness must be a number between 0" - " and 1.") + raise ValueError("Display brightness must be a number between 0" " and 1.") self.sendcmd("DISP:BRIG {}".format(newval)) @property @@ -287,6 +284,5 @@ def display_contrast(self): @display_contrast.setter def display_contrast(self, newval): if newval < 0 or newval > 1: - raise ValueError("Display contrast must be a number between 0" - " and 1.") + raise ValueError("Display contrast must be a number between 0" " and 1.") self.sendcmd("DISP:CONT {}".format(newval)) diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index 83e32525f..36b3a1cfa 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -17,15 +17,15 @@ # CONSTANTS ################################################################### -VALID_FRES_NAMES = ['4res', '4 res', 'four res', 'f res'] +VALID_FRES_NAMES = ["4res", "4 res", "four res", "f res"] -UNITS_CAPACITANCE = ['cap'] -UNITS_VOLTAGE = ['volt:dc', 'volt:ac', 'diod'] -UNITS_CURRENT = ['curr:dc', 'curr:ac'] -UNITS_RESISTANCE = ['res', 'fres'] + VALID_FRES_NAMES -UNITS_FREQUENCY = ['freq'] -UNITS_TIME = ['per'] -UNITS_TEMPERATURE = ['temp'] +UNITS_CAPACITANCE = ["cap"] +UNITS_VOLTAGE = ["volt:dc", "volt:ac", "diod"] +UNITS_CURRENT = ["curr:dc", "curr:ac"] +UNITS_RESISTANCE = ["res", "fres"] + VALID_FRES_NAMES +UNITS_FREQUENCY = ["freq"] +UNITS_TIME = ["per"] +UNITS_TEMPERATURE = ["temp"] # CLASSES ##################################################################### @@ -50,6 +50,7 @@ class Mode(Enum): """ Enum of valid measurement modes for (most) SCPI compliant multimeters """ + capacitance = "CAP" continuity = "CONT" current_ac = "CURR:AC" @@ -77,6 +78,7 @@ class TriggerMode(Enum): "Bus": Causes the instrument to trigger when a ``*TRG`` command is sent by software. This means calling the trigger() function. """ + immediate = "IMM" external = "EXT" bus = "BUS" @@ -86,6 +88,7 @@ class InputRange(Enum): """ Valid device range parameters outside of directly specifying the range. """ + minimum = "MIN" maximum = "MAX" default = "DEF" @@ -97,6 +100,7 @@ class Resolution(Enum): Valid measurement resolution parameters outside of directly the resolution. """ + minimum = "MIN" maximum = "MAX" default = "DEF" @@ -106,6 +110,7 @@ class TriggerCount(Enum): """ Valid trigger count parameters outside of directly the value. """ + minimum = "MIN" maximum = "MAX" default = "DEF" @@ -116,6 +121,7 @@ class SampleCount(Enum): """ Valid sample count parameters outside of directly the value. """ + minimum = "MIN" maximum = "MAX" default = "DEF" @@ -132,6 +138,7 @@ class SampleSource(Enum): #. "timer": Successive samples start one sample interval after the START of the previous sample. """ + immediate = "IMM" timer = "TIM" @@ -151,7 +158,7 @@ class SampleSource(Enum): :type: `~SCPIMultimeter.Mode` """, input_decoration=lambda x: SCPIMultimeter._mode_parse(x), - set_fmt="{}:{}" + set_fmt="{}:{}", ) trigger_mode = enum_property( @@ -165,7 +172,8 @@ class SampleSource(Enum): >>> dmm.trigger_mode = dmm.TriggerMode.external :type: `~SCPIMultimeter.TriggerMode` - """) + """, + ) @property def input_range(self): @@ -181,7 +189,7 @@ def input_range(self): :units: As appropriate for the current mode setting. :type: `~pint.Quantity`, or `~SCPIMultimeter.InputRange` """ - value = self.query('CONF?') + value = self.query("CONF?") mode = self.Mode(self._mode_parse(value)) value = value.split(" ")[1].split(",")[0] # Extract device range try: @@ -214,7 +222,7 @@ def resolution(self): :type: `int`, `float` or `~SCPIMultimeter.Resolution` """ - value = self.query('CONF?') + value = self.query("CONF?") value = value.split(" ")[1].split(",")[1] # Extract resolution try: return float(value) @@ -229,8 +237,10 @@ def resolution(self, newval): if isinstance(newval, self.Resolution): newval = newval.value elif not isinstance(newval, (float, int)): - raise TypeError("Resolution must be specified as an int, float, " - "or SCPIMultimeter.Resolution value.") + raise TypeError( + "Resolution must be specified as an int, float, " + "or SCPIMultimeter.Resolution value." + ) self.sendcmd("CONF:{} {},{}".format(mode.value, input_range, newval)) @property @@ -258,7 +268,7 @@ def trigger_count(self): :type: `int` or `~SCPIMultimeter.TriggerCount` """ - value = self.query('TRIG:COUN?') + value = self.query("TRIG:COUN?") try: return int(value) except ValueError: @@ -269,8 +279,10 @@ def trigger_count(self, newval): if isinstance(newval, self.TriggerCount): newval = newval.value elif not isinstance(newval, int): - raise TypeError("Trigger count must be specified as an int " - "or SCPIMultimeter.TriggerCount value.") + raise TypeError( + "Trigger count must be specified as an int " + "or SCPIMultimeter.TriggerCount value." + ) self.sendcmd("TRIG:COUN {}".format(newval)) @property @@ -299,7 +311,7 @@ def sample_count(self): :type: `int` or `~SCPIMultimeter.SampleCount` """ - value = self.query('SAMP:COUN?') + value = self.query("SAMP:COUN?") try: return int(value) except ValueError: @@ -310,8 +322,10 @@ def sample_count(self, newval): if isinstance(newval, self.SampleCount): newval = newval.value elif not isinstance(newval, int): - raise TypeError("Sample count must be specified as an int " - "or SCPIMultimeter.SampleCount value.") + raise TypeError( + "Sample count must be specified as an int " + "or SCPIMultimeter.SampleCount value." + ) self.sendcmd("SAMP:COUN {}".format(newval)) trigger_delay = unitful_property( @@ -323,7 +337,7 @@ def sample_count(self, newval): :units: As specified, or assumed to be of units seconds otherwise. :type: `~pint.Quantity` - """ + """, ) sample_source = enum_property( @@ -338,7 +352,7 @@ def sample_count(self, newval): after the trigger event. After that, it depends on which mode is used. :type: `SCPIMultimeter.SampleSource` - """ + """, ) sample_timer = unitful_property( @@ -355,7 +369,7 @@ def sample_count(self, newval): :units: As specified, or assumed to be of units seconds otherwise. :type: `~pint.Quantity` - """ + """, ) @property @@ -386,10 +400,12 @@ def measure(self, mode=None): if mode is None: mode = self.mode if not isinstance(mode, SCPIMultimeter.Mode): - raise TypeError("Mode must be specified as a SCPIMultimeter.Mode " - "value, got {} instead.".format(type(mode))) + raise TypeError( + "Mode must be specified as a SCPIMultimeter.Mode " + "value, got {} instead.".format(type(mode)) + ) # pylint: disable=no-member - value = float(self.query('MEAS:{}?'.format(mode.value))) + value = float(self.query("MEAS:{}?".format(mode.value))) return value * UNITS[mode] # INTERNAL FUNCTIONS ## @@ -413,19 +429,20 @@ def _mode_parse(val): val = "VOLT:DC" return val + # UNITS ####################################################################### UNITS = { SCPIMultimeter.Mode.capacitance: u.farad, - SCPIMultimeter.Mode.voltage_dc: u.volt, - SCPIMultimeter.Mode.voltage_ac: u.volt, - SCPIMultimeter.Mode.diode: u.volt, - SCPIMultimeter.Mode.current_ac: u.amp, - SCPIMultimeter.Mode.current_dc: u.amp, - SCPIMultimeter.Mode.resistance: u.ohm, + SCPIMultimeter.Mode.voltage_dc: u.volt, + SCPIMultimeter.Mode.voltage_ac: u.volt, + SCPIMultimeter.Mode.diode: u.volt, + SCPIMultimeter.Mode.current_ac: u.amp, + SCPIMultimeter.Mode.current_dc: u.amp, + SCPIMultimeter.Mode.resistance: u.ohm, SCPIMultimeter.Mode.fourpt_resistance: u.ohm, - SCPIMultimeter.Mode.frequency: u.hertz, - SCPIMultimeter.Mode.period: u.second, + SCPIMultimeter.Mode.frequency: u.hertz, + SCPIMultimeter.Mode.period: u.second, SCPIMultimeter.Mode.temperature: u.kelvin, - SCPIMultimeter.Mode.continuity: 1, + SCPIMultimeter.Mode.continuity: 1, } diff --git a/instruments/gentec_eo/blu.py b/instruments/gentec_eo/blu.py index bfd8241e0..252282d84 100644 --- a/instruments/gentec_eo/blu.py +++ b/instruments/gentec_eo/blu.py @@ -68,6 +68,7 @@ class Scale(Enum): `enum` class while the unit is omitted since it depends on the mode the head is in. """ + max1pico = "00" max3pico = "01" max10pico = "02" @@ -187,7 +188,7 @@ def available_scales(self): try: # get the response - resp = self._no_ack_query("*DVS").split('\r\n') + resp = self._no_ack_query("*DVS").split("\r\n") finally: # set back terminator and 3 second timeout self.terminator = _terminator @@ -197,7 +198,7 @@ def available_scales(self): retlist = [] # init return list of enums for line in resp: if len(line) > 0: # account for empty lines - index = line[line.find("[")+1:line.find("]")] + index = line[line.find("[") + 1 : line.find("]")] retlist.append(self.Scale(index)) return retlist @@ -215,7 +216,7 @@ def battery_state(self): array(100.) * % """ resp = self._no_ack_query("*QSO").rstrip() - resp = float(resp[resp.find("=")+1:len(resp)]) + resp = float(resp[resp.find("=") + 1 : len(resp)]) return u.Quantity(resp, u.percent) @property @@ -273,10 +274,10 @@ def measure_mode(self): resp = self._value_query("*GMD", tp=int) if resp == 0: self._power_mode = True - return 'power' + return "power" else: self._power_mode = False - return 'sse' + return "sse" @property def new_value_ready(self): @@ -332,7 +333,7 @@ def scale(self, newval): @property def single_shot_energy_mode(self): - """ Get / Set single shot energy mode. + """Get / Set single shot energy mode. :return: Is single shot energy mode turned on? :rtype: bool @@ -376,16 +377,18 @@ def trigger_level(self): """ level = self._no_ack_query("*GTL") # get the percent - retval = float(level[level.find(":")+1:level.find("%")]) / 100 + retval = float(level[level.find(":") + 1 : level.find("%")]) / 100 return retval @trigger_level.setter def trigger_level(self, newval): if newval < 0.001 or newval > 0.99: - raise ValueError("Trigger level {} is out of range. It must be " - "between 0.001 and 0.998.".format(newval)) + raise ValueError( + "Trigger level {} is out of range. It must be " + "between 0.001 and 0.998.".format(newval) + ) - newval = newval * 100. + newval = newval * 100.0 if newval >= 10: newval = str(round(newval, 1)).zfill(4) else: @@ -467,8 +470,9 @@ def user_offset(self, newval): elif newval.is_compatible_with(u.J): newval = newval.to(u.J).magnitude else: - raise ValueError("Value must be given in watts, " - "joules, or unitless.") + raise ValueError( + "Value must be given in watts, " "joules, or unitless." + ) sendval = _format_eight(newval) # sendval: 8 characters long self.sendcmd("*OFF{}".format(sendval)) diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 4a4005a4b..888a5484b 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -35,10 +35,7 @@ from struct import unpack from enum import Enum -from instruments.abstract_instruments import ( - PowerSupply, - PowerSupplyChannel -) +from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel from instruments.units import ureg as u from instruments.util_fns import assume_units @@ -92,8 +89,8 @@ def __init__(self, filelike): self.current_max = 6.0 * u.milliamp self.polarity = +1 self._device_timeout = False - self._voltage = 0. * u.volt - self._current = 0. * u.amp + self._voltage = 0.0 * u.volt + self._current = 0.0 * u.amp # ENUMS ## @@ -101,6 +98,7 @@ class Mode(Enum): """ Enum containing the possible modes of operations of the instrument """ + #: Constant voltage mode voltage = "0" #: Constant current mode @@ -110,6 +108,7 @@ class ResponseCode(Enum): """ Enum containing the possible reponse codes returned by the instrument. """ + #: A set command expects an acknowledge response (`A`) S = "A" #: A query command expects a response packet (`R`) @@ -123,6 +122,7 @@ class ErrorCode(Enum): """ Enum containing the possible error codes returned by the instrument. """ + #: Undefined command received (not S, Q, V or C) undefined_command = "1" #: The checksum calculated by the instrument does not correspond to the one received @@ -157,7 +157,7 @@ def voltage(self): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` """ - return self.polarity*self._voltage + return self.polarity * self._voltage @voltage.setter def voltage(self, newval): @@ -171,7 +171,7 @@ def current(self): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` """ - return self.polarity*self._current + return self.polarity * self._current @current.setter def current(self, newval): @@ -329,28 +329,32 @@ def set_status(self, voltage=None, current=None, output=None, reset=False): kept as what they were set to previously. """ if reset: - self._voltage = 0. * u.volt - self._current = 0. * u.amp + self._voltage = 0.0 * u.volt + self._current = 0.0 * u.amp cmd = format(4, "013d") else: # The maximum value is encoded as the maximum of three hex characters (4095) - cmd = '' - value_max = int(0xfff) + cmd = "" + value_max = int(0xFFF) # If the voltage is not specified, keep it as is - voltage = assume_units(voltage, u.volt) if voltage is not None else self.voltage - ratio = float(voltage.to(u.volt)/self.voltage_max.to(u.volt)) - voltage_int = int(round(value_max*ratio)) - self._voltage = self.voltage_max*float(voltage_int)/value_max - assert 0. * u.volt <= self._voltage <= self.voltage_max + voltage = ( + assume_units(voltage, u.volt) if voltage is not None else self.voltage + ) + ratio = float(voltage.to(u.volt) / self.voltage_max.to(u.volt)) + voltage_int = int(round(value_max * ratio)) + self._voltage = self.voltage_max * float(voltage_int) / value_max + assert 0.0 * u.volt <= self._voltage <= self.voltage_max cmd += format(voltage_int, "03X") # If the current is not specified, keep it as is - current = assume_units(current, u.amp) if current is not None else self.current - ratio = float(current.to(u.amp)/self.current_max.to(u.amp)) - current_int = int(round(value_max*ratio)) - self._current = self.current_max*float(current_int)/value_max - assert 0. * u.amp <= self._current <= self.current_max + current = ( + assume_units(current, u.amp) if current is not None else self.current + ) + ratio = float(current.to(u.amp) / self.current_max.to(u.amp)) + current_int = int(round(value_max * ratio)) + self._current = self.current_max * float(current_int) / value_max + assert 0.0 * u.amp <= self._current <= self.current_max cmd += format(current_int, "03X") # If the output status is not specified, keep it as is @@ -383,22 +387,22 @@ def _parse_response(self, response): :rtype: `dict` """ - (voltage, current, monitors) = \ - unpack("@3s3s3x1c2x", bytes(response, "utf-8")) + (voltage, current, monitors) = unpack("@3s3s3x1c2x", bytes(response, "utf-8")) try: voltage = self._parse_voltage(voltage) current = self._parse_current(current) mode, fault, output = self._parse_monitors(monitors) except: - raise RuntimeError("Cannot parse response " - "packet: {}".format(response)) + raise RuntimeError("Cannot parse response " "packet: {}".format(response)) - return {"voltage": voltage, - "current": current, - "mode": mode, - "fault": fault, - "output": output} + return { + "voltage": voltage, + "current": current, + "mode": mode, + "fault": fault, + "output": output, + } def _parse_voltage(self, word): """ @@ -410,9 +414,9 @@ def _parse_voltage(self, word): :rtype: `~pint.Quantity` """ - value = int(word.decode('utf-8'), 16) - value_max = int(0x3ff) - return self.polarity*self.voltage_max*float(value)/value_max + value = int(word.decode("utf-8"), 16) + value_max = int(0x3FF) + return self.polarity * self.voltage_max * float(value) / value_max def _parse_current(self, word): """ @@ -425,8 +429,8 @@ def _parse_current(self, word): :rtype: `~pint.Quantity` """ value = int(word.decode("utf-8"), 16) - value_max = int(0x3ff) - return self.polarity*self.current_max*float(value)/value_max + value_max = int(0x3FF) + return self.polarity * self.current_max * float(value) / value_max def _parse_monitors(self, word): """ diff --git a/instruments/holzworth/holzworth_hs9000.py b/instruments/holzworth/holzworth_hs9000.py index 9beef39ab..0da72c3c2 100644 --- a/instruments/holzworth/holzworth_hs9000.py +++ b/instruments/holzworth/holzworth_hs9000.py @@ -9,12 +9,12 @@ from instruments.units import ureg as u -from instruments.abstract_instruments.signal_generator import ( - SignalGenerator, - SGChannel -) +from instruments.abstract_instruments.signal_generator import SignalGenerator, SGChannel from instruments.util_fns import ( - ProxyList, split_unit_str, bounded_unitful_property, bool_property + ProxyList, + split_unit_str, + bounded_unitful_property, + bool_property, ) # CLASSES ##################################################################### @@ -49,8 +49,7 @@ def __init__(self, hs, idx_chan): # Some channel names, like "REF", are special and are preserved # as strs. self._ch_name = ( - idx_chan if isinstance(idx_chan, str) - else "CH{}".format(idx_chan + 1) + idx_chan if isinstance(idx_chan, str) else "CH{}".format(idx_chan + 1) ) # PRIVATE METHODS # @@ -143,7 +142,7 @@ def temperature(self): :type: `~pint.Quantity` :units: As specified or assumed to be of units GHz - """ + """, ) power, power_min, power_max = bounded_unitful_property( "PWR", @@ -162,7 +161,7 @@ def temperature(self): :type: `~pint.Quantity` :units: `instruments.units.dBm` - """ + """, ) phase, phase_min, phase_max = bounded_unitful_property( "PHASE", @@ -181,7 +180,7 @@ def temperature(self): :type: `~pint.Quantity` :units: As specified or assumed to be of units degrees - """ + """, ) output = bool_property( @@ -201,7 +200,7 @@ def temperature(self): >>> hs.channel[0].output = True :type: `bool` - """ + """, ) # PROXY LIST ## @@ -221,8 +220,8 @@ def _channel_idxs(self): return [ ( int(ch_name.replace("CH", "")) - 1 - if ch_name.startswith('CH') else - ch_name.strip() + if ch_name.startswith("CH") + else ch_name.strip() ) for ch_name in self.query(":ATTACH?").split(":") if ch_name diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index 976b09782..4330cc429 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -74,6 +74,7 @@ class MathMode(IntEnum): """ Enum with the supported math modes """ + off = 0 pass_fail = 1 statistic = 2 @@ -90,6 +91,7 @@ class Mode(Enum): """ Enum containing the supported mode codes """ + #: DC voltage dcv = "S0F1" #: AC voltage @@ -116,6 +118,7 @@ class Register(Enum): """ Enum with the register names for all `HP3456a` internal registers. """ + number_of_readings = "N" number_of_digits = "G" nplc = "I" @@ -134,6 +137,7 @@ class TriggerMode(IntEnum): """ Enum with valid trigger modes. """ + internal = 1 external = 2 single = 3 @@ -146,6 +150,7 @@ class ValidRange(Enum): powerline cycles to integrate over. """ + voltage = (1e-1, 1e0, 1e1, 1e2, 1e3) resistance = (1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9) nplc = (1e-1, 1e0, 1e1, 1e2) @@ -160,7 +165,8 @@ class ValidRange(Enum): :type: `HP3456a.Mode` """, writeonly=True, - set_fmt="{}{}") + set_fmt="{}{}", + ) autozero = bool_property( "Z", @@ -179,7 +185,8 @@ class ValidRange(Enum): suitable for high impendance measurements since no input switching is done.""", writeonly=True, - set_fmt="{}{}") + set_fmt="{}{}", + ) filter = bool_property( "FL", @@ -194,7 +201,8 @@ class ValidRange(Enum): ac converter and input amplifier. In these modes select the filter for measurements below 400Hz.""", writeonly=True, - set_fmt="{}{}") + set_fmt="{}{}", + ) math_mode = enum_property( "M", @@ -209,7 +217,8 @@ class ValidRange(Enum): :type: `HP3456a.MathMode` """, writeonly=True, - set_fmt="{}{}") + set_fmt="{}{}", + ) trigger_mode = enum_property( "T", @@ -223,7 +232,8 @@ class ValidRange(Enum): """, writeonly=True, - set_fmt="{}{}") + set_fmt="{}{}", + ) @property def number_of_readings(self): @@ -256,8 +266,9 @@ def number_of_digits(self): def number_of_digits(self, newval): newval = int(newval) if newval not in range(3, 7): - raise ValueError("Valid number_of_digits are: " - "{}".format(list(range(3, 7)))) + raise ValueError( + "Valid number_of_digits are: " "{}".format(list(range(3, 7))) + ) self._register_write(HP3456a.Register.number_of_digits, newval) @@ -281,8 +292,7 @@ def nplc(self, newval): if newval in valid: self._register_write(HP3456a.Register.nplc, newval) else: - raise ValueError("Valid nplc settings are: " - "{}".format(valid)) + raise ValueError("Valid nplc settings are: " "{}".format(valid)) @property def delay(self): @@ -426,8 +436,10 @@ def input_range(self, value): if value.lower() == "auto": self.sendcmd("R1W") else: - raise ValueError("Only 'auto' is acceptable when specifying " - "the input range as a string.") + raise ValueError( + "Only 'auto' is acceptable when specifying " + "the input range as a string." + ) elif isinstance(value, u.Quantity): if value.units == u.volt: @@ -437,18 +449,22 @@ def input_range(self, value): valid = HP3456a.ValidRange.resistance.value value = value.to(u.ohm) else: - raise ValueError("Value {} not quantity.volt or quantity.ohm" - "".format(value)) + raise ValueError( + "Value {} not quantity.volt or quantity.ohm" "".format(value) + ) value = float(value.magnitude) if value not in valid: - raise ValueError("Value {} outside valid ranges " - "{}".format(value, valid)) + raise ValueError( + "Value {} outside valid ranges " "{}".format(value, valid) + ) value = valid.index(value) + 2 self.sendcmd("R{}W".format(value)) else: - raise TypeError("Range setting must be specified as a float, int, " - "or the string 'auto', got {}".format(type(value))) + raise TypeError( + "Range setting must be specified as a float, int, " + "or the string 'auto', got {}".format(type(value)) + ) @property def relative(self): @@ -468,8 +484,10 @@ def relative(self, value): self._null = False self.sendcmd("M{}".format(HP3456a.MathMode.off.value)) else: - raise TypeError("Relative setting must be specified as a bool, " - "got {}".format(type(value))) + raise TypeError( + "Relative setting must be specified as a bool, " + "got {}".format(type(value)) + ) # METHODS ## @@ -570,11 +588,13 @@ def _register_read(self, name): except KeyError: pass if not isinstance(name, HP3456a.Register): - raise TypeError("register must be specified as a " - "HP3456a.Register, got {} " - "instead.".format(name)) + raise TypeError( + "register must be specified as a " + "HP3456a.Register, got {} " + "instead.".format(name) + ) self.sendcmd("RE{}".format(name.value)) - time.sleep(.1) + time.sleep(0.1) return float(self.query("", size=-1)) def _register_write(self, name, value): @@ -590,17 +610,19 @@ def _register_write(self, name, value): except KeyError: pass if not isinstance(name, HP3456a.Register): - raise TypeError("register must be specified as a " - "HP3456a.Register, got {} " - "instead.".format(name)) + raise TypeError( + "register must be specified as a " + "HP3456a.Register, got {} " + "instead.".format(name) + ) if name in [ - HP3456a.Register.mean, - HP3456a.Register.variance, - HP3456a.Register.count + HP3456a.Register.mean, + HP3456a.Register.variance, + HP3456a.Register.count, ]: raise ValueError("register {} is read only".format(name)) self.sendcmd("W{}ST{}".format(value, name.value)) - time.sleep(.1) + time.sleep(0.1) def trigger(self): """ @@ -608,6 +630,7 @@ def trigger(self): """ self.sendcmd("T3") + # UNITS ####################################################################### UNITS = { diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index 9f65fb44d..5f363da34 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -8,10 +8,7 @@ from enum import Enum -from instruments.abstract_instruments import ( - PowerSupply, - PowerSupplyChannel -) +from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel from instruments.units import ureg as u from instruments.util_fns import ProxyList, unitful_property, bool_property @@ -61,9 +58,7 @@ def _format_cmd(self, cmd): cmd = "{cmd} {idx}".format(cmd=cmd[0], idx=self._idx) else: cmd = "{cmd} {idx},{value}".format( - cmd=cmd[0], - idx=self._idx, - value=cmd[1] + cmd=cmd[0], idx=self._idx, value=cmd[1] ) return cmd @@ -117,7 +112,7 @@ def mode(self, newval): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) current = unitful_property( @@ -133,7 +128,7 @@ def mode(self, newval): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) voltage_sense = unitful_property( @@ -146,7 +141,7 @@ def mode(self, newval): :units: :math:`\\text{V}` (volts) :rtype: `~pint.Quantity` - """ + """, ) current_sense = unitful_property( @@ -159,7 +154,7 @@ def mode(self, newval): :units: :math:`\\text{A}` (amps) :rtype: `~pint.Quantity` - """ + """, ) overvoltage = unitful_property( @@ -174,7 +169,7 @@ def mode(self, newval): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) overcurrent = bool_property( @@ -187,7 +182,7 @@ def mode(self, newval): This is a toggle setting. It is either on or off. :type: `bool` - """ + """, ) output = bool_property( @@ -201,7 +196,7 @@ def mode(self, newval): while False will turn it off. :type: `bool` - """ + """, ) # METHODS ## @@ -210,8 +205,8 @@ def reset(self): """ Reset overvoltage and overcurrent errors to resume operation. """ - self.sendcmd('OVRST') - self.sendcmd('OCRST') + self.sendcmd("OVRST") + self.sendcmd("OCRST") # ENUMS # @@ -223,6 +218,7 @@ class Mode(Enum): constant-voltage output, so this class current does not do anything and is just a placeholder. """ + voltage = 0 current = 0 @@ -250,17 +246,17 @@ def voltage(self): of units Volts. :type: `tuple`[`~pint.Quantity`, ...] with units Volt """ - return tuple([ - self.channel[i].voltage for i in range(self.channel_count) - ]) + return tuple([self.channel[i].voltage for i in range(self.channel_count)]) @voltage.setter def voltage(self, newval): if isinstance(newval, (list, tuple)): if len(newval) is not self.channel_count: - raise ValueError('When specifying the voltage for all channels ' - 'as a list or tuple, it must be of ' - 'length {}.'.format(self.channel_count)) + raise ValueError( + "When specifying the voltage for all channels " + "as a list or tuple, it must be of " + "length {}.".format(self.channel_count) + ) for i in range(self.channel_count): self.channel[i].voltage = newval[i] else: @@ -276,17 +272,17 @@ def current(self): of units Amps. :type: `tuple`[`~pint.Quantity`, ...] with units Amp """ - return tuple([ - self.channel[i].current for i in range(self.channel_count) - ]) + return tuple([self.channel[i].current for i in range(self.channel_count)]) @current.setter def current(self, newval): if isinstance(newval, (list, tuple)): if len(newval) is not self.channel_count: - raise ValueError('When specifying the current for all channels ' - 'as a list or tuple, it must be of ' - 'length {}.'.format(self.channel_count)) + raise ValueError( + "When specifying the current for all channels " + "as a list or tuple, it must be of " + "length {}.".format(self.channel_count) + ) for i in range(self.channel_count): self.channel[i].current = newval[i] else: @@ -301,9 +297,7 @@ def voltage_sense(self): :units: :math:`\\text{V}` (volts) :rtype: `tuple`[`~pint.Quantity`, ...] """ - return tuple( - self.channel[i].voltage_sense for i in range(self.channel_count) - ) + return tuple(self.channel[i].voltage_sense for i in range(self.channel_count)) @property def current_sense(self): @@ -313,9 +307,7 @@ def current_sense(self): :units: :math:`\\text{A}` (amps) :rtype: `tuple`[`~pint.Quantity`, ...] """ - return tuple( - self.channel[i].current_sense for i in range(self.channel_count) - ) + return tuple(self.channel[i].current_sense for i in range(self.channel_count)) @property def channel_count(self): @@ -330,9 +322,9 @@ def channel_count(self): @channel_count.setter def channel_count(self, newval): if not isinstance(newval, int): - raise TypeError('Channel count must be specified as an integer.') + raise TypeError("Channel count must be specified as an integer.") if newval < 1: - raise ValueError('Channel count must be >=1') + raise ValueError("Channel count must be >=1") self._channel_count = newval # METHODS ## @@ -348,4 +340,4 @@ def clear(self): #) The power supply remains addressed to listen. #) The PON bit in the serial poll register is cleared. """ - self.sendcmd('CLR') + self.sendcmd("CLR") diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index 2fd662eb2..cc24c6300 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -37,8 +37,13 @@ from instruments.generic_scpi.scpi_instrument import SCPIInstrument from instruments.hp.hp6652a import HP6652a from instruments.units import ureg as u -from instruments.util_fns import (unitful_property, unitless_property, - bool_property, enum_property, int_property) +from instruments.util_fns import ( + unitful_property, + unitless_property, + bool_property, + enum_property, + int_property, +) # CLASSES ##################################################################### @@ -79,6 +84,7 @@ class ALCBandwidth(IntEnum): """ Enum containing valid ALC bandwidth modes for the hp6632b """ + normal = 1.5e4 fast = 6e4 @@ -86,24 +92,27 @@ class DigitalFunction(Enum): """ Enum containing valid digital function modes for the hp6632b """ - remote_inhibit = 'RIDF' - data = 'DIG' + + remote_inhibit = "RIDF" + data = "DIG" class DFISource(Enum): """ Enum containing valid DFI sources for the hp6632b """ - questionable = 'QUES' - operation = 'OPER' - event_status_bit = 'ESB' - request_service_bit = 'RQS' - off = 'OFF' + + questionable = "QUES" + operation = "OPER" + event_status_bit = "ESB" + request_service_bit = "RQS" + off = "OFF" class ErrorCodes(IntEnum): """ Enum containing generic-SCPI error codes along with codes specific to the HP6632b. """ + no_error = 0 # -100 BLOCK: COMMAND ERRORS ## @@ -226,16 +235,18 @@ class RemoteInhibit(Enum): """ Enum containing vlaid remote inhibit modes for the hp6632b. """ - latching = 'LATC' - live = 'LIVE' - off = 'OFF' + + latching = "LATC" + live = "LIVE" + off = "OFF" class SenseWindow(Enum): """ Enum containing valid sense window modes for the hp6632b. """ - hanning = 'HANN' - rectangular = 'RECT' + + hanning = "HANN" + rectangular = "RECT" # PROPERTIES ## @@ -250,7 +261,7 @@ class SenseWindow(Enum): denotes that it is, and `Fast` denotes that it is not. :type: `~HP6632b.ALCBandwidth` - """ + """, ) voltage_trigger = unitful_property( @@ -263,7 +274,7 @@ class SenseWindow(Enum): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) current_trigger = unitful_property( @@ -276,7 +287,7 @@ class SenseWindow(Enum): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) init_output_continuous = bool_property( @@ -290,11 +301,11 @@ class SenseWindow(Enum): levels. :type: `bool` - """ + """, ) current_sense_range = unitful_property( - 'SENS:CURR:RANGE', + "SENS:CURR:RANGE", u.ampere, doc=""" Get/set the sense current range by the current max value. @@ -305,13 +316,13 @@ class SenseWindow(Enum): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) output_dfi = bool_property( - 'OUTP:DFI', - '1', - '0', + "OUTP:DFI", + "1", + "0", doc=""" Get/set the discrete fault indicator (DFI) output from the dc source. The DFI is an open-collector logic signal connected to the read @@ -319,7 +330,7 @@ class SenseWindow(Enum): a fault is detected. :type: `bool` - """ + """, ) output_dfi_source = enum_property( @@ -329,7 +340,7 @@ class SenseWindow(Enum): Get/set the source for discrete fault indicator (DFI) events. :type: `~HP6632b.DFISource` - """ + """, ) output_remote_inhibit = enum_property( @@ -341,7 +352,7 @@ class SenseWindow(Enum): connection, which allows an external device to signal a fault. :type: `~HP6632b.RemoteInhibit` - """ + """, ) digital_function = enum_property( @@ -351,7 +362,7 @@ class SenseWindow(Enum): Get/set the inhibit+fault port to digital in+out or vice-versa. :type: `~HP6632b.DigitalFunction` - """ + """, ) digital_data = int_property( @@ -361,7 +372,7 @@ class SenseWindow(Enum): Get/set digital in+out port to data. Data can be an integer from 0-7. :type: `int` - """ + """, ) sense_sweep_points = unitless_property( @@ -370,7 +381,7 @@ class SenseWindow(Enum): Get/set the number of points in a measurement sweep. :type: `int` - """ + """, ) sense_sweep_interval = unitful_property( @@ -382,7 +393,7 @@ class SenseWindow(Enum): :units: As specified, or assumed to be :math:`\\text{s}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) sense_window = enum_property( @@ -392,7 +403,7 @@ class SenseWindow(Enum): Get/set the measurement window function. :type: `~HP6632b.SenseWindow` - """ + """, ) output_protection_delay = unitful_property( @@ -406,7 +417,7 @@ class SenseWindow(Enum): :units: As specified, or assumed to be :math:`\\text{s}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) # FUNCTIONS ## @@ -416,13 +427,13 @@ def init_output_trigger(self): Set the output trigger system to the initiated state. In this state, the power supply will respond to the next output trigger command. """ - self.sendcmd('INIT:NAME TRAN') + self.sendcmd("INIT:NAME TRAN") def abort_output_trigger(self): """ Set the output trigger system to the idle state. """ - self.sendcmd('ABORT') + self.sendcmd("ABORT") # SCPIInstrument commands that need local overrides @@ -459,7 +470,7 @@ def check_error_queue(self): done = False result = [] while not done: - err = int(self.query('SYST:ERR?').split(',')[0]) + err = int(self.query("SYST:ERR?").split(",")[0]) if err == self.ErrorCodes.no_error: done = True else: diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index 09470dfff..abdca8336 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -11,7 +11,7 @@ from instruments.units import ureg as u -from instruments.abstract_instruments import (PowerSupply, PowerSupplyChannel) +from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel from instruments.util_fns import unitful_property, bool_property @@ -70,7 +70,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) current = unitful_property( @@ -83,7 +83,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) voltage_sense = unitful_property( @@ -95,7 +95,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): :units: :math:`\\text{V}` (volts) :rtype: `~pint.Quantity` - """ + """, ) current_sense = unitful_property( @@ -107,7 +107,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): :units: :math:`\\text{A}` (amps) :rtype: `~pint.Quantity` - """ + """, ) overvoltage = unitful_property( @@ -120,7 +120,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) overcurrent = bool_property( @@ -133,7 +133,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): This is a toggle setting. It is either on or off. :type: `bool` - """ + """, ) output = bool_property( @@ -147,7 +147,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): while False will turn it off. :type: `bool` - """ + """, ) display_textmode = bool_property( @@ -164,7 +164,7 @@ class HP6652a(PowerSupply, PowerSupplyChannel): .. seealso:: `~HP6652a.display_text()` :type: `bool` - """ + """, ) @property @@ -176,8 +176,8 @@ def name(self): :rtype: `str` """ idn_string = self.query("*IDN?") - idn_list = idn_string.split(',') - return ' '.join(idn_list[:2]) + idn_list = idn_string.split(",") + return " ".join(idn_list[:2]) @property def mode(self): @@ -199,7 +199,7 @@ def reset(self): """ Reset overvoltage and overcurrent errors to resume operation. """ - self.sendcmd('OUTP:PROT:CLE') + self.sendcmd("OUTP:PROT:CLE") def display_text(self, text_to_display): """ @@ -248,4 +248,4 @@ def channel(self): :rtype: 'tuple' of length 1 containing a reference back to the parent HP6652a object. """ - return self, + return (self,) diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index a7ac30132..b04c0e6c0 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -37,10 +37,7 @@ from instruments.units import ureg as u -from instruments.abstract_instruments import ( - PowerSupply, - PowerSupplyChannel -) +from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ( int_property, @@ -48,7 +45,7 @@ bounded_unitful_property, bool_property, split_unit_str, - assume_units + assume_units, ) # CLASSES ##################################################################### @@ -150,7 +147,7 @@ def mode(self): Gets/Sets the active channel ID. :type: `HPe3631a.ChannelType` - """ + """, ) @property @@ -174,17 +171,21 @@ def voltage(self, newval): """ min_value, max_value = self.voltage_range if newval < min_value: - raise ValueError("Voltage quantity is too low. Got {}, minimum " - "value is {}".format(newval, min_value)) + raise ValueError( + "Voltage quantity is too low. Got {}, minimum " + "value is {}".format(newval, min_value) + ) if newval > max_value: - raise ValueError("Voltage quantity is too high. Got {}, maximum " - "value is {}".format(newval, max_value)) + raise ValueError( + "Voltage quantity is too high. Got {}, maximum " + "value is {}".format(newval, max_value) + ) # Rescale to the correct unit before printing. This will also # catch bad units. strval = "{:e}".format(assume_units(newval, u.volt).to(u.volt).magnitude) - self.sendcmd('SOUR:VOLT {}'.format(strval)) + self.sendcmd("SOUR:VOLT {}".format(strval)) @property def voltage_min(self): @@ -220,9 +221,9 @@ def voltage_range(self): :type: array of `~pint.Quantity` """ value = u.Quantity(*split_unit_str(self.query("SOUR:VOLT? MAX"), u.volt)) - if value < 0.: - return value, 0. - return 0., value + if value < 0.0: + return value, 0.0 + return 0.0, value current, current_min, current_max = bounded_unitful_property( "SOUR:CURR", @@ -234,7 +235,7 @@ def voltage_range(self): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) voltage_sense = unitful_property( @@ -246,7 +247,7 @@ def voltage_range(self): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `~pint.Quantity` - """ + """, ) current_sense = unitful_property( @@ -258,7 +259,7 @@ def voltage_range(self): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `~pint.Quantity` - """ + """, ) output = bool_property( @@ -272,5 +273,5 @@ def voltage_range(self): while OFF will turn it off. :type: `bool` - """ + """, ) diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index a0ba2e3fc..0e3db9f24 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -35,8 +35,8 @@ class Keithley195(Multimeter): def __init__(self, filelike): super(Keithley195, self).__init__(filelike) - self.sendcmd('YX') # Removes the termination CRLF - self.sendcmd('G1DX') # Disable returning prefix and suffix + self.sendcmd("YX") # Removes the termination CRLF + self.sendcmd("G1DX") # Disable returning prefix and suffix # ENUMS ## @@ -44,6 +44,7 @@ class Mode(IntEnum): """ Enum containing valid measurement modes for the Keithley 195 """ + voltage_dc = 0 voltage_ac = 1 resistance = 2 @@ -54,6 +55,7 @@ class TriggerMode(IntEnum): """ Enum containing valid trigger modes for the Keithley 195 """ + talk_continuous = 0 talk_one_shot = 1 get_continuous = 2 @@ -67,6 +69,7 @@ class ValidRange(Enum): """ Enum containing valid range settings for the Keithley 195 """ + voltage_dc = (20e-3, 200e-3, 2, 20, 200, 1000) voltage_ac = (20e-3, 200e-3, 2, 20, 200, 700) current_dc = (20e-6, 200e-6, 2e-3, 20e-3, 200e-3, 2) @@ -91,16 +94,18 @@ def mode(self): :type: `Keithley195.Mode` """ - return self.parse_status_word(self.get_status_word())['mode'] + return self.parse_status_word(self.get_status_word())["mode"] @mode.setter def mode(self, newval): if isinstance(newval, str): newval = self.Mode[newval] if not isinstance(newval, Keithley195.Mode): - raise TypeError("Mode must be specified as a Keithley195.Mode " - "value, got {} instead.".format(newval)) - self.sendcmd('F{}DX'.format(newval.value)) + raise TypeError( + "Mode must be specified as a Keithley195.Mode " + "value, got {} instead.".format(newval) + ) + self.sendcmd("F{}DX".format(newval.value)) @property def trigger_mode(self): @@ -127,17 +132,19 @@ def trigger_mode(self): :type: `Keithley195.TriggerMode` """ - return self.parse_status_word(self.get_status_word())['trigger'] + return self.parse_status_word(self.get_status_word())["trigger"] @trigger_mode.setter def trigger_mode(self, newval): if isinstance(newval, str): newval = Keithley195.TriggerMode[newval] if not isinstance(newval, Keithley195.TriggerMode): - raise TypeError('Drive must be specified as a ' - 'Keithley195.TriggerMode, got {} ' - 'instead.'.format(newval)) - self.sendcmd('T{}X'.format(newval.value)) + raise TypeError( + "Drive must be specified as a " + "Keithley195.TriggerMode, got {} " + "instead.".format(newval) + ) + self.sendcmd("T{}X".format(newval.value)) @property def relative(self): @@ -159,13 +166,13 @@ def relative(self): :type: `bool` """ - return self.parse_status_word(self.get_status_word())['relative'] + return self.parse_status_word(self.get_status_word())["relative"] @relative.setter def relative(self, newval): if not isinstance(newval, bool): - raise TypeError('Relative mode must be a boolean.') - self.sendcmd('Z{}DX'.format(int(newval))) + raise TypeError("Relative mode must be a boolean.") + self.sendcmd("Z{}DX".format(int(newval))) @property def input_range(self): @@ -189,9 +196,9 @@ def input_range(self): :rtype: `~pint.Quantity` or `str` """ - index = self.parse_status_word(self.get_status_word())['range'] + index = self.parse_status_word(self.get_status_word())["range"] if index == 0: - return 'auto' + return "auto" mode = self.mode value = Keithley195.ValidRange[mode.name].value[index - 1] @@ -201,12 +208,14 @@ def input_range(self): @input_range.setter def input_range(self, newval): if isinstance(newval, str): - if newval.lower() == 'auto': - self.sendcmd('R0DX') + if newval.lower() == "auto": + self.sendcmd("R0DX") return else: - raise ValueError('Only "auto" is acceptable when specifying ' - 'the input range as a string.') + raise ValueError( + 'Only "auto" is acceptable when specifying ' + "the input range as a string." + ) if isinstance(newval, u.Quantity): newval = float(newval.magnitude) @@ -216,12 +225,15 @@ def input_range(self, newval): if newval in valid: newval = valid.index(newval) + 1 else: - raise ValueError('Valid range settings for mode {} ' - 'are: {}'.format(mode, valid)) + raise ValueError( + "Valid range settings for mode {} " "are: {}".format(mode, valid) + ) else: - raise TypeError('Range setting must be specified as a float, int, ' - 'or the string "auto", got {}'.format(type(newval))) - self.sendcmd('R{}DX'.format(newval)) + raise TypeError( + "Range setting must be specified as a float, int, " + 'or the string "auto", got {}'.format(type(newval)) + ) + self.sendcmd("R{}DX".format(newval)) # METHODS # @@ -261,7 +273,7 @@ def measure(self, mode=None): time.sleep(2) # Gives the instrument a moment to settle else: mode = self.mode - value = self.query('') + value = self.query("") return float(value) * UNITS2[mode] def get_status_word(self): @@ -275,7 +287,7 @@ def get_status_word(self): :return: String containing setting information of the instrument :rtype: `str` """ - self.sendcmd('U0DX') + self.sendcmd("U0DX") return self._file.read_raw() @staticmethod @@ -294,29 +306,47 @@ def parse_status_word(statusword): # pylint: disable=too-many-locals :return: A parsed version of the status word as a Python dictionary :rtype: `dict` """ - if statusword[:3] != b'195': - raise ValueError('Status word starts with wrong prefix, expected ' - '195, got {}'.format(statusword)) - - (trigger, function, input_range, eoi, buf, rate, srqmode, relative, - delay, multiplex, selftest, data_fmt, data_ctrl, filter_mode, - terminator) = struct.unpack('@4c2s3c2s5c2s', statusword[4:]) - - return {'trigger': Keithley195.TriggerMode(int(trigger)), - 'mode': Keithley195.Mode(int(function)), - 'range': int(input_range), - 'eoi': (eoi == b'1'), - 'buffer': buf, - 'rate': rate, - 'srqmode': srqmode, - 'relative': (relative == b'1'), - 'delay': delay, - 'multiplex': (multiplex == b'1'), - 'selftest': selftest, - 'dataformat': data_fmt, - 'datacontrol': data_ctrl, - 'filter': filter_mode, - 'terminator': terminator} + if statusword[:3] != b"195": + raise ValueError( + "Status word starts with wrong prefix, expected " + "195, got {}".format(statusword) + ) + + ( + trigger, + function, + input_range, + eoi, + buf, + rate, + srqmode, + relative, + delay, + multiplex, + selftest, + data_fmt, + data_ctrl, + filter_mode, + terminator, + ) = struct.unpack("@4c2s3c2s5c2s", statusword[4:]) + + return { + "trigger": Keithley195.TriggerMode(int(trigger)), + "mode": Keithley195.Mode(int(function)), + "range": int(input_range), + "eoi": (eoi == b"1"), + "buffer": buf, + "rate": rate, + "srqmode": srqmode, + "relative": (relative == b"1"), + "delay": delay, + "multiplex": (multiplex == b"1"), + "selftest": selftest, + "dataformat": data_fmt, + "datacontrol": data_ctrl, + "filter": filter_mode, + "terminator": terminator, + } def trigger(self): """ @@ -325,7 +355,7 @@ def trigger(self): Do note that this is different from the standard SCPI ``*TRG`` command (which is not supported by the 195 anyways). """ - self.sendcmd('X') + self.sendcmd("X") def auto_range(self): """ @@ -333,22 +363,23 @@ def auto_range(self): This is the same as calling ``Keithley195.input_range = 'auto'`` """ - self.input_range = 'auto' + self.input_range = "auto" + # UNITS ####################################################################### UNITS = { - 'DCV': u.volt, - 'ACV': u.volt, - 'ACA': u.amp, - 'DCA': u.amp, - 'OHM': u.ohm, + "DCV": u.volt, + "ACV": u.volt, + "ACA": u.amp, + "DCA": u.amp, + "OHM": u.ohm, } UNITS2 = { - Keithley195.Mode.voltage_dc: u.volt, - Keithley195.Mode.voltage_ac: u.volt, - Keithley195.Mode.current_dc: u.amp, - Keithley195.Mode.current_ac: u.amp, - Keithley195.Mode.resistance: u.ohm, + Keithley195.Mode.voltage_dc: u.volt, + Keithley195.Mode.voltage_ac: u.volt, + Keithley195.Mode.current_dc: u.amp, + Keithley195.Mode.current_ac: u.amp, + Keithley195.Mode.resistance: u.ohm, } diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index 9cc1d543f..2413d3759 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -51,7 +51,7 @@ def __init__(self, parent, idx): @property def mode(self): - return Keithley2182.Mode(self._parent.query('SENS:FUNC?')) + return Keithley2182.Mode(self._parent.query("SENS:FUNC?")) @mode.setter def mode(self, newval): @@ -96,8 +96,8 @@ def measure(self, mode=None): if mode is not None: # self.mode = mode raise NotImplementedError - self._parent.sendcmd('SENS:CHAN {}'.format(self._idx)) - value = float(self._parent.query('SENS:DATA:FRES?')) + self._parent.sendcmd("SENS:CHAN {}".format(self._idx)) + value = float(self._parent.query("SENS:DATA:FRES?")) unit = self._parent.units return u.Quantity(value, unit) @@ -107,6 +107,7 @@ class Mode(Enum): """ Enum containing valid measurement modes for the Keithley 2182 """ + voltage_dc = "VOLT" temperature = "TEMP" @@ -114,11 +115,12 @@ class TriggerMode(Enum): """ Enum containing valid trigger modes for the Keithley 2182 """ - immediate = 'IMM' - external = 'EXT' - bus = 'BUS' - timer = 'TIM' - manual = 'MAN' + + immediate = "IMM" + external = "EXT" + bus = "BUS" + timer = "TIM" + manual = "MAN" # PROPERTIES # @@ -166,9 +168,8 @@ def relative(self, newval): if self.relative: self.sendcmd("SENS:{}:CHAN1:REF:ACQ".format(mode.value)) else: - newval = ("ON" if newval is True else "OFF") - self.sendcmd( - "SENS:{}:CHAN1:REF:STAT {}".format(mode.value, newval)) + newval = "ON" if newval is True else "OFF" + self.sendcmd("SENS:{}:CHAN1:REF:STAT {}".format(mode.value, newval)) @property def input_range(self): @@ -237,8 +238,10 @@ def measure(self, mode=None): if mode is None: mode = self.channel[0].mode if not isinstance(mode, Keithley2182.Mode): - raise TypeError("Mode must be specified as a Keithley2182.Mode " - "value, got {} instead.".format(mode)) + raise TypeError( + "Mode must be specified as a Keithley2182.Mode " + "value, got {} instead.".format(mode) + ) value = float(self.query("MEAS:{}?".format(mode.value))) unit = self.units return value * unit diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index ba7473ed8..c71b27057 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -66,6 +66,7 @@ class TriggerMode(Enum): """ Enum containing valid trigger modes for the Keithley 485 """ + #: Continuously measures current, returns on talk continuous_ontalk = 0 #: Measures current once and returns on talk @@ -83,6 +84,7 @@ class SRQDataMask(Enum): """ Enum containing valid SRQ data masks for the Keithley 485 """ + #: Service request (SRQ) disabled srq_disabled = 0 #: Read overflow @@ -104,6 +106,7 @@ class SRQErrorMask(Enum): """ Enum containing valid SRQ error masks for the Keithley 485 """ + #: Service request (SRQ) disabled srq_disabled = 0 #: Illegal Device-Dependent Command Option (IDDCO) @@ -125,6 +128,7 @@ class Status(Enum): """ Enum containing valid status keys in the measurement string """ + #: Measurement normal normal = b"N" #: Measurement zero-check @@ -198,8 +202,10 @@ def input_range(self, newval): self.sendcmd("R0X") return else: - raise ValueError("Only `auto` is acceptable when specifying " - "the range as a string.") + raise ValueError( + "Only `auto` is acceptable when specifying " + "the range as a string." + ) if isinstance(newval, u.Quantity): newval = float(newval.magnitude) @@ -209,8 +215,10 @@ def input_range(self, newval): else: raise ValueError("Valid range settings are: {}".format(valid)) else: - raise TypeError("Range setting must be specified as a float, int, " - "or the string `auto`, got {}".format(type(newval))) + raise TypeError( + "Range setting must be specified as a float, int, " + "or the string `auto`, got {}".format(type(newval)) + ) self.sendcmd("R{}X".format(newval)) @property @@ -293,9 +301,11 @@ def trigger_mode(self, newval): if isinstance(newval, str): newval = Keithley485.TriggerMode[newval] if not isinstance(newval, Keithley485.TriggerMode): - raise TypeError("Drive must be specified as a " - "Keithley485.TriggerMode, got {} " - "instead.".format(newval)) + raise TypeError( + "Drive must be specified as a " + "Keithley485.TriggerMode, got {} " + "instead.".format(newval) + ) self.sendcmd("T{}X".format(newval.value)) # METHODS # @@ -354,21 +364,31 @@ def _parse_status_word(self, statusword): :rtype: `dict` """ if statusword[:3] != "485": - raise ValueError("Status word starts with wrong " - "prefix: {}".format(statusword)) - - (zerocheck, log, device_range, relative, eoi_mode, - trigger, datamask, errormask) = \ - unpack("@6c2s2s", bytes(statusword[3:], "utf-8")) - - valid_range = {b"0": "auto", - b"1": 2e-9, - b"2": 2e-8, - b"3": 2e-7, - b"4": 2e-6, - b"5": 2e-5, - b"6": 2e-4, - b"7": 2e-3} + raise ValueError( + "Status word starts with wrong " "prefix: {}".format(statusword) + ) + + ( + zerocheck, + log, + device_range, + relative, + eoi_mode, + trigger, + datamask, + errormask, + ) = unpack("@6c2s2s", bytes(statusword[3:], "utf-8")) + + valid_range = { + b"0": "auto", + b"1": 2e-9, + b"2": 2e-8, + b"3": 2e-7, + b"4": 2e-6, + b"5": 2e-5, + b"6": 2e-4, + b"7": 2e-3, + } try: device_range = valid_range[device_range] @@ -376,18 +396,19 @@ def _parse_status_word(self, statusword): datamask = self.SRQDataMask(int(datamask)).name errormask = self.SRQErrorMask(int(errormask)).name except: - raise RuntimeError("Cannot parse status " - "word: {}".format(statusword)) - - return {"zerocheck": zerocheck == b"1", - "log": log == b"1", - "range": device_range, - "relative": relative == b"1", - "eoi_mode": eoi_mode == b"0", - "trigger": trigger, - "datamask": datamask, - "errormask": errormask, - "terminator": self.terminator} + raise RuntimeError("Cannot parse status " "word: {}".format(statusword)) + + return { + "zerocheck": zerocheck == b"1", + "log": log == b"1", + "range": device_range, + "relative": relative == b"1", + "eoi_mode": eoi_mode == b"0", + "trigger": trigger, + "datamask": datamask, + "errormask": errormask, + "terminator": self.terminator, + } def measure(self): """ @@ -408,8 +429,9 @@ def _parse_measurement(self, measurement): :rtype: `~pint.Quantity` """ - (status, function, base, current) = \ - unpack("@1c2s1c10s", bytes(measurement, "utf-8")) + (status, function, base, current) = unpack( + "@1c2s1c10s", bytes(measurement, "utf-8") + ) try: status = self.Status(status) @@ -420,10 +442,16 @@ def _parse_measurement(self, measurement): raise ValueError("Instrument not in normal mode: {}".format(status.name)) if function != b"DC": - raise ValueError("Instrument not returning DC function: {}".format(function)) + raise ValueError( + "Instrument not returning DC function: {}".format(function) + ) try: - current = float(current) * u.amp if base == b"A" else 10 ** (float(current)) * u.amp + current = ( + float(current) * u.amp + if base == b"A" + else 10 ** (float(current)) * u.amp + ) except: raise Exception("Cannot parse measurement: {}".format(measurement)) diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index 82d9292cc..5e4e5961e 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -61,7 +61,7 @@ def __init__(self, filelike): Initialise the instrument and remove CRLF line termination """ super(Keithley580, self).__init__(filelike) - self.sendcmd('Y:X') # Removes the termination CRLF characters + self.sendcmd("Y:X") # Removes the termination CRLF characters # ENUMS # @@ -69,6 +69,7 @@ class Polarity(IntEnum): """ Enum containing valid polarity modes for the Keithley 580 """ + positive = 0 negative = 1 @@ -76,6 +77,7 @@ class Drive(IntEnum): """ Enum containing valid drive modes for the Keithley 580 """ + pulsed = 0 dc = 1 @@ -83,6 +85,7 @@ class TriggerMode(IntEnum): """ Enum containing valid trigger modes for the Keithley 580 """ + talk_continuous = 0 talk_one_shot = 1 get_continuous = 2 @@ -105,8 +108,8 @@ def polarity(self): :type: `Keithley580.Polarity` """ - value = self.parse_status_word(self.get_status_word())['polarity'] - if value == '+': + value = self.parse_status_word(self.get_status_word())["polarity"] + if value == "+": return Keithley580.Polarity.positive else: return Keithley580.Polarity.negative @@ -116,11 +119,13 @@ def polarity(self, newval): if isinstance(newval, str): newval = Keithley580.Polarity[newval] if not isinstance(newval, Keithley580.Polarity): - raise TypeError('Polarity must be specified as a ' - 'Keithley580.Polarity, got {} ' - 'instead.'.format(newval)) + raise TypeError( + "Polarity must be specified as a " + "Keithley580.Polarity, got {} " + "instead.".format(newval) + ) - self.sendcmd('P{}X'.format(newval.value)) + self.sendcmd("P{}X".format(newval.value)) @property def drive(self): @@ -135,7 +140,7 @@ def drive(self): :type: `Keithley580.Drive` """ - value = self.parse_status_word(self.get_status_word())['drive'] + value = self.parse_status_word(self.get_status_word())["drive"] return Keithley580.Drive[value] @drive.setter @@ -143,11 +148,13 @@ def drive(self, newval): if isinstance(newval, str): newval = Keithley580.Drive[newval] if not isinstance(newval, Keithley580.Drive): - raise TypeError('Drive must be specified as a ' - 'Keithley580.Drive, got {} ' - 'instead.'.format(newval)) + raise TypeError( + "Drive must be specified as a " + "Keithley580.Drive, got {} " + "instead.".format(newval) + ) - self.sendcmd('D{}X'.format(newval.value)) + self.sendcmd("D{}X".format(newval.value)) @property def dry_circuit_test(self): @@ -164,13 +171,13 @@ def dry_circuit_test(self): :type: `bool` """ - return self.parse_status_word(self.get_status_word())['drycircuit'] + return self.parse_status_word(self.get_status_word())["drycircuit"] @dry_circuit_test.setter def dry_circuit_test(self, newval): if not isinstance(newval, bool): - raise TypeError('DryCircuitTest mode must be a boolean.') - self.sendcmd('C{}X'.format(int(newval))) + raise TypeError("DryCircuitTest mode must be a boolean.") + self.sendcmd("C{}X".format(int(newval))) @property def operate(self): @@ -181,13 +188,13 @@ def operate(self): :type: `bool` """ - return self.parse_status_word(self.get_status_word())['operate'] + return self.parse_status_word(self.get_status_word())["operate"] @operate.setter def operate(self, newval): if not isinstance(newval, bool): - raise TypeError('Operate mode must be a boolean.') - self.sendcmd('O{}X'.format(int(newval))) + raise TypeError("Operate mode must be a boolean.") + self.sendcmd("O{}X".format(int(newval))) @property def relative(self): @@ -209,13 +216,13 @@ def relative(self): :type: `bool` """ - return self.parse_status_word(self.get_status_word())['relative'] + return self.parse_status_word(self.get_status_word())["relative"] @relative.setter def relative(self, newval): if not isinstance(newval, bool): - raise TypeError('Relative mode must be a boolean.') - self.sendcmd('Z{}X'.format(int(newval))) + raise TypeError("Relative mode must be a boolean.") + self.sendcmd("Z{}X".format(int(newval))) @property def trigger_mode(self): @@ -246,10 +253,12 @@ def trigger_mode(self, newval): if isinstance(newval, str): newval = Keithley580.TriggerMode[newval] if not isinstance(newval, Keithley580.TriggerMode): - raise TypeError('Drive must be specified as a ' - 'Keithley580.TriggerMode, got {} ' - 'instead.'.format(newval)) - self.sendcmd('T{}X'.format(newval.value)) + raise TypeError( + "Drive must be specified as a " + "Keithley580.TriggerMode, got {} " + "instead.".format(newval) + ) + self.sendcmd("T{}X".format(newval.value)) @property def input_range(self): @@ -259,7 +268,7 @@ def input_range(self): :type: `~pint.Quantity` or `str` """ - value = self.parse_status_word(self.get_status_word())['range'] + value = self.parse_status_word(self.get_status_word())["range"] if isinstance(value, str): # if range is 'auto' return value else: @@ -267,15 +276,17 @@ def input_range(self): @input_range.setter def input_range(self, newval): - valid = ('auto', 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) + valid = ("auto", 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) if isinstance(newval, str): newval = newval.lower() - if newval == 'auto': - self.sendcmd('R0X') + if newval == "auto": + self.sendcmd("R0X") return else: - raise ValueError('Only "auto" is acceptable when specifying ' - 'the input range as a string.') + raise ValueError( + 'Only "auto" is acceptable when specifying ' + "the input range as a string." + ) if isinstance(newval, u.Quantity): newval = float(newval.magnitude) @@ -283,11 +294,13 @@ def input_range(self, newval): if newval in valid: newval = valid.index(newval) else: - raise ValueError('Valid range settings are: {}'.format(valid)) + raise ValueError("Valid range settings are: {}".format(valid)) else: - raise TypeError('Range setting must be specified as a float, int, ' - 'or the string "auto", got {}'.format(type(newval))) - self.sendcmd('R{}X'.format(newval)) + raise TypeError( + "Range setting must be specified as a float, int, " + 'or the string "auto", got {}'.format(type(newval)) + ) + self.sendcmd("R{}X".format(newval)) # METHODS # @@ -298,7 +311,7 @@ def trigger(self): Do note that this is different from the standard SCPI ``*TRG`` command (which is not supported by the 580 anyways). """ - self.sendcmd('X') + self.sendcmd("X") def auto_range(self): """ @@ -307,7 +320,7 @@ def auto_range(self): This is the same as calling the `Keithley580.set_resistance_range` method and setting the parameter to "AUTO". """ - self.sendcmd('R0X') + self.sendcmd("R0X") def set_calibration_value(self, value): """ @@ -316,7 +329,7 @@ def set_calibration_value(self, value): :param value: Calibration value to write """ # self.write('V+n.nnnnE+nn') - raise NotImplementedError('setCalibrationValue not implemented') + raise NotImplementedError("setCalibrationValue not implemented") def store_calibration_constants(self): """ @@ -324,7 +337,7 @@ def store_calibration_constants(self): not currently implemented. """ # self.write('L0X') - raise NotImplementedError('storeCalibrationConstants not implemented') + raise NotImplementedError("storeCalibrationConstants not implemented") def get_status_word(self): """ @@ -335,16 +348,16 @@ def get_status_word(self): :rtype: `str` """ tries = 5 - statusword = '' - while statusword[:3] != b'580' and tries != 0: + statusword = "" + while statusword[:3] != b"580" and tries != 0: tries -= 1 - self.sendcmd('U0X') + self.sendcmd("U0X") time.sleep(1) - self.sendcmd('') + self.sendcmd("") statusword = self._file.read_raw() if tries == 0: - raise IOError('could not retrieve status word') + raise IOError("could not retrieve status word") return statusword[:-1] @@ -362,50 +375,63 @@ def parse_status_word(self, statusword): :rtype: `dict` """ - if statusword[:3] != b'580': - raise ValueError('Status word starts with wrong ' - 'prefix: {}'.format(statusword)) - - (drive, polarity, drycircuit, operate, rng, - relative, eoi, trigger, sqrondata, sqronerror, - linefreq) = struct.unpack('@8c2s2sc', statusword[3:16]) - - valid = {'drive': {b'0': 'pulsed', - b'1': 'dc'}, - 'polarity': {b'0': '+', - b'1': '-'}, - 'range': {b'0': 'auto', - b'1': 0.2, - b'2': 2, - b'3': 20, - b'4': 2e2, - b'5': 2e3, - b'6': 2e4, - b'7': 2e5}, - 'linefreq': {b'0': '60Hz', - b'1': '50Hz'}} + if statusword[:3] != b"580": + raise ValueError( + "Status word starts with wrong " "prefix: {}".format(statusword) + ) + + ( + drive, + polarity, + drycircuit, + operate, + rng, + relative, + eoi, + trigger, + sqrondata, + sqronerror, + linefreq, + ) = struct.unpack("@8c2s2sc", statusword[3:16]) + + valid = { + "drive": {b"0": "pulsed", b"1": "dc"}, + "polarity": {b"0": "+", b"1": "-"}, + "range": { + b"0": "auto", + b"1": 0.2, + b"2": 2, + b"3": 20, + b"4": 2e2, + b"5": 2e3, + b"6": 2e4, + b"7": 2e5, + }, + "linefreq": {b"0": "60Hz", b"1": "50Hz"}, + } try: - drive = valid['drive'][drive] - polarity = valid['polarity'][polarity] - rng = valid['range'][rng] - linefreq = valid['linefreq'][linefreq] + drive = valid["drive"][drive] + polarity = valid["polarity"][polarity] + rng = valid["range"][rng] + linefreq = valid["linefreq"][linefreq] except: - raise RuntimeError('Cannot parse status ' - 'word: {}'.format(statusword)) - - return {'drive': drive, - 'polarity': polarity, - 'drycircuit': (drycircuit == b'1'), - 'operate': (operate == b'1'), - 'range': rng, - 'relative': (relative == b'1'), - 'eoi': eoi, - 'trigger': (trigger == b'1'), - 'sqrondata': sqrondata, - 'sqronerror': sqronerror, - 'linefreq': linefreq, - 'terminator': self.terminator} + raise RuntimeError("Cannot parse status " "word: {}".format(statusword)) + + return { + "drive": drive, + "polarity": polarity, + "drycircuit": (drycircuit == b"1"), + "operate": (operate == b"1"), + "range": rng, + "relative": (relative == b"1"), + "eoi": eoi, + "trigger": (trigger == b"1"), + "sqrondata": sqrondata, + "sqronerror": sqronerror, + "linefreq": linefreq, + "terminator": self.terminator, + } def measure(self): """ @@ -417,8 +443,8 @@ def measure(self): :rtype: `~pint.Quantity` """ self.trigger() - self.sendcmd('') - return self.parse_measurement(self._file.read_raw()[:-1])['resistance'] + self.sendcmd("") + return self.parse_measurement(self._file.read_raw()[:-1])["resistance"] @staticmethod def parse_measurement(measurement): @@ -433,38 +459,42 @@ def parse_measurement(measurement): :rtype: `dict` """ - (status, polarity, drycircuit, drive, resistance) = \ - struct.unpack('@4c11s', measurement) - - valid = {'status': {b'S': 'standby', - b'N': 'normal', - b'O': 'overflow', - b'Z': 'relative'}, - 'polarity': {b'+': '+', - b'-': '-'}, - 'drycircuit': {b'N': False, - b'D': True}, - 'drive': {b'P': 'pulsed', - b'D': 'dc'}} + (status, polarity, drycircuit, drive, resistance) = struct.unpack( + "@4c11s", measurement + ) + + valid = { + "status": { + b"S": "standby", + b"N": "normal", + b"O": "overflow", + b"Z": "relative", + }, + "polarity": {b"+": "+", b"-": "-"}, + "drycircuit": {b"N": False, b"D": True}, + "drive": {b"P": "pulsed", b"D": "dc"}, + } try: - status = valid['status'][status] - polarity = valid['polarity'][polarity] - drycircuit = valid['drycircuit'][drycircuit] - drive = valid['drive'][drive] + status = valid["status"][status] + polarity = valid["polarity"][polarity] + drycircuit = valid["drycircuit"][drycircuit] + drive = valid["drive"][drive] resistance = float(resistance) * u.ohm except: - raise Exception('Cannot parse measurement: {}'.format(measurement)) + raise Exception("Cannot parse measurement: {}".format(measurement)) - return {'status': status, - 'polarity': polarity, - 'drycircuit': drycircuit, - 'drive': drive, - 'resistance': resistance} + return { + "status": status, + "polarity": polarity, + "drycircuit": drycircuit, + "drive": drive, + "resistance": resistance, + } # COMMUNICATOR METHODS # def sendcmd(self, cmd): - super(Keithley580, self).sendcmd(cmd + ':') + super(Keithley580, self).sendcmd(cmd + ":") def query(self, cmd, size=-1): - return super(Keithley580, self).query(cmd + ':', size)[:-1] + return super(Keithley580, self).query(cmd + ":", size)[:-1] diff --git a/instruments/keithley/keithley6220.py b/instruments/keithley/keithley6220.py index ee5f68931..8f633091f 100644 --- a/instruments/keithley/keithley6220.py +++ b/instruments/keithley/keithley6220.py @@ -49,20 +49,22 @@ def channel(self): >>> ccs.channel[0].current = 0.01 >>> ccs.current = 0.01 """ - return self, + return (self,) @property def voltage(self): """ This property is not supported by the Keithley 6220. """ - raise NotImplementedError("The Keithley 6220 does not support voltage " - "settings.") + raise NotImplementedError( + "The Keithley 6220 does not support voltage " "settings." + ) @voltage.setter def voltage(self, newval): - raise NotImplementedError("The Keithley 6220 does not support voltage " - "settings.") + raise NotImplementedError( + "The Keithley 6220 does not support voltage " "settings." + ) current, current_min, current_max = bounded_unitful_property( "SOUR:CURR", @@ -74,7 +76,7 @@ def voltage(self, newval): :units: As specified, or assumed to be :math:`\\text{A}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) # METHODS # diff --git a/instruments/keithley/keithley6514.py b/instruments/keithley/keithley6514.py index 911be3067..866621d6b 100644 --- a/instruments/keithley/keithley6514.py +++ b/instruments/keithley/keithley6514.py @@ -35,38 +35,52 @@ class Mode(Enum): """ Enum containing valid measurement modes for the Keithley 6514 """ - voltage = 'VOLT:DC' - current = 'CURR:DC' - resistance = 'RES' - charge = 'CHAR' + + voltage = "VOLT:DC" + current = "CURR:DC" + resistance = "RES" + charge = "CHAR" class TriggerMode(Enum): """ Enum containing valid trigger modes for the Keithley 6514 """ - immediate = 'IMM' - tlink = 'TLINK' + + immediate = "IMM" + tlink = "TLINK" class ArmSource(Enum): """ Enum containing valid trigger arming sources for the Keithley 6514 """ - immediate = 'IMM' - timer = 'TIM' - bus = 'BUS' - tlink = 'TLIN' - stest = 'STES' - pstest = 'PST' - nstest = 'NST' - manual = 'MAN' + + immediate = "IMM" + timer = "TIM" + bus = "BUS" + tlink = "TLIN" + stest = "STES" + pstest = "PST" + nstest = "NST" + manual = "MAN" class ValidRange(Enum): """ Enum containing valid measurement ranges for the Keithley 6514 """ + voltage = (2, 20, 200) - current = (20e-12, 200e-12, 2e-9, 20e-9, - 200e-9, 2e-6, 20e-6, 200e-6, 2e-3, 20e-3) + current = ( + 20e-12, + 200e-12, + 2e-9, + 20e-9, + 200e-9, + 2e-6, + 20e-6, + 200e-6, + 2e-3, + 20e-3, + ) resistance = (2e3, 20e3, 200e3, 2e6, 20e6, 200e6, 2e9, 20e9, 200e9) charge = (20e-9, 200e-9, 2e-6, 20e-6) @@ -76,7 +90,7 @@ class ValidRange(Enum): Mode.voltage: u.volt, Mode.current: u.amp, Mode.resistance: u.ohm, - Mode.charge: u.coulomb + Mode.charge: u.coulomb, } # PRIVATE METHODS # @@ -91,11 +105,11 @@ def _valid_range(self, mode): elif mode == self.Mode.charge: return self.ValidRange.charge else: - raise ValueError('Invalid mode.') + raise ValueError("Invalid mode.") def _parse_measurement(self, ascii): # TODO: don't assume ASCII data format # pylint: disable=fixme - vals = list(map(float, ascii.split(','))) + vals = list(map(float, ascii.split(","))) reading = vals[0] * self.unit timestamp = vals[1] status = vals[2] @@ -105,48 +119,48 @@ def _parse_measurement(self, ascii): # The mode values have quotes around them for some annoying reason. mode = enum_property( - 'FUNCTION', + "FUNCTION", Mode, input_decoration=lambda val: val[1:-1], # output_decoration=lambda val: '"{}"'.format(val), set_fmt='{} "{}"', doc=""" Gets/sets the measurement mode of the Keithley 6514. - """ + """, ) trigger_mode = enum_property( - 'TRIGGER:SOURCE', + "TRIGGER:SOURCE", TriggerMode, doc=""" Gets/sets the trigger mode of the Keithley 6514. - """ + """, ) arm_source = enum_property( - 'ARM:SOURCE', + "ARM:SOURCE", ArmSource, doc=""" Gets/sets the arm source of the Keithley 6514. - """ + """, ) zero_check = bool_property( - 'SYST:ZCH', - inst_true='ON', - inst_false='OFF', + "SYST:ZCH", + inst_true="ON", + inst_false="OFF", doc=""" Gets/sets the zero checking status of the Keithley 6514. - """ + """, ) zero_correct = bool_property( - 'SYST:ZCOR', - inst_true='ON', - inst_false='OFF', + "SYST:ZCOR", + inst_true="ON", + inst_false="OFF", doc=""" Gets/sets the zero correcting status of the Keithley 6514. - """ + """, ) @property @@ -161,14 +175,13 @@ def auto_range(self): :type: `bool` """ # pylint: disable=no-member - out = self.query('{}:RANGE:AUTO?'.format(self.mode.value)) - return True if out == '1' else False + out = self.query("{}:RANGE:AUTO?".format(self.mode.value)) + return True if out == "1" else False @auto_range.setter def auto_range(self, newval): # pylint: disable=no-member - self.sendcmd('{}:RANGE:AUTO {}'.format( - self.mode.value, '1' if newval else '0')) + self.sendcmd("{}:RANGE:AUTO {}".format(self.mode.value, "1" if newval else "0")) @property def input_range(self): @@ -179,7 +192,7 @@ def input_range(self): """ # pylint: disable=no-member mode = self.mode - out = self.query('{}:RANGE:UPPER?'.format(mode.value)) + out = self.query("{}:RANGE:UPPER?".format(mode.value)) return float(out) * self._MODE_UNITS[mode] @input_range.setter @@ -188,9 +201,8 @@ def input_range(self, newval): mode = self.mode val = newval.to(self._MODE_UNITS[mode]).magnitude if val not in self._valid_range(mode).value: - raise ValueError( - 'Unexpected range limit for currently selected mode.') - self.sendcmd('{}:RANGE:UPPER {:e}'.format(mode.value, val)) + raise ValueError("Unexpected range limit for currently selected mode.") + self.sendcmd("{}:RANGE:UPPER {:e}".format(mode.value, val)) # METHODS ## @@ -207,7 +219,7 @@ def auto_config(self, mode): - Disable buffer operation - Enable autozero """ - self.sendcmd('CONF:{}'.format(mode.value)) + self.sendcmd("CONF:{}".format(mode.value)) def fetch(self): """ @@ -215,7 +227,7 @@ def fetch(self): (So does not issue a trigger) Returns a tuple of the form (reading, timestamp) """ - raw = self.query('FETC?') + raw = self.query("FETC?") reading, timestamp, _ = self._parse_measurement(raw) return reading, timestamp @@ -224,6 +236,6 @@ def read_measurements(self): Trigger and acquire readings using the current mode. Returns a tuple of the form (reading, timestamp) """ - raw = self.query('READ?') + raw = self.query("READ?") reading, timestamp, _ = self._parse_measurement(raw) return reading, timestamp diff --git a/instruments/lakeshore/lakeshore340.py b/instruments/lakeshore/lakeshore340.py index a5c6200c4..d8e093330 100644 --- a/instruments/lakeshore/lakeshore340.py +++ b/instruments/lakeshore/lakeshore340.py @@ -52,7 +52,7 @@ def temperature(self): :units: Kelvin :type: `~pint.Quantity` """ - value = self._parent.query('KRDG?{}'.format(self._idx)) + value = self._parent.query("KRDG?{}".format(self._idx)) return u.Quantity(float(value), u.kelvin) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore370.py b/instruments/lakeshore/lakeshore370.py index 8871201ad..a287c4241 100644 --- a/instruments/lakeshore/lakeshore370.py +++ b/instruments/lakeshore/lakeshore370.py @@ -29,7 +29,7 @@ class Lakeshore370(SCPIInstrument): def __init__(self, filelike): super(Lakeshore370, self).__init__(filelike) # Disable termination characters and enable EOI - self.sendcmd('IEEE 3,0') + self.sendcmd("IEEE 3,0") # INNER CLASSES ## @@ -56,7 +56,7 @@ def resistance(self): :units: Ohm :rtype: `~pint.Quantity` """ - value = self._parent.query('RDGR? {}'.format(self._idx)) + value = self._parent.query("RDGR? {}".format(self._idx)) return u.Quantity(float(value), u.ohm) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index d2f97c59d..444136179 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -14,22 +14,12 @@ # CONSTANTS ################################################################### -LAKESHORE_FIELD_UNITS = { - 1: u.gauss, - 2: u.tesla, - 3: u.oersted, - 4: u.amp / u.meter -} - -LAKESHORE_TEMP_UNITS = { - 1: u.celsius, - 2: u.kelvin -} - -LAKESHORE_FIELD_UNITS_INV = dict((v, k) for k, v in - LAKESHORE_FIELD_UNITS.items()) -LAKESHORE_TEMP_UNITS_INV = dict((v, k) for k, v in - LAKESHORE_TEMP_UNITS.items()) +LAKESHORE_FIELD_UNITS = {1: u.gauss, 2: u.tesla, 3: u.oersted, 4: u.amp / u.meter} + +LAKESHORE_TEMP_UNITS = {1: u.celsius, 2: u.kelvin} + +LAKESHORE_FIELD_UNITS_INV = dict((v, k) for k, v in LAKESHORE_FIELD_UNITS.items()) +LAKESHORE_TEMP_UNITS_INV = dict((v, k) for k, v in LAKESHORE_TEMP_UNITS.items()) # CLASSES ##################################################################### @@ -55,6 +45,7 @@ class Mode(IntEnum): """ Enum containing valid measurement modes for the Lakeshore 475 """ + dc = 1 rms = 2 peak = 3 @@ -63,6 +54,7 @@ class Filter(IntEnum): """ Enum containing valid filter modes for the Lakeshore 475 """ + wide = 1 narrow = 2 lowpass = 3 @@ -71,6 +63,7 @@ class PeakMode(IntEnum): """ Enum containing valid peak modes for the Lakeshore 475 """ + periodic = 1 pulse = 2 @@ -78,6 +71,7 @@ class PeakDisplay(IntEnum): """ Enum containing valid peak displays for the Lakeshore 475 """ + positive = 1 negative = 2 both = 3 @@ -91,7 +85,7 @@ def field(self): :type: `~pint.Quantity` """ - return float(self.query('RDGFIELD?')) * self.field_units + return float(self.query("RDGFIELD?")) * self.field_units @property def field_units(self): @@ -102,7 +96,7 @@ def field_units(self): :type: `~pint.Unit` """ - value = int(self.query('UNIT?')) + value = int(self.query("UNIT?")) return LAKESHORE_FIELD_UNITS[value] @field_units.setter @@ -111,9 +105,9 @@ def field_units(self, newval): if newval in LAKESHORE_FIELD_UNITS_INV: self.sendcmd(f"UNIT {LAKESHORE_FIELD_UNITS_INV[newval]}") else: - raise ValueError('Not an acceptable Python quantities object') + raise ValueError("Not an acceptable Python quantities object") else: - raise TypeError('Field units must be a Python quantity') + raise TypeError("Field units must be a Python quantity") @property def temp_units(self): @@ -124,7 +118,7 @@ def temp_units(self): :type: `~pint.Unit` """ - value = int(self.query('TUNIT?')) + value = int(self.query("TUNIT?")) return LAKESHORE_TEMP_UNITS[value] @temp_units.setter @@ -133,9 +127,9 @@ def temp_units(self, newval): if newval in LAKESHORE_TEMP_UNITS_INV: self.sendcmd(f"TUNIT {LAKESHORE_TEMP_UNITS_INV[newval]}") else: - raise TypeError('Not an acceptable Python quantities object') + raise TypeError("Not an acceptable Python quantities object") else: - raise TypeError('Temperature units must be a Python quantity') + raise TypeError("Temperature units must be a Python quantity") @property def field_setpoint(self): @@ -146,7 +140,7 @@ def field_setpoint(self): of units Gauss. :type: `~pint.Quantity` with units Gauss """ - value = self.query('CSETP?').strip() + value = self.query("CSETP?").strip() units = self.field_units return float(value) * units @@ -156,11 +150,13 @@ def field_setpoint(self, newval): newval = assume_units(newval, u.gauss) if newval.units != expected_units: - raise ValueError(f"Field setpoint must be specified in the same units " - f"that the field units are currently set to. Attempts units of " - f"{newval.units}, currently expecting {expected_units}.") + raise ValueError( + f"Field setpoint must be specified in the same units " + f"that the field units are currently set to. Attempts units of " + f"{newval.units}, currently expecting {expected_units}." + ) - self.sendcmd('CSETP {}'.format(newval.magnitude)) + self.sendcmd("CSETP {}".format(newval.magnitude)) @property def field_control_params(self): @@ -170,7 +166,7 @@ def field_control_params(self): :type: `tuple` of 2 `float` and 2 `~pint.Quantity` """ - params = self.query('CPARAM?').strip().split(',') + params = self.query("CPARAM?").strip().split(",") params = [float(x) for x in params] params[2] = params[2] * self.field_units / u.minute params[3] = params[3] * u.volt / u.minute @@ -179,21 +175,24 @@ def field_control_params(self): @field_control_params.setter def field_control_params(self, newval): if not isinstance(newval, tuple): - raise TypeError('Field control parameters must be specified as ' - ' a tuple') + raise TypeError("Field control parameters must be specified as " " a tuple") p, i, ramp_rate, control_slope_lim = newval expected_units = self.field_units / u.minute ramp_rate = assume_units(ramp_rate, expected_units) if ramp_rate.units != expected_units: - raise ValueError(f"Field control params ramp rate must be specified in the same units " - f"that the field units are currently set to, per minute. Attempts units of " - f"{ramp_rate.units}, currently expecting {expected_units}.") + raise ValueError( + f"Field control params ramp rate must be specified in the same units " + f"that the field units are currently set to, per minute. Attempts units of " + f"{ramp_rate.units}, currently expecting {expected_units}." + ) ramp_rate = float(ramp_rate.magnitude) unit = u.volt / u.minute - control_slope_lim = float(assume_units(control_slope_lim, unit).to(unit).magnitude) + control_slope_lim = float( + assume_units(control_slope_lim, unit).to(unit).magnitude + ) self.sendcmd(f"CPARAM {p},{i},{ramp_rate},{control_slope_lim}") @@ -277,14 +276,15 @@ def control_slope_limit(self, newval): field control. :type: `bool` - """ + """, ) # METHODS ## # pylint: disable=too-many-arguments - def change_measurement_mode(self, mode, resolution, filter_type, - peak_mode, peak_disp): + def change_measurement_mode( + self, mode, resolution, filter_type, peak_mode, peak_disp + ): """ Change the measurement mode of the Gaussmeter. @@ -308,23 +308,31 @@ def change_measurement_mode(self, mode, resolution, filter_type, :type peak_disp: `Lakeshore475.PeakDisplay` """ if not isinstance(mode, Lakeshore475.Mode): - raise TypeError("Mode setting must be a " - "`Lakeshore475.Mode` value, got {} " - "instead.".format(type(mode))) + raise TypeError( + "Mode setting must be a " + "`Lakeshore475.Mode` value, got {} " + "instead.".format(type(mode)) + ) if not isinstance(resolution, int): raise TypeError('Parameter "resolution" must be an integer.') if not isinstance(filter_type, Lakeshore475.Filter): - raise TypeError("Filter type setting must be a " - "`Lakeshore475.Filter` value, got {} " - "instead.".format(type(filter_type))) + raise TypeError( + "Filter type setting must be a " + "`Lakeshore475.Filter` value, got {} " + "instead.".format(type(filter_type)) + ) if not isinstance(peak_mode, Lakeshore475.PeakMode): - raise TypeError("Peak measurement type setting must be a " - "`Lakeshore475.PeakMode` value, got {} " - "instead.".format(type(peak_mode))) + raise TypeError( + "Peak measurement type setting must be a " + "`Lakeshore475.PeakMode` value, got {} " + "instead.".format(type(peak_mode)) + ) if not isinstance(peak_disp, Lakeshore475.PeakDisplay): - raise TypeError("Peak display type setting must be a " - "`Lakeshore475.PeakDisplay` value, got {} " - "instead.".format(type(peak_disp))) + raise TypeError( + "Peak display type setting must be a " + "`Lakeshore475.PeakDisplay` value, got {} " + "instead.".format(type(peak_disp)) + ) mode = mode.value filter_type = filter_type.value @@ -335,12 +343,10 @@ def change_measurement_mode(self, mode, resolution, filter_type, if resolution in range(3, 6): resolution -= 2 else: - raise ValueError('Only 3,4,5 are valid resolutions.') - - self.sendcmd('RDGMODE {},{},{},{},{}'.format( - mode, - resolution, - filter_type, - peak_mode, - peak_disp - )) + raise ValueError("Only 3,4,5 are valid resolutions.") + + self.sendcmd( + "RDGMODE {},{},{},{},{}".format( + mode, resolution, filter_type, peak_mode, peak_disp + ) + ) diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py index 50673932c..634342b75 100644 --- a/instruments/minghe/mhs5200a.py +++ b/instruments/minghe/mhs5200a.py @@ -25,6 +25,7 @@ class MHS5200(FunctionGenerator): communications protocol: https://github.com/wd5gnr/mhs5200a/blob/master/MHS5200AProtocol.pdf """ + def __init__(self, filelike): super(MHS5200, self).__init__(filelike) self._channel_count = 2 @@ -42,12 +43,10 @@ class Channel(FunctionGenerator.Channel): """ Class representing a channel on the MHS52000. """ + # pylint: disable=protected-access - __CHANNEL_NAMES = { - 1: '1', - 2: '2' - } + __CHANNEL_NAMES = {1: "1", 2: "2"} def __init__(self, mhs, idx): self._mhs = mhs @@ -61,11 +60,13 @@ def __init__(self, mhs, idx): def _get_amplitude_(self): query = ":r{0}a".format(self._chan) response = self._mhs.query(query) - return float(response.replace(query, ""))/100.0, self._mhs.VoltageMode.rms + return float(response.replace(query, "")) / 100.0, self._mhs.VoltageMode.rms def _set_amplitude_(self, magnitude, units): - if units == self._mhs.VoltageMode.peak_to_peak or \ - units == self._mhs.VoltageMode.rms: + if ( + units == self._mhs.VoltageMode.peak_to_peak + or units == self._mhs.VoltageMode.rms + ): magnitude = assume_units(magnitude, "V").to(u.V).magnitude elif units == self._mhs.VoltageMode.dBm: raise NotImplementedError("Decibel units are not supported.") @@ -83,12 +84,12 @@ def duty_cycle(self): """ query = ":r{0}d".format(self._chan) response = self._mhs.query(query) - duty = float(response.replace(query, ""))/10.0 + duty = float(response.replace(query, "")) / 10.0 return duty @duty_cycle.setter def duty_cycle(self, new_val): - query = ":s{0}d{1}".format(self._chan, int(100.0*new_val)) + query = ":s{0}d{1}".format(self._chan, int(100.0 * new_val)) self._mhs.sendcmd(query) @property @@ -99,8 +100,7 @@ def enable(self): :type: `bool` """ query = ":r{0}b".format(self._chan) - return int(self._mhs.query(query).replace(query, ""). - replace("\r", "")) + return int(self._mhs.query(query).replace(query, "").replace("\r", "")) @enable.setter def enable(self, newval): @@ -118,13 +118,12 @@ def frequency(self): """ query = ":r{0}f".format(self._chan) response = self._mhs.query(query) - freq = float(response.replace(query, ""))*u.Hz - return freq/100.0 + freq = float(response.replace(query, "")) * u.Hz + return freq / 100.0 @frequency.setter def frequency(self, new_val): - new_val = assume_units(new_val, u.Hz).to(u.Hz).\ - magnitude*100.0 + new_val = assume_units(new_val, u.Hz).to(u.Hz).magnitude * 100.0 query = ":s{0}f{1}".format(self._chan, int(new_val)) self._mhs.sendcmd(query) @@ -140,11 +139,11 @@ def offset(self): # need to convert query = ":r{0}o".format(self._chan) response = self._mhs.query(query) - return int(response.replace(query, ""))/100.0-1.20 + return int(response.replace(query, "")) / 100.0 - 1.20 @offset.setter def offset(self, new_val): - new_val = int(new_val*100)+120 + new_val = int(new_val * 100) + 120 query = ":s{0}o{1}".format(self._chan, new_val) self._mhs.sendcmd(query) @@ -160,7 +159,7 @@ def phase(self): # need to convert query = ":r{0}p".format(self._chan) response = self._mhs.query(query) - return int(response.replace(query, ""))*u.deg + return int(response.replace(query, "")) * u.deg @phase.setter def phase(self, new_val): @@ -181,14 +180,14 @@ def function(self): @function.setter def function(self, new_val): - query = ":s{0}w{1}".format(self._chan, - self._mhs.Function(new_val).value) + query = ":s{0}w{1}".format(self._chan, self._mhs.Function(new_val).value) self._mhs.sendcmd(query) class Function(Enum): """ Enum containing valid wave modes for """ + sine = 0 square = 1 triangular = 2 diff --git a/instruments/named_struct.py b/instruments/named_struct.py index 61a85ca0b..f10698031 100644 --- a/instruments/named_struct.py +++ b/instruments/named_struct.py @@ -43,7 +43,7 @@ class Field: __n_fields_created = 0 _field_birthday = None - _fmt = '' + _fmt = "" _name = None _owner_type = object @@ -63,7 +63,7 @@ def __init__(self, fmt, strip_null=False): raise TypeError("Field is specified with negative length.") def is_significant(self): - return not self._fmt.endswith('x') + return not self._fmt.endswith("x") @property def fmt_char(self): @@ -86,32 +86,30 @@ def __repr__(self): self._name, self._owner_type, self._fmt ) - return "".format( - self._fmt - ) + return "".format(self._fmt) def __str__(self): n, fmt_char = len(self), self.fmt_char c_type = { - 'x': 'char', - 'c': 'char', - 'b': 'char', - 'B': 'unsigned char', - '?': 'bool', - 'h': 'short', - 'H': 'unsigned short', - 'i': 'int', - 'I': 'unsigned int', - 'l': 'long', - 'L': 'unsigned long', - 'q': 'long long', - 'Q': 'unsigned long long', - 'f': 'float', - 'd': 'double', + "x": "char", + "c": "char", + "b": "char", + "B": "unsigned char", + "?": "bool", + "h": "short", + "H": "unsigned short", + "i": "int", + "I": "unsigned int", + "l": "long", + "L": "unsigned long", + "q": "long long", + "Q": "unsigned long long", + "f": "float", + "d": "double", # NB: no [], since that will be implied by n. - 's': 'char', - 'p': 'char', - 'P': 'void *' + "s": "char", + "p": "char", + "P": "void *", }[fmt_char] if n: @@ -146,10 +144,10 @@ class StringField(Field): """ _strip_null = False - _encoding = 'ascii' + _encoding = "ascii" - def __init__(self, length, encoding='ascii', strip_null=False): - super(StringField, self).__init__('{}s'.format(length)) + def __init__(self, length, encoding="ascii", strip_null=False): + super(StringField, self).__init__("{}s".format(length)) self._strip_null = strip_null self._encoding = encoding @@ -157,7 +155,7 @@ def __set__(self, obj, value): if isinstance(value, bytes): value = value.decode(self._encoding) if self._strip_null: - value = value.rstrip('\x00') + value = value.rstrip("\x00") value = value.encode(self._encoding) super(StringField, self).__set__(obj, value) @@ -175,13 +173,14 @@ class Padding(Field): """ def __init__(self, n_bytes=1): - super(Padding, self).__init__('{}x'.format(n_bytes)) + super(Padding, self).__init__("{}x".format(n_bytes)) class HasFields(type): """ Metaclass used for NamedStruct """ + def __new__(mcs, name, bases, attrs): # Since this is a metaclass, the __new__ method observes # creation of new *classes* and not new instances. @@ -192,17 +191,19 @@ def __new__(mcs, name, bases, attrs): # We now sort the fields by their birthdays and store them in an # ordered dict for easier look up later. - cls._fields = OrderedDict([ - (field_name, field) - for field_name, field in sorted( - [ - (field_name, field) - for field_name, field in attrs.items() - if isinstance(field, Field) - ], - key=lambda item: item[1]._field_birthday - ) - ]) + cls._fields = OrderedDict( + [ + (field_name, field) + for field_name, field in sorted( + [ + (field_name, field) + for field_name, field in attrs.items() + if isinstance(field, Field) + ], + key=lambda item: item[1]._field_birthday, + ) + ] + ) # Assign names and owner types to each field so that they can follow # the descriptor protocol. @@ -214,9 +215,7 @@ def __new__(mcs, name, bases, attrs): # that defines how to pack/unpack the new type. cls._struct = struct.Struct( # TODO: support alignment char at start. - " ".join([ - field._fmt for field in cls._fields.values() - ]) + " ".join([field._fmt for field in cls._fields.values()]) ) return cls @@ -253,12 +252,12 @@ class Foo(NamedStruct): def __init__(self, **kwargs): super(NamedStruct, self).__init__() - self._values = OrderedDict([ - ( - field._name, None - ) - for field in filter(Field.is_significant, self._fields.values()) - ]) + self._values = OrderedDict( + [ + (field._name, None) + for field in filter(Field.is_significant, self._fields.values()) + ] + ) for field_name, value in kwargs.items(): setattr(self, field_name, value) @@ -268,11 +267,14 @@ def _to_seq(self): @classmethod def _from_seq(cls, new_values): - return cls(**{ - field._name: new_value - for field, new_value in - zip(list(filter(Field.is_significant, cls._fields.values())), new_values) - }) + return cls( + **{ + field._name: new_value + for field, new_value in zip( + list(filter(Field.is_significant, cls._fields.values())), new_values + ) + } + ) def pack(self): """ @@ -307,15 +309,17 @@ def __hash__(self): def __str__(self): return "{name} {{\n{fields}\n}}".format( name=type(self).__name__, - fields="\n".join([ - " {field}{value};".format( - field=field, - value=( - " = {}".format(repr(self._values[field._name])) - if field.is_significant() - else "" + fields="\n".join( + [ + " {field}{value};".format( + field=field, + value=( + " = {}".format(repr(self._values[field._name])) + if field.is_significant() + else "" + ), ) - ) - for field in self._fields.values() - ]) + for field in self._fields.values() + ] + ), ) diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index 45e751b55..b61b40db1 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -7,8 +7,6 @@ from .agilis import _Axis, AGUC2 from .errors import NewportError -from .newportesp301 import ( - NewportESP301, NewportESP301Axis, NewportESP301HomeSearchMode -) +from .newportesp301 import NewportESP301, NewportESP301Axis, NewportESP301HomeSearchMode from .newport_pmc8742 import PicoMotorController8742 diff --git a/instruments/newport/agilis.py b/instruments/newport/agilis.py index 900563caf..77b732974 100644 --- a/instruments/newport/agilis.py +++ b/instruments/newport/agilis.py @@ -69,13 +69,11 @@ def axis_status(self): """ Returns the status of the current axis. """ - resp = self._cont.ag_query("{} TS".format( - int(self._ax) - )) - if resp.find('TS') == -1: + resp = self._cont.ag_query("{} TS".format(int(self._ax))) + if resp.find("TS") == -1: return "Status code query failed." - resp = int(resp.replace(str(int(self._ax)) + 'TS', '')) + resp = int(resp.replace(str(int(self._ax)) + "TS", "")) status_message = agilis_status_message(resp) return status_message @@ -100,22 +98,16 @@ def jog(self): :return: Jog motion set :rtype: `int` """ - resp = self._cont.ag_query("{} JA?".format( - int(self._ax) - )) + resp = self._cont.ag_query("{} JA?".format(int(self._ax))) return int(resp.split("JA")[1]) @jog.setter def jog(self, mode): mode = int(mode) if mode < -4 or mode > 4: - raise ValueError("Jog mode out of range. Must be between -4 and " - "4.") + raise ValueError("Jog mode out of range. Must be between -4 and " "4.") - self._cont.ag_sendcmd("{} JA {}".format( - int(self._ax), - mode - )) + self._cont.ag_sendcmd("{} JA {}".format(int(self._ax), mode)) @property def number_of_steps(self): @@ -137,9 +129,7 @@ def number_of_steps(self): :return: Number of steps :rtype: int """ - resp = self._cont.ag_query("{} TP".format( - int(self._ax) - )) + resp = self._cont.ag_query("{} TP".format(int(self._ax))) return int(resp.split("TP")[1]) @property @@ -151,22 +141,19 @@ def move_relative(self): If queried, command returns the current target position. At least this is the expected behaviour, never worked with the rotation stage. """ - resp = self._cont.ag_query("{} PR?".format( - int(self._ax) - )) + resp = self._cont.ag_query("{} PR?".format(int(self._ax))) return int(resp.split("PR")[1]) @move_relative.setter def move_relative(self, steps): steps = int(steps) if steps < -2147483648 or steps > 2147483647: - raise ValueError("Number of steps are out of range. They must be " - "between -2,147,483,648 and 2,147,483,647") + raise ValueError( + "Number of steps are out of range. They must be " + "between -2,147,483,648 and 2,147,483,647" + ) - self._cont.ag_sendcmd("{} PR {}".format( - int(self._ax), - steps - )) + self._cont.ag_sendcmd("{} PR {}".format(int(self._ax), steps)) @property def move_to_limit(self): @@ -183,22 +170,16 @@ def move_to_limit(self): Returns the distance of the current position to the limit in 1/1000th of the total travel. """ - resp = self._cont.ag_query("{} MA?".format( - int(self._ax) - )) + resp = self._cont.ag_query("{} MA?".format(int(self._ax))) return int(resp.split("MA")[1]) @move_to_limit.setter def move_to_limit(self, mode): mode = int(mode) if mode < -4 or mode > 4: - raise ValueError("Jog mode out of range. Must be between -4 and " - "4.") + raise ValueError("Jog mode out of range. Must be between -4 and " "4.") - self._cont.ag_sendcmd("{} MA {}".format( - int(self._ax), - mode - )) + self._cont.ag_sendcmd("{} MA {}".format(int(self._ax), mode)) @property def step_amplitude(self): @@ -217,12 +198,8 @@ def step_amplitude(self): response. :rtype: (`int`, `int`) """ - resp_neg = self._cont.ag_query("{} SU-?".format( - int(self._ax) - )) - resp_pos = self._cont.ag_query("{} SU+?".format( - int(self._ax) - )) + resp_neg = self._cont.ag_query("{} SU-?".format(int(self._ax))) + resp_pos = self._cont.ag_query("{} SU+?".format(int(self._ax))) return int(resp_neg.split("SU")[1]), int(resp_pos.split("SU")[1]) @step_amplitude.setter @@ -234,15 +211,14 @@ def step_amplitude(self, nns): for nn in nns: nn = int(nn) if nn < -50 or nn > 50 or nn == 0: - raise ValueError("Step amplitude {} outside the valid range. " - "It must be between -50 and -1 or between " - "1 and 50.".format(nn)) + raise ValueError( + "Step amplitude {} outside the valid range. " + "It must be between -50 and -1 or between " + "1 and 50.".format(nn) + ) for nn in nns: - self._cont.ag_sendcmd("{} SU {}".format( - int(self._ax), - int(nn) - )) + self._cont.ag_sendcmd("{} SU {}".format(int(self._ax), int(nn))) @property def step_delay(self): @@ -258,22 +234,18 @@ def step_delay(self): :return: Step delay :rtype: `int` """ - resp = self._cont.ag_query("{} DL?".format( - int(self._ax) - )) + resp = self._cont.ag_query("{} DL?".format(int(self._ax))) return int(resp.split("DL")[1]) @step_delay.setter def step_delay(self, nn): nn = int(nn) if nn < 0 or nn > 200000: - raise ValueError("Step delay is out of range. It must be between " - "0 and 200000.") + raise ValueError( + "Step delay is out of range. It must be between " "0 and 200000." + ) - self._cont.ag_sendcmd("{} DL {}".format( - int(self._ax), - nn - )) + self._cont.ag_sendcmd("{} DL {}".format(int(self._ax), nn)) # MODES # @@ -298,31 +270,31 @@ def am_i_still(self, max_retries=5): status = self.axis_status if status == agilis_status_message(0): return True - elif status == agilis_status_message(1) or \ - status == agilis_status_message(2) or \ - status == agilis_status_message(3): + elif ( + status == agilis_status_message(1) + or status == agilis_status_message(2) + or status == agilis_status_message(3) + ): return False else: retries += 1 - raise IOError("The function `am_i_still` ran out of maximum retries. " - "Could not query the status of the axis.") + raise IOError( + "The function `am_i_still` ran out of maximum retries. " + "Could not query the status of the axis." + ) def stop(self): """ Stops the axis. This is useful to interrupt a jogging motion. """ - self._cont.ag_sendcmd("{} ST".format( - int(self._ax) - )) + self._cont.ag_sendcmd("{} ST".format(int(self._ax))) def zero_position(self): """ Resets the step counter to zero. See `number_of_steps` for details. """ - self._cont.ag_sendcmd("{} ZP".format( - int(self._ax) - )) + self._cont.ag_sendcmd("{} ZP".format(int(self._ax))) class AGUC2(Instrument): @@ -374,7 +346,7 @@ def __init__(self, filelike): super(AGUC2, self).__init__(filelike) # Instrument requires '\r\n' line termination - self.terminator = '\r\n' + self.terminator = "\r\n" # Some local variables self._remote_mode = False @@ -386,6 +358,7 @@ class Axes(IntEnum): """ Enumeration of valid delay channels for the AG-UC2 controller. """ + X = 1 Y = 2 @@ -423,10 +396,10 @@ def enable_remote_mode(self): def enable_remote_mode(self, newval): if newval and not self._remote_mode: self._remote_mode = True - self.ag_sendcmd('MR') + self.ag_sendcmd("MR") elif not newval and self._remote_mode: self._remote_mode = False - self.ag_sendcmd('ML') + self.ag_sendcmd("ML") @property def error_previous_command(self): @@ -434,12 +407,12 @@ def error_previous_command(self): Retrieves the error of the previous command and translates it into a string. The string is returned """ - resp = self.ag_query('TE') + resp = self.ag_query("TE") - if resp.find('TE') == -1: + if resp.find("TE") == -1: return "Error code query failed." - resp = int(resp.replace('TE', '')) + resp = int(resp.replace("TE", "")) error_message = agilis_error_message(resp) return error_message @@ -448,7 +421,7 @@ def firmware_version(self): """ Returns the firmware version of the controller """ - resp = self.ag_query('VE') + resp = self.ag_query("VE") return resp @property @@ -545,7 +518,7 @@ def agilis_error_message(error_code): -3: "Wrong format for parameter nn (or must not be specified)", -4: "Parameter nn out of range", -5: "Not allowed in local mode", - -6: "Not allowed in current state" + -6: "Not allowed in current state", } if error_code in error_dict.keys(): @@ -556,14 +529,14 @@ def agilis_error_message(error_code): def agilis_status_message(status_code): """ - Returns a string with the status message for a given Agilis status - code. + Returns a string with the status message for a given Agilis status + code. - :param int status_code: status code as returned + :param int status_code: status code as returned - :return: status message - :rtype: string - """ + :return: status message + :rtype: string + """ if not isinstance(status_code, int): return "Status code is not an integer." @@ -571,9 +544,9 @@ def agilis_status_message(status_code): 0: "Ready (not moving).", 1: "Stepping (currently executing a `move_relative` command).", 2: "Jogging (currently executing a `jog` command with command" - "parameter different than 0).", + "parameter different than 0).", 3: "Moving to limit (currently executing `measure_current_position`, " - "`move_to_limit`, or `move_absolute` command).", + "`move_to_limit`, or `move_absolute` command).", } if status_code in status_dict.keys(): diff --git a/instruments/newport/errors.py b/instruments/newport/errors.py index 6ae80c8fe..dc66091de 100644 --- a/instruments/newport/errors.py +++ b/instruments/newport/errors.py @@ -16,86 +16,87 @@ class NewportError(IOError): """ Raised in response to an error with a Newport-brand instrument. """ + start_time = datetime.datetime.now() # Dict Containing all possible errors. # Uses strings for keys in order to handle axis messageDict = { - '0': "NO ERROR DETECTED", - '1': "PCI COMMUNICATION TIME-OUT", - '2': "Reserved for future use", - '3': "Reserved for future use", - '4': "EMERGENCY SOP ACTIVATED", - '5': "Reserved for future use", - '6': "COMMAND DOES NOT EXIST", - '7': "PARAMETER OUT OF RANGE", - '8': "CABLE INTERLOCK ERROR", - '9': "AXIS NUMBER OUT OF RANGE", - '10': "Reserved for future use", - '11': "Reserved for future use", - '12': "Reserved for future use", - '13': "GROUP NUMBER MISSING", - '14': "GROUP NUMBER OUT OF RANGE", - '15': "GROUP NUMBER NOT ASSIGNED", - '16': "GROUP NUMBER ALREADY ASSIGNED", - '17': "GROUP AXIS OUT OF RANGE", - '18': "GROUP AXIS ALREADY ASSIGNED", - '19': "GROUP AXIS DUPLICATED", - '20': "DATA ACQUISITION IS BUSY", - '21': "DATA ACQUISITION SETUP ERROR", - '22': "DATA ACQUISITION NOT ENABLED", - '23': "SERVO CYCLE (400 µS) TICK FAILURE", - '24': "Reserved for future use", - '25': "DOWNLOAD IN PROGRESS", - '26': "STORED PROGRAM NOT STARTEDL", - '27': "COMMAND NOT ALLOWEDL", - '28': "STORED PROGRAM FLASH AREA FULL", - '29': "GROUP PARAMETER MISSING", - '30': "GROUP PARAMETER OUT OF RANGE", - '31': "GROUP MAXIMUM VELOCITY EXCEEDED", - '32': "GROUP MAXIMUM ACCELERATION EXCEEDED", - '33': "GROUP MAXIMUM DECELERATION EXCEEDED", - '34': " GROUP MOVE NOT ALLOWED DURING MOTION", - '35': "PROGRAM NOT FOUND", - '36': "Reserved for future use", - '37': "AXIS NUMBER MISSING", - '38': "COMMAND PARAMETER MISSING", - '39': "PROGRAM LABEL NOT FOUND", - '40': "LAST COMMAND CANNOT BE REPEATED", - '41': "MAX NUMBER OF LABELS PER PROGRAM EXCEEDED", - 'x00': "MOTOR TYPE NOT DEFINED", - 'x01': "PARAMETER OUT OF RANGE", - 'x02': "AMPLIFIER FAULT DETECTED", - 'x03': "FOLLOWING ERROR THRESHOLD EXCEEDED", - 'x04': "POSITIVE HARDWARE LIMIT DETECTED", - 'x05': "NEGATIVE HARDWARE LIMIT DETECTED", - 'x06': "POSITIVE SOFTWARE LIMIT DETECTED", - 'x07': "NEGATIVE SOFTWARE LIMIT DETECTED", - 'x08': "MOTOR / STAGE NOT CONNECTED", - 'x09': "FEEDBACK SIGNAL FAULT DETECTED", - 'x10': "MAXIMUM VELOCITY EXCEEDED", - 'x11': "MAXIMUM ACCELERATION EXCEEDED", - 'x12': "Reserved for future use", - 'x13': "MOTOR NOT ENABLED", - 'x14': "Reserved for future use", - 'x15': "MAXIMUM JERK EXCEEDED", - 'x16': "MAXIMUM DAC OFFSET EXCEEDED", - 'x17': "ESP CRITICAL SETTINGS ARE PROTECTED", - 'x18': "ESP STAGE DEVICE ERROR", - 'x19': "ESP STAGE DATA INVALID", - 'x20': "HOMING ABORTED", - 'x21': "MOTOR CURRENT NOT DEFINED", - 'x22': "UNIDRIVE COMMUNICATIONS ERROR", - 'x23': "UNIDRIVE NOT DETECTED", - 'x24': "SPEED OUT OF RANGE", - 'x25': "INVALID TRAJECTORY MASTER AXIS", - 'x26': "PARAMETER CHARGE NOT ALLOWED", - 'x27': "INVALID TRAJECTORY MODE FOR HOMING", - 'x28': "INVALID ENCODER STEP RATIO", - 'x29': "DIGITAL I/O INTERLOCK DETECTED", - 'x30': "COMMAND NOT ALLOWED DURING HOMING", - 'x31': "COMMAND NOT ALLOWED DUE TO GROUP", - 'x32': "INVALID TRAJECTORY MODE FOR MOVING" + "0": "NO ERROR DETECTED", + "1": "PCI COMMUNICATION TIME-OUT", + "2": "Reserved for future use", + "3": "Reserved for future use", + "4": "EMERGENCY SOP ACTIVATED", + "5": "Reserved for future use", + "6": "COMMAND DOES NOT EXIST", + "7": "PARAMETER OUT OF RANGE", + "8": "CABLE INTERLOCK ERROR", + "9": "AXIS NUMBER OUT OF RANGE", + "10": "Reserved for future use", + "11": "Reserved for future use", + "12": "Reserved for future use", + "13": "GROUP NUMBER MISSING", + "14": "GROUP NUMBER OUT OF RANGE", + "15": "GROUP NUMBER NOT ASSIGNED", + "16": "GROUP NUMBER ALREADY ASSIGNED", + "17": "GROUP AXIS OUT OF RANGE", + "18": "GROUP AXIS ALREADY ASSIGNED", + "19": "GROUP AXIS DUPLICATED", + "20": "DATA ACQUISITION IS BUSY", + "21": "DATA ACQUISITION SETUP ERROR", + "22": "DATA ACQUISITION NOT ENABLED", + "23": "SERVO CYCLE (400 µS) TICK FAILURE", + "24": "Reserved for future use", + "25": "DOWNLOAD IN PROGRESS", + "26": "STORED PROGRAM NOT STARTEDL", + "27": "COMMAND NOT ALLOWEDL", + "28": "STORED PROGRAM FLASH AREA FULL", + "29": "GROUP PARAMETER MISSING", + "30": "GROUP PARAMETER OUT OF RANGE", + "31": "GROUP MAXIMUM VELOCITY EXCEEDED", + "32": "GROUP MAXIMUM ACCELERATION EXCEEDED", + "33": "GROUP MAXIMUM DECELERATION EXCEEDED", + "34": " GROUP MOVE NOT ALLOWED DURING MOTION", + "35": "PROGRAM NOT FOUND", + "36": "Reserved for future use", + "37": "AXIS NUMBER MISSING", + "38": "COMMAND PARAMETER MISSING", + "39": "PROGRAM LABEL NOT FOUND", + "40": "LAST COMMAND CANNOT BE REPEATED", + "41": "MAX NUMBER OF LABELS PER PROGRAM EXCEEDED", + "x00": "MOTOR TYPE NOT DEFINED", + "x01": "PARAMETER OUT OF RANGE", + "x02": "AMPLIFIER FAULT DETECTED", + "x03": "FOLLOWING ERROR THRESHOLD EXCEEDED", + "x04": "POSITIVE HARDWARE LIMIT DETECTED", + "x05": "NEGATIVE HARDWARE LIMIT DETECTED", + "x06": "POSITIVE SOFTWARE LIMIT DETECTED", + "x07": "NEGATIVE SOFTWARE LIMIT DETECTED", + "x08": "MOTOR / STAGE NOT CONNECTED", + "x09": "FEEDBACK SIGNAL FAULT DETECTED", + "x10": "MAXIMUM VELOCITY EXCEEDED", + "x11": "MAXIMUM ACCELERATION EXCEEDED", + "x12": "Reserved for future use", + "x13": "MOTOR NOT ENABLED", + "x14": "Reserved for future use", + "x15": "MAXIMUM JERK EXCEEDED", + "x16": "MAXIMUM DAC OFFSET EXCEEDED", + "x17": "ESP CRITICAL SETTINGS ARE PROTECTED", + "x18": "ESP STAGE DEVICE ERROR", + "x19": "ESP STAGE DATA INVALID", + "x20": "HOMING ABORTED", + "x21": "MOTOR CURRENT NOT DEFINED", + "x22": "UNIDRIVE COMMUNICATIONS ERROR", + "x23": "UNIDRIVE NOT DETECTED", + "x24": "SPEED OUT OF RANGE", + "x25": "INVALID TRAJECTORY MASTER AXIS", + "x26": "PARAMETER CHARGE NOT ALLOWED", + "x27": "INVALID TRAJECTORY MODE FOR HOMING", + "x28": "INVALID ENCODER STEP RATIO", + "x29": "DIGITAL I/O INTERLOCK DETECTED", + "x30": "COMMAND NOT ALLOWED DURING HOMING", + "x31": "COMMAND NOT ALLOWED DUE TO GROUP", + "x32": "INVALID TRAJECTORY MODE FOR MOVING", } def __init__(self, errcode=None, timestamp=None): @@ -113,19 +114,20 @@ def __init__(self, errcode=None, timestamp=None): if self._axis == 0: self._axis = None error_message = self.get_message(str(errcode)) - error = "Newport Error: {0}. Error Message: {1}. " \ - "At time : {2}".format(str(errcode), - error_message, - self._timestamp) + error = ( + "Newport Error: {0}. Error Message: {1}. " + "At time : {2}".format(str(errcode), error_message, self._timestamp) + ) super(NewportError, self).__init__(error) else: - error_message = self.get_message('x{0:02d}'.format(self._errcode)) - error = "Newport Error: {0}. Axis: {1}. " \ - "Error Message: {2}. " \ - "At time : {3}".format(str(self._errcode), - self._axis, - error_message, - self._timestamp) + error_message = self.get_message("x{0:02d}".format(self._errcode)) + error = ( + "Newport Error: {0}. Axis: {1}. " + "Error Message: {2}. " + "At time : {3}".format( + str(self._errcode), self._axis, error_message, self._timestamp + ) + ) super(NewportError, self).__init__(error) else: diff --git a/instruments/newport/newport_pmc8742.py b/instruments/newport/newport_pmc8742.py index fb4290bee..7398c770a 100644 --- a/instruments/newport/newport_pmc8742.py +++ b/instruments/newport/newport_pmc8742.py @@ -94,6 +94,7 @@ def __init__(self, filelike): class Axis: """PicoMotorController8742 Axis class for individual motors.""" + def __init__(self, parent, idx): """Initialize the axis with the parent and the number. @@ -105,10 +106,12 @@ def __init__(self, parent, idx): raise TypeError("Don't do that.") if idx > 3 and not parent.multiple_controllers: - raise IndexError("You requested an axis that is only " - "available in multi controller mode, " - "however, have not enabled it. See " - "`multi_controllers` routine.") + raise IndexError( + "You requested an axis that is only " + "available in multi controller mode, " + "however, have not enabled it. See " + "`multi_controllers` routine." + ) # set controller self._parent = parent @@ -129,6 +132,7 @@ class MotorType(IntEnum): unkown motor are connected. See also `motor_check` command to set these values per controller automatically. """ + none = 0 unknown = 1 tiny = 2 @@ -158,14 +162,16 @@ def acceleration(self): >>> ax = inst.axis[0] >>> ax.acceleration = u.Quantity(500, 1/u.s**-2) """ - return assume_units(int(self.query("AC?")), u.s**-2) + return assume_units(int(self.query("AC?")), u.s ** -2) @acceleration.setter def acceleration(self, value): - value = int(assume_units(value, u.s**-2).to(u.s**-2).magnitude) + value = int(assume_units(value, u.s ** -2).to(u.s ** -2).magnitude) if not 1 <= value <= 200000: - raise ValueError(f"Acceleration must be between 1 and " - f"200,000 s^-2 but is {value}.") + raise ValueError( + f"Acceleration must be between 1 and " + f"200,000 s^-2 but is {value}." + ) self.sendcmd(f"AC{value}") @property @@ -194,8 +200,10 @@ def home_position(self): @home_position.setter def home_position(self, value): if not -2147483648 <= value <= 2147483647: - raise ValueError(f"Home position must be between -2147483648 " - f"and 2147483647, but is {value}.") + raise ValueError( + f"Home position must be between -2147483648 " + f"and 2147483647, but is {value}." + ) self.sendcmd(f"DH{int(value)}") @property @@ -241,8 +249,10 @@ def motor_type(self): @motor_type.setter def motor_type(self, value): if not isinstance(value, self.MotorType): - raise TypeError(f"Set motor type must be of type `MotorType` " - f"but is of type {type(value)}.") + raise TypeError( + f"Set motor type must be of type `MotorType` " + f"but is of type {type(value)}." + ) self.sendcmd(f"QM{value.value}") @property @@ -271,8 +281,10 @@ def move_absolute(self): @move_absolute.setter def move_absolute(self, value): if not -2147483648 <= value <= 2147483647: - raise ValueError(f"Set position must be between -2147483648 " - f"and 2147483647, but is {value}.") + raise ValueError( + f"Set position must be between -2147483648 " + f"and 2147483647, but is {value}." + ) self.sendcmd(f"PA{int(value)}") @property @@ -301,8 +313,10 @@ def move_relative(self): @move_relative.setter def move_relative(self, value): if not -2147483648 <= value <= 2147483647: - raise ValueError(f"Set motion must be between -2147483648 " - f"and 2147483647, but is {value}.") + raise ValueError( + f"Set motion must be between -2147483648 " + f"and 2147483647, but is {value}." + ) self.sendcmd(f"PR{int(value)}") @property @@ -349,7 +363,7 @@ def velocity(self): >>> ax.velocity = u.Quantity(500, 1/u.s) """ retval = int(self.query("VA?")) - return u.Quantity(retval, 1/u.s) + return u.Quantity(retval, 1 / u.s) @velocity.setter def velocity(self, value): @@ -360,9 +374,11 @@ def velocity(self, value): value = int(assume_units(value, 1 / u.s).to(1 / u.s).magnitude) if not 0 < value <= max_velocity: - raise ValueError(f"The maximum allowed velocity for the set " - f"motor is {max_velocity}. The requested " - f"velocity of {value} is out of range.") + raise ValueError( + f"The maximum allowed velocity for the set " + f"motor is {max_velocity}. The requested " + f"velocity of {value} is out of range." + ) self.sendcmd(f"VA{value}") # METHODS # @@ -451,7 +467,7 @@ def controller_configuration(self): :return: Bitmask of the controller configuration. :rtype: str, binary configuration """ - return self.query('ZZ?', axs=False) + return self.query("ZZ?", axs=False) @controller_configuration.setter def controller_configuration(self, value): @@ -613,13 +629,15 @@ def query(self, cmd, size=-1, axs=True): retval = self._parent.query(command, size=size) - if retval[:len(self._address)] != self._address: - raise IOError(f"Expected to hear back from secondary " - f"controller {self._address}, instead " - f"controller {retval[:len(self._address)]} " - f"answered.") + if retval[: len(self._address)] != self._address: + raise IOError( + f"Expected to hear back from secondary " + f"controller {self._address}, instead " + f"controller {retval[:len(self._address)]} " + f"answered." + ) - return retval[len(self._address):] + return retval[len(self._address) :] @property def axis(self): diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index 7c833f934..5cdffee20 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -28,6 +28,7 @@ class NewportESP301HomeSearchMode(IntEnum): """ Enum containing different search modes code """ + #: Search along specified axes for the +0 position. zero_position_count = 0 #: Search for combined Home and Index signals. @@ -49,6 +50,7 @@ class NewportESP301Units(IntEnum): """ Enum containing what `units` return means. """ + encoder_step = 0 motor_step = 1 millimeter = 2 @@ -68,12 +70,14 @@ class NewportESP301MotorType(IntEnum): """ Enum for different motor types. """ + undefined = 0 dc_servo = 1 stepper_motor = 2 commutated_stepper_motor = 3 commutated_brushless_servo = 4 + # CLASSES ##################################################################### # pylint: disable=too-many-lines @@ -143,7 +147,7 @@ def _newport_cmd(self, cmd, params=tuple(), target=None, errcheck=True): raw_cmd = "{target}{cmd}{params}".format( target=target if target is not None else "", cmd=cmd.upper(), - params=",".join(map(str, params)) + params=",".join(map(str, params)), ) if self._execute_immediately: @@ -175,7 +179,7 @@ def _execute_cmd(self, raw_cmd, errcheck=True): self.sendcmd(raw_cmd) if errcheck: - err_resp = self.query('TB?') + err_resp = self.query("TB?") # pylint: disable=unused-variable code, timestamp, msg = err_resp.split(",") @@ -193,14 +197,13 @@ def _home(self, axis, search_mode, errcheck=True): the methods in this class and the axis class can both point to the same thing. """ - self._newport_cmd( - "OR", target=axis, params=[search_mode], errcheck=errcheck) + self._newport_cmd("OR", target=axis, params=[search_mode], errcheck=errcheck) def search_for_home( - self, - axis=1, - search_mode=NewportESP301HomeSearchMode.zero_position_count.value, - errcheck=True + self, + axis=1, + search_mode=NewportESP301HomeSearchMode.zero_position_count.value, + errcheck=True, ): """ Searches the specified axis for home using the method specified @@ -245,8 +248,9 @@ def define_program(self, program_id): Must be in ``range(1, 101)``. """ if program_id not in range(1, 101): - raise ValueError("Invalid program ID. Must be an integer from " - "1 to 100 (inclusive).") + raise ValueError( + "Invalid program ID. Must be an integer from " "1 to 100 (inclusive)." + ) self._newport_cmd("XX", target=program_id) try: self._newport_cmd("EP", target=program_id) @@ -270,8 +274,7 @@ def execute_bulk_command(self, errcheck=True): """ self._execute_immediately = False yield - command_string = reduce( - lambda x, y: x + ' ; ' + y + ' ; ', self._command_list) + command_string = reduce(lambda x, y: x + " ; " + y + " ; ", self._command_list) # TODO: is _bulk_query_resp getting back to user? self._bulk_query_resp = self._execute_cmd(command_string, errcheck) self._command_list = [] @@ -284,8 +287,9 @@ def run_program(self, program_id): :param int program_id: ID number for previously saved user program """ if program_id not in range(1, 101): - raise ValueError("Invalid program ID. Must be an integer from " - "1 to 100 (inclusive).") + raise ValueError( + "Invalid program ID. Must be an integer from " "1 to 100 (inclusive)." + ) self._newport_cmd("EX", target=program_id) @@ -298,6 +302,7 @@ class NewportESP301Axis: instantiated by the user directly, but is returned by `NewportESP301.axis`. """ + # quantities micro inch # micro_inch = u.UnitQuantity('micro-inch', u.inch / 1e6, symbol='uin') micro_inch = u.uinch @@ -308,24 +313,25 @@ class NewportESP301Axis: # going to do this until I have a physical device _unit_dict = { - 0: u.count, - 1: u.count, - 2: u.mm, - 3: u.um, - 4: u.inch, - 5: u.mil, - 6: micro_inch, # compound unit for micro-inch - 7: u.deg, - 8: u.grad, - 9: u.rad, - 10: u.mrad, - 11: u.urad, + 0: u.count, + 1: u.count, + 2: u.mm, + 3: u.um, + 4: u.inch, + 5: u.mil, + 6: micro_inch, # compound unit for micro-inch + 7: u.deg, + 8: u.grad, + 9: u.rad, + 10: u.mrad, + 11: u.urad, } def __init__(self, controller, axis_id): if not isinstance(controller, NewportESP301): - raise TypeError("Axis must be controlled by a Newport ESP-301 " - "motor controller.") + raise TypeError( + "Axis must be controlled by a Newport ESP-301 " "motor controller." + ) self._controller = controller self._axis_id = axis_id + 1 @@ -355,16 +361,10 @@ def _get_units(self): .. seealso:: NewportESP301Units """ - return NewportESP301Units( - int(self._newport_cmd("SN?", target=self.axis_id)) - ) + return NewportESP301Units(int(self._newport_cmd("SN?", target=self.axis_id))) def _set_units(self, new_units): - return self._newport_cmd( - "SN", - target=self.axis_id, - params=[int(new_units)] - ) + return self._newport_cmd("SN", target=self.axis_id, params=[int(new_units)]) # PROPERTIES ## @@ -401,15 +401,18 @@ def acceleration(self): return assume_units( float(self._newport_cmd("AC?", target=self.axis_id)), - self._units / (u.s**2) + self._units / (u.s ** 2), ) @acceleration.setter def acceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (u.s**2)).to( - self._units / (u.s**2)).magnitude) + newval = float( + assume_units(newval, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) self._newport_cmd("AC", target=self.axis_id, params=[newval]) @property @@ -423,15 +426,18 @@ def deceleration(self): """ return assume_units( float(self._newport_cmd("AG?", target=self.axis_id)), - self._units / (u.s**2) + self._units / (u.s ** 2), ) @deceleration.setter def deceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (u.s**2)).to( - self._units / (u.s**2)).magnitude) + newval = float( + assume_units(newval, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) self._newport_cmd("AG", target=self.axis_id, params=[newval]) @property @@ -445,13 +451,16 @@ def estop_deceleration(self): """ return assume_units( float(self._newport_cmd("AE?", target=self.axis_id)), - self._units / (u.s**2) + self._units / (u.s ** 2), ) @estop_deceleration.setter def estop_deceleration(self, decel): - decel = float(assume_units(decel, self._units / (u.s**2)).to( - self._units / (u.s**2)).magnitude) + decel = float( + assume_units(decel, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) self._newport_cmd("AE", target=self.axis_id, params=[decel]) @property @@ -466,13 +475,16 @@ def jerk(self): return assume_units( float(self._newport_cmd("JK?", target=self.axis_id)), - self._units / (u.s**3) + self._units / (u.s ** 3), ) @jerk.setter def jerk(self, jerk): - jerk = float(assume_units(jerk, self._units / (u.s**3)).to( - self._units / (u.s**3)).magnitude) + jerk = float( + assume_units(jerk, self._units / (u.s ** 3)) + .to(self._units / (u.s ** 3)) + .magnitude + ) self._newport_cmd("JK", target=self.axis_id, params=[jerk]) @property @@ -485,14 +497,14 @@ def velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("VA?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("VA?", target=self.axis_id)), self._units / u.s ) @velocity.setter def velocity(self, velocity): - velocity = float(assume_units(velocity, self._units / (u.s)).to( - self._units / u.s).magnitude) + velocity = float( + assume_units(velocity, self._units / (u.s)).to(self._units / u.s).magnitude + ) self._newport_cmd("VA", target=self.axis_id, params=[velocity]) @property @@ -505,16 +517,16 @@ def max_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("VU?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("VU?", target=self.axis_id)), self._units / u.s ) @max_velocity.setter def max_velocity(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / u.s).to( - self._units / u.s).magnitude) + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) self._newport_cmd("VU", target=self.axis_id, params=[newval]) @property @@ -527,16 +539,16 @@ def max_base_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("VB?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("VB?", target=self.axis_id)), self._units / u.s ) @max_base_velocity.setter def max_base_velocity(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / u.s).to( - self._units / u.s).magnitude) + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) self._newport_cmd("VB", target=self.axis_id, params=[newval]) @property @@ -549,18 +561,16 @@ def jog_high_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("JH?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("JH?", target=self.axis_id)), self._units / u.s ) @jog_high_velocity.setter def jog_high_velocity(self, newval): if newval is None: return - newval = float(assume_units( - newval, - self._units / u.s - ).to(self._units / u.s).magnitude) + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) self._newport_cmd("JH", target=self.axis_id, params=[newval]) @property @@ -573,18 +583,16 @@ def jog_low_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("JW?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("JW?", target=self.axis_id)), self._units / u.s ) @jog_low_velocity.setter def jog_low_velocity(self, newval): if newval is None: return - newval = float(assume_units( - newval, - self._units / u.s - ).to(self._units / u.s).magnitude) + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) self._newport_cmd("JW", target=self.axis_id, params=[newval]) @property @@ -597,18 +605,16 @@ def homing_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("OH?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("OH?", target=self.axis_id)), self._units / u.s ) @homing_velocity.setter def homing_velocity(self, newval): if newval is None: return - newval = float(assume_units( - newval, - self._units / u.s - ).to(self._units / u.s).magnitude) + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) self._newport_cmd("OH", target=self.axis_id, params=[newval]) @property @@ -622,15 +628,18 @@ def max_acceleration(self): """ return assume_units( float(self._newport_cmd("AU?", target=self.axis_id)), - self._units / (u.s**2) + self._units / (u.s ** 2), ) @max_acceleration.setter def max_acceleration(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units / (u.s**2)).to( - self._units / (u.s**2)).magnitude) + newval = float( + assume_units(newval, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) self._newport_cmd("AU", target=self.axis_id, params=[newval]) @property @@ -647,8 +656,11 @@ def max_deceleration(self): @max_deceleration.setter def max_deceleration(self, decel): - decel = float(assume_units(decel, self._units / (u.s**2)).to( - self._units / (u.s**2)).magnitude) + decel = float( + assume_units(decel, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) self.max_acceleration = decel @property @@ -661,8 +673,7 @@ def position(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("TP?", target=self.axis_id)), - self._units + float(self._newport_cmd("TP?", target=self.axis_id)), self._units ) @property @@ -675,8 +686,7 @@ def desired_position(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("DP?", target=self.axis_id)), - self._units + float(self._newport_cmd("DP?", target=self.axis_id)), self._units ) @property @@ -689,8 +699,7 @@ def desired_velocity(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("DV?", target=self.axis_id)), - self._units / u.s + float(self._newport_cmd("DV?", target=self.axis_id)), self._units / u.s ) @property @@ -704,16 +713,14 @@ def home(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("DH?", target=self.axis_id)), - self._units + float(self._newport_cmd("DH?", target=self.axis_id)), self._units ) @home.setter def home(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units).to( - self._units).magnitude) + newval = float(assume_units(newval, self._units).to(self._units).magnitude) self._newport_cmd("DH", target=self.axis_id, params=[newval]) @property @@ -749,16 +756,14 @@ def encoder_resolution(self): """ return assume_units( - float(self._newport_cmd("SU?", target=self.axis_id)), - self._units + float(self._newport_cmd("SU?", target=self.axis_id)), self._units ) @encoder_resolution.setter def encoder_resolution(self, newval): if newval is None: return - newval = float(assume_units(newval, self._units).to( - self._units).magnitude) + newval = float(assume_units(newval, self._units).to(self._units).magnitude) self._newport_cmd("SU", target=self.axis_id, params=[newval]) @property @@ -772,18 +777,14 @@ def full_step_resolution(self): """ return assume_units( - float(self._newport_cmd("FR?", target=self.axis_id)), - self._units + float(self._newport_cmd("FR?", target=self.axis_id)), self._units ) @full_step_resolution.setter def full_step_resolution(self, newval): if newval is None: return - newval = float(assume_units( - newval, - self._units - ).to(self._units).magnitude) + newval = float(assume_units(newval, self._units).to(self._units).magnitude) self._newport_cmd("FR", target=self.axis_id, params=[newval]) @property @@ -795,14 +796,12 @@ def left_limit(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("SL?", target=self.axis_id)), - self._units + float(self._newport_cmd("SL?", target=self.axis_id)), self._units ) @left_limit.setter def left_limit(self, limit): - limit = float(assume_units(limit, self._units).to( - self._units).magnitude) + limit = float(assume_units(limit, self._units).to(self._units).magnitude) self._newport_cmd("SL", target=self.axis_id, params=[limit]) @property @@ -814,14 +813,12 @@ def right_limit(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("SR?", target=self.axis_id)), - self._units + float(self._newport_cmd("SR?", target=self.axis_id)), self._units ) @right_limit.setter def right_limit(self, limit): - limit = float(assume_units(limit, self._units).to( - self._units).magnitude) + limit = float(assume_units(limit, self._units).to(self._units).magnitude) self._newport_cmd("SR", target=self.axis_id, params=[limit]) @property @@ -833,18 +830,14 @@ def error_threshold(self): :type: `~pint.Quantity` or `float` """ return assume_units( - float(self._newport_cmd("FE?", target=self.axis_id)), - self._units + float(self._newport_cmd("FE?", target=self.axis_id)), self._units ) @error_threshold.setter def error_threshold(self, newval): if newval is None: return - newval = float(assume_units( - newval, - self._units - ).to(self._units).magnitude) + newval = float(assume_units(newval, self._units).to(self._units).magnitude) self._newport_cmd("FE", target=self.axis_id, params=[newval]) @property @@ -856,17 +849,13 @@ def current(self): of current newport :math:`\\text{A}` :type: `~pint.Quantity` or `float` """ - return assume_units( - float(self._newport_cmd("QI?", target=self.axis_id)), - u.A - ) + return assume_units(float(self._newport_cmd("QI?", target=self.axis_id)), u.A) @current.setter def current(self, newval): if newval is None: return - current = float(assume_units(newval, u.A).to( - u.A).magnitude) + current = float(assume_units(newval, u.A).to(u.A).magnitude) self._newport_cmd("QI", target=self.axis_id, params=[current]) @property @@ -878,17 +867,13 @@ def voltage(self): of current newport :math:`\\text{V}` :type: `~pint.Quantity` or `float` """ - return assume_units( - float(self._newport_cmd("QV?", target=self.axis_id)), - u.V - ) + return assume_units(float(self._newport_cmd("QV?", target=self.axis_id)), u.V) @voltage.setter def voltage(self, newval): if newval is None: return - voltage = float(assume_units(newval, u.V).to( - u.V).magnitude) + voltage = float(assume_units(newval, u.V).to(u.V).magnitude) self._newport_cmd("QV", target=self.axis_id, params=[voltage]) @property @@ -904,10 +889,9 @@ def motor_type(self): :type: `int` :rtype: `NewportESP301MotorType` """ - return NewportESP301MotorType(int(self._newport_cmd( - "QM?", - target=self._axis_id - ))) + return NewportESP301MotorType( + int(self._newport_cmd("QM?", target=self._axis_id)) + ) @motor_type.setter def motor_type(self, newval): @@ -977,11 +961,7 @@ def microstep_factor(self, newval): if newval < 1 or newval > 250: raise ValueError("Microstep factor must be between 1 and 250") else: - self._newport_cmd( - "QS", - target=self._axis_id, - params=[newval] - ) + self._newport_cmd("QS", target=self._axis_id, params=[newval]) @property def hardware_limit_configuration(self): @@ -1086,8 +1066,7 @@ def encoder_position(self): # MOVEMENT METHODS # def search_for_home( - self, - search_mode=NewportESP301HomeSearchMode.zero_position_count.value + self, search_mode=NewportESP301HomeSearchMode.zero_position_count.value ): """ Searches this axis only @@ -1110,8 +1089,7 @@ def move(self, position, absolute=True, wait=False, block=False): commands until movement is finished :param bool block: If True, will block code until movement is finished """ - position = float(assume_units(position, self._units).to( - self._units).magnitude) + position = float(assume_units(position, self._units).to(self._units).magnitude) if absolute: self._newport_cmd("PA", params=[position], target=self.axis_id) else: @@ -1163,10 +1141,8 @@ def wait_for_position(self, position): :type position: float or :class:`~pint.Quantity` """ - position = float(assume_units(position, self._units).to( - self._units).magnitude) - self._newport_cmd( - "WP", target=self.axis_id, params=[position]) + position = float(assume_units(position, self._units).to(self._units).magnitude) + self._newport_cmd("WP", target=self.axis_id, params=[position]) def wait_for_motion(self, poll_interval=0.01, max_wait=None): """ @@ -1184,11 +1160,9 @@ def wait_for_motion(self, poll_interval=0.01, max_wait=None): # In programming mode, the "WS" command should be # sent instead, and the two parameters to this method should # be ignored. - poll_interval = float(assume_units(poll_interval, u.s).to( - u.s).magnitude) + poll_interval = float(assume_units(poll_interval, u.s).to(u.s).magnitude) if max_wait is not None: - max_wait = float(assume_units(max_wait, u.s).to( - u.s).magnitude) + max_wait = float(assume_units(max_wait, u.s).to(u.s).magnitude) tic = time.time() while True: if self.is_motion_done: @@ -1247,49 +1221,54 @@ def setup_axis(self, **kwargs): * 'reduce_motor_torque_percentage' = percentage between 0 and 100 """ - self.motor_type = kwargs.get('motor_type') - self.feedback_configuration = kwargs.get('feedback_configuration') - self.full_step_resolution = kwargs.get('full_step_resolution') - self.position_display_resolution = kwargs.get('position_display_' - 'resolution') - self.current = kwargs.get('current') - self.voltage = kwargs.get('voltage') - self.units = int(kwargs.get('units')) - self.encoder_resolution = kwargs.get('encoder_resolution') - self.max_acceleration = kwargs.get('max_acceleration') - self.max_velocity = kwargs.get('max_velocity') - self.max_base_velocity = kwargs.get('max_base_velocity') - self.homing_velocity = kwargs.get('homing_velocity') - self.jog_high_velocity = kwargs.get('jog_high_velocity') - self.jog_low_velocity = kwargs.get('jog_low_velocity') - self.acceleration = kwargs.get('acceleration') - self.velocity = kwargs.get('velocity') - self.deceleration = kwargs.get('deceleration') - self.estop_deceleration = kwargs.get('estop_deceleration') - self.jerk = kwargs.get('jerk') - self.error_threshold = kwargs.get('error_threshold') - self.proportional_gain = kwargs.get('proportional_gain') - self.derivative_gain = kwargs.get('derivative_gain') - self.integral_gain = kwargs.get('integral_gain') - self.integral_saturation_gain = kwargs.get('integral_saturation_gain') - self.home = kwargs.get('home') - self.microstep_factor = kwargs.get('microstep_factor') - self.acceleration_feed_forward = kwargs.get('acceleration_feed_forward') - self.trajectory = kwargs.get('trajectory') - self.hardware_limit_configuration = kwargs.get('hardware_limit_' - 'configuration') - if 'reduce_motor_torque_time' in kwargs and 'reduce_motor_torque_percentage' in kwargs: - motor_time = kwargs['reduce_motor_torque_time'] + self.motor_type = kwargs.get("motor_type") + self.feedback_configuration = kwargs.get("feedback_configuration") + self.full_step_resolution = kwargs.get("full_step_resolution") + self.position_display_resolution = kwargs.get("position_display_" "resolution") + self.current = kwargs.get("current") + self.voltage = kwargs.get("voltage") + self.units = int(kwargs.get("units")) + self.encoder_resolution = kwargs.get("encoder_resolution") + self.max_acceleration = kwargs.get("max_acceleration") + self.max_velocity = kwargs.get("max_velocity") + self.max_base_velocity = kwargs.get("max_base_velocity") + self.homing_velocity = kwargs.get("homing_velocity") + self.jog_high_velocity = kwargs.get("jog_high_velocity") + self.jog_low_velocity = kwargs.get("jog_low_velocity") + self.acceleration = kwargs.get("acceleration") + self.velocity = kwargs.get("velocity") + self.deceleration = kwargs.get("deceleration") + self.estop_deceleration = kwargs.get("estop_deceleration") + self.jerk = kwargs.get("jerk") + self.error_threshold = kwargs.get("error_threshold") + self.proportional_gain = kwargs.get("proportional_gain") + self.derivative_gain = kwargs.get("derivative_gain") + self.integral_gain = kwargs.get("integral_gain") + self.integral_saturation_gain = kwargs.get("integral_saturation_gain") + self.home = kwargs.get("home") + self.microstep_factor = kwargs.get("microstep_factor") + self.acceleration_feed_forward = kwargs.get("acceleration_feed_forward") + self.trajectory = kwargs.get("trajectory") + self.hardware_limit_configuration = kwargs.get( + "hardware_limit_" "configuration" + ) + if ( + "reduce_motor_torque_time" in kwargs + and "reduce_motor_torque_percentage" in kwargs + ): + motor_time = kwargs["reduce_motor_torque_time"] motor_time = int(assume_units(motor_time, u.ms).to(u.ms).magnitude) if motor_time < 0 or motor_time > 60000: raise ValueError("Time must be between 0 and 60000 ms") - percentage = kwargs['reduce_motor_torque_percentage'] - percentage = int(assume_units(percentage, u.percent).to( - u.percent).magnitude) + percentage = kwargs["reduce_motor_torque_percentage"] + percentage = int( + assume_units(percentage, u.percent).to(u.percent).magnitude + ) if percentage < 0 or percentage > 100: raise ValueError(r"Percentage must be between 0 and 100%") self._newport_cmd( - "QR", target=self._axis_id, params=[motor_time, percentage]) + "QR", target=self._axis_id, params=[motor_time, percentage] + ) # update motor configuration self._newport_cmd("UF", target=self._axis_id) @@ -1332,35 +1311,33 @@ def read_setup(self): """ config = dict() - config['units'] = self.units - config['motor_type'] = self.motor_type - config['feedback_configuration'] = self.feedback_configuration - config['full_step_resolution'] = self.full_step_resolution - config[ - 'position_display_resolution'] = self.position_display_resolution - config['current'] = self.current - config['max_velocity'] = self.max_velocity - config['encoder_resolution'] = self.encoder_resolution - config['acceleration'] = self.acceleration - config['deceleration'] = self.deceleration - config['velocity'] = self.velocity - config['max_acceleration'] = self.max_acceleration - config['homing_velocity'] = self.homing_velocity - config['jog_high_velocity'] = self.jog_high_velocity - config['jog_low_velocity'] = self.jog_low_velocity - config['estop_deceleration'] = self.estop_deceleration - config['jerk'] = self.jerk + config["units"] = self.units + config["motor_type"] = self.motor_type + config["feedback_configuration"] = self.feedback_configuration + config["full_step_resolution"] = self.full_step_resolution + config["position_display_resolution"] = self.position_display_resolution + config["current"] = self.current + config["max_velocity"] = self.max_velocity + config["encoder_resolution"] = self.encoder_resolution + config["acceleration"] = self.acceleration + config["deceleration"] = self.deceleration + config["velocity"] = self.velocity + config["max_acceleration"] = self.max_acceleration + config["homing_velocity"] = self.homing_velocity + config["jog_high_velocity"] = self.jog_high_velocity + config["jog_low_velocity"] = self.jog_low_velocity + config["estop_deceleration"] = self.estop_deceleration + config["jerk"] = self.jerk # config['error_threshold'] = self.error_threshold - config['proportional_gain'] = self.proportional_gain - config['derivative_gain'] = self.derivative_gain - config['integral_gain'] = self.integral_gain - config['integral_saturation_gain'] = self.integral_saturation_gain - config['home'] = self.home - config['microstep_factor'] = self.microstep_factor - config['acceleration_feed_forward'] = self.acceleration_feed_forward - config['trajectory'] = self.trajectory - config[ - 'hardware_limit_configuration'] = self.hardware_limit_configuration + config["proportional_gain"] = self.proportional_gain + config["derivative_gain"] = self.derivative_gain + config["integral_gain"] = self.integral_gain + config["integral_saturation_gain"] = self.integral_saturation_gain + config["home"] = self.home + config["microstep_factor"] = self.microstep_factor + config["acceleration_feed_forward"] = self.acceleration_feed_forward + config["trajectory"] = self.trajectory + config["hardware_limit_configuration"] = self.hardware_limit_configuration return config def get_status(self): @@ -1375,11 +1352,11 @@ def get_status(self): :rtype: dict """ status = dict() - status['units'] = self.units - status['position'] = self.position - status['desired_position'] = self.desired_position - status['desired_velocity'] = self.desired_velocity - status['is_motion_done'] = self.is_motion_done + status["units"] = self.units + status["position"] = self.position + status["desired_position"] = self.desired_position + status["desired_velocity"] = self.desired_velocity + status["is_motion_done"] = self.is_motion_done return status @@ -1406,8 +1383,7 @@ def _get_unit_num(self, quantity): if quant == quantity: return num - raise KeyError( - "{0} is not a valid unit for Newport Axis".format(quantity)) + raise KeyError("{0} is not a valid unit for Newport Axis".format(quantity)) # pylint: disable=protected-access def _newport_cmd(self, cmd, **kwargs): diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index 0d976082d..76286f4e2 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -42,6 +42,7 @@ class Status(IntEnum): """ Enum containing the valid states of the laser """ + normal = 1 inner_modulation = 2 power_scan = 3 @@ -61,6 +62,7 @@ class _AutomaticCurrentControl: .. warning:: This class is not designed to be accessed directly. It should be interfaced via `LM.acc` """ + def __init__(self, parent): self._parent = parent self._enabled = False @@ -83,7 +85,7 @@ def target(self): :type: `~pint.Quantity` """ response = float(self._parent.query("rstli?")) - return response*u.mA + return response * u.mA @property def enabled(self): @@ -106,8 +108,10 @@ def enabled(self): @enabled.setter def enabled(self, newval): if not isinstance(newval, bool): - raise TypeError("ACC driver enabled property must be specified" - "with a boolean, got {}.".format(type(newval))) + raise TypeError( + "ACC driver enabled property must be specified" + "with a boolean, got {}.".format(type(newval)) + ) if newval: self._parent.sendcmd("lcen") else: @@ -150,6 +154,7 @@ class _AutomaticPowerControl: .. warning:: This class is not designed to be accessed directly. It should be interfaced via `LM.apc` """ + def __init__(self, parent): self._parent = parent self._enabled = False @@ -172,7 +177,7 @@ def target(self): :type: `~pint.Quantity` """ response = self._parent.query("rslp?") - return float(response)*u.mW + return float(response) * u.mW @property def enabled(self): @@ -195,8 +200,10 @@ def enabled(self): @enabled.setter def enabled(self, newval): if not isinstance(newval, bool): - raise TypeError("APC driver enabled property must be specified " - "with a boolean, got {}.".format(type(newval))) + raise TypeError( + "APC driver enabled property must be specified " + "with a boolean, got {}.".format(type(newval)) + ) if newval: self._parent.sendcmd("len") else: @@ -238,6 +245,7 @@ class _Modulation: .. warning:: This class is not designed to be accessed directly. It should be interfaced via `LM.modulation` """ + def __init__(self, parent): self._parent = parent self._enabled = False @@ -263,12 +271,12 @@ def on_time(self): :type: `~pint.Quantity` """ response = self._parent.query("stsont?") - return float(response)*u.ms + return float(response) * u.ms @on_time.setter def on_time(self, newval): newval = assume_units(newval, u.ms).to(u.ms).magnitude - self._parent.sendcmd("stsont:"+str(newval)) + self._parent.sendcmd("stsont:" + str(newval)) @property def off_time(self): @@ -291,12 +299,12 @@ def off_time(self): :type: `~pint.Quantity` """ response = self._parent.query("stsofft?") - return float(response)*u.ms + return float(response) * u.ms @off_time.setter def off_time(self, newval): newval = assume_units(newval, u.ms).to(u.ms).magnitude - self._parent.sendcmd("stsofft:"+str(newval)) + self._parent.sendcmd("stsofft:" + str(newval)) @property def enabled(self): @@ -319,8 +327,10 @@ def enabled(self): @enabled.setter def enabled(self, newval): if not isinstance(newval, bool): - raise TypeError("Modulation enabled property must be specified " - "with a boolean, got {}.".format(type(newval))) + raise TypeError( + "Modulation enabled property must be specified " + "with a boolean, got {}.".format(type(newval)) + ) if newval: self._parent.sendcmd("stm") else: @@ -335,6 +345,7 @@ class _ThermoElectricCooler: .. warning:: This class is not designed to be accessed directly. It should be interfaced via `LM.tec` """ + def __init__(self, parent): self._parent = parent self._enabled = False @@ -356,7 +367,7 @@ def current(self): :type: `~pint.Quantity` """ response = self._parent.query("rti?") - return float(response)*u.mA + return float(response) * u.mA @property def target(self): @@ -398,8 +409,10 @@ def enabled(self): @enabled.setter def enabled(self, newval): if not isinstance(newval, bool): - raise TypeError("TEC enabled property must be specified with " - "a boolean, got {}.".format(type(newval))) + raise TypeError( + "TEC enabled property must be specified with " + "a boolean, got {}.".format(type(newval)) + ) if newval: self._parent.sendcmd("tecon") else: @@ -432,12 +445,12 @@ def current(self): :type: `~pint.Quantity` """ response = self.query("rli?") - return float(response)*u.mA + return float(response) * u.mA @current.setter def current(self, newval): newval = assume_units(newval, u.mA).to(u.mA).magnitude - self.sendcmd("slc:"+str(newval)) + self.sendcmd("slc:" + str(newval)) @property def maximum_current(self): @@ -450,11 +463,11 @@ def maximum_current(self): :type: `~pint.Quantity` """ response = self.query("rlcm?") - return float(response)*u.mA + return float(response) * u.mA @maximum_current.setter def maximum_current(self, newval): - newval = assume_units(newval, u.mA).to('mA').magnitude + newval = assume_units(newval, u.mA).to("mA").magnitude self.sendcmd("smlc:" + str(newval)) @property @@ -467,12 +480,12 @@ def power(self): :rtype: `~pint.Quantity` """ response = self.query("rlp?") - return float(response)*u.mW + return float(response) * u.mW @power.setter def power(self, newval): newval = assume_units(newval, u.mW).to(u.mW).magnitude - self.sendcmd("slp:"+str(newval)) + self.sendcmd("slp:" + str(newval)) @property def serial_number(self): @@ -509,7 +522,7 @@ def temperature(self): @temperature.setter def temperature(self, newval): newval = convert_temperature(newval, u.degC).magnitude - self.sendcmd("stt:"+str(newval)) + self.sendcmd("stt:" + str(newval)) @property def enabled(self): @@ -523,8 +536,10 @@ def enabled(self): @enabled.setter def enabled(self, newval): if not isinstance(newval, bool): - raise TypeError("Laser module enabled property must be specified " - "with a boolean, got {}.".format(type(newval))) + raise TypeError( + "Laser module enabled property must be specified " + "with a boolean, got {}.".format(type(newval)) + ) if newval: self.sendcmd("lon") else: diff --git a/instruments/optional_dep_finder.py b/instruments/optional_dep_finder.py index f73ac0e6d..11033f379 100644 --- a/instruments/optional_dep_finder.py +++ b/instruments/optional_dep_finder.py @@ -7,6 +7,7 @@ # pylint: disable=unused-import try: import numpy + _numpy_installed = True except ImportError: numpy = None diff --git a/instruments/oxford/oxforditc503.py b/instruments/oxford/oxforditc503.py index 07fd06fa7..cca4c1783 100644 --- a/instruments/oxford/oxforditc503.py +++ b/instruments/oxford/oxforditc503.py @@ -29,7 +29,7 @@ class OxfordITC503(Instrument): def __init__(self, filelike): super(OxfordITC503, self).__init__(filelike) self.terminator = "\r" - self.sendcmd('C3') # Enable remote commands + self.sendcmd("C3") # Enable remote commands # INNER CLASSES # @@ -56,7 +56,7 @@ def temperature(self): :units: Kelvin :type: `~pint.Quantity` """ - value = float(self._parent.query('R{}'.format(self._idx))[1:]) + value = float(self._parent.query("R{}".format(self._idx))[1:]) return u.Quantity(value, u.kelvin) # PROPERTIES # diff --git a/instruments/phasematrix/phasematrix_fsw0020.py b/instruments/phasematrix/phasematrix_fsw0020.py index 48df12f65..5ff3161a8 100644 --- a/instruments/phasematrix/phasematrix_fsw0020.py +++ b/instruments/phasematrix/phasematrix_fsw0020.py @@ -36,7 +36,7 @@ def reset(self): Note that no commands will be accepted by the generator for at least :math:`5 \mu\text{s}`. """ - self.sendcmd('0E.') + self.sendcmd("0E.") @property def frequency(self): @@ -48,7 +48,7 @@ def frequency(self): :type: `~pint.Quantity` :units: frequency, assumed to be GHz """ - return (int(self.query('04.'), 16) * u.mHz).to(u.GHz) + return (int(self.query("04."), 16) * u.mHz).to(u.GHz) @frequency.setter def frequency(self, newval): @@ -58,7 +58,7 @@ def frequency(self, newval): # Write the integer to the serial port in ASCII-encoded # uppercase-hexadecimal format, with padding to 12 nybbles. - self.sendcmd('0C{:012X}.'.format(newval)) + self.sendcmd("0C{:012X}.".format(newval)) # No return data, so no readline needed. @@ -72,7 +72,7 @@ def power(self): :type: `~pint.Quantity` :units: log-power, assumed to be dBm """ - return u.Quantity((int(self.query('0D.'), 16)), u.cBm).to(u.dBm) + return u.Quantity((int(self.query("0D."), 16)), u.cBm).to(u.dBm) @power.setter def power(self, newval): @@ -84,7 +84,7 @@ def power(self, newval): newval = int(assume_units(newval, u.dBm).to(u.cBm).magnitude) # Command code 0x03, parameter length 2 bytes (4 nybbles) - self.sendcmd('03{:04X}.'.format(newval)) + self.sendcmd("03{:04X}.".format(newval)) @property def phase(self): @@ -105,7 +105,7 @@ def blanking(self): @blanking.setter def blanking(self, newval): - self.sendcmd('05{:02X}.'.format(1 if newval else 0)) + self.sendcmd("05{:02X}.".format(1 if newval else 0)) @property def ref_output(self): @@ -118,7 +118,7 @@ def ref_output(self): @ref_output.setter def ref_output(self, newval): - self.sendcmd('08{:02X}.'.format(1 if newval else 0)) + self.sendcmd("08{:02X}.".format(1 if newval else 0)) @property def output(self): @@ -132,7 +132,7 @@ def output(self): @output.setter def output(self, newval): - self.sendcmd('0F{:02X}.'.format(1 if newval else 0)) + self.sendcmd("0F{:02X}.".format(1 if newval else 0)) @property def pulse_modulation(self): @@ -145,7 +145,7 @@ def pulse_modulation(self): @pulse_modulation.setter def pulse_modulation(self, newval): - self.sendcmd('09{:02X}.'.format(1 if newval else 0)) + self.sendcmd("09{:02X}.".format(1 if newval else 0)) @property def am_modulation(self): @@ -158,4 +158,4 @@ def am_modulation(self): @am_modulation.setter def am_modulation(self, newval): - self.sendcmd('0A{:02X}.'.format(1 if newval else 0)) + self.sendcmd("0A{:02X}.".format(1 if newval else 0)) diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index 4bfa34e72..56c848f0b 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -10,8 +10,7 @@ from instruments.generic_scpi import SCPIInstrument from instruments.units import ureg as u -from instruments.util_fns import (enum_property, bool_property, int_property, - ProxyList) +from instruments.util_fns import enum_property, bool_property, int_property, ProxyList # CLASSES ##################################################################### @@ -72,6 +71,7 @@ class InputSource(IntEnum): """ Enum containing valid input source modes for the AVS 47 """ + ground = 0 actual = 1 reference = 2 @@ -102,7 +102,7 @@ def sensor(self): interface and locks-out the front panel. :type: `bool` - """ + """, ) input_source = enum_property( @@ -113,7 +113,7 @@ def sensor(self): Gets/sets the input source. :type: `PicowattAVS47.InputSource` - """ + """, ) mux_channel = int_property( @@ -127,7 +127,7 @@ def sensor(self): :type: `int` """, - valid_set=range(8) + valid_set=range(8), ) excitation = int_property( @@ -139,7 +139,7 @@ def sensor(self): :type: `int` """, - valid_set=range(8) + valid_set=range(8), ) display = int_property( @@ -151,5 +151,5 @@ def sensor(self): :type: `int` """, - valid_set=range(8) + valid_set=range(8), ) diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index bc7dc7570..ea26b6ccc 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -12,9 +12,7 @@ from instruments.generic_scpi.scpi_instrument import SCPIInstrument from instruments.units import ureg as u -from instruments.util_fns import ( - ProxyList, assume_units, split_unit_str -) +from instruments.util_fns import ProxyList, assume_units, split_unit_str # CLASSES ##################################################################### @@ -65,8 +63,11 @@ def __init__(self, filelike): self.TriggerMode = self._TriggerModeOld def _ack_expected(self, msg=""): - return msg if self._ack_on and self.firmware[0] >= 2 and \ - self.firmware[1] > 1 else None + return ( + msg + if self._ack_on and self.firmware[0] >= 2 and self.firmware[1] > 1 + else None + ) # ENUMS # @@ -74,6 +75,7 @@ class _TriggerModeNew(Enum): """ Enum containing valid trigger modes for the CC1 """ + continuous = "MODE CONT" start_stop = "MODE STOP" @@ -81,6 +83,7 @@ class _TriggerModeOld(Enum): """ Enum containing valid trigger modes for the CC1 """ + continuous = "0" start_stop = "1" @@ -92,11 +95,7 @@ class Channel: Class representing a channel on the Qubitekk CC1. """ - __CHANNEL_NAMES = { - 1: 'C1', - 2: 'C2', - 3: 'CO' - } + __CHANNEL_NAMES = {1: "C1", 2: "C2", 3: "CO"} def __init__(self, cc1, idx): self._cc1 = cc1 @@ -130,8 +129,7 @@ def count(self): tries -= 1 if tries == 0: - raise IOError(f"Could not read the count of channel " - f"{self._chan}.") + raise IOError(f"Could not read the count of channel " f"{self._chan}.") self._count = count return self._count @@ -161,8 +159,9 @@ def acknowledge(self, new_val): self.sendcmd(":ACKN ON") self._ack_on = True else: - raise NotImplementedError("Acknowledge message not implemented in " - "this version.") + raise NotImplementedError( + "Acknowledge message not implemented in " "this version." + ) @property def gate(self): @@ -176,8 +175,7 @@ def gate(self): @gate.setter def gate(self, newval): if not isinstance(newval, bool): - raise TypeError("Bool properties must be specified with a " - "boolean value") + raise TypeError("Bool properties must be specified with a " "boolean value") self.sendcmd( self._set_fmt.format("GATE", self._bool[0] if newval else self._bool[1]) ) @@ -194,8 +192,7 @@ def subtract(self): @subtract.setter def subtract(self, newval): if not isinstance(newval, bool): - raise TypeError("Bool properties must be specified with a " - "boolean value") + raise TypeError("Bool properties must be specified with a " "boolean value") self.sendcmd( self._set_fmt.format("SUBT", self._bool[0] if newval else self._bool[1]) ) @@ -255,11 +252,11 @@ def delay(self): @delay.setter def delay(self, new_val): new_val = assume_units(new_val, u.ns).to(u.ns) - if new_val < 0*u.ns or new_val > 14*u.ns: + if new_val < 0 * u.ns or new_val > 14 * u.ns: raise ValueError("New delay value is out of bounds.") if new_val.magnitude % 2 != 0: raise ValueError("New magnitude must be an even number") - self.sendcmd(":DELA "+str(int(new_val.magnitude))) + self.sendcmd(":DELA " + str(int(new_val.magnitude))) @property def dwell_time(self): @@ -275,7 +272,7 @@ def dwell_time(self): # dwell time as being seconds rather than ms dwell_time = u.Quantity(*split_unit_str(self.query("DWEL?"), "s")) if self.firmware[0] <= 2 and self.firmware[1] <= 1: - return dwell_time/1000.0 + return dwell_time / 1000.0 return dwell_time @@ -304,7 +301,7 @@ def firmware(self): else: value = self._firmware.replace("Firmware v", "").split(".") if len(value) < 3: - for _ in range(3-len(value)): + for _ in range(3 - len(value)): value.append(0) value = tuple(map(int, value)) self._firmware = value diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index c08e05b92..e3e182a24 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -13,7 +13,10 @@ from instruments.abstract_instruments import Instrument from instruments.units import ureg as u from instruments.util_fns import ( - int_property, enum_property, unitful_property, assume_units + int_property, + enum_property, + unitful_property, + assume_units, ) # CLASSES ##################################################################### @@ -24,12 +27,13 @@ class MC1(Instrument): The MC1 is a controller for the qubitekk motor controller. Used with a linear actuator to perform a HOM dip. """ + def __init__(self, filelike): super(MC1, self).__init__(filelike) self.terminator = "\r" - self._increment = 1*u.ms - self._lower_limit = -300*u.ms - self._upper_limit = 300*u.ms + self._increment = 1 * u.ms + self._lower_limit = -300 * u.ms + self._upper_limit = 300 * u.ms self._firmware = None self._controller = None @@ -39,6 +43,7 @@ class MotorType(Enum): """ Enum for the motor types for the MC1 """ + radio = "Radio" relay = "Relay" @@ -96,7 +101,7 @@ def upper_limit(self, newval): :units: milliseconds """, units=u.ms, - readonly=True + readonly=True, ) inertia = unitful_property( @@ -108,10 +113,10 @@ def upper_limit(self, newval): :type: `~pint.Quantity` :units: milliseconds """, - format_code='{:.0f}', + format_code="{:.0f}", units=u.ms, - valid_range=(0*u.ms, 100*u.ms), - set_fmt=":{} {}" + valid_range=(0 * u.ms, 100 * u.ms), + set_fmt=":{} {}", ) @property @@ -125,7 +130,7 @@ def internal_position(self): :type: `~pint.Quantity` :units: milliseconds """ - response = int(self.query("POSI?"))*self.step_size + response = int(self.query("POSI?")) * self.step_size return response metric_position = unitful_property( @@ -137,7 +142,7 @@ def internal_position(self): :units: millimeters """, units=u.mm, - readonly=True + readonly=True, ) setting = int_property( @@ -150,7 +155,7 @@ def internal_position(self): :type: `int` """, valid_set=range(2), - set_fmt=":{} {}" + set_fmt=":{} {}", ) step_size = unitful_property( @@ -162,10 +167,10 @@ def internal_position(self): :type: `~pint.Quantity` :units: milliseconds """, - format_code='{:.0f}', + format_code="{:.0f}", units=u.ms, - valid_range=(1*u.ms, 100*u.ms), - set_fmt=":{} {}" + valid_range=(1 * u.ms, 100 * u.ms), + set_fmt=":{} {}", ) @property @@ -183,19 +188,19 @@ def firmware(self): self._firmware = self.query("FIRM?") value = self._firmware.split(".") if len(value) < 3: - for _ in range(3-len(value)): + for _ in range(3 - len(value)): value.append(0) value = tuple(map(int, value)) self._firmware = value return self._firmware controller = enum_property( - 'MOTO', + "MOTO", MotorType, doc=""" Get the motor controller type. """, - readonly=True + readonly=True, ) @property @@ -208,7 +213,7 @@ def move_timeout(self): :units: milliseconds """ response = int(self.query("TIME?")) - return response*self.step_size + return response * self.step_size # METHODS # @@ -244,7 +249,7 @@ def move(self, new_position): """ new_position = assume_units(new_position, u.ms).to(u.ms) if self.lower_limit <= new_position <= self.upper_limit: - clock_cycles = new_position/self.step_size + clock_cycles = new_position / self.step_size cmd = f":MOVE {int(clock_cycles)}" self.sendcmd(cmd) else: diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 965c87362..36d024eec 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -9,7 +9,9 @@ from enum import Enum from instruments.abstract_instruments import ( - Oscilloscope, OscilloscopeChannel, OscilloscopeDataSource + Oscilloscope, + OscilloscopeChannel, + OscilloscopeDataSource, ) from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList, bool_property, enum_property @@ -33,6 +35,7 @@ class AcquisitionType(Enum): """ Enum containing valid acquisition types for the Rigol DS1000 """ + normal = "NORM" average = "AVER" peak_detect = "PEAK" @@ -55,9 +58,11 @@ def name(self): def read_waveform(self, bin_format=True): # TODO: add DIG, FFT. if self.name not in ["CHAN1", "CHAN2", "DIG", "MATH", "FFT"]: - raise NotImplementedError("Rigol DS1000 series does not " - "supportreading waveforms from " - "{}.".format(self.name)) + raise NotImplementedError( + "Rigol DS1000 series does not " + "supportreading waveforms from " + "{}.".format(self.name) + ) self._parent.sendcmd(":WAV:DATA? {}".format(self.name)) data = self._parent.binblockread(2) # TODO: check width return data @@ -76,6 +81,7 @@ class Coupling(Enum): """ Enum containing valid coupling modes for the Rigol DS1000 """ + ac = "AC" dc = "DC" ground = "GND" @@ -86,7 +92,8 @@ def __init__(self, parent, idx): # Initialize as a data source with name CHAN{}. super(RigolDS1000Series.Channel, self).__init__( - self._parent, "CHAN{}".format(self._idx)) + self._parent, "CHAN{}".format(self._idx) + ) def sendcmd(self, cmd): """ @@ -156,7 +163,7 @@ def acquire_averages(self): @acquire_averages.setter def acquire_averages(self, newval): - if newval not in [2**i for i in range(1, 9)]: + if newval not in [2 ** i for i in range(1, 9)]: raise ValueError( "Number of averages {} not supported by instrument; " "must be a power of 2 from 2 to 256.".format(newval) @@ -194,8 +201,7 @@ def stop(self): # # Many of the :KEY: commands are not yet implemented as methods. - panel_locked = bool_property(":KEY:LOCK", inst_true="ENAB", - inst_false="DIS") + panel_locked = bool_property(":KEY:LOCK", inst_true="ENAB", inst_false="DIS") def release_panel(self): # TODO: better name? diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index 406f044cc..da02df13a 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -32,6 +32,7 @@ class SRS345(SCPIInstrument, FunctionGenerator): >>> print(srs.offset) >>> srs.function = srs.Function.triangle """ + # FIXME: need to add OUTX 1 here, but doing so seems to cause a syntax # error on the instrument. @@ -39,26 +40,21 @@ class SRS345(SCPIInstrument, FunctionGenerator): _UNIT_MNEMONICS = { FunctionGenerator.VoltageMode.peak_to_peak: "VP", - FunctionGenerator.VoltageMode.rms: "VR", - FunctionGenerator.VoltageMode.dBm: "DB", + FunctionGenerator.VoltageMode.rms: "VR", + FunctionGenerator.VoltageMode.dBm: "DB", } - _MNEMONIC_UNITS = dict((mnem, unit) - for unit, mnem in _UNIT_MNEMONICS.items()) + _MNEMONIC_UNITS = dict((mnem, unit) for unit, mnem in _UNIT_MNEMONICS.items()) # FunctionGenerator CONTRACT # def _get_amplitude_(self): resp = self.query("AMPL?").strip() - return ( - float(resp[:-2]), - self._MNEMONIC_UNITS[resp[-2:]] - ) + return (float(resp[:-2]), self._MNEMONIC_UNITS[resp[-2:]]) def _set_amplitude_(self, magnitude, units): - self.sendcmd( - "AMPL {}{}".format(magnitude, self._UNIT_MNEMONICS[units])) + self.sendcmd("AMPL {}{}".format(magnitude, self._UNIT_MNEMONICS[units])) # ENUMS ## @@ -66,6 +62,7 @@ class Function(IntEnum): """ Enum containing valid output function modes for the SRS 345 """ + sinusoid = 0 square = 1 triangle = 2 @@ -83,7 +80,7 @@ class Function(IntEnum): :units: As specified, or assumed to be :math:`\\text{Hz}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) function = enum_property( @@ -94,7 +91,7 @@ class Function(IntEnum): Gets/sets the output function of the function generator. :type: `~SRS345.Function` - """ + """, ) offset = unitful_property( @@ -105,7 +102,7 @@ class Function(IntEnum): :units: As specified, or assumed to be :math:`\\text{V}` otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) phase = unitful_property( @@ -117,5 +114,5 @@ class Function(IntEnum): :units: As specified, or assumed to be degrees (:math:`{}^{\\circ}`) otherwise. :type: `float` or `~pint.Quantity` - """ + """, ) diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index f9b76556a..616f40b57 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -15,18 +15,21 @@ from instruments.abstract_instruments.comm import ( GPIBCommunicator, SerialCommunicator, - LoopbackCommunicator + LoopbackCommunicator, ) from instruments.generic_scpi import SCPIInstrument from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import ( - bool_property, bounded_unitful_property, enum_property, unitful_property + bool_property, + bounded_unitful_property, + enum_property, + unitful_property, ) # CONSTANTS ################################################################### -VALID_SAMPLE_RATES = [2.0**n for n in range(-4, 10)] +VALID_SAMPLE_RATES = [2.0 ** n for n in range(-4, 10)] VALID_SAMPLE_RATES += ["trigger"] # CLASSES ##################################################################### @@ -68,8 +71,12 @@ def __init__(self, filelike, outx_mode=None): elif isinstance(self._file, LoopbackCommunicator): pass else: - warnings.warn("OUTX command has not been set. Instrument " - "behaviour is unknown.", UserWarning) + warnings.warn( + "OUTX command has not been set. Instrument " + "behaviour is unknown.", + UserWarning, + ) + # ENUMS # class FreqSource(IntEnum): @@ -77,6 +84,7 @@ class FreqSource(IntEnum): """ Enum for the SRS830 frequency source settings. """ + external = 0 internal = 1 @@ -85,6 +93,7 @@ class Coupling(IntEnum): """ Enum for the SRS830 channel coupling settings. """ + ac = 0 dc = 1 @@ -92,6 +101,7 @@ class BufferMode(IntEnum): """ Enum for the SRS830 buffer modes. """ + one_shot = 0 loop = 1 @@ -99,6 +109,7 @@ class Mode(Enum): """ Enum containing valid modes for the SRS 830 """ + x = "x" y = "y" r = "r" @@ -129,7 +140,7 @@ class Mode(Enum): or uses the internal reference. :type: `SRS830.FreqSource` - """ + """, ) frequency = unitful_property( @@ -142,7 +153,7 @@ class Mode(Enum): :units: As specified (if a `~pint.Quantity`) or assumed to be of units Hertz. :type: `~pint.Quantity` with units Hertz. - """ + """, ) phase, phase_min, phase_max = bounded_unitful_property( @@ -157,7 +168,7 @@ class Mode(Enum): :units: As specified (if a `~pint.Quantity`) or assumed to be of units degrees. :type: `~pint.Quantity` with units degrees. - """ + """, ) amplitude, amplitude_min, amplitude_max = bounded_unitful_property( @@ -172,7 +183,7 @@ class Mode(Enum): :units: As specified (if a `~pint.Quantity`) or assumed to be of units volts. Value should be specified as peak-to-peak. :type: `~pint.Quantity` with units volts peak-to-peak. - """ + """, ) input_shield_ground = bool_property( @@ -183,7 +194,7 @@ class Mode(Enum): Function sets the input shield grounding to either 'float' or 'ground'. :type: `bool` - """ + """, ) coupling = enum_property( @@ -194,7 +205,7 @@ class Mode(Enum): Gets/sets the input coupling to either 'ac' or 'dc'. :type: `SRS830.Coupling` - """ + """, ) @property @@ -207,7 +218,7 @@ def sample_rate(self): :type: `~pint.Quantity` with units Hertz. """ - value = int(self.query('SRAT?')) + value = int(self.query("SRAT?")) if value == 14: return "trigger" return u.Quantity(VALID_SAMPLE_RATES[value], u.Hz) @@ -218,10 +229,12 @@ def sample_rate(self, newval): newval = newval.lower() if newval in VALID_SAMPLE_RATES: - self.sendcmd('SRAT {}'.format(VALID_SAMPLE_RATES.index(newval))) + self.sendcmd("SRAT {}".format(VALID_SAMPLE_RATES.index(newval))) else: - raise ValueError('Valid samples rates given by {} ' - 'and "trigger".'.format(VALID_SAMPLE_RATES)) + raise ValueError( + "Valid samples rates given by {} " + 'and "trigger".'.format(VALID_SAMPLE_RATES) + ) buffer_mode = enum_property( "SEND", @@ -235,7 +248,7 @@ def sample_rate(self, newval): will repeat from the start. :type: `SRS830.BufferMode` - """ + """, ) @property @@ -248,12 +261,11 @@ def num_data_points(self): resp = None i = 0 while not resp and i < 10: - resp = self.query('SPTS?').strip() + resp = self.query("SPTS?").strip() i += 1 if not resp: raise IOError( - "Expected integer response from instrument, got {}".format( - repr(resp)) + "Expected integer response from instrument, got {}".format(repr(resp)) ) return int(resp) @@ -269,7 +281,7 @@ def num_data_points(self): other, FAST1, is for legacy systems which this package does not support. :type: `bool` - """ + """, ) # AUTO- METHODS # @@ -290,11 +302,11 @@ def auto_offset(self, mode): mode = SRS830.Mode[mode] if mode not in self._XYR_MODE_MAP: - raise ValueError('Specified mode not valid for this function.') + raise ValueError("Specified mode not valid for this function.") mode = self._XYR_MODE_MAP[mode] - self.sendcmd('AOFF {}'.format(mode)) + self.sendcmd("AOFF {}".format(mode)) def auto_phase(self): """ @@ -304,7 +316,7 @@ def auto_phase(self): Do not send this message again without waiting the correct amount of time for the lock-in to finish. """ - self.sendcmd('APHS') + self.sendcmd("APHS") # META-METHODS # @@ -356,11 +368,11 @@ def take_measurement(self, sample_rate, num_samples): or if numpy is installed, `numpy.array`[`numpy.array`, `numpy.array`] """ if num_samples > 16383: - raise ValueError('Number of samples cannot exceed 16383.') + raise ValueError("Number of samples cannot exceed 16383.") sample_time = math.ceil(num_samples / sample_rate) - self.init(sample_rate, SRS830.BufferMode['one_shot']) + self.init(sample_rate, SRS830.BufferMode["one_shot"]) self.start_data_transfer() time.sleep(sample_time + 0.1) @@ -376,8 +388,8 @@ def take_measurement(self, sample_rate, num_samples): except IOError: pass - ch1 = self.read_data_buffer('ch1') - ch2 = self.read_data_buffer('ch2') + ch1 = self.read_data_buffer("ch1") + ch2 = self.read_data_buffer("ch2") if numpy: return numpy.array([ch1, ch2]) @@ -406,25 +418,25 @@ def set_offset_expand(self, mode, offset, expand): mode = SRS830.Mode[mode] if mode not in self._XYR_MODE_MAP: - raise ValueError('Specified mode not valid for this function.') + raise ValueError("Specified mode not valid for this function.") mode = self._XYR_MODE_MAP[mode] if not isinstance(offset, (int, float)): - raise TypeError('Offset parameter must be an integer or a float.') + raise TypeError("Offset parameter must be an integer or a float.") if not isinstance(expand, (int, float)): - raise TypeError('Expand parameter must be an integer or a float.') + raise TypeError("Expand parameter must be an integer or a float.") if (offset > 105) or (offset < -105): - raise ValueError('Offset mustbe -105 <= offset <= +105.') + raise ValueError("Offset mustbe -105 <= offset <= +105.") valid = [1, 10, 100] if expand in valid: expand = valid.index(expand) else: - raise ValueError('Expand must be 1, 10, 100.') + raise ValueError("Expand must be 1, 10, 100.") - self.sendcmd('OEXP {},{},{}'.format(mode, int(offset), expand)) + self.sendcmd("OEXP {},{},{}".format(mode, int(offset), expand)) def start_scan(self): """ @@ -432,18 +444,26 @@ def start_scan(self): this is used to start the scan. The scan starts after a delay of 0.5 seconds. """ - self.sendcmd('STRD') + self.sendcmd("STRD") def pause(self): """ Has the instrument pause data capture. """ - self.sendcmd('PAUS') + self.sendcmd("PAUS") _data_snap_modes = { - Mode.x: 1, Mode.y: 2, Mode.r: 3, Mode.theta: 4, Mode.aux1: 5, - Mode.aux2: 6, Mode.aux3: 7, Mode.aux4: 8, Mode.ref: 9, - Mode.ch1: 10, Mode.ch2: 11 + Mode.x: 1, + Mode.y: 2, + Mode.r: 3, + Mode.theta: 4, + Mode.aux1: 5, + Mode.aux2: 6, + Mode.aux3: 7, + Mode.aux4: 8, + Mode.ref: 9, + Mode.ch1: 10, + Mode.ch2: 11, } def data_snap(self, mode1, mode2): @@ -474,19 +494,17 @@ def data_snap(self, mode1, mode2): mode2 = mode2.lower() mode2 = SRS830.Mode[mode2] - if ((mode1 not in self._data_snap_modes) or - (mode2 not in self._data_snap_modes)): - raise ValueError('Specified mode not valid for this function.') + if (mode1 not in self._data_snap_modes) or (mode2 not in self._data_snap_modes): + raise ValueError("Specified mode not valid for this function.") mode1 = self._XYR_MODE_MAP[mode1] mode2 = self._XYR_MODE_MAP[mode2] if mode1 == mode2: - raise ValueError('Both parameters for the data snapshot are the ' - 'same.') + raise ValueError("Both parameters for the data snapshot are the " "same.") - result = self.query('SNAP? {},{}'.format(mode1, mode2)) - return list(map(float, result.split(','))) + result = self.query("SNAP? {},{}".format(mode1, mode2)) + return list(map(float, result.split(","))) _valid_read_data_buffer = {Mode.ch1: 1, Mode.ch2: 2} @@ -509,7 +527,7 @@ def read_data_buffer(self, channel): channel = SRS830.Mode[channel] if channel not in self._valid_read_data_buffer: - raise ValueError('Specified mode not valid for this function.') + raise ValueError("Specified mode not valid for this function.") channel = self._valid_read_data_buffer[channel] @@ -518,30 +536,31 @@ def read_data_buffer(self, channel): # Query device for entire buffer, returning in ASCII, then # converting to a list of floats before returning to the # calling method - data = self.query('TRCA?{},0,{}'.format(channel, N)).strip() + data = self.query("TRCA?{},0,{}".format(channel, N)).strip() if numpy: - return numpy.fromstring(data, sep=',') + return numpy.fromstring(data, sep=",") return tuple(map(float, data.split(","))) def clear_data_buffer(self): """ Clears the data buffer of the SRS830. """ - self.sendcmd('REST') + self.sendcmd("REST") _valid_channel_display = [ - { # channel1 - Mode.x: 0, Mode.r: 1, Mode.xnoise: 2, Mode.aux1: 3, Mode.aux2: 4 - }, + {Mode.x: 0, Mode.r: 1, Mode.xnoise: 2, Mode.aux1: 3, Mode.aux2: 4}, # channel1 { # channel2 - Mode.y: 0, Mode.theta: 1, Mode.ynoise: 2, Mode.aux3: 3, - Mode.aux4: 4 - } + Mode.y: 0, + Mode.theta: 1, + Mode.ynoise: 2, + Mode.aux3: 3, + Mode.aux4: 4, + }, ] _valid_channel_ratio = [ {Mode.none: 0, Mode.aux1: 1, Mode.aux2: 2}, # channel1 - {Mode.none: 0, Mode.aux3: 1, Mode.aux4: 2} # channel2 + {Mode.none: 0, Mode.aux3: 1, Mode.aux4: 2}, # channel2 ] _valid_channel = {Mode.ch1: 1, Mode.ch2: 2} @@ -578,18 +597,16 @@ def set_channel_display(self, channel, display, ratio): ratio = SRS830.Mode[ratio] if channel not in self._valid_channel: - raise ValueError('Specified channel not valid for this function.') + raise ValueError("Specified channel not valid for this function.") channel = self._valid_channel[channel] if display not in self._valid_channel_display[channel - 1]: - raise ValueError('Specified display mode not valid for this ' - 'function.') + raise ValueError("Specified display mode not valid for this " "function.") if ratio not in self._valid_channel_ratio[channel - 1]: - raise ValueError('Specified display ratio not valid for this ' - 'function.') + raise ValueError("Specified display ratio not valid for this " "function.") display = self._valid_channel_display[channel - 1][display] ratio = self._valid_channel_ratio[channel - 1][ratio] - self.sendcmd('DDEF {},{},{}'.format(channel, display, ratio)) + self.sendcmd("DDEF {},{},{}".format(channel, display, ratio)) diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 65b45e58f..7cfa43077 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -30,18 +30,15 @@ def __init__(self, filelike): # DICTIONARIES # - _BOOL_NAMES = { - 'On': True, - 'Off': False - } + _BOOL_NAMES = {"On": True, "Off": False} # Note that the SRS CTC-100 uses '\xb0' to represent '°'. _UNIT_NAMES = { - '\xb0C': u.celsius, - 'W': u.watt, - 'V': u.volt, - '\xea': u.ohm, - '': u.dimensionless + "\xb0C": u.celsius, + "W": u.watt, + "V": u.volt, + "\xea": u.ohm, + "": u.dimensionless, } # INNER CLASSES ## @@ -50,10 +47,11 @@ class SensorType(Enum): """ Enum containing valid sensor types for the SRS CTC-100 """ - rtd = 'RTD' - thermistor = 'Thermistor' - diode = 'Diode' - rox = 'ROX' + + rtd = "RTD" + thermistor = "Thermistor" + diode = "Diode" + rox = "ROX" class Channel: @@ -75,14 +73,10 @@ def __init__(self, ctc, chan_name): # PRIVATE METHODS # def _get(self, prop_name): - return self._ctc.query("{}.{}?".format( - self._rem_name, - prop_name - )).strip() + return self._ctc.query("{}.{}?".format(self._rem_name, prop_name)).strip() def _set(self, prop_name, newval): - self._ctc.sendcmd( - '{}.{} = "{}"'.format(self._rem_name, prop_name, newval)) + self._ctc.sendcmd('{}.{} = "{}"'.format(self._rem_name, prop_name, newval)) # DISPLAY AND PROGRAMMING # # These properties control how the channel is identified in scripts @@ -101,7 +95,7 @@ def name(self): @name.setter def name(self, newval): - self._set('name', newval) + self._set("name", newval) # TODO: check for errors! self._chan_name = newval self._rem_name = newval.replace(" ", "") @@ -120,10 +114,7 @@ def value(self): # WARNING: Queries all units all the time. # TODO: Make an OutputChannel that subclasses this class, # and add a setter for value. - return u.Quantity( - float(self._get('value')), - self.units - ) + return u.Quantity(float(self._get("value")), self.units) @property def units(self): @@ -149,7 +140,7 @@ def sensor_type(self): :type: `SRSCTC100.SensorType` """ - return self._ctc.SensorType(self._get('sensor')) + return self._ctc.SensorType(self._get("sensor")) # STATS # # The following properties control and query the statistics of the @@ -162,12 +153,12 @@ def stats_enabled(self): :type: `bool` """ - return True if self._get('stats') == 'On' else False + return True if self._get("stats") == "On" else False @stats_enabled.setter def stats_enabled(self, newval): # FIXME: replace with bool_property factory - self._set('stats', 'On' if newval else 'Off') + self._set("stats", "On" if newval else "Off") @property def stats_points(self): @@ -177,11 +168,11 @@ def stats_points(self): :type: `int` """ - return int(self._get('points')) + return int(self._get("points")) @stats_points.setter def stats_points(self, newval): - self._set('points', int(newval)) + self._set("points", int(newval)) @property def average(self): @@ -191,10 +182,7 @@ def average(self): :type: `~pint.Quantity` """ - return u.Quantity( - float(self._get('average')), - self.units - ) + return u.Quantity(float(self._get("average")), self.units) @property def std_dev(self): @@ -204,14 +192,11 @@ def std_dev(self): :type: `~pint.Quantity` """ - return u.Quantity( - float(self._get('SD')), - self.units - ) + return u.Quantity(float(self._get("SD")), self.units) # LOGGING # - def get_log_point(self, which='next', units=None): + def get_log_point(self, which="next", units=None): """ Get a log data point from the instrument. @@ -229,13 +214,12 @@ def get_log_point(self, which='next', units=None): units = self.units point = [ - s.strip() for s in - self._ctc.query( - 'getLog.xy {}, {}'.format(self._chan_name, which) - ).split(',') + s.strip() + for s in self._ctc.query( + "getLog.xy {}, {}".format(self._chan_name, which) + ).split(",") ] - return u.Quantity(float(point[0]), 'ms'), \ - u.Quantity(float(point[1]), units) + return u.Quantity(float(point[0]), "ms"), u.Quantity(float(point[1]), units) def get_log(self): """ @@ -252,8 +236,7 @@ def get_log(self): units = self.units # Find out how many points there are. - n_points = int( - self._ctc.query('getLog.xy? {}'.format(self._chan_name))) + n_points = int(self._ctc.query("getLog.xy? {}".format(self._chan_name))) # Make an empty quantity that size for the times and for the channel # values. @@ -267,9 +250,9 @@ def get_log(self): # Reset the position to the first point, then save it. # pylint: disable=protected-access with self._ctc._error_checking_disabled(): - ts[0], temps[0] = self.get_log_point('first', units) + ts[0], temps[0] = self.get_log_point("first", units) for idx in range(1, n_points): - ts[idx], temps[idx] = self.get_log_point('next', units) + ts[idx], temps[idx] = self.get_log_point("next", units) # Do an actual error check now. if self._ctc.error_check_toggle: @@ -302,10 +285,7 @@ def _channel_names(self): # As a consequence, users of this instrument MUST use spaces # matching the pretty name and not the remote-programming name. # CG could not think of a good way around this. - names = [ - name.strip() - for name in self.query('getOutput.names?').split(',') - ] + names = [name.strip() for name in self.query("getOutput.names?").split(",")] return names def channel_units(self): @@ -318,8 +298,7 @@ def channel_units(self): :rtype: `dict` with channel names as keys and units as values """ unit_strings = [ - unit_str.strip() - for unit_str in self.query('getOutput.units?').split(',') + unit_str.strip() for unit_str in self.query("getOutput.units?").split(",") ] return dict( (chan_name, self._UNIT_NAMES[unit_str]) @@ -334,8 +313,8 @@ def errcheck(self): :return: Nothing """ - errs = super(SRSCTC100, self).query('geterror?').strip() - err_code, err_descript = errs.split(',') + errs = super(SRSCTC100, self).query("geterror?").strip() + err_code, err_descript = errs.split(",") err_code = int(err_code) if err_code == 0: return err_code @@ -376,14 +355,16 @@ def display_figures(self): :type: `int` """ - return int(self.query('system.display.figures?')) + return int(self.query("system.display.figures?")) @display_figures.setter def display_figures(self, newval): if newval not in range(7): - raise ValueError("Number of display figures must be an integer " - "from 0 to 6, inclusive.") - self.sendcmd('system.display.figures = {}'.format(newval)) + raise ValueError( + "Number of display figures must be an integer " + "from 0 to 6, inclusive." + ) + self.sendcmd("system.display.figures = {}".format(newval)) @property def error_check_toggle(self): @@ -423,4 +404,4 @@ def clear_log(self): Not sure if this works. """ - self.sendcmd('System.Log.Clear yes') + self.sendcmd("System.Log.Clear yes") diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index c8e938874..33678d27f 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -65,11 +65,11 @@ def delay(self): @delay.setter def delay(self, newval): newval = (newval[0], assume_units(newval[1], u.s)) - self._ddg.sendcmd("DLAY {},{},{}".format( - int(self._chan), - int(newval[0].idx), - newval[1].to("s").magnitude - )) + self._ddg.sendcmd( + "DLAY {},{},{}".format( + int(self._chan), int(newval[0].idx), newval[1].to("s").magnitude + ) + ) class SRSDG645(SCPIInstrument): @@ -103,6 +103,7 @@ class LevelPolarity(IntEnum): """ Polarities for output levels. """ + positive = 1 negative = 0 @@ -111,6 +112,7 @@ class Outputs(IntEnum): """ Enumeration of valid outputs from the DDG. """ + T0 = 0 AB = 1 CD = 2 @@ -122,6 +124,7 @@ class Channels(IntEnum): """ Enumeration of valid delay channels for the DDG. """ + T0 = 0 T1 = 1 A = 2 @@ -138,6 +141,7 @@ class DisplayMode(IntEnum): """ Enumeration of possible modes for the physical front-panel display. """ + trigger_rate = 0 trigger_threshold = 1 trigger_single_shot = 2 @@ -159,6 +163,7 @@ class TriggerSource(IntEnum): """ Enumeration of the different allowed trigger sources and modes. """ + internal = 0 external_rising = 1 external_falling = 2 @@ -193,12 +198,12 @@ def polarity(self): @polarity.setter def polarity(self, newval): if not isinstance(newval, self._parent.LevelPolarity): - raise TypeError("Mode must be specified as a " - "SRSDG645.LevelPolarity value, got {} " - "instead.".format(type(newval))) - self._parent.sendcmd("LPOL {},{}".format( - self._idx, int(newval.value) - )) + raise TypeError( + "Mode must be specified as a " + "SRSDG645.LevelPolarity value, got {} " + "instead.".format(type(newval)) + ) + self._parent.sendcmd("LPOL {},{}".format(self._idx, int(newval.value))) @property def level_amplitude(self): @@ -209,13 +214,12 @@ def level_amplitude(self): :units: As specified, or :math:`\\text{V}` by default. """ return u.Quantity( - float(self._parent.query('LAMP? {}'.format(self._idx))), - 'V' + float(self._parent.query("LAMP? {}".format(self._idx))), "V" ) @level_amplitude.setter def level_amplitude(self, newval): - newval = assume_units(newval, 'V').magnitude + newval = assume_units(newval, "V").magnitude self._parent.sendcmd("LAMP {},{}".format(self._idx, newval)) @property @@ -227,13 +231,12 @@ def level_offset(self): :units: As specified, or :math:`\\text{V}` by default. """ return u.Quantity( - float(self._parent.query('LOFF? {}'.format(self._idx))), - 'V' + float(self._parent.query("LOFF? {}".format(self._idx))), "V" ) @level_offset.setter def level_offset(self, newval): - newval = assume_units(newval, 'V').magnitude + newval = assume_units(newval, "V").magnitude self._parent.sendcmd("LOFF {},{}".format(self._idx, newval)) # PROPERTIES # diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index ccf7cb7b7..77ac24447 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -64,16 +64,16 @@ def amplitude(self): :type: `~pint.Quantity` with units Volts peak-to-peak. """ return u.Quantity( - float(self._tek.query("FG:{}:AMPL?".format(self._name)).strip()), - u.V + float(self._tek.query("FG:{}:AMPL?".format(self._name)).strip()), u.V ) @amplitude.setter def amplitude(self, newval): - self._tek.sendcmd("FG:{}:AMPL {}".format( - self._name, - assume_units(newval, u.V).to(u.V).magnitude - )) + self._tek.sendcmd( + "FG:{}:AMPL {}".format( + self._name, assume_units(newval, u.V).to(u.V).magnitude + ) + ) @property def offset(self): @@ -85,16 +85,16 @@ def offset(self): :type: `~pint.Quantity` with units Volts. """ return u.Quantity( - float(self._tek.query("FG:{}:OFFS?".format(self._name)).strip()), - u.V + float(self._tek.query("FG:{}:OFFS?".format(self._name)).strip()), u.V ) @offset.setter def offset(self, newval): - self._tek.sendcmd("FG:{}:OFFS {}".format( - self._name, - assume_units(newval, u.V).to(u.V).magnitude - )) + self._tek.sendcmd( + "FG:{}:OFFS {}".format( + self._name, assume_units(newval, u.V).to(u.V).magnitude + ) + ) @property def frequency(self): @@ -106,16 +106,13 @@ def frequency(self): of units Hertz. :type: `~pint.Quantity` with units Hertz. """ - return u.Quantity( - float(self._tek.query("FG:FREQ?").strip()), - u.Hz - ) + return u.Quantity(float(self._tek.query("FG:FREQ?").strip()), u.Hz) @frequency.setter def frequency(self, newval): - self._tek.sendcmd("FG:FREQ {}HZ".format( - assume_units(newval, u.Hz).to(u.Hz).magnitude - )) + self._tek.sendcmd( + "FG:FREQ {}HZ".format(assume_units(newval, u.Hz).to(u.Hz).magnitude) + ) @property def polarity(self): @@ -124,15 +121,18 @@ def polarity(self): :type: `TekAWG2000.Polarity` """ - return TekAWG2000.Polarity(self._tek.query("FG:{}:POL?".format( - self._name)).strip()) + return TekAWG2000.Polarity( + self._tek.query("FG:{}:POL?".format(self._name)).strip() + ) @polarity.setter def polarity(self, newval): if not isinstance(newval, TekAWG2000.Polarity): - raise TypeError("Polarity settings must be a " - "`TekAWG2000.Polarity` value, got {} " - "instead.".format(type(newval))) + raise TypeError( + "Polarity settings must be a " + "`TekAWG2000.Polarity` value, got {} " + "instead.".format(type(newval)) + ) self._tek.sendcmd("FG:{}:POL {}".format(self._name, newval.value)) @@ -144,14 +144,17 @@ def shape(self): :type: `TekAWG2000.Shape` """ - return TekAWG2000.Shape(self._tek.query("FG:{}:SHAP?".format( - self._name)).strip().split(',')[0]) + return TekAWG2000.Shape( + self._tek.query("FG:{}:SHAP?".format(self._name)).strip().split(",")[0] + ) @shape.setter def shape(self, newval): if not isinstance(newval, TekAWG2000.Shape): - raise TypeError("Shape settings must be a `TekAWG2000.Shape` " - "value, got {} instead.".format(type(newval))) + raise TypeError( + "Shape settings must be a `TekAWG2000.Shape` " + "value, got {} instead.".format(type(newval)) + ) self._tek.sendcmd("FG:{}:SHAP {}".format(self._name, newval.value)) # ENUMS # @@ -160,6 +163,7 @@ class Polarity(Enum): """ Enum containing valid polarity modes for the AWG2000 """ + normal = "NORMAL" inverted = "INVERTED" @@ -167,6 +171,7 @@ class Shape(Enum): """ Enum containing valid waveform shape modes for hte AWG2000 """ + sine = "SINUSOID" pulse = "PULSE" ramp = "RAMP" @@ -231,8 +236,10 @@ def upload_waveform(self, yzero, ymult, xincr, waveform): exceed 1. """ if numpy is None: - raise ImportError("Missing optional dependency numpy, which is required" - "for uploading waveforms.") + raise ImportError( + "Missing optional dependency numpy, which is required" + "for uploading waveforms." + ) if not isinstance(yzero, float) and not isinstance(yzero, int): raise TypeError("yzero must be specified as a float or int") @@ -253,7 +260,7 @@ def upload_waveform(self, yzero, ymult, xincr, waveform): self.sendcmd("WFMP:YMULT {}".format(ymult)) self.sendcmd("WFMP:XINCR {}".format(xincr)) - waveform *= (2**12 - 1) + waveform *= 2 ** 12 - 1 waveform = waveform.astype("", - TekDPO70000.ByteOrder.little_endian: "<" - }[byte_order], (n_bytes if n_bytes is not None else ""), { - TekDPO70000.BinaryFormat.int: "i", - TekDPO70000.BinaryFormat.uint: "u", - TekDPO70000.BinaryFormat.float: "f" - }[binary_format]) + return "{}{}{}".format( + { + TekDPO70000.ByteOrder.big_endian: ">", + TekDPO70000.ByteOrder.little_endian: "<", + }[byte_order], + (n_bytes if n_bytes is not None else ""), + { + TekDPO70000.BinaryFormat.int: "i", + TekDPO70000.BinaryFormat.uint: "u", + TekDPO70000.BinaryFormat.float: "f", + }[binary_format], + ) # CLASSES # @@ -189,7 +209,7 @@ def read_waveform(self, bin_format=True): dtype = self._parent._dtype( self._parent.outgoing_binary_format, self._parent.outgoing_byte_order, - n_bytes=None + n_bytes=None, ) self._parent.sendcmd("CURV?") raw = self._parent.binblockread(n_bytes, fmt=dtype) @@ -227,10 +247,7 @@ def __init__(self, parent, idx): self._idx = idx + 1 # 1-based. # Initialize as a data source with name MATH{}. - super(TekDPO70000.Math, self).__init__( - parent, - "MATH{}".format(self._idx) - ) + super(TekDPO70000.Math, self).__init__(parent, "MATH{}".format(self._idx)) def sendcmd(self, cmd): """ @@ -259,6 +276,7 @@ class FilterMode(Enum): Enum containing valid filter modes for a math channel on the TekDPO70000 series oscilloscope. """ + centered = "CENT" shifted = "SHIF" @@ -267,6 +285,7 @@ class Mag(Enum): Enum containing valid amplitude units for a math channel on the TekDPO70000 series oscilloscope. """ + linear = "LINEA" db = "DB" dbm = "DBM" @@ -276,6 +295,7 @@ class Phase(Enum): Enum containing valid phase units for a math channel on the TekDPO70000 series oscilloscope. """ + degrees = "DEG" radians = "RAD" group_delay = "GROUPD" @@ -285,6 +305,7 @@ class SpectralWindow(Enum): Enum containing valid spectral windows for a math channel on the TekDPO70000 series oscilloscope. """ + rectangular = "RECTANG" hamming = "HAMM" hanning = "HANN" @@ -298,37 +319,31 @@ class SpectralWindow(Enum): "DEF", doc=""" A text string specifying the math to do, ex. CH1+CH2 - """ + """, ) - filter_mode = enum_property( - "FILT:MOD", - FilterMode - ) + filter_mode = enum_property("FILT:MOD", FilterMode) - filter_risetime = unitful_property( - "FILT:RIS", - u.second - ) + filter_risetime = unitful_property("FILT:RIS", u.second) label = string_property( "LAB:NAM", doc=""" Just a human readable label for the channel. - """ + """, ) label_xpos = unitless_property( "LAB:XPOS", doc=""" The x position, in divisions, to place the label. - """ + """, ) label_ypos = unitless_property( "LAB:YPOS", doc="""The y position, in divisions, to place the label. - """ + """, ) num_avg = unitless_property( @@ -336,7 +351,7 @@ class SpectralWindow(Enum): doc=""" The number of acquisistions over which exponential averaging is performed. - """ + """, ) spectral_center = unitful_property( @@ -345,7 +360,7 @@ class SpectralWindow(Enum): doc=""" The desired frequency of the spectral analyzer output data span in Hz. - """ + """, ) spectral_gatepos = unitful_property( @@ -354,7 +369,7 @@ class SpectralWindow(Enum): doc=""" The gate position. Units are represented in seconds, with respect to trigger position. - """ + """, ) spectral_gatewidth = unitful_property( @@ -362,21 +377,17 @@ class SpectralWindow(Enum): u.second, doc=""" The time across the 10-division screen in seconds. - """ + """, ) - spectral_lock = bool_property( - "SPEC:LOCK", - inst_true="ON", - inst_false="OFF" - ) + spectral_lock = bool_property("SPEC:LOCK", inst_true="ON", inst_false="OFF") spectral_mag = enum_property( "SPEC:MAG", Mag, doc=""" Whether the spectral magnitude is linear, db, or dbm. - """ + """, ) spectral_phase = enum_property( @@ -384,7 +395,7 @@ class SpectralWindow(Enum): Phase, doc=""" Whether the spectral phase is degrees, radians, or group delay. - """ + """, ) spectral_reflevel = unitless_property( @@ -392,12 +403,10 @@ class SpectralWindow(Enum): doc=""" The value that represents the topmost display screen graticule. The units depend on spectral_mag. - """ + """, ) - spectral_reflevel_offset = unitless_property( - "SPEC:REFLEVELO" - ) + spectral_reflevel_offset = unitless_property("SPEC:REFLEVELO") spectral_resolution_bandwidth = unitful_property( "SPEC:RESB", @@ -405,7 +414,7 @@ class SpectralWindow(Enum): doc=""" The desired resolution bandwidth value. Units are represented in Hertz. - """ + """, ) spectral_span = unitful_property( @@ -414,7 +423,7 @@ class SpectralWindow(Enum): doc=""" Specifies the frequency span of the output data vector from the spectral analyzer. - """ + """, ) spectral_suppress = unitless_property( @@ -422,7 +431,7 @@ class SpectralWindow(Enum): doc=""" The magnitude level that data with magnitude values below this value are displayed as zero phase. - """ + """, ) spectral_unwrap = bool_property( @@ -431,27 +440,24 @@ class SpectralWindow(Enum): inst_false="OFF", doc=""" Enables or disables phase wrapping. - """ + """, ) - spectral_window = enum_property( - "SPEC:WIN", - SpectralWindow - ) + spectral_window = enum_property("SPEC:WIN", SpectralWindow) threshhold = unitful_property( "THRESH", u.volt, doc=""" The math threshhold in volts - """ + """, ) unit_string = string_property( "UNITS", doc=""" Just a label for the units...doesn"t actually change anything. - """ + """, ) autoscale = bool_property( @@ -460,14 +466,14 @@ class SpectralWindow(Enum): inst_false="OFF", doc=""" Enables or disables the auto-scaling of new math waveforms. - """ + """, ) position = unitless_property( "VERT:POS", doc=""" The vertical position, in divisions from the center graticule. - """ + """, ) scale = unitful_property( @@ -476,14 +482,15 @@ class SpectralWindow(Enum): doc=""" The scale in volts per division. The range is from ``100e-36`` to ``100e+36``. - """ + """, ) def _scale_raw_data(self, data): # TODO: incorperate the unit_string somehow if numpy: return self.scale * ( - (TekDPO70000.VERT_DIVS / 2) * data.astype(float) / (2**15) - self.position + (TekDPO70000.VERT_DIVS / 2) * data.astype(float) / (2 ** 15) + - self.position ) scale = self.scale @@ -511,8 +518,7 @@ def __init__(self, parent, idx): # Initialize as a data source with name CH{}. super(TekDPO70000.Channel, self).__init__( - self._parent, - "CH{}".format(self._idx) + self._parent, "CH{}".format(self._idx) ) def sendcmd(self, cmd): @@ -541,6 +547,7 @@ class Coupling(Enum): """ Enum containing valid coupling modes for the oscilloscope channel """ + ac = "AC" dc = "DC" dc_reject = "DCREJ" @@ -558,70 +565,61 @@ class Coupling(Enum): >>> inst = ik.tektronix.TekDPO70000.open_tcpip("192.168.0.1", 8080) >>> channel = inst.channel[0] >>> channel.coupling = channel.Coupling.ac - """ + """, ) - bandwidth = unitful_property( - 'BAN', - u.Hz - ) + bandwidth = unitful_property("BAN", u.Hz) - deskew = unitful_property( - 'DESK', - u.second - ) + deskew = unitful_property("DESK", u.second) - termination = unitful_property( - 'TERM', - u.ohm - ) + termination = unitful_property("TERM", u.ohm) label = string_property( - 'LAB:NAM', + "LAB:NAM", doc=""" Just a human readable label for the channel. - """ + """, ) label_xpos = unitless_property( - 'LAB:XPOS', + "LAB:XPOS", doc=""" The x position, in divisions, to place the label. - """ + """, ) label_ypos = unitless_property( - 'LAB:YPOS', + "LAB:YPOS", doc=""" The y position, in divisions, to place the label. - """ + """, ) offset = unitful_property( - 'OFFS', + "OFFS", u.volt, doc=""" The vertical offset in units of volts. Voltage is given by ``offset+scale*(5*raw/2^15 - position)``. - """ + """, ) position = unitless_property( - 'POS', + "POS", doc=""" The vertical position, in divisions from the center graticule, ranging from ``-8`` to ``8``. Voltage is given by ``offset+scale*(5*raw/2^15 - position)``. - """ + """, ) scale = unitful_property( - 'SCALE', + "SCALE", u.volt, doc=""" Vertical channel scale in units volts/division. Voltage is given by ``offset+scale*(5*raw/2^15 - position)``. - """ + """, ) def _scale_raw_data(self, data): @@ -630,13 +628,18 @@ def _scale_raw_data(self, data): offset = self.offset if numpy: - return scale * ( - (TekDPO70000.VERT_DIVS / 2) * - data.astype(float) / (2**15) - position - ) + offset + return ( + scale + * ( + (TekDPO70000.VERT_DIVS / 2) * data.astype(float) / (2 ** 15) + - position + ) + + offset + ) return tuple( - scale * ((TekDPO70000.VERT_DIVS / 2) * d / (2 ** 15) - position) + offset + scale * ((TekDPO70000.VERT_DIVS / 2) * d / (2 ** 15) - position) + + offset for d in map(float, data) ) @@ -657,134 +660,116 @@ def ref(self): # For some settings that probably won't be used that often, use # string_property instead of setting up an enum property. acquire_enhanced_enob = string_property( - 'ACQ:ENHANCEDE', - bookmark_symbol='', + "ACQ:ENHANCEDE", + bookmark_symbol="", doc=""" Valid values are AUTO and OFF. - """ + """, ) acquire_enhanced_state = bool_property( - 'ACQ:ENHANCEDE:STATE', - inst_false='0', # TODO: double check that these are correct - inst_true='1' + "ACQ:ENHANCEDE:STATE", + inst_false="0", # TODO: double check that these are correct + inst_true="1", ) acquire_interp_8bit = string_property( - 'ACQ:INTERPE', - bookmark_symbol='', + "ACQ:INTERPE", + bookmark_symbol="", doc=""" Valid values are AUTO, ON and OFF. - """ + """, ) - acquire_magnivu = bool_property( - 'ACQ:MAG', - inst_true='ON', - inst_false='OFF' - ) + acquire_magnivu = bool_property("ACQ:MAG", inst_true="ON", inst_false="OFF") - acquire_mode = enum_property( - 'ACQ:MOD', - AcquisitionMode - ) + acquire_mode = enum_property("ACQ:MOD", AcquisitionMode) - acquire_mode_actual = enum_property( - 'ACQ:MOD:ACT', - AcquisitionMode, - readonly=True - ) + acquire_mode_actual = enum_property("ACQ:MOD:ACT", AcquisitionMode, readonly=True) acquire_num_acquisitions = int_property( - 'ACQ:NUMAC', + "ACQ:NUMAC", readonly=True, doc=""" The number of waveform acquisitions that have occurred since starting acquisition with the ACQuire:STATE RUN command - """ + """, ) acquire_num_avgs = int_property( - 'ACQ:NUMAV', + "ACQ:NUMAV", doc=""" The number of waveform acquisitions to average. - """ + """, ) acquire_num_envelop = int_property( - 'ACQ:NUME', + "ACQ:NUME", doc=""" The number of waveform acquisitions to be enveloped - """ + """, ) acquire_num_frames = int_property( - 'ACQ:NUMFRAMESACQ', + "ACQ:NUMFRAMESACQ", readonly=True, doc=""" The number of frames acquired when in FastFrame Single Sequence and acquisitions are running. - """ + """, ) acquire_num_samples = int_property( - 'ACQ:NUMSAM', + "ACQ:NUMSAM", doc=""" The minimum number of acquired samples that make up a waveform database (WfmDB) waveform for single sequence mode and Mask Pass/Fail Completion Test. The default value is 16,000 samples. The range is 5,000 to 2,147,400,000 samples. - """ + """, ) - acquire_sampling_mode = enum_property( - 'ACQ:SAMP', - SamplingMode - ) + acquire_sampling_mode = enum_property("ACQ:SAMP", SamplingMode) acquire_state = enum_property( - 'ACQ:STATE', + "ACQ:STATE", AcquisitionState, doc=""" This command starts or stops acquisitions. - """ + """, ) acquire_stop_after = enum_property( - 'ACQ:STOPA', + "ACQ:STOPA", StopAfter, doc=""" This command sets or queries whether the instrument continually acquires acquisitions or acquires a single sequence. - """ + """, ) - data_framestart = int_property('DAT:FRAMESTAR') + data_framestart = int_property("DAT:FRAMESTAR") - data_framestop = int_property('DAT:FRAMESTOP') + data_framestop = int_property("DAT:FRAMESTOP") data_start = int_property( - 'DAT:STAR', + "DAT:STAR", doc=""" The first data point that will be transferred, which ranges from 1 to the record length. - """ + """, ) # TODO: Look into the following troublesome datasheet note: "When using the # CURVe command, DATa:STOP is ignored and WFMInpre:NR_Pt is used." data_stop = int_property( - 'DAT:STOP', + "DAT:STOP", doc=""" The last data point that will be transferred. - """ + """, ) - data_sync_sources = bool_property( - 'DAT:SYNCSOU', - inst_true='ON', - inst_false='OFF' - ) + data_sync_sources = bool_property("DAT:SYNCSOU", inst_true="ON", inst_false="OFF") @property def data_source(self): @@ -796,12 +781,12 @@ def data_source(self): :type: `TekDPO70000.Channel` or `TekDPO70000.Math` """ - val = self.query('DAT:SOU?') - if val[0:2] == 'CH': + val = self.query("DAT:SOU?") + if val[0:2] == "CH": out = self.channel[int(val[2]) - 1] - elif val[0:2] == 'MA': + elif val[0:2] == "MA": out = self.math[int(val[4]) - 1] - elif val[0:2] == 'RE': + elif val[0:2] == "RE": out = self.ref[int(val[3]) - 1] else: raise NotImplementedError @@ -810,8 +795,7 @@ def data_source(self): @data_source.setter def data_source(self, newval): if not isinstance(newval, self.DataSource): - raise TypeError( - "{} is not a valid data source.".format(type(newval))) + raise TypeError("{} is not a valid data source.".format(type(newval))) self.sendcmd("DAT:SOU {}".format(newval.name)) # Some Tek scopes require this after the DAT:SOU command, or else @@ -819,131 +803,121 @@ def data_source(self, newval): time.sleep(0.02) horiz_acq_duration = unitful_property( - 'HOR:ACQDURATION', + "HOR:ACQDURATION", u.second, readonly=True, doc=""" The duration of the acquisition. - """ + """, ) horiz_acq_length = int_property( - 'HOR:ACQLENGTH', + "HOR:ACQLENGTH", readonly=True, doc=""" The record length. - """ + """, ) - horiz_delay_mode = bool_property( - 'HOR:DEL:MOD', - inst_true='1', - inst_false='0' - ) + horiz_delay_mode = bool_property("HOR:DEL:MOD", inst_true="1", inst_false="0") horiz_delay_pos = unitful_property( - 'HOR:DEL:POS', + "HOR:DEL:POS", u.percent, doc=""" The percentage of the waveform that is displayed left of the center graticule. - """ + """, ) horiz_delay_time = unitful_property( - 'HOR:DEL:TIM', + "HOR:DEL:TIM", u.second, doc=""" The base trigger delay time setting. - """ + """, ) horiz_interp_ratio = unitless_property( - 'HOR:MAI:INTERPR', + "HOR:MAI:INTERPR", readonly=True, doc=""" The ratio of interpolated points to measured points. - """ + """, ) horiz_main_pos = unitful_property( - 'HOR:MAI:POS', + "HOR:MAI:POS", u.percent, doc=""" The percentage of the waveform that is displayed left of the center graticule. - """ + """, ) - horiz_unit = string_property('HOR:MAI:UNI') + horiz_unit = string_property("HOR:MAI:UNI") - horiz_mode = enum_property( - 'HOR:MODE', - HorizontalMode - ) + horiz_mode = enum_property("HOR:MODE", HorizontalMode) horiz_record_length_lim = int_property( - 'HOR:MODE:AUTO:LIMIT', + "HOR:MODE:AUTO:LIMIT", doc=""" The recond length limit in samples. - """ + """, ) horiz_record_length = int_property( - 'HOR:MODE:RECO', + "HOR:MODE:RECO", doc=""" The recond length in samples. See `horiz_mode`; manual mode lets you change the record length, while the length is readonly for auto and constant mode. - """ + """, ) horiz_sample_rate = unitful_property( - 'HOR:MODE:SAMPLER', + "HOR:MODE:SAMPLER", u.Hz, doc=""" The sample rate in samples per second. - """ + """, ) horiz_scale = unitful_property( - 'HOR:MODE:SCA', + "HOR:MODE:SCA", u.second, doc=""" The horizontal scale in seconds per division. The horizontal scale is readonly when `horiz_mode` is manual. - """ + """, ) horiz_pos = unitful_property( - 'HOR:POS', + "HOR:POS", u.percent, doc=""" The position of the trigger point on the screen, left is 0%, right is 100%. - """ + """, ) horiz_roll = string_property( - 'HOR:ROLL', - bookmark_symbol='', + "HOR:ROLL", + bookmark_symbol="", doc=""" Valid arguments are AUTO, OFF, and ON. - """ + """, ) - trigger_state = enum_property( - 'TRIG:STATE', - TriggerState - ) + trigger_state = enum_property("TRIG:STATE", TriggerState) # Waveform Transfer Properties outgoing_waveform_encoding = enum_property( - 'WFMO:ENC', + "WFMO:ENC", WaveformEncoding, doc=""" Controls the encoding used for outgoing waveforms (instrument → host). - """ + """, ) outgoing_binary_format = enum_property( @@ -952,7 +926,7 @@ def data_source(self, newval): doc=""" Controls the data type of samples when transferring waveforms from the instrument to the host using binary encoding. - """ + """, ) outgoing_byte_order = enum_property( @@ -960,7 +934,7 @@ def data_source(self, newval): ByteOrder, doc=""" Controls whether binary data is returned in little or big endian. - """ + """, ) outgoing_n_bytes = int_property( @@ -971,7 +945,7 @@ def data_source(self, newval): waveforms in binary encodings. Must be either 1, 2, 4 or 8. - """ + """, ) # METHODS # @@ -987,7 +961,7 @@ def force_trigger(self): """ Forces a trigger event to happen for the oscilloscope. """ - self.sendcmd('TRIG FORC') + self.sendcmd("TRIG FORC") # TODO: consider moving the next few methods to Oscilloscope. def run(self): diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index 9e30efe4c..ef72fd8b9 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -71,7 +71,7 @@ def read_waveform(self, bin_format=True): self._tek.sendcmd("DAT:ENC ASCI") # Set the data encoding format to ASCII raw = self._tek.query("CURVE?") - raw = raw.split(',') # Break up comma delimited string + raw = raw.split(",") # Break up comma delimited string if numpy: raw = numpy.array(raw, dtype=numpy.float) # Convert to ndarray else: @@ -81,8 +81,7 @@ def read_waveform(self, bin_format=True): # Set encoding to signed, big-endian data_width = self._tek.data_width self._tek.sendcmd("CURVE?") - raw = self._tek.binblockread( - data_width) # Read in the binary block, + raw = self._tek.binblockread(data_width) # Read in the binary block, # data width of 2 bytes # pylint: disable=protected-access @@ -94,14 +93,21 @@ def read_waveform(self, bin_format=True): xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr - ptcnt = self._tek.query(f"WFMP:{self.name}:NR_P?") # Retrieve number of data points + ptcnt = self._tek.query( + f"WFMP:{self.name}:NR_P?" + ) # Retrieve number of data points if numpy: x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) else: - x = tuple(float(val) * float(xincr) + float(xzero) for val in range(int(ptcnt))) - y = tuple(((x - float(yoffs)) * float(ymult)) + float(yzero) for x in raw) + x = tuple( + float(val) * float(xincr) + float(xzero) + for val in range(int(ptcnt)) + ) + y = tuple( + ((x - float(yoffs)) * float(ymult)) + float(yzero) for x in raw + ) return x, y @@ -127,15 +133,15 @@ def coupling(self): :type: `TekTDS224.Coupling` """ - return TekTDS224.Coupling( - self._tek.query(f'CH{self._idx}:COUPL?') - ) + return TekTDS224.Coupling(self._tek.query(f"CH{self._idx}:COUPL?")) @coupling.setter def coupling(self, newval): if not isinstance(newval, TekTDS224.Coupling): - raise TypeError(f"Coupling setting must be a `TekTDS224.Coupling` value," - f"got {type(newval)} instead.") + raise TypeError( + f"Coupling setting must be a `TekTDS224.Coupling` value," + f"got {type(newval)} instead." + ) self._tek.sendcmd(f"CH{self._idx}:COUPL {newval.value}") @@ -163,6 +169,7 @@ class Coupling(Enum): """ Enum containing valid coupling modes for the Tek TDS224 """ + ac = "AC" dc = "DC" ground = "GND" @@ -199,7 +206,9 @@ def ref(self): :rtype: `_TekTDS224DataSource` """ - return ProxyList(self, lambda s, idx: _TekTDS224DataSource(s, f"REF{idx + 1}"), range(4)) + return ProxyList( + self, lambda s, idx: _TekTDS224DataSource(s, f"REF{idx + 1}"), range(4) + ) @property def math(self): diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index 96da27d59..613a85f7f 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -62,9 +62,13 @@ class _TekTDS5xxMeasurement: def __init__(self, tek, idx): self._tek = tek self._id = idx + 1 - resp = self._tek.query('MEASU:MEAS{}?'.format(self._id)) - self._data = dict(zip(['enabled', 'type', 'units', 'src1', 'src2', - 'edge1', 'edge2', 'dir'], resp.split(';'))) + resp = self._tek.query("MEASU:MEAS{}?".format(self._id)) + self._data = dict( + zip( + ["enabled", "type", "units", "src1", "src2", "edge1", "edge2", "dir"], + resp.split(";"), + ) + ) def read(self): """ @@ -73,9 +77,9 @@ def read(self): :rtype: `dict` of measurement parameters """ - if int(self._data['enabled']): - resp = self._tek.query('MEASU:MEAS{}:VAL?'.format(self._id)) - self._data['value'] = float(resp) + if int(self._data["enabled"]): + resp = self._tek.query("MEASU:MEAS{}:VAL?".format(self._id)) + self._data["value"] = float(resp) return self._data return self._data @@ -123,18 +127,18 @@ def read_waveform(self, bin_format=True): if not bin_format: # Set the data encoding format to ASCII - self._parent.sendcmd('DAT:ENC ASCI') - raw = self._parent.query('CURVE?') - raw = raw.split(',') # Break up comma delimited string + self._parent.sendcmd("DAT:ENC ASCI") + raw = self._parent.query("CURVE?") + raw = raw.split(",") # Break up comma delimited string if numpy: raw = numpy.array(raw, dtype=numpy.float) # Convert to numpy array else: raw = map(float, raw) else: # Set encoding to signed, big-endian - self._parent.sendcmd('DAT:ENC RIB') + self._parent.sendcmd("DAT:ENC RIB") data_width = self._parent.data_width - self._parent.sendcmd('CURVE?') + self._parent.sendcmd("CURVE?") # Read in the binary block, data width of 2 bytes raw = self._parent.binblockread(data_width) @@ -143,16 +147,16 @@ def read_waveform(self, bin_format=True): self._parent._file.read_raw(1) # Retrieve Y offset - yoffs = float(self._parent.query('WFMP:{}:YOF?'.format(self.name))) + yoffs = float(self._parent.query("WFMP:{}:YOF?".format(self.name))) # Retrieve Y multiply - ymult = float(self._parent.query('WFMP:{}:YMU?'.format(self.name))) + ymult = float(self._parent.query("WFMP:{}:YMU?".format(self.name))) # Retrieve Y zero - yzero = float(self._parent.query('WFMP:{}:YZE?'.format(self.name))) + yzero = float(self._parent.query("WFMP:{}:YZE?".format(self.name))) # Retrieve X incr - xincr = float(self._parent.query('WFMP:{}:XIN?'.format(self.name))) + xincr = float(self._parent.query("WFMP:{}:XIN?".format(self.name))) # Retrieve number of data points - ptcnt = int(self._parent.query('WFMP:{}:NR_P?'.format(self.name))) + ptcnt = int(self._parent.query("WFMP:{}:NR_P?".format(self.name))) if numpy: x = numpy.arange(float(ptcnt)) * float(xincr) @@ -186,15 +190,15 @@ def coupling(self): :type: `TekTDS5xx.Coupling` """ - return TekTDS5xx.Coupling( - self._parent.query("CH{}:COUPL?".format(self._idx)) - ) + return TekTDS5xx.Coupling(self._parent.query("CH{}:COUPL?".format(self._idx))) @coupling.setter def coupling(self, newval): if not isinstance(newval, TekTDS5xx.Coupling): - raise TypeError("Coupling setting must be a `TekTDS5xx.Coupling`" - " value, got {} instead.".format(type(newval))) + raise TypeError( + "Coupling setting must be a `TekTDS5xx.Coupling`" + " value, got {} instead.".format(type(newval)) + ) self._parent.sendcmd("CH{}:COUPL {}".format(self._idx, newval.value)) @@ -205,15 +209,15 @@ def bandwidth(self): :type: `TekTDS5xx.Bandwidth` """ - return TekTDS5xx.Bandwidth( - self._parent.query("CH{}:BAND?".format(self._idx)) - ) + return TekTDS5xx.Bandwidth(self._parent.query("CH{}:BAND?".format(self._idx))) @bandwidth.setter def bandwidth(self, newval): if not isinstance(newval, TekTDS5xx.Bandwidth): - raise TypeError("Bandwidth setting must be a `TekTDS5xx.Bandwidth`" - " value, got {} instead.".format(type(newval))) + raise TypeError( + "Bandwidth setting must be a `TekTDS5xx.Bandwidth`" + " value, got {} instead.".format(type(newval)) + ) self._parent.sendcmd("CH{}:BAND {}".format(self._idx, newval.value)) @@ -224,15 +228,15 @@ def impedance(self): :type: `TekTDS5xx.Impedance` """ - return TekTDS5xx.Impedance( - self._parent.query("CH{}:IMP?".format(self._idx)) - ) + return TekTDS5xx.Impedance(self._parent.query("CH{}:IMP?".format(self._idx))) @impedance.setter def impedance(self, newval): if not isinstance(newval, TekTDS5xx.Impedance): - raise TypeError("Impedance setting must be a `TekTDS5xx.Impedance`" - " value, got {} instead.".format(type(newval))) + raise TypeError( + "Impedance setting must be a `TekTDS5xx.Impedance`" + " value, got {} instead.".format(type(newval)) + ) self._parent.sendcmd("CH{}:IMP {}".format(self._idx, newval.value)) @@ -259,8 +263,10 @@ def scale(self, newval): self._parent.sendcmd("CH{0}:SCA {1:.3E}".format(self._idx, newval)) resp = float(self._parent.query("CH{}:SCA?".format(self._idx))) if newval != resp: - raise ValueError("Tried to set CH{0} Scale to {1} but got {2}" - " instead".format(self._idx, newval, resp)) + raise ValueError( + "Tried to set CH{0} Scale to {1} but got {2}" + " instead".format(self._idx, newval, resp) + ) class TekTDS5xx(SCPIInstrument, Oscilloscope): @@ -281,6 +287,7 @@ class Coupling(Enum): """ Available coupling options for input sources and trigger """ + ac = "AC" dc = "DC" ground = "GND" @@ -290,6 +297,7 @@ class Bandwidth(Enum): """ Bandwidth in MHz """ + Twenty = "TWE" OneHundred = "HUN" TwoHundred = "TWO" @@ -300,6 +308,7 @@ class Impedance(Enum): """ Available options for input source impedance """ + Fifty = "FIF" OneMeg = "MEG" @@ -308,8 +317,9 @@ class Edge(Enum): """ Available Options for trigger slope """ - Rising = 'RIS' - Falling = 'FALL' + + Rising = "RIS" + Falling = "FALL" class Trigger(Enum): @@ -317,18 +327,20 @@ class Trigger(Enum): Available Trigger sources (AUX not Available on TDS520A/TDS540A) """ - CH1 = 'CH1' - CH2 = 'CH2' - CH3 = 'CH3' - CH4 = 'CH4' - AUX = 'AUX' - LINE = 'LINE' + + CH1 = "CH1" + CH2 = "CH2" + CH3 = "CH3" + CH4 = "CH4" + AUX = "AUX" + LINE = "LINE" class Source(Enum): """ Available Data sources """ + CH1 = "CH1" CH2 = "CH2" CH3 = "CH3" @@ -383,7 +395,7 @@ def ref(self): return ProxyList( self, lambda s, idx: _TekTDS5xxDataSource(s, "REF{}".format(idx + 1)), - range(4) + range(4), ) @property @@ -396,7 +408,7 @@ def math(self): return ProxyList( self, lambda s, idx: _TekTDS5xxDataSource(s, "MATH{}".format(idx + 1)), - range(3) + range(3), ) @property @@ -407,18 +419,16 @@ def sources(self): :rtype: `list` """ active = [] - channels = list(map(int, self.query('SEL?').split(';')[0:11])) + channels = list(map(int, self.query("SEL?").split(";")[0:11])) for idx in range(0, 4): if channels[idx]: active.append(_TekTDS5xxChannel(self, idx)) for idx in range(4, 7): if channels[idx]: - active.append(_TekTDS5xxDataSource(self, "MATH{}".format( - idx - 3))) + active.append(_TekTDS5xxDataSource(self, "MATH{}".format(idx - 3))) for idx in range(7, 11): if channels[idx]: - active.append( - _TekTDS5xxDataSource(self, "REF{}".format(idx - 6))) + active.append(_TekTDS5xxDataSource(self, "REF{}".format(idx - 6))) return active @property @@ -440,8 +450,10 @@ def data_source(self, newval): if isinstance(newval, _TekTDS5xxDataSource): newval = TekTDS5xx.Source(newval.name) if not isinstance(newval, TekTDS5xx.Source): - raise TypeError("Source setting must be a `TekTDS5xx.Source`" - " value, got {} instead.".format(type(newval))) + raise TypeError( + "Source setting must be a `TekTDS5xx.Source`" + " value, got {} instead.".format(type(newval)) + ) self.sendcmd("DAT:SOU {}".format(newval.value)) time.sleep(0.01) # Let the instrument catch up. @@ -472,15 +484,17 @@ def horizontal_scale(self): :type: `float` """ - return float(self.query('HOR:MAI:SCA?')) + return float(self.query("HOR:MAI:SCA?")) @horizontal_scale.setter def horizontal_scale(self, newval): self.sendcmd("HOR:MAI:SCA {0:.3E}".format(newval)) - resp = float(self.query('HOR:MAI:SCA?')) + resp = float(self.query("HOR:MAI:SCA?")) if newval != resp: - raise ValueError("Tried to set Horizontal Scale to {} but got {}" - " instead".format(newval, resp)) + raise ValueError( + "Tried to set Horizontal Scale to {} but got {}" + " instead".format(newval, resp) + ) @property def trigger_level(self): @@ -489,15 +503,17 @@ def trigger_level(self): :type: `float` """ - return float(self.query('TRIG:MAI:LEV?')) + return float(self.query("TRIG:MAI:LEV?")) @trigger_level.setter def trigger_level(self, newval): self.sendcmd("TRIG:MAI:LEV {0:.3E}".format(newval)) - resp = float(self.query('TRIG:MAI:LEV?')) + resp = float(self.query("TRIG:MAI:LEV?")) if newval != resp: - raise ValueError("Tried to set trigger level to {} but got {}" - " instead".format(newval, resp)) + raise ValueError( + "Tried to set trigger level to {} but got {}" + " instead".format(newval, resp) + ) @property def trigger_coupling(self): @@ -511,8 +527,10 @@ def trigger_coupling(self): @trigger_coupling.setter def trigger_coupling(self, newval): if not isinstance(newval, TekTDS5xx.Coupling): - raise TypeError("Coupling setting must be a `TekTDS5xx.Coupling`" - " value, got {} instead.".format(type(newval))) + raise TypeError( + "Coupling setting must be a `TekTDS5xx.Coupling`" + " value, got {} instead.".format(type(newval)) + ) self.sendcmd("TRIG:MAI:EDGE:COUP {}".format(newval.value)) @@ -528,8 +546,10 @@ def trigger_slope(self): @trigger_slope.setter def trigger_slope(self, newval): if not isinstance(newval, TekTDS5xx.Edge): - raise TypeError("Edge setting must be a `TekTDS5xx.Edge`" - " value, got {} instead.".format(type(newval))) + raise TypeError( + "Edge setting must be a `TekTDS5xx.Edge`" + " value, got {} instead.".format(type(newval)) + ) self.sendcmd("TRIG:MAI:EDGE:SLO {}".format(newval.value)) @@ -545,9 +565,11 @@ def trigger_source(self): @trigger_source.setter def trigger_source(self, newval): if not isinstance(newval, TekTDS5xx.Trigger): - raise TypeError("Trigger source setting must be a " - "`TekTDS5xx.Trigger` value, got {} " - "instead.".format(type(newval))) + raise TypeError( + "Trigger source setting must be a " + "`TekTDS5xx.Trigger` value, got {} " + "instead.".format(type(newval)) + ) self.sendcmd("TRIG:MAI:EDGE:SOU {}".format(newval.value)) @@ -558,14 +580,15 @@ def clock(self): :type: `datetime.datetime` """ - resp = self.query('DATE?;:TIME?') + resp = self.query("DATE?;:TIME?") return datetime.strptime(resp, '"%Y-%m-%d";"%H:%M:%S"') @clock.setter def clock(self, newval): if not isinstance(newval, datetime): - raise ValueError("Expected datetime.datetime " - "but got {} instead".format(type(newval))) + raise ValueError( + "Expected datetime.datetime " "but got {} instead".format(type(newval)) + ) self.sendcmd(newval.strftime('DATE "%Y-%m-%d";:TIME "%H:%M:%S"')) @property @@ -575,14 +598,13 @@ def display_clock(self): :type: `bool` """ - return bool(int(self.query('DISPLAY:CLOCK?'))) + return bool(int(self.query("DISPLAY:CLOCK?"))) @display_clock.setter def display_clock(self, newval): if not isinstance(newval, bool): - raise ValueError("Expected bool but got " - "{} instead".format(type(newval))) - self.sendcmd('DISPLAY:CLOCK {}'.format(int(newval))) + raise ValueError("Expected bool but got " "{} instead".format(type(newval))) + self.sendcmd("DISPLAY:CLOCK {}".format(int(newval))) def get_hardcopy(self): """ @@ -590,14 +612,13 @@ def get_hardcopy(self): :rtype: `string` """ - self.sendcmd('HARDC:PORT GPI;HARDC:LAY PORT;:HARDC:FORM BMP') - self.sendcmd('HARDC START') + self.sendcmd("HARDC:PORT GPI;HARDC:LAY PORT;:HARDC:FORM BMP") + self.sendcmd("HARDC START") time.sleep(1) header = self._file.read_raw(size=54) # Get BMP Length in kilobytes from DIB header, because file header is # bad - length = reduce( - operator.mul, struct.unpack('>> xdat, ydat = channel.read_waveform() # read waveform """ if bin_format: - raise NotImplementedError("Bin format reading is currently " - "not implemented for the MAUI " - "routine.") + raise NotImplementedError( + "Bin format reading is currently " + "not implemented for the MAUI " + "routine." + ) if single: # get current trigger state (to reset after read) @@ -266,31 +272,31 @@ def read_waveform(self, bin_format=False, single=True): self._parent.trigger_state = trig_state # format the string to appropriate data - retval = retval.replace('"', '').split() + retval = retval.replace('"', "").split() if numpy: dat_val = numpy.array(retval, dtype=numpy.float) # Convert to ndarray else: dat_val = tuple(map(float, retval)) # format horizontal data into floats - horiz_off = float(horiz_off.replace('"', '').split(':')[1]) - horiz_int = float(horiz_int.replace('"', '').split(':')[1]) + horiz_off = float(horiz_off.replace('"', "").split(":")[1]) + horiz_int = float(horiz_int.replace('"', "").split(":")[1]) # create time base if numpy: dat_time = numpy.arange( - horiz_off, - horiz_off + horiz_int * (len(dat_val)), - horiz_int + horiz_off, horiz_off + horiz_int * (len(dat_val)), horiz_int ) else: - dat_time = tuple(val * horiz_int + horiz_off for val in range(len(dat_val))) + dat_time = tuple( + val * horiz_int + horiz_off for val in range(len(dat_val)) + ) # fix length bug, sometimes dat_time is longer than dat_signal if len(dat_time) > len(dat_val): - dat_time = dat_time[0:len(dat_val)] + dat_time = dat_time[0 : len(dat_val)] else: # in case the opposite is the case - dat_val = dat_val[0:len(dat_time)] + dat_val = dat_val[0 : len(dat_time)] if numpy: return numpy.stack((dat_time, dat_val)) @@ -301,15 +307,15 @@ def read_waveform(self, bin_format=False, single=True): command="TRA", doc=""" Gets/Sets if a given trace is turned on or off. - + Example usage: - + >>> import instruments as ik >>> address = "TCPIP0::192.168.0.10::INSTR" >>> inst = inst = ik.teledyne.MAUI.open_visa(address) >>> channel = inst.channel[0] >>> channel.trace = False - """ + """, ) class Channel(DataSource, OscilloscopeChannel): @@ -323,13 +329,10 @@ class Channel(DataSource, OscilloscopeChannel): def __init__(self, parent, idx): self._parent = parent - self._idx = idx + 1 # 1-based + self._idx = idx + 1 # 1-based # Initialize as a data source with name C{}. - super(MAUI.Channel, self).__init__( - self._parent, - "C{}".format(self._idx) - ) + super(MAUI.Channel, self).__init__(self._parent, "C{}".format(self._idx)) # ENUMS # @@ -338,6 +341,7 @@ class Coupling(Enum): Enum containing valid coupling modes for the oscilloscope channel. 1 MOhm and 50 Ohm are included. """ + ac1M = "A1M" dc1M = "D1M" dc50 = "D50" @@ -356,7 +360,7 @@ class Coupling(Enum): >>> inst = inst = ik.teledyne.MAUI.open_visa(address) >>> channel = inst.channel[0] >>> channel.coupling = channel.Coupling.dc50 - """ + """, ) # PROPERTIES # @@ -374,14 +378,12 @@ def offset(self): >>> channel = inst.channel[0] # set up channel >>> channel.offset = u.Quantity(-1, u.V) """ - return u.Quantity( - float(self.query('OFST?')), u.V - ) + return u.Quantity(float(self.query("OFST?")), u.V) @offset.setter def offset(self, newval): - newval = assume_units(newval, 'V').to(u.V).magnitude - self.sendcmd('OFST {}'.format(newval)) + newval = assume_units(newval, "V").to(u.V).magnitude + self.sendcmd("OFST {}".format(newval)) @property def scale(self): @@ -395,14 +397,12 @@ def scale(self): >>> channel = inst.channel[0] # set up channel >>> channel.scale = u.Quantity(20, u.mV) """ - return u.Quantity( - float(self.query('VDIV?')), u.V - ) + return u.Quantity(float(self.query("VDIV?")), u.V) @scale.setter def scale(self, newval): - newval = assume_units(newval, 'V').to(u.V).magnitude - self.sendcmd('VDIV {}'.format(newval)) + newval = assume_units(newval, "V").to(u.V).magnitude + self.sendcmd("VDIV {}".format(newval)) # METHODS # @@ -429,8 +429,7 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - return self._parent.query("C{}:{}".format(self._idx, cmd), - size=size) + return self._parent.query("C{}:{}".format(self._idx, cmd), size=size) class Math(DataSource): @@ -443,13 +442,10 @@ class Math(DataSource): def __init__(self, parent, idx): self._parent = parent - self._idx = idx + 1 # 1-based + self._idx = idx + 1 # 1-based # Initialize as a data source with name C{}. - super(MAUI.Math, self).__init__( - self._parent, - "F{}".format(self._idx) - ) + super(MAUI.Math, self).__init__(self._parent, "F{}".format(self._idx)) # CLASSES # @@ -485,7 +481,7 @@ def current_setting(self): Gets the current setting and returns it as the full command, as sent to the scope when setting an operator. """ - return self._parent.query('DEF?') + return self._parent.query("DEF?") # METHODS - OPERATORS # @@ -499,7 +495,7 @@ def absolute(self, src): send_str = "'ABS({})'".format(src_str) self._send_operator(send_str) - def average(self, src, average_type='summed', sweeps=1000): + def average(self, src, average_type="summed", sweeps=1000): """ Average of wave form. @@ -511,20 +507,17 @@ def average(self, src, average_type='summed', sweeps=1000): """ src_str = _source(src) - avgtp_str = 'SUMMED' - if average_type == 'continuous': - avgtp_str = 'CONTINUOUS' + avgtp_str = "SUMMED" + if average_type == "continuous": + avgtp_str = "CONTINUOUS" send_str = "'AVG({})',AVERAGETYPE,{},SWEEPS,{}".format( - src_str, - avgtp_str, - sweeps + src_str, avgtp_str, sweeps ) self._send_operator(send_str) - def derivative(self, src, vscale=1e6, voffset=0, - autoscale=True): + def derivative(self, src, vscale=1e6, voffset=0, autoscale=True): """ Derivative of waveform using subtraction of adjacent samples. If vscale and voffset are unitless, V/s are @@ -537,24 +530,18 @@ def derivative(self, src, vscale=1e6, voffset=0, """ src_str = _source(src) - vscale = assume_units(vscale, u.V/u.s).to( - u.V/u.s - ).magnitude + vscale = assume_units(vscale, u.V / u.s).to(u.V / u.s).magnitude - voffset = assume_units(voffset, u.V/u.s).to( - u.V/u.s - ).magnitude + voffset = assume_units(voffset, u.V / u.s).to(u.V / u.s).magnitude - autoscale_str = 'OFF' + autoscale_str = "OFF" if autoscale: - autoscale_str = 'ON' + autoscale_str = "ON" - send_str = "'DERI({})',VERSCALE,{},VEROFFSET,{}," \ - "ENABLEAUTOSCALE,{}".format( - src_str, - vscale, - voffset, - autoscale_str) + send_str = ( + "'DERI({})',VERSCALE,{},VEROFFSET,{}," + "ENABLEAUTOSCALE,{}".format(src_str, vscale, voffset, autoscale_str) + ) self._send_operator(send_str) @@ -572,14 +559,12 @@ def difference(self, src1, src2, vscale_variable=False): src1_str = _source(src1) src2_str = _source(src2) - opt_str = 'FALSE' + opt_str = "FALSE" if vscale_variable: - opt_str = 'TRUE' + opt_str = "TRUE" send_str = "'{}-{}',VERSCALEVARIABLE,{}".format( - src1_str, - src2_str, - opt_str + src1_str, src2_str, opt_str ) self._send_operator(send_str) @@ -593,8 +578,9 @@ def envelope(self, src, sweeps=1000, limit_sweeps=True): :param bool limit_sweeps: Limit the number of sweeps? """ src_str = _source(src) - send_str = "'EXTR({})',SWEEPS,{},LIMITNUMSWEEPS,{}".\ - format(src_str, sweeps, limit_sweeps) + send_str = "'EXTR({})',SWEEPS,{},LIMITNUMSWEEPS,{}".format( + src_str, sweeps, limit_sweeps + ) self._send_operator(send_str) def eres(self, src, bits=0.5): @@ -616,8 +602,9 @@ def eres(self, src, bits=0.5): self._send_operator(send_str) - def fft(self, src, type='powerspectrum', window='vonhann', - suppress_dc=True): + def fft( + self, src, type="powerspectrum", window="vonhann", suppress_dc=True + ): """ Fast Fourier Transform of signal. @@ -633,26 +620,34 @@ def fft(self, src, type='powerspectrum', window='vonhann', """ src_str = _source(src) - type_possible = ['real', 'imaginary', 'magnitude', 'phase', - 'powerspectrum', 'powerdensity'] + type_possible = [ + "real", + "imaginary", + "magnitude", + "phase", + "powerspectrum", + "powerdensity", + ] if type not in type_possible: - type = 'powerspectrum' - - window_possible = ['blackmanharris', 'flattop', 'hamming', - 'rectangular', 'vonhann'] + type = "powerspectrum" + + window_possible = [ + "blackmanharris", + "flattop", + "hamming", + "rectangular", + "vonhann", + ] if window not in window_possible: - window = 'vonhann' + window = "vonhann" if suppress_dc: - opt = 'ON' + opt = "ON" else: - opt = 'OFF' + opt = "OFF" send_str = "'FFT({})',TYPE,{},WINDOW,{},SUPPRESSDC,{}".format( - src_str, - type, - window, - opt + src_str, type, window, opt ) self._send_operator(send_str) @@ -666,12 +661,12 @@ def floor(self, src, sweeps=1000, limit_sweeps=True): :param bool limit_sweeps: Limit the number of sweeps? """ src_str = _source(src) - send_str = "'FLOOR({})',SWEEPS,{},LIMITNUMSWEEPS,{}".\ - format(src_str, sweeps, limit_sweeps) + send_str = "'FLOOR({})',SWEEPS,{},LIMITNUMSWEEPS,{}".format( + src_str, sweeps, limit_sweeps + ) self._send_operator(send_str) - def integral(self, src, multiplier=1, adder=0, vscale=1e-3, - voffset=0): + def integral(self, src, multiplier=1, adder=0, vscale=1e-3, voffset=0): """ Integral of waveform. @@ -683,21 +678,14 @@ def integral(self, src, multiplier=1, adder=0, vscale=1e-3, """ src_str = _source(src) - vscale = assume_units(vscale, u.Wb).to( - u.Wb - ).magnitude + vscale = assume_units(vscale, u.Wb).to(u.Wb).magnitude - voffset = assume_units(voffset, u.Wb).to( - u.Wb - ).magnitude + voffset = assume_units(voffset, u.Wb).to(u.Wb).magnitude - send_str = "'INTG({}),MULTIPLIER,{},ADDER,{},VERSCALE,{}," \ - "VEROFFSET,{}".format( - src_str, - multiplier, - adder, - vscale, - voffset) + send_str = ( + "'INTG({}),MULTIPLIER,{},ADDER,{},VERSCALE,{}," + "VEROFFSET,{}".format(src_str, multiplier, adder, vscale, voffset) + ) self._send_operator(send_str) @@ -720,10 +708,7 @@ def product(self, src1, src2): src1_str = _source(src1) src2_str = _source(src2) - send_str = "'{}*{}'".format( - src1_str, - src2_str - ) + send_str = "'{}*{}'".format(src1_str, src2_str) self._send_operator(send_str) @@ -737,10 +722,7 @@ def ratio(self, src1, src2): src1_str = _source(src1) src2_str = _source(src2) - send_str = "'{}/{}'".format( - src1_str, - src2_str - ) + send_str = "'{}/{}'".format(src1_str, src2_str) self._send_operator(send_str) @@ -764,14 +746,10 @@ def rescale(self, src, multiplier=1, adder=0): """ src_str = _source(src) - adder = assume_units(adder, u.V).to( - u.V - ).magnitude + adder = assume_units(adder, u.V).to(u.V).magnitude send_str = "'RESC({})',MULTIPLIER,{},ADDER,{}".format( - src_str, - multiplier, - adder + src_str, multiplier, adder ) self._send_operator(send_str) @@ -813,10 +791,7 @@ def sum(self, src1, src2): src1_str = _source(src1) src2_str = _source(src2) - send_str = "'{}+{}'".format( - src1_str, - src2_str - ) + send_str = "'{}+{}'".format(src1_str, src2_str) self._send_operator(send_str) @@ -829,22 +804,19 @@ def trend(self, src, vscale=1, center=0, autoscale=True): """ src_str = _source(src) - vscale = assume_units(vscale, u.V).to( - u.V - ).magnitude + vscale = assume_units(vscale, u.V).to(u.V).magnitude - center = assume_units(center, u.V).to( - u.V - ).magnitude + center = assume_units(center, u.V).to(u.V).magnitude if autoscale: - auto_str = 'ON' + auto_str = "ON" else: - auto_str = 'OFF' + auto_str = "OFF" - send_str = "'TREND({})',VERSCALE,{},CENTER,{}," \ - "AUTOFINDSCALE,{}".format(src_str, vscale, - center, auto_str) + send_str = ( + "'TREND({})',VERSCALE,{},CENTER,{}," + "AUTOFINDSCALE,{}".format(src_str, vscale, center, auto_str) + ) self._send_operator(send_str) @@ -857,17 +829,16 @@ def roof(self, src, sweeps=1000, limit_sweeps=True): :param bool limit_sweeps: Limit the number of sweeps? """ src_str = _source(src) - send_str = "'ROOF({})',SWEEPS,{},LIMITNUMSWEEPS,{}".\ - format(src_str, sweeps, limit_sweeps) + send_str = "'ROOF({})',SWEEPS,{},LIMITNUMSWEEPS,{}".format( + src_str, sweeps, limit_sweeps + ) self._send_operator(send_str) def _send_operator(self, cmd): """ Set the operator in the scope. """ - self._parent.sendcmd("{},{}".format( - "DEFINE EQN", - cmd)) + self._parent.sendcmd("{},{}".format("DEFINE EQN", cmd)) # PROPERTIES # @@ -892,7 +863,7 @@ def operator(self): def clear_sweeps(self): """Clear the sweeps in a measurement.""" - self._parent.clear_sweeps() # re-implemented because handy + self._parent.clear_sweeps() # re-implemented because handy def sendcmd(self, cmd): """ @@ -917,8 +888,7 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - return self._parent.query("F{}:{}".format(self._idx, cmd), - size=size) + return self._parent.query("F{}:{}".format(self._idx, cmd), size=size) class Measurement: @@ -931,7 +901,7 @@ class Measurement: def __init__(self, parent, idx): self._parent = parent - self._idx = idx + 1 # 1-based + self._idx = idx + 1 # 1-based # CLASSES # @@ -940,6 +910,7 @@ class State(Enum): Enum class for Measurement Parameters. Required to turn it on or off. """ + statistics = "CUST,STAT" histogram_icon = "CUST,HISTICON" both = "CUST,BOTH" @@ -953,14 +924,14 @@ class State(Enum): doc=""" Sets / Gets the measurement state. Valid values are 'statistics', 'histogram_icon', 'both', 'off'. - + Example: >>> import instruments as ik >>> import instruments.units as u >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") >>> msr1 = inst.measurement[0] # set up first measurement >>> msr1.measurement_state = msr1.State.both # set to `both` - """ + """, ) @property @@ -972,24 +943,28 @@ def statistics(self): :return tuple: (average, low, high, sigma, sweeps) :return type: (float, float, float, float, float) """ - ret_str = self.query("PAST? CUST, P{}".format(self._idx)).\ - rstrip().split(',') + ret_str = ( + self.query("PAST? CUST, P{}".format(self._idx)).rstrip().split(",") + ) # parse the return string -> put into dictionary: - ret_dict = {ret_str[it]: ret_str[it+1] for it in - range(0, len(ret_str), 2)} + ret_dict = { + ret_str[it]: ret_str[it + 1] for it in range(0, len(ret_str), 2) + } try: stats = ( - float(ret_dict['AVG']), - float(ret_dict['LOW']), - float(ret_dict['HIGH']), - float(ret_dict['SIGMA']), - float(ret_dict['SWEEPS']) + float(ret_dict["AVG"]), + float(ret_dict["LOW"]), + float(ret_dict["HIGH"]), + float(ret_dict["SIGMA"]), + float(ret_dict["SWEEPS"]), ) except ValueError: # some statistics did not return - raise ValueError("Some statistics did not return useful " - "values. The return string is {}. Please " - "ensure that statistics is properly turned " - "on.".format(ret_str)) + raise ValueError( + "Some statistics did not return useful " + "values. The return string is {}. Please " + "ensure that statistics is properly turned " + "on.".format(ret_str) + ) return stats # METHODS # @@ -1024,17 +999,14 @@ def set_parameter(self, param, src): >>> msr1.set_parameter(inst.MeasurementParameters.rise_time_10_90, 0) """ if not isinstance(param, self._parent.MeasurementParameters): - raise AttributeError("Parameter must be selected from {}.". - format(self._parent.MeasurementParameters) - ) - - send_str = \ - "PACU {},{},{}".format( - self._idx, - param.value, - _source(src) + raise AttributeError( + "Parameter must be selected from {}.".format( + self._parent.MeasurementParameters + ) ) + send_str = "PACU {},{},{}".format(self._idx, param.value, _source(src)) + self.sendcmd(send_str) def sendcmd(self, cmd): @@ -1060,8 +1032,7 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - return self._parent.query(cmd, - size=size) + return self._parent.query(cmd, size=size) # PROPERTIES # @@ -1105,8 +1076,7 @@ def measurement(self): >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") >>> msr = inst.measurement[0] # get first measurement parameter """ - return ProxyList(self, self.Measurement, - range(self.number_measurements)) + return ProxyList(self, self.Measurement, range(self.number_measurements)) @property def ref(self): @@ -1241,14 +1211,12 @@ def time_div(self): >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") >>> inst.time_div = u.Quantity(200, u.ns) """ - return u.Quantity( - float(self.query('TDIV?')), u.s - ) + return u.Quantity(float(self.query("TDIV?")), u.s) @time_div.setter def time_div(self, newval): - newval = assume_units(newval, 's').to(u.s).magnitude - self.sendcmd('TDIV {}'.format(newval)) + newval = assume_units(newval, "s").to(u.s).magnitude + self.sendcmd("TDIV {}".format(newval)) # TRIGGER PROPERTIES @@ -1258,13 +1226,13 @@ def time_div(self, newval): doc=""" Sets / Gets the trigger state. Valid values are are defined in `TriggerState` enum class. - + Example: >>> import instruments as ik >>> import instruments.units as u >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") >>> inst.trigger_state = inst.TriggerState.normal - """ + """, ) @property @@ -1280,14 +1248,12 @@ def trigger_delay(self): >>> inst.trigger_delay = u.Quantity(60, u.ns) """ - return u.Quantity( - float(self.query('TRDL?')), u.s - ) + return u.Quantity(float(self.query("TRDL?")), u.s) @trigger_delay.setter def trigger_delay(self, newval): - newval = assume_units(newval, 's').to(u.s).magnitude - self.sendcmd('TRDL {}'.format(newval)) + newval = assume_units(newval, "s").to(u.s).magnitude + self.sendcmd("TRDL {}".format(newval)) @property def trigger_source(self): @@ -1314,14 +1280,13 @@ def trigger_source(self): >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") >>> inst.trigger_source = inst.TriggerSource.ext # external trigger """ - retval = self.query('TRIG_SELECT?').split(',')[2] + retval = self.query("TRIG_SELECT?").split(",")[2] return self.TriggerSource(retval) @trigger_source.setter def trigger_source(self, newval): curr_trig_typ = self.trigger_type - cmd = 'TRIG_SELECT {},SR,{}'.format(curr_trig_typ.value, - newval.value) + cmd = "TRIG_SELECT {},SR,{}".format(curr_trig_typ.value, newval.value) self.sendcmd(cmd) @property @@ -1344,14 +1309,13 @@ def trigger_type(self): >>> inst = ik.teledyne.MAUI.open_visa("TCPIP0::192.168.0.10::INSTR") >>> inst.trigger_type = inst.TriggerType.edge # trigger on edge """ - retval = self.query('TRIG_SELECT?').split(',')[0] + retval = self.query("TRIG_SELECT?").split(",")[0] return self.TriggerType(retval) @trigger_type.setter def trigger_type(self, newval): curr_trig_src = self.trigger_source - cmd = 'TRIG_SELECT {},SR,{}'.format(newval.value, - curr_trig_src.value) + cmd = "TRIG_SELECT {},SR,{}".format(newval.value, curr_trig_src.value) self.sendcmd(cmd) # METHODS # @@ -1390,7 +1354,7 @@ def run(self): self.trigger_state = self.TriggerState.auto def stop(self): - """ Disables the trigger for the oscilloscope. + """Disables the trigger for the oscilloscope. Example: >>> import instruments as ik @@ -1407,10 +1371,12 @@ def stop(self): def _source(src): """Stich the source together properly and return it.""" if isinstance(src, int): - return 'C{}'.format(src + 1) + return "C{}".format(src + 1) elif isinstance(src, tuple) and len(src) == 2: - return '{}{}'.format(src[0].upper(), int(src[1]) + 1) + return "{}{}".format(src[0].upper(), int(src[1]) + 1) else: - raise ValueError('An invalid source was specified. ' - 'Source must be an integer or a tuple of ' - 'length 2.') + raise ValueError( + "An invalid source was specified. " + "Source must be an integer or a tuple of " + "length 2." + ) diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index 534960fc6..c833e1af1 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -62,8 +62,9 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n", repeat=1): item.encode("utf-8") if isinstance(item, str) else item for item in ins_to_host ] - ins_to_host = sep.encode("utf-8").join(ins_to_host) + \ - (sep.encode("utf-8") if ins_to_host else b"") + ins_to_host = sep.encode("utf-8").join(ins_to_host) + ( + sep.encode("utf-8") if ins_to_host else b"" + ) elif isinstance(ins_to_host, str): ins_to_host = ins_to_host.encode("utf-8") ins_to_host *= repeat @@ -73,8 +74,9 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n", repeat=1): item.encode("utf-8") if isinstance(item, str) else item for item in host_to_ins ] - host_to_ins = sep.encode("utf-8").join(host_to_ins) + \ - (sep.encode("utf-8") if host_to_ins else b"") + host_to_ins = sep.encode("utf-8").join(host_to_ins) + ( + sep.encode("utf-8") if host_to_ins else b"" + ) elif isinstance(host_to_ins, str): host_to_ins = host_to_ins.encode("utf-8") host_to_ins *= repeat @@ -84,14 +86,17 @@ def expected_protocol(ins_class, host_to_ins, ins_to_host, sep="\n", repeat=1): yield ins_class.open_test(stdin, stdout) - assert stdout.getvalue() == host_to_ins, \ - """Expected: + assert ( + stdout.getvalue() == host_to_ins + ), """Expected: {} Got: -{}""".format(repr(host_to_ins), repr(stdout.getvalue())) +{}""".format( + repr(host_to_ins), repr(stdout.getvalue()) + ) # current = stdin.tell() # stdin.seek(0, 2) @@ -115,9 +120,11 @@ def make_name_test(ins_class, name_cmd="*IDN?"): Given an instrument class, produces a test which asserts that the instrument correctly reports its name in response to a standard command. """ + def test(): with expected_protocol(ins_class, name_cmd + "\n", "NAME\n") as ins: assert ins.name == "NAME" + return test @@ -127,8 +134,12 @@ def iterable_eq(a, b): """ if numpy and (isinstance(a, numpy.ndarray) or isinstance(b, numpy.ndarray)): # pylint: disable=unidiomatic-typecheck - assert type(a) == type(b), f"Expected two numpy arrays, got {type(a)}, {type(b)}" - assert len(a) == len(b), f"Length of iterables is not the same, got {len(a)} and {len(b)}" + assert type(a) == type( + b + ), f"Expected two numpy arrays, got {type(a)}, {type(b)}" + assert len(a) == len( + b + ), f"Length of iterables is not the same, got {len(a)} and {len(b)}" assert (a == b).all() elif isinstance(a, u.Quantity) and isinstance(b, u.Quantity): unit_eq(a, b) diff --git a/instruments/tests/test_abstract_inst/test_electrometer.py b/instruments/tests/test_abstract_inst/test_electrometer.py index 878f20112..2071316a6 100644 --- a/instruments/tests/test_abstract_inst/test_electrometer.py +++ b/instruments/tests/test_abstract_inst/test_electrometer.py @@ -20,92 +20,60 @@ def em(monkeypatch): """Patch and return electrometer class for direct access of metaclass.""" inst = ik.abstract_instruments.Electrometer - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst def test_electrometer_mode(em): """Get / set mode to ensure the abstract property exists.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: _ = inst.mode inst.mode = 42 def test_electrometer_unit(em): """Get unit to ensure the abstract property exists.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: _ = inst.unit def test_electrometer_trigger_mode(em): """Get / set trigger mode to ensure the abstract property exists.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: _ = inst.trigger_mode inst.trigger_mode = 42 def test_electrometer_input_range(em): """Get / set input range to ensure the abstract property exists.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: _ = inst.input_range inst.input_range = 42 def test_electrometer_zero_check(em): """Get / set zero check to ensure the abstract property exists.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: _ = inst.zero_check inst.zero_check = 42 def test_electrometer_zero_correct(em): """Get / set zero correct to ensure the abstract property exists.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: _ = inst.zero_correct inst.zero_correct = 42 def test_electrometer_fetch(em): """Raise NotImplementedError for fetch method.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: with pytest.raises(NotImplementedError): inst.fetch() def test_electrometer_read_measurements(em): """Raise NotImplementedError for read_measurements method.""" - with expected_protocol( - em, - [], - [] - ) as inst: + with expected_protocol(em, [], []) as inst: with pytest.raises(NotImplementedError): inst.read_measurements() diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index 5c7cd3f1c..e0da86bd0 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -18,6 +18,7 @@ # pylint: disable=missing-function-docstring,redefined-outer-name,protected-access + @pytest.fixture def fg(): return ik.abstract_instruments.FunctionGenerator.open_test() @@ -87,7 +88,9 @@ def test_func_gen_two_channel_passes_thru_call_getter(fg, mocker): mock_channel = mocker.MagicMock() mock_properties = [mocker.PropertyMock(return_value=1) for _ in range(5)] - mocker.patch("instruments.abstract_instruments.FunctionGenerator.Channel", new=mock_channel) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.Channel", new=mock_channel + ) type(mock_channel()).amplitude = mock_properties[0] type(mock_channel()).frequency = mock_properties[1] type(mock_channel()).function = mock_properties[2] @@ -109,11 +112,26 @@ def test_func_gen_one_channel_passes_thru_call_getter(fg, mocker): mock_properties = [mocker.PropertyMock(return_value=1) for _ in range(4)] mock_method = mocker.MagicMock(return_value=(1, u.V)) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.frequency", new=mock_properties[0]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.function", new=mock_properties[1]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.offset", new=mock_properties[2]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.phase", new=mock_properties[3]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator._get_amplitude_", new=mock_method) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.frequency", + new=mock_properties[0], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.function", + new=mock_properties[1], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.offset", + new=mock_properties[2], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.phase", + new=mock_properties[3], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator._get_amplitude_", + new=mock_method, + ) fg._channel_count = 1 _ = fg.channel[0].amplitude @@ -132,7 +150,9 @@ def test_func_gen_two_channel_passes_thru_call_setter(fg, mocker): mock_channel = mocker.MagicMock() mock_properties = [mocker.PropertyMock() for _ in range(5)] - mocker.patch("instruments.abstract_instruments.FunctionGenerator.Channel", new=mock_channel) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.Channel", new=mock_channel + ) type(mock_channel()).amplitude = mock_properties[0] type(mock_channel()).frequency = mock_properties[1] type(mock_channel()).function = mock_properties[2] @@ -154,11 +174,26 @@ def test_func_gen_one_channel_passes_thru_call_setter(fg, mocker): mock_properties = [mocker.PropertyMock() for _ in range(4)] mock_method = mocker.MagicMock() - mocker.patch("instruments.abstract_instruments.FunctionGenerator.frequency", new=mock_properties[0]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.function", new=mock_properties[1]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.offset", new=mock_properties[2]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator.phase", new=mock_properties[3]) - mocker.patch("instruments.abstract_instruments.FunctionGenerator._set_amplitude_", new=mock_method) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.frequency", + new=mock_properties[0], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.function", + new=mock_properties[1], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.offset", + new=mock_properties[2], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator.phase", + new=mock_properties[3], + ) + mocker.patch( + "instruments.abstract_instruments.FunctionGenerator._set_amplitude_", + new=mock_method, + ) fg._channel_count = 1 fg.channel[0].amplitude = 1 @@ -175,19 +210,16 @@ def test_func_gen_one_channel_passes_thru_call_setter(fg, mocker): def test_func_gen_channel_set_amplitude_dbm(mocker): """Get amplitude of channel when units are in dBm.""" - with expected_protocol( - ik.abstract_instruments.FunctionGenerator, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.abstract_instruments.FunctionGenerator, [], []) as inst: value = 3.14 # mock out the _get_amplitude of parent to return value in dBm mocker.patch.object( - inst, '_get_amplitude_', return_value=( + inst, + "_get_amplitude_", + return_value=( value, - ik.abstract_instruments.FunctionGenerator.VoltageMode.dBm - ) + ik.abstract_instruments.FunctionGenerator.VoltageMode.dBm, + ), ) channel = inst.channel[0] @@ -196,15 +228,10 @@ def test_func_gen_channel_set_amplitude_dbm(mocker): def test_func_gen_channel_sendcmd(mocker): """Send a command via parent class function.""" - with expected_protocol( - ik.abstract_instruments.FunctionGenerator, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.abstract_instruments.FunctionGenerator, [], []) as inst: cmd = "COMMAND" # mock out parent's send command - mock_sendcmd = mocker.patch.object(inst, 'sendcmd') + mock_sendcmd = mocker.patch.object(inst, "sendcmd") channel = inst.channel[0] channel.sendcmd(cmd) mock_sendcmd.assert_called_with(cmd) @@ -212,17 +239,12 @@ def test_func_gen_channel_sendcmd(mocker): def test_func_gen__channel_sendcmd(mocker): """Send a query via parent class function.""" - with expected_protocol( - ik.abstract_instruments.FunctionGenerator, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.abstract_instruments.FunctionGenerator, [], []) as inst: cmd = "QUERY" size = 13 retval = "ANSWER" # mock out parent's query command - mock_query = mocker.patch.object(inst, 'query', return_value=retval) + mock_query = mocker.patch.object(inst, "query", return_value=retval) channel = inst.channel[0] assert channel.query(cmd, size=size) == retval mock_query.assert_called_with(cmd, size) diff --git a/instruments/tests/test_abstract_inst/test_multimeter.py b/instruments/tests/test_abstract_inst/test_multimeter.py index 77b2190b5..cd07d9b3d 100644 --- a/instruments/tests/test_abstract_inst/test_multimeter.py +++ b/instruments/tests/test_abstract_inst/test_multimeter.py @@ -20,59 +20,39 @@ def mul(monkeypatch): """Patch and return Multimeter class for access.""" inst = ik.abstract_instruments.Multimeter - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst def test_multimeter_mode(mul): """Get / set mode: ensure existence.""" - with expected_protocol( - mul, - [], - [] - ) as inst: + with expected_protocol(mul, [], []) as inst: _ = inst.mode inst.mode = 42 def test_multimeter_trigger_mode(mul): """Get / set trigger mode: ensure existence.""" - with expected_protocol( - mul, - [], - [] - ) as inst: + with expected_protocol(mul, [], []) as inst: _ = inst.trigger_mode inst.trigger_mode = 42 def test_multimeter_relative(mul): """Get / set relative: ensure existence.""" - with expected_protocol( - mul, - [], - [] - ) as inst: + with expected_protocol(mul, [], []) as inst: _ = inst.relative inst.relative = 42 def test_multimeter_input_range(mul): """Get / set input range: ensure existence.""" - with expected_protocol( - mul, - [], - [] - ) as inst: + with expected_protocol(mul, [], []) as inst: _ = inst.input_range inst.input_range = 42 def test_multimeter_measure(mul): """Measure: ensure existence.""" - with expected_protocol( - mul, - [], - [] - ) as inst: - inst.measure('mode') + with expected_protocol(mul, [], []) as inst: + inst.measure("mode") diff --git a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py index 62c6de53f..1ced09d46 100644 --- a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py +++ b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py @@ -20,7 +20,7 @@ def osa(monkeypatch): """Patch and return Optical Spectrum Analyzer class for access.""" inst = ik.abstract_instruments.OpticalSpectrumAnalyzer - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -28,7 +28,7 @@ def osa(monkeypatch): def osc(monkeypatch): """Patch and return OSAChannel class for access.""" inst = ik.abstract_instruments.OSAChannel - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -37,22 +37,14 @@ def osc(monkeypatch): def test_osa_channel(osa): """Get channel: ensure existence.""" - with expected_protocol( - osa, - [], - [] - ) as inst: + with expected_protocol(osa, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.channel def test_osa_start_wl(osa): """Get / set start wavelength: ensure existence.""" - with expected_protocol( - osa, - [], - [] - ) as inst: + with expected_protocol(osa, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.start_wl with pytest.raises(NotImplementedError): @@ -61,11 +53,7 @@ def test_osa_start_wl(osa): def test_osa_stop_wl(osa): """Get / set stop wavelength: ensure existence.""" - with expected_protocol( - osa, - [], - [] - ) as inst: + with expected_protocol(osa, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.stop_wl with pytest.raises(NotImplementedError): @@ -74,11 +62,7 @@ def test_osa_stop_wl(osa): def test_osa_bandwidth(osa): """Get / set bandwidth: ensure existence.""" - with expected_protocol( - osa, - [], - [] - ) as inst: + with expected_protocol(osa, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.bandwidth with pytest.raises(NotImplementedError): @@ -87,11 +71,7 @@ def test_osa_bandwidth(osa): def test_osa_start_sweep(osa): """Start sweep: ensure existence.""" - with expected_protocol( - osa, - [], - [] - ) as inst: + with expected_protocol(osa, [], []) as inst: with pytest.raises(NotImplementedError): inst.start_sweep() diff --git a/instruments/tests/test_abstract_inst/test_oscilloscope.py b/instruments/tests/test_abstract_inst/test_oscilloscope.py index 27a0bdd4b..24d9072b4 100644 --- a/instruments/tests/test_abstract_inst/test_oscilloscope.py +++ b/instruments/tests/test_abstract_inst/test_oscilloscope.py @@ -20,7 +20,7 @@ def osc(monkeypatch): """Patch and return Oscilloscope class for access.""" inst = ik.abstract_instruments.Oscilloscope - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -28,7 +28,7 @@ def osc(monkeypatch): def osc_ch(monkeypatch): """Patch and return OscilloscopeChannel class for access.""" inst = ik.abstract_instruments.OscilloscopeChannel - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -36,7 +36,7 @@ def osc_ch(monkeypatch): def osc_ds(monkeypatch): """Patch and return OscilloscopeDataSource class for access.""" inst = ik.abstract_instruments.OscilloscopeDataSource - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -45,44 +45,28 @@ def osc_ds(monkeypatch): def test_oscilloscope_channel(osc): """Get channel: ensure existence.""" - with expected_protocol( - osc, - [], - [] - ) as inst: + with expected_protocol(osc, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.channel def test_oscilloscope_ref(osc): """Get ref: ensure existence.""" - with expected_protocol( - osc, - [], - [] - ) as inst: + with expected_protocol(osc, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.ref def test_oscilloscope_math(osc): """Get math: ensure existence.""" - with expected_protocol( - osc, - [], - [] - ) as inst: + with expected_protocol(osc, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.math def test_oscilloscope_force_trigger(osc): """Force a trigger: ensure existence.""" - with expected_protocol( - osc, - [], - [] - ) as inst: + with expected_protocol(osc, [], []) as inst: with pytest.raises(NotImplementedError): inst.force_trigger() @@ -104,8 +88,8 @@ def test_oscilloscope_channel_coupling(osc_ch): def test_oscilloscope_data_source_init(osc_ds): """Initialize Oscilloscope Data Source.""" - parent = 'parent' - name = 'name' + parent = "parent" + name = "name" inst = osc_ds(parent, name) assert inst._parent == parent assert inst._name == name @@ -114,8 +98,8 @@ def test_oscilloscope_data_source_init(osc_ds): def test_oscilloscope_data_source_name(osc_ds): """Get data source name: ensure existence.""" - parent = 'parent' - name = 'name' + parent = "parent" + name = "name" inst = osc_ds(parent, name) with pytest.raises(NotImplementedError): _ = inst.name @@ -123,8 +107,8 @@ def test_oscilloscope_data_source_name(osc_ds): def test_oscilloscope_data_source_read_waveform(osc_ds): """Read data source waveform: ensure existence.""" - parent = 'parent' - name = 'name' + parent = "parent" + name = "name" inst = osc_ds(parent, name) with pytest.raises(NotImplementedError): inst.read_waveform() diff --git a/instruments/tests/test_abstract_inst/test_power_supply.py b/instruments/tests/test_abstract_inst/test_power_supply.py index 528b85796..cda524c67 100644 --- a/instruments/tests/test_abstract_inst/test_power_supply.py +++ b/instruments/tests/test_abstract_inst/test_power_supply.py @@ -20,7 +20,7 @@ def ps(monkeypatch): """Patch and return Power Supply class for access.""" inst = ik.abstract_instruments.PowerSupply - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -28,7 +28,7 @@ def ps(monkeypatch): def ps_ch(monkeypatch): """Patch and return Power Supply Channel class for access.""" inst = ik.abstract_instruments.PowerSupplyChannel - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -37,33 +37,21 @@ def ps_ch(monkeypatch): def test_power_supply_channel(ps): """Get channel: ensure existence.""" - with expected_protocol( - ps, - [], - [] - ) as inst: + with expected_protocol(ps, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.channel def test_power_supply_voltage(ps): """Get / set voltage: ensure existence.""" - with expected_protocol( - ps, - [], - [] - ) as inst: + with expected_protocol(ps, [], []) as inst: _ = inst.voltage inst.voltage = 42 def test_power_supply_current(ps): """Get / set current: ensure existence.""" - with expected_protocol( - ps, - [], - [] - ) as inst: + with expected_protocol(ps, [], []) as inst: _ = inst.current inst.current = 42 diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py index 72bd6aeb2..0d943b793 100644 --- a/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py @@ -19,7 +19,7 @@ def sgc(monkeypatch): """Patch and return SGChannel for direct access of metaclass.""" inst = ik.abstract_instruments.signal_generator.SGChannel - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py index cf0154d39..926057609 100644 --- a/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py @@ -20,16 +20,12 @@ def sg(monkeypatch): """Patch and return signal generator for direct access of metaclass.""" inst = ik.abstract_instruments.signal_generator.SignalGenerator - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst def test_signal_generator_channel(sg): """Get channel: Ensure existence.""" - with expected_protocol( - sg, - [], - [] - ) as inst: + with expected_protocol(sg, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.channel diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py index 15234025d..f2ee7074e 100644 --- a/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py @@ -20,15 +20,11 @@ def scsg(monkeypatch): """Patch and return signal generator for direct access of metaclass.""" inst = ik.abstract_instruments.signal_generator.SingleChannelSG - monkeypatch.setattr(inst, '__abstractmethods__', set()) + monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst def test_signal_generator_channel(scsg): """Get channel: Ensure existence.""" - with expected_protocol( - scsg, - [], - [] - ) as inst: + with expected_protocol(scsg, [], []) as inst: assert inst.channel[0] == inst diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index 1c519e833..e1dbc1dad 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -21,18 +21,16 @@ def test_agilent33220a_amplitude(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "VOLT:UNIT?", - "VOLT?", - "VOLT:UNIT VPP", - "VOLT 2.0", - "VOLT:UNIT DBM", - "VOLT 1.5" - ], [ - "VPP", - "+1.000000E+00" - ] + ik.agilent.Agilent33220a, + [ + "VOLT:UNIT?", + "VOLT?", + "VOLT:UNIT VPP", + "VOLT 2.0", + "VOLT:UNIT DBM", + "VOLT 1.5", + ], + ["VPP", "+1.000000E+00"], ) as fg: assert fg.amplitude == (1 * u.V, fg.VoltageMode.peak_to_peak) fg.amplitude = 2 * u.V @@ -41,13 +39,7 @@ def test_agilent33220a_amplitude(): def test_agilent33220a_frequency(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FREQ?", - "FREQ 1.005000e+02" - ], [ - "+1.234000E+03" - ] + ik.agilent.Agilent33220a, ["FREQ?", "FREQ 1.005000e+02"], ["+1.234000E+03"] ) as fg: assert fg.frequency == 1234 * u.Hz fg.frequency = 100.5 * u.Hz @@ -55,13 +47,7 @@ def test_agilent33220a_frequency(): def test_agilent33220a_function(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FUNC?", - "FUNC:SQU" - ], [ - "SIN" - ] + ik.agilent.Agilent33220a, ["FUNC?", "FUNC:SQU"], ["SIN"] ) as fg: assert fg.function == fg.Function.sinusoid fg.function = fg.Function.square @@ -69,13 +55,11 @@ def test_agilent33220a_function(): def test_agilent33220a_offset(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "VOLT:OFFS?", - "VOLT:OFFS 4.321000e-01" - ], [ - "+1.234000E+01", - ] + ik.agilent.Agilent33220a, + ["VOLT:OFFS?", "VOLT:OFFS 4.321000e-01"], + [ + "+1.234000E+01", + ], ) as fg: assert fg.offset == 12.34 * u.V fg.offset = 0.4321 * u.V @@ -83,13 +67,11 @@ def test_agilent33220a_offset(): def test_agilent33220a_duty_cycle(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FUNC:SQU:DCYC?", - "FUNC:SQU:DCYC 75" - ], [ - "53", - ] + ik.agilent.Agilent33220a, + ["FUNC:SQU:DCYC?", "FUNC:SQU:DCYC 75"], + [ + "53", + ], ) as fg: assert fg.duty_cycle == 53 fg.duty_cycle = 75 @@ -97,13 +79,11 @@ def test_agilent33220a_duty_cycle(): def test_agilent33220a_ramp_symmetry(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "FUNC:RAMP:SYMM?", - "FUNC:RAMP:SYMM 75" - ], [ - "53", - ] + ik.agilent.Agilent33220a, + ["FUNC:RAMP:SYMM?", "FUNC:RAMP:SYMM 75"], + [ + "53", + ], ) as fg: assert fg.ramp_symmetry == 53 fg.ramp_symmetry = 75 @@ -111,13 +91,11 @@ def test_agilent33220a_ramp_symmetry(): def test_agilent33220a_output(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP?", - "OUTP OFF" - ], [ - "ON", - ] + ik.agilent.Agilent33220a, + ["OUTP?", "OUTP OFF"], + [ + "ON", + ], ) as fg: assert fg.output is True fg.output = False @@ -125,13 +103,11 @@ def test_agilent33220a_output(): def test_agilent33220a_output_sync(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP:SYNC?", - "OUTP:SYNC OFF" - ], [ - "ON", - ] + ik.agilent.Agilent33220a, + ["OUTP:SYNC?", "OUTP:SYNC OFF"], + [ + "ON", + ], ) as fg: assert fg.output_sync is True fg.output_sync = False @@ -139,13 +115,11 @@ def test_agilent33220a_output_sync(): def test_agilent33220a_output_polarity(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP:POL?", - "OUTP:POL NORM" - ], [ - "INV", - ] + ik.agilent.Agilent33220a, + ["OUTP:POL?", "OUTP:POL NORM"], + [ + "INV", + ], ) as fg: assert fg.output_polarity == fg.OutputPolarity.inverted fg.output_polarity = fg.OutputPolarity.normal @@ -153,16 +127,9 @@ def test_agilent33220a_output_polarity(): def test_agilent33220a_load_resistance(): with expected_protocol( - ik.agilent.Agilent33220a, - [ - "OUTP:LOAD?", - "OUTP:LOAD?", - "OUTP:LOAD 100", - "OUTP:LOAD MAX" - ], [ - "50", - "INF" - ] + ik.agilent.Agilent33220a, + ["OUTP:LOAD?", "OUTP:LOAD?", "OUTP:LOAD 100", "OUTP:LOAD MAX"], + ["50", "INF"], ) as fg: assert fg.load_resistance == 50 * u.ohm assert fg.load_resistance == fg.LoadResistance.high_impedance @@ -173,12 +140,7 @@ def test_agilent33220a_load_resistance(): @given(value=st.floats().filter(lambda x: x < 0 or x > 10000)) def test_agilent33220a_load_resistance_value_invalid(value): """Raise ValueError when resistance value loaded is out of range.""" - with expected_protocol( - ik.agilent.Agilent33220a, - [ - ], [ - ] - ) as fg: + with expected_protocol(ik.agilent.Agilent33220a, [], []) as fg: with pytest.raises(ValueError) as err_info: fg.load_resistance = value err_msg = err_info.value.args[0] @@ -187,12 +149,7 @@ def test_agilent33220a_load_resistance_value_invalid(value): def test_phase_not_implemented_error(): """Raise a NotImplementedError when getting / setting the phase.""" - with expected_protocol( - ik.agilent.Agilent33220a, - [ - ], [ - ] - ) as fg: + with expected_protocol(ik.agilent.Agilent33220a, [], []) as fg: with pytest.raises(NotImplementedError): _ = fg.phase() with pytest.raises(NotImplementedError): diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index 547a5cab8..cbce74918 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -10,12 +10,7 @@ import instruments as ik from instruments.optional_dep_finder import numpy -from instruments.tests import ( - expected_protocol, - iterable_eq, - make_name_test, - unit_eq -) +from instruments.tests import expected_protocol, iterable_eq, make_name_test, unit_eq from instruments.units import ureg as u # TESTS ###################################################################### @@ -25,81 +20,53 @@ def test_agilent34410a_read(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "READ?" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+1.86850000E-03" - ] + ik.agilent.Agilent34410a, + ["CONF?", "READ?"], + ["VOLT +1.000000E+01,+3.000000E-06", "+1.86850000E-03"], ) as dmm: - unit_eq(dmm.read_meter(), +1.86850000E-03 * u.volt) + unit_eq(dmm.read_meter(), +1.86850000e-03 * u.volt) def test_agilent34410a_data_point_count(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:POIN?", - ], [ - "+215", - ] + ik.agilent.Agilent34410a, + [ + "DATA:POIN?", + ], + [ + "+215", + ], ) as dmm: assert dmm.data_point_count == 215 def test_agilent34410a_init(): """Switch device from `idle` to `wait-for-trigger state`.""" - with expected_protocol( - ik.agilent.Agilent34410a, - [ - "INIT" - ], - [ - ] - ) as dmm: + with expected_protocol(ik.agilent.Agilent34410a, ["INIT"], []) as dmm: dmm.init() def test_agilent34410a_abort(): """Abort all current measurements.""" - with expected_protocol( - ik.agilent.Agilent34410a, - [ - "ABOR" - ], - [ - ] - ) as dmm: + with expected_protocol(ik.agilent.Agilent34410a, ["ABOR"], []) as dmm: dmm.abort() def test_agilent34410a_clear_memory(): """Clear non-volatile memory.""" - with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:DEL NVMEM" - ], - [ - ] - ) as dmm: + with expected_protocol(ik.agilent.Agilent34410a, ["DATA:DEL NVMEM"], []) as dmm: dmm.clear_memory() def test_agilent34410a_r(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FORM:DATA REAL,64", - "R? 1" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - # pylint: disable=no-member - b"#18" + bytes.fromhex("3FF0000000000000") - ] + ik.agilent.Agilent34410a, + ["CONF?", "FORM:DATA REAL,64", "R? 1"], + [ + "VOLT +1.000000E+01,+3.000000E-06", + # pylint: disable=no-member + b"#18" + bytes.fromhex("3FF0000000000000"), + ], ) as dmm: expected = (u.Quantity(1, u.volt),) if numpy: @@ -111,16 +78,13 @@ def test_agilent34410a_r(): def test_agilent34410a_r_count_zero(): """Read measurements with count set to zero.""" with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FORM:DATA REAL,64", - "R?" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - # pylint: disable=no-member - b"#18" + bytes.fromhex("3FF0000000000000") - ] + ik.agilent.Agilent34410a, + ["CONF?", "FORM:DATA REAL,64", "R?"], + [ + "VOLT +1.000000E+01,+3.000000E-06", + # pylint: disable=no-member + b"#18" + bytes.fromhex("3FF0000000000000"), + ], ) as dmm: expected = (u.Quantity(1, u.volt),) if numpy: @@ -133,12 +97,13 @@ def test_agilent34410a_r_type_error(): """Raise TypeError if count is not a integer.""" wrong_type = "42" with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + ], + [ + "VOLT +1.000000E+01,+3.000000E-06", + ], ) as dmm: with pytest.raises(TypeError) as err_info: dmm.r(wrong_type) @@ -148,70 +113,49 @@ def test_agilent34410a_r_type_error(): def test_agilent34410a_fetch(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FETC?" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + ["CONF?", "FETC?"], + ["VOLT +1.000000E+01,+3.000000E-06", "+4.27150000E-03,5.27150000E-03"], ) as dmm: data = dmm.fetch() - expected = (4.27150000E-03 * u.volt, 5.27150000E-03 * u.volt) + expected = (4.27150000e-03 * u.volt, 5.27150000e-03 * u.volt) if numpy: - expected = (4.27150000E-03, 5.27150000E-03) * u.volt + expected = (4.27150000e-03, 5.27150000e-03) * u.volt iterable_eq(data, expected) def test_agilent34410a_read_data(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "FORM:DATA ASC", - "DATA:REM? 2" - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + ["CONF?", "FORM:DATA ASC", "DATA:REM? 2"], + ["VOLT +1.000000E+01,+3.000000E-06", "+4.27150000E-03,5.27150000E-03"], ) as dmm: data = dmm.read_data(2) - unit_eq(data[0], 4.27150000E-03 * u.volt) - unit_eq(data[1], 5.27150000E-03 * u.volt) + unit_eq(data[0], 4.27150000e-03 * u.volt) + unit_eq(data[1], 5.27150000e-03 * u.volt) def test_agilent34410a_read_data_count_minus_one(): """Read data for all data points available.""" sample_count = 100 with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:POIN?", - "CONF?", - "FORM:DATA ASC", - f"DATA:REM? {sample_count}" - ], [ - f"{sample_count}", - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + ["DATA:POIN?", "CONF?", "FORM:DATA ASC", f"DATA:REM? {sample_count}"], + [ + f"{sample_count}", + "VOLT +1.000000E+01,+3.000000E-06", + "+4.27150000E-03,5.27150000E-03", + ], ) as dmm: data = dmm.read_data(-1) - unit_eq(data[0], 4.27150000E-03 * u.volt) - unit_eq(data[1], 5.27150000E-03 * u.volt) + unit_eq(data[0], 4.27150000e-03 * u.volt) + unit_eq(data[1], 5.27150000e-03 * u.volt) def test_agilent34410a_read_data_type_error(): """Raise Type error if count is not an integer.""" wrong_type = "42" - with expected_protocol( - ik.agilent.Agilent34410a, - [ - ], - [ - ] - ) as dmm: + with expected_protocol(ik.agilent.Agilent34410a, [], []) as dmm: with pytest.raises(TypeError) as err_info: dmm.read_data(wrong_type) err_msg = err_info.value.args[0] @@ -220,42 +164,35 @@ def test_agilent34410a_read_data_type_error(): def test_agilent34410a_read_data_nvmem(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "CONF?", - "DATA:DATA? NVMEM", - ], [ - "VOLT +1.000000E+01,+3.000000E-06", - "+4.27150000E-03,5.27150000E-03" - ] + ik.agilent.Agilent34410a, + [ + "CONF?", + "DATA:DATA? NVMEM", + ], + ["VOLT +1.000000E+01,+3.000000E-06", "+4.27150000E-03,5.27150000E-03"], ) as dmm: data = dmm.read_data_nvmem() - unit_eq(data[0], 4.27150000E-03 * u.volt) - unit_eq(data[1], 5.27150000E-03 * u.volt) + unit_eq(data[0], 4.27150000e-03 * u.volt) + unit_eq(data[1], 5.27150000e-03 * u.volt) def test_agilent34410a_read_last_data(): with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:LAST?", - ], [ - "+1.73730000E-03 VDC", - ] + ik.agilent.Agilent34410a, + [ + "DATA:LAST?", + ], + [ + "+1.73730000E-03 VDC", + ], ) as dmm: - unit_eq(dmm.read_last_data(), 1.73730000E-03 * u.volt) + unit_eq(dmm.read_last_data(), 1.73730000e-03 * u.volt) def test_agilent34410a_read_last_data_na(): """Return 9.91e37 if no data are available to read.""" na_value_str = "9.91000000E+37" with expected_protocol( - ik.agilent.Agilent34410a, - [ - "DATA:LAST?" - ], - [ - na_value_str - ] + ik.agilent.Agilent34410a, ["DATA:LAST?"], [na_value_str] ) as dmm: assert dmm.read_last_data() == float(na_value_str) diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 6102d8877..1642d2124 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -18,11 +18,20 @@ import instruments as ik from instruments.optional_dep_finder import numpy from instruments.tests import expected_protocol + # pylint: disable=unused-import from instruments.abstract_instruments.comm import ( - SocketCommunicator, USBCommunicator, VisaCommunicator, FileCommunicator, - LoopbackCommunicator, GPIBCommunicator, AbstractCommunicator, - USBTMCCommunicator, VXI11Communicator, serial_manager, SerialCommunicator + SocketCommunicator, + USBCommunicator, + VisaCommunicator, + FileCommunicator, + LoopbackCommunicator, + GPIBCommunicator, + AbstractCommunicator, + USBTMCCommunicator, + VXI11Communicator, + serial_manager, + SerialCommunicator, ) from instruments.errors import AcknowledgementError, PromptError from instruments.tests import iterable_eq @@ -36,14 +45,15 @@ # BINBLOCKREAD TESTS + def test_instrument_binblockread(): with expected_protocol( - ik.Instrument, - [], - [ - b"#210" + bytes.fromhex("00000001000200030004") + b"0", - ], - sep="\n" + ik.Instrument, + [], + [ + b"#210" + bytes.fromhex("00000001000200030004") + b"0", + ], + sep="\n", ) as inst: actual_data = inst.binblockread(2) expected = (0, 1, 2, 3, 4) @@ -90,6 +100,7 @@ def test_instrument_binblockread_bad_block_start(): # OPEN CONNECTION TESTS + @mock.patch("instruments.abstract_instruments.instrument.SocketCommunicator") @mock.patch("instruments.abstract_instruments.instrument.socket") def test_instrument_open_tcpip(mock_socket, mock_socket_comm): @@ -106,17 +117,16 @@ def test_instrument_open_tcpip(mock_socket, mock_socket_comm): @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) inst = ik.Instrument.open_serial("/dev/port", baud=1234) assert isinstance(inst._file, SerialCommunicator) is True mock_serial_manager.new_serial_connection.assert_called_with( - "/dev/port", - baud=1234, - timeout=3, - write_timeout=3 + "/dev/port", baud=1234, timeout=3, write_timeout=3 ) @@ -125,6 +135,7 @@ class fake_serial: Create a fake serial.Serial() object so that tests can be run without accessing a non-existant port. """ + # pylint: disable=unused-variable, unused-argument, no-self-use def __init__(self, device, baudrate=None, timeout=None, writeTimeout=None): self.device = device @@ -138,49 +149,48 @@ def isOpen(self): # TEST OPEN_SERIAL WITH USB IDENTIFIERS ###################################### + def fake_comports(): """ Generate a fake list of comports to compare against. """ - fake_device = ListPortInfo(device='COM1') + fake_device = ListPortInfo(device="COM1") fake_device.vid = 0 fake_device.pid = 1000 - fake_device.serial_number = 'a1' + fake_device.serial_number = "a1" - fake_device2 = ListPortInfo(device='COM2') + fake_device2 = ListPortInfo(device="COM2") fake_device2.vid = 1 fake_device2.pid = 1010 - fake_device2.serial_number = 'c0' + fake_device2.serial_number = "c0" return [fake_device, fake_device2] @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) inst = ik.Instrument.open_serial(baud=1234, vid=1, pid=1010) assert isinstance(inst._file, SerialCommunicator) is True mock_serial_manager.new_serial_connection.assert_called_with( - "COM2", - baud=1234, - timeout=3, - write_timeout=3 + "COM2", baud=1234, timeout=3, write_timeout=3 ) @mock.patch("instruments.abstract_instruments.instrument.comports", new=fake_comports) @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_and_serial_number(mock_serial_manager): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) inst = ik.Instrument.open_serial(baud=1234, vid=0, pid=1000, serial_number="a1") assert isinstance(inst._file, SerialCommunicator) is True mock_serial_manager.new_serial_connection.assert_called_with( - "COM1", - baud=1234, - timeout=3, - write_timeout=3 + "COM1", baud=1234, timeout=3, write_timeout=3 ) @@ -188,15 +198,15 @@ def test_instrument_open_serial_by_usb_ids_and_serial_number(mock_serial_manager @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_multiple_matches(_, mock_comports): with pytest.raises(serial.SerialException): - fake_device = ListPortInfo(device='COM1') + fake_device = ListPortInfo(device="COM1") fake_device.vid = 0 fake_device.pid = 1000 - fake_device.serial_number = 'a1' + fake_device.serial_number = "a1" - fake_device2 = ListPortInfo(device='COM2') + fake_device2 = ListPortInfo(device="COM2") fake_device2.vid = 0 fake_device2.pid = 1000 - fake_device2.serial_number = 'b2' + fake_device2.serial_number = "b2" mock_comports.return_value = [fake_device, fake_device2] @@ -207,7 +217,9 @@ def test_instrument_open_serial_by_usb_ids_multiple_matches(_, mock_comports): @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_incorrect_serial_num(mock_serial_manager): with pytest.raises(ValueError): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) _ = ik.Instrument.open_serial(baud=1234, vid=0, pid=1000, serial_number="xyz") @@ -215,7 +227,9 @@ def test_instrument_open_serial_by_usb_ids_incorrect_serial_num(mock_serial_mana @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_cant_find(mock_serial_manager): with pytest.raises(ValueError): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) _ = ik.Instrument.open_serial(baud=1234, vid=1234, pid=1000) @@ -223,7 +237,9 @@ def test_instrument_open_serial_by_usb_ids_cant_find(mock_serial_manager): @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_no_port(mock_serial_manager): with pytest.raises(ValueError): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) _ = ik.Instrument.open_serial(baud=1234) @@ -231,7 +247,9 @@ def test_instrument_open_serial_no_port(mock_serial_manager): @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_ids_and_port(mock_serial_manager): with pytest.raises(ValueError): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) _ = ik.Instrument.open_serial(port="COM1", baud=1234, vid=1234, pid=1000) @@ -239,7 +257,9 @@ def test_instrument_open_serial_by_usb_ids_and_port(mock_serial_manager): @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_vid_no_pid(mock_serial_manager): with pytest.raises(ValueError): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) _ = ik.Instrument.open_serial(baud=1234, vid=1234) @@ -247,16 +267,21 @@ def test_instrument_open_serial_by_usb_vid_no_pid(mock_serial_manager): @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_serial_by_usb_pid_no_vid(mock_serial_manager): with pytest.raises(ValueError): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) _ = ik.Instrument.open_serial(baud=1234, pid=1234) # TEST OPEN_GPIBUSB ########################################################## + @mock.patch("instruments.abstract_instruments.instrument.GPIBCommunicator") @mock.patch("instruments.abstract_instruments.instrument.serial_manager") def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): - mock_serial_manager.new_serial_connection.return_value.__class__ = SerialCommunicator + mock_serial_manager.new_serial_connection.return_value.__class__ = ( + SerialCommunicator + ) mock_gpib_comm.return_value.__class__ = GPIBCommunicator inst = ik.Instrument.open_gpibusb("/dev/port", gpib_address=1, model="gi") @@ -264,16 +289,11 @@ def test_instrument_open_gpibusb(mock_serial_manager, mock_gpib_comm): assert isinstance(inst._file, GPIBCommunicator) is True mock_serial_manager.new_serial_connection.assert_called_with( - "/dev/port", - baud=460800, - timeout=3, - write_timeout=3 + "/dev/port", baud=460800, timeout=3, write_timeout=3 ) mock_gpib_comm.assert_called_with( - mock_serial_manager.new_serial_connection.return_value, - 1, - "gi" + mock_serial_manager.new_serial_connection.return_value, 1, "gi" ) @@ -285,9 +305,7 @@ def test_instrument_open_gpibethernet(mock_socket_manager, mock_gpib_comm): host = "192.168.1.13" port = 1818 - inst = ik.Instrument.open_gpibethernet( - host, port, gpib_address=1, model="pl" - ) + inst = ik.Instrument.open_gpibethernet(host, port, gpib_address=1, model="pl") mock_socket_manager.socket.assert_called() mock_socket_manager.socket().connect.assert_called_with((host, port)) @@ -399,6 +417,7 @@ def test_instrument_open_file(mock_file_comm): # OPEN URI TESTS + @mock.patch("instruments.abstract_instruments.instrument.Instrument.open_serial") def test_instrument_open_from_uri_serial(mock_open_conn): _ = ik.Instrument.open_from_uri("serial:///dev/foobar") @@ -469,6 +488,7 @@ def test_instrument_open_from_uri_invalid_scheme(): # INIT TESTS + def test_instrument_init_bad_filelike(): with pytest.raises(TypeError): _ = ik.Instrument(mock.MagicMock()) @@ -495,6 +515,7 @@ def test_instrument_init_loopbackcomm(): # COMM TESTS + def test_instrument_default_ack_expected(): mock_filelike = mock.MagicMock() mock_filelike.__class__ = AbstractCommunicator @@ -753,13 +774,13 @@ def test_instrument_read(): inst._file.read.return_value = "foobar" assert inst.read() == "foobar" - inst._file.read.assert_called_with(-1, 'utf-8') + inst._file.read.assert_called_with(-1, "utf-8") inst._file = mock.MagicMock() inst._file.read.return_value = "foobar" assert inst.read(6) == "foobar" - inst._file.read.assert_called_with(6, 'utf-8') + inst._file.read.assert_called_with(6, "utf-8") def test_instrument_write(): @@ -773,6 +794,7 @@ def test_instrument_write(): # PROPERTIES # + def test_instrument_timeout(): mock_filelike = mock.MagicMock() mock_filelike.__class__ = AbstractCommunicator diff --git a/instruments/tests/test_comm/test_gpibusb.py b/instruments/tests/test_comm/test_gpibusb.py index d5b6455d2..6c164d06b 100644 --- a/instruments/tests/test_comm/test_gpibusb.py +++ b/instruments/tests/test_comm/test_gpibusb.py @@ -231,13 +231,15 @@ def test_gpibusbcomm_sendcmd(): comm._version = 5 comm._sendcmd("mock") - comm._file.sendcmd.assert_has_calls([ - mock.call("+a:1"), - mock.call("++eoi 1"), - mock.call("++read_tmo_ms 1000"), - mock.call("++eos 2"), - mock.call("mock") - ]) + comm._file.sendcmd.assert_has_calls( + [ + mock.call("+a:1"), + mock.call("++eoi 1"), + mock.call("++read_tmo_ms 1000"), + mock.call("++eos 2"), + mock.call("mock"), + ] + ) def test_gpibusbcomm_sendcmd_empty_string(): diff --git a/instruments/tests/test_comm/test_loopback.py b/instruments/tests/test_comm/test_loopback.py index b88d7d0ad..9386e415d 100644 --- a/instruments/tests/test_comm/test_loopback.py +++ b/instruments/tests/test_comm/test_loopback.py @@ -79,7 +79,7 @@ def test_loopbackcomm_read_raw(): comm = LoopbackCommunicator(stdin=mock_stdin) assert comm.read_raw() == b"abc" - mock_stdin.read.assert_has_calls([mock.call(1)]*4) + mock_stdin.read.assert_has_calls([mock.call(1)] * 4) assert mock_stdin.read.call_count == 4 mock_stdin.read = mock.MagicMock() @@ -94,7 +94,7 @@ def test_loopbackcomm_read_raw_2char_terminator(): comm._terminator = "\r\n" assert comm.read_raw() == b"abc" - mock_stdin.read.assert_has_calls([mock.call(1)]*5) + mock_stdin.read.assert_has_calls([mock.call(1)] * 5) assert mock_stdin.read.call_count == 5 diff --git a/instruments/tests/test_comm/test_serial.py b/instruments/tests/test_comm/test_serial.py index a87523cbf..5ce660401 100644 --- a/instruments/tests/test_comm/test_serial.py +++ b/instruments/tests/test_comm/test_serial.py @@ -89,7 +89,7 @@ def test_serialcomm_read_raw(): comm._conn.read = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\n"]) assert comm.read_raw() == b"abc" - comm._conn.read.assert_has_calls([mock.call(1)]*4) + comm._conn.read.assert_has_calls([mock.call(1)] * 4) assert comm._conn.read.call_count == 4 comm._conn.read = mock.MagicMock() diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index 81c1861cd..79a0795ef 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -97,7 +97,7 @@ def test_socketcomm_read_raw(): comm._conn.recv = mock.MagicMock(side_effect=[b"a", b"b", b"c", b"\n"]) assert comm.read_raw() == b"abc" - comm._conn.recv.assert_has_calls([mock.call(1)]*4) + comm._conn.recv.assert_has_calls([mock.call(1)] * 4) assert comm._conn.recv.call_count == 4 comm._conn.recv = mock.MagicMock() diff --git a/instruments/tests/test_comm/test_usb_communicator.py b/instruments/tests/test_comm/test_usb_communicator.py index 2e2eb3591..5404e522a 100644 --- a/instruments/tests/test_comm/test_usb_communicator.py +++ b/instruments/tests/test_comm/test_usb_communicator.py @@ -46,9 +46,7 @@ def test_init(usb_util, dev): # shortcuts for asserting calls cfg = dev.get_active_configuration() interface_number = cfg[(0, 0)].bInterfaceNumber - _ = dev.control.get_interface( - dev, cfg[(0, 0)].bInterfaceNumber - ) + _ = dev.control.get_interface(dev, cfg[(0, 0)].bInterfaceNumber) inst = USBCommunicator(dev) @@ -106,8 +104,10 @@ def test_terminator_wrong_type(inst): with pytest.raises(TypeError) as err: inst.terminator = 42 msg = err.value.args[0] - assert msg == "Terminator for USBCommunicator must be specified as a " \ - "character string." + assert ( + msg == "Terminator for USBCommunicator must be specified as a " + "character string." + ) @given(val=st.integers(min_value=1)) @@ -181,9 +181,11 @@ def test_read_raw_termination_char_not_found(inst): with pytest.raises(IOError) as err: _ = inst.read_raw() err_msg = err.value.args[0] - assert err_msg == f"Did not find the terminator in the returned " \ - f"string. Total size of {default_read_size} might " \ - f"not be enough." + assert ( + err_msg == f"Did not find the terminator in the returned " + f"string. Total size of {default_read_size} might " + f"not be enough." + ) def test_write_raw(inst): diff --git a/instruments/tests/test_comm/test_visa_communicator.py b/instruments/tests/test_comm/test_visa_communicator.py index 2bccd7f44..c45ea2a57 100644 --- a/instruments/tests/test_comm/test_visa_communicator.py +++ b/instruments/tests/test_comm/test_visa_communicator.py @@ -49,15 +49,13 @@ def test_visacomm_address(visa_inst): with pytest.raises(NotImplementedError) as err: comm.address = "new address" err_msg = err.value.args[0] - assert err_msg == ( - "Changing addresses of a VISA Instrument is not supported." - ) + assert err_msg == ("Changing addresses of a VISA Instrument is not supported.") def test_visacomm_terminator(visa_inst): """Get / Set terminator.""" comm = VisaCommunicator(visa_inst) - comm.terminator = ("\r") + comm.terminator = "\r" assert comm.terminator == "\r" @@ -79,9 +77,7 @@ def test_visacomm_terminator_not_single_char(visa_inst): with pytest.raises(ValueError) as err: comm.terminator = "\r\n" err_msg = err.value.args[0] - assert err_msg == ( - "Terminator for VisaCommunicator must only be 1 character long." - ) + assert err_msg == ("Terminator for VisaCommunicator must only be 1 character long.") def test_visacomm_timeout(visa_inst): @@ -97,7 +93,7 @@ def test_visacomm_close(visa_inst, mocker): """Raise an IOError if comms cannot be closed.""" io_error_mock = mocker.Mock() io_error_mock.side_effect = IOError - mock_close = mocker.patch.object(visa_inst, 'close', io_error_mock) + mock_close = mocker.patch.object(visa_inst, "close", io_error_mock) comm = VisaCommunicator(visa_inst) comm.close() mock_close.assert_called() # but error will just pass! @@ -106,9 +102,7 @@ def test_visacomm_close(visa_inst, mocker): def test_visacomm_read_raw(visa_inst, mocker): """Read raw data from instrument without size specification.""" comm = VisaCommunicator(visa_inst) - mock_read_raw = mocker.patch.object( - visa_inst, 'read_raw', return_value=b'asdf' - ) + mock_read_raw = mocker.patch.object(visa_inst, "read_raw", return_value=b"asdf") comm.read_raw() mock_read_raw.assert_called() assert comm._buf == bytearray() @@ -118,11 +112,9 @@ def test_visacomm_read_raw_size(visa_inst, mocker): """Read raw data from instrument with size specification.""" comm = VisaCommunicator(visa_inst) size = 3 - mock_read_bytes = mocker.patch.object( - visa_inst, 'read_bytes', return_value=b'123' - ) + mock_read_bytes = mocker.patch.object(visa_inst, "read_bytes", return_value=b"123") ret_val = comm.read_raw(size=size) - assert ret_val == b'123' + assert ret_val == b"123" mock_read_bytes.assert_called() assert comm._buf == bytearray() @@ -140,9 +132,9 @@ def test_visacomm_read_raw_wrong_size(visa_inst): def test_visacomm_write_raw(visa_inst, mocker): """Write raw message to instrument.""" - mock_write = mocker.patch.object(visa_inst, 'write_raw') + mock_write = mocker.patch.object(visa_inst, "write_raw") comm = VisaCommunicator(visa_inst) - msg = b'12345' + msg = b"12345" comm.write_raw(msg) mock_write.assert_called_with(msg) @@ -163,17 +155,17 @@ def test_visacomm_tell_not_implemented(visa_inst): def test_visacomm_sendcmd(visa_inst, mocker): """Write to device.""" - mock_write = mocker.patch.object(VisaCommunicator, 'write') + mock_write = mocker.patch.object(VisaCommunicator, "write") comm = VisaCommunicator(visa_inst) - msg = 'asdf' + msg = "asdf" comm._sendcmd(msg) mock_write.assert_called_with(msg + comm.terminator) def test_visacomm_query(visa_inst, mocker): """Query device.""" - mock_query = mocker.patch.object(visa_inst, 'query') + mock_query = mocker.patch.object(visa_inst, "query") comm = VisaCommunicator(visa_inst) - msg = 'asdf' + msg = "asdf" comm._query(msg) mock_query.assert_called_with(msg + comm.terminator) diff --git a/instruments/tests/test_config.py b/instruments/tests/test_config.py index b1a9c7f29..ca8c1bac0 100644 --- a/instruments/tests/test_config.py +++ b/instruments/tests/test_config.py @@ -12,52 +12,62 @@ from instruments.units import ureg as u from instruments import Instrument -from instruments.config import ( - load_instruments, yaml -) +from instruments.config import load_instruments, yaml # TEST CASES ################################################################# # pylint: disable=protected-access,missing-docstring + def test_load_test_instrument(): - config_data = StringIO(u""" + config_data = StringIO( + u""" test: class: !!python/name:instruments.Instrument uri: test:// -""") +""" + ) insts = load_instruments(config_data) - assert isinstance(insts['test'], Instrument) + assert isinstance(insts["test"], Instrument) + def test_load_test_instrument_subtree(): - config_data = StringIO(u""" + config_data = StringIO( + u""" instruments: test: class: !!python/name:instruments.Instrument uri: test:// -""") +""" + ) insts = load_instruments(config_data, conf_path="/instruments") - assert isinstance(insts['test'], Instrument) + assert isinstance(insts["test"], Instrument) + def test_yaml_quantity_tag(): - yaml_data = StringIO(u""" + yaml_data = StringIO( + u""" a: b: !Q 37 tesla c: !Q 41.2 inches d: !Q 98 -""") +""" + ) data = yaml.load(yaml_data, Loader=yaml.Loader) - assert data['a']['b'] == u.Quantity(37, 'tesla') - assert data['a']['c'] == u.Quantity(41.2, 'inches') - assert data['a']['d'] == 98 + assert data["a"]["b"] == u.Quantity(37, "tesla") + assert data["a"]["c"] == u.Quantity(41.2, "inches") + assert data["a"]["d"] == 98 + def test_load_test_instrument_setattr(): - config_data = StringIO(u""" + config_data = StringIO( + u""" test: class: !!python/name:instruments.Instrument uri: test:// attrs: foo: !Q 111 GHz -""") +""" + ) insts = load_instruments(config_data) - assert insts['test'].foo == u.Quantity(111, 'GHz') + assert insts["test"].foo == u.Quantity(111, "GHz") diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index b064a0410..e2d00a681 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -27,42 +27,35 @@ "rfebd 03 0", "rfebd 04 0", "rfebd 05 0", - "rfebd 06 0" -] -none_response = [ - "CR:Ack=2", - "CR:Ack=2", - "CR:Ack=2", - "CR:Ack=2", - "CR:Ack=2", - "CR:Ack=2" + "rfebd 06 0", ] +none_response = ["CR:Ack=2", "CR:Ack=2", "CR:Ack=2", "CR:Ack=2", "CR:Ack=2", "CR:Ack=2"] # Default initialization sequence (scan function) that binds a multimeter # to port 1 and a temperature module to port 2. init_sequence = [ - "rfebd 01 0", # 1 - "rfgus 01", # 2 - "rfebd 02 0", # 3 - "rfgus 02", # 4 - "rfebd 03 0", # 5 - "rfebd 04 0", # 6 - "rfebd 05 0", # 7 - "rfebd 06 0" # 8 + "rfebd 01 0", # 1 + "rfgus 01", # 2 + "rfebd 02 0", # 3 + "rfgus 02", # 4 + "rfebd 03 0", # 5 + "rfebd 04 0", # 6 + "rfebd 05 0", # 7 + "rfebd 06 0", # 8 ] init_response = [ - "CR:Ack=0:RFEBD", # 1.1 + "CR:Ack=0:RFEBD", # 1.1 "ME:R:S#=01:DCC=012:PH=64", # 1.2 - "CR:Ack=0:RFGUS", # 2.1 + "CR:Ack=0:RFGUS", # 2.1 "ME:R:S#=01:DCC=004:PH=46333030304643", # 2.2 - "CR:Ack=0:RFEBD", # 3.1 + "CR:Ack=0:RFEBD", # 3.1 "ME:R:S#=01:DCC=012:PH=64", # 3.2 - "CR:Ack=0:RFGUS", # 4.1 + "CR:Ack=0:RFGUS", # 4.1 "ME:R:S#=02:DCC=004:PH=54333030304643", # 4.2 "CR:Ack=2", # 5 "CR:Ack=2", # 6 "CR:Ack=2", # 7 - "CR:Ack=2" # 8 + "CR:Ack=2", # 8 ] @@ -70,42 +63,38 @@ # to port 1. Adopted from `init_sequence` and `init_response`, thus # counting does not contain 4. init_sequence_mm_only = [ - "rfebd 01 0", # 1 - "rfgus 01", # 2 - "rfebd 02 0", # 3 - "rfebd 03 0", # 5 - "rfebd 04 0", # 6 - "rfebd 05 0", # 7 - "rfebd 06 0" # 8 + "rfebd 01 0", # 1 + "rfgus 01", # 2 + "rfebd 02 0", # 3 + "rfebd 03 0", # 5 + "rfebd 04 0", # 6 + "rfebd 05 0", # 7 + "rfebd 06 0", # 8 ] init_response_mm_only = [ - "CR:Ack=0:RFEBD", # 1.1 + "CR:Ack=0:RFEBD", # 1.1 "ME:R:S#=01:DCC=012:PH=64", # 1.2 - "CR:Ack=0:RFGUS", # 2.1 + "CR:Ack=0:RFGUS", # 2.1 "ME:R:S#=01:DCC=004:PH=46333030304643", # 2.2 - "CR:Ack=2", # 3 + "CR:Ack=2", # 3 "CR:Ack=2", # 5 "CR:Ack=2", # 6 "CR:Ack=2", # 7 - "CR:Ack=2" # 8 + "CR:Ack=2", # 8 ] def test_mode(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - "rfemd 01 1", # 1 - "rfemd 01 2" # 2 - ], - init_response + - [ - "CR:Ack=0:RFEMD", # 1.1 - "ME:R:S#=01:DCC=010:PH=00000006020C0600", # 1.2 - "CR:Ack=0:RFEMD" # 2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + ["rfemd 01 1", "rfemd 01 2"], # 1 # 2 + init_response + + [ + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=00000006020C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + ], + "\r", ) as inst: assert inst.mode == inst.Mode.voltage_dc @@ -113,10 +102,7 @@ def test_mode(): def test_mode_key_error(): """Raise KeyError if the Module is not available.""" with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: # kill positions to trigger error inst.positions = {} @@ -129,31 +115,23 @@ def test_mode_key_error(): def test_trigger_mode_attribute_error(): """Raise AttributeError since trigger mode not supported.""" with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: with pytest.raises(AttributeError) as err_info: _ = inst.trigger_mode err_msg = err_info.value.args[0] - assert err_msg == "The `Fluke3000` only supports single trigger when " \ - "queried" + assert err_msg == "The `Fluke3000` only supports single trigger when " "queried" def test_relative_attribute_error(): """Raise AttributeError since relative measurement mode not supported.""" with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: with pytest.raises(AttributeError) as err_info: _ = inst.relative err_msg = err_info.value.args[0] - assert err_msg == "The `Fluke3000` FC does not support relative " \ - "measurements" + assert err_msg == "The `Fluke3000` FC does not support relative " "measurements" def test_input_range_attribute_error(): @@ -162,41 +140,37 @@ def test_input_range_attribute_error(): multimeter. """ with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: with pytest.raises(AttributeError) as err_info: _ = inst.input_range err_msg = err_info.value.args[0] - assert err_msg == "The `Fluke3000` FC is an autoranging only " \ - "multimeter" + assert err_msg == "The `Fluke3000` FC is an autoranging only " "multimeter" def test_connect(): with expected_protocol( - ik.fluke.Fluke3000, - none_sequence + - [ - "ri", # 1 - "rfsm 1", # 2 - "rfdis", # 3 - ] + - init_sequence, - none_response + - [ - "CR:Ack=0:RI", # 1.1 - "SI:PON=Power On", # 1.2 - "RE:O", # 1.3 - "CR:Ack=0:RFSM:Radio On Master", # 2.1 - "RE:M", # 2.2 - "CR:Ack=0:RFDIS", # 3.1 - "ME:S", # 3.2 - "ME:D:010200000000", # 3.3 - ] + - init_response, - "\r" + ik.fluke.Fluke3000, + none_sequence + + [ + "ri", # 1 + "rfsm 1", # 2 + "rfdis", # 3 + ] + + init_sequence, + none_response + + [ + "CR:Ack=0:RI", # 1.1 + "SI:PON=Power On", # 1.2 + "RE:O", # 1.3 + "CR:Ack=0:RFSM:Radio On Master", # 2.1 + "RE:M", # 2.2 + "CR:Ack=0:RFDIS", # 3.1 + "ME:S", # 3.2 + "ME:D:010200000000", # 3.3 + ] + + init_response, + "\r", ) as inst: assert inst.positions[ik.fluke.Fluke3000.Module.m3000] == 1 assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 @@ -206,10 +180,7 @@ def test_connect_no_modules_available(): """Raise ValueError if no modules are avilable.""" with pytest.raises(ValueError) as err_info: with expected_protocol( - ik.fluke.Fluke3000, - none_sequence, - none_response, - "\r" + ik.fluke.Fluke3000, none_sequence, none_response, "\r" ) as inst: _ = inst err_msg = err_info.value.args[0] @@ -218,10 +189,7 @@ def test_connect_no_modules_available(): def test_scan(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: assert inst.positions[ik.fluke.Fluke3000.Module.m3000] == 1 assert inst.positions[ik.fluke.Fluke3000.Module.t3000] == 2 @@ -235,10 +203,7 @@ def test_scan_module_not_implemented(): mod_response[3] = f"ME:R:S#=01:DCC=004:PH={module_id}" # new module id with pytest.raises(NotImplementedError) as err_info: with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - mod_response, - "\r" + ik.fluke.Fluke3000, init_sequence, mod_response, "\r" ) as inst: _ = inst err_msg = err_info.value.args[0] @@ -247,21 +212,17 @@ def test_scan_module_not_implemented(): def test_reset(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - "ri", # 1 - "rfsm 1" # 2 - ], - init_response + - [ - "CR:Ack=0:RI", # 1.1 - "SI:PON=Power On", # 1.2 - "RE:O", # 1.3 - "CR:Ack=0:RFSM:Radio On Master", # 2.1 - "RE:M" # 2.2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + ["ri", "rfsm 1"], # 1 # 2 + init_response + + [ + "CR:Ack=0:RI", # 1.1 + "SI:PON=Power On", # 1.2 + "RE:O", # 1.3 + "CR:Ack=0:RFSM:Radio On Master", # 2.1 + "RE:M", # 2.2 + ], + "\r", ) as inst: inst.reset() @@ -272,15 +233,12 @@ def test_flush(mocker): Mocking `read()` to generate the error. """ with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: # mock read to raise OSError os_error_mock = mocker.Mock() os_error_mock.side_effect = OSError - read_mock = mocker.patch.object(inst, 'read', os_error_mock) + read_mock = mocker.patch.object(inst, "read", os_error_mock) # now flush inst.flush() read_mock.assert_called() @@ -288,22 +246,17 @@ def test_flush(mocker): def test_measure(): with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - "rfemd 01 1", # 1 - "rfemd 01 2", # 2 - "rfemd 02 0" # 3 - ], - init_response + - [ - "CR:Ack=0:RFEMD", # 1.1 - "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 - "CR:Ack=0:RFEMD", # 2 - "CR:Ack=0:RFEMD", # 3.1 - "ME:R:S#=02:DCC=010:PH=FD00C08207220000" # 3.2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + ["rfemd 01 1", "rfemd 01 2", "rfemd 02 0"], # 1 # 2 # 3 + init_response + + [ + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + "CR:Ack=0:RFEMD", # 3.1 + "ME:R:S#=02:DCC=010:PH=FD00C08207220000", # 3.2 + ], + "\r", ) as inst: assert inst.measure(inst.Mode.voltage_dc) == 0.509 * u.volt assert inst.measure(inst.Mode.temperature) == u.Quantity(-25.3, u.degC) @@ -312,10 +265,7 @@ def test_measure(): def test_measure_invalid_mode(): """Raise ValueError if measurement mode is not supported.""" with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: wrong_mode = 42 with pytest.raises(ValueError) as err_info: @@ -331,16 +281,15 @@ def test_measure_no_module_with_mode(): """ mode_not_available = ik.fluke.Fluke3000.Mode.temperature with expected_protocol( - ik.fluke.Fluke3000, - init_sequence_mm_only, - init_response_mm_only, - "\r" + ik.fluke.Fluke3000, init_sequence_mm_only, init_response_mm_only, "\r" ) as inst: with pytest.raises(ValueError) as err_info: inst.measure(mode=mode_not_available) err_msg = err_info.value.args[0] - assert err_msg == f"Device necessary to measure {mode_not_available} " \ - f"is not available" + assert ( + err_msg == f"Device necessary to measure {mode_not_available} " + f"is not available" + ) def test_measure_inconsistent_answer(mocker): @@ -353,34 +302,34 @@ def test_measure_inconsistent_answer(mocker): """ mode_issue = 42 # expect 02, answer something different - unexpected with expected_protocol( - ik.fluke.Fluke3000, - init_sequence + - [ - # bad query - "rfemd 01 1", # 1 - "rfemd 01 2", # 2 - "rfemd 01 2", # 2 - # try again - "rfemd 01 1", # 1 - "rfemd 01 2", # 2 - ], - init_response + - [ - # bad response - "CR:Ack=0:RFEMD", # 1.1 - f"ME:R:S#=01:DCC=010:PH=FD010006{mode_issue}0C0600", # 1.2 - "CR:Ack=0:RFEMD", # 2 - "CR:Ack=0:RFEMD", # 2 - # "", # something to flush - # try again - "CR:Ack=0:RFEMD", # 1.1 - "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 - "CR:Ack=0:RFEMD", # 2 - ], - "\r" + ik.fluke.Fluke3000, + init_sequence + + [ + # bad query + "rfemd 01 1", # 1 + "rfemd 01 2", # 2 + "rfemd 01 2", # 2 + # try again + "rfemd 01 1", # 1 + "rfemd 01 2", # 2 + ], + init_response + + [ + # bad response + "CR:Ack=0:RFEMD", # 1.1 + f"ME:R:S#=01:DCC=010:PH=FD010006{mode_issue}0C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + "CR:Ack=0:RFEMD", # 2 + # "", # something to flush + # try again + "CR:Ack=0:RFEMD", # 1.1 + "ME:R:S#=01:DCC=010:PH=FD010006020C0600", # 1.2 + "CR:Ack=0:RFEMD", # 2 + ], + "\r", ) as inst: # mock out flush - flush_mock = mocker.patch.object(inst, 'flush', return_value=None) + flush_mock = mocker.patch.object(inst, "flush", return_value=None) assert inst.measure(inst.Mode.voltage_dc) == 0.509 * u.volt # assert that flush was called once flush_mock.assert_called_once() @@ -389,27 +338,22 @@ def test_measure_inconsistent_answer(mocker): def test_parse_ph_not_in_result(): """Raise ValueError if 'PH' is not in `result`.""" with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: mode = inst.Mode.temperature bad_result = "42" with pytest.raises(ValueError) as err_info: inst._parse(bad_result, mode) err_msg = err_info.value.args[0] - assert err_msg == "Cannot parse a string that does not contain a " \ - "return value" + assert ( + err_msg == "Cannot parse a string that does not contain a " "return value" + ) def test_parse_wrong_mode(): """Raise ValueError if multimeter not in the right mode.""" with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: mode_requested = inst.Mode.temperature result = "ME:R:S#=01:DCC=010:PH=FD010006020C0600" @@ -417,10 +361,12 @@ def test_parse_wrong_mode(): with pytest.raises(ValueError) as err_info: inst._parse(result, mode_requested) err_msg = err_info.value.args[0] - assert err_msg == f"Mode {mode_requested.name} was requested but " \ - f"the Fluke 3000FC Multimeter is in mode " \ - f"{mode_result.name} instead. Could not read the " \ - f"requested quantity." + assert ( + err_msg == f"Mode {mode_requested.name} was requested but " + f"the Fluke 3000FC Multimeter is in mode " + f"{mode_result.name} instead. Could not read the " + f"requested quantity." + ) def test_parse_factor_wrong_code(): @@ -429,10 +375,7 @@ def test_parse_factor_wrong_code(): byte = format(int(data[6:8], 16), "08b") code = int(byte[1:4], 2) with expected_protocol( - ik.fluke.Fluke3000, - init_sequence, - init_response, - "\r" + ik.fluke.Fluke3000, init_sequence, init_response, "\r" ) as inst: with pytest.raises(ValueError) as err_info: inst._parse_factor(data) diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index 50d19ae78..c5d1bec91 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -20,19 +20,17 @@ def test_scpi_func_gen_amplitude(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "VOLT:UNIT?", - "VOLT?", - "VOLT:UNIT VPP", - "VOLT 2.0", - "VOLT:UNIT DBM", - "VOLT 1.5" - ], [ - "VPP", - "+1.000000E+00" - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + [ + "VOLT:UNIT?", + "VOLT?", + "VOLT:UNIT VPP", + "VOLT 2.0", + "VOLT:UNIT DBM", + "VOLT 1.5", + ], + ["VPP", "+1.000000E+00"], + repeat=2, ) as fg: assert fg.amplitude == (1 * u.V, fg.VoltageMode.peak_to_peak) fg.amplitude = 2 * u.V @@ -45,14 +43,10 @@ def test_scpi_func_gen_amplitude(): def test_scpi_func_gen_frequency(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "FREQ?", - "FREQ 1.005000e+02" - ], [ - "+1.234000E+03" - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + ["FREQ?", "FREQ 1.005000e+02"], + ["+1.234000E+03"], + repeat=2, ) as fg: assert fg.frequency == 1234 * u.Hz fg.frequency = 100.5 * u.Hz @@ -63,14 +57,7 @@ def test_scpi_func_gen_frequency(): def test_scpi_func_gen_function(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "FUNC?", - "FUNC SQU" - ], [ - "SIN" - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, ["FUNC?", "FUNC SQU"], ["SIN"], repeat=2 ) as fg: assert fg.function == fg.Function.sinusoid fg.function = fg.Function.square @@ -81,14 +68,12 @@ def test_scpi_func_gen_function(): def test_scpi_func_gen_offset(): with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - "VOLT:OFFS?", - "VOLT:OFFS 4.321000e-01" - ], [ - "+1.234000E+01", - ], - repeat=2 + ik.generic_scpi.SCPIFunctionGenerator, + ["VOLT:OFFS?", "VOLT:OFFS 4.321000e-01"], + [ + "+1.234000E+01", + ], + repeat=2, ) as fg: assert fg.offset == 12.34 * u.V fg.offset = 0.4321 * u.V @@ -100,10 +85,9 @@ def test_scpi_func_gen_offset(): def test_scpi_func_gen_phase(): """Raise NotImplementedError when set / get phase.""" with expected_protocol( - ik.generic_scpi.SCPIFunctionGenerator, - [ - ], [ - ], + ik.generic_scpi.SCPIFunctionGenerator, + [], + [], ) as fg: with pytest.raises(NotImplementedError): _ = fg.phase diff --git a/instruments/tests/test_generic_scpi/test_scpi_instrument.py b/instruments/tests/test_generic_scpi/test_scpi_instrument.py index 6e18c0d2f..64edb012c 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_instrument.py +++ b/instruments/tests/test_generic_scpi/test_scpi_instrument.py @@ -24,13 +24,7 @@ def test_scpi_instrument_scpi_version(): """Get name of instrument.""" retval = "12345" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "SYST:VERS?" - ], - [ - f"{retval}" - ] + ik.generic_scpi.SCPIInstrument, ["SYST:VERS?"], [f"{retval}"] ) as inst: assert inst.scpi_version == retval @@ -39,13 +33,7 @@ def test_scpi_instrument_scpi_version(): def test_scpi_instrument_op_complete(retval): """Check if operation is completed.""" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*OPC?" - ], - [ - f"{retval}" - ] + ik.generic_scpi.SCPIInstrument, ["*OPC?"], [f"{retval}"] ) as inst: assert inst.op_complete == bool(int(retval)) @@ -54,14 +42,7 @@ def test_scpi_instrument_op_complete(retval): def test_scpi_instrument_power_on_status_off(retval): """Get / set power on status for instrument to on.""" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*PSC 0", - "*PSC?" - ], - [ - "0" - ] + ik.generic_scpi.SCPIInstrument, ["*PSC 0", "*PSC?"], ["0"] ) as inst: inst.power_on_status = retval assert not inst.power_on_status @@ -71,14 +52,7 @@ def test_scpi_instrument_power_on_status_off(retval): def test_scpi_instrument_power_on_status_on(retval): """Get / set power on status for instrument to on.""" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*PSC 1", - "*PSC?" - ], - [ - "1" - ] + ik.generic_scpi.SCPIInstrument, ["*PSC 1", "*PSC?"], ["1"] ) as inst: inst.power_on_status = retval assert inst.power_on_status @@ -86,13 +60,7 @@ def test_scpi_instrument_power_on_status_on(retval): def test_scpi_instrument_power_on_status_value_error(): """Raise ValueError if power on status set with invalid value.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, [], []) as inst: with pytest.raises(ValueError): inst.power_on_status = 42 @@ -100,15 +68,7 @@ def test_scpi_instrument_power_on_status_value_error(): def test_scpi_instrument_self_test_ok(): """Check if self test returns okay.""" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*TST?", - "*TST?" - ], - [ - "0", # ok - "not ok" - ] + ik.generic_scpi.SCPIInstrument, ["*TST?", "*TST?"], ["0", "not ok"] # ok ) as inst: assert inst.self_test_ok assert not inst.self_test_ok @@ -116,53 +76,25 @@ def test_scpi_instrument_self_test_ok(): def test_scpi_instrument_reset(): """Reset the instrument.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*RST" - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, ["*RST"], []) as inst: inst.reset() def test_scpi_instrument_clear(): """Clear the instrument.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*CLS" - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, ["*CLS"], []) as inst: inst.clear() def test_scpi_instrument_trigger(): """Trigger the instrument.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*TRG" - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, ["*TRG"], []) as inst: inst.trigger() def test_scpi_instrument_wait_to_continue(): """Wait to continue the instrument.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - "*WAI" - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, ["*WAI"], []) as inst: inst.wait_to_continue() @@ -171,15 +103,15 @@ def test_scpi_instrument_line_frequency(): freq_hz = 100 freq_mhz = u.Quantity(100000, u.mHz) with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - f"SYST:LFR {freq_hz}", - "SYST:LFR?", - f"SYST:LFR {freq_mhz.to('Hz').magnitude}", - ], - [ - f"{freq_hz}", - ] + ik.generic_scpi.SCPIInstrument, + [ + f"SYST:LFR {freq_hz}", + "SYST:LFR?", + f"SYST:LFR {freq_mhz.to('Hz').magnitude}", + ], + [ + f"{freq_hz}", + ], ) as inst: inst.line_frequency = freq_hz unit_eq(inst.line_frequency, freq_hz * u.hertz) @@ -194,13 +126,11 @@ def test_scpi_instrument_check_error_queue(): err2 = ErrorCodes.invalid_separator err3 = 13 # invalid error number with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - f"SYST:ERR:CODE:ALL?" - ], - [ - f"{err1.value},{err2.value},{err3}", - ] + ik.generic_scpi.SCPIInstrument, + [f"SYST:ERR:CODE:ALL?"], + [ + f"{err1.value},{err2.value},{err3}", + ], ) as inst: assert inst.check_error_queue() == [err2, err3] @@ -209,67 +139,53 @@ def test_scpi_instrument_check_error_queue(): def test_scpi_instrument_display_brightness(val): """Get / set display brightness.""" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - f"DISP:BRIG {val}", - f"DISP:BRIG?" - ], - [ - f"{val}", - ] + ik.generic_scpi.SCPIInstrument, + [f"DISP:BRIG {val}", f"DISP:BRIG?"], + [ + f"{val}", + ], ) as inst: inst.display_brightness = val assert inst.display_brightness == val -@given(val=st.floats(allow_nan=False, allow_infinity=False).filter( - lambda x: x < 0 or x > 1)) +@given( + val=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x < 0 or x > 1 + ) +) def test_scpi_instrument_display_brightness_invalid_value(val): """Raise ValueError if display brightness set with invalid value.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.display_brightness = val err_msg = err_info.value.args[0] - assert err_msg == "Display brightness must be a number between 0 " \ - "and 1." + assert err_msg == "Display brightness must be a number between 0 " "and 1." @given(val=st.floats(min_value=0, max_value=1)) def test_scpi_instrument_display_contrast(val): """Get / set display contrast.""" with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - f"DISP:CONT {val}", - f"DISP:CONT?" - ], - [ - f"{val}", - ] + ik.generic_scpi.SCPIInstrument, + [f"DISP:CONT {val}", f"DISP:CONT?"], + [ + f"{val}", + ], ) as inst: inst.display_contrast = val assert inst.display_contrast == val -@given(val=st.floats(allow_nan=False, allow_infinity=False).filter( - lambda x: x < 0 or x > 1)) +@given( + val=st.floats(allow_nan=False, allow_infinity=False).filter( + lambda x: x < 0 or x > 1 + ) +) def test_scpi_instrument_display_contrast_invalid_value(val): """Raise ValueError if display contrast set with invalid value.""" - with expected_protocol( - ik.generic_scpi.SCPIInstrument, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.generic_scpi.SCPIInstrument, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.display_contrast = val err_msg = err_info.value.args[0] - assert err_msg == "Display contrast must be a number between 0 " \ - "and 1." + assert err_msg == "Display contrast must be a number between 0 " "and 1." diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index 329d9b7de..8db6cd8cc 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -20,13 +20,9 @@ def test_scpi_multimeter_mode(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", - "CONF:CURR:AC" - ], [ - "FRES +1.000000E+01,+3.000000E-06" - ] + ik.generic_scpi.SCPIMultimeter, + ["CONF?", "CONF:CURR:AC"], + ["FRES +1.000000E+01,+3.000000E-06"], ) as dmm: assert dmm.mode == dmm.Mode.fourpt_resistance dmm.mode = dmm.Mode.current_ac @@ -34,13 +30,7 @@ def test_scpi_multimeter_mode(): def test_scpi_multimeter_trigger_mode(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "TRIG:SOUR?", - "TRIG:SOUR EXT" - ], [ - "BUS" - ] + ik.generic_scpi.SCPIMultimeter, ["TRIG:SOUR?", "TRIG:SOUR EXT"], ["BUS"] ) as dmm: assert dmm.trigger_mode == dmm.TriggerMode.bus dmm.trigger_mode = dmm.TriggerMode.external @@ -48,20 +38,21 @@ def test_scpi_multimeter_trigger_mode(): def test_scpi_multimeter_input_range(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", # 1 - "CONF?", # 2 - "CONF?", # 3.1 - "CONF:FRES MIN", # 3.2 - "CONF?", # 4.1 - "CONF:CURR:DC 1" # 4.2 - ], [ - "CURR:AC +1.000000E+01,+3.000000E-06", # 1 - "CURR:AC AUTO,+3.000000E-06", # 2 - "FRES +1.000000E+01,+3.000000E-06", # 3 - "CURR:DC +1.000000E+01,+3.000000E-06" # 4 - ] + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", # 1 + "CONF?", # 2 + "CONF?", # 3.1 + "CONF:FRES MIN", # 3.2 + "CONF?", # 4.1 + "CONF:CURR:DC 1", # 4.2 + ], + [ + "CURR:AC +1.000000E+01,+3.000000E-06", # 1 + "CURR:AC AUTO,+3.000000E-06", # 2 + "FRES +1.000000E+01,+3.000000E-06", # 3 + "CURR:DC +1.000000E+01,+3.000000E-06", # 4 + ], ) as dmm: unit_eq(dmm.input_range, 1e1 * u.amp) assert dmm.input_range == dmm.InputRange.automatic @@ -71,20 +62,21 @@ def test_scpi_multimeter_input_range(): def test_scpi_multimeter_resolution(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", # 1 - "CONF?", # 2 - "CONF?", # 3.1 - "CONF:FRES +1.000000E+01,MIN", # 3.2 - "CONF?", # 4.1 - "CONF:CURR:DC +1.000000E+01,3e-06" # 4.2 - ], [ - "VOLT +1.000000E+01,+3.000000E-06", # 1 - "VOLT +1.000000E+01,MAX", # 2 - "FRES +1.000000E+01,+3.000000E-06", # 3 - "CURR:DC +1.000000E+01,+3.000000E-06" # 4 - ] + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", # 1 + "CONF?", # 2 + "CONF?", # 3.1 + "CONF:FRES +1.000000E+01,MIN", # 3.2 + "CONF?", # 4.1 + "CONF:CURR:DC +1.000000E+01,3e-06", # 4.2 + ], + [ + "VOLT +1.000000E+01,+3.000000E-06", # 1 + "VOLT +1.000000E+01,MAX", # 2 + "FRES +1.000000E+01,+3.000000E-06", # 3 + "CURR:DC +1.000000E+01,+3.000000E-06", # 4 + ], ) as dmm: assert dmm.resolution == 3e-06 assert dmm.resolution == dmm.Resolution.maximum @@ -95,33 +87,26 @@ def test_scpi_multimeter_resolution(): def test_scpi_multimeter_resolution_type_error(): """Raise TypeError if resolution value has the wrong type.""" with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?" - ], [ - "VOLT +1.000000E+01,+3.000000E-06" - ] + ik.generic_scpi.SCPIMultimeter, ["CONF?"], ["VOLT +1.000000E+01,+3.000000E-06"] ) as dmm: wrong_type = "42" with pytest.raises(TypeError) as err_info: dmm.resolution = wrong_type err_msg = err_info.value.args[0] - assert err_msg == ("Resolution must be specified as an int, float, " - "or SCPIMultimeter.Resolution value.") + assert err_msg == ( + "Resolution must be specified as an int, float, " + "or SCPIMultimeter.Resolution value." + ) def test_scpi_multimeter_trigger_count(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "TRIG:COUN?", - "TRIG:COUN?", - "TRIG:COUN MIN", - "TRIG:COUN 10" - ], [ - "+10", - "INF", - ] + ik.generic_scpi.SCPIMultimeter, + ["TRIG:COUN?", "TRIG:COUN?", "TRIG:COUN MIN", "TRIG:COUN 10"], + [ + "+10", + "INF", + ], ) as dmm: assert dmm.trigger_count == 10 assert dmm.trigger_count == dmm.TriggerCount.infinity @@ -131,32 +116,25 @@ def test_scpi_multimeter_trigger_count(): def test_scpi_multimeter_trigger_count_type_error(): """Raise TypeError if trigger count value has the wrong type.""" - with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - ], [ - ] - ) as dmm: + with expected_protocol(ik.generic_scpi.SCPIMultimeter, [], []) as dmm: wrong_type = "42" with pytest.raises(TypeError) as err_info: dmm.trigger_count = wrong_type err_msg = err_info.value.args[0] - assert err_msg == ("Trigger count must be specified as an int " - "or SCPIMultimeter.TriggerCount value.") + assert err_msg == ( + "Trigger count must be specified as an int " + "or SCPIMultimeter.TriggerCount value." + ) def test_scpi_multimeter_sample_count(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "SAMP:COUN?", - "SAMP:COUN?", - "SAMP:COUN MIN", - "SAMP:COUN 10" - ], [ - "+10", - "MAX", - ] + ik.generic_scpi.SCPIMultimeter, + ["SAMP:COUN?", "SAMP:COUN?", "SAMP:COUN MIN", "SAMP:COUN 10"], + [ + "+10", + "MAX", + ], ) as dmm: assert dmm.sample_count == 10 assert dmm.sample_count == dmm.SampleCount.maximum @@ -166,28 +144,27 @@ def test_scpi_multimeter_sample_count(): def test_scpi_multimeter_sample_count_type_error(): """Raise TypeError if sample count is of invalid type.""" - with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - ], [ - ] - ) as dmm: + with expected_protocol(ik.generic_scpi.SCPIMultimeter, [], []) as dmm: wrong_type = "42" with pytest.raises(TypeError) as err_info: dmm.sample_count = wrong_type err_msg = err_info.value.args[0] - assert err_msg == ("Sample count must be specified as an int " - "or SCPIMultimeter.SampleCount value.") + assert err_msg == ( + "Sample count must be specified as an int " + "or SCPIMultimeter.SampleCount value." + ) + def test_scpi_multimeter_trigger_delay(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "TRIG:DEL?", - f"TRIG:DEL {1:e}", - ], [ - "+1", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "TRIG:DEL?", + f"TRIG:DEL {1:e}", + ], + [ + "+1", + ], ) as dmm: unit_eq(dmm.trigger_delay, 1 * u.second) dmm.trigger_delay = 1000 * u.millisecond @@ -195,13 +172,14 @@ def test_scpi_multimeter_trigger_delay(): def test_scpi_multimeter_sample_source(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "SAMP:SOUR?", - "SAMP:SOUR TIM", - ], [ - "IMM", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "SAMP:SOUR?", + "SAMP:SOUR TIM", + ], + [ + "IMM", + ], ) as dmm: assert dmm.sample_source == dmm.SampleSource.immediate dmm.sample_source = dmm.SampleSource.timer @@ -209,13 +187,14 @@ def test_scpi_multimeter_sample_source(): def test_scpi_multimeter_sample_timer(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "SAMP:TIM?", - f"SAMP:TIM {1:e}", - ], [ - "+1", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "SAMP:TIM?", + f"SAMP:TIM {1:e}", + ], + [ + "+1", + ], ) as dmm: unit_eq(dmm.sample_timer, 1 * u.second) dmm.sample_timer = 1000 * u.millisecond @@ -223,12 +202,7 @@ def test_scpi_multimeter_sample_timer(): def test_scpi_multimeter_relative_not_implemented(): """Raise NotImplementedError when set / get relative.""" - with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - ], [ - ] - ) as dmm: + with expected_protocol(ik.generic_scpi.SCPIMultimeter, [], []) as dmm: with pytest.raises(NotImplementedError): _ = dmm.relative with pytest.raises(NotImplementedError): @@ -237,12 +211,13 @@ def test_scpi_multimeter_relative_not_implemented(): def test_scpi_multimeter_measure(): with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "MEAS:VOLT:DC?", - ], [ - "+4.23450000E-03", - ] + ik.generic_scpi.SCPIMultimeter, + [ + "MEAS:VOLT:DC?", + ], + [ + "+4.23450000E-03", + ], ) as dmm: unit_eq(dmm.measure(dmm.Mode.voltage_dc), 4.2345e-03 * u.volt) @@ -250,30 +225,27 @@ def test_scpi_multimeter_measure(): def test_scpi_multimeter_measure_mode_none(): """Read current mode if not specified, test with volt, DC mode.""" with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - "CONF?", - "MEAS:VOLT:DC?", - ], [ - "VOLT:DC", - "+4.23450000E-03", - ] - + ik.generic_scpi.SCPIMultimeter, + [ + "CONF?", + "MEAS:VOLT:DC?", + ], + [ + "VOLT:DC", + "+4.23450000E-03", + ], ) as dmm: unit_eq(dmm.measure(), 4.2345e-03 * u.volt) def test_scpi_multimeter_measure_invalid_mode(): """Raise TypeError if mode is not of type SCPIMultimeter.Mode.""" - with expected_protocol( - ik.generic_scpi.SCPIMultimeter, - [ - ], [ - ] - ) as dmm: + with expected_protocol(ik.generic_scpi.SCPIMultimeter, [], []) as dmm: wrong_type = 42 with pytest.raises(TypeError) as err_info: dmm.measure(mode=wrong_type) err_msg = err_info.value.args[0] - assert err_msg == f"Mode must be specified as a SCPIMultimeter.Mode " \ - f"value, got {type(wrong_type)} instead." + assert ( + err_msg == f"Mode must be specified as a SCPIMultimeter.Mode " + f"value, got {type(wrong_type)} instead." + ) diff --git a/instruments/tests/test_gentec_eo/test_blu.py b/instruments/tests/test_gentec_eo/test_blu.py index f8c777f19..7c0426126 100644 --- a/instruments/tests/test_gentec_eo/test_blu.py +++ b/instruments/tests/test_gentec_eo/test_blu.py @@ -24,12 +24,10 @@ def test_blu_initialization(): """Initialize the device.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - ], - [ - ], - sep="\r\n", + ik.gentec_eo.Blu, + [], + [], + sep="\r\n", ) as blu: assert blu.terminator == "\r\n" assert blu._power_mode is None @@ -41,16 +39,10 @@ def test_blu_initialization(): def test_blu_anticipation(): """Get / Set the instrument into anticipation mode.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GAN", - "*ANT0" - ], - [ - "Anticipation: 1", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GAN", "*ANT0"], + ["Anticipation: 1", "ACK"], + sep="\r\n", ) as blu: assert blu.anticipation blu.anticipation = False @@ -59,16 +51,10 @@ def test_blu_anticipation(): def test_blu_auto_scale(): """Get / Set the instrument into automatic scaling mode.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GAS", - "*SAS0" - ], - [ - "Autoscale: 1", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GAS", "*SAS0"], + ["Autoscale: 1", "ACK"], + sep="\r\n", ) as blu: assert blu.auto_scale blu.auto_scale = False @@ -85,20 +71,18 @@ def test_blu_available_scales(): send all the data. """ with expected_protocol( - ik.gentec_eo.Blu, - [ - "*DVS" - ], - [ - "[22]: 100.0 m\r\n" - "[23]: 300.0 m\r\n" - "[24]: 1.000\r\n" - "[25]: 3.000\r\n" - "[26]: 10.00\r\n" - "[27]: 30.00\r\n" - "[28]: 100.0\r\n" - ], - sep="", + ik.gentec_eo.Blu, + ["*DVS"], + [ + "[22]: 100.0 m\r\n" + "[23]: 300.0 m\r\n" + "[24]: 1.000\r\n" + "[25]: 3.000\r\n" + "[26]: 10.00\r\n" + "[27]: 30.00\r\n" + "[28]: 100.0\r\n" + ], + sep="", ) as blu: ret_scale = [ blu.Scale.max100milli, @@ -107,7 +91,7 @@ def test_blu_available_scales(): blu.Scale.max3, blu.Scale.max10, blu.Scale.max30, - blu.Scale.max100 + blu.Scale.max100, ] assert blu.available_scales == ret_scale @@ -120,14 +104,10 @@ def test_blu_available_scales_error(): case raises a ValueError. """ with expected_protocol( - ik.gentec_eo.Blu, - [ - "*DVS" - ], - [ - "bogus" - ], - sep="", + ik.gentec_eo.Blu, + ["*DVS"], + ["bogus"], + sep="", ) as blu: _terminator = blu.terminator _timeout = blu.timeout @@ -140,14 +120,10 @@ def test_blu_available_scales_error(): def test_blu_battery_state(): """Get the battery state of the instrument in percent.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*QSO" - ], - [ - "98" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*QSO"], + ["98"], + sep="\r\n", ) as blu: assert blu.battery_state == u.Quantity(98, u.percent) @@ -155,16 +131,10 @@ def test_blu_battery_state(): def test_blu_current_value_watts(): """Get the current value in Watt mode.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GMD", - "*CVU" - ], - [ - "Mode: 0", - "42" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GMD", "*CVU"], + ["Mode: 0", "42"], + sep="\r\n", ) as blu: assert blu.current_value == u.Quantity(42, u.W) @@ -172,16 +142,10 @@ def test_blu_current_value_watts(): def test_blu_current_value_joules(): """Get the current value in Watt mode.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GMD", - "*CVU" - ], - [ - "Mode: 2", - "42" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GMD", "*CVU"], + ["Mode: 2", "42"], + sep="\r\n", ) as blu: assert blu.current_value == u.Quantity(42, u.J) @@ -192,14 +156,10 @@ def test_blu_head_type(): Here, an example head is returned. """ with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GFW" - ], - [ - "NIG : 104552, Wattmeter, V1.95" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GFW"], + ["NIG : 104552, Wattmeter, V1.95"], + sep="\r\n", ) as blu: example_head = "NIG : 104552, Wattmeter, V1.95" assert blu.head_type == example_head @@ -213,16 +173,10 @@ def test_blu_measure_mode(): before. """ with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GMD", - "*GMD" - ], - [ - "Mode: 0", - "Mode: 2" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GMD", "*GMD"], + ["Mode: 0", "Mode: 2"], + sep="\r\n", ) as blu: # power mode assert blu.measure_mode == "power" @@ -236,16 +190,10 @@ def test_blu_measure_mode(): def test_blu_new_value_ready(): """Query if a new value is ready for reading.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*NVU", - "*NVU" - ], - [ - "New Data Not Available", - "New Data Available" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*NVU", "*NVU"], + ["New Data Not Available", "New Data Available"], + sep="\r\n", ) as blu: assert not blu.new_value_ready assert blu.new_value_ready @@ -255,16 +203,10 @@ def test_blu_new_value_ready(): def test_blu_scale(scale): """Get / set the instrument scale manually.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - f"*SCS{scale.value}", - "*GCR" - ], - [ - "ACK", - f"Range: {scale.value}" - ], - sep="\r\n", + ik.gentec_eo.Blu, + [f"*SCS{scale.value}", "*GCR"], + ["ACK", f"Range: {scale.value}"], + sep="\r\n", ) as blu: blu.scale = scale assert blu.scale == scale @@ -273,16 +215,10 @@ def test_blu_scale(scale): def test_blu_single_shot_energy_mode(): """Get / set the single shot energy mode.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GSE", - "*SSE1" - ], - [ - "SSE: 0", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GSE", "*SSE1"], + ["SSE: 0", "ACK"], + sep="\r\n", ) as blu: assert not blu.single_shot_energy_mode assert blu._power_mode @@ -293,20 +229,15 @@ def test_blu_single_shot_energy_mode(): def test_blu_trigger_level(): """Get / set the trigger level.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GTL", - "*STL53.4", - "*STL01.2", - "*STL1.23" - ], - [ - "Trigger level: 15.4% (4.6 Watts) of max power: 30 Watts", - "ACK", - "ACK", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GTL", "*STL53.4", "*STL01.2", "*STL1.23"], + [ + "Trigger level: 15.4% (4.6 Watts) of max power: 30 Watts", + "ACK", + "ACK", + "ACK", + ], + sep="\r\n", ) as blu: assert blu.trigger_level == 0.154 blu.trigger_level = 0.534 @@ -317,12 +248,10 @@ def test_blu_trigger_level(): def test_blu_trigger_level_invalid_value(): """Raise error when trigger level value set is out of bound.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - ], - [ - ], - sep="\r\n", + ik.gentec_eo.Blu, + [], + [], + sep="\r\n", ) as blu: with pytest.raises(ValueError): blu.trigger_level = -0.3 @@ -333,14 +262,10 @@ def test_blu_trigger_level_invalid_value(): def test_blu_usb_state(): """Get the status if USB cable is plugged in.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*USB" - ], - [ - "USB: 1" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*USB"], + ["USB: 1"], + sep="\r\n", ) as blu: assert blu.usb_state @@ -348,37 +273,22 @@ def test_blu_usb_state(): def test_blu_user_multiplier(): """Get / set user multiplier.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GUM", - "*MUL435.6666" - ], - [ - "User Multiplier: 3.3000000e+01", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GUM", "*MUL435.6666"], + ["User Multiplier: 3.3000000e+01", "ACK"], + sep="\r\n", ) as blu: - assert blu.user_multiplier == 33. + assert blu.user_multiplier == 33.0 blu.user_multiplier = 435.6666 def test_blu_user_offset_watts(): """Get / set user offset in watts.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GMD", # get power mode - "*GUO", - "*OFF000042.0" - - ], - [ - "Mode: 0", # power mode watts - "User Offset : 1.500e-3", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GMD", "*GUO", "*OFF000042.0"], # get power mode + ["Mode: 0", "User Offset : 1.500e-3", "ACK"], # power mode watts + sep="\r\n", ) as blu: assert blu.user_offset == u.Quantity(1.5, u.mW) blu.user_offset = u.Quantity(42.0, u.W) @@ -387,19 +297,10 @@ def test_blu_user_offset_watts(): def test_blu_user_offset_joules(): """Get / set user offset in joules.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GMD", # get power mode - "*GUO", - "*OFF000042.0" - - ], - [ - "Mode: 2", # power mode joules - "User Offset : 1.500e-3", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GMD", "*GUO", "*OFF000042.0"], # get power mode + ["Mode: 2", "User Offset : 1.500e-3", "ACK"], # power mode joules + sep="\r\n", ) as blu: assert blu.user_offset == u.Quantity(0.0015, u.J) blu.user_offset = u.Quantity(42.0, u.J) @@ -408,15 +309,10 @@ def test_blu_user_offset_joules(): def test_blu_user_offset_unitless(): """Set user offset unitless.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*OFF000042.0" - - ], - [ - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*OFF000042.0"], + ["ACK"], + sep="\r\n", ) as blu: blu.user_offset = 42.0 @@ -424,12 +320,10 @@ def test_blu_user_offset_unitless(): def test_blu_user_offset_unit_error(): """Raise ValueError if unit is invalid.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - ], - [ - ], - sep="\r\n", + ik.gentec_eo.Blu, + [], + [], + sep="\r\n", ) as blu: with pytest.raises(ValueError): blu.user_offset = u.Quantity(42, u.mm) @@ -438,14 +332,10 @@ def test_blu_user_offset_unit_error(): def test_blu_version(): """Query version of device.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*VER" - ], - [ - "Blu firmware Version 1.95" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*VER"], + ["Blu firmware Version 1.95"], + sep="\r\n", ) as blu: version = "Blu firmware Version 1.95" assert blu.version == version @@ -454,18 +344,10 @@ def test_blu_version(): def test_blu_wavelength(): """Get / set the wavelength.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GWL", - "*PWC00527", - "*PWC00527" - ], - [ - "PWC: 1064", - "ACK", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GWL", "*PWC00527", "*PWC00527"], + ["PWC: 1064", "ACK", "ACK"], + sep="\r\n", ) as blu: assert blu.wavelength == u.Quantity(1064, u.nm) blu.wavelength = u.Quantity(0.527, u.um) @@ -475,16 +357,10 @@ def test_blu_wavelength(): def test_blu_wavelength_out_of_bound(): """Get / set the wavelength when value is out of bound.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*PWC00000", - "*PWC00000" - ], - [ - "ACK", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*PWC00000", "*PWC00000"], + ["ACK", "ACK"], + sep="\r\n", ) as blu: blu.wavelength = u.Quantity(1000, u.um) blu.wavelength = -3 @@ -493,18 +369,10 @@ def test_blu_wavelength_out_of_bound(): def test_blu_zero_offset(): """Get / set the zero offset.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*GZO", - "*SOU", - "*COU" - ], - [ - "Zero: 1", - "ACK", - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*GZO", "*SOU", "*COU"], + ["Zero: 1", "ACK", "ACK"], + sep="\r\n", ) as blu: assert blu.zero_offset blu.zero_offset = True @@ -517,14 +385,10 @@ def test_blu_zero_offset(): def test_blu_confirm_connection(): """Confirm a bluetooth connection.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*RDY" - ], - [ - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*RDY"], + ["ACK"], + sep="\r\n", ) as blu: blu.confirm_connection() @@ -532,14 +396,10 @@ def test_blu_confirm_connection(): def test_blu_disconnect(): """Disconnect bluetooth connection.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*BTD" - ], - [ - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*BTD"], + ["ACK"], + sep="\r\n", ) as blu: blu.disconnect() @@ -547,14 +407,10 @@ def test_blu_disconnect(): def test_blu_scale_down(): """Set the scale one level lower.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*SSD" - ], - [ - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*SSD"], + ["ACK"], + sep="\r\n", ) as blu: blu.scale_down() @@ -562,14 +418,10 @@ def test_blu_scale_down(): def test_blu_scale_up(): """Set the scale one level higher.""" with expected_protocol( - ik.gentec_eo.Blu, - [ - "*SSU" - ], - [ - "ACK" - ], - sep="\r\n", + ik.gentec_eo.Blu, + ["*SSU"], + ["ACK"], + sep="\r\n", ) as blu: blu.scale_up() @@ -580,17 +432,15 @@ def test_no_ack_query_error(mocker): Mocking query here in order to raise an error on query. """ with expected_protocol( - ik.gentec_eo.Blu, - [ - ], - [ - ], - sep="\r\n", + ik.gentec_eo.Blu, + [], + [], + sep="\r\n", ) as blu: # mock query w/ IOError io_error_mock = mocker.Mock() io_error_mock.side_effect = IOError - mocker.patch.object(blu, 'query', io_error_mock) + mocker.patch.object(blu, "query", io_error_mock) # do the query with pytest.raises(IOError): _ = blu._no_ack_query("QUERY") @@ -602,11 +452,14 @@ def test_no_ack_query_error(mocker): def test_format_eight_type(): """Ensure type returned is string.""" - assert isinstance(ik.gentec_eo.blu._format_eight(3.), str) + assert isinstance(ik.gentec_eo.blu._format_eight(3.0), str) -@given(value=st.floats(min_value=-1e100, max_value=1e100, - exclude_min=True, exclude_max=True)) +@given( + value=st.floats( + min_value=-1e100, max_value=1e100, exclude_min=True, exclude_max=True + ) +) def test_format_eight_length_values(value): """Ensure format eight routine works. @@ -615,5 +468,5 @@ def test_format_eight_length_values(value): and that it is correct to 1% with given number. """ value_read = ik.gentec_eo.blu._format_eight(value) - assert value == pytest.approx(float(value_read), abs(value) / 100.) + assert value == pytest.approx(float(value_read), abs(value) / 100.0) assert len(value_read) == 8 diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index a99d84734..a402294ca 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -17,6 +17,7 @@ # pylint: disable=protected-access + def set_defaults(inst): """ Sets default values for the voltage and current range of the Glassman FR @@ -28,28 +29,17 @@ def set_defaults(inst): def test_channel(): - with expected_protocol( - ik.glassman.GlassmanFR, - [], - [], - "\r" - ) as inst: + with expected_protocol(ik.glassman.GlassmanFR, [], [], "\r") as inst: assert len(inst.channel) == 1 assert inst.channel[0] == inst def test_voltage(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51", - "\x01S3330000000001CD" - ], - [ - "R00000000000040", - "A" - ], - "\r" + ik.glassman.GlassmanFR, + ["\x01Q51", "\x01S3330000000001CD"], + ["R00000000000040", "A"], + "\r", ) as inst: set_defaults(inst) inst.voltage = 10.0 * u.kilovolt @@ -58,16 +48,10 @@ def test_voltage(): def test_current(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51", - "\x01S0003330000001CD" - ], - [ - "R00000000000040", - "A" - ], - "\r" + ik.glassman.GlassmanFR, + ["\x01Q51", "\x01S0003330000001CD"], + ["R00000000000040", "A"], + "\r", ) as inst: set_defaults(inst) inst.current = 1.2 * u.milliamp @@ -76,14 +60,7 @@ def test_current(): def test_voltage_sense(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51" - ], - [ - "R10A00000010053" - ], - "\r" + ik.glassman.GlassmanFR, ["\x01Q51"], ["R10A00000010053"], "\r" ) as inst: set_defaults(inst) assert round(inst.voltage_sense) == 13.0 * u.kilovolt @@ -91,14 +68,7 @@ def test_voltage_sense(): def test_current_sense(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51" - ], - [ - "R0001550001004C" - ], - "\r" + ik.glassman.GlassmanFR, ["\x01Q51"], ["R0001550001004C"], "\r" ) as inst: set_defaults(inst) assert inst.current_sense == 2.0 * u.milliamp @@ -106,16 +76,10 @@ def test_current_sense(): def test_mode(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51", - "\x01Q51" - ], - [ - "R00000000000040", - "R00000000010041" - ], - "\r" + ik.glassman.GlassmanFR, + ["\x01Q51", "\x01Q51"], + ["R00000000000040", "R00000000010041"], + "\r", ) as inst: assert inst.mode == inst.Mode.voltage assert inst.mode == inst.Mode.current @@ -123,20 +87,10 @@ def test_mode(): def test_output(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01S0000000000001C4", - "\x01Q51", - "\x01S0000000000002C5", - "\x01Q51" - ], - [ - "A", - "R00000000000040", - "A", - "R00000000040044" - ], - "\r" + ik.glassman.GlassmanFR, + ["\x01S0000000000001C4", "\x01Q51", "\x01S0000000000002C5", "\x01Q51"], + ["A", "R00000000000040", "A", "R00000000040044"], + "\r", ) as inst: inst.output = False assert not inst.output @@ -146,14 +100,7 @@ def test_output(): def test_output_type_error(): """Raise TypeError when setting output w non-boolean value.""" - with expected_protocol( - ik.glassman.GlassmanFR, - [ - ], - [ - ], - "\r" - ) as inst: + with expected_protocol(ik.glassman.GlassmanFR, [], [], "\r") as inst: with pytest.raises(TypeError) as err_info: inst.output = 42 err_msg = err_info.value.args[0] @@ -164,44 +111,26 @@ def test_output_type_error(): def test_fault(value): """Get the instrument status: True if fault.""" with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q51" - ], - [ - f"R000000000{value}004{value}", - ], - "\r" + ik.glassman.GlassmanFR, + ["\x01Q51"], + [ + f"R000000000{value}004{value}", + ], + "\r", ) as inst: assert inst.fault == bool(value) def test_version(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01V56" - ], - [ - "B1465" - ], - "\r" + ik.glassman.GlassmanFR, ["\x01V56"], ["B1465"], "\r" ) as inst: assert inst.version == "14" def test_device_timeout(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01C073", - "\x01C174" - ], - [ - "A", - "A" - ], - "\r" + ik.glassman.GlassmanFR, ["\x01C073", "\x01C174"], ["A", "A"], "\r" ) as inst: inst.device_timeout = True assert inst.device_timeout @@ -211,14 +140,7 @@ def test_device_timeout(): def test_device_timeout_type_error(): """Raise TypeError if device timeout mode not set with boolean.""" - with expected_protocol( - ik.glassman.GlassmanFR, - [ - ], - [ - ], - "\r" - ) as inst: + with expected_protocol(ik.glassman.GlassmanFR, [], [], "\r") as inst: with pytest.raises(TypeError) as err_info: inst.device_timeout = 42 err_msg = err_info.value.args[0] @@ -226,14 +148,7 @@ def test_device_timeout_type_error(): def test_sendcmd(): - with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01123ABC5C" - ], - [], - "\r" - ) as inst: + with expected_protocol(ik.glassman.GlassmanFR, ["\x01123ABC5C"], [], "\r") as inst: inst.sendcmd("123ABC") @@ -241,14 +156,7 @@ def test_query(): """Query the instrument.""" response = "R123ABC5C" with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q123ABCAD" - ], - [ - response - ], - "\r" + ik.glassman.GlassmanFR, ["\x01Q123ABCAD"], [response], "\r" ) as inst: assert inst.query("Q123ABC") == response[1:-2] @@ -257,14 +165,7 @@ def test_query_invalid_response_code(): """Raise ValueError when query receives an invalid response code.""" response = "A123ABC5C" with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q123ABCAD" - ], - [ - response - ], - "\r" + ik.glassman.GlassmanFR, ["\x01Q123ABCAD"], [response], "\r" ) as inst: with pytest.raises(ValueError) as err_info: inst.query("Q123ABC") @@ -276,14 +177,7 @@ def test_query_invalid_checksum(): """Raise ValueError if query returns with invalid checksum.""" response = "R123ABC5A" with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q123ABCAD" - ], - [ - response - ], - "\r" + ik.glassman.GlassmanFR, ["\x01Q123ABCAD"], [response], "\r" ) as inst: with pytest.raises(ValueError) as err_info: inst.query("Q123ABC") @@ -298,14 +192,7 @@ def test_query_error(err): check_sum = ord(err_code) % 256 response = f"E{err_code}{format(check_sum, '02X')}" with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01Q123ABCAD" - ], - [ - response - ], - "\r" + ik.glassman.GlassmanFR, ["\x01Q123ABCAD"], [response], "\r" ) as inst: with pytest.raises(ValueError) as err_info: inst.query("Q123ABC") @@ -315,33 +202,20 @@ def test_query_error(err): def test_reset(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01S0000000000004C7" - ], - [ - "A" - ], - "\r" + ik.glassman.GlassmanFR, ["\x01S0000000000004C7"], ["A"], "\r" ) as inst: inst.reset() def test_set_status(): with expected_protocol( - ik.glassman.GlassmanFR, - [ - "\x01S3333330000002D7", - "\x01Q51" - ], - [ - "A", - "R00000000040044" - ], - "\r" + ik.glassman.GlassmanFR, + ["\x01S3333330000002D7", "\x01Q51"], + ["A", "R00000000040044"], + "\r", ) as inst: set_defaults(inst) - inst.set_status(voltage=10*u.kilovolt, current=1.2*u.milliamp, output=True) + inst.set_status(voltage=10 * u.kilovolt, current=1.2 * u.milliamp, output=True) assert inst.output assert inst.voltage == 10 * u.kilovolt assert inst.current == 1.2 * u.milliamp @@ -350,14 +224,7 @@ def test_set_status(): def test_parse_invalid_response(): """Raise a RunTime error if response cannot be parsed.""" response = "000000000X00" # invalid monitors - with expected_protocol( - ik.glassman.GlassmanFR, - [ - ], - [ - ], - "\r" - ) as inst: + with expected_protocol(ik.glassman.GlassmanFR, [], [], "\r") as inst: with pytest.raises(RuntimeError) as err_info: inst._parse_response(response) err_msg = err_info.value.args[0] diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index 870184c75..a6160479f 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -20,44 +20,34 @@ def test_hs9000_name(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:IDN?" - ], - [ - ":CH1:CH2:FOO", - "Foobar name" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:IDN?"], + [":CH1:CH2:FOO", "Foobar name"], + sep="\n", ) as hs: assert hs.name == "Foobar name" def test_channel_idx_list(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ], - [ - ":CH1:CH2:FOO" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ], + [":CH1:CH2:FOO"], + sep="\n", ) as hs: assert hs._channel_idxs() == [0, 1, "FOO"] def test_channel_returns_inner_class(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ], - [ - ":CH1:CH2:FOO" - ], - sep="\n" + ik.holzworth.HS9000, + [ + ":ATTACH?", + ], + [":CH1:CH2:FOO"], + sep="\n", ) as hs: channel = hs.channel[0] assert isinstance(channel, hs.Channel) is True @@ -105,16 +95,10 @@ def test_channel_save_state(): def test_channel_temperature(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:TEMP?" - ], - [ - ":CH1:CH2:FOO", - "10 C" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:TEMP?"], + [":CH1:CH2:FOO", "10 C"], + sep="\n", ) as hs: channel = hs.channel[0] assert channel.temperature == u.Quantity(10, u.degC) @@ -122,20 +106,10 @@ def test_channel_temperature(): def test_channel_frequency_getter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:FREQ?", - ":CH1:FREQ:MIN?", - ":CH1:FREQ:MAX?" - ], - [ - ":CH1:CH2:FOO", - "1000 MHz", - "100 MHz", - "10 GHz" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:FREQ?", ":CH1:FREQ:MIN?", ":CH1:FREQ:MAX?"], + [":CH1:CH2:FOO", "1000 MHz", "100 MHz", "10 GHz"], + sep="\n", ) as hs: channel = hs.channel[0] assert channel.frequency == 1 * u.GHz @@ -145,19 +119,10 @@ def test_channel_frequency_getter(): def test_channel_frequency_setter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:FREQ:MIN?", - ":CH1:FREQ:MAX?", - ":CH1:FREQ {:e}".format(1) - ], - [ - ":CH1:CH2:FOO", - "100 MHz", - "10 GHz" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:FREQ:MIN?", ":CH1:FREQ:MAX?", ":CH1:FREQ {:e}".format(1)], + [":CH1:CH2:FOO", "100 MHz", "10 GHz"], + sep="\n", ) as hs: channel = hs.channel[0] channel.frequency = 1 * u.GHz @@ -165,20 +130,10 @@ def test_channel_frequency_setter(): def test_channel_power_getter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PWR?", - ":CH1:PWR:MIN?", - ":CH1:PWR:MAX?" - ], - [ - ":CH1:CH2:FOO", - "0", - "-100", - "20" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:PWR?", ":CH1:PWR:MIN?", ":CH1:PWR:MAX?"], + [":CH1:CH2:FOO", "0", "-100", "20"], + sep="\n", ) as hs: channel = hs.channel[0] assert channel.power == u.Quantity(0, u.dBm) @@ -188,19 +143,10 @@ def test_channel_power_getter(): def test_channel_power_setter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PWR:MIN?", - ":CH1:PWR:MAX?", - ":CH1:PWR {:e}".format(0) - ], - [ - ":CH1:CH2:FOO", - "-100", - "20" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:PWR:MIN?", ":CH1:PWR:MAX?", ":CH1:PWR {:e}".format(0)], + [":CH1:CH2:FOO", "-100", "20"], + sep="\n", ) as hs: channel = hs.channel[0] channel.power = u.Quantity(0, u.dBm) @@ -208,20 +154,10 @@ def test_channel_power_setter(): def test_channel_phase_getter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PHASE?", - ":CH1:PHASE:MIN?", - ":CH1:PHASE:MAX?" - ], - [ - ":CH1:CH2:FOO", - "0", - "-180", - "+180" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:PHASE?", ":CH1:PHASE:MIN?", ":CH1:PHASE:MAX?"], + [":CH1:CH2:FOO", "0", "-180", "+180"], + sep="\n", ) as hs: channel = hs.channel[0] assert channel.phase == 0 * u.degree @@ -231,19 +167,10 @@ def test_channel_phase_getter(): def test_channel_phase_setter(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PHASE:MIN?", - ":CH1:PHASE:MAX?", - ":CH1:PHASE {:e}".format(0) - ], - [ - ":CH1:CH2:FOO", - "-180", - "+180" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:PHASE:MIN?", ":CH1:PHASE:MAX?", ":CH1:PHASE {:e}".format(0)], + [":CH1:CH2:FOO", "-180", "+180"], + sep="\n", ) as hs: channel = hs.channel[0] channel.phase = 0 * u.degree @@ -251,18 +178,10 @@ def test_channel_phase_setter(): def test_channel_output(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":ATTACH?", - ":CH1:PWR:RF?", - ":CH1:PWR:RF:ON", - ":CH1:PWR:RF:OFF" - ], - [ - ":CH1:CH2:FOO", - "OFF" - ], - sep="\n" + ik.holzworth.HS9000, + [":ATTACH?", ":CH1:PWR:RF?", ":CH1:PWR:RF:ON", ":CH1:PWR:RF:OFF"], + [":CH1:CH2:FOO", "OFF"], + sep="\n", ) as hs: channel = hs.channel[0] assert channel.output is False @@ -272,16 +191,10 @@ def test_channel_output(): def test_hs9000_is_ready(): with expected_protocol( - ik.holzworth.HS9000, - [ - ":COMM:READY?", - ":COMM:READY?" - ], - [ - "Ready", - "DANGER DANGER" - ], - sep="\n" + ik.holzworth.HS9000, + [":COMM:READY?", ":COMM:READY?"], + ["Ready", "DANGER DANGER"], + sep="\n", ) as hs: assert hs.ready is True assert hs.ready is False diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index f4fed1098..2bcc628bf 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -22,49 +22,32 @@ @pytest.fixture(autouse=True) def time_mock(mocker): """Mock out time to speed up.""" - return mocker.patch.object(time, 'sleep', return_value=None) + return mocker.patch.object(time, "sleep", return_value=None) def test_hp3456a_trigger_mode(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "T4", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "T4", + ], + [""], + sep="\r", ) as dmm: dmm.trigger_mode = dmm.TriggerMode.hold def test_hp3456a_number_of_digits(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W6STG", - "REG" - ], [ - "+06.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "W6STG", "REG"], ["+06.00000E+0"], sep="\r" ) as dmm: dmm.number_of_digits = 7 def test_hp3456a_number_of_digits_invalid(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W6STG", - "REG" - ], [ - "+06.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "W6STG", "REG"], ["+06.00000E+0"], sep="\r" ) as dmm: dmm.number_of_digits = 6 assert dmm.number_of_digits == 6 @@ -72,29 +55,20 @@ def test_hp3456a_number_of_digits_invalid(): def test_hp3456a_auto_range(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "R1W", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "R1W", + ], + [""], + sep="\r", ) as dmm: dmm.auto_range() def test_hp3456a_number_of_readings(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W10STN", - "REN" - ], [ - "+10.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "W10STN", "REN"], ["+10.00000E+0"], sep="\r" ) as dmm: dmm.number_of_readings = 10 assert dmm.number_of_readings == 10 @@ -102,15 +76,7 @@ def test_hp3456a_number_of_readings(): def test_hp3456a_nplc(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W1STI", - "REI" - ], [ - "+1.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "W1STI", "REI"], ["+1.00000E+0"], sep="\r" ) as dmm: dmm.nplc = 1 assert dmm.nplc == 1 @@ -118,72 +84,59 @@ def test_hp3456a_nplc(): def test_hp3456a_nplc_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "W1STI", - "REI" - ], [ - "+1.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "W1STI", "REI"], ["+1.00000E+0"], sep="\r" ) as dmm: dmm.nplc = 0 def test_hp3456a_mode(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "S0F4", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "S0F4", + ], + [""], + sep="\r", ) as dmm: dmm.mode = dmm.Mode.resistance_2wire def test_hp3456a_math_mode(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "M2", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "M2", + ], + [""], + sep="\r", ) as dmm: dmm.math_mode = dmm.MathMode.statistic def test_hp3456a_trigger(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "T3", - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "T3", + ], + [""], + sep="\r", ) as dmm: dmm.trigger() def test_hp3456a_fetch(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1" - ], - [ - "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0", - "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0" - ], - sep="\r" + ik.hp.HP3456a, + ["HO0T4SO1"], + [ + "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0", + "+000.1055E+0,+000.1043E+0,+000.1005E+0,+000.1014E+0", + ], + sep="\r", ) as dmm: v = dmm.fetch(dmm.Mode.resistance_2wire) assert v == [0.1055 * u.ohm, 0.1043 * u.ohm, 0.1005 * u.ohm, 0.1014 * u.ohm] @@ -193,57 +146,46 @@ def test_hp3456a_fetch(): def test_hp3456a_variance(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REV", - ], [ - "+04.93111E-6" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REV", + ], + ["+04.93111E-6"], + sep="\r", ) as dmm: - assert dmm.variance == +04.93111E-6 + assert dmm.variance == +04.93111e-6 def test_hp3456a_count(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REC", - ], [ - "+10.00000E+0" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REC", + ], + ["+10.00000E+0"], + sep="\r", ) as dmm: assert dmm.count == +10 def test_hp3456a_mean(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REM", - ], [ - "+102.1000E-3" - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "REM", + ], + ["+102.1000E-3"], + sep="\r", ) as dmm: - assert dmm.mean == +102.1000E-3 + assert dmm.mean == +102.1000e-3 def test_hp3456a_delay(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "RED", - "W1STD" - ], [ - "-000.0000E+0" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "RED", "W1STD"], ["-000.0000E+0"], sep="\r" ) as dmm: assert dmm.delay == 0 dmm.delay = 1 * u.sec @@ -251,97 +193,51 @@ def test_hp3456a_delay(): def test_hp3456a_lower(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REL", - "W0.0993STL" - ], [ - "+099.3000E-3" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "REL", "W0.0993STL"], ["+099.3000E-3"], sep="\r" ) as dmm: - assert dmm.lower == +099.3000E-3 - dmm.lower = +099.3000E-3 + assert dmm.lower == +099.3000e-3 + dmm.lower = +099.3000e-3 def test_hp3456a_upper(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "REU", - "W0.1055STU" - ], [ - "+105.5000E-3" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "REU", "W0.1055STU"], ["+105.5000E-3"], sep="\r" ) as dmm: - assert dmm.upper == +105.5000E-3 - dmm.upper = +105.5000E-3 + assert dmm.upper == +105.5000e-3 + dmm.upper = +105.5000e-3 def test_hp3456a_ryz(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "RER", - "REY", - "REZ", - "W600.0STR", - "W1.0STY", - "W0.1055STZ" - ], [ - "+0600.000E+0", - "+1.000000E+0", - "+105.5000E-3" - ], - sep="\r" + ik.hp.HP3456a, + ["HO0T4SO1", "RER", "REY", "REZ", "W600.0STR", "W1.0STY", "W0.1055STZ"], + ["+0600.000E+0", "+1.000000E+0", "+105.5000E-3"], + sep="\r", ) as dmm: - assert dmm.r == +0600.000E+0 - assert dmm.y == +1.000000E+0 - assert dmm.z == +105.5000E-3 - dmm.r = +0600.000E+0 - dmm.y = +1.000000E+0 - dmm.z = +105.5000E-3 + assert dmm.r == +0600.000e0 + assert dmm.y == +1.000000e0 + assert dmm.z == +105.5000e-3 + dmm.r = +0600.000e0 + dmm.y = +1.000000e0 + dmm.z = +105.5000e-3 def test_hp3456a_measure(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "S1F1W1STNT3", - "S0F4W1STNT3", - "S0F1W1STNT3", - "W1STNT3" - ], - [ - "+00.00000E-3", - "+000.1010E+0", - "+000.0002E-3", - "+000.0002E-3" - ], - sep="\r" + ik.hp.HP3456a, + ["HO0T4SO1", "S1F1W1STNT3", "S0F4W1STNT3", "S0F1W1STNT3", "W1STNT3"], + ["+00.00000E-3", "+000.1010E+0", "+000.0002E-3", "+000.0002E-3"], + sep="\r", ) as dmm: assert dmm.measure(dmm.Mode.ratio_dcv_dcv) == 0 - assert dmm.measure(dmm.Mode.resistance_2wire) == +000.1010E+0 * u.ohm - assert dmm.measure(dmm.Mode.dcv) == +000.0002E-3 * u.volt - assert dmm.measure() == +000.0002E-3 + assert dmm.measure(dmm.Mode.resistance_2wire) == +000.1010e0 * u.ohm + assert dmm.measure(dmm.Mode.dcv) == +000.0002e-3 * u.volt + assert dmm.measure() == +000.0002e-3 def test_hp3456a_input_range(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "R2W", - "R3W" - ], [ - "" - ], - sep="\r" + ik.hp.HP3456a, ["HO0T4SO1", "R2W", "R3W"], [""], sep="\r" ) as dmm: dmm.input_range = 10 ** -1 * u.volt dmm.input_range = 1e3 * u.ohm @@ -351,55 +247,44 @@ def test_hp3456a_input_range(): def test_hp3456a_input_range_invalid_str(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm.input_range = "derp" def test_hp3456a_input_range_invalid_range(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm.input_range = 1 * u.ohm def test_hp3456a_input_range_bad_type(): with pytest.raises(TypeError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm.input_range = True def test_hp3456a_input_range_bad_units(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm.input_range = 1 * u.amp def test_hp3456a_relative(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "M0", - "M3", - ], [ - "", - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "M0", + "M3", + ], + [ + "", + ], + sep="\r", ) as dmm: dmm.relative = False dmm.relative = True @@ -408,25 +293,23 @@ def test_hp3456a_relative(): def test_hp3456a_relative_bad_type(): with pytest.raises(TypeError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm.relative = "derp" def test_hp3456a_auto_zero(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "Z0", - "Z1", - ], [ - "", - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "Z0", + "Z1", + ], + [ + "", + ], + sep="\r", ) as dmm: dmm.autozero = False dmm.autozero = True @@ -434,15 +317,16 @@ def test_hp3456a_auto_zero(): def test_hp3456a_filter(): with expected_protocol( - ik.hp.HP3456a, - [ - "HO0T4SO1", - "FL0", - "FL1", - ], [ - "", - ], - sep="\r" + ik.hp.HP3456a, + [ + "HO0T4SO1", + "FL0", + "FL1", + ], + [ + "", + ], + sep="\r", ) as dmm: dmm.filter = False dmm.filter = True @@ -450,29 +334,20 @@ def test_hp3456a_filter(): def test_hp3456a_register_read_bad_name(): with pytest.raises(TypeError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm._register_read("foobar") def test_hp3456a_register_write_bad_name(): with pytest.raises(TypeError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm._register_write("foobar", 1) def test_hp3456a_register_write_bad_register(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP3456a, - [], - [], - sep="\r" + ik.hp.HP3456a, [], [], sep="\r" ) as dmm: dmm._register_write(dmm.Register.mean, 1) diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 262071828..7a48d6ddf 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -22,12 +22,7 @@ def test_channel_returns_inner_class(): - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, [], [], sep="\n") as hp: channel = hp.channel[0] assert isinstance(channel, hp.Channel) is True assert channel._idx == 1 @@ -61,12 +56,7 @@ def test_channel_query(): def test_mode(): """Raise NotImplementedError when mode is called.""" - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, [], [], sep="\n") as hp: channel = hp.channel[0] with pytest.raises(NotImplementedError): _ = channel.mode @@ -76,15 +66,7 @@ def test_mode(): def test_channel_voltage(): with expected_protocol( - ik.hp.HP6624a, - [ - "VSET? 1", - "VSET 1,{:.1f}".format(5) - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, ["VSET? 1", "VSET 1,{:.1f}".format(5)], ["2"], sep="\n" ) as hp: assert hp.channel[0].voltage == 2 * u.V hp.channel[0].voltage = 5 * u.V @@ -92,92 +74,45 @@ def test_channel_voltage(): def test_channel_current(): with expected_protocol( - ik.hp.HP6624a, - [ - "ISET? 1", - "ISET 1,{:.1f}".format(5) - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, ["ISET? 1", "ISET 1,{:.1f}".format(5)], ["2"], sep="\n" ) as hp: assert hp.channel[0].current == 2 * u.amp hp.channel[0].current = 5 * u.amp def test_channel_voltage_sense(): - with expected_protocol( - ik.hp.HP6624a, - [ - "VOUT? 1" - ], - [ - "2" - ], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, ["VOUT? 1"], ["2"], sep="\n") as hp: assert hp.channel[0].voltage_sense == 2 * u.V def test_channel_current_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "IOUT? 1", - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, + [ + "IOUT? 1", + ], + ["2"], + sep="\n", ) as hp: assert hp.channel[0].current_sense == 2 * u.A def test_channel_overvoltage(): with expected_protocol( - ik.hp.HP6624a, - [ - "OVSET? 1", - f"OVSET 1,{5:.1f}" - ], - [ - "2" - ], - sep="\n" + ik.hp.HP6624a, ["OVSET? 1", f"OVSET 1,{5:.1f}"], ["2"], sep="\n" ) as hp: assert hp.channel[0].overvoltage == 2 * u.V hp.channel[0].overvoltage = 5 * u.V def test_channel_overcurrent(): - with expected_protocol( - ik.hp.HP6624a, - [ - "OVP? 1", - "OVP 1,1" - ], - [ - "1" - ], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, ["OVP? 1", "OVP 1,1"], ["1"], sep="\n") as hp: assert hp.channel[0].overcurrent is True hp.channel[0].overcurrent = True def test_channel_output(): - with expected_protocol( - ik.hp.HP6624a, - [ - "OUT? 1", - "OUT 1,1" - ], - [ - "1" - ], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, ["OUT? 1", "OUT 1,1"], ["1"], sep="\n") as hp: assert hp.channel[0].output is True hp.channel[0].output = True @@ -192,30 +127,23 @@ def test_channel_reset(): def test_all_voltage(): with expected_protocol( - ik.hp.HP6624a, - [ - "VSET? 1", - "VSET? 2", - "VSET? 3", - "VSET? 4", - - "VSET 1,{:.1f}".format(5), - "VSET 2,{:.1f}".format(5), - "VSET 3,{:.1f}".format(5), - "VSET 4,{:.1f}".format(5), - - "VSET 1,{:.1f}".format(1), - "VSET 2,{:.1f}".format(2), - "VSET 3,{:.1f}".format(3), - "VSET 4,{:.1f}".format(4) - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + [ + "VSET? 1", + "VSET? 2", + "VSET? 3", + "VSET? 4", + "VSET 1,{:.1f}".format(5), + "VSET 2,{:.1f}".format(5), + "VSET 3,{:.1f}".format(5), + "VSET 4,{:.1f}".format(5), + "VSET 1,{:.1f}".format(1), + "VSET 2,{:.1f}".format(2), + "VSET 3,{:.1f}".format(3), + "VSET 4,{:.1f}".format(4), + ], + ["2", "3", "4", "5"], + sep="\n", ) as hp: expected = (2 * u.V, 3 * u.V, 4 * u.V, 5 * u.V) iterable_eq(hp.voltage, expected) @@ -225,40 +153,30 @@ def test_all_voltage(): def test_all_voltage_wrong_length(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" + ik.hp.HP6624a, [], [], sep="\n" ) as hp: hp.voltage = (1 * u.volt, 2 * u.volt) def test_all_current(): with expected_protocol( - ik.hp.HP6624a, - [ - "ISET? 1", - "ISET? 2", - "ISET? 3", - "ISET? 4", - - "ISET 1,{:.1f}".format(5), - "ISET 2,{:.1f}".format(5), - "ISET 3,{:.1f}".format(5), - "ISET 4,{:.1f}".format(5), - - "ISET 1,{:.1f}".format(1), - "ISET 2,{:.1f}".format(2), - "ISET 3,{:.1f}".format(3), - "ISET 4,{:.1f}".format(4) - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + [ + "ISET? 1", + "ISET? 2", + "ISET? 3", + "ISET? 4", + "ISET 1,{:.1f}".format(5), + "ISET 2,{:.1f}".format(5), + "ISET 3,{:.1f}".format(5), + "ISET 4,{:.1f}".format(5), + "ISET 1,{:.1f}".format(1), + "ISET 2,{:.1f}".format(2), + "ISET 3,{:.1f}".format(3), + "ISET 4,{:.1f}".format(4), + ], + ["2", "3", "4", "5"], + sep="\n", ) as hp: expected = (2 * u.A, 3 * u.A, 4 * u.A, 5 * u.A) iterable_eq(hp.current, expected) @@ -268,30 +186,17 @@ def test_all_current(): def test_all_current_wrong_length(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" + ik.hp.HP6624a, [], [], sep="\n" ) as hp: hp.current = (1 * u.amp, 2 * u.amp) def test_all_voltage_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "VOUT? 1", - "VOUT? 2", - "VOUT? 3", - "VOUT? 4" - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + ["VOUT? 1", "VOUT? 2", "VOUT? 3", "VOUT? 4"], + ["2", "3", "4", "5"], + sep="\n", ) as hp: expected = (2 * u.V, 3 * u.V, 4 * u.V, 5 * u.V) iterable_eq(hp.voltage_sense, expected) @@ -299,63 +204,35 @@ def test_all_voltage_sense(): def test_all_current_sense(): with expected_protocol( - ik.hp.HP6624a, - [ - "IOUT? 1", - "IOUT? 2", - "IOUT? 3", - "IOUT? 4" - ], - [ - "2", - "3", - "4", - "5" - ], - sep="\n" + ik.hp.HP6624a, + ["IOUT? 1", "IOUT? 2", "IOUT? 3", "IOUT? 4"], + ["2", "3", "4", "5"], + sep="\n", ) as hp: expected = (2 * u.A, 3 * u.A, 4 * u.A, 5 * u.A) iterable_eq(hp.current_sense, expected) def test_clear(): - with expected_protocol( - ik.hp.HP6624a, - [ - "CLR" - ], - [], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, ["CLR"], [], sep="\n") as hp: hp.clear() def test_channel_count(): - with expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6624a, [], [], sep="\n") as hp: assert hp.channel_count == 4 hp.channel_count = 3 def test_channel_count_wrong_type(): with pytest.raises(TypeError), expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" + ik.hp.HP6624a, [], [], sep="\n" ) as hp: hp.channel_count = "foobar" def test_channel_count_too_small(): with pytest.raises(ValueError), expected_protocol( - ik.hp.HP6624a, - [], - [], - sep="\n" + ik.hp.HP6624a, [], [], sep="\n" ) as hp: hp.channel_count = 0 diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index 6d845af74..08957c56a 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -20,13 +20,7 @@ def test_hp6632b_display_textmode(): with expected_protocol( - ik.hp.HP6632b, - [ - "DISP:MODE?", - "DISP:MODE TEXT" - ], [ - "NORM" - ] + ik.hp.HP6632b, ["DISP:MODE?", "DISP:MODE TEXT"], ["NORM"] ) as psu: assert psu.display_textmode is False psu.display_textmode = True @@ -34,40 +28,21 @@ def test_hp6632b_display_textmode(): def test_hp6632b_display_text(): with expected_protocol( - ik.hp.HP6632b, - [ - 'DISP:TEXT "TEST"', - 'DISP:TEXT "TEST AAAAAAAAAA"' - ], - [] + ik.hp.HP6632b, ['DISP:TEXT "TEST"', 'DISP:TEXT "TEST AAAAAAAAAA"'], [] ) as psu: assert psu.display_text("TEST") == "TEST" assert psu.display_text("TEST AAAAAAAAAAAAAAAA") == "TEST AAAAAAAAAA" def test_hp6632b_output(): - with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP?", - "OUTP 1" - ], [ - "0" - ] - ) as psu: + with expected_protocol(ik.hp.HP6632b, ["OUTP?", "OUTP 1"], ["0"]) as psu: assert psu.output is False psu.output = True def test_hp6632b_voltage(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT?", - "VOLT {:e}".format(1) - ], [ - "10.0" - ] + ik.hp.HP6632b, ["VOLT?", "VOLT {:e}".format(1)], ["10.0"] ) as psu: unit_eq(psu.voltage, 10 * u.volt) psu.voltage = 1.0 * u.volt @@ -75,25 +50,18 @@ def test_hp6632b_voltage(): def test_hp6632b_voltage_sense(): with expected_protocol( - ik.hp.HP6632b, - [ - "MEAS:VOLT?", - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "MEAS:VOLT?", + ], + ["10.0"], ) as psu: unit_eq(psu.voltage_sense, 10 * u.volt) def test_hp6632b_overvoltage(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT:PROT?", - "VOLT:PROT {:e}".format(1) - ], [ - "10.0" - ] + ik.hp.HP6632b, ["VOLT:PROT?", "VOLT:PROT {:e}".format(1)], ["10.0"] ) as psu: unit_eq(psu.overvoltage, 10 * u.volt) psu.overvoltage = 1.0 * u.volt @@ -101,13 +69,7 @@ def test_hp6632b_overvoltage(): def test_hp6632b_current(): with expected_protocol( - ik.hp.HP6632b, - [ - "CURR?", - "CURR {:e}".format(1) - ], [ - "10.0" - ] + ik.hp.HP6632b, ["CURR?", "CURR {:e}".format(1)], ["10.0"] ) as psu: unit_eq(psu.current, 10 * u.amp) psu.current = 1.0 * u.amp @@ -115,25 +77,18 @@ def test_hp6632b_current(): def test_hp6632b_current_sense(): with expected_protocol( - ik.hp.HP6632b, - [ - "MEAS:CURR?", - ], [ - "10.0" - ] + ik.hp.HP6632b, + [ + "MEAS:CURR?", + ], + ["10.0"], ) as psu: unit_eq(psu.current_sense, 10 * u.amp) def test_hp6632b_overcurrent(): with expected_protocol( - ik.hp.HP6632b, - [ - "CURR:PROT:STAT?", - "CURR:PROT:STAT 1" - ], [ - "0" - ] + ik.hp.HP6632b, ["CURR:PROT:STAT?", "CURR:PROT:STAT 1"], ["0"] ) as psu: assert psu.overcurrent is False psu.overcurrent = True @@ -141,13 +96,7 @@ def test_hp6632b_overcurrent(): def test_hp6632b_current_sense_range(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:CURR:RANGE?", - "SENS:CURR:RANGE {:e}".format(1) - ], [ - "0.05" - ] + ik.hp.HP6632b, ["SENS:CURR:RANGE?", "SENS:CURR:RANGE {:e}".format(1)], ["0.05"] ) as psu: unit_eq(psu.current_sense_range, 0.05 * u.amp) psu.current_sense_range = 1 * u.amp @@ -155,13 +104,7 @@ def test_hp6632b_current_sense_range(): def test_hp6632b_output_dfi_source(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP:DFI:SOUR?", - "OUTP:DFI:SOUR QUES" - ], [ - "OPER" - ] + ik.hp.HP6632b, ["OUTP:DFI:SOUR?", "OUTP:DFI:SOUR QUES"], ["OPER"] ) as psu: assert psu.output_dfi_source == psu.DFISource.operation psu.output_dfi_source = psu.DFISource.questionable @@ -169,13 +112,7 @@ def test_hp6632b_output_dfi_source(): def test_hp6632b_output_remote_inhibit(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP:RI:MODE?", - "OUTP:RI:MODE LATC" - ], [ - "LIVE" - ] + ik.hp.HP6632b, ["OUTP:RI:MODE?", "OUTP:RI:MODE LATC"], ["LIVE"] ) as psu: assert psu.output_remote_inhibit == psu.RemoteInhibit.live psu.output_remote_inhibit = psu.RemoteInhibit.latching @@ -183,41 +120,21 @@ def test_hp6632b_output_remote_inhibit(): def test_hp6632b_digital_function(): with expected_protocol( - ik.hp.HP6632b, - [ - "DIG:FUNC?", - "DIG:FUNC DIG" - ], [ - "RIDF" - ] + ik.hp.HP6632b, ["DIG:FUNC?", "DIG:FUNC DIG"], ["RIDF"] ) as psu: assert psu.digital_function == psu.DigitalFunction.remote_inhibit psu.digital_function = psu.DigitalFunction.data def test_hp6632b_digital_data(): - with expected_protocol( - ik.hp.HP6632b, - [ - "DIG:DATA?", - "DIG:DATA 1" - ], [ - "5" - ] - ) as psu: + with expected_protocol(ik.hp.HP6632b, ["DIG:DATA?", "DIG:DATA 1"], ["5"]) as psu: assert psu.digital_data == 5 psu.digital_data = 1 def test_hp6632b_sense_sweep_points(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:SWE:POIN?", - "SENS:SWE:POIN {:e}".format(2048) - ], [ - "5" - ] + ik.hp.HP6632b, ["SENS:SWE:POIN?", "SENS:SWE:POIN {:e}".format(2048)], ["5"] ) as psu: assert psu.sense_sweep_points == 5 psu.sense_sweep_points = 2048 @@ -225,13 +142,9 @@ def test_hp6632b_sense_sweep_points(): def test_hp6632b_sense_sweep_interval(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:SWE:TINT?", - "SENS:SWE:TINT {:e}".format(1e-05) - ], [ - "1.56e-05" - ] + ik.hp.HP6632b, + ["SENS:SWE:TINT?", "SENS:SWE:TINT {:e}".format(1e-05)], + ["1.56e-05"], ) as psu: unit_eq(psu.sense_sweep_interval, 1.56e-05 * u.second) psu.sense_sweep_interval = 1e-05 * u.second @@ -239,13 +152,7 @@ def test_hp6632b_sense_sweep_interval(): def test_hp6632b_sense_window(): with expected_protocol( - ik.hp.HP6632b, - [ - "SENS:WIND?", - "SENS:WIND RECT" - ], [ - "HANN" - ] + ik.hp.HP6632b, ["SENS:WIND?", "SENS:WIND RECT"], ["HANN"] ) as psu: assert psu.sense_window == psu.SenseWindow.hanning psu.sense_window = psu.SenseWindow.rectangular @@ -253,13 +160,7 @@ def test_hp6632b_sense_window(): def test_hp6632b_output_protection_delay(): with expected_protocol( - ik.hp.HP6632b, - [ - "OUTP:PROT:DEL?", - "OUTP:PROT:DEL {:e}".format(5e-02) - ], [ - "8e-02" - ] + ik.hp.HP6632b, ["OUTP:PROT:DEL?", "OUTP:PROT:DEL {:e}".format(5e-02)], ["8e-02"] ) as psu: unit_eq(psu.output_protection_delay, 8e-02 * u.second) psu.output_protection_delay = 5e-02 * u.second @@ -267,25 +168,18 @@ def test_hp6632b_output_protection_delay(): def test_hp6632b_voltage_alc_bandwidth(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT:ALC:BAND?", - ], [ - "6e4" - ] + ik.hp.HP6632b, + [ + "VOLT:ALC:BAND?", + ], + ["6e4"], ) as psu: assert psu.voltage_alc_bandwidth == psu.ALCBandwidth.fast def test_hp6632b_voltage_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "VOLT:TRIG?", - "VOLT:TRIG {:e}".format(1) - ], [ - "1e+0" - ] + ik.hp.HP6632b, ["VOLT:TRIG?", "VOLT:TRIG {:e}".format(1)], ["1e+0"] ) as psu: unit_eq(psu.voltage_trigger, 1 * u.volt) psu.voltage_trigger = 1 * u.volt @@ -293,13 +187,7 @@ def test_hp6632b_voltage_trigger(): def test_hp6632b_current_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "CURR:TRIG?", - "CURR:TRIG {:e}".format(0.1) - ], [ - "1e-01" - ] + ik.hp.HP6632b, ["CURR:TRIG?", "CURR:TRIG {:e}".format(0.1)], ["1e-01"] ) as psu: unit_eq(psu.current_trigger, 0.1 * u.amp) psu.current_trigger = 0.1 * u.amp @@ -307,33 +195,29 @@ def test_hp6632b_current_trigger(): def test_hp6632b_init_output_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "INIT:NAME TRAN", - ], - [] + ik.hp.HP6632b, + [ + "INIT:NAME TRAN", + ], + [], ) as psu: psu.init_output_trigger() def test_hp6632b_abort_output_trigger(): with expected_protocol( - ik.hp.HP6632b, - [ - "ABORT", - ], - [] + ik.hp.HP6632b, + [ + "ABORT", + ], + [], ) as psu: psu.abort_output_trigger() def test_line_frequency(): """Raise NotImplemented error when called.""" - with expected_protocol( - ik.hp.HP6632b, - [], - [] - ) as psu: + with expected_protocol(ik.hp.HP6632b, [], []) as psu: with pytest.raises(NotImplementedError): psu.line_frequency = 42 with pytest.raises(NotImplementedError): @@ -342,11 +226,7 @@ def test_line_frequency(): def test_display_brightness(): """Raise NotImplemented error when called.""" - with expected_protocol( - ik.hp.HP6632b, - [], - [] - ) as psu: + with expected_protocol(ik.hp.HP6632b, [], []) as psu: with pytest.raises(NotImplementedError): psu.display_brightness = 42 with pytest.raises(NotImplementedError): @@ -355,11 +235,7 @@ def test_display_brightness(): def test_display_contrast(): """Raise NotImplemented error when called.""" - with expected_protocol( - ik.hp.HP6632b, - [], - [] - ) as psu: + with expected_protocol(ik.hp.HP6632b, [], []) as psu: with pytest.raises(NotImplementedError): psu.display_contrast = 42 with pytest.raises(NotImplementedError): @@ -368,16 +244,12 @@ def test_display_contrast(): def test_hp6632b_check_error_queue(): with expected_protocol( - ik.hp.HP6632b, - [ - "SYST:ERR?", - "SYST:ERR?", - ], [ - '-222,"Data out of range"', - '+0,"No error"' - ] + ik.hp.HP6632b, + [ + "SYST:ERR?", + "SYST:ERR?", + ], + ['-222,"Data out of range"', '+0,"No error"'], ) as psu: err_queue = psu.check_error_queue() - assert err_queue == [ - psu.ErrorCodes.data_out_of_range - ], f"got {err_queue}" + assert err_queue == [psu.ErrorCodes.data_out_of_range], f"got {err_queue}" diff --git a/instruments/tests/test_hp/test_hp6652a.py b/instruments/tests/test_hp/test_hp6652a.py index 925fc14ec..ec5b37c38 100644 --- a/instruments/tests/test_hp/test_hp6652a.py +++ b/instruments/tests/test_hp/test_hp6652a.py @@ -16,64 +16,34 @@ def test_name(): with expected_protocol( - ik.hp.HP6652a, - [ - "*IDN?" - ], - [ - "FOO,BAR,AAA,BBBB" - ], - sep="\n" + ik.hp.HP6652a, ["*IDN?"], ["FOO,BAR,AAA,BBBB"], sep="\n" ) as hp: assert hp.name == "FOO BAR" def test_mode(): """Raise NotImplementedError when called.""" - with expected_protocol( - ik.hp.HP6652a, - [ - ], - [ - ], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6652a, [], [], sep="\n") as hp: with pytest.raises(NotImplementedError): _ = hp.mode with pytest.raises(NotImplementedError): hp.mode = 42 + def test_reset(): - with expected_protocol( - ik.hp.HP6652a, - [ - "OUTP:PROT:CLE" - ], - [], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6652a, ["OUTP:PROT:CLE"], [], sep="\n") as hp: hp.reset() def test_display_text(): with expected_protocol( - ik.hp.HP6652a, - [ - 'DISP:TEXT "TEST"', - 'DISP:TEXT "TEST AAAAAAAAAA"' - ], - [] + ik.hp.HP6652a, ['DISP:TEXT "TEST"', 'DISP:TEXT "TEST AAAAAAAAAA"'], [] ) as psu: assert psu.display_text("TEST") == "TEST" assert psu.display_text("TEST AAAAAAAAAAAAAAAA") == "TEST AAAAAAAAAA" def test_channel(): - with expected_protocol( - ik.hp.HP6652a, - [], - [], - sep="\n" - ) as hp: + with expected_protocol(ik.hp.HP6652a, [], [], sep="\n") as hp: assert hp.channel[0] == hp assert len(hp.channel) == 1 diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index 62fff59c1..cf2c55a22 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -18,27 +18,18 @@ # TESTS ####################################################################### + @pytest.fixture(autouse=True) def time_mock(mocker): """Mock out time such that the tests go faster.""" - return mocker.patch.object(time, 'sleep', return_value=None) + return mocker.patch.object(time, "sleep", return_value=None) def test_channel(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", - "INST:NSEL?", - "INST:NSEL?", - "INST:NSEL 2", - "INST:NSEL?" - ], - [ - "1", - "1", - "2" - ] + ik.hp.HPe3631a, + ["SYST:REM", "INST:NSEL?", "INST:NSEL?", "INST:NSEL 2", "INST:NSEL?"], + ["1", "1", "2"], ) as inst: assert inst.channelid == 1 assert inst.channel[2] == inst @@ -48,17 +39,9 @@ def test_channel(): def test_channelid(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "INST:NSEL?", # 1 - "INST:NSEL 2", # 2 - "INST:NSEL?" # 3 - ], - [ - "1", # 1 - "2" # 3 - ] + ik.hp.HPe3631a, + ["SYST:REM", "INST:NSEL?", "INST:NSEL 2", "INST:NSEL?"], # 0 # 1 # 2 # 3 + ["1", "2"], # 1 # 3 ) as inst: assert inst.channelid == 1 inst.channelid = 2 @@ -67,14 +50,7 @@ def test_channelid(): def test_mode(): """Raise AttributeError since instrument sets mode automatically.""" - with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM" - ], - [ - ] - ) as inst: + with expected_protocol(ik.hp.HPe3631a, ["SYST:REM"], []) as inst: with pytest.raises(AttributeError) as err_info: _ = inst.mode() err_msg = err_info.value.args[0] @@ -83,25 +59,18 @@ def test_mode(): def test_voltage(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "SOUR:VOLT? MAX", # 1 - "SOUR:VOLT? MAX", # 2 - "SOUR:VOLT? MAX", # 3.1 - "SOUR:VOLT 3.000000e+00", # 3.2 - "SOUR:VOLT?", # 4 - "SOUR:VOLT? MAX", # 5 - "SOUR:VOLT? MAX" # 6 - ], - [ - "6.0", # 1 - "6.0", # 2 - "6.0", # 3.1 - "3.0", # 4 - "6.0", # 5 - "6.0" # 6 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:VOLT? MAX", # 1 + "SOUR:VOLT? MAX", # 2 + "SOUR:VOLT? MAX", # 3.1 + "SOUR:VOLT 3.000000e+00", # 3.2 + "SOUR:VOLT?", # 4 + "SOUR:VOLT? MAX", # 5 + "SOUR:VOLT? MAX", # 6 + ], + ["6.0", "6.0", "6.0", "3.0", "6.0", "6.0"], # 1 # 2 # 3.1 # 4 # 5 # 6 ) as inst: assert inst.voltage_min == 0.0 * u.volt assert inst.voltage_max == 6.0 * u.volt @@ -111,59 +80,60 @@ def test_voltage(): newval = -1.0 * u.volt inst.voltage = newval err_msg = err_info.value.args[0] - assert err_msg == f"Voltage quantity is too low. Got {newval}, " \ - f"minimum value is {0.}" + assert ( + err_msg == f"Voltage quantity is too low. Got {newval}, " + f"minimum value is {0.}" + ) with pytest.raises(ValueError) as err_info: newval = 7.0 * u.volt inst.voltage = newval err_msg = err_info.value.args[0] - assert err_msg == f"Voltage quantity is too high. Got {newval}, " \ - f"maximum value is {u.Quantity(6.0, u.V)}" + assert ( + err_msg == f"Voltage quantity is too high. Got {newval}, " + f"maximum value is {u.Quantity(6.0, u.V)}" + ) def test_voltage_range_negative(): """Get voltage max if negative.""" - max_volts = -6. + max_volts = -6.0 with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "SOUR:VOLT? MAX" # 1 - ], - [ - f"{max_volts}", # 1 - ] + ik.hp.HPe3631a, + ["SYST:REM", "SOUR:VOLT? MAX"], # 0 # 1 + [ + f"{max_volts}", # 1 + ], ) as inst: - expected_value = u.Quantity(max_volts, u.V), 0. + expected_value = u.Quantity(max_volts, u.V), 0.0 received_value = inst.voltage_range assert expected_value == received_value def test_current(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "SOUR:CURR? MIN", # 1.1 - "SOUR:CURR? MAX", # 1.2 - "SOUR:CURR? MIN", # 2.1 - "SOUR:CURR? MAX", # 2.2 - "SOUR:CURR 2.000000e+00", # 3 - "SOUR:CURR?", # 4 - "SOUR:CURR? MIN", # 5 - "SOUR:CURR? MIN", # 6.1 - "SOUR:CURR? MAX" # 6.2 - ], - [ - "0.0", # 1.1 - "5.0", # 1.2 - "0.0", # 2.1 - "5.0", # 2.2 - "2.0", # 4 - "0.0", # 5 - "0.0", # 6.1 - "5.0" # 6.2 - ] + ik.hp.HPe3631a, + [ + "SYST:REM", # 0 + "SOUR:CURR? MIN", # 1.1 + "SOUR:CURR? MAX", # 1.2 + "SOUR:CURR? MIN", # 2.1 + "SOUR:CURR? MAX", # 2.2 + "SOUR:CURR 2.000000e+00", # 3 + "SOUR:CURR?", # 4 + "SOUR:CURR? MIN", # 5 + "SOUR:CURR? MIN", # 6.1 + "SOUR:CURR? MAX", # 6.2 + ], + [ + "0.0", # 1.1 + "5.0", # 1.2 + "0.0", # 2.1 + "5.0", # 2.2 + "2.0", # 4 + "0.0", # 5 + "0.0", # 6.1 + "5.0", # 6.2 + ], ) as inst: assert inst.current_min == 0.0 * u.amp assert inst.current_max == 5.0 * u.amp @@ -181,27 +151,13 @@ def test_current(): def test_voltage_sense(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "MEAS:VOLT?" # 1 - ], - [ - "1.234" # 1 - ] + ik.hp.HPe3631a, ["SYST:REM", "MEAS:VOLT?"], ["1.234"] # 0 # 1 # 1 ) as inst: assert inst.voltage_sense == 1.234 * u.volt def test_current_sense(): with expected_protocol( - ik.hp.HPe3631a, - [ - "SYST:REM", # 0 - "MEAS:CURR?" # 1 - ], - [ - "1.234" # 1 - ] + ik.hp.HPe3631a, ["SYST:REM", "MEAS:CURR?"], ["1.234"] # 0 # 1 # 1 ) as inst: assert inst.current_sense == 1.234 * u.amp diff --git a/instruments/tests/test_keithley/test_keithley195.py b/instruments/tests/test_keithley/test_keithley195.py index 927f1d8f8..df4df17c1 100644 --- a/instruments/tests/test_keithley/test_keithley195.py +++ b/instruments/tests/test_keithley/test_keithley195.py @@ -54,10 +54,24 @@ def statusword(): terminator = b"1" statusword_p1 = b"195 " # sends a space after 195! - statusword_p2 = struct.pack('@4c2s3c2s5c2s', trigger, mode, range, eoi, - buffer, rate, srqmode, relative, delay, - multiplex, selftest, dataformat, datacontrol, - filter, terminator) + statusword_p2 = struct.pack( + "@4c2s3c2s5c2s", + trigger, + mode, + range, + eoi, + buffer, + rate, + srqmode, + relative, + delay, + multiplex, + selftest, + dataformat, + datacontrol, + filter, + terminator, + ) return statusword_p1 + statusword_p2 @@ -67,17 +81,7 @@ def statusword(): def test_keithley195_mode(init, statusword): """Get / set the measurement mode.""" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "F2DX", - "U0DX" - - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "F2DX", "U0DX"], [statusword], sep="\n" ) as mul: mul.mode = mul.Mode.resistance assert mul.mode == mul.Mode.resistance @@ -86,55 +90,29 @@ def test_keithley195_mode(init, statusword): def test_keithley195_mode_string(init, statusword): """Get / set the measurement mode using a string.""" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "F2DX", - "U0DX" - - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "F2DX", "U0DX"], [statusword], sep="\n" ) as mul: - mul.mode = 'resistance' + mul.mode = "resistance" assert mul.mode == mul.Mode.resistance def test_keithley195_mode_type_error(init): """Raise type error when setting the mode with the wrong type.""" wrong_type = 42 - with expected_protocol( - ik.keithley.Keithley195, - [ - init - ], - [ - ], - sep="\n" - ) as mul: + with expected_protocol(ik.keithley.Keithley195, [init], [], sep="\n") as mul: with pytest.raises(TypeError) as err_info: mul.mode = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Mode must be specified as a Keithley195.Mode " \ - f"value, got {wrong_type} instead." + assert ( + err_msg == f"Mode must be specified as a Keithley195.Mode " + f"value, got {wrong_type} instead." + ) def test_keithley195_trigger_mode(init, statusword): """Get / set the trigger mode.""" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "T1X", - "U0DX" - - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "T1X", "U0DX"], [statusword], sep="\n" ) as mul: mul.trigger_mode = mul.TriggerMode.talk_one_shot assert mul.trigger_mode == mul.TriggerMode.talk_one_shot @@ -143,56 +121,29 @@ def test_keithley195_trigger_mode(init, statusword): def test_keithley195_trigger_mode_string(init, statusword): """Get / set the trigger using a string.""" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "T1X", - "U0DX" - - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "T1X", "U0DX"], [statusword], sep="\n" ) as mul: - mul.trigger_mode = 'talk_one_shot' + mul.trigger_mode = "talk_one_shot" assert mul.trigger_mode == mul.TriggerMode.talk_one_shot def test_keithley195_trigger_mode_type_error(init): """Raise type error when setting the trigger mode with the wrong type.""" wrong_type = 42 - with expected_protocol( - ik.keithley.Keithley195, - [ - init - ], - [ - ], - sep="\n" - ) as mul: + with expected_protocol(ik.keithley.Keithley195, [init], [], sep="\n") as mul: with pytest.raises(TypeError) as err_info: mul.trigger_mode = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Drive must be specified as a " \ - f"Keithley195.TriggerMode, got {wrong_type} instead." + assert ( + err_msg == f"Drive must be specified as a " + f"Keithley195.TriggerMode, got {wrong_type} instead." + ) def test_keithley195_relative(init, statusword): """Get / set the relative mode""" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "Z0DX", - "Z1DX", - "U0DX" - - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "Z0DX", "Z1DX", "U0DX"], [statusword], sep="\n" ) as mul: mul.relative = False mul.relative = True @@ -203,13 +154,12 @@ def test_keithley195_relative_type_error(init): """Raise type error when setting relative non-bool.""" wrong_type = 42 with expected_protocol( - ik.keithley.Keithley195, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley195, + [ + init, + ], + [], + sep="\n", ) as mul: with pytest.raises(TypeError) as err_info: mul.relative = wrong_type @@ -217,8 +167,7 @@ def test_keithley195_relative_type_error(init): assert err_msg == "Relative mode must be a boolean." -@pytest.mark.parametrize("range", - ik.keithley.Keithley195.ValidRange.resistance.value) +@pytest.mark.parametrize("range", ik.keithley.Keithley195.ValidRange.resistance.value) def test_keithley195_input_range(init, statusword, range): """Get / set input range. @@ -233,23 +182,18 @@ def test_keithley195_input_range(init, statusword, range): # units units = ik.keithley.keithley195.UNITS2[mode] with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "U0DX", - f"R{index + 1}DX", - "U0DX", - f"R{index + 1}DX", - "U0DX", # query - "U0DX" - ], - [ - statusword, - statusword, - new_statusword, - new_statusword - ], - sep="\n" + ik.keithley.Keithley195, + [ + init, + "U0DX", + f"R{index + 1}DX", + "U0DX", + f"R{index + 1}DX", + "U0DX", # query + "U0DX", + ], + [statusword, statusword, new_statusword, new_statusword], + sep="\n", ) as mul: mul.input_range = range mul.input_range = u.Quantity(range, units) @@ -263,38 +207,23 @@ def test_keithley195_input_range_auto(init, statusword): new_statusword[6] = "0" new_statusword = "".join(new_statusword) with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "R0DX", - "U0DX" - ], - [ - new_statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "R0DX", "U0DX"], [new_statusword], sep="\n" ) as mul: - mul.input_range = 'Auto' - assert mul.input_range == 'auto' + mul.input_range = "Auto" + assert mul.input_range == "auto" def test_keithley195_input_range_set_wrong_string(init): """Raise Value error if input range set w/ string other than 'auto'.""" - bad_string = 'forty-two' - with expected_protocol( - ik.keithley.Keithley195, - [ - init - ], - [ - ], - sep="\n" - ) as mul: + bad_string = "forty-two" + with expected_protocol(ik.keithley.Keithley195, [init], [], sep="\n") as mul: with pytest.raises(ValueError) as err_info: mul.input_range = bad_string err_msg = err_info.value.args[0] - assert err_msg == "Only \"auto\" is acceptable when specifying the " \ - "input range as a string." + assert ( + err_msg == 'Only "auto" is acceptable when specifying the ' + "input range as a string." + ) def test_keithley195_input_range_set_wrong_range(init, statusword): @@ -303,15 +232,7 @@ def test_keithley195_input_range_set_wrong_range(init, statusword): valid = ik.keithley.Keithley195.ValidRange[mode.name].value out_of_range_value = 42 with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "U0DX" - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "U0DX"], [statusword], sep="\n" ) as mul: with pytest.raises(ValueError) as err_info: mul.input_range = out_of_range_value @@ -323,22 +244,16 @@ def test_keithley195_input_range_set_wrong_type(init, statusword): """Raise TypeError if input range set w/ wrong type.""" wrong_type = {"The Answer": 42} with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "U0DX" - ], - [ - statusword - ], - sep="\n" + ik.keithley.Keithley195, [init, "U0DX"], [statusword], sep="\n" ) as mul: with pytest.raises(TypeError) as err_info: mul.input_range = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Range setting must be specified as a float, " \ - f"int, or the string \"auto\", got " \ - f"{type(wrong_type)}" + assert ( + err_msg == f"Range setting must be specified as a float, " + f'int, or the string "auto", got ' + f"{type(wrong_type)}" + ) @given(value=st.floats(allow_infinity=False, allow_nan=False)) @@ -347,16 +262,7 @@ def test_measure_mode_is_none(init, statusword, value): mode = ik.keithley.Keithley195.Mode(int(statusword.decode()[5])) units = ik.keithley.keithley195.UNITS2[mode] with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "U0DX" - ], - [ - statusword, - f"{value}" - ], - sep="\n" + ik.keithley.Keithley195, [init, "U0DX"], [statusword, f"{value}"], sep="\n" ) as mul: assert mul.measure() == value * units @@ -367,16 +273,7 @@ def test_measure_mode_is_current(init, statusword): units = ik.keithley.keithley195.UNITS2[mode] value = 3.14 with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "U0DX" - ], - [ - statusword, - f"{value}" - ], - sep="\n" + ik.keithley.Keithley195, [init, "U0DX"], [statusword, f"{value}"], sep="\n" ) as mul: assert mul.measure(mode=mode) == value * units @@ -387,24 +284,17 @@ def test_measure_new_mode(init, statusword, mocker): Mock time.sleep() call and assert it is called with 2 seconds. """ # patch call to time.sleep with mock - mock_time = mocker.patch.object(time, 'sleep', return_value=None) + mock_time = mocker.patch.object(time, "sleep", return_value=None) # new modes new_mode = ik.keithley.Keithley195.Mode(0) units = ik.keithley.keithley195.UNITS2[new_mode] value = 3.14 with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "U0DX", - "F0DX" # send new mode - ], - [ - statusword, - f"{value}" - ], - sep="\n" + ik.keithley.Keithley195, + [init, "U0DX", "F0DX"], # send new mode + [statusword, f"{value}"], + sep="\n", ) as mul: assert mul.measure(mode=new_mode) == value * units @@ -416,46 +306,37 @@ def test_parse_status_word_value_error(init): """Raise ValueError if status word does not start with '195'.""" wrong_statusword = "42 314" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley195, + [ + init, + ], + [], + sep="\n", ) as mul: with pytest.raises(ValueError) as err_info: mul.parse_status_word(wrong_statusword) err_msg = err_info.value.args[0] - assert err_msg == f"Status word starts with wrong prefix, expected " \ - f"195, got {wrong_statusword}" + assert ( + err_msg == f"Status word starts with wrong prefix, expected " + f"195, got {wrong_statusword}" + ) def test_trigger(init): """Send a trigger command.""" - with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "X" - ], - [ - ], - sep="\n" - ) as mul: + with expected_protocol(ik.keithley.Keithley195, [init, "X"], [], sep="\n") as mul: mul.trigger() def test_auto_range(init): """Set input range to 'auto'.""" with expected_protocol( - ik.keithley.Keithley195, - [ - init, - "R0DX", - ], - [ - ], - sep="\n" + ik.keithley.Keithley195, + [ + init, + "R0DX", + ], + [], + sep="\n", ) as mul: mul.auto_range() diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index 86d6042c9..ddb8d8e14 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -27,13 +27,13 @@ def test_channel(): def test_channel_mode(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - ], - [ - "VOLT", - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + ], + [ + "VOLT", + ], ) as inst: channel = inst.channel[0] assert channel.mode == inst.Mode.voltage_dc @@ -43,13 +43,7 @@ def test_channel_mode(): def test_channel_trigger_mode(): """Raise NotImplementedError when getting / setting trigger mode.""" - with expected_protocol( - ik.keithley.Keithley2182, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley2182, [], []) as inst: channel = inst.channel[0] with pytest.raises(NotImplementedError): _ = channel.trigger_mode @@ -59,13 +53,7 @@ def test_channel_trigger_mode(): def test_channel_relative(): """Raise NotImplementedError when getting / setting relative.""" - with expected_protocol( - ik.keithley.Keithley2182, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley2182, [], []) as inst: channel = inst.channel[0] with pytest.raises(NotImplementedError): _ = channel.relative @@ -75,13 +63,7 @@ def test_channel_relative(): def test_channel_input_range(): """Raise NotImplementedError when getting / setting input range.""" - with expected_protocol( - ik.keithley.Keithley2182, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley2182, [], []) as inst: channel = inst.channel[0] with pytest.raises(NotImplementedError): _ = channel.input_range @@ -91,13 +73,7 @@ def test_channel_input_range(): def test_channel_measure_mode_not_none(): """Raise NotImplementedError measuring with non-None mode.""" - with expected_protocol( - ik.keithley.Keithley2182, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley2182, [], []) as inst: channel = inst.channel[0] with pytest.raises(NotImplementedError): channel.measure(mode="Some Mode") @@ -105,16 +81,12 @@ def test_channel_measure_mode_not_none(): def test_channel_measure_voltage(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:CHAN 1", - "SENS:DATA:FRES?", - "SENS:FUNC?" - ], - [ - "1.234", - "VOLT", - ] + ik.keithley.Keithley2182, + ["SENS:CHAN 1", "SENS:DATA:FRES?", "SENS:FUNC?"], + [ + "1.234", + "VOLT", + ], ) as inst: channel = inst.channel[0] assert channel.measure() == 1.234 * u.volt @@ -122,18 +94,9 @@ def test_channel_measure_voltage(): def test_channel_measure_temperature(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:CHAN 1", - "SENS:DATA:FRES?", - "SENS:FUNC?", - "UNIT:TEMP?" - ], - [ - "1.234", - "TEMP", - "C" - ] + ik.keithley.Keithley2182, + ["SENS:CHAN 1", "SENS:DATA:FRES?", "SENS:FUNC?", "UNIT:TEMP?"], + ["1.234", "TEMP", "C"], ) as inst: channel = inst.channel[0] assert channel.measure() == u.Quantity(1.234, u.degC) @@ -141,49 +104,26 @@ def test_channel_measure_temperature(): def test_channel_measure_unknown_temperature_units(): with pytest.raises(ValueError), expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:CHAN 1", - "SENS:DATA:FRES?", - "SENS:FUNC?", - "UNIT:TEMP?" - ], - [ - "1.234", - "TEMP", - "Z" - ] + ik.keithley.Keithley2182, + ["SENS:CHAN 1", "SENS:DATA:FRES?", "SENS:FUNC?", "UNIT:TEMP?"], + ["1.234", "TEMP", "Z"], ) as inst: inst.channel[0].measure() def test_units(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "UNIT:TEMP?", - - "SENS:FUNC?", - "UNIT:TEMP?", - - "SENS:FUNC?", - "UNIT:TEMP?", - - "SENS:FUNC?" - ], - [ - "TEMP", - "C", - - "TEMP", - "F", - - "TEMP", - "K", - - "VOLT" - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "UNIT:TEMP?", + "SENS:FUNC?", + "UNIT:TEMP?", + "SENS:FUNC?", + "UNIT:TEMP?", + "SENS:FUNC?", + ], + ["TEMP", "C", "TEMP", "F", "TEMP", "K", "VOLT"], ) as inst: assert inst.units == u.degC assert inst.units == u.degF @@ -193,15 +133,12 @@ def test_units(): def test_fetch(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "FETC?", - "SENS:FUNC?" - ], - [ - "1.234,1,5.678", - "VOLT", - ] + ik.keithley.Keithley2182, + ["FETC?", "SENS:FUNC?"], + [ + "1.234,1,5.678", + "VOLT", + ], ) as inst: data = inst.fetch() vals = [1.234, 1, 5.678] @@ -213,99 +150,79 @@ def test_fetch(): def test_measure(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "MEAS:VOLT?", - "SENS:FUNC?", - ], - [ - "VOLT", - "1.234", - "VOLT" - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "MEAS:VOLT?", + "SENS:FUNC?", + ], + ["VOLT", "1.234", "VOLT"], ) as inst: assert inst.measure() == 1.234 * u.volt def test_measure_invalid_mode(): with pytest.raises(TypeError), expected_protocol( - ik.keithley.Keithley2182, - [], - [] + ik.keithley.Keithley2182, [], [] ) as inst: inst.measure("derp") def test_relative_get(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "SENS:VOLT:CHAN1:REF:STAT?" - ], - [ - "VOLT", - "ON" - ] + ik.keithley.Keithley2182, + ["SENS:FUNC?", "SENS:VOLT:CHAN1:REF:STAT?"], + ["VOLT", "ON"], ) as inst: assert inst.relative is True def test_relative_set_already_enabled(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "SENS:FUNC?", - "SENS:VOLT:CHAN1:REF:STAT?", - "SENS:VOLT:CHAN1:REF:ACQ" - ], - [ - "VOLT", - "VOLT", - "ON", - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "SENS:FUNC?", + "SENS:VOLT:CHAN1:REF:STAT?", + "SENS:VOLT:CHAN1:REF:ACQ", + ], + [ + "VOLT", + "VOLT", + "ON", + ], ) as inst: inst.relative = True def test_relative_set_start_disabled(): with expected_protocol( - ik.keithley.Keithley2182, - [ - "SENS:FUNC?", - "SENS:FUNC?", - "SENS:VOLT:CHAN1:REF:STAT?", - "SENS:VOLT:CHAN1:REF:STAT ON" - ], - [ - "VOLT", - "VOLT", - "OFF", - ] + ik.keithley.Keithley2182, + [ + "SENS:FUNC?", + "SENS:FUNC?", + "SENS:VOLT:CHAN1:REF:STAT?", + "SENS:VOLT:CHAN1:REF:STAT ON", + ], + [ + "VOLT", + "VOLT", + "OFF", + ], ) as inst: inst.relative = True def test_relative_set_wrong_type(): with pytest.raises(TypeError), expected_protocol( - ik.keithley.Keithley2182, - [], - [] + ik.keithley.Keithley2182, [], [] ) as inst: inst.relative = "derp" def test_input_range(): """Raise NotImplementedError when getting / setting input range.""" - with expected_protocol( - ik.keithley.Keithley2182, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley2182, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.input_range with pytest.raises(NotImplementedError): diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index 6bade5797..fe2f33f75 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -18,16 +18,10 @@ # pylint: disable=protected-access + def test_zero_check(): with expected_protocol( - ik.keithley.Keithley485, - [ - "C0X", - "C1X", - "U0X" - ], [ - "4851000000000:" - ] + ik.keithley.Keithley485, ["C0X", "C1X", "U0X"], ["4851000000000:"] ) as inst: inst.zero_check = False inst.zero_check = True @@ -40,14 +34,7 @@ def test_zero_check(): def test_log(): with expected_protocol( - ik.keithley.Keithley485, - [ - "D0X", - "D1X", - "U0X" - ], [ - "4850100000000:" - ] + ik.keithley.Keithley485, ["D0X", "D1X", "U0X"], ["4850100000000:"] ) as inst: inst.log = False inst.log = True @@ -60,30 +47,16 @@ def test_log(): def test_input_range(): with expected_protocol( - ik.keithley.Keithley485, - [ - "R0X", - "R7X", - "U0X" - ], [ - "4850070000000:" - ] + ik.keithley.Keithley485, ["R0X", "R7X", "U0X"], ["4850070000000:"] ) as inst: inst.input_range = "auto" inst.input_range = 2e-3 - assert inst.input_range == 2. * u.milliamp + assert inst.input_range == 2.0 * u.milliamp def test_relative(): with expected_protocol( - ik.keithley.Keithley485, - [ - "Z0X", - "Z1X", - "U0X" - ], [ - "4850001000000:" - ] + ik.keithley.Keithley485, ["Z0X", "Z1X", "U0X"], ["4850001000000:"] ) as inst: inst.relative = False inst.relative = True @@ -96,14 +69,7 @@ def test_relative(): def test_eoi_mode(): with expected_protocol( - ik.keithley.Keithley485, - [ - "K0X", - "K1X", - "U0X" - ], [ - "4850000100000:" - ] + ik.keithley.Keithley485, ["K0X", "K1X", "U0X"], ["4850000100000:"] ) as inst: inst.eoi_mode = True inst.eoi_mode = False @@ -116,14 +82,7 @@ def test_eoi_mode(): def test_trigger_mode(): with expected_protocol( - ik.keithley.Keithley485, - [ - "T0X", - "T5X", - "U0X" - ], [ - "4850000050000:" - ] + ik.keithley.Keithley485, ["T0X", "T5X", "U0X"], ["4850000050000:"] ) as inst: inst.trigger_mode = "continuous_ontalk" inst.trigger_mode = "oneshot_onx" @@ -132,19 +91,15 @@ def test_trigger_mode(): newval = 42 inst.trigger_mode = newval err_msg = err_info.value.args[0] - assert err_msg == f"Drive must be specified as a " \ - f"Keithley485.TriggerMode, got {newval} instead." + assert ( + err_msg == f"Drive must be specified as a " + f"Keithley485.TriggerMode, got {newval} instead." + ) def test_auto_range(): with expected_protocol( - ik.keithley.Keithley485, - [ - "R0X", - "U0X" - ], [ - "4850000000000:" - ] + ik.keithley.Keithley485, ["R0X", "U0X"], ["4850000000000:"] ) as inst: inst.auto_range() assert inst.input_range == "auto" @@ -155,11 +110,7 @@ def test_input_range_value(newval): """Set input range with a given value from list.""" valid = ("auto", 2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3) with expected_protocol( - ik.keithley.Keithley485, - [ - f"R{valid.index(newval)}X" - ], [ - ] + ik.keithley.Keithley485, [f"R{valid.index(newval)}X"], [] ) as inst: inst.input_range = newval @@ -170,11 +121,7 @@ def test_input_range_quantity(): newval = 2e-9 quant = u.Quantity(newval, u.A) with expected_protocol( - ik.keithley.Keithley485, - [ - f"R{valid.index(newval)}X" - ], [ - ] + ik.keithley.Keithley485, [f"R{valid.index(newval)}X"], [] ) as inst: inst.input_range = quant @@ -182,12 +129,7 @@ def test_input_range_quantity(): def test_input_range_invalid_value(): """Raise ValueError if invalid value is given.""" valid = ("auto", 2e-9, 2e-8, 2e-7, 2e-6, 2e-5, 2e-4, 2e-3) - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.input_range = 42 err_msg = err_info.value.args[0] @@ -197,57 +139,39 @@ def test_input_range_invalid_value(): def test_input_range_invalid_type(): """Raise TypeError if invalid type is given.""" invalid_type = [42] - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.input_range = invalid_type err_msg = err_info.value.args[0] - assert err_msg == f"Range setting must be specified as a float, " \ - f"int, or the string `auto`, got " \ - f"{type(invalid_type)}" + assert ( + err_msg == f"Range setting must be specified as a float, " + f"int, or the string `auto`, got " + f"{type(invalid_type)}" + ) def test_input_range_invalid_string(): """Raise ValueError if input range set with invalid string.""" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.input_range = "2e-9" err_msg = err_info.value.args[0] - assert err_msg == "Only `auto` is acceptable when specifying the " \ - "range as a string." + assert ( + err_msg == "Only `auto` is acceptable when specifying the " + "range as a string." + ) def test_get_status(): with expected_protocol( - ik.keithley.Keithley485, - [ - "U0X" - ], [ - "4850000000000:" - ] + ik.keithley.Keithley485, ["U0X"], ["4850000000000:"] ) as inst: inst.get_status() def test_measure(): with expected_protocol( - ik.keithley.Keithley485, - [ - "X", - "X" - ], [ - "NDCA+1.2345E-9", - "NDCL-9.0000E+0" - ] + ik.keithley.Keithley485, ["X", "X"], ["NDCA+1.2345E-9", "NDCL-9.0000E+0"] ) as inst: assert 1.2345 * u.nanoamp == inst.measure() assert 1 * u.nanoamp == inst.measure() @@ -256,20 +180,9 @@ def test_measure(): def test_get_status_word_fails(): """Raise IOError if status word query fails > 5 times.""" with expected_protocol( - ik.keithley.Keithley485, - [ - "U0X", - "U0X", - "U0X", - "U0X", - "U0X" - ], [ - "", - "", - "", - "", - "" - ] + ik.keithley.Keithley485, + ["U0X", "U0X", "U0X", "U0X", "U0X"], + ["", "", "", "", ""], ) as inst: with pytest.raises(IOError) as err_info: inst._get_status_word() @@ -280,28 +193,19 @@ def test_get_status_word_fails(): def test_parse_status_word_wrong_prefix(): """Raise ValueError if statusword has wrong prefix.""" wrong_statusword = "wrong statusword" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(ValueError) as err_info: inst._parse_status_word(wrong_statusword) err_msg = err_info.value.args[0] - assert err_msg == f"Status word starts with wrong prefix: " \ - f"{wrong_statusword}" + assert ( + err_msg == f"Status word starts with wrong prefix: " f"{wrong_statusword}" + ) def test_parse_status_word_cannot_parse(): """Raise RuntimeError if statusword cannot be parsed.""" bad_statusword = "485FFFFFFFFFF" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(RuntimeError) as err_info: inst._parse_status_word(bad_statusword) err_msg = err_info.value.args[0] @@ -312,29 +216,21 @@ def test_parse_measurement_invalid_status(): """Raise ValueError if invalild status encountered.""" status = "L" bad_measurement = f"{status}DCA+1.2345E-9" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(ValueError) as err_info: inst._parse_measurement(bad_measurement) err_msg = err_info.value.args[0] - assert err_msg == f"Invalid status word in measurement: " \ - f"{bytes(status, 'utf-8')}" + assert ( + err_msg == f"Invalid status word in measurement: " + f"{bytes(status, 'utf-8')}" + ) def test_parse_measurement_bad_status(): """Raise ValueError if non-normal status encountered.""" status = ik.keithley.Keithley485.Status.overflow bad_measurement = f"{status.value.decode('utf-8')}DCA+1.2345E-9" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(ValueError) as err_info: inst._parse_measurement(bad_measurement) err_msg = err_info.value.args[0] @@ -345,28 +241,20 @@ def test_parse_measurement_bad_function(): """Raise ValueError if non-normal function encountered.""" function = "XX" bad_measurement = f"N{function}A+1.2345E-9" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(ValueError) as err_info: inst._parse_measurement(bad_measurement) err_msg = err_info.value.args[0] - assert err_msg == f"Instrument not returning DC function: " \ - f"{bytes(function, 'utf-8')}" + assert ( + err_msg == f"Instrument not returning DC function: " + f"{bytes(function, 'utf-8')}" + ) def test_parse_measurement_bad_measurement(): """Raise ValueError if non-normal function encountered.""" bad_measurement = f"NDCA+1.23X5E-9" - with expected_protocol( - ik.keithley.Keithley485, - [ - ], [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley485, [], []) as inst: with pytest.raises(Exception) as err_info: inst._parse_measurement(bad_measurement) err_msg = err_info.value.args[0] diff --git a/instruments/tests/test_keithley/test_keithley580.py b/instruments/tests/test_keithley/test_keithley580.py index ed5d0c4c8..02ba8b3ef 100644 --- a/instruments/tests/test_keithley/test_keithley580.py +++ b/instruments/tests/test_keithley/test_keithley580.py @@ -47,18 +47,37 @@ def create_statusword(): :return: Method to make a status word. :rtype: `method` """ - def make_statusword(drive=b"1", polarity=b"0", drycircuit=b"0", - operate=b"0", rng=b"0", relative=b"0", trigger=b"1", - linefreq=b"0"): + + def make_statusword( + drive=b"1", + polarity=b"0", + drycircuit=b"0", + operate=b"0", + rng=b"0", + relative=b"0", + trigger=b"1", + linefreq=b"0", + ): """Create the status word.""" # other variables eoi = b"0" sqrondata = b"0" sqronerror = b"0" - status_word = struct.pack('@8c2s2sc', drive, polarity, drycircuit, - operate, rng, relative, eoi, trigger, - sqrondata, sqronerror, linefreq) + status_word = struct.pack( + "@8c2s2sc", + drive, + polarity, + drycircuit, + operate, + rng, + relative, + eoi, + trigger, + sqrondata, + sqronerror, + linefreq, + ) return b"580" + status_word @@ -75,12 +94,15 @@ def create_measurement(): :return: Method to make a measurement. :rtype: `method` """ - def make_measurement(status=b"N", polarity=b"+", drycircuit=b"D", - drive=b"P", resistance=b"42"): + + def make_measurement( + status=b"N", polarity=b"+", drycircuit=b"D", drive=b"P", resistance=b"42" + ): """Create a measurement.""" resistance = bytes(resistance.decode().zfill(11), "utf-8") - measurement = struct.pack('@4c11s', status, polarity, drycircuit, - drive, resistance) + measurement = struct.pack( + "@4c11s", status, polarity, drycircuit, drive, resistance + ) return measurement @@ -93,7 +115,7 @@ def mock_time(mocker): Use by default, such that getting status word is fast in tests. """ - return mocker.patch.object(time, 'sleep', return_value=None) + return mocker.patch.object(time, "sleep", return_value=None) # PROPERTIES # @@ -104,36 +126,29 @@ def test_polarity(init, create_statusword, newval): """Get / set instrument polarity.""" status_word = create_statusword(polarity=bytes(str(newval.value), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"P{newval.value}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"P{newval.value}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.polarity = newval assert inst.polarity == newval -@pytest.mark.parametrize("newval_str", [it.name for it in - ik.keithley.Keithley580.Polarity]) +@pytest.mark.parametrize( + "newval_str", [it.name for it in ik.keithley.Keithley580.Polarity] +) def test_polarity_string(init, newval_str): """Set polarity with a string.""" newval = ik.keithley.Keithley580.Polarity[newval_str] with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"P{newval.value}X" + ":", - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + f"P{newval.value}X" + ":", + ], + [], + sep="\n", ) as inst: inst.polarity = newval_str @@ -142,20 +157,21 @@ def test_polarity_wrong_type(init): """Raise TypeError if setting polarity with wrong type.""" wrong_type = 42 with expected_protocol( - ik.keithley.Keithley580, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(TypeError) as err_info: inst.polarity = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Polarity must be specified as a " \ - f"Keithley580.Polarity, got {wrong_type} " \ - f"instead." + assert ( + err_msg == f"Polarity must be specified as a " + f"Keithley580.Polarity, got {wrong_type} " + f"instead." + ) @pytest.mark.parametrize("newval", ik.keithley.Keithley580.Drive) @@ -163,36 +179,29 @@ def test_drive(init, create_statusword, newval): """Get / set instrument drive.""" status_word = create_statusword(drive=bytes(str(newval.value), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"D{newval.value}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"D{newval.value}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.drive = newval assert inst.drive == newval -@pytest.mark.parametrize("newval_str", [it.name for it in - ik.keithley.Keithley580.Drive]) +@pytest.mark.parametrize( + "newval_str", [it.name for it in ik.keithley.Keithley580.Drive] +) def test_drive_string(init, newval_str): """Set drive with a string.""" newval = ik.keithley.Keithley580.Drive[newval_str] with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"D{newval.value}X" + ":", - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + f"D{newval.value}X" + ":", + ], + [], + sep="\n", ) as inst: inst.drive = newval_str @@ -201,39 +210,32 @@ def test_drive_wrong_type(init): """Raise TypeError if setting drive with wrong type.""" wrong_type = 42 with expected_protocol( - ik.keithley.Keithley580, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(TypeError) as err_info: inst.drive = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Drive must be specified as a " \ - f"Keithley580.Drive, got {wrong_type} " \ - f"instead." + assert ( + err_msg == f"Drive must be specified as a " + f"Keithley580.Drive, got {wrong_type} " + f"instead." + ) @pytest.mark.parametrize("newval", (True, False)) def test_dry_circuit_test(init, create_statusword, newval): """Get / set dry circuit test.""" - status_word = create_statusword( - drycircuit=bytes(str(int(newval)), "utf-8")) + status_word = create_statusword(drycircuit=bytes(str(int(newval)), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"C{int(newval)}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"C{int(newval)}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.dry_circuit_test = newval assert inst.dry_circuit_test == newval @@ -243,13 +245,12 @@ def test_dry_circuit_test_wrong_type(init): """Raise TypeError if setting dry circuit test with wrong type.""" wrong_type = 42 with expected_protocol( - ik.keithley.Keithley580, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(TypeError) as err_info: inst.dry_circuit_test = wrong_type @@ -260,20 +261,12 @@ def test_dry_circuit_test_wrong_type(init): @pytest.mark.parametrize("newval", (True, False)) def test_operate(init, create_statusword, newval): """Get / set operate.""" - status_word = create_statusword( - operate=bytes(str(int(newval)), "utf-8")) + status_word = create_statusword(operate=bytes(str(int(newval)), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"O{int(newval)}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"O{int(newval)}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.operate = newval assert inst.operate == newval @@ -283,13 +276,12 @@ def test_operate_wrong_type(init): """Raise TypeError if setting operate with wrong type.""" wrong_type = 42 with expected_protocol( - ik.keithley.Keithley580, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(TypeError) as err_info: inst.operate = wrong_type @@ -300,20 +292,12 @@ def test_operate_wrong_type(init): @pytest.mark.parametrize("newval", (True, False)) def test_relative(init, create_statusword, newval): """Get / set relative.""" - status_word = create_statusword( - relative=bytes(str(int(newval)), "utf-8")) + status_word = create_statusword(relative=bytes(str(int(newval)), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"Z{int(newval)}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"Z{int(newval)}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.relative = newval assert inst.relative == newval @@ -323,13 +307,12 @@ def test_relative_wrong_type(init): """Raise TypeError if setting relative with wrong type.""" wrong_type = 42 with expected_protocol( - ik.keithley.Keithley580, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(TypeError) as err_info: inst.relative = wrong_type @@ -343,13 +326,12 @@ def test_trigger_mode_get(init): Unclear why this is not implemented. """ with expected_protocol( - ik.keithley.Keithley580, - [ - init, - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(NotImplementedError): assert inst.trigger_mode @@ -359,14 +341,7 @@ def test_trigger_mode_get(init): def test_trigger_mode_set(init, newval): """Set instrument trigger mode.""" with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"T{newval.value}X" + ":" - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, [init, f"T{newval.value}X" + ":"], [], sep="\n" ) as inst: inst.trigger_mode = newval @@ -376,14 +351,7 @@ def test_trigger_mode_set_string(init, newval): """Set instrument trigger mode as a string.""" newval_str = newval.name with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"T{newval.value}X" + ":" - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, [init, f"T{newval.value}X" + ":"], [], sep="\n" ) as inst: inst.trigger_mode = newval_str @@ -391,46 +359,31 @@ def test_trigger_mode_set_string(init, newval): def test_trigger_mode_set_type_error(init): """Raise TypeError when setting trigger mode with wrong type.""" wrong_type = 42 - with expected_protocol( - ik.keithley.Keithley580, - [ - init - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(TypeError) as err_info: inst.trigger_mode = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Drive must be specified as a " \ - f"Keithley580.TriggerMode, got " \ - f"{wrong_type} instead." + assert ( + err_msg == f"Drive must be specified as a " + f"Keithley580.TriggerMode, got " + f"{wrong_type} instead." + ) @pytest.mark.parametrize("newval", (2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5)) def test_input_range_float(init, create_statusword, newval): """Get / set input range with a float, unitful and unitless.""" - valid = ('auto', 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) + valid = ("auto", 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) newval_unitful = newval * u.ohm newval_index = valid.index(newval) - status_word = create_statusword( - rng=bytes(str(newval_index), "utf-8")) + status_word = create_statusword(rng=bytes(str(newval_index), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"R{newval_index}X" + ":", - f"R{newval_index}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"R{newval_index}X" + ":", f"R{newval_index}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.input_range = newval inst.input_range = newval_unitful @@ -439,43 +392,28 @@ def test_input_range_float(init, create_statusword, newval): def test_input_range_auto(init, create_statusword): """Get / set input range auto.""" - newval = 'auto' + newval = "auto" newval_index = 0 - status_word = create_statusword( - rng=bytes(str(newval_index), "utf-8")) + status_word = create_statusword(rng=bytes(str(newval_index), "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - f"R{newval_index}X" + ":", - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, f"R{newval_index}X" + ":", "U0X:", ":"], + [status_word + b":"], + sep="\n", ) as inst: inst.input_range = newval assert inst.input_range == newval -@given(newval=st.floats().filter( - lambda x: x not in (2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5))) +@given( + newval=st.floats().filter(lambda x: x not in (2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5)) +) def test_input_range_float_value_error(init, newval): """Raise ValueError if input range set to invalid value.""" - valid = ('auto', 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) - with expected_protocol( - ik.keithley.Keithley580, - [ - init - ], - [ - ], - sep="\n" - ) as inst: + valid = ("auto", 2e-1, 2e0, 2e1, 2e2, 2e3, 2e4, 2e5) + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(ValueError) as err_info: inst.input_range = newval err_msg = err_info.value.args[0] @@ -484,43 +422,31 @@ def test_input_range_float_value_error(init, newval): def test_input_range_auto_value_error(init): """Raise ValueError if string set as input range is not 'auto'.""" - newval = 'automatic' + newval = "automatic" - with expected_protocol( - ik.keithley.Keithley580, - [ - init - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(ValueError) as err_info: inst.input_range = newval err_msg = err_info.value.args[0] - assert err_msg == "Only \"auto\" is acceptable when specifying the " \ - "input range as a string." + assert ( + err_msg == 'Only "auto" is acceptable when specifying the ' + "input range as a string." + ) def test_input_range_type_error(init): """Raise TypeError if input range is set with wrong type.""" wrong_type = {"The Answer": 42} - with expected_protocol( - ik.keithley.Keithley580, - [ - init - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(TypeError) as err_info: inst.input_range = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Range setting must be specified as a float, " \ - f"int, or the string \"auto\", got " \ - f"{type(wrong_type)}" + assert ( + err_msg == f"Range setting must be specified as a float, " + f'int, or the string "auto", got ' + f"{type(wrong_type)}" + ) # METHODS # @@ -528,30 +454,14 @@ def test_input_range_type_error(init): def test_trigger(init): """Send a trigger to instrument.""" - with expected_protocol( - ik.keithley.Keithley580, - [ - init, - "X:" - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init, "X:"], [], sep="\n") as inst: inst.trigger() def test_auto_range(init): """Put instrument into auto range mode.""" with expected_protocol( - ik.keithley.Keithley580, - [ - init, - "R0X:" - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, [init, "R0X:"], [], sep="\n" ) as inst: inst.auto_range() @@ -559,15 +469,7 @@ def test_auto_range(init): def test_set_calibration_value(init): """Raise NotImplementedError when trying to set calibration value.""" value = None - with expected_protocol( - ik.keithley.Keithley580, - [ - init - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(NotImplementedError) as err_info: inst.set_calibration_value(value) err_msg = err_info.value.args[0] @@ -576,15 +478,7 @@ def test_set_calibration_value(init): def test_store_calibration_constants(init): """Raise NotImplementedError when trying to store calibration constants.""" - with expected_protocol( - ik.keithley.Keithley580, - [ - init - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(NotImplementedError) as err_info: inst.store_calibration_constants() err_msg = err_info.value.args[0] @@ -599,16 +493,7 @@ def test_get_status_word(init, create_statusword, mock_time): status_word = create_statusword() with expected_protocol( - ik.keithley.Keithley580, - [ - init, - "U0X:", - ":" - ], - [ - status_word + b":" - ], - sep="\n" + ik.keithley.Keithley580, [init, "U0X:", ":"], [status_word + b":"], sep="\n" ) as inst: assert inst.get_status_word() == status_word mock_time.assert_called_with(1) @@ -619,28 +504,16 @@ def test_get_status_word_fails(init, mock_time): wrong_status_word = b"195 12345" with expected_protocol( - ik.keithley.Keithley580, - [ - init, - "U0X:", - ":", - "U0X:", - ":", - "U0X:", - ":", - "U0X:", - ":", - "U0X:", - ":" - ], - [ - wrong_status_word, - wrong_status_word, - wrong_status_word, - wrong_status_word, - wrong_status_word - ], - sep="\n" + ik.keithley.Keithley580, + [init, "U0X:", ":", "U0X:", ":", "U0X:", ":", "U0X:", ":", "U0X:", ":"], + [ + wrong_status_word, + wrong_status_word, + wrong_status_word, + wrong_status_word, + wrong_status_word, + ], + sep="\n", ) as inst: with pytest.raises(IOError) as err_info: inst.get_status_word() @@ -658,9 +531,7 @@ def test_parse_status_word(init, create_statusword, line_frequency): Here, we thus just use the default status word created by the fixture and only parametrize where other routines do not. """ - status_word = create_statusword( - linefreq=bytes(line_frequency[0], "utf-8") - ) + status_word = create_statusword(linefreq=bytes(line_frequency[0], "utf-8")) # create the dictionary to compare to expected_dict = { "drive": "dc", @@ -671,48 +542,33 @@ def test_parse_status_word(init, create_statusword, line_frequency): "relative": False, "eoi": b"0", "trigger": True, - "sqrondata": struct.pack('@2s', b"0"), - "sqronerror": struct.pack('@2s', b"0"), + "sqrondata": struct.pack("@2s", b"0"), + "sqronerror": struct.pack("@2s", b"0"), "linefreq": line_frequency[1], } - with expected_protocol( - ik.keithley.Keithley580, - [ - init - - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: # add terminator to expected dict: expected_dict["terminator"] = inst.terminator assert inst.parse_status_word(status_word) == expected_dict -@given(drive=st.integers(min_value=2, max_value=9), - polarity=st.integers(min_value=2, max_value=9), - rng=st.integers(min_value=8, max_value=9), - linefreq=st.integers(min_value=2, max_value=9)) -def test_parse_status_word_invalid_values(init, create_statusword, drive, - polarity, rng, linefreq): +@given( + drive=st.integers(min_value=2, max_value=9), + polarity=st.integers(min_value=2, max_value=9), + rng=st.integers(min_value=8, max_value=9), + linefreq=st.integers(min_value=2, max_value=9), +) +def test_parse_status_word_invalid_values( + init, create_statusword, drive, polarity, rng, linefreq +): """Raise RuntimeError if status word contains invalid values.""" status_word = create_statusword( drive=bytes(str(drive), "utf-8"), polarity=bytes(str(polarity), "utf-8"), rng=bytes(str(rng), "utf-8"), - linefreq=bytes(str(linefreq), "utf-8") + linefreq=bytes(str(linefreq), "utf-8"), ) - with expected_protocol( - ik.keithley.Keithley580, - [ - init - - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(RuntimeError) as err_info: inst.parse_status_word(status_word) err_msg = err_info.value.args[0] @@ -722,21 +578,14 @@ def test_parse_status_word_invalid_values(init, create_statusword, drive, def test_parse_status_word_invalid_prefix(init): """Raise ValueError if status word has invalid prefix.""" invalid_status_word = b"314 424242" - with expected_protocol( - ik.keithley.Keithley580, - [ - init - - ], - [ - ], - sep="\n" - ) as inst: + with expected_protocol(ik.keithley.Keithley580, [init], [], sep="\n") as inst: with pytest.raises(ValueError) as err_info: inst.parse_status_word(invalid_status_word) err_msg = err_info.value.args[0] - assert err_msg == f"Status word starts with wrong prefix: " \ - f"{invalid_status_word}" + assert ( + err_msg == f"Status word starts with wrong prefix: " + f"{invalid_status_word}" + ) # MEASUREMENT # @@ -749,16 +598,10 @@ def test_measure(init, create_measurement, resistance): resistance_byte = bytes(f"{resistance:.3f}", "utf-8") measurement = create_measurement(resistance=resistance_byte) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - "X:", # trigger - ":" - ], - [ - measurement + b":" - ], - sep="\n" + ik.keithley.Keithley580, + [init, "X:", ":"], # trigger + [measurement + b":"], + sep="\n", ) as inst: read_value = inst.measure() assert read_value.magnitude == pytest.approx(resistance, rel=1e-5) @@ -769,8 +612,9 @@ def test_measure(init, create_measurement, resistance): @pytest.mark.parametrize("polarity", (b"+", b"-")) @pytest.mark.parametrize("drycircuit", (b"N", b"D")) @pytest.mark.parametrize("drive", (b"P", b"D")) -def test_parse_measurement(init, create_measurement, status, polarity, - drycircuit, drive): +def test_parse_measurement( + init, create_measurement, status, polarity, drycircuit, drive +): """Parse a given measurement.""" resistance = b"42" measurement = create_measurement( @@ -778,20 +622,16 @@ def test_parse_measurement(init, create_measurement, status, polarity, polarity=polarity, drycircuit=drycircuit, drive=drive, - resistance=resistance + resistance=resistance, ) # valid states - valid = {'status': {b'S': 'standby', - b'N': 'normal', - b'O': 'overflow', - b'Z': 'relative'}, - 'polarity': {b'+': '+', - b'-': '-'}, - 'drycircuit': {b'N': False, - b'D': True}, - 'drive': {b'P': 'pulsed', - b'D': 'dc'}} + valid = { + "status": {b"S": "standby", b"N": "normal", b"O": "overflow", b"Z": "relative"}, + "polarity": {b"+": "+", b"-": "-"}, + "drycircuit": {b"N": False, b"D": True}, + "drive": {b"P": "pulsed", b"D": "dc"}, + } # create expected dictionary dict_expected = { @@ -799,18 +639,16 @@ def test_parse_measurement(init, create_measurement, status, polarity, "polarity": valid["polarity"][polarity], "drycircuit": valid["drycircuit"][drycircuit], "drive": valid["drive"][drive], - "resistance": float(resistance.decode()) * u.ohm + "resistance": float(resistance.decode()) * u.ohm, } with expected_protocol( - ik.keithley.Keithley580, - [ - init, - - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: assert inst.parse_measurement(measurement) == dict_expected @@ -820,14 +658,12 @@ def test_parse_measurement_invalid(init, create_measurement): measurement = create_measurement(status=bytes("V", "utf-8")) with expected_protocol( - ik.keithley.Keithley580, - [ - init, - - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, + [ + init, + ], + [], + sep="\n", ) as inst: with pytest.raises(Exception) as exc_info: inst.parse_measurement(measurement) @@ -842,15 +678,7 @@ def test_sendcmd(init): """Send a command to the instrument.""" cmd = "COMMAND" with expected_protocol( - ik.keithley.Keithley580, - [ - init, - cmd + ":" - - ], - [ - ], - sep="\n" + ik.keithley.Keithley580, [init, cmd + ":"], [], sep="\n" ) as inst: inst.sendcmd(cmd) @@ -860,15 +688,6 @@ def test_query(init): cmd = "COMMAND" answer = "ANSWER" with expected_protocol( - ik.keithley.Keithley580, - [ - init, - cmd + ":" - - ], - [ - answer + ":" - ], - sep="\n" + ik.keithley.Keithley580, [init, cmd + ":"], [answer + ":"], sep="\n" ) as inst: assert inst.query(cmd) == answer diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index d68b79597..6d3e7ba9f 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -24,35 +24,24 @@ def test_channel(): def test_voltage(): """Raise NotImplementedError when getting / setting voltage.""" - with expected_protocol( - ik.keithley.Keithley6220, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.keithley.Keithley6220, [], []) as inst: with pytest.raises(NotImplementedError) as err_info: _ = inst.voltage err_msg = err_info.value.args[0] - assert err_msg == "The Keithley 6220 does not support voltage " \ - "settings." + assert err_msg == "The Keithley 6220 does not support voltage " "settings." with pytest.raises(NotImplementedError) as err_info: inst.voltage = 42 err_msg = err_info.value.args[0] - assert err_msg == "The Keithley 6220 does not support voltage " \ - "settings." + assert err_msg == "The Keithley 6220 does not support voltage " "settings." def test_current(): with expected_protocol( - ik.keithley.Keithley6220, - [ - "SOUR:CURR?", - "SOUR:CURR {:e}".format(0.05) - ], - [ - "0.1", - ] + ik.keithley.Keithley6220, + ["SOUR:CURR?", "SOUR:CURR {:e}".format(0.05)], + [ + "0.1", + ], ) as inst: assert inst.current == 100 * u.milliamp assert inst.current_min == -105 * u.milliamp @@ -61,11 +50,5 @@ def test_current(): def test_disable(): - with expected_protocol( - ik.keithley.Keithley6220, - [ - "SOUR:CLE:IMM" - ], - [] - ) as inst: + with expected_protocol(ik.keithley.Keithley6220, ["SOUR:CLE:IMM"], []) as inst: inst.disable() diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index ca997c991..88e078040 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -34,13 +34,11 @@ def test_valid_range_invalid(): def test_parse_measurement(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + ], + ['"VOLT:DC"'], ) as inst: reading, timestamp, status = inst._parse_measurement("1.0,1234,5678") assert reading == 1.0 * u.volt @@ -50,14 +48,7 @@ def test_parse_measurement(): def test_mode(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - 'FUNCTION "VOLT:DC"' - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, ["FUNCTION?", 'FUNCTION "VOLT:DC"'], ['"VOLT:DC"'] ) as inst: assert inst.mode == inst.Mode.voltage inst.mode = inst.Mode.voltage @@ -65,14 +56,7 @@ def test_mode(): def test_trigger_source(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "TRIGGER:SOURCE?", - "TRIGGER:SOURCE IMM" - ], - [ - "TLINK" - ] + ik.keithley.Keithley6514, ["TRIGGER:SOURCE?", "TRIGGER:SOURCE IMM"], ["TLINK"] ) as inst: assert inst.trigger_mode == inst.TriggerMode.tlink inst.trigger_mode = inst.TriggerMode.immediate @@ -80,14 +64,7 @@ def test_trigger_source(): def test_arm_source(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "ARM:SOURCE?", - "ARM:SOURCE IMM" - ], - [ - "TIM" - ] + ik.keithley.Keithley6514, ["ARM:SOURCE?", "ARM:SOURCE IMM"], ["TIM"] ) as inst: assert inst.arm_source == inst.ArmSource.timer inst.arm_source = inst.ArmSource.immediate @@ -95,14 +72,7 @@ def test_arm_source(): def test_zero_check(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "SYST:ZCH?", - "SYST:ZCH ON" - ], - [ - "OFF" - ] + ik.keithley.Keithley6514, ["SYST:ZCH?", "SYST:ZCH ON"], ["OFF"] ) as inst: assert inst.zero_check is False inst.zero_check = True @@ -110,14 +80,7 @@ def test_zero_check(): def test_zero_correct(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "SYST:ZCOR?", - "SYST:ZCOR ON" - ], - [ - "OFF" - ] + ik.keithley.Keithley6514, ["SYST:ZCOR?", "SYST:ZCOR ON"], ["OFF"] ) as inst: assert inst.zero_correct is False inst.zero_correct = True @@ -125,31 +88,20 @@ def test_zero_correct(): def test_unit(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + ], + ['"VOLT:DC"'], ) as inst: assert inst.unit == u.volt def test_auto_range(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - "VOLT:DC:RANGE:AUTO?", - "FUNCTION?", - "VOLT:DC:RANGE:AUTO 1" - ], - [ - '"VOLT:DC"', - "0", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + ["FUNCTION?", "VOLT:DC:RANGE:AUTO?", "FUNCTION?", "VOLT:DC:RANGE:AUTO 1"], + ['"VOLT:DC"', "0", '"VOLT:DC"'], ) as inst: assert inst.auto_range is False inst.auto_range = True @@ -157,18 +109,14 @@ def test_auto_range(): def test_input_range(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?", - "VOLT:DC:RANGE:UPPER?", - "FUNCTION?", - "VOLT:DC:RANGE:UPPER {:e}".format(20) - ], - [ - '"VOLT:DC"', - "10", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FUNCTION?", + "VOLT:DC:RANGE:UPPER?", + "FUNCTION?", + "VOLT:DC:RANGE:UPPER {:e}".format(20), + ], + ['"VOLT:DC"', "10", '"VOLT:DC"'], ) as inst: assert inst.input_range == 10 * u.volt inst.input_range = 20 * u.volt @@ -176,39 +124,30 @@ def test_input_range(): def test_input_range_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.keithley.Keithley6514, - [ - "FUNCTION?" - ], - [ - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, ["FUNCTION?"], ['"VOLT:DC"'] ) as inst: inst.input_range = 10 * u.volt def test_auto_config(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "CONF:VOLT:DC", - ], - [] + ik.keithley.Keithley6514, + [ + "CONF:VOLT:DC", + ], + [], ) as inst: inst.auto_config(inst.Mode.voltage) def test_fetch(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "FETC?", - "FUNCTION?", - ], - [ - "1.0,1234,5678", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "FETC?", + "FUNCTION?", + ], + ["1.0,1234,5678", '"VOLT:DC"'], ) as inst: reading, timestamp = inst.fetch() assert reading == 1.0 * u.volt @@ -217,15 +156,12 @@ def test_fetch(): def test_read(): with expected_protocol( - ik.keithley.Keithley6514, - [ - "READ?", - "FUNCTION?", - ], - [ - "1.0,1234,5678", - '"VOLT:DC"' - ] + ik.keithley.Keithley6514, + [ + "READ?", + "FUNCTION?", + ], + ["1.0,1234,5678", '"VOLT:DC"'], ) as inst: reading, timestamp = inst.read_measurements() assert reading == 1.0 * u.volt diff --git a/instruments/tests/test_lakeshore/test_lakeshore340.py b/instruments/tests/test_lakeshore/test_lakeshore340.py index 6c20143f4..ab28be9e1 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore340.py +++ b/instruments/tests/test_lakeshore/test_lakeshore340.py @@ -22,11 +22,9 @@ def test_lakeshore340_sensor_init(): Test initialization of sensor class. """ with expected_protocol( - ik.lakeshore.Lakeshore340, - [ - ], - [ - ], + ik.lakeshore.Lakeshore340, + [], + [], ) as cryo: sensor = cryo.sensor[0] assert sensor._parent is cryo @@ -38,12 +36,8 @@ def test_lakeshore340_sensor_temperature(): Receive a unitful temperature from a sensor. """ with expected_protocol( - ik.lakeshore.Lakeshore340, - [ - "KRDG?1" - ], - [ - "77" - ], + ik.lakeshore.Lakeshore340, + ["KRDG?1"], + ["77"], ) as cryo: assert cryo.sensor[0].temperature == u.Quantity(77, u.K) diff --git a/instruments/tests/test_lakeshore/test_lakeshore370.py b/instruments/tests/test_lakeshore/test_lakeshore370.py index aaa2a4d99..2f1099e2a 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore370.py +++ b/instruments/tests/test_lakeshore/test_lakeshore370.py @@ -33,12 +33,9 @@ def test_lakeshore370_channel_init(init): Test initialization of channel class. """ with expected_protocol( - ik.lakeshore.Lakeshore370, - [ - init - ], - [ - ], + ik.lakeshore.Lakeshore370, + [init], + [], ) as lsh: channel = lsh.channel[7] assert channel._parent is lsh @@ -50,13 +47,8 @@ def test_lakeshore370_channel_resistance(init): Receive a unitful resistance from a channel. """ with expected_protocol( - ik.lakeshore.Lakeshore370, - [ - init, - "RDGR? 1" - ], - [ - "100." - ], + ik.lakeshore.Lakeshore370, + [init, "RDGR? 1"], + ["100."], ) as lsh: assert lsh.channel[0].resistance == u.Quantity(100, u.ohm) diff --git a/instruments/tests/test_lakeshore/test_lakeshore475.py b/instruments/tests/test_lakeshore/test_lakeshore475.py index c02039fb7..ae4b758a7 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore475.py +++ b/instruments/tests/test_lakeshore/test_lakeshore475.py @@ -23,17 +23,11 @@ def test_lakeshore475_field(): Get field from connected probe unitful. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "RDGFIELD?", - "UNIT?" - ], - [ - "200.", - "2" - ], + ik.lakeshore.Lakeshore475, + ["RDGFIELD?", "UNIT?"], + ["200.", "2"], ) as lsh: - assert lsh.field == u.Quantity(200., u.tesla) + assert lsh.field == u.Quantity(200.0, u.tesla) def test_lakeshore475_field_units(): @@ -41,14 +35,9 @@ def test_lakeshore475_field_units(): Get / set field unit on device. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "UNIT?", - "UNIT 2" - ], - [ - "3" - ], + ik.lakeshore.Lakeshore475, + ["UNIT?", "UNIT 2"], + ["3"], ) as lsh: assert lsh.field_units == u.oersted lsh.field_units = u.tesla @@ -59,11 +48,9 @@ def test_lakeshore475_field_units_invalid_unit(): Raise a ValueError if an invalid unit is given. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: with pytest.raises(ValueError) as exc_info: lsh.field_units = u.m @@ -76,11 +63,9 @@ def test_lakeshore475_field_units_not_a_unit(): Raise a ValueError if something else than a quantity is given. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: with pytest.raises(TypeError) as exc_info: lsh.field_units = 42 @@ -93,14 +78,9 @@ def test_lakeshore475_temp_units(): Get / set temperature unit on device. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "TUNIT?", - "TUNIT 2" - ], - [ - "1" - ], + ik.lakeshore.Lakeshore475, + ["TUNIT?", "TUNIT 2"], + ["1"], ) as lsh: assert lsh.temp_units == u.celsius lsh.temp_units = u.kelvin @@ -111,11 +91,9 @@ def test_lakeshore475_temp_units_invalid_unit(): Raise a ValueError if an invalid unit is given. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: with pytest.raises(TypeError) as exc_info: lsh.temp_units = u.fahrenheit @@ -128,11 +106,9 @@ def test_lakeshore475_temp_units_not_a_unit(): Raise a ValueError if something else than a quantity is given. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: with pytest.raises(TypeError) as exc_info: lsh.temp_units = 42 @@ -145,26 +121,21 @@ def test_lakeshore475_field_setpoint(): Get / set field set point. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CSETP?", - "UNIT?", - "UNIT?", - "CSETP 1.0", # send 1 tesla - "UNIT?", - "CSETP 23.0" # send 23 unitless (equals gauss) - ], - [ - "10.", - "1", - "2", - "1" - ], + ik.lakeshore.Lakeshore475, + [ + "CSETP?", + "UNIT?", + "UNIT?", + "CSETP 1.0", # send 1 tesla + "UNIT?", + "CSETP 23.0", # send 23 unitless (equals gauss) + ], + ["10.", "1", "2", "1"], ) as lsh: assert lsh.field_setpoint == u.Quantity(10, u.gauss) - lsh.field_setpoint = u.Quantity(1., u.tesla) - lsh.field_setpoint = 23. + lsh.field_setpoint = u.Quantity(1.0, u.tesla) + lsh.field_setpoint = 23.0 def test_lakeshore475_field_setpoint_wrong_units(): @@ -172,16 +143,14 @@ def test_lakeshore475_field_setpoint_wrong_units(): Setting the field setpoint with the wrong units """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "UNIT?", - ], - [ - "1" - ], + ik.lakeshore.Lakeshore475, + [ + "UNIT?", + ], + ["1"], ) as lsh: with pytest.raises(ValueError) as exc_info: - lsh.field_setpoint = u.Quantity(1., u.tesla) + lsh.field_setpoint = u.Quantity(1.0, u.tesla) exc_msg = exc_info.value.args[0] assert "Field setpoint must be specified in the same units" in exc_msg @@ -191,22 +160,16 @@ def test_lakeshore475_field_get_control_params(): Get field control parameters. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CPARAM?", - "UNIT?" - ], - [ - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2" # teslas - ], + ik.lakeshore.Lakeshore475, + ["CPARAM?", "UNIT?"], + ["+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", "2"], # teslas ) as lsh: current_params = lsh.field_control_params assert current_params == ( 1.0, 10.0, u.Quantity(42.0, u.tesla / u.min), - u.Quantity(100.0, u.volt / u.min) + u.Quantity(100.0, u.volt / u.min), ) @@ -215,32 +178,19 @@ def test_lakeshore475_field_set_control_params(): Set field control parameters, unitful and using assumed units. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "UNIT?", - "CPARAM 5.0,50.0,120.0,60.0", - "UNIT?", - "CPARAM 5.0,50.0,120.0,60.0" - ], - [ - "2", # teslas - "2" # teslas - ], + ik.lakeshore.Lakeshore475, + ["UNIT?", "CPARAM 5.0,50.0,120.0,60.0", "UNIT?", "CPARAM 5.0,50.0,120.0,60.0"], + ["2", "2"], # teslas # teslas ) as lsh: # currently set units are used lsh.field_control_params = ( 5.0, 50.0, u.Quantity(120.0, u.tesla / u.min), - u.Quantity(60.0, u.volt / u.min) + u.Quantity(60.0, u.volt / u.min), ) # no units are used - lsh.field_control_params = ( - 5.0, - 50.0, - 120.0, - 60.0 - ) + lsh.field_control_params = (5.0, 50.0, 120.0, 60.0) def test_lakeshore475_field_set_control_params_not_a_tuple(): @@ -248,15 +198,14 @@ def test_lakeshore475_field_set_control_params_not_a_tuple(): Set field control parameters with wrong type. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [], - [], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: with pytest.raises(TypeError) as exc_info: lsh.field_control_params = 42 exc_msg = exc_info.value.args[0] - assert exc_msg == "Field control parameters must be specified as " \ - " a tuple" + assert exc_msg == "Field control parameters must be specified as " " a tuple" def test_lakeshore475_field_set_control_params_wrong_units(): @@ -264,23 +213,26 @@ def test_lakeshore475_field_set_control_params_wrong_units(): Set field control parameters with the wrong units """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "UNIT?", - ], - [ - "1", # gauss - ], + ik.lakeshore.Lakeshore475, + [ + "UNIT?", + ], + [ + "1", # gauss + ], ) as lsh: - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError) as exc_info: lsh.field_control_params = ( 5.0, 50.0, u.Quantity(120.0, u.tesla / u.min), - u.Quantity(60.0, u.volt / u.min) + u.Quantity(60.0, u.volt / u.min), ) exc_msg = exc_info.value.args[0] - assert "Field control params ramp rate must be specified in the same units" in exc_msg + assert ( + "Field control params ramp rate must be specified in the same units" + in exc_msg + ) def test_lakeshore475_p_value(): @@ -288,22 +240,22 @@ def test_lakeshore475_p_value(): Get / set p-value. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CPARAM?", - "UNIT?", - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM 5.0,10.0,42.0,100.0", - ], - [ - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "2" - ], + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 5.0,10.0,42.0,100.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + ], ) as lsh: assert lsh.p_value == 1.0 lsh.p_value = 5.0 @@ -314,22 +266,22 @@ def test_lakeshore475_i_value(): Get / set i-value. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CPARAM?", - "UNIT?", - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM 1.0,5.0,42.0,100.0", - ], - [ - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "2" - ], + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,5.0,42.0,100.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + ], ) as lsh: assert lsh.i_value == 10.0 lsh.i_value = 5.0 @@ -340,33 +292,33 @@ def test_lakeshore475_ramp_rate(): Get / set ramp rate, unitful and not. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM 1.0,10.0,420.0,100.0", - "UNIT?", - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM 1.0,10.0,420.0,100.0", - ], - [ - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "2", - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "2", - "2", - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", - "2" - ], + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,420.0,100.0", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,420.0,100.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + "2", + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", + "2", + ], ) as lsh: assert lsh.ramp_rate == u.Quantity(42.0, u.tesla / u.min) lsh.ramp_rate = u.Quantity(420.0, u.tesla / u.min) @@ -378,29 +330,29 @@ def test_lakeshore475_control_slope_limit(): Get / set slope limit, unitful and not. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CPARAM?", - "UNIT?", - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM 1.0,10.0,42.0,42.0", - "CPARAM?", - "UNIT?", - "UNIT?", - "CPARAM 1.0,10.0,42.0,42.0", - ], - [ - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", # teslas - "2", - "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", - "2", - "2" - ], + ik.lakeshore.Lakeshore475, + [ + "CPARAM?", + "UNIT?", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,42.0,42.0", + "CPARAM?", + "UNIT?", + "UNIT?", + "CPARAM 1.0,10.0,42.0,42.0", + ], + [ + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", # teslas + "2", + "+1.0E+0,+1.0E+1,+4.2E+1,+1.0E+2", + "2", + "2", + ], ) as lsh: assert lsh.control_slope_limit == u.Quantity(100.0, u.V / u.min) lsh.control_slope_limit = u.Quantity(42000.0, u.mV / u.min) @@ -412,14 +364,9 @@ def test_lakeshore475_control_mode(): Get / set control mode. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "CMODE?", - "CMODE 1" - ], - [ - "0" - ], + ik.lakeshore.Lakeshore475, + ["CMODE?", "CMODE 1"], + ["0"], ) as lsh: assert not lsh.control_mode lsh.control_mode = True @@ -434,12 +381,9 @@ def test_lakeshore475_change_measurement_mode(): sent to device. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - "RDGMODE 1,2,3,2,1" - ], - [ - ], + ik.lakeshore.Lakeshore475, + ["RDGMODE 1,2,3,2,1"], + [], ) as lsh: # parameters to send mode = lsh.Mode.dc @@ -448,13 +392,7 @@ def test_lakeshore475_change_measurement_mode(): peak_mode = lsh.PeakMode.pulse peak_disp = lsh.PeakDisplay.positive # send them - lsh.change_measurement_mode( - mode, - resolution, - filter_type, - peak_mode, - peak_disp - ) + lsh.change_measurement_mode(mode, resolution, filter_type, peak_mode, peak_disp) def test_lakeshore475_change_measurement_mode_mismatched_type(): @@ -462,11 +400,9 @@ def test_lakeshore475_change_measurement_mode_mismatched_type(): Ensure that mismatched input type raises a TypeError. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: # parameters to send mode = lsh.Mode.dc @@ -477,65 +413,45 @@ def test_lakeshore475_change_measurement_mode_mismatched_type(): # check mode with pytest.raises(TypeError) as exc_info: lsh.change_measurement_mode( - 42, - resolution, - filter_type, - peak_mode, - peak_disp + 42, resolution, filter_type, peak_mode, peak_disp ) exc_msg = exc_info.value.args[0] - assert exc_msg == f"Mode setting must be a `Lakeshore475.Mode` " \ - f"value, got {type(42)} instead." + assert ( + exc_msg == f"Mode setting must be a `Lakeshore475.Mode` " + f"value, got {type(42)} instead." + ) # check resolution with pytest.raises(TypeError) as exc_info: - lsh.change_measurement_mode( - mode, - 3.14, - filter_type, - peak_mode, - peak_disp - ) + lsh.change_measurement_mode(mode, 3.14, filter_type, peak_mode, peak_disp) exc_msg = exc_info.value.args[0] assert exc_msg == 'Parameter "resolution" must be an integer.' # check filter_type with pytest.raises(TypeError) as exc_info: - lsh.change_measurement_mode( - mode, - resolution, - 42, - peak_mode, - peak_disp - ) + lsh.change_measurement_mode(mode, resolution, 42, peak_mode, peak_disp) exc_msg = exc_info.value.args[0] - assert exc_msg == f"Filter type setting must be a " \ - f"`Lakeshore475.Filter` value, " \ - f"got {type(42)} instead." + assert ( + exc_msg == f"Filter type setting must be a " + f"`Lakeshore475.Filter` value, " + f"got {type(42)} instead." + ) # check peak_mode with pytest.raises(TypeError) as exc_info: - lsh.change_measurement_mode( - mode, - resolution, - filter_type, - 42, - peak_disp - ) + lsh.change_measurement_mode(mode, resolution, filter_type, 42, peak_disp) exc_msg = exc_info.value.args[0] - assert exc_msg == f"Peak measurement type setting must be a " \ - f"`Lakeshore475.PeakMode` value, " \ - f"got {type(42)} instead." + assert ( + exc_msg == f"Peak measurement type setting must be a " + f"`Lakeshore475.PeakMode` value, " + f"got {type(42)} instead." + ) # check peak_display with pytest.raises(TypeError) as exc_info: - lsh.change_measurement_mode( - mode, - resolution, - filter_type, - peak_mode, - 42 - ) + lsh.change_measurement_mode(mode, resolution, filter_type, peak_mode, 42) exc_msg = exc_info.value.args[0] - assert exc_msg == f"Peak display type setting must be a " \ - f"`Lakeshore475.PeakDisplay` value, " \ - f"got {type(42)} instead." + assert ( + exc_msg == f"Peak display type setting must be a " + f"`Lakeshore475.PeakDisplay` value, " + f"got {type(42)} instead." + ) def test_lakeshore475_change_measurement_mode_invalid_resolution(): @@ -543,11 +459,9 @@ def test_lakeshore475_change_measurement_mode_invalid_resolution(): Ensure that mismatched input type raises a TypeError. """ with expected_protocol( - ik.lakeshore.Lakeshore475, - [ - ], - [ - ], + ik.lakeshore.Lakeshore475, + [], + [], ) as lsh: # parameters to send mode = lsh.Mode.dc @@ -556,23 +470,11 @@ def test_lakeshore475_change_measurement_mode_invalid_resolution(): peak_disp = lsh.PeakDisplay.positive # check resolution too low with pytest.raises(ValueError) as exc_info: - lsh.change_measurement_mode( - mode, - 2, - filter_type, - peak_mode, - peak_disp - ) + lsh.change_measurement_mode(mode, 2, filter_type, peak_mode, peak_disp) exc_msg = exc_info.value.args[0] assert exc_msg == "Only 3,4,5 are valid resolutions." # check resolution too high with pytest.raises(ValueError) as exc_info: - lsh.change_measurement_mode( - mode, - 6, - filter_type, - peak_mode, - peak_disp - ) + lsh.change_measurement_mode(mode, 6, filter_type, peak_mode, peak_disp) exc_msg = exc_info.value.args[0] assert exc_msg == "Only 3,4,5 are valid resolutions." diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index 27d5516c8..63948c0a0 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -19,55 +19,29 @@ def test_mhs_amplitude(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1a", - ":r2a", - ":s1a660", - ":s2a800" - ], - [ - ":r1a330", - ":r2a500", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1a", ":r2a", ":s1a660", ":s2a800"], + [":r1a330", ":r2a500", "ok", "ok"], + sep="\r\n", ) as mhs: - assert mhs.channel[0].amplitude[0] == 3.3*u.V - assert mhs.channel[1].amplitude[0] == 5.0*u.V - mhs.channel[0].amplitude = 6.6*u.V - mhs.channel[1].amplitude = 8.0*u.V + assert mhs.channel[0].amplitude[0] == 3.3 * u.V + assert mhs.channel[1].amplitude[0] == 5.0 * u.V + mhs.channel[0].amplitude = 6.6 * u.V + mhs.channel[1].amplitude = 8.0 * u.V def test_mhs_amplitude_dbm_notimplemented(): - with expected_protocol( - ik.minghe.MHS5200, - [], - [], - sep="\r\n" - ) as mhs: + with expected_protocol(ik.minghe.MHS5200, [], [], sep="\r\n") as mhs: with pytest.raises(NotImplementedError): mhs.channel[0].amplitude = u.Quantity(6.6, u.dBm) def test_mhs_duty_cycle(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1d", - ":r2d", - ":s1d6", - ":s2d80" - - ], - [ - ":r1d010", - ":r2d100", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1d", ":r2d", ":s1d6", ":s2d80"], + [":r1d010", ":r2d100", "ok", "ok"], + sep="\r\n", ) as mhs: assert mhs.channel[0].duty_cycle == 1.0 assert mhs.channel[1].duty_cycle == 10.0 @@ -77,20 +51,10 @@ def test_mhs_duty_cycle(): def test_mhs_enable(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1b", - ":r2b", - ":s1b0", - ":s2b1" - ], - [ - ":r1b1", - ":r2b0", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1b", ":r2b", ":s1b0", ":s2b1"], + [":r1b1", ":r2b0", "ok", "ok"], + sep="\r\n", ) as mhs: assert mhs.channel[0].enable assert not mhs.channel[1].enable @@ -100,45 +64,23 @@ def test_mhs_enable(): def test_mhs_frequency(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1f", - ":r2f", - ":s1f600000", - ":s2f800000" - - ], - [ - ":r1f3300000", - ":r2f50000000", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1f", ":r2f", ":s1f600000", ":s2f800000"], + [":r1f3300000", ":r2f50000000", "ok", "ok"], + sep="\r\n", ) as mhs: - assert mhs.channel[0].frequency == 33.0*u.kHz - assert mhs.channel[1].frequency == 500.0*u.kHz - mhs.channel[0].frequency = 6*u.kHz - mhs.channel[1].frequency = 8*u.kHz + assert mhs.channel[0].frequency == 33.0 * u.kHz + assert mhs.channel[1].frequency == 500.0 * u.kHz + mhs.channel[0].frequency = 6 * u.kHz + mhs.channel[1].frequency = 8 * u.kHz def test_mhs_offset(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1o", - ":r2o", - ":s1o60", - ":s2o180" - - ], - [ - ":r1o120", - ":r2o0", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1o", ":r2o", ":s1o60", ":s2o180"], + [":r1o120", ":r2o0", "ok", "ok"], + sep="\r\n", ) as mhs: assert mhs.channel[0].offset == 0 assert mhs.channel[1].offset == -1.2 @@ -148,21 +90,10 @@ def test_mhs_offset(): def test_mhs_phase(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1p", - ":r2p", - ":s1p60", - ":s2p180" - - ], - [ - ":r1p120", - ":r2p0", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1p", ":r2p", ":s1p60", ":s2p180"], + [":r1p120", ":r2p0", "ok", "ok"], + sep="\r\n", ) as mhs: assert mhs.channel[0].phase == 120 * u.degree assert mhs.channel[1].phase == 0 * u.degree @@ -172,21 +103,10 @@ def test_mhs_phase(): def test_mhs_wave_type(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r1w", - ":r2w", - ":s1w2", - ":s2w3" - - ], - [ - ":r1w0", - ":r2w1", - "ok", - "ok" - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r1w", ":r2w", ":s1w2", ":s2w3"], + [":r1w0", ":r2w1", "ok", "ok"], + sep="\r\n", ) as mhs: assert mhs.channel[0].function == mhs.Function.sine assert mhs.channel[1].function == mhs.Function.square @@ -196,42 +116,25 @@ def test_mhs_wave_type(): def test_mhs_serial_number(): with expected_protocol( - ik.minghe.MHS5200, - [ - ":r0c" - - ], - [ - ":r0c5225A1", - ], - sep="\r\n" + ik.minghe.MHS5200, + [":r0c"], + [ + ":r0c5225A1", + ], + sep="\r\n", ) as mhs: assert mhs.serial_number == "5225A1" def test_mhs_get_amplitude(): """Raise NotImplementedError when trying to get amplitude""" - with expected_protocol( - ik.minghe.MHS5200, - [ - ], - [ - ], - sep="\r\n" - ) as mhs: + with expected_protocol(ik.minghe.MHS5200, [], [], sep="\r\n") as mhs: with pytest.raises(NotImplementedError): mhs._get_amplitude_() def test_mhs_set_amplitude(): """Raise NotImplementedError when trying to set amplitude""" - with expected_protocol( - ik.minghe.MHS5200, - [ - ], - [ - ], - sep="\r\n" - ) as mhs: + with expected_protocol(ik.minghe.MHS5200, [], [], sep="\r\n") as mhs: with pytest.raises(NotImplementedError): mhs._set_amplitude_(1, 2) diff --git a/instruments/tests/test_named_struct.py b/instruments/tests/test_named_struct.py index b65943e7b..ffee3f32a 100644 --- a/instruments/tests/test_named_struct.py +++ b/instruments/tests/test_named_struct.py @@ -12,9 +12,7 @@ from hypothesis import given import hypothesis.strategies as st -from instruments.named_struct import ( - Field, StringField, Padding, NamedStruct -) +from instruments.named_struct import Field, StringField, Padding, NamedStruct # TESTS ###################################################################### @@ -22,13 +20,17 @@ # We disable pylint warnings that are not as applicable for unit tests. # pylint: disable=no-member,protected-access,blacklisted-name,missing-docstring,no-self-use + class TestNamedStruct(TestCase): - @given(st.integers(min_value=0, max_value=0x7FFF*2+1), st.integers(min_value=0, max_value=0xFF)) + @given( + st.integers(min_value=0, max_value=0x7FFF * 2 + 1), + st.integers(min_value=0, max_value=0xFF), + ) def test_roundtrip(self, var1, var2): class Foo(NamedStruct): - a = Field('H') + a = Field("H") padding = Padding(12) - b = Field('B') + b = Field("B") foo = Foo(a=var1, b=var2) assert Foo.unpack(foo.pack()) == foo @@ -37,32 +39,33 @@ def test_str(self): class Foo(NamedStruct): a = StringField(8, strip_null=False) b = StringField(9, strip_null=True) - c = StringField(2, encoding='utf-8') + c = StringField(2, encoding="utf-8") - foo = Foo(a="0123456\x00", b='abc', c=u'α') + foo = Foo(a="0123456\x00", b="abc", c=u"α") assert Foo.unpack(foo.pack()) == foo # Also check that we can get fields out directly. - self.assertEqual(foo.a, '0123456\x00') - self.assertEqual(foo.b, 'abc') - self.assertEqual(foo.c, u'α') + self.assertEqual(foo.a, "0123456\x00") + self.assertEqual(foo.b, "abc") + self.assertEqual(foo.c, u"α") def test_negative_len(self): """ Checks whether negative field lengths correctly raise. """ with self.assertRaises(TypeError): - class Foo(NamedStruct): # pylint: disable=unused-variable + + class Foo(NamedStruct): # pylint: disable=unused-variable a = StringField(-1) def test_equality(self): class Foo(NamedStruct): - a = Field('H') - b = Field('B') - c = StringField(5, encoding='utf8', strip_null=True) + a = Field("H") + b = Field("B") + c = StringField(5, encoding="utf8", strip_null=True) - foo1 = Foo(a=0x1234, b=0x56, c=u'ω') - foo2 = Foo(a=0xabcd, b=0xef, c=u'α') + foo1 = Foo(a=0x1234, b=0x56, c=u"ω") + foo2 = Foo(a=0xABCD, b=0xEF, c=u"α") assert foo1 == foo1 assert foo1 != foo2 diff --git a/instruments/tests/test_newport/test_agilis.py b/instruments/tests/test_newport/test_agilis.py index 2116c070d..6fdea6dc1 100644 --- a/instruments/tests/test_newport/test_agilis.py +++ b/instruments/tests/test_newport/test_agilis.py @@ -25,7 +25,7 @@ @pytest.fixture(autouse=True) def mock_time(mocker): """Mock `time.sleep` for and set to zero as autouse fixture.""" - return mocker.patch.object(time, 'sleep', return_value=None) + return mocker.patch.object(time, "sleep", return_value=None) # CONTROLLER TESTS # @@ -35,16 +35,7 @@ def test_aguc2_enable_remote_mode(): """ Check enabling of remote mode. """ - with expected_protocol( - ik.newport.AGUC2, - [ - "MR", - "ML" - ], - [ - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, ["MR", "ML"], [], sep="\r\n") as agl: agl.enable_remote_mode = True assert agl.enable_remote_mode is True agl.enable_remote_mode = False @@ -53,16 +44,7 @@ def test_aguc2_enable_remote_mode(): def test_aguc2_error_previous_command_no_error(): """Test return of an error value (`No Error`) from previous command.""" - with expected_protocol( - ik.newport.AGUC2, - [ - "TE" - ], - [ - "TE0" - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, ["TE"], ["TE0"], sep="\r\n") as agl: assert agl.error_previous_command == "No error" @@ -72,15 +54,7 @@ def test_aguc2_error_previous_command(): return "Error code must be given as an integer." will be returned because no actual error code is fed to the error message checker. """ - with expected_protocol( - ik.newport.AGUC2, - [ - "TE" - ], - [ - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, ["TE"], [], sep="\r\n") as agl: assert agl.error_previous_command == "Error code query failed." @@ -90,14 +64,7 @@ def test_aguc2_firmware_version(): AG-UC2 v2.2.1 """ with expected_protocol( - ik.newport.AGUC2, - [ - "VE" - ], - [ - "AG-UC2 v2.2.1" - ], - sep="\r\n" + ik.newport.AGUC2, ["VE"], ["AG-UC2 v2.2.1"], sep="\r\n" ) as agl: assert agl.firmware_version == "AG-UC2 v2.2.1" @@ -107,15 +74,7 @@ def test_aguc2_limit_status(): Check the limit status routine. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "PH" - ], - [ - "PH0" - ], - sep="\r\n" + ik.newport.AGUC2, ["MR", "PH"], ["PH0"], sep="\r\n" # initialize remote mode ) as agl: assert agl.limit_status == "PH0" @@ -124,14 +83,7 @@ def test_aguc2_sleep_time(): """ Check setting, getting the sleep time. """ - with expected_protocol( - ik.newport.AGUC2, - [ - ], - [ - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, [], [], sep="\r\n") as agl: agl.sleep_time = 3 assert agl.sleep_time == 3 with pytest.raises(ValueError): @@ -142,15 +94,7 @@ def test_aguc2_reset_controller(): """ Check reset controller function. """ - with expected_protocol( - ik.newport.AGUC2, - [ - "RS" - ], - [ - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, ["RS"], [], sep="\r\n") as agl: agl.reset_controller() assert agl.enable_remote_mode is False @@ -160,30 +104,17 @@ def test_aguc2_ag_sendcmd(): Check agilis sendcommand wrapper. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR" # some command, here remote mode - ], - [ - ], - sep="\r\n" + ik.newport.AGUC2, ["MR"], [], sep="\r\n" # some command, here remote mode ) as agl: agl.ag_sendcmd("MR") def test_aguc2_ag_query(): """ - Check agilis query wrapper. + Check agilis query wrapper. """ with expected_protocol( - ik.newport.AGUC2, - [ - "VE" - ], - [ - "AG-UC2 v2.2.1" - ], - sep="\r\n" + ik.newport.AGUC2, ["VE"], ["AG-UC2 v2.2.1"], sep="\r\n" ) as agl: assert agl.ag_query("VE") == "AG-UC2 v2.2.1" @@ -193,16 +124,9 @@ def test_aguc2_ag_query_io_error(mocker): # mock the query to raise an IOError io_error_mock = mocker.Mock() io_error_mock.side_effect = IOError - mocker.patch.object(ik.newport.AGUC2, 'query', io_error_mock) + mocker.patch.object(ik.newport.AGUC2, "query", io_error_mock) - with expected_protocol( - ik.newport.AGUC2, - [ - ], - [ - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, [], [], sep="\r\n") as agl: assert agl.ag_query("VE") == "Query timed out." @@ -212,14 +136,7 @@ def test_aguc2_ag_query_io_error(mocker): @pytest.mark.parametrize("axis", ik.newport.AGUC2.Axes) def test_aguc2_axis_init_enum(axis): """Initialize an axis externally with an enum.""" - with expected_protocol( - ik.newport.AGUC2, - [ - ], - [ - ], - sep="\r\n" - ) as agl: + with expected_protocol(ik.newport.AGUC2, [], [], sep="\r\n") as agl: ax = ik.newport.agilis._Axis(agl, axis) assert ax._ax == axis.value @@ -237,15 +154,13 @@ def test_aguc2_axis_init_wrong_type(): def test_aguc2_axis_am_i_still(axis, still): """Check if axis is still or not.""" with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - f"{axis.value} TS", - ], - [ - f"{axis.value}TS {int(not still)}" - ], - sep="\r\n" + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + f"{axis.value} TS", + ], + [f"{axis.value}TS {int(not still)}"], + sep="\r\n", ) as agl: assert agl.axis[axis].am_i_still() == still @@ -253,17 +168,10 @@ def test_aguc2_axis_am_i_still(axis, still): def test_aguc2_axis_am_i_still_io_error(): """Raise IOError if max retries achieved.""" with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 TS", - "2 TS", - "2 TS", - "2 TS" - ], - [ - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "1 TS", "2 TS", "2 TS", "2 TS"], # initialize remote mode + [], + sep="\r\n", ) as agl: with pytest.raises(IOError): agl.axis["X"].am_i_still(max_retries=1) @@ -275,15 +183,13 @@ def test_aguc2_axis_am_i_still_io_error(): def test_aguc2_axis_axis_status_not_moving(axis): """Check status of axis and return axis not moving.""" with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - f"{axis.value} TS", - ], - [ - f"{axis.value}TS0" - ], - sep="\r\n" + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + f"{axis.value} TS", + ], + [f"{axis.value}TS0"], + sep="\r\n", ) as agl: assert agl.axis[axis].axis_status == "Ready (not moving)." @@ -294,15 +200,10 @@ def test_aguc2_axis_axis_status(): "Status code query failed." since no instrument is connected. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 TS", - "2 TS" - ], - [ - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "1 TS", "2 TS"], # initialize remote mode + [], + sep="\r\n", ) as agl: assert agl.axis["X"].axis_status == "Status code query failed." assert agl.axis["Y"].axis_status == "Status code query failed." @@ -311,19 +212,10 @@ def test_aguc2_axis_axis_status(): def test_aguc2_axis_jog(): """Get / set jog function.""" with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 JA 3", - "1 JA?", - "2 JA -4", - "2 JA?" - ], - [ - "1JA3", - "2JA-4" - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "1 JA 3", "1 JA?", "2 JA -4", "2 JA?"], # initialize remote mode + ["1JA3", "2JA-4"], + sep="\r\n", ) as agl: agl.axis["X"].jog = 3 assert agl.axis["X"].jog == 3 @@ -340,15 +232,13 @@ def test_aguc2_axis_number_of_steps(): Check the number of steps function. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 TP", - ], - [ - "1TP0" - ], - sep="\r\n" + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 TP", + ], + ["1TP0"], + sep="\r\n", ) as agl: assert agl.axis["X"].number_of_steps == 0 @@ -358,19 +248,10 @@ def test_aguc2_axis_move_relative(): Check the move relative function. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 PR 1000", - "1 PR?", - "2 PR -340", - "2 PR?" - ], - [ - "1PR1000", - "2PR-340" - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "1 PR 1000", "1 PR?", "2 PR -340", "2 PR?"], # initialize remote mode + ["1PR1000", "2PR-340"], + sep="\r\n", ) as agl: agl.axis["X"].move_relative = 1000 assert agl.axis["X"].move_relative == 1000 @@ -388,16 +269,10 @@ def test_aguc2_axis_move_to_limit(): This function is UNTESTED to work, here simply command sending is checked """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "2 MA 3", - "2 MA?" - ], - [ - "2MA42" - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "2 MA 3", "2 MA?"], # initialize remote mode + ["2MA42"], + sep="\r\n", ) as agl: agl.axis["Y"].move_to_limit = 3 assert agl.axis["Y"].move_to_limit == 42 @@ -412,21 +287,18 @@ def test_aguc2_axis_step_amplitude(): Check for step amplitude function """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 SU-?", - "1 SU+?", - "1 SU -35", - "1 SU 47", - "1 SU -23", - "1 SU 13" - - ], - [ - "1SU-35", "1SU+35" - ], - sep="\r\n" + ik.newport.AGUC2, + [ + "MR", # initialize remote mode + "1 SU-?", + "1 SU+?", + "1 SU -35", + "1 SU 47", + "1 SU -23", + "1 SU 13", + ], + ["1SU-35", "1SU+35"], + sep="\r\n", ) as agl: assert agl.axis["X"].step_amplitude == (-35, 35) agl.axis["X"].step_amplitude = -35 @@ -445,17 +317,10 @@ def test_aguc2_axis_step_delay(): Check the step delay function. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "2 DL?", - "1 DL 1000", - "1 DL 200" - ], - [ - "2DL0" - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "2 DL?", "1 DL 1000", "1 DL 200"], # initialize remote mode + ["2DL0"], + sep="\r\n", ) as agl: assert agl.axis["Y"].step_delay == 0 agl.axis["X"].step_delay = 1000 @@ -471,15 +336,10 @@ def test_aguc2_axis_stop(): Check the stop function. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 ST", - "2 ST" - ], - [ - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "1 ST", "2 ST"], # initialize remote mode + [], + sep="\r\n", ) as agl: agl.axis["X"].stop() agl.axis["Y"].stop() @@ -490,15 +350,10 @@ def test_aguc2_axis_zero_position(): Check the stop function. """ with expected_protocol( - ik.newport.AGUC2, - [ - "MR", # initialize remote mode - "1 ZP", - "2 ZP" - ], - [ - ], - sep="\r\n" + ik.newport.AGUC2, + ["MR", "1 ZP", "2 ZP"], # initialize remote mode + [], + sep="\r\n", ) as agl: agl.axis["X"].zero_position() agl.axis["Y"].zero_position() @@ -510,35 +365,46 @@ def test_aguc2_axis_zero_position(): def test_agilis_error_message(): # regular error messages assert ik.newport.agilis.agilis_error_message(0) == "No error" - assert ik.newport.agilis.agilis_error_message(-6) == "Not allowed in " \ - "current state" + assert ( + ik.newport.agilis.agilis_error_message(-6) == "Not allowed in " "current state" + ) # out of range integers - assert ik.newport.agilis.agilis_error_message(1) == "An unknown error " \ - "occurred." - assert ik.newport.agilis.agilis_error_message(-7) == "An unknown error " \ - "occurred." + assert ik.newport.agilis.agilis_error_message(1) == "An unknown error " "occurred." + assert ik.newport.agilis.agilis_error_message(-7) == "An unknown error " "occurred." # non-integers - assert ik.newport.agilis.agilis_error_message(-7.5) == "Error code is " \ - "not an integer." - assert ik.newport.agilis.agilis_error_message("TE0") == "Error code is " \ - "not an integer." + assert ( + ik.newport.agilis.agilis_error_message(-7.5) == "Error code is " + "not an integer." + ) + assert ( + ik.newport.agilis.agilis_error_message("TE0") == "Error code is " + "not an integer." + ) def test_agilis_status_message(): # regular status messages assert ik.newport.agilis.agilis_status_message(0) == "Ready (not moving)." - assert ik.newport.agilis.agilis_status_message(3) == \ - "Moving to limit (currently executing " \ - "`measure_current_position`, `move_to_limit`, or " \ - "`move_absolute` command)." + assert ( + ik.newport.agilis.agilis_status_message(3) + == "Moving to limit (currently executing " + "`measure_current_position`, `move_to_limit`, or " + "`move_absolute` command)." + ) # out of range integers - assert ik.newport.agilis.agilis_status_message(4) == "An unknown " \ - "status occurred." - assert ik.newport.agilis.agilis_status_message(-1) == "An unknown " \ - "status occurred." + assert ( + ik.newport.agilis.agilis_status_message(4) == "An unknown " "status occurred." + ) + assert ( + ik.newport.agilis.agilis_status_message(-1) == "An unknown " "status occurred." + ) # non integers - assert ik.newport.agilis.agilis_status_message(3.14) == "Status code is " \ - "not an integer." - assert ik.newport.agilis.agilis_status_message("1TS0") == "Status code " \ - "is not an " \ - "integer." + assert ( + ik.newport.agilis.agilis_status_message(3.14) == "Status code is " + "not an integer." + ) + assert ( + ik.newport.agilis.agilis_status_message("1TS0") == "Status code " + "is not an " + "integer." + ) diff --git a/instruments/tests/test_newport/test_newport_pmc8742.py b/instruments/tests/test_newport/test_newport_pmc8742.py index f808190ef..701732804 100644 --- a/instruments/tests/test_newport/test_newport_pmc8742.py +++ b/instruments/tests/test_newport/test_newport_pmc8742.py @@ -22,12 +22,7 @@ def test_init(): """Initialize a new Picomotor PMC8742 instrument.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: assert inst.terminator == "\r\n" assert not inst.multiple_controllers @@ -36,15 +31,7 @@ def test_init(): def test_controller_address(): """Set and get controller address.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "SA2", - "SA?" - ], - [ - "2" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["SA2", "SA?"], ["2"], sep="\r\n" ) as inst: inst.controller_address = 2 assert inst.controller_address == 2 @@ -53,17 +40,10 @@ def test_controller_address(): def test_controller_configuration(): """Set and get controller configuration.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "ZZ11", - "ZZ11", - "ZZ11", - "ZZ?" - ], - [ - "11" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + ["ZZ11", "ZZ11", "ZZ11", "ZZ?"], + ["11"], + sep="\r\n", ) as inst: inst.controller_configuration = 3 inst.controller_configuration = 0b11 @@ -74,16 +54,10 @@ def test_controller_configuration(): def test_dhcp_mode(): """Set and get DHCP mode.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "IPMODE0", - "IPMODE1", - "IPMODE?" - ], - [ - "1" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + ["IPMODE0", "IPMODE1", "IPMODE?"], + ["1"], + sep="\r\n", ) as inst: inst.dhcp_mode = False inst.dhcp_mode = True @@ -93,14 +67,7 @@ def test_dhcp_mode(): def test_error_code(): """Get error code.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "TE?" - ], - [ - "0" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["TE?"], ["0"], sep="\r\n" ) as inst: assert inst.error_code == 0 @@ -108,14 +75,10 @@ def test_error_code(): def test_error_code_and_message(): """Get error code and message as tuple.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "TB?" - ], - [ - "0, NO ERROR DETECTED" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + ["TB?"], + ["0, NO ERROR DETECTED"], + sep="\r\n", ) as inst: err_expected = (0, "NO ERROR DETECTED") err_received = inst.error_code_and_message @@ -126,14 +89,7 @@ def test_error_code_and_message(): def test_firmware_version(): """Get firmware version.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "VE?" - ], - [ - "0123456789" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["VE?"], ["0123456789"], sep="\r\n" ) as inst: assert inst.firmware_version == "0123456789" @@ -142,15 +98,10 @@ def test_gateway(): """Set / get gateway.""" ip_addr = "192.168.1.1" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"GATEWAY {ip_addr}", - "GATEWAY?" - ], - [ - f"{ip_addr}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"GATEWAY {ip_addr}", "GATEWAY?"], + [f"{ip_addr}"], + sep="\r\n", ) as inst: inst.gateway = ip_addr assert inst.gateway == ip_addr @@ -160,15 +111,10 @@ def test_hostname(): """Set / get hostname.""" host = "192.168.1.1" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"HOSTNAME {host}", - "HOSTNAME?" - ], - [ - f"{host}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"HOSTNAME {host}", "HOSTNAME?"], + [f"{host}"], + sep="\r\n", ) as inst: inst.hostname = host assert inst.hostname == host @@ -178,15 +124,10 @@ def test_ip_address(): """Set / get ip address.""" ip_addr = "192.168.1.1" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"IPADDR {ip_addr}", - "IPADDR?" - ], - [ - f"{ip_addr}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"IPADDR {ip_addr}", "IPADDR?"], + [f"{ip_addr}"], + sep="\r\n", ) as inst: inst.ip_address = ip_addr assert inst.ip_address == ip_addr @@ -196,14 +137,7 @@ def test_mac_address(): """Set / get mac address.""" mac_addr = "5827809, 8087" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "MACADDR?" - ], - [ - f"{mac_addr}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["MACADDR?"], [f"{mac_addr}"], sep="\r\n" ) as inst: assert inst.mac_address == mac_addr @@ -211,14 +145,7 @@ def test_mac_address(): def test_name(): """Get name of the current instrument.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "*IDN?" - ], - [ - "NAME" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["*IDN?"], ["NAME"], sep="\r\n" ) as inst: assert inst.name == "NAME" @@ -227,15 +154,10 @@ def test_netmask(): """Set / get netmask.""" ip_addr = "192.168.1.1" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"NETMASK {ip_addr}", - "NETMASK?" - ], - [ - f"{ip_addr}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"NETMASK {ip_addr}", "NETMASK?"], + [f"{ip_addr}"], + sep="\r\n", ) as inst: inst.netmask = ip_addr assert inst.netmask == ip_addr @@ -244,14 +166,7 @@ def test_netmask(): def test_scan_controller(): """Scan connected controllers.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "SC?" - ], - [ - "11" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["SC?"], ["11"], sep="\r\n" ) as inst: assert inst.scan_controllers == "11" @@ -259,16 +174,7 @@ def test_scan_controller(): def test_scan_done(): """Query if a controller scan is completed.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "SD?", - "SD?" - ], - [ - "1", - "0" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["SD?", "SD?"], ["1", "0"], sep="\r\n" ) as inst: assert inst.scan_done assert not inst.scan_done @@ -277,13 +183,7 @@ def test_scan_done(): def test_abort_motion(): """Abort all motion.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "AB" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["AB"], [], sep="\r\n" ) as inst: inst.abort_motion() @@ -291,13 +191,7 @@ def test_abort_motion(): def test_motor_check(): """Check the connected motors.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "MC" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["MC"], [], sep="\r\n" ) as inst: inst.motor_check() @@ -306,14 +200,7 @@ def test_motor_check(): def test_scan(mode): """Scan address configuration of motors for default and other modes.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "SC2", - f"SC{mode}" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["SC2", f"SC{mode}"], [], sep="\r\n" ) as inst: inst.scan() inst.scan(mode) @@ -322,13 +209,7 @@ def test_scan(mode): def test_purge(): """Purge the memory.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "XX" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["XX"], [], sep="\r\n" ) as inst: inst.purge() @@ -337,14 +218,7 @@ def test_purge(): def test_recall_parameters(mode): """Recall parameters, by default the factory set values.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "*RCL0", - f"*RCL{mode}" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["*RCL0", f"*RCL{mode}"], [], sep="\r\n" ) as inst: inst.recall_parameters() inst.recall_parameters(mode) @@ -353,13 +227,7 @@ def test_recall_parameters(mode): def test_reset(): """Soft reset of the controller.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "*RST" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["*RST"], [], sep="\r\n" ) as inst: inst.reset() @@ -367,13 +235,7 @@ def test_reset(): def test_save_settings(): """Save settings of the controller.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "SM" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["SM"], [], sep="\r\n" ) as inst: inst.save_settings() @@ -383,14 +245,7 @@ def test_query_bad_header(): retval = b"\xff\xfd\x03\xff\xfb\x01192.168.2.161" val_expected = "192.168.2.161" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "IPADDR?" - ], - [ - retval - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["IPADDR?"], [retval], sep="\r\n" ) as inst: assert inst.ip_address == val_expected @@ -402,12 +257,7 @@ def test_query_bad_header(): def test_axis_returns(ax): """Return axis with given axis number testing all valid axes.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: axis = inst.axis[ax] assert isinstance(axis, ik.newport.PicoMotorController8742.Axis) @@ -426,12 +276,7 @@ def test_axis_returns_type_error(): def test_axis_return_index_error(ax): """Raise IndexError if axis out of bounds and in one controller mode.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: with pytest.raises(IndexError): _ = inst.axis[ax] @@ -440,19 +285,13 @@ def test_axis_return_index_error(ax): @given(val=st.integers(min_value=1, max_value=200000)) def test_axis_acceleration(val): """Set / get axis acceleration unitful and without units.""" - val_unit = u.Quantity(val, u.s**-2) - val_unit_other = val_unit.to(u.min**-2) - with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1AC{val}", - f"1AC{val}", - "1AC?" - ], - [ - f"{val}" - ], - sep="\r\n" + val_unit = u.Quantity(val, u.s ** -2) + val_unit_other = val_unit.to(u.min ** -2) + with expected_protocol( + ik.newport.PicoMotorController8742, + [f"1AC{val}", f"1AC{val}", "1AC?"], + [f"{val}"], + sep="\r\n", ) as inst: axis = inst.axis[0] axis.acceleration = val @@ -464,12 +303,7 @@ def test_axis_acceleration(val): def test_axis_acceleration_value_error(val): """Raise ValueError if acceleration out of range.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError): @@ -480,15 +314,10 @@ def test_axis_acceleration_value_error(val): def test_axis_home_position(val): """Set / get axis home position.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1DH{val}", - "1DH?" - ], - [ - f"{val}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"1DH{val}", "1DH?"], + [f"{val}"], + sep="\r\n", ) as inst: axis = inst.axis[0] axis.home_position = val @@ -499,12 +328,7 @@ def test_axis_home_position(val): def test_axis_home_position_value_error(val): """Raise ValueError if home position out of range.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError): @@ -516,34 +340,20 @@ def test_axis_is_stopped(val): """Query if axis is stopped.""" exp_result = True if val == "1" else False with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "1MD?" - ], - [ - f"{val}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["1MD?"], [f"{val}"], sep="\r\n" ) as inst: axis = inst.axis[0] assert axis.is_stopped == exp_result -@pytest.mark.parametrize( - "val", ik.newport.PicoMotorController8742.Axis.MotorType -) +@pytest.mark.parametrize("val", ik.newport.PicoMotorController8742.Axis.MotorType) def test_axis_motor_type(val): """Set / get motor type.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1QM{val.value}", - "1QM?" - ], - [ - f"{val.value}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"1QM{val.value}", "1QM?"], + [f"{val.value}"], + sep="\r\n", ) as inst: axis = inst.axis[0] axis.motor_type = val @@ -553,12 +363,7 @@ def test_axis_motor_type(val): def test_axis_motor_type_wrong_type(): """Raise TypeError if not appropriate motor type.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(TypeError): @@ -569,15 +374,10 @@ def test_axis_motor_type_wrong_type(): def test_axis_move_absolute(val): """Set / get axis move absolute.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1PA{val}", - "1PA?" - ], - [ - f"{val}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"1PA{val}", "1PA?"], + [f"{val}"], + sep="\r\n", ) as inst: axis = inst.axis[0] axis.move_absolute = val @@ -588,12 +388,7 @@ def test_axis_move_absolute(val): def test_axis_move_absolute_value_error(val): """Raise ValueError if move absolute out of range.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError): @@ -604,15 +399,10 @@ def test_axis_move_absolute_value_error(val): def test_axis_move_relative(val): """Set / get axis move relative.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1PR{val}", - "1PR?" - ], - [ - f"{val}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"1PR{val}", "1PR?"], + [f"{val}"], + sep="\r\n", ) as inst: axis = inst.axis[0] axis.move_relative = val @@ -623,12 +413,7 @@ def test_axis_move_relative(val): def test_axis_move_relative_value_error(val): """Raise ValueError if move relative out of range.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError): @@ -638,14 +423,7 @@ def test_axis_move_relative_value_error(val): def test_axis_position(): """Query position of an axis.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "1TP?" - ], - [ - "42" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["1TP?"], ["42"], sep="\r\n" ) as inst: axis = inst.axis[0] assert axis.position == 42 @@ -657,20 +435,10 @@ def test_axis_velocity(val): val_unit = u.Quantity(val, 1 / u.s) val_unit_other = val_unit.to(1 / u.hour) with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1QM?", - f"1VA{val}", - f"1QM?", - f"1VA{val}", - "1VA?" - ], - [ - "3", - "3", - f"{val}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"1QM?", f"1VA{val}", f"1QM?", f"1VA{val}", "1VA?"], + ["3", "3", f"{val}"], + sep="\r\n", ) as inst: axis = inst.axis[0] axis.velocity = val @@ -683,14 +451,7 @@ def test_axis_velocity(val): def test_axis_velocity_value_error_regular(val, motor): """Raise ValueError if velocity is out of range for non-tiny motor.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "1QM?" - ], - [ - f"{motor}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["1QM?"], [f"{motor}"], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError): @@ -701,14 +462,7 @@ def test_axis_velocity_value_error_regular(val, motor): def test_axis_velocity_value_error_tiny(val): """Raise ValueError if velocity is out of range for tiny motor.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - "1QM?" - ], - [ - "2" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, ["1QM?"], ["2"], sep="\r\n" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError): @@ -719,13 +473,7 @@ def test_axis_velocity_value_error_tiny(val): def test_axis_move_indefinite(direction): """Move axis indefinitely.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1MV{direction}" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [f"1MV{direction}"], [], sep="\r\n" ) as inst: axis = inst.axis[0] axis.move_indefinite(direction) @@ -734,13 +482,7 @@ def test_axis_move_indefinite(direction): def test_axis_stop(): """Stop axis.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1ST" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [f"1ST"], [], sep="\r\n" ) as inst: axis = inst.axis[0] axis.stop() @@ -752,12 +494,7 @@ def test_axis_stop(): def test_multi_controllers(): """Enable and disable multiple controllers.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: inst.multiple_controllers = True assert inst.multiple_controllers @@ -765,16 +502,11 @@ def test_multi_controllers(): assert not inst.multiple_controllers -@given(ax=st.integers(min_value=0, max_value=31*4-1)) +@given(ax=st.integers(min_value=0, max_value=31 * 4 - 1)) def test_axis_return_multi(ax): """Return axis properly for multi-controller setup.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: inst.multiple_controllers = True axis = inst.axis[ax] @@ -788,52 +520,37 @@ def test_axis_return_multi(ax): def test_axis_return_multi_index_error(ax): """Raise IndexError if axis out of bounds and in multi controller mode.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [], [], sep="\r\n" ) as inst: inst.multiple_controllers = True with pytest.raises(IndexError): _ = inst.axis[ax] -@given(ax=st.integers(min_value=0, max_value=31*4-1)) +@given(ax=st.integers(min_value=0, max_value=31 * 4 - 1)) def test_axis_sendcmd_multi(ax): """Send correct command in multiple axis mode.""" address = ax // 4 + 1 axis = ax % 4 + 1 with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"{address}>{axis}CMD" - ], - [ - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [f"{address}>{axis}CMD"], [], sep="\r\n" ) as inst: inst.multiple_controllers = True axis = inst.axis[ax] axis.sendcmd("CMD") -@given(ax=st.integers(min_value=0, max_value=31*4-1)) +@given(ax=st.integers(min_value=0, max_value=31 * 4 - 1)) def test_axis_query_multi(ax): """Query command in multiple axis mode and strip address routing.""" address = ax // 4 + 1 axis = ax % 4 + 1 answer_expected = f"{axis}ANSWER" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"{address}>{axis}CMD" - ], - [ - f"{address}>{answer_expected}" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, + [f"{address}>{axis}CMD"], + [f"{address}>{answer_expected}"], + sep="\r\n", ) as inst: inst.multiple_controllers = True axis = inst.axis[ax] @@ -843,14 +560,7 @@ def test_axis_query_multi(ax): def test_axis_query_multi_io_error(): """Raise IOError if query response from wrong controller.""" with expected_protocol( - ik.newport.PicoMotorController8742, - [ - f"1>1CMD" - ], - [ - f"4>1ANSWER" - ], - sep="\r\n" + ik.newport.PicoMotorController8742, [f"1>1CMD"], [f"4>1ANSWER"], sep="\r\n" ) as inst: inst.multiple_controllers = True axis = inst.axis[0] diff --git a/instruments/tests/test_newport/test_newportesp301.py b/instruments/tests/test_newport/test_newportesp301.py index 3343cc46f..642724d7d 100644 --- a/instruments/tests/test_newport/test_newportesp301.py +++ b/instruments/tests/test_newport/test_newportesp301.py @@ -26,14 +26,7 @@ def test_init(): """Initialize a Newport ESP301 instrument.""" - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: assert inst._execute_immediately assert inst._command_list == [] assert inst._bulk_query_resp == "" @@ -44,16 +37,10 @@ def test_init(): def test_axis_returns_axis_class(ax): """Return axis class with given axis number.""" with expected_protocol( - ik.newport.NewportESP301, - [ - f"{ax+1}SN?", - "TB?" # error check query - ], - [ - "1", - "0,0,0" - ], - sep="\r" + ik.newport.NewportESP301, + [f"{ax+1}SN?", "TB?"], # error check query + ["1", "0,0,0"], + sep="\r", ) as inst: axis = inst.axis[ax] assert isinstance(axis, ik.newport.NewportESP301Axis) @@ -69,18 +56,9 @@ def test_newport_cmd(mocker): params = (1, 2, 3) # stitch together raw command to send raw_cmd = f"{target}{cmd}{','.join(map(str, params))}" - with expected_protocol( - ik.newport.NewportESP301, - [ - raw_cmd - ], - [ - ], - sep="\r" - ) as inst: - execute_spy = mocker.spy(inst, '_execute_cmd') - resp = inst._newport_cmd(cmd, params=params, target=target, - errcheck=False) + with expected_protocol(ik.newport.NewportESP301, [raw_cmd], [], sep="\r") as inst: + execute_spy = mocker.spy(inst, "_execute_cmd") + resp = inst._newport_cmd(cmd, params=params, target=target, errcheck=False) assert resp is None execute_spy.assert_called_with(raw_cmd, False) @@ -95,14 +73,7 @@ def test_newport_cmd_add_to_list(): params = (1, 2, 3) # stitch together raw command to send raw_cmd = f"{target}{cmd}{','.join(map(str, params))}" - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: inst._execute_immediately = False resp = inst._newport_cmd(cmd, params=params, target=target) assert resp is None @@ -118,21 +89,13 @@ def test_newport_cmd_with_axis(): raw_cmd = f"{ax+1}{cmd}{','.join(map(str, params))}" with expected_protocol( - ik.newport.NewportESP301, - [ - f"{ax+1}SN?", - "TB?", # error check query - raw_cmd - ], - [ - "1", - "0,0,0" - ], - sep="\r" + ik.newport.NewportESP301, + [f"{ax+1}SN?", "TB?", raw_cmd], # error check query + ["1", "0,0,0"], + sep="\r", ) as inst: axis = inst.axis[ax] - resp = inst._newport_cmd(cmd, params=params, target=axis, - errcheck=False) + resp = inst._newport_cmd(cmd, params=params, target=axis, errcheck=False) assert resp is None @@ -142,16 +105,10 @@ def test_execute_cmd_query(): response = "RESPONSE" with expected_protocol( - ik.newport.NewportESP301, - [ - query, - "TB?" - ], - [ - response, - "0,0,0" # no error - ], - sep="\r" + ik.newport.NewportESP301, + [query, "TB?"], + [response, "0,0,0"], # no error + sep="\r", ) as inst: assert inst._execute_cmd(query) == response @@ -164,15 +121,7 @@ def test_execute_cmd_query_error(): """ cmd = "COMMAND" with expected_protocol( - ik.newport.NewportESP301, - [ - cmd, - "TB?" - ], - [ - "13,0,0" # no error - ], - sep="\r" + ik.newport.NewportESP301, [cmd, "TB?"], ["13,0,0"], sep="\r" # no error ) as inst: with pytest.raises(ik.newport.errors.NewportError) as err_info: inst._execute_cmd(cmd) @@ -189,18 +138,12 @@ def test_home(mocker): axis = "ax" params = 1, 2, 3 errcheck = False - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: - mock_cmd = mocker.patch.object(inst, '_newport_cmd') + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: + mock_cmd = mocker.patch.object(inst, "_newport_cmd") inst._home(axis, params, errcheck) - mock_cmd.assert_called_with("OR", target=axis, params=[params], - errcheck=errcheck) + mock_cmd.assert_called_with( + "OR", target=axis, params=[params], errcheck=errcheck + ) @pytest.mark.parametrize("search_mode", ik.newport.NewportESP301HomeSearchMode) @@ -212,18 +155,12 @@ def test_search_for_home(mocker, search_mode): """ axis = 3 errcheck = True - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: - mock_cmd = mocker.patch.object(inst, '_home') + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: + mock_cmd = mocker.patch.object(inst, "_home") inst.search_for_home(axis, search_mode, errcheck) - mock_cmd.assert_called_with(axis=axis, search_mode=search_mode, - errcheck=errcheck) + mock_cmd.assert_called_with( + axis=axis, search_mode=search_mode, errcheck=errcheck + ) def test_reset(mocker): @@ -232,15 +169,8 @@ def test_reset(mocker): Mock `_newport_cmd`, this routine is already tested. Just assert that it is called with correct arguments. """ - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: - mock_cmd = mocker.patch.object(inst, '_newport_cmd') + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: + mock_cmd = mocker.patch.object(inst, "_newport_cmd") inst.reset() mock_cmd.assert_called_with("RS", errcheck=False) @@ -252,21 +182,14 @@ def test_define_program(mocker, prg_id): Mock out the `_newport_cmd` routine. Already tested and not required. """ - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: - mock_cmd = mocker.patch.object(inst, '_newport_cmd') + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: + mock_cmd = mocker.patch.object(inst, "_newport_cmd") with inst.define_program(prg_id): pass calls = ( mocker.call("XX", target=prg_id), mocker.call("EP", target=prg_id), - mocker.call("QP") + mocker.call("QP"), ) mock_cmd.has_calls(calls) @@ -274,20 +197,15 @@ def test_define_program(mocker, prg_id): @given(prg_id=st.integers().filter(lambda x: x < 1 or x > 100)) def test_define_program_value_error(prg_id): """Raise ValueError when defining program with invalid ID.""" - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: with pytest.raises(ValueError) as err_info: with inst.define_program(prg_id): pass err_msg = err_info.value.args[0] - assert err_msg == "Invalid program ID. Must be an integer from 1 to " \ - "100 (inclusive)." + assert ( + err_msg == "Invalid program ID. Must be an integer from 1 to " + "100 (inclusive)." + ) @pytest.mark.parametrize("errcheck", (True, False)) @@ -301,29 +219,25 @@ def test_execute_bulk_command(mocker, errcheck): 11. """ ax = 0 - move_commands_sent = '1PA1.0 ; 1PA10.0 ; ; 1PA11.0 ; ' + move_commands_sent = "1PA1.0 ; 1PA10.0 ; ; 1PA11.0 ; " resp = "Response" with expected_protocol( - ik.newport.NewportESP301, - [ - f"{ax+1}SN?", - "TB?", # error check query - ], - [ - "1", - "0,0,0" - ], - sep="\r" + ik.newport.NewportESP301, + [ + f"{ax+1}SN?", + "TB?", # error check query + ], + ["1", "0,0,0"], + sep="\r", ) as inst: axis = inst.axis[ax] - mock_exec = mocker.patch.object(inst, '_execute_cmd', - return_value=resp) + mock_exec = mocker.patch.object(inst, "_execute_cmd", return_value=resp) with inst.execute_bulk_command(errcheck=errcheck): assert not inst._execute_immediately # some move commands - axis.move(1.) - axis.move(10.) - axis.move(11.) + axis.move(1.0) + axis.move(10.0) + axis.move(11.0) mock_exec.assert_called_with(move_commands_sent, errcheck) assert inst._bulk_query_resp == resp assert inst._command_list == [] @@ -337,15 +251,8 @@ def test_run_program(mocker, prg_id): Mock out the `_newport_cmd` routine. Already tested and not required. """ - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: - mock_cmd = mocker.patch.object(inst, '_newport_cmd') + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: + mock_cmd = mocker.patch.object(inst, "_newport_cmd") inst.run_program(prg_id) mock_cmd.assert_called_with("EX", target=prg_id) @@ -353,19 +260,14 @@ def test_run_program(mocker, prg_id): @given(prg_id=st.integers().filter(lambda x: x < 1 or x > 100)) def test_run_program_value_error(prg_id): """Raise ValueError when defining program with invalid ID.""" - with expected_protocol( - ik.newport.NewportESP301, - [ - ], - [ - ], - sep="\r" - ) as inst: + with expected_protocol(ik.newport.NewportESP301, [], [], sep="\r") as inst: with pytest.raises(ValueError) as err_info: inst.run_program(prg_id) err_msg = err_info.value.args[0] - assert err_msg == "Invalid program ID. Must be an integer from 1 to " \ - "100 (inclusive)." + assert ( + err_msg == "Invalid program ID. Must be an integer from 1 to " + "100 (inclusive)." + ) # AXIS # @@ -378,14 +280,7 @@ def test_run_program_value_error(prg_id): def test_axis_init(): """Initialize a new axis.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] assert axis._controller == inst @@ -398,8 +293,9 @@ def test_axis_init_type_error(): with pytest.raises(TypeError) as err_info: _ = ik.newport.newportesp301.NewportESP301Axis(42, 0) err_msg = err_info.value.args[0] - assert err_msg == "Axis must be controlled by a Newport ESP-301 motor " \ - "controller." + assert ( + err_msg == "Axis must be controlled by a Newport ESP-301 motor " "controller." + ) def test_axis_units_of(mocker): @@ -412,18 +308,11 @@ def test_axis_units_of(mocker): get_unit = ik.newport.newportesp301.NewportESP301Units.millimeter set_unit = ik.newport.newportesp301.NewportESP301Units.inches with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_get = mocker.patch.object(axis, '_get_units', return_value=get_unit) - mock_set = mocker.patch.object(axis, '_set_units', return_value=None) + mock_get = mocker.patch.object(axis, "_get_units", return_value=get_unit) + mock_set = mocker.patch.object(axis, "_set_units", return_value=None) with axis._units_of(set_unit): mock_get.assert_called() mock_set.assert_called_with(set_unit) @@ -438,17 +327,10 @@ def test_axis_get_units(mocker): resp = "2" unit = ik.newport.newportesp301.NewportESP301Units(int(resp)) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value=resp) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=resp) assert unit == axis._get_units() mock_cmd.assert_called_with("SN?", target=1) @@ -461,17 +343,10 @@ def test_axis_set_units(mocker): """ unit = ik.newport.newportesp301.NewportESP301Units.radian # just pick one with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value=None) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=None) assert axis._set_units(unit) is None mock_cmd.assert_called_with("SN", target=1, params=[int(unit)]) @@ -479,14 +354,7 @@ def test_axis_set_units(mocker): def test_axis_id(): """Get axis ID.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] assert axis.axis_id == 1 @@ -499,17 +367,10 @@ def test_axis_is_motion_done(mocker, resp): Mock out the command sending, as above. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value=resp) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=resp) assert axis.is_motion_done is bool(int(resp)) mock_cmd.assert_called_with("MD?", target=1) @@ -521,35 +382,20 @@ def test_axis_acceleration(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.acceleration = value mock_cmd.assert_called_with("AC", target=1, params=[float(value)]) - assert axis.acceleration == u.Quantity(value, axis._units / u.s**2) + assert axis.acceleration == u.Quantity(value, axis._units / u.s ** 2) mock_cmd.assert_called_with("AC?", target=1) def test_axis_acceleration_none(): """Set axis acceleration with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.acceleration = None @@ -562,35 +408,20 @@ def test_axis_deceleration(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.deceleration = value mock_cmd.assert_called_with("AG", target=1, params=[float(value)]) - assert axis.deceleration == u.Quantity(value, axis._units / u.s**2) + assert axis.deceleration == u.Quantity(value, axis._units / u.s ** 2) mock_cmd.assert_called_with("AG?", target=1) def test_axis_deceleration_none(): """Set axis deceleration with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.deceleration = None @@ -603,22 +434,13 @@ def test_axis_estop_deceleration(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.estop_deceleration = value mock_cmd.assert_called_with("AE", target=1, params=[float(value)]) - assert axis.estop_deceleration == u.Quantity(value, - axis._units / u.s**2) + assert axis.estop_deceleration == u.Quantity(value, axis._units / u.s ** 2) mock_cmd.assert_called_with("AE?", target=1) @@ -629,21 +451,13 @@ def test_axis_jerk(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.jerk = value mock_cmd.assert_called_with("JK", target=1, params=[float(value)]) - assert axis.jerk == u.Quantity(value, axis._units / u.s**3) + assert axis.jerk == u.Quantity(value, axis._units / u.s ** 3) mock_cmd.assert_called_with("JK?", target=1) @@ -654,23 +468,16 @@ def test_axis_velocity(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.velocity = value mock_cmd.assert_called_with("VA", target=1, params=[float(value)]) assert axis.velocity == u.Quantity(value, axis._units / u.s) mock_cmd.assert_called_with("VA?", target=1) + def test_axis_max_velocity(mocker): """Set / get axis maximum velocity. @@ -678,18 +485,10 @@ def test_axis_max_velocity(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.max_velocity = value mock_cmd.assert_called_with("VU", target=1, params=[float(value)]) assert axis.max_velocity == u.Quantity(value, axis._units / u.s) @@ -699,14 +498,7 @@ def test_axis_max_velocity(mocker): def test_axis_max_velocity_none(): """Set axis maximum velocity with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.max_velocity = None @@ -719,18 +511,10 @@ def test_axis_max_base_velocity(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.max_base_velocity = value mock_cmd.assert_called_with("VB", target=1, params=[float(value)]) assert axis.max_base_velocity == u.Quantity(value, axis._units / u.s) @@ -740,14 +524,7 @@ def test_axis_max_base_velocity(mocker): def test_axis_max_base_velocity_none(): """Set axis maximum base velocity with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.max_base_velocity = None @@ -760,18 +537,10 @@ def test_axis_jog_high_velocity(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.jog_high_velocity = value mock_cmd.assert_called_with("JH", target=1, params=[float(value)]) assert axis.jog_high_velocity == u.Quantity(value, axis._units / u.s) @@ -781,14 +550,7 @@ def test_axis_jog_high_velocity(mocker): def test_axis_jog_high_velocity_none(): """Set axis jog high velocity with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.jog_high_velocity = None @@ -801,18 +563,10 @@ def test_axis_jog_low_velocity(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.jog_low_velocity = value mock_cmd.assert_called_with("JW", target=1, params=[float(value)]) assert axis.jog_low_velocity == u.Quantity(value, axis._units / u.s) @@ -822,14 +576,7 @@ def test_axis_jog_low_velocity(mocker): def test_axis_jog_low_velocity_none(): """Set axis jog low velocity with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.jog_low_velocity = None @@ -842,18 +589,10 @@ def test_axis_homing_velocity(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.homing_velocity = value mock_cmd.assert_called_with("OH", target=1, params=[float(value)]) assert axis.homing_velocity == u.Quantity(value, axis._units / u.s) @@ -863,14 +602,7 @@ def test_axis_homing_velocity(mocker): def test_axis_homing_velocity_none(): """Set axis homing velocity with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.homing_velocity = None @@ -883,35 +615,20 @@ def test_axis_max_acceleration(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.max_acceleration = value mock_cmd.assert_called_with("AU", target=1, params=[float(value)]) - assert axis.max_acceleration == u.Quantity(value, axis._units / u.s**2) + assert axis.max_acceleration == u.Quantity(value, axis._units / u.s ** 2) mock_cmd.assert_called_with("AU?", target=1) def test_axis_max_acceleration_none(): """Set axis maximum acceleration with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.max_acceleration = None @@ -924,21 +641,13 @@ def test_axis_max_deceleration(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.max_deceleration = value mock_cmd.assert_called_with("AU", target=1, params=[float(value)]) - assert axis.max_deceleration == u.Quantity(value, axis._units / u.s**2) + assert axis.max_deceleration == u.Quantity(value, axis._units / u.s ** 2) mock_cmd.assert_called_with("AU?", target=1) @@ -949,18 +658,10 @@ def test_axis_position(mocker): """ retval = "42" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=retval) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=retval) assert axis.position == u.Quantity(float(retval), axis._units) mock_cmd.assert_called_with("TP?", target=1) @@ -972,18 +673,10 @@ def test_axis_desired_position(mocker): """ retval = "42" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=retval) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=retval) assert axis.desired_position == u.Quantity(float(retval), axis._units) mock_cmd.assert_called_with("DP?", target=1) @@ -995,20 +688,11 @@ def test_axis_desired_velocity(mocker): """ retval = "42" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=retval) - assert axis.desired_velocity == u.Quantity(float(retval), - axis._units / u.s) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=retval) + assert axis.desired_velocity == u.Quantity(float(retval), axis._units / u.s) mock_cmd.assert_called_with("DV?", target=1) @@ -1019,18 +703,10 @@ def test_axis_home(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.home = value mock_cmd.assert_called_with("DH", target=1, params=[float(value)]) assert axis.home == u.Quantity(value, axis._units) @@ -1040,14 +716,7 @@ def test_axis_home(mocker): def test_axis_home_none(): """Set axis home with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.home = None @@ -1059,17 +728,10 @@ def test_axis_units(mocker): Mock out `_newport_cmd` since tested elsewhere. Returns u.counts """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', return_value="0") + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value="0") assert axis.units == u.counts mock_cmd.reset_mock() # set units with None @@ -1092,18 +754,10 @@ def test_axis_encoder_resolution(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.encoder_resolution = value mock_cmd.assert_called_with("SU", target=1, params=[float(value)]) assert axis.encoder_resolution == u.Quantity(value, axis._units) @@ -1113,14 +767,7 @@ def test_axis_encoder_resolution(mocker): def test_axis_encoder_resolution_none(): """Set axis encoder resolution with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.encoder_resolution = None @@ -1133,18 +780,10 @@ def test_axis_full_step_resolution(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.full_step_resolution = value mock_cmd.assert_called_with("FR", target=1, params=[float(value)]) assert axis.full_step_resolution == u.Quantity(value, axis._units) @@ -1154,14 +793,7 @@ def test_axis_full_step_resolution(mocker): def test_axis_full_step_resolution_none(): """Set axis full step resolution with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.full_step_resolution = None @@ -1174,18 +806,10 @@ def test_axis_left_limit(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.left_limit = value mock_cmd.assert_called_with("SL", target=1, params=[float(value)]) assert axis.left_limit == u.Quantity(value, axis._units) @@ -1199,18 +823,10 @@ def test_axis_right_limit(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.right_limit = value mock_cmd.assert_called_with("SR", target=1, params=[float(value)]) assert axis.right_limit == u.Quantity(value, axis._units) @@ -1224,18 +840,10 @@ def test_axis_error_threshold(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.error_threshold = value mock_cmd.assert_called_with("FE", target=1, params=[float(value)]) assert axis.error_threshold == u.Quantity(value, axis._units) @@ -1245,14 +853,7 @@ def test_axis_error_threshold(mocker): def test_axis_error_threshold_none(): """Set axis error threshold with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.error_threshold = None @@ -1265,18 +866,10 @@ def test_axis_current(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.current = value mock_cmd.assert_called_with("QI", target=1, params=[float(value)]) assert axis.current == u.Quantity(value, u.A) @@ -1286,14 +879,7 @@ def test_axis_current(mocker): def test_axis_current_none(): """Set axis current with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.current = None @@ -1306,18 +892,10 @@ def test_axis_voltage(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.voltage = value mock_cmd.assert_called_with("QV", target=1, params=[float(value)]) assert axis.voltage == u.Quantity(value, u.V) @@ -1327,14 +905,7 @@ def test_axis_voltage(mocker): def test_axis_voltage_none(): """Set axis voltage with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.voltage = None @@ -1347,18 +918,10 @@ def test_axis_motor_type(mocker): """ value = 1 # DC Servo with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.motor_type = value mock_cmd.assert_called_with("QM", target=1, params=[float(value)]) assert axis.motor_type == value @@ -1368,14 +931,7 @@ def test_axis_motor_type(mocker): def test_axis_motor_type_none(): """Set axis motor type with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.motor_type = None @@ -1386,21 +942,13 @@ def test_axis_feedback_configuration(mocker): Mock out `_newport_cmd` since tested elsewhere. """ - value_ret = 'A13\r\n' # 2 additional characters that will be cancelled + value_ret = "A13\r\n" # 2 additional characters that will be cancelled value = int(value_ret[:-2], 16) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value_ret) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value_ret) axis.feedback_configuration = value mock_cmd.assert_called_with("ZB", target=1, params=[float(value)]) assert axis.feedback_configuration == value @@ -1410,14 +958,7 @@ def test_axis_feedback_configuration(mocker): def test_axis_feedback_configuration_none(): """Set axis feedback configuration with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.feedback_configuration = None @@ -1430,18 +971,10 @@ def test_axis_position_display_resolution(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.position_display_resolution = value mock_cmd.assert_called_with("FP", target=1, params=[float(value)]) assert axis.position_display_resolution == value @@ -1451,14 +984,7 @@ def test_axis_position_display_resolution(mocker): def test_axis_position_display_resolution_none(): """Set axis position display resolution with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.position_display_resolution = None @@ -1471,18 +997,10 @@ def test_axis_trajectory(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.trajectory = value mock_cmd.assert_called_with("TJ", target=1, params=[float(value)]) assert axis.trajectory == value @@ -1492,14 +1010,7 @@ def test_axis_trajectory(mocker): def test_axis_trajectory_none(): """Set axis trajectory with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.trajectory = None @@ -1512,18 +1023,10 @@ def test_axis_microstep_factor(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.microstep_factor = value mock_cmd.assert_called_with("QS", target=1, params=[float(value)]) assert axis.microstep_factor == value @@ -1533,14 +1036,7 @@ def test_axis_microstep_factor(mocker): def test_axis_microstep_factor_none(): """Set axis microstep factor with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.microstep_factor = None @@ -1550,14 +1046,7 @@ def test_axis_microstep_factor_none(): def test_axis_microstep_factor_out_of_range(fct): """Raise ValueError when microstep factor is out of range.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] with pytest.raises(ValueError) as err_info: @@ -1571,21 +1060,13 @@ def test_axis_hardware_limit_configuration(mocker): Mock out `_newport_cmd` since tested elsewhere. """ - value_ret = '42\r\n' # add two characters to delete later + value_ret = "42\r\n" # add two characters to delete later value = int(value_ret[:-2]) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value_ret) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value_ret) axis.hardware_limit_configuration = value mock_cmd.assert_called_with("ZH", target=1, params=[float(value)]) assert axis.hardware_limit_configuration == value @@ -1595,14 +1076,7 @@ def test_axis_hardware_limit_configuration(mocker): def test_axis_hardware_limit_configuration_none(): """Set axis hardware limit configuration with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.hardware_limit_configuration = None @@ -1615,18 +1089,10 @@ def test_axis_acceleration_feed_forward(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) axis.acceleration_feed_forward = value mock_cmd.assert_called_with("AF", target=1, params=[float(value)]) assert axis.acceleration_feed_forward == value @@ -1636,14 +1102,7 @@ def test_axis_acceleration_feed_forward(mocker): def test_axis_acceleration_feed_forward_none(): """Set axis acceleration feed forward with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.acceleration_feed_forward = None @@ -1654,21 +1113,13 @@ def test_axis_proportional_gain(mocker): Mock out `_newport_cmd` since tested elsewhere. """ - value_ret = '42\r' + value_ret = "42\r" value = float(value_ret[:-1]) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value_ret) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value_ret) axis.proportional_gain = value mock_cmd.assert_called_with("KP", target=1, params=[float(value)]) assert axis.proportional_gain == float(value) @@ -1678,14 +1129,7 @@ def test_axis_proportional_gain(mocker): def test_axis_proportional_gain_none(): """Set axis proportional gain with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.proportional_gain = None @@ -1696,21 +1140,13 @@ def test_axis_derivative_gain(mocker): Mock out `_newport_cmd` since tested elsewhere. """ - value_ret = '42' + value_ret = "42" value = float(value_ret) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value_ret) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value_ret) axis.derivative_gain = value mock_cmd.assert_called_with("KD", target=1, params=[float(value)]) assert axis.derivative_gain == float(value) @@ -1720,14 +1156,7 @@ def test_axis_derivative_gain(mocker): def test_axis_derivative_gain_none(): """Set axis derivative gain with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.derivative_gain = None @@ -1738,21 +1167,13 @@ def test_axis_integral_gain(mocker): Mock out `_newport_cmd` since tested elsewhere. """ - value_ret = '42' + value_ret = "42" value = float(value_ret) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value_ret) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value_ret) axis.integral_gain = value mock_cmd.assert_called_with("KI", target=1, params=[float(value)]) assert axis.integral_gain == float(value) @@ -1762,14 +1183,7 @@ def test_axis_integral_gain(mocker): def test_axis_integral_gain_none(): """Set axis integral gain with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.integral_gain = None @@ -1780,21 +1194,13 @@ def test_axis_integral_saturation_gain(mocker): Mock out `_newport_cmd` since tested elsewhere. """ - value_ret = '42' + value_ret = "42" value = float(value_ret) with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value_ret) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value_ret) axis.integral_saturation_gain = value mock_cmd.assert_called_with("KS", target=1, params=[float(value)]) assert axis.integral_saturation_gain == float(value) @@ -1804,14 +1210,7 @@ def test_axis_integral_saturation_gain(mocker): def test_axis_integral_saturation_gain_none(): """Set axis integral saturation gain with `None` does nothing.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] axis.integral_saturation_gain = None @@ -1828,21 +1227,12 @@ def test_axis_encoder_position(mocker): value = 42 get_unit = ik.newport.newportesp301.NewportESP301Units.millimeter with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" - ) as inst: - axis = inst.axis[0] - mock_get = mocker.patch.object(axis, '_get_units', - return_value=get_unit) - mock_set = mocker.patch.object(axis, '_set_units', return_value=None) - mock_cmd = mocker.patch.object(axis, '_newport_cmd', - return_value=value) + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" + ) as inst: + axis = inst.axis[0] + mock_get = mocker.patch.object(axis, "_get_units", return_value=get_unit) + mock_set = mocker.patch.object(axis, "_set_units", return_value=None) + mock_cmd = mocker.patch.object(axis, "_newport_cmd", return_value=value) assert axis.encoder_position == u.Quantity(value, u.count) mock_get.assert_called() mock_set.assert_called_with(get_unit) @@ -1852,25 +1242,17 @@ def test_axis_encoder_position(mocker): # AXIS METHODS # -@pytest.mark.parametrize("mode", - ik.newport.newportesp301.NewportESP301HomeSearchMode) +@pytest.mark.parametrize("mode", ik.newport.newportesp301.NewportESP301HomeSearchMode) def test_axis_search_for_home(mocker, mode): """Search for home. Mock out `search_for_home` of controller since already tested. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_search = mocker.patch.object(axis._controller, 'search_for_home') + mock_search = mocker.patch.object(axis._controller, "search_for_home") axis.search_for_home(search_mode=mode.value) mock_search.assert_called_with(axis=1, search_mode=mode.value) @@ -1883,17 +1265,10 @@ def test_axis_move_absolute(mocker): """ position = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.move(position) mock_cmd.assert_called_with("PA", params=[position], target=1) @@ -1906,21 +1281,14 @@ def test_axis_move_relative_wait(mocker): """ position = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.move(position, absolute=False, wait=True) calls = ( mocker.call("PR", params=[position], target=1), - mocker.call("WP", target=1, params=[float(position)]) + mocker.call("WP", target=1, params=[float(position)]), ) mock_cmd.assert_has_calls(calls) @@ -1934,24 +1302,17 @@ def test_axis_move_relative_wait_block(mocker): """ position = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") mock_cmd.side_effect = [None, None, False, True] axis.move(position, absolute=False, wait=True, block=True) calls = ( mocker.call("PR", params=[position], target=1), mocker.call("WP", target=1, params=[float(position)]), mocker.call("MD?", target=1), - mocker.call("MD?", target=1) + mocker.call("MD?", target=1), ) mock_cmd.assert_has_calls(calls) @@ -1962,17 +1323,10 @@ def test_axis_move_to_hardware_limit(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.move_to_hardware_limit() mock_cmd.assert_called_with("MT", target=1) @@ -1983,17 +1337,10 @@ def test_axis_move_indefinitely(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.move_indefinitely() mock_cmd.assert_called_with("MV", target=1) @@ -2004,17 +1351,10 @@ def test_axis_abort_motion(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.abort_motion() mock_cmd.assert_called_with("AB", target=1) @@ -2025,17 +1365,10 @@ def test_axis_wait_for_stop(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.wait_for_stop() mock_cmd.assert_called_with("WS", target=1) @@ -2046,17 +1379,10 @@ def test_axis_stop_motion(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.stop_motion() mock_cmd.assert_called_with("ST", target=1) @@ -2068,17 +1394,10 @@ def test_axis_wait_for_position(mocker): """ value = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.wait_for_position(value) mock_cmd.assert_called_with("WP", target=1, params=[float(value)]) @@ -2090,20 +1409,13 @@ def test_axis_wait_for_motion_max_wait_zero(mocker): zero. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mocker.patch.object(axis, '_newport_cmd', return_value="0") + mocker.patch.object(axis, "_newport_cmd", return_value="0") with pytest.raises(IOError) as err_info: - axis.wait_for_motion(max_wait=0.) + axis.wait_for_motion(max_wait=0.0) err_msg = err_info.value.args[0] assert err_msg == "Timed out waiting for motion to finish." @@ -2115,26 +1427,18 @@ def test_axis_wait_for_motion_max_wait_some_time(mocker): Mocking `time.time`, `time.sleep`, and `_newport_cmd`. Using generators to create the appropriate times.. """ - interval = 42. + interval = 42.0 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: # patch time and sleep - mock_time = mocker.patch.object(time, 'time', return_value=None) + mock_time = mocker.patch.object(time, "time", return_value=None) mock_time.side_effect = [0.0, 0.0, 0.1] - mock_sleep = mocker.patch.object(time, 'sleep', return_value=None) + mock_sleep = mocker.patch.object(time, "sleep", return_value=None) # get axis axis = inst.axis[0] # patch status - mock_status = mocker.patch.object(axis, '_newport_cmd', - return_value=None) + mock_status = mocker.patch.object(axis, "_newport_cmd", return_value=None) mock_status.side_effect = ["0", "0", "1"] assert axis.wait_for_motion(poll_interval=interval) is None # make sure the routine has called sleep @@ -2147,17 +1451,10 @@ def test_axis_enable(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.enable() mock_cmd.assert_called_with("MO", target=1) @@ -2168,17 +1465,10 @@ def test_axis_disable(mocker): Mock out `_newport_cmd` since tested elsewhere. """ with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") axis.disable() mock_cmd.assert_called_with("MF", target=1) @@ -2192,7 +1482,7 @@ def test_axis_setup_axis(mocker): current = 1 voltage = 2 units = ik.newport.newportesp301.NewportESP301Units.radian - encoder_resolution = 3. + encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 homing_velocity = 6 @@ -2219,18 +1509,11 @@ def test_axis_setup_axis(mocker): hardware_limit_configuration = 27 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') - mocker.patch.object(axis, 'read_setup', return_value=True) + mock_cmd = mocker.patch.object(axis, "_newport_cmd") + mocker.patch.object(axis, "read_setup", return_value=True) ax_setup = axis.setup_axis( motor_type=motor_type, current=current, @@ -2269,8 +1552,7 @@ def test_axis_setup_axis(mocker): mocker.call("QM", target=1, params=[int(motor_type)]), mocker.call("ZB", target=1, params=[int(feedback_configuration)]), mocker.call("FR", target=1, params=[float(full_step_resolution)]), - mocker.call("FP", target=1, - params=[int(position_display_resolution)]), + mocker.call("FP", target=1, params=[int(position_display_resolution)]), mocker.call("QI", target=1, params=[float(current)]), mocker.call("QV", target=1, params=[float(voltage)]), mocker.call("SN", target=1, params=[units.value]), @@ -2290,15 +1572,12 @@ def test_axis_setup_axis(mocker): mocker.call("KP", target=1, params=[float(proportional_gain)]), mocker.call("KD", target=1, params=[float(derivative_gain)]), mocker.call("KI", target=1, params=[float(integral_gain)]), - mocker.call("KS", target=1, - params=[float(integral_saturation_gain)]), + mocker.call("KS", target=1, params=[float(integral_saturation_gain)]), mocker.call("DH", target=1, params=[float(home)]), mocker.call("QS", target=1, params=[float(microstep_factor)]), - mocker.call("AF", target=1, - params=[float(acceleration_feed_forward)]), + mocker.call("AF", target=1, params=[float(acceleration_feed_forward)]), mocker.call("TJ", target=1, params=[int(trajectory)]), - mocker.call("ZH", target=1, - params=[int(hardware_limit_configuration)]), + mocker.call("ZH", target=1, params=[int(hardware_limit_configuration)]), ) mock_cmd.assert_has_calls(calls_params, any_order=True) @@ -2306,7 +1585,7 @@ def test_axis_setup_axis(mocker): calls_final = ( mocker.call("UF", target=1), mocker.call("QD", target=1), - mocker.call("SM") + mocker.call("SM"), ) mock_cmd.assert_has_calls(calls_final) mock_cmd.assert_called_with("SM") @@ -2321,7 +1600,7 @@ def test_axis_setup_axis_torque(mocker): current = 1 voltage = 2 units = ik.newport.newportesp301.NewportESP301Units.radian - encoder_resolution = 3. + encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 homing_velocity = 6 @@ -2351,18 +1630,11 @@ def test_axis_setup_axis_torque(mocker): rmt_perc = 13 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') - mocker.patch.object(axis, 'read_setup', return_value=True) + mock_cmd = mocker.patch.object(axis, "_newport_cmd") + mocker.patch.object(axis, "read_setup", return_value=True) axis.setup_axis( motor_type=motor_type, current=current, @@ -2394,12 +1666,10 @@ def test_axis_setup_axis_torque(mocker): acceleration_feed_forward=acceleration_feed_forward, hardware_limit_configuration=hardware_limit_configuration, reduce_motor_torque_time=rmt_time, - reduce_motor_torque_percentage=rmt_perc + reduce_motor_torque_percentage=rmt_perc, ) # ensure the torque settings are set - call_torque = ( - mocker.call("QR", target=1, params=[rmt_time, rmt_perc]), - ) + call_torque = (mocker.call("QR", target=1, params=[rmt_time, rmt_perc]),) mock_cmd.assert_has_calls(call_torque) @@ -2414,7 +1684,7 @@ def test_axis_setup_axis_torque_time_out_of_range(mocker, rmt_time): current = 1 voltage = 2 units = ik.newport.newportesp301.NewportESP301Units.radian - encoder_resolution = 3. + encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 homing_velocity = 6 @@ -2443,18 +1713,11 @@ def test_axis_setup_axis_torque_time_out_of_range(mocker, rmt_time): rmt_perc = 13 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mocker.patch.object(axis, '_newport_cmd') - mocker.patch.object(axis, 'read_setup', return_value=True) + mocker.patch.object(axis, "_newport_cmd") + mocker.patch.object(axis, "read_setup", return_value=True) with pytest.raises(ValueError) as err_info: axis.setup_axis( motor_type=motor_type, @@ -2487,7 +1750,7 @@ def test_axis_setup_axis_torque_time_out_of_range(mocker, rmt_time): acceleration_feed_forward=acceleration_feed_forward, hardware_limit_configuration=hardware_limit_configuration, reduce_motor_torque_time=rmt_time, - reduce_motor_torque_percentage=rmt_perc + reduce_motor_torque_percentage=rmt_perc, ) err_msg = err_info.value.args[0] assert err_msg == "Time must be between 0 and 60000 ms" @@ -2503,7 +1766,7 @@ def test_axis_setup_axis_torque_percentage_out_of_range(mocker, rmt_perc): current = 1 voltage = 2 units = ik.newport.newportesp301.NewportESP301Units.radian - encoder_resolution = 3. + encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 homing_velocity = 6 @@ -2532,18 +1795,11 @@ def test_axis_setup_axis_torque_percentage_out_of_range(mocker, rmt_perc): rmt_time = 42 with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mocker.patch.object(axis, '_newport_cmd') - mocker.patch.object(axis, 'read_setup', return_value=True) + mocker.patch.object(axis, "_newport_cmd") + mocker.patch.object(axis, "read_setup", return_value=True) with pytest.raises(ValueError) as err_info: axis.setup_axis( motor_type=motor_type, @@ -2576,7 +1832,7 @@ def test_axis_setup_axis_torque_percentage_out_of_range(mocker, rmt_perc): acceleration_feed_forward=acceleration_feed_forward, hardware_limit_configuration=hardware_limit_configuration, reduce_motor_torque_time=rmt_time, - reduce_motor_torque_percentage=rmt_perc + reduce_motor_torque_percentage=rmt_perc, ) err_msg = err_info.value.args[0] assert err_msg == r"Percentage must be between 0 and 100%" @@ -2588,73 +1844,66 @@ def test_axis_read_setup(mocker): Mock out `_newport_cmd` since tested elsewhere. """ config = { - 'units': u.mm, - 'motor_type': ik.newport.newportesp301.NewportESP301MotorType.dc_servo, - 'feedback_configuration': 1, # last 2 removed at return - 'full_step_resolution': u.Quantity(2.0, u.mm), - 'position_display_resolution': 3, - 'current': u.Quantity(4.0, u.A), - 'max_velocity': u.Quantity(5.0, u.mm / u.s), - 'encoder_resolution': u.Quantity(6.0, u.mm), - 'acceleration': u.Quantity(7.0, u.mm / u.s**2), - 'deceleration': u.Quantity(8.0, u.mm / u.s**2), - 'velocity': u.Quantity(9.0, u.mm / u.s), - 'max_acceleration': u.Quantity(10.0, u.mm / u.s**2.), - 'homing_velocity': u.Quantity(11.0, u.mm / u.s), - 'jog_high_velocity': u.Quantity(12.0, u.mm / u.s), - 'jog_low_velocity': u.Quantity(13.0, u.mm / u.s), - 'estop_deceleration': u.Quantity(14.0, u.mm / u.s**2.), - 'jerk': u.Quantity(14.0, u.mm / u.s**3.), - 'proportional_gain': 15.0, # last 1 removed at return - 'derivative_gain': 16.0, - 'integral_gain': 17.0, - 'integral_saturation_gain': 18.0, - 'home': u.Quantity(19.0, u.mm), - 'microstep_factor': 20, - 'acceleration_feed_forward': 21.0, - 'trajectory': 22, - 'hardware_limit_configuration': 23 # last 2 removed + "units": u.mm, + "motor_type": ik.newport.newportesp301.NewportESP301MotorType.dc_servo, + "feedback_configuration": 1, # last 2 removed at return + "full_step_resolution": u.Quantity(2.0, u.mm), + "position_display_resolution": 3, + "current": u.Quantity(4.0, u.A), + "max_velocity": u.Quantity(5.0, u.mm / u.s), + "encoder_resolution": u.Quantity(6.0, u.mm), + "acceleration": u.Quantity(7.0, u.mm / u.s ** 2), + "deceleration": u.Quantity(8.0, u.mm / u.s ** 2), + "velocity": u.Quantity(9.0, u.mm / u.s), + "max_acceleration": u.Quantity(10.0, u.mm / u.s ** 2.0), + "homing_velocity": u.Quantity(11.0, u.mm / u.s), + "jog_high_velocity": u.Quantity(12.0, u.mm / u.s), + "jog_low_velocity": u.Quantity(13.0, u.mm / u.s), + "estop_deceleration": u.Quantity(14.0, u.mm / u.s ** 2.0), + "jerk": u.Quantity(14.0, u.mm / u.s ** 3.0), + "proportional_gain": 15.0, # last 1 removed at return + "derivative_gain": 16.0, + "integral_gain": 17.0, + "integral_saturation_gain": 18.0, + "home": u.Quantity(19.0, u.mm), + "microstep_factor": 20, + "acceleration_feed_forward": 21.0, + "trajectory": 22, + "hardware_limit_configuration": 23, # last 2 removed } with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") mock_cmd.side_effect = [ ik.newport.newportesp301.NewportESP301Units.millimeter.value, - config['motor_type'].value, + config["motor_type"].value, f"{config['feedback_configuration']}**", # 2 extra - config['full_step_resolution'].magnitude, - config['position_display_resolution'], - config['current'].magnitude, - config['max_velocity'].magnitude, - config['encoder_resolution'].magnitude, - config['acceleration'].magnitude, - config['deceleration'].magnitude, - config['velocity'].magnitude, - config['max_acceleration'].magnitude, - config['homing_velocity'].magnitude, - config['jog_high_velocity'].magnitude, - config['jog_low_velocity'].magnitude, - config['estop_deceleration'].magnitude, - config['jerk'].magnitude, + config["full_step_resolution"].magnitude, + config["position_display_resolution"], + config["current"].magnitude, + config["max_velocity"].magnitude, + config["encoder_resolution"].magnitude, + config["acceleration"].magnitude, + config["deceleration"].magnitude, + config["velocity"].magnitude, + config["max_acceleration"].magnitude, + config["homing_velocity"].magnitude, + config["jog_high_velocity"].magnitude, + config["jog_low_velocity"].magnitude, + config["estop_deceleration"].magnitude, + config["jerk"].magnitude, f"{config['proportional_gain']}*", # 1 extra - config['derivative_gain'], - config['integral_gain'], - config['integral_saturation_gain'], - config['home'].magnitude, - config['microstep_factor'], - config['acceleration_feed_forward'], - config['trajectory'], - f"{config['hardware_limit_configuration']}**" + config["derivative_gain"], + config["integral_gain"], + config["integral_saturation_gain"], + config["home"].magnitude, + config["microstep_factor"], + config["acceleration_feed_forward"], + config["trajectory"], + f"{config['hardware_limit_configuration']}**", ] assert axis.read_setup() == config @@ -2669,26 +1918,19 @@ def test_axis_get_status(mocker): "position": u.Quantity(1.0, u.mm), "desired_position": u.Quantity(2.0, u.mm), "desired_velocity": u.Quantity(3.0, u.mm / u.s), - "is_motion_done": True + "is_motion_done": True, } with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis, '_newport_cmd') + mock_cmd = mocker.patch.object(axis, "_newport_cmd") mock_cmd.side_effect = [ "2", status["position"].magnitude, status["desired_position"].magnitude, status["desired_velocity"].magnitude, - "1" + "1", ] assert axis.get_status() == status @@ -2697,14 +1939,7 @@ def test_axis_get_status(mocker): def test_axis_get_pq_unit(num): """Get units for specified axis.""" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] assert axis._get_pq_unit(num) == axis._unit_dict[num] @@ -2719,14 +1954,7 @@ def test_axis_get_unit_num(num): if num == 1: num = 0 # u.count twice with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] quant = axis._unit_dict[num] @@ -2738,21 +1966,13 @@ def test_axis_get_unit_num_invalid_unit(): """Raise KeyError if unit not valid.""" invalid_unit = u.ly with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] with pytest.raises(KeyError) as err_info: axis._get_unit_num(invalid_unit) err_msg = err_info.value.args[0] - assert err_msg == f"{invalid_unit} is not a valid unit for Newport " \ - f"Axis" + assert err_msg == f"{invalid_unit} is not a valid unit for Newport " f"Axis" def test_axis_newport_cmd(mocker): @@ -2763,16 +1983,9 @@ def test_axis_newport_cmd(mocker): cmd = 123 some_keyword = "keyword" with expected_protocol( - ik.newport.NewportESP301, - [ - ax_init[0] - ], - [ - ax_init[1] - ], - sep="\r" + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: axis = inst.axis[0] - mock_cmd = mocker.patch.object(axis._controller, '_newport_cmd') + mock_cmd = mocker.patch.object(axis._controller, "_newport_cmd") axis._newport_cmd(cmd, some_keyword=some_keyword) mock_cmd.assert_called_with(cmd, some_keyword=some_keyword) diff --git a/instruments/tests/test_ondax/test_lm.py b/instruments/tests/test_ondax/test_lm.py index 6e3edef90..4d23c85d5 100644 --- a/instruments/tests/test_ondax/test_lm.py +++ b/instruments/tests/test_ondax/test_lm.py @@ -17,183 +17,74 @@ def test_acc_target(): - with expected_protocol( - ondax.LM, - [ - "rstli?" - ], - [ - "100" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rstli?"], ["100"], sep="\r") as lm: assert lm.acc.target == 100 * u.mA def test_acc_enable(): - with expected_protocol( - ondax.LM, - [ - "lcen" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["lcen"], ["OK"], sep="\r") as lm: lm.acc.enabled = True assert lm.acc.enabled def test_acc_disable(): - with expected_protocol( - ondax.LM, - [ - "lcdis" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["lcdis"], ["OK"], sep="\r") as lm: lm.acc.enabled = False assert not lm.acc.enabled def test_acc_enable_not_boolean(): with pytest.raises(TypeError): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, [], [], sep="\r") as lm: lm.acc.enabled = "foobar" def test_acc_on(): - with expected_protocol( - ondax.LM, - [ - "lcon" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["lcon"], ["OK"], sep="\r") as lm: lm.acc.on() def test_acc_off(): - with expected_protocol( - ondax.LM, - [ - "lcoff" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["lcoff"], ["OK"], sep="\r") as lm: lm.acc.off() def test_apc_target(): - with expected_protocol( - ondax.LM, - [ - "rslp?" - ], - [ - "100" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rslp?"], ["100"], sep="\r") as lm: assert lm.apc.target == 100 * u.mW def test_apc_enable(): - with expected_protocol( - ondax.LM, - [ - "len" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["len"], ["OK"], sep="\r") as lm: lm.apc.enabled = True assert lm.apc.enabled def test_apc_disable(): - with expected_protocol( - ondax.LM, - [ - "ldis" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["ldis"], ["OK"], sep="\r") as lm: lm.apc.enabled = False assert not lm.apc.enabled def test_apc_enable_not_boolean(): with pytest.raises(TypeError): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, [], [], sep="\r") as lm: lm.apc.enabled = "foobar" def test_apc_start(): - with expected_protocol( - ondax.LM, - [ - "sps" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["sps"], ["OK"], sep="\r") as lm: lm.apc.start() def test_apc_stop(): - with expected_protocol( - ondax.LM, - [ - "cps" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["cps"], ["OK"], sep="\r") as lm: lm.apc.stop() def test_modulation_on_time(): with expected_protocol( - ondax.LM, - [ - "stsont?", - "stsont:20" - ], - [ - "10", - "OK" - ], - sep="\r" + ondax.LM, ["stsont?", "stsont:20"], ["10", "OK"], sep="\r" ) as lm: assert lm.modulation.on_time == 10 * u.ms lm.modulation.on_time = 20 * u.ms @@ -201,157 +92,66 @@ def test_modulation_on_time(): def test_modulation_off_time(): with expected_protocol( - ondax.LM, - [ - "stsofft?", - "stsofft:20" - ], - [ - "10", - "OK" - ], - sep="\r" + ondax.LM, ["stsofft?", "stsofft:20"], ["10", "OK"], sep="\r" ) as lm: assert lm.modulation.off_time == 10 * u.ms lm.modulation.off_time = 20 * u.ms def test_modulation_enabled(): - with expected_protocol( - ondax.LM, - [ - "stm" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["stm"], ["OK"], sep="\r") as lm: lm.modulation.enabled = True assert lm.modulation.enabled def test_modulation_disabled(): - with expected_protocol( - ondax.LM, - [ - "ctm" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["ctm"], ["OK"], sep="\r") as lm: lm.modulation.enabled = False assert not lm.modulation.enabled def test_modulation_enable_not_boolean(): with pytest.raises(TypeError): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, [], [], sep="\r") as lm: lm.modulation.enabled = "foobar" def test_tec_current(): - with expected_protocol( - ondax.LM, - [ - "rti?" - ], - [ - "100" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rti?"], ["100"], sep="\r") as lm: assert lm.tec.current == 100 * u.mA def test_tec_target(): - with expected_protocol( - ondax.LM, - [ - "rstt?" - ], - [ - "22" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rstt?"], ["22"], sep="\r") as lm: assert lm.tec.target == u.Quantity(22, u.degC) def test_tec_enable(): - with expected_protocol( - ondax.LM, - [ - "tecon" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["tecon"], ["OK"], sep="\r") as lm: lm.tec.enabled = True assert lm.tec.enabled def test_tec_disable(): - with expected_protocol( - ondax.LM, - [ - "tecoff" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["tecoff"], ["OK"], sep="\r") as lm: lm.tec.enabled = False assert not lm.tec.enabled def test_tec_enable_not_boolean(): with pytest.raises(TypeError): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, [], [], sep="\r") as lm: lm.tec.enabled = "foobar" def test_firmware(): - with expected_protocol( - ondax.LM, - [ - "rsv?" - ], - [ - "3.27" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rsv?"], ["3.27"], sep="\r") as lm: assert lm.firmware == "3.27" def test_current(): with expected_protocol( - ondax.LM, - [ - "rli?", - "slc:100" - ], - [ - "120", - "OK" - ], - sep="\r" + ondax.LM, ["rli?", "slc:100"], ["120", "OK"], sep="\r" ) as lm: assert lm.current == 120 * u.mA lm.current = 100 * u.mA @@ -359,16 +159,7 @@ def test_current(): def test_maximum_current(): with expected_protocol( - ondax.LM, - [ - "rlcm?", - "smlc:100" - ], - [ - "120", - "OK" - ], - sep="\r" + ondax.LM, ["rlcm?", "smlc:100"], ["120", "OK"], sep="\r" ) as lm: assert lm.maximum_current == 120 * u.mA lm.maximum_current = 100 * u.mA @@ -376,130 +167,51 @@ def test_maximum_current(): def test_power(): with expected_protocol( - ondax.LM, - [ - "rlp?", - "slp:100" - ], - [ - "120", - "OK" - ], - sep="\r" + ondax.LM, ["rlp?", "slp:100"], ["120", "OK"], sep="\r" ) as lm: assert lm.power == 120 * u.mW lm.power = 100 * u.mW def test_serial_number(): - with expected_protocol( - ondax.LM, - [ - "rsn?" - ], - [ - "B099999" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rsn?"], ["B099999"], sep="\r") as lm: assert lm.serial_number == "B099999" def test_status(): - with expected_protocol( - ondax.LM, - [ - "rlrs?" - ], - [ - "1" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rlrs?"], ["1"], sep="\r") as lm: assert lm.status == lm.Status(1) def test_temperature(): - with expected_protocol( - ondax.LM, - [ - "rtt?", - "stt:40" - ], - [ - "35", - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["rtt?", "stt:40"], ["35", "OK"], sep="\r") as lm: assert lm.temperature == u.Quantity(35, u.degC) lm.temperature = u.Quantity(40, u.degC) def test_enable(): - with expected_protocol( - ondax.LM, - [ - "lon" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["lon"], ["OK"], sep="\r") as lm: lm.enabled = True assert lm.enabled def test_disable(): - with expected_protocol( - ondax.LM, - [ - "loff" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["loff"], ["OK"], sep="\r") as lm: lm.enabled = False assert not lm.enabled def test_enable_not_boolean(): with pytest.raises(TypeError): - with expected_protocol( - ondax.LM, - [], - [], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, [], [], sep="\r") as lm: lm.enabled = "foobar" def test_save(): - with expected_protocol( - ondax.LM, - [ - "ssc" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["ssc"], ["OK"], sep="\r") as lm: lm.save() def test_reset(): - with expected_protocol( - ondax.LM, - [ - "reset" - ], - [ - "OK" - ], - sep="\r" - ) as lm: + with expected_protocol(ondax.LM, ["reset"], ["OK"], sep="\r") as lm: lm.reset() diff --git a/instruments/tests/test_oxford/test_oxforditc503.py b/instruments/tests/test_oxford/test_oxforditc503.py index 0986152cb..de679b985 100644 --- a/instruments/tests/test_oxford/test_oxforditc503.py +++ b/instruments/tests/test_oxford/test_oxforditc503.py @@ -15,29 +15,14 @@ def test_sensor_returns_sensor_class(): - with expected_protocol( - ik.oxford.OxfordITC503, - [ - "C3" - ], - [], - sep="\r" - ) as inst: + with expected_protocol(ik.oxford.OxfordITC503, ["C3"], [], sep="\r") as inst: sensor = inst.sensor[0] assert isinstance(sensor, inst.Sensor) is True def test_sensor_temperature(): with expected_protocol( - ik.oxford.OxfordITC503, - [ - "C3", - "R1" - ], - [ - "R123" - ], - sep="\r" + ik.oxford.OxfordITC503, ["C3", "R1"], ["R123"], sep="\r" ) as inst: sensor = inst.sensor[0] assert sensor.temperature == u.Quantity(123, u.kelvin) diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index d683b09c3..5d59c974c 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -18,26 +18,15 @@ def test_reset(): - with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0E." - ], - [] - ) as inst: + with expected_protocol(ik.phasematrix.PhaseMatrixFSW0020, ["0E."], []) as inst: inst.reset() def test_frequency(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "04.", - "0C{:012X}.".format(int((10 * u.GHz).to(u.mHz).magnitude)) - ], - [ - "00E8D4A51000" - ] + ik.phasematrix.PhaseMatrixFSW0020, + ["04.", "0C{:012X}.".format(int((10 * u.GHz).to(u.mHz).magnitude))], + ["00E8D4A51000"], ) as inst: assert inst.frequency == 1.0000000000000002 * u.GHz inst.frequency = 10 * u.GHz @@ -45,14 +34,9 @@ def test_frequency(): def test_power(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0D.", - "03{:04X}.".format(int(u.Quantity(10, u.dBm).to(u.cBm).magnitude)) - ], - [ - "-064" - ] + ik.phasematrix.PhaseMatrixFSW0020, + ["0D.", "03{:04X}.".format(int(u.Quantity(10, u.dBm).to(u.cBm).magnitude))], + ["-064"], ) as inst: assert inst.power == u.Quantity(-10, u.dBm) inst.power = u.Quantity(10, u.dBm) @@ -60,13 +44,7 @@ def test_power(): def test_phase(): """Raise NotImplementedError when phase is set / got.""" - with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.phasematrix.PhaseMatrixFSW0020, [], []) as inst: with pytest.raises(NotImplementedError): _ = inst.phase with pytest.raises(NotImplementedError): @@ -75,12 +53,9 @@ def test_phase(): def test_blanking(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "05{:02X}.".format(1), - "05{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + ["05{:02X}.".format(1), "05{:02X}.".format(0)], + [], ) as inst: inst.blanking = True inst.blanking = False @@ -90,12 +65,9 @@ def test_blanking(): def test_ref_output(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "08{:02X}.".format(1), - "08{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + ["08{:02X}.".format(1), "08{:02X}.".format(0)], + [], ) as inst: inst.ref_output = True inst.ref_output = False @@ -105,12 +77,9 @@ def test_ref_output(): def test_output(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0F{:02X}.".format(1), - "0F{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + ["0F{:02X}.".format(1), "0F{:02X}.".format(0)], + [], ) as inst: inst.output = True inst.output = False @@ -120,12 +89,9 @@ def test_output(): def test_pulse_modulation(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "09{:02X}.".format(1), - "09{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + ["09{:02X}.".format(1), "09{:02X}.".format(0)], + [], ) as inst: inst.pulse_modulation = True inst.pulse_modulation = False @@ -135,12 +101,9 @@ def test_pulse_modulation(): def test_am_modulation(): with expected_protocol( - ik.phasematrix.PhaseMatrixFSW0020, - [ - "0A{:02X}.".format(1), - "0A{:02X}.".format(0) - ], - [] + ik.phasematrix.PhaseMatrixFSW0020, + ["0A{:02X}.".format(1), "0A{:02X}.".format(0)], + [], ) as inst: inst.am_modulation = True inst.am_modulation = False diff --git a/instruments/tests/test_picowatt/test_picowatt_avs47.py b/instruments/tests/test_picowatt/test_picowatt_avs47.py index c67bca372..c625dce3f 100644 --- a/instruments/tests/test_picowatt/test_picowatt_avs47.py +++ b/instruments/tests/test_picowatt/test_picowatt_avs47.py @@ -21,67 +21,31 @@ def test_sensor_is_sensor_class(): def test_init(): - with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0" - ], - [] - ): + with expected_protocol(ik.picowatt.PicowattAVS47, ["HDR 0"], []): pass def test_sensor_resistance_same_channel(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "MUX?", - "ADC", - "RES?" - ], - [ - "0", - "123" - ] + ik.picowatt.PicowattAVS47, ["HDR 0", "MUX?", "ADC", "RES?"], ["0", "123"] ) as inst: assert inst.sensor[0].resistance == 123 * u.ohm def test_sensor_resistance_different_channel(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "MUX?", - "INP 0", - "MUX 0", - "INP 1", - "ADC", - "RES?" - ], - [ - "1", - "123" - ] + ik.picowatt.PicowattAVS47, + ["HDR 0", "MUX?", "INP 0", "MUX 0", "INP 1", "ADC", "RES?"], + ["1", "123"], ) as inst: assert inst.sensor[0].resistance == 123 * u.ohm def test_remote(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "REM?", - "REM?", - "REM 1", - "REM 0" - ], - [ - "0", - "1" - ] + ik.picowatt.PicowattAVS47, + ["HDR 0", "REM?", "REM?", "REM 1", "REM 0"], + ["0", "1"], ) as inst: assert inst.remote is False assert inst.remote is True @@ -91,15 +55,11 @@ def test_remote(): def test_input_source(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "INP?", - "INP 1" - ], - [ - "0", - ] + ik.picowatt.PicowattAVS47, + ["HDR 0", "INP?", "INP 1"], + [ + "0", + ], ) as inst: assert inst.input_source == inst.InputSource.ground inst.input_source = inst.InputSource.actual @@ -107,15 +67,11 @@ def test_input_source(): def test_mux_channel(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "MUX?", - "MUX 1" - ], - [ - "3", - ] + ik.picowatt.PicowattAVS47, + ["HDR 0", "MUX?", "MUX 1"], + [ + "3", + ], ) as inst: assert inst.mux_channel == 3 inst.mux_channel = 1 @@ -123,15 +79,11 @@ def test_mux_channel(): def test_excitation(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "EXC?", - "EXC 1" - ], - [ - "3", - ] + ik.picowatt.PicowattAVS47, + ["HDR 0", "EXC?", "EXC 1"], + [ + "3", + ], ) as inst: assert inst.excitation == 3 inst.excitation = 1 @@ -139,15 +91,11 @@ def test_excitation(): def test_display(): with expected_protocol( - ik.picowatt.PicowattAVS47, - [ - "HDR 0", - "DIS?", - "DIS 1" - ], - [ - "3", - ] + ik.picowatt.PicowattAVS47, + ["HDR 0", "DIS?", "DIS 1"], + [ + "3", + ], ) as inst: assert inst.display == 3 inst.display = 1 diff --git a/instruments/tests/test_property_factories/test_bool_property.py b/instruments/tests/test_property_factories/test_bool_property.py index d0bfbe94b..e446d53fc 100644 --- a/instruments/tests/test_property_factories/test_bool_property.py +++ b/instruments/tests/test_property_factories/test_bool_property.py @@ -17,12 +17,13 @@ # pylint: disable=missing-docstring + def test_bool_property_basics(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1') - mock2 = bool_property('MOCK2', inst_true='YES', inst_false='NO') + mock1 = bool_property("MOCK1") + mock2 = bool_property("MOCK2", inst_true="YES", inst_false="NO") - mock_inst = BoolMock({'MOCK1?': 'OFF', 'MOCK2?': 'YES'}) + mock_inst = BoolMock({"MOCK1?": "OFF", "MOCK2?": "YES"}) assert mock_inst.mock1 is False assert mock_inst.mock2 is True @@ -30,65 +31,67 @@ class BoolMock(MockInstrument): mock_inst.mock1 = True mock_inst.mock2 = False - assert mock_inst.value == 'MOCK1?\nMOCK2?\nMOCK1 ON\nMOCK2 NO\n' + assert mock_inst.value == "MOCK1?\nMOCK2?\nMOCK1 ON\nMOCK2 NO\n" def test_bool_property_set_fmt(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', set_fmt="{}={}") + mock1 = bool_property("MOCK1", set_fmt="{}={}") - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({"MOCK1?": "OFF"}) mock_instrument.mock1 = True - assert mock_instrument.value == 'MOCK1=ON\n' + assert mock_instrument.value == "MOCK1=ON\n" def test_bool_property_readonly_writing_fails(): with pytest.raises(AttributeError): + class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', readonly=True) + mock1 = bool_property("MOCK1", readonly=True) - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({"MOCK1?": "OFF"}) mock_instrument.mock1 = True def test_bool_property_readonly_reading_passes(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', readonly=True) + mock1 = bool_property("MOCK1", readonly=True) - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({"MOCK1?": "OFF"}) assert mock_instrument.mock1 is False def test_bool_property_writeonly_reading_fails(): with pytest.raises(AttributeError): + class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', writeonly=True) + mock1 = bool_property("MOCK1", writeonly=True) - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({"MOCK1?": "OFF"}) _ = mock_instrument.mock1 def test_bool_property_writeonly_writing_passes(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', writeonly=True) + mock1 = bool_property("MOCK1", writeonly=True) - mock_instrument = BoolMock({'MOCK1?': 'OFF'}) + mock_instrument = BoolMock({"MOCK1?": "OFF"}) mock_instrument.mock1 = False def test_bool_property_set_cmd(): class BoolMock(MockInstrument): - mock1 = bool_property('MOCK1', set_cmd='FOOBAR') + mock1 = bool_property("MOCK1", set_cmd="FOOBAR") - mock_inst = BoolMock({'MOCK1?': 'OFF'}) + mock_inst = BoolMock({"MOCK1?": "OFF"}) assert mock_inst.mock1 is False mock_inst.mock1 = True - assert mock_inst.value == 'MOCK1?\nFOOBAR ON\n' + assert mock_inst.value == "MOCK1?\nFOOBAR ON\n" diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index d082041c0..9639102da 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -22,12 +22,12 @@ def test_bounded_unitful_property_basics(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz + "MOCK", units=u.hertz ) mock_inst = BoundedUnitfulMock( - {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) + {"MOCK?": "1000", "MOCK:MIN?": "10", "MOCK:MAX?": "9999"} + ) assert mock_inst.property == 1000 * u.hertz assert mock_inst.property_min == 10 * u.hertz @@ -38,28 +38,30 @@ class BoundedUnitfulMock(MockInstrument): def test_bounded_unitful_property_set_outside_max(): with pytest.raises(ValueError): + class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz + "MOCK", units=u.hertz ) mock_inst = BoundedUnitfulMock( - {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) + {"MOCK?": "1000", "MOCK:MIN?": "10", "MOCK:MAX?": "9999"} + ) mock_inst.property = 10000 * u.hertz # Should raise ValueError def test_bounded_unitful_property_set_outside_min(): with pytest.raises(ValueError): + class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz + "MOCK", units=u.hertz ) mock_inst = BoundedUnitfulMock( - {'MOCK?': '1000', 'MOCK:MIN?': '10', 'MOCK:MAX?': '9999'}) + {"MOCK?": "1000", "MOCK:MIN?": "10", "MOCK:MAX?": "9999"} + ) mock_inst.property = 1 * u.hertz # Should raise ValueError @@ -67,37 +69,31 @@ class BoundedUnitfulMock(MockInstrument): def test_bounded_unitful_property_min_fmt_str(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz, - min_fmt_str="{} MIN?" + "MOCK", units=u.hertz, min_fmt_str="{} MIN?" ) - mock_inst = BoundedUnitfulMock({'MOCK MIN?': '10'}) + mock_inst = BoundedUnitfulMock({"MOCK MIN?": "10"}) assert mock_inst.property_min == 10 * u.Hz - assert mock_inst.value == 'MOCK MIN?\n' + assert mock_inst.value == "MOCK MIN?\n" def test_bounded_unitful_property_max_fmt_str(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz, - max_fmt_str="{} MAX?" + "MOCK", units=u.hertz, max_fmt_str="{} MAX?" ) - mock_inst = BoundedUnitfulMock({'MOCK MAX?': '9999'}) + mock_inst = BoundedUnitfulMock({"MOCK MAX?": "9999"}) assert mock_inst.property_max == 9999 * u.Hz - assert mock_inst.value == 'MOCK MAX?\n' + assert mock_inst.value == "MOCK MAX?\n" def test_bounded_unitful_property_static_range(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz, - valid_range=(10, 9999) + "MOCK", units=u.hertz, valid_range=(10, 9999) ) mock_inst = BoundedUnitfulMock() @@ -109,9 +105,7 @@ class BoundedUnitfulMock(MockInstrument): def test_bounded_unitful_property_static_range_with_units(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz, - valid_range=(10 * u.kilohertz, 9999 * u.kilohertz) + "MOCK", units=u.hertz, valid_range=(10 * u.kilohertz, 9999 * u.kilohertz) ) mock_inst = BoundedUnitfulMock() @@ -122,39 +116,22 @@ class BoundedUnitfulMock(MockInstrument): @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_passes_kwargs(mock_unitful_property): - bounded_unitful_property( - command='MOCK', - units=u.Hz, - derp="foobar" - ) + bounded_unitful_property(command="MOCK", units=u.Hz, derp="foobar") mock_unitful_property.assert_called_with( - 'MOCK', - u.Hz, - derp="foobar", - valid_range=(mock.ANY, mock.ANY) + "MOCK", u.Hz, derp="foobar", valid_range=(mock.ANY, mock.ANY) ) @mock.patch("instruments.util_fns.unitful_property") def test_bounded_unitful_property_valid_range_none(mock_unitful_property): - bounded_unitful_property( - command='MOCK', - units=u.Hz, - valid_range=(None, None) - ) - mock_unitful_property.assert_called_with( - 'MOCK', - u.Hz, - valid_range=(None, None) - ) + bounded_unitful_property(command="MOCK", units=u.Hz, valid_range=(None, None)) + mock_unitful_property.assert_called_with("MOCK", u.Hz, valid_range=(None, None)) def test_bounded_unitful_property_returns_none(): class BoundedUnitfulMock(MockInstrument): property, property_min, property_max = bounded_unitful_property( - 'MOCK', - units=u.hertz, - valid_range=(None, None) + "MOCK", units=u.hertz, valid_range=(None, None) ) mock_inst = BoundedUnitfulMock() diff --git a/instruments/tests/test_property_factories/test_enum_property.py b/instruments/tests/test_property_factories/test_enum_property.py index 09f8e68af..bf7eeea99 100644 --- a/instruments/tests/test_property_factories/test_enum_property.py +++ b/instruments/tests/test_property_factories/test_enum_property.py @@ -20,69 +20,66 @@ def test_enum_property(): class SillyEnum(Enum): - a = 'aa' - b = 'bb' + a = "aa" + b = "bb" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum) - b = enum_property('MOCK:B', SillyEnum) + a = enum_property("MOCK:A", SillyEnum) + b = enum_property("MOCK:B", SillyEnum) - mock_inst = EnumMock({'MOCK:A?': 'aa', 'MOCK:B?': 'bb'}) + mock_inst = EnumMock({"MOCK:A?": "aa", "MOCK:B?": "bb"}) assert mock_inst.a == SillyEnum.a assert mock_inst.b == SillyEnum.b # Test EnumValues, string values and string names. mock_inst.a = SillyEnum.b - mock_inst.b = 'a' - mock_inst.b = 'bb' + mock_inst.b = "a" + mock_inst.b = "bb" - assert mock_inst.value == 'MOCK:A?\nMOCK:B?\nMOCK:A bb\nMOCK:B aa\nMOCK:B bb\n' + assert mock_inst.value == "MOCK:A?\nMOCK:B?\nMOCK:A bb\nMOCK:B aa\nMOCK:B bb\n" def test_enum_property_invalid(): with pytest.raises(ValueError): + class SillyEnum(Enum): - a = 'aa' - b = 'bb' + a = "aa" + b = "bb" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum) + a = enum_property("MOCK:A", SillyEnum) - mock_inst = EnumMock({'MOCK:A?': 'aa', 'MOCK:B?': 'bb'}) + mock_inst = EnumMock({"MOCK:A?": "aa", "MOCK:B?": "bb"}) - mock_inst.a = 'c' + mock_inst.a = "c" def test_enum_property_set_fmt(): class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, set_fmt="{}={}") + a = enum_property("MOCK:A", SillyEnum, set_fmt="{}={}") mock_instrument = EnumMock() - mock_instrument.a = 'aa' - assert mock_instrument.value == 'MOCK:A=aa\n' + mock_instrument.a = "aa" + assert mock_instrument.value == "MOCK:A=aa\n" def test_enum_property_input_decoration(): class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - @staticmethod def _input_decorator(_): - return 'aa' - a = enum_property( - 'MOCK:A', - SillyEnum, - input_decoration=_input_decorator - ) + return "aa" - mock_instrument = EnumMock({'MOCK:A?': 'garbage'}) + a = enum_property("MOCK:A", SillyEnum, input_decoration=_input_decorator) + + mock_instrument = EnumMock({"MOCK:A?": "garbage"}) assert mock_instrument.a == SillyEnum.a @@ -93,65 +90,54 @@ class SillyEnum(IntEnum): class EnumMock(MockInstrument): - a = enum_property( - 'MOCK:A', - SillyEnum, - input_decoration=int - ) + a = enum_property("MOCK:A", SillyEnum, input_decoration=int) - mock_instrument = EnumMock({'MOCK:A?': '1'}) + mock_instrument = EnumMock({"MOCK:A?": "1"}) assert mock_instrument.a == SillyEnum.a def test_enum_property_output_decoration(): class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - @staticmethod def _output_decorator(_): - return 'foobar' - a = enum_property( - 'MOCK:A', - SillyEnum, - output_decoration=_output_decorator - ) + return "foobar" + + a = enum_property("MOCK:A", SillyEnum, output_decoration=_output_decorator) mock_instrument = EnumMock() mock_instrument.a = SillyEnum.a - assert mock_instrument.value == 'MOCK:A foobar\n' + assert mock_instrument.value == "MOCK:A foobar\n" def test_enum_property_output_decoration_not_a_function(): class SillyEnum(Enum): - a = '.23' + a = ".23" class EnumMock(MockInstrument): - a = enum_property( - 'MOCK:A', - SillyEnum, - output_decoration=float - ) + a = enum_property("MOCK:A", SillyEnum, output_decoration=float) mock_instrument = EnumMock() mock_instrument.a = SillyEnum.a - assert mock_instrument.value == 'MOCK:A 0.23\n' + assert mock_instrument.value == "MOCK:A 0.23\n" def test_enum_property_writeonly_reading_fails(): with pytest.raises(AttributeError): + class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, writeonly=True) + a = enum_property("MOCK:A", SillyEnum, writeonly=True) mock_instrument = EnumMock() @@ -160,53 +146,54 @@ class EnumMock(MockInstrument): def test_enum_property_writeonly_writing_passes(): class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, writeonly=True) + a = enum_property("MOCK:A", SillyEnum, writeonly=True) mock_instrument = EnumMock() mock_instrument.a = SillyEnum.a - assert mock_instrument.value == 'MOCK:A aa\n' + assert mock_instrument.value == "MOCK:A aa\n" def test_enum_property_readonly_writing_fails(): with pytest.raises(AttributeError): + class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, readonly=True) + a = enum_property("MOCK:A", SillyEnum, readonly=True) - mock_instrument = EnumMock({'MOCK:A?': 'aa'}) + mock_instrument = EnumMock({"MOCK:A?": "aa"}) mock_instrument.a = SillyEnum.a def test_enum_property_readonly_reading_passes(): class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, readonly=True) + a = enum_property("MOCK:A", SillyEnum, readonly=True) - mock_instrument = EnumMock({'MOCK:A?': 'aa'}) + mock_instrument = EnumMock({"MOCK:A?": "aa"}) assert mock_instrument.a == SillyEnum.a - assert mock_instrument.value == 'MOCK:A?\n' + assert mock_instrument.value == "MOCK:A?\n" def test_enum_property_set_cmd(): class SillyEnum(Enum): - a = 'aa' + a = "aa" class EnumMock(MockInstrument): - a = enum_property('MOCK:A', SillyEnum, set_cmd='FOOBAR:A') + a = enum_property("MOCK:A", SillyEnum, set_cmd="FOOBAR:A") - mock_inst = EnumMock({'MOCK:A?': 'aa'}) + mock_inst = EnumMock({"MOCK:A?": "aa"}) assert mock_inst.a == SillyEnum.a mock_inst.a = SillyEnum.a - assert mock_inst.value == 'MOCK:A?\nFOOBAR:A aa\n' + assert mock_inst.value == "MOCK:A?\nFOOBAR:A aa\n" diff --git a/instruments/tests/test_property_factories/test_int_property.py b/instruments/tests/test_property_factories/test_int_property.py index fd0bb8fdc..26d083358 100644 --- a/instruments/tests/test_property_factories/test_int_property.py +++ b/instruments/tests/test_property_factories/test_int_property.py @@ -19,8 +19,9 @@ def test_int_property_outside_valid_set(): with pytest.raises(ValueError): + class IntMock(MockInstrument): - mock_property = int_property('MOCK', valid_set=set([1, 2])) + mock_property = int_property("MOCK", valid_set=set([1, 2])) mock_inst = IntMock() mock_inst.mock_property = 3 @@ -28,31 +29,32 @@ class IntMock(MockInstrument): def test_int_property_valid_set(): class IntMock(MockInstrument): - int_property = int_property('MOCK', valid_set=set([1, 2])) + int_property = int_property("MOCK", valid_set=set([1, 2])) - mock_inst = IntMock({'MOCK?': '1'}) + mock_inst = IntMock({"MOCK?": "1"}) assert mock_inst.int_property == 1 mock_inst.int_property = 2 - assert mock_inst.value == 'MOCK?\nMOCK 2\n' + assert mock_inst.value == "MOCK?\nMOCK 2\n" def test_int_property_no_set(): class IntMock(MockInstrument): - int_property = int_property('MOCK') + int_property = int_property("MOCK") mock_inst = IntMock() mock_inst.int_property = 1 - assert mock_inst.value == 'MOCK 1\n' + assert mock_inst.value == "MOCK 1\n" def test_int_property_writeonly_reading_fails(): with pytest.raises(AttributeError): + class IntMock(MockInstrument): - int_property = int_property('MOCK', writeonly=True) + int_property = int_property("MOCK", writeonly=True) mock_inst = IntMock() @@ -61,50 +63,51 @@ class IntMock(MockInstrument): def test_int_property_writeonly_writing_passes(): class IntMock(MockInstrument): - int_property = int_property('MOCK', writeonly=True) + int_property = int_property("MOCK", writeonly=True) mock_inst = IntMock() mock_inst.int_property = 1 - assert mock_inst.value == 'MOCK {:d}\n'.format(1) + assert mock_inst.value == "MOCK {:d}\n".format(1) def test_int_property_readonly_writing_fails(): with pytest.raises(AttributeError): + class IntMock(MockInstrument): - int_property = int_property('MOCK', readonly=True) + int_property = int_property("MOCK", readonly=True) - mock_inst = IntMock({'MOCK?': '1'}) + mock_inst = IntMock({"MOCK?": "1"}) mock_inst.int_property = 1 def test_int_property_readonly_reading_passes(): class IntMock(MockInstrument): - int_property = int_property('MOCK', readonly=True) + int_property = int_property("MOCK", readonly=True) - mock_inst = IntMock({'MOCK?': '1'}) + mock_inst = IntMock({"MOCK?": "1"}) assert mock_inst.int_property == 1 def test_int_property_format_code(): class IntMock(MockInstrument): - int_property = int_property('MOCK', format_code='{:e}') + int_property = int_property("MOCK", format_code="{:e}") mock_inst = IntMock() mock_inst.int_property = 1 - assert mock_inst.value == 'MOCK {:e}\n'.format(1) + assert mock_inst.value == "MOCK {:e}\n".format(1) def test_int_property_set_cmd(): class IntMock(MockInstrument): - int_property = int_property('MOCK', set_cmd='FOOBAR') + int_property = int_property("MOCK", set_cmd="FOOBAR") - mock_inst = IntMock({'MOCK?': '1'}) + mock_inst = IntMock({"MOCK?": "1"}) assert mock_inst.int_property == 1 mock_inst.int_property = 1 - assert mock_inst.value == 'MOCK?\nFOOBAR 1\n' + assert mock_inst.value == "MOCK?\nFOOBAR 1\n" diff --git a/instruments/tests/test_property_factories/test_rproperty.py b/instruments/tests/test_property_factories/test_rproperty.py index 06b09da3b..56362a0a2 100644 --- a/instruments/tests/test_property_factories/test_rproperty.py +++ b/instruments/tests/test_property_factories/test_rproperty.py @@ -20,7 +20,6 @@ def test_rproperty_basic(): class Mock(MockInstrument): - def __init__(self): super(Mock, self).__init__() self._value = 0 @@ -30,6 +29,7 @@ def mockget(self): def mockset(self, newval): self._value = newval + mockproperty = rproperty(fget=mockget, fset=mockset) mock_inst = Mock() @@ -39,14 +39,15 @@ def mockset(self, newval): def test_rproperty_readonly_writing_fails(): with pytest.raises(AttributeError): - class Mock(MockInstrument): + class Mock(MockInstrument): def __init__(self): super(Mock, self).__init__() self._value = 0 def mockset(self, newval): # pragma: no cover self._value = newval + mockproperty = rproperty(fget=None, fset=mockset, readonly=True) mock_inst = Mock() @@ -55,13 +56,13 @@ def mockset(self, newval): # pragma: no cover def test_rproperty_readonly_reading_passes(): class Mock(MockInstrument): - def __init__(self): super(Mock, self).__init__() self._value = 0 def mockget(self): return self._value + mockproperty = rproperty(fget=mockget, fset=None, readonly=True) mock_inst = Mock() @@ -70,14 +71,15 @@ def mockget(self): def test_rproperty_writeonly_reading_fails(): with pytest.raises(AttributeError): - class Mock(MockInstrument): + class Mock(MockInstrument): def __init__(self): super(Mock, self).__init__() self._value = 0 def mockget(self): # pragma: no cover return self._value + mockproperty = rproperty(fget=mockget, fset=None, writeonly=True) mock_inst = Mock() @@ -86,13 +88,13 @@ def mockget(self): # pragma: no cover def test_rproperty_writeonly_writing_passes(): class Mock(MockInstrument): - def __init__(self): super(Mock, self).__init__() self._value = 0 def mockset(self, newval): self._value = newval + mockproperty = rproperty(fget=None, fset=mockset, writeonly=True) mock_inst = Mock() diff --git a/instruments/tests/test_property_factories/test_string_property.py b/instruments/tests/test_property_factories/test_string_property.py index 9af227f13..d856584bc 100644 --- a/instruments/tests/test_property_factories/test_string_property.py +++ b/instruments/tests/test_property_factories/test_string_property.py @@ -17,47 +17,47 @@ def test_string_property_basics(): class StringMock(MockInstrument): - mock_property = string_property('MOCK') + mock_property = string_property("MOCK") - mock_inst = StringMock({'MOCK?': '"foobar"'}) + mock_inst = StringMock({"MOCK?": '"foobar"'}) - assert mock_inst.mock_property == 'foobar' + assert mock_inst.mock_property == "foobar" - mock_inst.mock_property = 'foo' + mock_inst.mock_property = "foo" assert mock_inst.value == 'MOCK?\nMOCK "foo"\n' def test_string_property_different_bookmark_symbol(): class StringMock(MockInstrument): - mock_property = string_property('MOCK', bookmark_symbol='%^') + mock_property = string_property("MOCK", bookmark_symbol="%^") - mock_inst = StringMock({'MOCK?': '%^foobar%^'}) + mock_inst = StringMock({"MOCK?": "%^foobar%^"}) - assert mock_inst.mock_property == 'foobar' + assert mock_inst.mock_property == "foobar" - mock_inst.mock_property = 'foo' - assert mock_inst.value == 'MOCK?\nMOCK %^foo%^\n' + mock_inst.mock_property = "foo" + assert mock_inst.value == "MOCK?\nMOCK %^foo%^\n" def test_string_property_no_bookmark_symbol(): class StringMock(MockInstrument): - mock_property = string_property('MOCK', bookmark_symbol='') + mock_property = string_property("MOCK", bookmark_symbol="") - mock_inst = StringMock({'MOCK?': 'foobar'}) + mock_inst = StringMock({"MOCK?": "foobar"}) - assert mock_inst.mock_property == 'foobar' + assert mock_inst.mock_property == "foobar" - mock_inst.mock_property = 'foo' - assert mock_inst.value == 'MOCK?\nMOCK foo\n' + mock_inst.mock_property = "foo" + assert mock_inst.value == "MOCK?\nMOCK foo\n" def test_string_property_set_cmd(): class StringMock(MockInstrument): - mock_property = string_property('MOCK', set_cmd='FOOBAR') + mock_property = string_property("MOCK", set_cmd="FOOBAR") - mock_inst = StringMock({'MOCK?': '"derp"'}) + mock_inst = StringMock({"MOCK?": '"derp"'}) - assert mock_inst.mock_property == 'derp' + assert mock_inst.mock_property == "derp" - mock_inst.mock_property = 'qwerty' + mock_inst.mock_property = "qwerty" assert mock_inst.value == 'MOCK?\nFOOBAR "qwerty"\n' diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index aedc31a79..fb2fd59c3 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -21,51 +21,51 @@ def test_unitful_property_basics(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', units=u.hertz) + unitful_property = unitful_property("MOCK", units=u.hertz) - mock_inst = UnitfulMock({'MOCK?': '1000'}) + mock_inst = UnitfulMock({"MOCK?": "1000"}) assert mock_inst.unitful_property == 1000 * u.hertz mock_inst.unitful_property = 1000 * u.hertz - assert mock_inst.value == 'MOCK?\nMOCK {:e}\n'.format(1000) + assert mock_inst.value == "MOCK?\nMOCK {:e}\n".format(1000) def test_unitful_property_format_code(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', u.hertz, format_code='{:f}') + unitful_property = unitful_property("MOCK", u.hertz, format_code="{:f}") mock_inst = UnitfulMock() mock_inst.unitful_property = 1000 * u.hertz - assert mock_inst.value == 'MOCK {:f}\n'.format(1000) + assert mock_inst.value == "MOCK {:f}\n".format(1000) def test_unitful_property_rescale_units(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz) + unitful_property = unitful_property("MOCK", u.hertz) mock_inst = UnitfulMock() mock_inst.unitful_property = 1 * u.kilohertz - assert mock_inst.value == 'MOCK {:e}\n'.format(1000) + assert mock_inst.value == "MOCK {:e}\n".format(1000) def test_unitful_property_no_units_on_set(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz) + unitful_property = unitful_property("MOCK", u.hertz) mock_inst = UnitfulMock() mock_inst.unitful_property = 1000 - assert mock_inst.value == 'MOCK {:e}\n'.format(1000) + assert mock_inst.value == "MOCK {:e}\n".format(1000) def test_unitful_property_wrong_units(): with pytest.raises(pint.errors.DimensionalityError): + class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz) + unitful_property = unitful_property("MOCK", u.hertz) mock_inst = UnitfulMock() @@ -74,8 +74,9 @@ class UnitfulMock(MockInstrument): def test_unitful_property_writeonly_reading_fails(): with pytest.raises(AttributeError): + class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz, writeonly=True) + unitful_property = unitful_property("MOCK", u.hertz, writeonly=True) mock_inst = UnitfulMock() @@ -84,49 +85,48 @@ class UnitfulMock(MockInstrument): def test_unitful_property_writeonly_writing_passes(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz, writeonly=True) + unitful_property = unitful_property("MOCK", u.hertz, writeonly=True) mock_inst = UnitfulMock() mock_inst.unitful_property = 1 * u.hertz - assert mock_inst.value == 'MOCK {:e}\n'.format(1) + assert mock_inst.value == "MOCK {:e}\n".format(1) def test_unitful_property_readonly_writing_fails(): with pytest.raises(AttributeError): + class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz, readonly=True) + unitful_property = unitful_property("MOCK", u.hertz, readonly=True) - mock_inst = UnitfulMock({'MOCK?': '1'}) + mock_inst = UnitfulMock({"MOCK?": "1"}) mock_inst.unitful_property = 1 * u.hertz def test_unitful_property_readonly_reading_passes(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property('MOCK', u.hertz, readonly=True) + unitful_property = unitful_property("MOCK", u.hertz, readonly=True) - mock_inst = UnitfulMock({'MOCK?': '1'}) + mock_inst = UnitfulMock({"MOCK?": "1"}) assert mock_inst.unitful_property == 1 * u.hertz def test_unitful_property_valid_range(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', u.hertz, valid_range=(0, 10)) + unitful_property = unitful_property("MOCK", u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock() mock_inst.unitful_property = 0 mock_inst.unitful_property = 10 - assert mock_inst.value == 'MOCK {:e}\nMOCK {:e}\n'.format(0, 10) + assert mock_inst.value == "MOCK {:e}\nMOCK {:e}\n".format(0, 10) def test_unitful_property_valid_range_functions(): class UnitfulMock(MockInstrument): - def min_value(self): return 0 @@ -134,21 +134,22 @@ def max_value(self): return 10 unitful_property = unitful_property( - 'MOCK', u.hertz, valid_range=(min_value, max_value)) + "MOCK", u.hertz, valid_range=(min_value, max_value) + ) mock_inst = UnitfulMock() mock_inst.unitful_property = 0 mock_inst.unitful_property = 10 - assert mock_inst.value == 'MOCK {:e}\nMOCK {:e}\n'.format(0, 10) + assert mock_inst.value == "MOCK {:e}\nMOCK {:e}\n".format(0, 10) def test_unitful_property_minimum_value(): with pytest.raises(ValueError): + class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', u.hertz, valid_range=(0, 10)) + unitful_property = unitful_property("MOCK", u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock() @@ -157,9 +158,9 @@ class UnitfulMock(MockInstrument): def test_unitful_property_maximum_value(): with pytest.raises(ValueError): + class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', u.hertz, valid_range=(0, 10)) + unitful_property = unitful_property("MOCK", u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock() @@ -168,17 +169,13 @@ class UnitfulMock(MockInstrument): def test_unitful_property_input_decoration(): class UnitfulMock(MockInstrument): - @staticmethod def _input_decorator(_): - return '1' - a = unitful_property( - 'MOCK:A', - u.hertz, - input_decoration=_input_decorator - ) + return "1" + + a = unitful_property("MOCK:A", u.hertz, input_decoration=_input_decorator) - mock_instrument = UnitfulMock({'MOCK:A?': 'garbage'}) + mock_instrument = UnitfulMock({"MOCK:A?": "garbage"}) assert mock_instrument.a == 1 * u.Hz @@ -186,56 +183,43 @@ def _input_decorator(_): def test_unitful_property_input_decoration_not_a_function(): class UnitfulMock(MockInstrument): - a = unitful_property( - 'MOCK:A', - u.hertz, - input_decoration=float - ) + a = unitful_property("MOCK:A", u.hertz, input_decoration=float) - mock_instrument = UnitfulMock({'MOCK:A?': '.123'}) + mock_instrument = UnitfulMock({"MOCK:A?": ".123"}) assert mock_instrument.a == 0.123 * u.Hz def test_unitful_property_output_decoration(): class UnitfulMock(MockInstrument): - @staticmethod def _output_decorator(_): - return '1' - a = unitful_property( - 'MOCK:A', - u.hertz, - output_decoration=_output_decorator - ) + return "1" + + a = unitful_property("MOCK:A", u.hertz, output_decoration=_output_decorator) mock_instrument = UnitfulMock() mock_instrument.a = 345 * u.hertz - assert mock_instrument.value == 'MOCK:A 1\n' + assert mock_instrument.value == "MOCK:A 1\n" def test_unitful_property_output_decoration_not_a_function(): class UnitfulMock(MockInstrument): - a = unitful_property( - 'MOCK:A', - u.hertz, - output_decoration=bool - ) + a = unitful_property("MOCK:A", u.hertz, output_decoration=bool) mock_instrument = UnitfulMock() mock_instrument.a = 1 * u.hertz - assert mock_instrument.value == 'MOCK:A True\n' + assert mock_instrument.value == "MOCK:A True\n" def test_unitful_property_split_str(): class UnitfulMock(MockInstrument): - unitful_property = unitful_property( - 'MOCK', u.hertz, valid_range=(0, 10)) + unitful_property = unitful_property("MOCK", u.hertz, valid_range=(0, 10)) mock_inst = UnitfulMock({"MOCK?": "1 kHz"}) @@ -246,14 +230,10 @@ class UnitfulMock(MockInstrument): def test_unitful_property_name_read_not_none(): class UnitfulMock(MockInstrument): - a = unitful_property( - 'MOCK', - units=u.hertz, - set_cmd='FOOBAR' - ) + a = unitful_property("MOCK", units=u.hertz, set_cmd="FOOBAR") - mock_inst = UnitfulMock({'MOCK?': '1000'}) + mock_inst = UnitfulMock({"MOCK?": "1000"}) assert mock_inst.a == 1000 * u.hertz mock_inst.a = 1000 * u.hertz - assert mock_inst.value == 'MOCK?\nFOOBAR {:e}\n'.format(1000) + assert mock_inst.value == "MOCK?\nFOOBAR {:e}\n".format(1000) diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index bbf7f67d7..32412649c 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -20,40 +20,42 @@ def test_unitless_property_basics(): class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK') + mock_property = unitless_property("MOCK") - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({"MOCK?": "1"}) assert mock_inst.mock_property == 1 mock_inst.mock_property = 1 - assert mock_inst.value == 'MOCK?\nMOCK {:e}\n'.format(1) + assert mock_inst.value == "MOCK?\nMOCK {:e}\n".format(1) def test_unitless_property_units(): with pytest.raises(ValueError): + class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK') + mock_property = unitless_property("MOCK") - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({"MOCK?": "1"}) mock_inst.mock_property = 1 * u.volt def test_unitless_property_format_code(): class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', format_code='{:f}') + mock_property = unitless_property("MOCK", format_code="{:f}") mock_inst = UnitlessMock() mock_inst.mock_property = 1 - assert mock_inst.value == 'MOCK {:f}\n'.format(1) + assert mock_inst.value == "MOCK {:f}\n".format(1) def test_unitless_property_writeonly_reading_fails(): with pytest.raises(AttributeError): + class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', writeonly=True) + mock_property = unitless_property("MOCK", writeonly=True) mock_inst = UnitlessMock() @@ -62,40 +64,41 @@ class UnitlessMock(MockInstrument): def test_unitless_property_writeonly_writing_passes(): class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', writeonly=True) + mock_property = unitless_property("MOCK", writeonly=True) mock_inst = UnitlessMock() mock_inst.mock_property = 1 - assert mock_inst.value == 'MOCK {:e}\n'.format(1) + assert mock_inst.value == "MOCK {:e}\n".format(1) def test_unitless_property_readonly_writing_fails(): with pytest.raises(AttributeError): + class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', readonly=True) + mock_property = unitless_property("MOCK", readonly=True) - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({"MOCK?": "1"}) mock_inst.mock_property = 1 def test_unitless_property_readonly_reading_passes(): class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', readonly=True) + mock_property = unitless_property("MOCK", readonly=True) - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({"MOCK?": "1"}) assert mock_inst.mock_property == 1 def test_unitless_property_set_cmd(): class UnitlessMock(MockInstrument): - mock_property = unitless_property('MOCK', set_cmd='FOOBAR') + mock_property = unitless_property("MOCK", set_cmd="FOOBAR") - mock_inst = UnitlessMock({'MOCK?': '1'}) + mock_inst = UnitlessMock({"MOCK?": "1"}) assert mock_inst.mock_property == 1 mock_inst.mock_property = 1 - assert mock_inst.value == 'MOCK?\nFOOBAR {:e}\n'.format(1) + assert mock_inst.value == "MOCK?\nFOOBAR {:e}\n".format(1) diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index 9f9a963db..a1f75306d 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -24,7 +24,7 @@ def test_init_os_error(mocker): """ stdout = BytesIO(":ACKN OF\nFIRM?\n".encode("utf-8")) stdin = BytesIO("Firmware v2.010\n".encode("utf-8")) - mock_read = mocker.patch.object(ik.qubitekk.CC1, 'read') + mock_read = mocker.patch.object(ik.qubitekk.CC1, "read") mock_read.side_effect = OSError _ = ik.qubitekk.CC1.open_test(stdin, stdout) mock_read.assert_called_with(-1) @@ -32,41 +32,20 @@ def test_init_os_error(mocker): def test_cc1_count(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "COUN:C1?" - ], - [ - "", - "Firmware v2.010", - "20" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "COUN:C1?"], + ["", "Firmware v2.010", "20"], + sep="\n", ) as cc: assert cc.channel[0].count == 20.0 def test_cc1_count_valule_error(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "COUN:C1?" - ], - [ - "", - "Firmware v2.010", - "bad_count", - "try1" - "try2" - "try3" - "try4" - "try5" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "COUN:C1?"], + ["", "Firmware v2.010", "bad_count", "try1" "try2" "try3" "try4" "try5"], + sep="\n", ) as cc: with pytest.raises(IOError) as err_info: _ = cc.channel[0].count @@ -76,19 +55,14 @@ def test_cc1_count_valule_error(): def test_cc1_window(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "WIND?", - ":WIND 7" - ], - [ - "", - "Firmware v2.010", - "2", - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "WIND?", ":WIND 7"], + [ + "", + "Firmware v2.010", + "2", + ], + sep="\n", ) as cc: unit_eq(cc.window, u.Quantity(2, "ns")) cc.window = 7 @@ -96,37 +70,20 @@ def test_cc1_window(): def test_cc1_window_error(): with pytest.raises(ValueError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":WIND 10" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":WIND 10"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.window = 10 def test_cc1_delay(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "DELA?", - ":DELA 2" - ], - [ - "", - "Firmware v2.010", - "8", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "DELA?", ":DELA 2"], + ["", "Firmware v2.010", "8", ""], + sep="\n", ) as cc: unit_eq(cc.delay, u.Quantity(8, "ns")) cc.delay = 2 @@ -134,54 +91,30 @@ def test_cc1_delay(): def test_cc1_delay_error1(): with pytest.raises(ValueError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":DELA -1" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":DELA -1"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.delay = -1 def test_cc1_delay_error2(): with pytest.raises(ValueError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":DELA 1" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":DELA 1"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.delay = 1 def test_cc1_dwell_old_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "DWEL?", - ":DWEL 2" - ], - [ - "Unknown Command", - "Firmware v2.001", - "8000", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "DWEL?", ":DWEL 2"], + ["Unknown Command", "Firmware v2.001", "8000", ""], + sep="\n", ) as cc: unit_eq(cc.dwell_time, u.Quantity(8, "s")) cc.dwell_time = 2 @@ -189,19 +122,10 @@ def test_cc1_dwell_old_firmware(): def test_cc1_dwell_new_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "DWEL?", - ":DWEL 2" - ], - [ - "", - "Firmware v2.010", - "8" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "DWEL?", ":DWEL 2"], + ["", "Firmware v2.010", "8"], + sep="\n", ) as cc: unit_eq(cc.dwell_time, u.Quantity(8, "s")) cc.dwell_time = 2 @@ -209,103 +133,56 @@ def test_cc1_dwell_new_firmware(): def test_cc1_dwell_time_error(): with pytest.raises(ValueError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":DWEL -1" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":DWEL -1"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.dwell_time = -1 def test_cc1_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, [":ACKN OF", "FIRM?"], ["", "Firmware v2.010"], sep="\n" ) as cc: assert cc.firmware == (2, 10, 0) def test_cc1_firmware_2(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?"], + ["Unknown Command", "Firmware v2"], + sep="\n", ) as cc: assert cc.firmware == (2, 0, 0) def test_cc1_firmware_3(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2.010.1" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?"], + ["Unknown Command", "Firmware v2.010.1"], + sep="\n", ) as cc: assert cc.firmware == (2, 10, 1) def test_cc1_firmware_repeat_query(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "FIRM?" - ], - [ - "Unknown Command", - "Unknown", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "FIRM?"], + ["Unknown Command", "Unknown", "Firmware v2.010"], + sep="\n", ) as cc: assert cc.firmware == (2, 10, 0) def test_cc1_gate_new_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "GATE?", - ":GATE:ON", - ":GATE:OFF" - - ], - [ - "", - "Firmware v2.010", - "ON" - ], + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "GATE?", ":GATE:ON", ":GATE:OFF"], + ["", "Firmware v2.010", "ON"], ) as cc: assert cc.gate is True cc.gate = True @@ -314,23 +191,10 @@ def test_cc1_gate_new_firmware(): def test_cc1_gate_old_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "GATE?", - ":GATE 1", - ":GATE 0" - - ], - [ - "Unknown Command", - "Firmware v2.001", - "1", - "", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "GATE?", ":GATE 1", ":GATE 0"], + ["Unknown Command", "Firmware v2.001", "1", "", ""], + sep="\n", ) as cc: assert cc.gate is True cc.gate = True @@ -339,39 +203,20 @@ def test_cc1_gate_old_firmware(): def test_cc1_gate_error(): with pytest.raises(TypeError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":GATE blo" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":GATE blo"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.gate = "blo" def test_cc1_subtract_new_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "SUBT?", - ":SUBT:ON", - ":SUBT:OFF" - - ], - [ - "", - "Firmware v2.010", - "ON", - ":SUBT:OFF" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "SUBT?", ":SUBT:ON", ":SUBT:OFF"], + ["", "Firmware v2.010", "ON", ":SUBT:OFF"], + sep="\n", ) as cc: assert cc.subtract is True cc.subtract = True @@ -380,38 +225,20 @@ def test_cc1_subtract_new_firmware(): def test_cc1_subtract_error(): with pytest.raises(TypeError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":SUBT blo" - - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":SUBT blo"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.subtract = "blo" def test_cc1_trigger_mode(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "TRIG?", - ":TRIG:MODE CONT", - ":TRIG:MODE STOP" - ], - [ - "", - "Firmware v2.010", - "MODE STOP" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "TRIG?", ":TRIG:MODE CONT", ":TRIG:MODE STOP"], + ["", "Firmware v2.010", "MODE STOP"], + sep="\n", ) as cc: assert cc.trigger_mode is cc.TriggerMode.start_stop cc.trigger_mode = cc.TriggerMode.continuous @@ -420,22 +247,10 @@ def test_cc1_trigger_mode(): def test_cc1_trigger_mode_old_firmware(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "TRIG?", - ":TRIG 0", - ":TRIG 1" - ], - [ - "Unknown Command", - "Firmware v2.001", - "1", - "", - "" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "TRIG?", ":TRIG 0", ":TRIG 1"], + ["Unknown Command", "Firmware v2.001", "1", "", ""], + sep="\n", ) as cc: assert cc.trigger_mode == cc.TriggerMode.start_stop cc.trigger_mode = cc.TriggerMode.continuous @@ -444,56 +259,27 @@ def test_cc1_trigger_mode_old_firmware(): def test_cc1_trigger_mode_error(): with pytest.raises(ValueError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, [":ACKN OF", "FIRM?"], ["", "Firmware v2.010"], sep="\n" ) as cc: cc.trigger_mode = "blo" def test_cc1_clear(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - "CLEA" - ], - [ - "", - "Firmware v2.010" - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", "CLEA"], + ["", "Firmware v2.010"], + sep="\n", ) as cc: cc.clear_counts() def test_acknowledge(): with expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?", - ":ACKN ON", - "CLEA", - ":ACKN OF", - "CLEA" - ], - [ - "", - "Firmware v2.010", - "CLEA", - ":ACKN OF" - - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?", ":ACKN ON", "CLEA", ":ACKN OF", "CLEA"], + ["", "Firmware v2.010", "CLEA", ":ACKN OF"], + sep="\n", ) as cc: assert not cc.acknowledge cc.acknowledge = True @@ -506,33 +292,19 @@ def test_acknowledge(): def test_acknowledge_notimplementederror(): with pytest.raises(NotImplementedError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2.001" - - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?"], + ["Unknown Command", "Firmware v2.001"], + sep="\n", ) as cc: cc.acknowledge = True def test_acknowledge_not_implemented_error(): # pylint: disable=protected-access with pytest.raises(NotImplementedError), expected_protocol( - ik.qubitekk.CC1, - [ - ":ACKN OF", - "FIRM?" - ], - [ - "Unknown Command", - "Firmware v2.001" - - ], - sep="\n" + ik.qubitekk.CC1, + [":ACKN OF", "FIRM?"], + ["Unknown Command", "Firmware v2.001"], + sep="\n", ) as cc: cc.acknowledge = True diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index 7eb19b53a..d448be39f 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -18,30 +18,21 @@ def test_mc1_increment(): - with expected_protocol( - ik.qubitekk.MC1, - [], [], sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, [], [], sep="\r") as mc: assert mc.increment == 1 * u.ms mc.increment = 3 * u.ms assert mc.increment == 3 * u.ms def test_mc1_lower_limit(): - with expected_protocol( - ik.qubitekk.MC1, - [], [], sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, [], [], sep="\r") as mc: assert mc.lower_limit == -300 * u.ms mc.lower_limit = -400 * u.ms assert mc.lower_limit == -400 * u.ms def test_mc1_upper_limit(): - with expected_protocol( - ik.qubitekk.MC1, - [], [], sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, [], [], sep="\r") as mc: assert mc.upper_limit == 300 * u.ms mc.upper_limit = 400 * u.ms assert mc.upper_limit == 400 * u.ms @@ -49,15 +40,7 @@ def test_mc1_upper_limit(): def test_mc1_setting(): with expected_protocol( - ik.qubitekk.MC1, - [ - "OUTP?", - ":OUTP 0" - ], - [ - "1" - ], - sep="\r" + ik.qubitekk.MC1, ["OUTP?", ":OUTP 0"], ["1"], sep="\r" ) as mc: assert mc.setting == 1 mc.setting = 0 @@ -65,189 +48,83 @@ def test_mc1_setting(): def test_mc1_internal_position(): with expected_protocol( - ik.qubitekk.MC1, - [ - "POSI?", - "STEP?" - - ], - [ - "-100", - "1" - ], - sep="\r" + ik.qubitekk.MC1, ["POSI?", "STEP?"], ["-100", "1"], sep="\r" ) as mc: - assert mc.internal_position == -100*u.ms + assert mc.internal_position == -100 * u.ms def test_mc1_metric_position(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "METR?" - ], - [ - "-3.14159" - ], - sep="\r" - ) as mc: - assert mc.metric_position == -3.14159*u.mm + with expected_protocol(ik.qubitekk.MC1, ["METR?"], ["-3.14159"], sep="\r") as mc: + assert mc.metric_position == -3.14159 * u.mm def test_mc1_direction(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "DIRE?" - ], - [ - "-100" - ], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["DIRE?"], ["-100"], sep="\r") as mc: assert mc.direction == -100 * u.ms def test_mc1_firmware(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "FIRM?" - ], - [ - "1.0.1" - ], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["FIRM?"], ["1.0.1"], sep="\r") as mc: assert mc.firmware == (1, 0, 1) def test_mc1_firmware_no_patch_info(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "FIRM?" - ], - [ - "1.0" - ], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["FIRM?"], ["1.0"], sep="\r") as mc: assert mc.firmware == (1, 0, 0) def test_mc1_inertia(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "INER?" - ], - [ - "20" - ], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["INER?"], ["20"], sep="\r") as mc: assert mc.inertia == 20 * u.ms def test_mc1_step(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "STEP?" - ], - [ - "20" - ], - sep="\r" - ) as mc: - assert mc.step_size == 20*u.ms + with expected_protocol(ik.qubitekk.MC1, ["STEP?"], ["20"], sep="\r") as mc: + assert mc.step_size == 20 * u.ms def test_mc1_motor(): - with expected_protocol( - ik.qubitekk.MC1, - [ - "MOTO?" - ], - [ - "Radio" - ], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["MOTO?"], ["Radio"], sep="\r") as mc: assert mc.controller == mc.MotorType.radio def test_mc1_move_timeout(): with expected_protocol( - ik.qubitekk.MC1, - [ - "TIME?", - "STEP?" - ], - [ - "200", - "1" - ], - sep="\r" + ik.qubitekk.MC1, ["TIME?", "STEP?"], ["200", "1"], sep="\r" ) as mc: - assert mc.move_timeout == 200*u.ms + assert mc.move_timeout == 200 * u.ms def test_mc1_is_centering(): - with expected_protocol( - ik.qubitekk.MC1, - ["CENT?"], - ["1"], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["CENT?"], ["1"], sep="\r") as mc: assert mc.is_centering() is True def test_mc1_is_centering_false(): - with expected_protocol( - ik.qubitekk.MC1, - ["CENT?"], - ["0"], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, ["CENT?"], ["0"], sep="\r") as mc: assert mc.is_centering() is False def test_mc1_center(): - with expected_protocol( - ik.qubitekk.MC1, - [":CENT"], - [""], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, [":CENT"], [""], sep="\r") as mc: mc.center() def test_mc1_reset(): - with expected_protocol( - ik.qubitekk.MC1, - [":RESE"], - [""], - sep="\r" - ) as mc: + with expected_protocol(ik.qubitekk.MC1, [":RESE"], [""], sep="\r") as mc: mc.reset() def test_mc1_move(): with expected_protocol( - ik.qubitekk.MC1, - ["STEP?", ":MOVE 0"], - ["1"], - sep="\r" + ik.qubitekk.MC1, ["STEP?", ":MOVE 0"], ["1"], sep="\r" ) as mc: mc.move(0) def test_mc1_move_value_error(): with pytest.raises(ValueError) as exc_info, expected_protocol( - ik.qubitekk.MC1, - [], [], sep="\r" + ik.qubitekk.MC1, [], [], sep="\r" ) as mc: mc.move(-1000) exc_msg = exc_info.value.args[0] diff --git a/instruments/tests/test_rigol/test_rigolds1000.py b/instruments/tests/test_rigol/test_rigolds1000.py index e43ab19b2..ccc4035e5 100644 --- a/instruments/tests/test_rigol/test_rigolds1000.py +++ b/instruments/tests/test_rigol/test_rigolds1000.py @@ -29,13 +29,7 @@ def test_channel_initialization(): """Ensure correct initialization of channel object.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [], []) as osc: channel = osc.channel[0] assert channel._parent is osc assert channel._idx == 1 @@ -44,14 +38,7 @@ def test_channel_initialization(): def test_channel_coupling(): """Get / set channel coupling.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":CHAN1:COUP?", - ":CHAN2:COUP DC" - ], - [ - "AC" - ] + ik.rigol.RigolDS1000Series, [":CHAN1:COUP?", ":CHAN2:COUP DC"], ["AC"] ) as osc: assert osc.channel[0].coupling == osc.channel[0].Coupling.ac osc.channel[1].coupling = osc.channel[1].Coupling.dc @@ -60,14 +47,7 @@ def test_channel_coupling(): def test_channel_bw_limit(): """Get / set instrument bw limit.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":CHAN2:BWL?", - ":CHAN1:BWL ON" - ], - [ - "OFF" - ] + ik.rigol.RigolDS1000Series, [":CHAN2:BWL?", ":CHAN1:BWL ON"], ["OFF"] ) as osc: assert not osc.channel[1].bw_limit osc.channel[0].bw_limit = True @@ -76,14 +56,7 @@ def test_channel_bw_limit(): def test_channel_display(): """Get / set instrument display.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":CHAN2:DISP?", - ":CHAN1:DISP ON" - ], - [ - "OFF" - ] + ik.rigol.RigolDS1000Series, [":CHAN2:DISP?", ":CHAN1:DISP ON"], ["OFF"] ) as osc: assert not osc.channel[1].display osc.channel[0].display = True @@ -92,14 +65,7 @@ def test_channel_display(): def test_channel_invert(): """Get / set instrument invert.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":CHAN2:INV?", - ":CHAN1:INV ON" - ], - [ - "OFF" - ] + ik.rigol.RigolDS1000Series, [":CHAN2:INV?", ":CHAN1:INV ON"], ["OFF"] ) as osc: assert not osc.channel[1].invert osc.channel[0].invert = True @@ -108,14 +74,7 @@ def test_channel_invert(): def test_channel_filter(): """Get / set instrument filter.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":CHAN2:FILT?", - ":CHAN1:FILT ON" - ], - [ - "OFF" - ] + ik.rigol.RigolDS1000Series, [":CHAN2:FILT?", ":CHAN1:FILT ON"], ["OFF"] ) as osc: assert not osc.channel[1].filter osc.channel[0].filter = True @@ -124,14 +83,7 @@ def test_channel_filter(): def test_channel_vernier(): """Get / set instrument vernier.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":CHAN2:VERN?", - ":CHAN1:VERN ON" - ], - [ - "OFF" - ] + ik.rigol.RigolDS1000Series, [":CHAN2:VERN?", ":CHAN1:VERN ON"], ["OFF"] ) as osc: assert not osc.channel[1].vernier osc.channel[0].vernier = True @@ -139,26 +91,16 @@ def test_channel_vernier(): def test_channel_name(): """Get channel name - DataSource property.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [], []) as osc: assert osc.channel[0].name == "CHAN1" def test_channel_read_waveform(): """Read waveform of channel object.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":WAV:DATA? CHAN2" - ], - [ - b"#210" + bytes.fromhex("00000001000200030004") + b"0" - ] + ik.rigol.RigolDS1000Series, + [":WAV:DATA? CHAN2"], + [b"#210" + bytes.fromhex("00000001000200030004") + b"0"], ) as osc: expected = (0, 1, 2, 3, 4) if numpy: @@ -171,26 +113,16 @@ def test_channel_read_waveform(): def test_math_name(): """Ensure correct naming of math object.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [], []) as osc: assert osc.math.name == "MATH" def test_math_read_waveform(): """Read waveform of of math object.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":WAV:DATA? MATH" - ], - [ - b"#210" + bytes.fromhex("00000001000200030004") + b"0" - ] + ik.rigol.RigolDS1000Series, + [":WAV:DATA? MATH"], + [b"#210" + bytes.fromhex("00000001000200030004") + b"0"], ) as osc: expected = (0, 1, 2, 3, 4) if numpy: @@ -203,25 +135,13 @@ def test_math_read_waveform(): def test_ref_name(): """Ensure correct naming of ref object.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [], []) as osc: assert osc.ref.name == "REF" def test_ref_read_waveform_raises_error(): """Ensure error raising when reading waveform of REF channel.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [], []) as osc: with pytest.raises(NotImplementedError): osc.ref.read_waveform() @@ -232,14 +152,7 @@ def test_ref_read_waveform_raises_error(): def test_acquire_type(): """Get / Set acquire type.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":ACQ:TYPE?", - ":ACQ:TYPE PEAK" - ], - [ - "NORM" - ] + ik.rigol.RigolDS1000Series, [":ACQ:TYPE?", ":ACQ:TYPE PEAK"], ["NORM"] ) as osc: assert osc.acquire_type == osc.AcquisitionType.normal osc.acquire_type = osc.AcquisitionType.peak_detect @@ -248,14 +161,7 @@ def test_acquire_type(): def test_acquire_averages(): """Get / Set acquire averages.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":ACQ:AVER?", - ":ACQ:AVER 128" - ], - [ - "16" - ] + ik.rigol.RigolDS1000Series, [":ACQ:AVER?", ":ACQ:AVER 128"], ["16"] ) as osc: assert osc.acquire_averages == 16 osc.acquire_averages = 128 @@ -263,13 +169,7 @@ def test_acquire_averages(): def test_acquire_averages_bad_values(): """Raise error when bad values encountered.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [], []) as osc: with pytest.raises(ValueError): osc.acquire_averages = 0 with pytest.raises(ValueError): @@ -284,54 +184,26 @@ def test_acquire_averages_bad_values(): def test_force_trigger(): """Force a trigger.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":FORC" - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [":FORC"], []) as osc: osc.force_trigger() def test_run(): """Run the instrument.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":RUN" - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [":RUN"], []) as osc: osc.run() def test_stop(): """Stop the instrument.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":STOP" - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [":STOP"], []) as osc: osc.stop() def test_panel_locked(): """Get / set the panel_locked bool property.""" with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":KEY:LOCK?", - ":KEY:LOCK DIS" - ], - [ - "ENAB" - ] + ik.rigol.RigolDS1000Series, [":KEY:LOCK?", ":KEY:LOCK DIS"], ["ENAB"] ) as osc: assert osc.panel_locked osc.panel_locked = False @@ -339,12 +211,5 @@ def test_panel_locked(): def test_release_panel(): """Get / set the panel_locked bool property.""" - with expected_protocol( - ik.rigol.RigolDS1000Series, - [ - ":KEY:FORC" - ], - [ - ] - ) as osc: + with expected_protocol(ik.rigol.RigolDS1000Series, [":KEY:FORC"], []) as osc: osc.release_panel() diff --git a/instruments/tests/test_split_str.py b/instruments/tests/test_split_str.py index 963b5c49c..c6808af83 100644 --- a/instruments/tests/test_split_str.py +++ b/instruments/tests/test_split_str.py @@ -10,9 +10,7 @@ import pytest from instruments.units import ureg as u -from instruments.util_fns import ( - split_unit_str -) +from instruments.util_fns import split_unit_str # TEST CASES ################################################################# @@ -63,10 +61,7 @@ def test_split_unit_str_lookups(): This checks that the unit lookup parameter is correctly called, which can be used to map between units as string and their pyquantities equivalent. """ - unit_dict = { - "FOO": "foobars", - "SNA": "snafus" - } + unit_dict = {"FOO": "foobars", "SNA": "snafus"} mag, units = split_unit_str("42 FOO", lookup=unit_dict.__getitem__) assert mag == 42 assert units == "foobars" diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index 9a5985f6f..2883b89a9 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -19,15 +19,11 @@ def test_amplitude(): with expected_protocol( - ik.srs.SRS345, - [ - "AMPL?", - "AMPL 0.1VP", - "AMPL 0.1VR" - ], - [ - "1.234VP", - ] + ik.srs.SRS345, + ["AMPL?", "AMPL 0.1VP", "AMPL 0.1VR"], + [ + "1.234VP", + ], ) as inst: iterable_eq(inst.amplitude, (1.234 * u.V, inst.VoltageMode.peak_to_peak)) inst.amplitude = 0.1 * u.V @@ -36,14 +32,14 @@ def test_amplitude(): def test_frequency(): with expected_protocol( - ik.srs.SRS345, - [ - "FREQ?", - "FREQ {:e}".format(0.1), - ], - [ - "1.234", - ] + ik.srs.SRS345, + [ + "FREQ?", + "FREQ {:e}".format(0.1), + ], + [ + "1.234", + ], ) as inst: assert inst.frequency == 1.234 * u.Hz inst.frequency = 0.1 * u.Hz @@ -51,14 +47,11 @@ def test_frequency(): def test_function(): with expected_protocol( - ik.srs.SRS345, - [ - "FUNC?", - "FUNC 0" - ], - [ - "1", - ] + ik.srs.SRS345, + ["FUNC?", "FUNC 0"], + [ + "1", + ], ) as inst: assert inst.function == inst.Function.square inst.function = inst.Function.sinusoid @@ -66,14 +59,14 @@ def test_function(): def test_offset(): with expected_protocol( - ik.srs.SRS345, - [ - "OFFS?", - "OFFS {:e}".format(0.1), - ], - [ - "1.234", - ] + ik.srs.SRS345, + [ + "OFFS?", + "OFFS {:e}".format(0.1), + ], + [ + "1.234", + ], ) as inst: assert inst.offset == 1.234 * u.V inst.offset = 0.1 * u.V @@ -81,14 +74,14 @@ def test_offset(): def test_phase(): with expected_protocol( - ik.srs.SRS345, - [ - "PHSE?", - "PHSE {:e}".format(0.1), - ], - [ - "1.234", - ] + ik.srs.SRS345, + [ + "PHSE?", + "PHSE {:e}".format(0.1), + ], + [ + "1.234", + ], ) as inst: assert inst.phase == 1.234 * u.degree inst.phase = 0.1 * u.degree diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index c317df577..445fa3357 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -31,14 +31,14 @@ @pytest.fixture(autouse=True) def time_mock(mocker): """Mock out sleep such that test runs fast.""" - return mocker.patch.object(time, 'sleep', return_value=None) + return mocker.patch.object(time, "sleep", return_value=None) @pytest.mark.parametrize("mode", (1, 2)) def test_init_mode_given(mocker, mode): """Test initialization with a given mode.""" comm = LoopbackCommunicator() - send_spy = mocker.spy(comm, 'sendcmd') + send_spy = mocker.spy(comm, "sendcmd") ik.srs.SRS830(comm, outx_mode=mode) send_spy.assert_called_with(f"OUTX {mode}") @@ -47,7 +47,7 @@ def test_init_mode_gpibcomm(mocker): """Test initialization with GPIBCommunicator""" mock_gpib = mocker.MagicMock() comm = GPIBCommunicator(mock_gpib, 1) - mock_send = mocker.patch.object(comm, 'sendcmd') + mock_send = mocker.patch.object(comm, "sendcmd") ik.srs.SRS830(comm) mock_send.assert_called_with("OUTX 1") @@ -55,7 +55,7 @@ def test_init_mode_gpibcomm(mocker): def test_init_mode_serial_comm(mocker): """Test initialization with SerialCommunicator""" comm = SerialCommunicator(serial.Serial()) - mock_send = mocker.patch.object(comm, 'sendcmd') + mock_send = mocker.patch.object(comm, "sendcmd") ik.srs.SRS830(comm) mock_send.assert_called_with("OUTX 2") @@ -66,20 +66,18 @@ def test_init_mode_invalid(): with pytest.warns(UserWarning) as wrn_info: ik.srs.SRS830(comm) wrn_msg = wrn_info[0].message.args[0] - assert wrn_msg == "OUTX command has not been set. Instrument behaviour " \ - "is unknown." + assert ( + wrn_msg == "OUTX command has not been set. Instrument behaviour " "is unknown." + ) def test_frequency_source(): with expected_protocol( - ik.srs.SRS830, - [ - "FMOD?", - "FMOD 0" - ], - [ - "1", - ] + ik.srs.SRS830, + ["FMOD?", "FMOD 0"], + [ + "1", + ], ) as inst: assert inst.frequency_source == inst.FreqSource.internal inst.frequency_source = inst.FreqSource.external @@ -87,14 +85,11 @@ def test_frequency_source(): def test_frequency(): with expected_protocol( - ik.srs.SRS830, - [ - "FREQ?", - "FREQ {:e}".format(1000) - ], - [ - "12.34", - ] + ik.srs.SRS830, + ["FREQ?", "FREQ {:e}".format(1000)], + [ + "12.34", + ], ) as inst: assert inst.frequency == 12.34 * u.Hz inst.frequency = 1 * u.kHz @@ -102,14 +97,11 @@ def test_frequency(): def test_phase(): with expected_protocol( - ik.srs.SRS830, - [ - "PHAS?", - "PHAS {:e}".format(10) - ], - [ - "-45", - ] + ik.srs.SRS830, + ["PHAS?", "PHAS {:e}".format(10)], + [ + "-45", + ], ) as inst: assert inst.phase == -45 * u.degrees inst.phase = 10 * u.degrees @@ -117,14 +109,11 @@ def test_phase(): def test_amplitude(): with expected_protocol( - ik.srs.SRS830, - [ - "SLVL?", - "SLVL {:e}".format(1) - ], - [ - "0.1", - ] + ik.srs.SRS830, + ["SLVL?", "SLVL {:e}".format(1)], + [ + "0.1", + ], ) as inst: assert inst.amplitude == 0.1 * u.V inst.amplitude = 1 * u.V @@ -132,14 +121,11 @@ def test_amplitude(): def test_input_shield_ground(): with expected_protocol( - ik.srs.SRS830, - [ - "IGND?", - "IGND 1" - ], - [ - "0", - ] + ik.srs.SRS830, + ["IGND?", "IGND 1"], + [ + "0", + ], ) as inst: assert inst.input_shield_ground is False inst.input_shield_ground = True @@ -147,14 +133,11 @@ def test_input_shield_ground(): def test_coupling(): with expected_protocol( - ik.srs.SRS830, - [ - "ICPL?", - "ICPL 0" - ], - [ - "1", - ] + ik.srs.SRS830, + ["ICPL?", "ICPL 0"], + [ + "1", + ], ) as inst: assert inst.coupling == inst.Coupling.dc inst.coupling = inst.Coupling.ac @@ -162,17 +145,7 @@ def test_coupling(): def test_sample_rate(): # sends index of VALID_SAMPLE_RATES with expected_protocol( - ik.srs.SRS830, - [ - "SRAT?", - "SRAT?", - "SRAT {:d}".format(5), - "SRAT 14" - ], - [ - "8", - "14" - ] + ik.srs.SRS830, ["SRAT?", "SRAT?", "SRAT {:d}".format(5), "SRAT 14"], ["8", "14"] ) as inst: assert inst.sample_rate == 16 * u.Hz assert inst.sample_rate == "trigger" @@ -181,24 +154,17 @@ def test_sample_rate(): # sends index of VALID_SAMPLE_RATES def test_sample_rate_invalid(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.sample_rate = "foobar" def test_buffer_mode(): with expected_protocol( - ik.srs.SRS830, - [ - "SEND?", - "SEND 1" - ], - [ - "0", - ] + ik.srs.SRS830, + ["SEND?", "SEND 1"], + [ + "0", + ], ) as inst: assert inst.buffer_mode == inst.BufferMode.one_shot inst.buffer_mode = inst.BufferMode.loop @@ -206,13 +172,11 @@ def test_buffer_mode(): def test_num_data_points(): with expected_protocol( - ik.srs.SRS830, - [ - "SPTS?" - ], - [ - "5", - ] + ik.srs.SRS830, + ["SPTS?"], + [ + "5", + ], ) as inst: assert inst.num_data_points == 5 @@ -220,116 +184,77 @@ def test_num_data_points(): def test_num_data_points_no_answer(): """Raise IOError after no answer 10 times.""" answer = "" - with expected_protocol( - ik.srs.SRS830, - ["SPTS?"] * 10, - [answer] * 10 - ) as inst: + with expected_protocol(ik.srs.SRS830, ["SPTS?"] * 10, [answer] * 10) as inst: with pytest.raises(IOError) as err_info: _ = inst.num_data_points err_msg = err_info.value.args[0] - assert err_msg == f"Expected integer response from instrument, got " \ - f"{repr(answer)}" + assert ( + err_msg == f"Expected integer response from instrument, got " + f"{repr(answer)}" + ) def test_data_transfer(): with expected_protocol( - ik.srs.SRS830, - [ - "FAST?", - "FAST 2" - ], - [ - "0", - ] + ik.srs.SRS830, + ["FAST?", "FAST 2"], + [ + "0", + ], ) as inst: assert inst.data_transfer is False inst.data_transfer = True def test_auto_offset(): - with expected_protocol( - ik.srs.SRS830, - [ - "AOFF 1", - "AOFF 1" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["AOFF 1", "AOFF 1"], []) as inst: inst.auto_offset(inst.Mode.x) inst.auto_offset("x") def test_auto_offset_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [ - "AOFF 1", - ], - [] + ik.srs.SRS830, + [ + "AOFF 1", + ], + [], ) as inst: inst.auto_offset(inst.Mode.theta) def test_auto_phase(): - with expected_protocol( - ik.srs.SRS830, - [ - "APHS" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["APHS"], []) as inst: inst.auto_phase() def test_init(): - with expected_protocol( - ik.srs.SRS830, - [ - "REST", - "SRAT 5", - "SEND 1" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["REST", "SRAT 5", "SEND 1"], []) as inst: inst.init(sample_rate=2, buffer_mode=inst.BufferMode.loop) def test_start_data_transfer(): - with expected_protocol( - ik.srs.SRS830, - [ - "FAST 2", - "STRD" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["FAST 2", "STRD"], []) as inst: inst.start_data_transfer() def test_take_measurement(): with expected_protocol( - ik.srs.SRS830, - [ - "REST", - "SRAT 4", - "SEND 0", - "FAST 2", - "STRD", - "PAUS", - "SPTS?", - "SPTS?", - "TRCA?1,0,2", - "SPTS?", - "TRCA?2,0,2" - ], - [ - "2", - "2", - "1.234,5.678", - "2", - "0.456,5.321" - ] + ik.srs.SRS830, + [ + "REST", + "SRAT 4", + "SEND 0", + "FAST 2", + "STRD", + "PAUS", + "SPTS?", + "SPTS?", + "TRCA?1,0,2", + "SPTS?", + "TRCA?2,0,2", + ], + ["2", "2", "1.234,5.678", "2", "0.456,5.321"], ) as inst: resp = inst.take_measurement(sample_rate=1, num_samples=2) expected = ((1.234, 5.678), (0.456, 5.321)) @@ -344,32 +269,15 @@ def test_take_measurement_num_dat_points_fails(): This is the way it is currently implemented. """ with expected_protocol( - ik.srs.SRS830, - [ - "REST", - "SRAT 4", - "SEND 0", - "FAST 2", - "STRD", - "PAUS" - ] + - [ - "SPTS?" - ] * 11 + - [ - "TRCA?1,0,2", - "SPTS?", - "TRCA?2,0,2" - ], - [ - "", - ] * 10 + - [ - "2", - "1.234,5.678", - "2", - "0.456,5.321" - ] + ik.srs.SRS830, + ["REST", "SRAT 4", "SEND 0", "FAST 2", "STRD", "PAUS"] + + ["SPTS?"] * 11 + + ["TRCA?1,0,2", "SPTS?", "TRCA?2,0,2"], + [ + "", + ] + * 10 + + ["2", "1.234,5.678", "2", "0.456,5.321"], ) as inst: resp = inst.take_measurement(sample_rate=1, num_samples=2) expected = ((1.234, 5.678), (0.456, 5.321)) @@ -379,171 +287,87 @@ def test_take_measurement_num_dat_points_fails(): def test_take_measurement_invalid_num_samples(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: _ = inst.take_measurement(sample_rate=1, num_samples=16384) def test_set_offset_expand(): - with expected_protocol( - ik.srs.SRS830, - [ - "OEXP 1,0,0" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["OEXP 1,0,0"], []) as inst: inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=1) def test_set_offset_expand_mode_as_str(): - with expected_protocol( - ik.srs.SRS830, - [ - "OEXP 1,0,0" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["OEXP 1,0,0"], []) as inst: inst.set_offset_expand(mode="x", offset=0, expand=1) def test_set_offset_expand_invalid_mode(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_offset_expand(mode=inst.Mode.theta, offset=0, expand=1) def test_set_offset_expand_invalid_offset(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_offset_expand(mode=inst.Mode.x, offset=106, expand=1) def test_set_offset_expand_invalid_expand(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand=5) def test_set_offset_expand_invalid_type_offset(): - with pytest.raises(TypeError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(TypeError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_offset_expand(mode=inst.Mode.x, offset="derp", expand=1) def test_set_offset_expand_invalid_type_expand(): - with pytest.raises(TypeError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(TypeError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_offset_expand(mode=inst.Mode.x, offset=0, expand="derp") def test_start_scan(): - with expected_protocol( - ik.srs.SRS830, - [ - "STRD" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["STRD"], []) as inst: inst.start_scan() def test_pause(): - with expected_protocol( - ik.srs.SRS830, - [ - "PAUS" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["PAUS"], []) as inst: inst.pause() def test_data_snap(): - with expected_protocol( - ik.srs.SRS830, - [ - "SNAP? 1,2" - ], - [ - "1.234,9.876" - ] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["SNAP? 1,2"], ["1.234,9.876"]) as inst: data = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.y) expected = [1.234, 9.876] iterable_eq(data, expected) def test_data_snap_mode_as_str(): - with expected_protocol( - ik.srs.SRS830, - [ - "SNAP? 1,2" - ], - [ - "1.234,9.876" - ] - ) as inst: - data = inst.data_snap(mode1='x', mode2='y') + with expected_protocol(ik.srs.SRS830, ["SNAP? 1,2"], ["1.234,9.876"]) as inst: + data = inst.data_snap(mode1="x", mode2="y") expected = [1.234, 9.876] iterable_eq(data, expected) def test_data_snap_invalid_snap_mode1(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: _ = inst.data_snap(mode1=inst.Mode.xnoise, mode2=inst.Mode.y) def test_data_snap_invalid_snap_mode2(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.ynoise) def test_data_snap_identical_modes(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: _ = inst.data_snap(mode1=inst.Mode.x, mode2=inst.Mode.x) def test_read_data_buffer(): with expected_protocol( - ik.srs.SRS830, - [ - "SPTS?", - "TRCA?1,0,2" - ], - [ - "2", - "1.234,9.876" - ] + ik.srs.SRS830, ["SPTS?", "TRCA?1,0,2"], ["2", "1.234,9.876"] ) as inst: data = inst.read_data_buffer(channel=inst.Mode.ch1) expected = (1.234, 9.876) @@ -554,15 +378,7 @@ def test_read_data_buffer(): def test_read_data_buffer_mode_as_str(): with expected_protocol( - ik.srs.SRS830, - [ - "SPTS?", - "TRCA?1,0,2" - ], - [ - "2", - "1.234,9.876" - ] + ik.srs.SRS830, ["SPTS?", "TRCA?1,0,2"], ["2", "1.234,9.876"] ) as inst: data = inst.read_data_buffer(channel="ch1") expected = (1.234, 9.876) @@ -572,89 +388,45 @@ def test_read_data_buffer_mode_as_str(): def test_read_data_buffer_invalid_mode(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: _ = inst.read_data_buffer(channel=inst.Mode.x) def test_clear_data_buffer(): - with expected_protocol( - ik.srs.SRS830, - [ - "REST" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["REST"], []) as inst: inst.clear_data_buffer() def test_set_channel_display(): - with expected_protocol( - ik.srs.SRS830, - [ - "DDEF 1,0,0" - ], - [] - ) as inst: + with expected_protocol(ik.srs.SRS830, ["DDEF 1,0,0"], []) as inst: inst.set_channel_display( - channel=inst.Mode.ch1, - display=inst.Mode.x, - ratio=inst.Mode.none + channel=inst.Mode.ch1, display=inst.Mode.x, ratio=inst.Mode.none ) def test_set_channel_display_params_as_str(): - with expected_protocol( - ik.srs.SRS830, - [ - "DDEF 1,0,0" - ], - [] - ) as inst: - inst.set_channel_display( - channel="ch1", - display="x", - ratio="none" - ) + with expected_protocol(ik.srs.SRS830, ["DDEF 1,0,0"], []) as inst: + inst.set_channel_display(channel="ch1", display="x", ratio="none") def test_set_channel_display_invalid_channel(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_channel_display( - channel=inst.Mode.x, - display=inst.Mode.x, - ratio=inst.Mode.none + channel=inst.Mode.x, display=inst.Mode.x, ratio=inst.Mode.none ) def test_set_channel_display_invalid_display(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_channel_display( channel=inst.Mode.ch1, display=inst.Mode.y, # y is only valid for ch2, not ch1! - ratio=inst.Mode.none + ratio=inst.Mode.none, ) def test_set_channel_display_invalid_ratio(): - with pytest.raises(ValueError), expected_protocol( - ik.srs.SRS830, - [], - [] - ) as inst: + with pytest.raises(ValueError), expected_protocol(ik.srs.SRS830, [], []) as inst: inst.set_channel_display( - channel=inst.Mode.ch1, - display=inst.Mode.x, - ratio=inst.Mode.xnoise + channel=inst.Mode.ch1, display=inst.Mode.x, ratio=inst.Mode.xnoise ) diff --git a/instruments/tests/test_srs/test_srsctc100.py b/instruments/tests/test_srs/test_srsctc100.py index 53fb14f5e..67341a0ba 100644 --- a/instruments/tests/test_srs/test_srsctc100.py +++ b/instruments/tests/test_srs/test_srsctc100.py @@ -46,15 +46,7 @@ @pytest.mark.parametrize("channel", ch_names) def test_srsctc100_channel_init(channel): """Initialize a channel.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query - ], - [ - ch_names_str - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [ch_names_query], [ch_names_str]) as inst: with inst._error_checking_disabled(): ch = inst.channel[channel] assert ch._ctc is inst @@ -67,14 +59,9 @@ def test_srsctc100_channel_name(): old_name = ch_names[0] new_name = "New channel" with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{old_name.replace(' ', '')}.name = \"{new_name}\"" - ], - [ - ch_names_str - ] + ik.srs.SRSCTC100, + [ch_names_query, f"{old_name.replace(' ', '')}.name = \"{new_name}\""], + [ch_names_str], ) as inst: with inst._error_checking_disabled(): ch = inst.channel[ch_names[0]] @@ -95,15 +82,9 @@ def test_srsctc100_channel_get(channel): cmd = "COMMAND" answ = "ANSWER" with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.{cmd}?" - ], - [ - ch_names_str, - answ - ] + ik.srs.SRSCTC100, + [ch_names_query, f"{channel.replace(' ', '')}.{cmd}?"], + [ch_names_str, answ], ) as inst: with inst._error_checking_disabled(): assert inst.channel[channel]._get(cmd) == answ @@ -118,14 +99,9 @@ def test_srsctc100_channel_set(channel): cmd = "COMMAND" newval = "NEWVAL" with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.{cmd} = \"{newval}\"" - ], - [ - ch_names_str - ] + ik.srs.SRSCTC100, + [ch_names_query, f"{channel.replace(' ', '')}.{cmd} = \"{newval}\""], + [ch_names_str], ) as inst: with inst._error_checking_disabled(): inst.channel[channel]._set(cmd, newval) @@ -138,19 +114,19 @@ def test_srsctc100_channel_value(): value = 42 with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.value?", - "getOutput.units?", - ch_names_query - ], - [ - ch_names_str, - f"{value}", - ",".join(ch_units), - ch_names_str, - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.value?", + "getOutput.units?", + ch_names_query, + ], + [ + ch_names_str, + f"{value}", + ",".join(ch_units), + ch_names_str, + ], ) as inst: with inst._error_checking_disabled(): assert inst.channel[channel].value == u.Quantity(value, unit) @@ -161,17 +137,13 @@ def test_srsctc100_channel_units_single(): channel = ch_names[0] unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - "getOutput.units?", - ch_names_query - ], - [ - ch_names_str, - ",".join(ch_units), - ch_names_str, - ] + ik.srs.SRSCTC100, + [ch_names_query, "getOutput.units?", ch_names_query], + [ + ch_names_str, + ",".join(ch_units), + ch_names_str, + ], ) as inst: with inst._error_checking_disabled(): ch = inst.channel[channel] @@ -183,15 +155,12 @@ def test_srsctc100_channel_sensor_type(sensor): """Get type of sensor attached to specified channel.""" channel = ch_names[0] with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.sensor?", - ], - [ - ch_names_str, - f"{sensor.value}" - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.sensor?", + ], + [ch_names_str, f"{sensor.value}"], ) as inst: with inst._error_checking_disabled(): assert inst.channel[channel].sensor_type == sensor @@ -203,16 +172,13 @@ def test_srsctc100_channel_stats_enabled(newval): channel = ch_names[0] value_inst = "On" if newval else "Off" with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.stats = \"{value_inst}\"", - f"{channel.replace(' ', '')}.stats?" - ], - [ - ch_names_str, - f"{value_inst}" - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.stats = \"{value_inst}\"", + f"{channel.replace(' ', '')}.stats?", + ], + [ch_names_str, f"{value_inst}"], ) as inst: with inst._error_checking_disabled(): ch = inst.channel[channel] @@ -225,16 +191,13 @@ def test_srsctc100_channel_stats_points(points): """Get / set stats points in valid range.""" channel = ch_names[0] with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.points = \"{points}\"", - f"{channel.replace(' ', '')}.points?" - ], - [ - ch_names_str, - f"{points}" - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.points = \"{points}\"", + f"{channel.replace(' ', '')}.points?", + ], + [ch_names_str, f"{points}"], ) as inst: with inst._error_checking_disabled(): ch = inst.channel[channel] @@ -248,19 +211,19 @@ def test_srsctc100_channel_average(): unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] value = 42 with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.average?", - "getOutput.units?", - ch_names_query - ], - [ - ch_names_str, - f"{value}", - ",".join(ch_units), - ch_names_str, - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.average?", + "getOutput.units?", + ch_names_query, + ], + [ + ch_names_str, + f"{value}", + ",".join(ch_units), + ch_names_str, + ], ) as inst: with inst._error_checking_disabled(): assert inst.channel[channel].average == u.Quantity(value, unit) @@ -272,19 +235,19 @@ def test_srsctc100_channel_std_dev(): unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] value = 42 with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"{channel.replace(' ', '')}.SD?", - "getOutput.units?", - ch_names_query - ], - [ - ch_names_str, - f"{value}", - ",".join(ch_units), - ch_names_str, - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + f"{channel.replace(' ', '')}.SD?", + "getOutput.units?", + ch_names_query, + ], + [ + ch_names_str, + f"{value}", + ",".join(ch_units), + ch_names_str, + ], ) as inst: with inst._error_checking_disabled(): assert inst.channel[channel].std_dev == u.Quantity(value, unit) @@ -297,26 +260,22 @@ def test_get_log_point(channel): unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_name_unit_dict[channel]] values = (13, 42) which = "first" - values_out = (u.Quantity(float(values[0]), u.ms), - u.Quantity(float(values[1]), unit)) + values_out = ( + u.Quantity(float(values[0]), u.ms), + u.Quantity(float(values[1]), unit), + ) with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - "getOutput.units?", - ch_names_query, - f"getLog.xy {channel}, {which}" - ], - [ - ch_names_str, - ",".join(ch_units), - ch_names_str, - f"{values[0]},{values[1]}" - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + "getOutput.units?", + ch_names_query, + f"getLog.xy {channel}, {which}", + ], + [ch_names_str, ",".join(ch_units), ch_names_str, f"{values[0]},{values[1]}"], ) as inst: with inst._error_checking_disabled(): - assert (inst.channel[channel].get_log_point(which=which) == - values_out) + assert inst.channel[channel].get_log_point(which=which) == values_out def test_get_log_point_with_unit(): @@ -325,23 +284,20 @@ def test_get_log_point_with_unit(): unit = ik.srs.SRSCTC100._UNIT_NAMES[ch_units[0]] values = (13, 42) which = "first" - values_out = (u.Quantity(float(values[0]), u.ms), - u.Quantity(float(values[1]), unit)) + values_out = ( + u.Quantity(float(values[0]), u.ms), + u.Quantity(float(values[1]), unit), + ) with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - f"getLog.xy {channel}, {which}" - ], - [ - ch_names_str, - f"{values[0]},{values[1]}" - ] + ik.srs.SRSCTC100, + [ch_names_query, f"getLog.xy {channel}, {which}"], + [ch_names_str, f"{values[0]},{values[1]}"], ) as inst: with inst._error_checking_disabled(): - assert (inst.channel[channel].get_log_point(which=which, - units=unit) == - values_out) + assert ( + inst.channel[channel].get_log_point(which=which, units=unit) + == values_out + ) @pytest.mark.parametrize("channel", ch_names) @@ -363,10 +319,12 @@ def test_channel_get_log(channel): err_check_reci = "0,NO ERROR" # stich together strings to read all the values - str_log_next_send = "\n".join([f"getLog.xy {channel}, next" for - it in range(1, n_points)]) - str_log_next_reci = "\n".join([f"{times[it]},{values[it]}" for - it in range(1, n_points)]) + str_log_next_send = "\n".join( + [f"getLog.xy {channel}, next" for it in range(1, n_points)] + ) + str_log_next_reci = "\n".join( + [f"{times[it]},{values[it]}" for it in range(1, n_points)] + ) # make data to compare with if numpy: @@ -384,33 +342,33 @@ def test_channel_get_log(channel): temps = tuple(temps) with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query, - err_check_send, - "getOutput.units?", - err_check_send, - ch_names_query, - err_check_send, - f"getLog.xy? {channel}", - err_check_send, - f"getLog.xy {channel}, first", # query first point - str_log_next_send, - err_check_send - ], - [ - ch_names_str, - err_check_reci, - ",".join(ch_units), - err_check_reci, - ch_names_str, - err_check_reci, - f"{n_points}", - err_check_reci, - f"{times[0]},{values[0]}", - str_log_next_reci, - err_check_reci - ] + ik.srs.SRSCTC100, + [ + ch_names_query, + err_check_send, + "getOutput.units?", + err_check_send, + ch_names_query, + err_check_send, + f"getLog.xy? {channel}", + err_check_send, + f"getLog.xy {channel}, first", # query first point + str_log_next_send, + err_check_send, + ], + [ + ch_names_str, + err_check_reci, + ",".join(ch_units), + err_check_reci, + ch_names_str, + err_check_reci, + f"{n_points}", + err_check_reci, + f"{times[0]},{values[0]}", + str_log_next_reci, + err_check_reci, + ], ) as inst: ch = inst.channel[channel] ts_read, temps_read = ch.get_log() @@ -424,27 +382,13 @@ def test_channel_get_log(channel): def test_srsctc100_init(): """Initialize the SRS CTC-100 instrument.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [], []) as inst: assert inst._do_errcheck def test_srsctc100_channel_names(): """Get current channel names from instrument.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - ch_names_query - ], - [ - ch_names_str - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [ch_names_query], [ch_names_str]) as inst: with inst._error_checking_disabled(): assert inst._channel_names() == ch_names @@ -452,15 +396,9 @@ def test_srsctc100_channel_names(): def test_srsctc100_channel_units_all(): """Get units for all channels.""" with expected_protocol( - ik.srs.SRSCTC100, - [ - "getOutput.units?", - ch_names_query - ], - [ - ",".join(ch_units), - ch_names_str - ] + ik.srs.SRSCTC100, + ["getOutput.units?", ch_names_query], + [",".join(ch_units), ch_names_str], ) as inst: with inst._error_checking_disabled(): # create a unit dictionary to compare the return to @@ -473,29 +411,13 @@ def test_srsctc100_channel_units_all(): def test_srsctc100_errcheck(): """Error check - no error returned.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - "geterror?" - ], - [ - "0,NO ERROR" - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, ["geterror?"], ["0,NO ERROR"]) as inst: assert inst.errcheck() == 0 def test_srsctc100_errcheck_error_raised(): """Error check - error raises.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - "geterror?" - ], - [ - "42,THE ANSWER" - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, ["geterror?"], ["42,THE ANSWER"]) as inst: with pytest.raises(IOError) as exc_info: inst.errcheck() exc_msg = exc_info.value.args[0] @@ -504,13 +426,7 @@ def test_srsctc100_errcheck_error_raised(): def test_srsctc100_error_checking_disabled_context(): """Context dialogue to disable error checking.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [], []) as inst: # by default, error checking enabled with inst._error_checking_disabled(): assert not inst._do_errcheck @@ -523,14 +439,9 @@ def test_srsctc100_error_checking_disabled_context(): def test_srsctc100_display_figures(figures): """Get / set significant figures of display.""" with expected_protocol( - ik.srs.SRSCTC100, - [ - f"system.display.figures = {figures}", - "system.display.figures?" - ], - [ - f"{figures}" - ] + ik.srs.SRSCTC100, + [f"system.display.figures = {figures}", "system.display.figures?"], + [f"{figures}"], ) as inst: with inst._error_checking_disabled(): inst.display_figures = figures @@ -540,31 +451,21 @@ def test_srsctc100_display_figures(figures): @given(figures=st.integers().filter(lambda x: x < 0 or x > 6)) def test_srsctc100_display_figures_value_error(figures): """Raise ValueError when setting an invalid number of figures.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [], []) as inst: with inst._error_checking_disabled(): with pytest.raises(ValueError) as exc_info: inst.display_figures = figures exc_msg = exc_info.value.args[0] - assert exc_msg == "Number of display figures must be an " \ - "integer from 0 to 6, inclusive." + assert ( + exc_msg == "Number of display figures must be an " + "integer from 0 to 6, inclusive." + ) @pytest.mark.parametrize("newval", (True, False)) def test_srsctc100_error_check_toggle(newval): """Get / set error check bool.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [], []) as inst: inst.error_check_toggle = newval assert inst.error_check_toggle == newval @@ -572,13 +473,7 @@ def test_srsctc100_error_check_toggle(newval): def test_srsctc100_error_check_toggle_type_error(): """Raise type error when error check toggle set with non-bool.""" newval = 42 - with expected_protocol( - ik.srs.SRSCTC100, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, [], []) as inst: with pytest.raises(TypeError): inst.error_check_toggle = newval @@ -587,14 +482,7 @@ def test_srsctc100_sendcmd(): """Send a command and error check.""" cmd = "COMMAND" with expected_protocol( - ik.srs.SRSCTC100, - [ - cmd, - "geterror?" - ], - [ - "0,NO ERROR" - ] + ik.srs.SRSCTC100, [cmd, "geterror?"], ["0,NO ERROR"] ) as inst: inst.sendcmd("COMMAND") @@ -604,28 +492,13 @@ def test_srsctc100_query(): cmd = "COMMAND" answ = "ANSWER" with expected_protocol( - ik.srs.SRSCTC100, - [ - cmd, - "geterror?" - ], - [ - answ, - "0,NO ERROR" - ] + ik.srs.SRSCTC100, [cmd, "geterror?"], [answ, "0,NO ERROR"] ) as inst: assert inst.query("COMMAND") == answ def test_srsctc100_clear_log(): """Clear the log.""" - with expected_protocol( - ik.srs.SRSCTC100, - [ - "System.Log.Clear yes" - ], - [ - ] - ) as inst: + with expected_protocol(ik.srs.SRSCTC100, ["System.Log.Clear yes"], []) as inst: with inst._error_checking_disabled(): inst.clear_log() diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index a652c006e..7d50d35e8 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -47,15 +47,9 @@ def test_srsdg645_channel_delay(): SRSDG645: Get / set delay. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "DLAY?2", - "DLAY 3,2,60", - "DLAY 5,4,10" - ], - [ - "0,42" - ], + ik.srs.SRSDG645, + ["DLAY?2", "DLAY 3,2,60", "DLAY 5,4,10"], + ["0,42"], ) as ddg: ref, t = ddg.channel["A"].delay assert ref == ddg.Channels.T0 @@ -80,14 +74,12 @@ def test_srsdg645_output_level(): SRSDG645: Checks getting/setting unitful output level. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "LAMP? 1", - "LAMP 1,4.0", - ], - [ - "3.2" - ] + ik.srs.SRSDG645, + [ + "LAMP? 1", + "LAMP 1,4.0", + ], + ["3.2"], ) as ddg: unit_eq(ddg.output["AB"].level_amplitude, u.Quantity(3.2, "V")) ddg.output["AB"].level_amplitude = 4.0 @@ -98,14 +90,12 @@ def test_srsdg645_output_offset(): SRSDG645: Checks getting/setting unitful output offset. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "LOFF? 1", - "LOFF 1,2.0", - ], - [ - "1.2" - ] + ik.srs.SRSDG645, + [ + "LOFF? 1", + "LOFF 1,2.0", + ], + ["1.2"], ) as ddg: unit_eq(ddg.output["AB"].level_offset, u.Quantity(1.2, "V")) ddg.output["AB"].level_offset = 2.0 @@ -115,16 +105,7 @@ def test_srsdg645_output_polarity(): """ SRSDG645: Checks getting/setting """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "LPOL? 1", - "LPOL 2,0" - ], - [ - "1" - ] - ) as ddg: + with expected_protocol(ik.srs.SRSDG645, ["LPOL? 1", "LPOL 2,0"], ["1"]) as ddg: assert ddg.output["AB"].polarity == ddg.LevelPolarity.positive ddg.output["CD"].polarity = ddg.LevelPolarity.negative @@ -133,13 +114,7 @@ def test_srsdg645_output_polarity_raise_type_error(): """ SRSDG645: Polarity setter with wrong input - raise type error. """ - with expected_protocol( - ik.srs.SRSDG645, - [ - ], - [ - ] - ) as ddg: + with expected_protocol(ik.srs.SRSDG645, [], []) as ddg: with pytest.raises(TypeError): ddg.output["AB"].polarity = 1 @@ -148,36 +123,16 @@ def test_srsdg645_display(): """ SRSDG645: Set / get display mode. """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "DISP?", - "DISP 0,0" - ], - [ - "12,3" - ] - ) as ddg: - assert ddg.display == (ddg.DisplayMode.channel_levels, - ddg.Channels.B) - ddg.display = (ddg.DisplayMode.trigger_rate, - ddg.Channels.T0) + with expected_protocol(ik.srs.SRSDG645, ["DISP?", "DISP 0,0"], ["12,3"]) as ddg: + assert ddg.display == (ddg.DisplayMode.channel_levels, ddg.Channels.B) + ddg.display = (ddg.DisplayMode.trigger_rate, ddg.Channels.T0) def test_srsdg645_enable_adv_triggering(): """ SRSDG645: Set / get if advanced triggering is enabled. """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "ADVT?", - "ADVT 1" - ], - [ - "0" - ] - ) as ddg: + with expected_protocol(ik.srs.SRSDG645, ["ADVT?", "ADVT 1"], ["0"]) as ddg: assert not ddg.enable_adv_triggering ddg.enable_adv_triggering = True @@ -187,35 +142,18 @@ def test_srsdg645_trigger_rate(): SRSDG645: Set / get trigger rate. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "TRAT?", - "TRAT 10000", - "TRAT 1000" - ], - [ - "+1000.000000" - ] + ik.srs.SRSDG645, ["TRAT?", "TRAT 10000", "TRAT 1000"], ["+1000.000000"] ) as ddg: assert ddg.trigger_rate == u.Quantity(1000, u.Hz) ddg.trigger_rate = 10000 - ddg.trigger_rate = u.Quantity(1000, u.Hz) # unitful send + ddg.trigger_rate = u.Quantity(1000, u.Hz) # unitful send def test_srsdg645_trigger_source(): """ SRSDG645: Set / get trigger source. """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "TSRC?", - "TSRC 1" - ], - [ - "0" - ] - ) as ddg: + with expected_protocol(ik.srs.SRSDG645, ["TSRC?", "TSRC 1"], ["0"]) as ddg: assert ddg.trigger_source == ddg.TriggerSource.internal ddg.trigger_source = ddg.TriggerSource.external_rising @@ -225,15 +163,7 @@ def test_srsdg645_holdoff(): SRSDG645: Set / get hold off. """ with expected_protocol( - ik.srs.SRSDG645, - [ - "HOLD?", - "HOLD 0", - "HOLD 0.01" - ], - [ - "+0.001001000000" - ] + ik.srs.SRSDG645, ["HOLD?", "HOLD 0", "HOLD 0.01"], ["+0.001001000000"] ) as ddg: assert u.Quantity(1001, u.us) == ddg.holdoff ddg.holdoff = 0 @@ -244,73 +174,38 @@ def test_srsdg645_enable_burst_mode(): """ SRSDG645: Checks getting/setting of enabling burst mode. """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "BURM?", - "BURM 1" - ], - [ - "0" - ] - ) as ddg: + with expected_protocol(ik.srs.SRSDG645, ["BURM?", "BURM 1"], ["0"]) as ddg: assert ddg.enable_burst_mode is False ddg.enable_burst_mode = True def test_srsdg645_enable_burst_t0_first(): """ - SRSDG645: Checks getting/setting of enabling T0 output on first - in burst mode. - """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "BURT?", - "BURT 1" - ], - [ - "0" - ] - ) as ddg: + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ + with expected_protocol(ik.srs.SRSDG645, ["BURT?", "BURT 1"], ["0"]) as ddg: assert ddg.enable_burst_t0_first is False ddg.enable_burst_t0_first = True def test_srsdg645_burst_count(): """ - SRSDG645: Checks getting/setting of enabling T0 output on first - in burst mode. - """ - with expected_protocol( - ik.srs.SRSDG645, - [ - "BURC?", - "BURC 42" - ], - [ - "10" - ] - ) as ddg: + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ + with expected_protocol(ik.srs.SRSDG645, ["BURC?", "BURC 42"], ["10"]) as ddg: assert ddg.burst_count == 10 ddg.burst_count = 42 def test_srsdg645_burst_period(): """ - SRSDG645: Checks getting/setting of enabling T0 output on first - in burst mode. - """ + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ with expected_protocol( - ik.srs.SRSDG645, - [ - "BURP?", - "BURP 13", - "BURP 0.1" - ], - [ - "100E-9" - ] + ik.srs.SRSDG645, ["BURP?", "BURP 13", "BURP 0.1"], ["100E-9"] ) as ddg: unit_eq(ddg.burst_period, u.Quantity(100, "ns").to(u.sec)) ddg.burst_period = u.Quantity(13, "s") @@ -319,19 +214,11 @@ def test_srsdg645_burst_period(): def test_srsdg645_burst_delay(): """ - SRSDG645: Checks getting/setting of enabling T0 output on first - in burst mode. - """ + SRSDG645: Checks getting/setting of enabling T0 output on first + in burst mode. + """ with expected_protocol( - ik.srs.SRSDG645, - [ - "BURD?", - "BURD 42", - "BURD 0.1" - ], - [ - "0" - ] + ik.srs.SRSDG645, ["BURD?", "BURD 42", "BURD 0.1"], ["0"] ) as ddg: unit_eq(ddg.burst_delay, u.Quantity(0, "s")) ddg.burst_delay = u.Quantity(42, "s") diff --git a/instruments/tests/test_tektronix/test_tekawg2000.py b/instruments/tests/test_tektronix/test_tekawg2000.py index 5c39a96da..59a7863f9 100644 --- a/instruments/tests/test_tektronix/test_tekawg2000.py +++ b/instruments/tests/test_tektronix/test_tekawg2000.py @@ -36,13 +36,7 @@ @pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_id) def test_channel_init(channel): """Channel initialization.""" - with expected_protocol( - ik.tektronix.TekAWG2000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekAWG2000, [], []) as inst: assert inst.channel[channel]._tek is inst assert inst.channel[channel]._name == f"CH{channel + 1}" assert inst.channel[channel]._old_dsrc is None @@ -51,34 +45,28 @@ def test_channel_init(channel): @pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_id) def test_channel_name(channel): """Get the name of the channel.""" - with expected_protocol( - ik.tektronix.TekAWG2000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekAWG2000, [], []) as inst: assert inst.channel[channel].name == f"CH{channel + 1}" @pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_id) -@given(val_read=st.floats(min_value=0.02, max_value=2), - val_unitless=st.floats(min_value=0.02, max_value=2), - val_millivolt=st.floats(min_value=0.02, max_value=2000)) +@given( + val_read=st.floats(min_value=0.02, max_value=2), + val_unitless=st.floats(min_value=0.02, max_value=2), + val_millivolt=st.floats(min_value=0.02, max_value=2000), +) def test_channel_amplitude(channel, val_read, val_unitless, val_millivolt): """Get / set amplitude.""" val_read = u.Quantity(val_read, u.V) val_unitful = u.Quantity(val_millivolt, u.mV) with expected_protocol( - ik.tektronix.TekAWG2000, - [ - f"FG:CH{channel+1}:AMPL?", - f"FG:CH{channel+1}:AMPL {val_unitless}", - f"FG:CH{channel+1}:AMPL {val_unitful.to(u.V).magnitude}" - ], - [ - f"{val_read.magnitude}" - ] + ik.tektronix.TekAWG2000, + [ + f"FG:CH{channel+1}:AMPL?", + f"FG:CH{channel+1}:AMPL {val_unitless}", + f"FG:CH{channel+1}:AMPL {val_unitful.to(u.V).magnitude}", + ], + [f"{val_read.magnitude}"], ) as inst: assert inst.channel[channel].amplitude == val_read inst.channel[channel].amplitude = val_unitless @@ -86,23 +74,23 @@ def test_channel_amplitude(channel, val_read, val_unitless, val_millivolt): @pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_id) -@given(val_read=st.floats(min_value=0.02, max_value=2), - val_unitless=st.floats(min_value=0.02, max_value=2), - val_millivolt=st.floats(min_value=0.02, max_value=2000)) +@given( + val_read=st.floats(min_value=0.02, max_value=2), + val_unitless=st.floats(min_value=0.02, max_value=2), + val_millivolt=st.floats(min_value=0.02, max_value=2000), +) def test_channel_offset(channel, val_read, val_unitless, val_millivolt): """Get / set offset.""" val_read = u.Quantity(val_read, u.V) val_unitful = u.Quantity(val_millivolt, u.mV) with expected_protocol( - ik.tektronix.TekAWG2000, - [ - f"FG:CH{channel+1}:OFFS?", - f"FG:CH{channel+1}:OFFS {val_unitless}", - f"FG:CH{channel+1}:OFFS {val_unitful.to(u.V).magnitude}" - ], - [ - f"{val_read.magnitude}" - ] + ik.tektronix.TekAWG2000, + [ + f"FG:CH{channel+1}:OFFS?", + f"FG:CH{channel+1}:OFFS {val_unitless}", + f"FG:CH{channel+1}:OFFS {val_unitful.to(u.V).magnitude}", + ], + [f"{val_read.magnitude}"], ) as inst: assert inst.channel[channel].offset == val_read inst.channel[channel].offset = val_unitless @@ -110,23 +98,23 @@ def test_channel_offset(channel, val_read, val_unitless, val_millivolt): @pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_id) -@given(val_read=st.floats(min_value=1, max_value=200000), - val_unitless=st.floats(min_value=1, max_value=200000), - val_kilohertz=st.floats(min_value=1, max_value=200)) +@given( + val_read=st.floats(min_value=1, max_value=200000), + val_unitless=st.floats(min_value=1, max_value=200000), + val_kilohertz=st.floats(min_value=1, max_value=200), +) def test_channel_frequency(channel, val_read, val_unitless, val_kilohertz): """Get / set offset.""" val_read = u.Quantity(val_read, u.Hz) val_unitful = u.Quantity(val_kilohertz, u.kHz) with expected_protocol( - ik.tektronix.TekAWG2000, - [ - f"FG:FREQ?", - f"FG:FREQ {val_unitless}HZ", - f"FG:FREQ {val_unitful.to(u.Hz).magnitude}HZ" - ], - [ - f"{val_read.magnitude}" - ] + ik.tektronix.TekAWG2000, + [ + f"FG:FREQ?", + f"FG:FREQ {val_unitless}HZ", + f"FG:FREQ {val_unitful.to(u.Hz).magnitude}HZ", + ], + [f"{val_read.magnitude}"], ) as inst: assert inst.channel[channel].frequency == val_read inst.channel[channel].frequency = val_unitless @@ -138,14 +126,9 @@ def test_channel_frequency(channel, val_read, val_unitless, val_kilohertz): def test_channel_polarity(channel, polarity): """Get / set polarity.""" with expected_protocol( - ik.tektronix.TekAWG2000, - [ - f"FG:CH{channel+1}:POL?", - f"FG:CH{channel+1}:POL {polarity.value}" - ], - [ - f"{polarity.value}" - ] + ik.tektronix.TekAWG2000, + [f"FG:CH{channel+1}:POL?", f"FG:CH{channel+1}:POL {polarity.value}"], + [f"{polarity.value}"], ) as inst: assert inst.channel[channel].polarity == polarity inst.channel[channel].polarity = polarity @@ -155,18 +138,14 @@ def test_channel_polarity(channel, polarity): def test_channel_polarity_type_mismatch(channel): """Raise a TypeError if a wrong type is selected as the polarity.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekAWG2000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekAWG2000, [], []) as inst: with pytest.raises(TypeError) as exc_info: inst.channel[channel].polarity = wrong_type exc_msg = exc_info.value.args[0] - assert exc_msg == f"Polarity settings must be a `TekAWG2000.Polarity` " \ - f"value, got {type(wrong_type)} instead." + assert ( + exc_msg == f"Polarity settings must be a `TekAWG2000.Polarity` " + f"value, got {type(wrong_type)} instead." + ) @pytest.mark.parametrize("channel", channels_to_try, ids=channels_to_try_id) @@ -174,14 +153,9 @@ def test_channel_polarity_type_mismatch(channel): def test_channel_shape(channel, shape): """Get / set shape.""" with expected_protocol( - ik.tektronix.TekAWG2000, - [ - f"FG:CH{channel+1}:SHAP?", - f"FG:CH{channel+1}:SHAP {shape.value}" - ], - [ - f"{shape.value}, 0" # pulse duty cycle - ] + ik.tektronix.TekAWG2000, + [f"FG:CH{channel+1}:SHAP?", f"FG:CH{channel+1}:SHAP {shape.value}"], + [f"{shape.value}, 0"], # pulse duty cycle ) as inst: assert inst.channel[channel].shape == shape inst.channel[channel].shape = shape @@ -191,18 +165,14 @@ def test_channel_shape(channel, shape): def test_channel_shape_type_mismatch(channel): """Raise a TypeError if a wrong type is selected as the shape.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekAWG2000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekAWG2000, [], []) as inst: with pytest.raises(TypeError) as exc_info: inst.channel[channel].shape = wrong_type exc_msg = exc_info.value.args[0] - assert exc_msg == f"Shape settings must be a `TekAWG2000.Shape` " \ - f"value, got {type(wrong_type)} instead." + assert ( + exc_msg == f"Shape settings must be a `TekAWG2000.Shape` " + f"value, got {type(wrong_type)} instead." + ) # INSTRUMENT # @@ -212,14 +182,9 @@ def test_waveform_name(): """Get / set the waveform name.""" file_name = "test_file" with expected_protocol( - ik.tektronix.TekAWG2000, - [ - "DATA:DEST?", - f"DATA:DEST \"{file_name}\"" - ], - [ - f"{file_name}" - ] + ik.tektronix.TekAWG2000, + ["DATA:DEST?", f'DATA:DEST "{file_name}"'], + [f"{file_name}"], ) as inst: assert inst.waveform_name == file_name inst.waveform_name = file_name @@ -228,13 +193,7 @@ def test_waveform_name(): def test_waveform_name_type_mismatch(): """Raise a TypeError when something else than a string is given.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekAWG2000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekAWG2000, [], []) as inst: with pytest.raises(TypeError) as exc_info: inst.waveform_name = wrong_type exc_msg = exc_info.value.args[0] @@ -242,67 +201,61 @@ def test_waveform_name_type_mismatch(): @pytest.mark.skipif(numpy is None, reason="Numpy required for this test") -@given(yzero=st.floats(min_value=-5, max_value=5), - ymult=st.floats(min_value=0.00001), - xincr=st.floats(min_value=5e-8, max_value=1e-1), - waveform=st.lists(st.floats(min_value=0, max_value=1), min_size=1)) +@given( + yzero=st.floats(min_value=-5, max_value=5), + ymult=st.floats(min_value=0.00001), + xincr=st.floats(min_value=5e-8, max_value=1e-1), + waveform=st.lists(st.floats(min_value=0, max_value=1), min_size=1), +) def test_upload_waveform(yzero, ymult, xincr, waveform): """Upload a waveform from the PC to the instrument.""" # prep waveform waveform = numpy.array(waveform) - waveform_send = waveform * (2**12 - 1) + waveform_send = waveform * (2 ** 12 - 1) waveform_send = waveform_send.astype("", - ik.tektronix.TekDPO70000.ByteOrder.little_endian: "<" + ik.tektronix.TekDPO70000.ByteOrder.little_endian: "<", } - value_expected = f"{byte_order_dict[byte_order]}" \ - f"{n_bytes}" \ - f"{binary_format_dict[binary_format]}" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ], - [ - ] - ) as inst: - assert inst._dtype(binary_format, byte_order, n_bytes) == \ - value_expected + value_expected = ( + f"{byte_order_dict[byte_order]}" + f"{n_bytes}" + f"{binary_format_dict[binary_format]}" + ) + with expected_protocol(ik.tektronix.TekDPO70000, [], []) as inst: + assert inst._dtype(binary_format, byte_order, n_bytes) == value_expected # DATA SOURCE - TESTED WITH CHANNELS # @@ -73,19 +66,16 @@ def test_dtype(binary_format, byte_order, n_bytes): def test_data_source_name(): """Query the name of a data source.""" channel = 0 - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [], []) as inst: assert inst.channel[channel].name == f"CH{channel+1}" @pytest.mark.parametrize("channel", [it for it in range(4)]) -@given(values=st.lists(st.integers(min_value=-2147483648, - max_value=2147483647), min_size=1)) +@given( + values=st.lists( + st.integers(min_value=-2147483648, max_value=2147483647), min_size=1 + ) +) def test_data_source_read_waveform(channel, values): """Read waveform from data source, binary format only!""" # select one set to test for: @@ -93,51 +83,59 @@ def test_data_source_read_waveform(channel, values): byte_order = ik.tektronix.TekDPO70000.ByteOrder.big_endian n_bytes = 4 # get the dtype - dtype_set = ik.tektronix.TekDPO70000._dtype(binary_format, byte_order, - n_bytes=None) + dtype_set = ik.tektronix.TekDPO70000._dtype(binary_format, byte_order, n_bytes=None) # pack the values - values_packed = b"".join(struct.pack(dtype_set, - value) for value in values) + values_packed = b"".join(struct.pack(dtype_set, value) for value in values) values_len = str(len(values_packed)).encode() values_len_of_len = str(len(values_len)).encode() # scale the values - scale = 1. - position = 0. - offset = 0. - scaled_values = [scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2**15) - position) + offset - for v in values] + scale = 1.0 + position = 0.0 + offset = 0.0 + scaled_values = [ + scale + * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) + + offset + for v in values + ] if numpy: values = numpy.array(values) - scaled_values = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * - values.astype(float) / (2**15) - position - ) + offset + scaled_values = ( + scale + * ( + (ik.tektronix.TekDPO70000.VERT_DIVS / 2) + * values.astype(float) + / (2 ** 15) + - position + ) + + offset + ) # run through the instrument with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "DAT:SOU?", # query data source - "DAT:ENC FAS", # fastest encoding - "WFMO:BYT_N?", # get n_bytes - "WFMO:BN_F?", # outgoing binary format - "WFMO:BYT_O?", # outgoing byte order - "CURV?", # query data - f"CH{channel + 1}:SCALE?", # scale raw data - f"CH{channel + 1}:POS?", - f"CH{channel + 1}:OFFS?" - - ], - [ - f"CH{channel+1}", - f"{n_bytes}", - f"{binary_format.value}", - f"{byte_order.value}", - b"#" + values_len_of_len + values_len + values_packed, - f"{scale}", - f"{position}", - f"{offset}" - ] + ik.tektronix.TekDPO70000, + [ + "DAT:SOU?", # query data source + "DAT:ENC FAS", # fastest encoding + "WFMO:BYT_N?", # get n_bytes + "WFMO:BN_F?", # outgoing binary format + "WFMO:BYT_O?", # outgoing byte order + "CURV?", # query data + f"CH{channel + 1}:SCALE?", # scale raw data + f"CH{channel + 1}:POS?", + f"CH{channel + 1}:OFFS?", + ], + [ + f"CH{channel+1}", + f"{n_bytes}", + f"{binary_format.value}", + f"{byte_order.value}", + b"#" + values_len_of_len + values_len + values_packed, + f"{scale}", + f"{position}", + f"{offset}", + ], ) as inst: # query waveform actual_waveform = inst.channel[channel].read_waveform() @@ -155,57 +153,66 @@ def test_data_source_read_waveform_with_old_data_source(): byte_order = ik.tektronix.TekDPO70000.ByteOrder.big_endian n_bytes = 4 # get the dtype - dtype_set = ik.tektronix.TekDPO70000._dtype(binary_format, byte_order, - n_bytes=None) + dtype_set = ik.tektronix.TekDPO70000._dtype(binary_format, byte_order, n_bytes=None) # pack the values values = range(10) if numpy: values = numpy.arange(10) - values_packed = b"".join(struct.pack(dtype_set, - value) for value in values) + values_packed = b"".join(struct.pack(dtype_set, value) for value in values) values_len = str(len(values_packed)).encode() values_len_of_len = str(len(values_len)).encode() # scale the values - scale = 1. - position = 0. - offset = 0. - scaled_values = [scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) + offset - for v in values] + scale = 1.0 + position = 0.0 + offset = 0.0 + scaled_values = [ + scale + * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) + + offset + for v in values + ] if numpy: - scaled_values = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * - values.astype(float) / (2 ** 15) - position - ) + offset + scaled_values = ( + scale + * ( + (ik.tektronix.TekDPO70000.VERT_DIVS / 2) + * values.astype(float) + / (2 ** 15) + - position + ) + + offset + ) # old data source to set manually - ensure it is set back later old_dsrc = "MATH1" # run through the instrument with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "DAT:SOU?", # query data source - f"DAT:SOU CH{channel + 1}", # set current data source - "DAT:ENC FAS", # fastest encoding - "WFMO:BYT_N?", # get n_bytes - "WFMO:BN_F?", # outgoing binary format - "WFMO:BYT_O?", # outgoing byte order - "CURV?", # query data - f"CH{channel + 1}:SCALE?", # scale raw data - f"CH{channel + 1}:POS?", - f"CH{channel + 1}:OFFS?", - f"DAT:SOU {old_dsrc}" - ], - [ - old_dsrc, - f"{n_bytes}", - f"{binary_format.value}", - f"{byte_order.value}", - b"#" + values_len_of_len + values_len + values_packed, - f"{scale}", - f"{position}", - f"{offset}" - ] + ik.tektronix.TekDPO70000, + [ + "DAT:SOU?", # query data source + f"DAT:SOU CH{channel + 1}", # set current data source + "DAT:ENC FAS", # fastest encoding + "WFMO:BYT_N?", # get n_bytes + "WFMO:BN_F?", # outgoing binary format + "WFMO:BYT_O?", # outgoing byte order + "CURV?", # query data + f"CH{channel + 1}:SCALE?", # scale raw data + f"CH{channel + 1}:POS?", + f"CH{channel + 1}:OFFS?", + f"DAT:SOU {old_dsrc}", + ], + [ + old_dsrc, + f"{n_bytes}", + f"{binary_format.value}", + f"{byte_order.value}", + b"#" + values_len_of_len + values_len + values_packed, + f"{scale}", + f"{position}", + f"{offset}", + ], ) as inst: # query waveform actual_waveform = inst.channel[channel].read_waveform() @@ -221,13 +228,7 @@ def test_data_source_read_waveform_with_old_data_source(): @pytest.mark.parametrize("math", [it for it in range(4)]) def test_math_init(math): """Initialize a math channel.""" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [], []) as inst: assert inst.math[math]._parent is inst assert inst.math[math]._idx == math + 1 @@ -237,12 +238,7 @@ def test_math_sendcmd(math): """Send a command from a math channel.""" cmd = "TEST" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math+1}:{cmd}" - ], - [ - ] + ik.tektronix.TekDPO70000, [f"MATH{math+1}:{cmd}"], [] ) as inst: inst.math[math].sendcmd(cmd) @@ -253,32 +249,24 @@ def test_math_query(math): cmd = "TEST" answ = "ANSWER" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math+1}:{cmd}" - ], - [ - answ - ] + ik.tektronix.TekDPO70000, [f"MATH{math+1}:{cmd}"], [answ] ) as inst: assert inst.math[math].query(cmd) == answ -@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", - blacklist_categories=('Cs',)))) +@given( + value=st.text( + alphabet=st.characters(blacklist_characters="\n", blacklist_categories=("Cs",)) + ) +) def test_math_define(value): """Get / set a string operation from the Math mode.""" math = 0 cmd = "DEF" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math+1}:{cmd} \"{value}\"", - f"MATH{math+1}:{cmd}?" - ], - [ - f"\"{value}\"" - ] + ik.tektronix.TekDPO70000, + [f'MATH{math+1}:{cmd} "{value}"', f"MATH{math+1}:{cmd}?"], + [f'"{value}"'], ) as inst: inst.math[math].define = value assert inst.math[math].define == value @@ -290,14 +278,9 @@ def test_math_filter_mode(value): math = 0 cmd = "FILT:MOD" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value.value}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value.value}", f"MATH{math + 1}:{cmd}?"], + [f"{value.value}"], ) as inst: inst.math[math].filter_mode = value assert inst.math[math].filter_mode == value @@ -310,76 +293,70 @@ def test_math_filter_risetime(value): cmd = "FILT:RIS" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].filter_risetime = value inst.math[math].filter_risetime = value_unitful unit_eq(inst.math[math].filter_risetime, value_unitful) -@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", - blacklist_categories=('Cs',)))) +@given( + value=st.text( + alphabet=st.characters(blacklist_characters="\n", blacklist_categories=("Cs",)) + ) +) def test_math_label(value): """Get / set a label for the math channel.""" math = 0 cmd = "LAB:NAM" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math+1}:{cmd} \"{value}\"", - f"MATH{math+1}:{cmd}?" - ], - [ - f"\"{value}\"" - ] + ik.tektronix.TekDPO70000, + [f'MATH{math+1}:{cmd} "{value}"', f"MATH{math+1}:{cmd}?"], + [f'"{value}"'], ) as inst: inst.math[math].label = value assert inst.math[math].label == value -@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.HOR_DIVS, - max_value=ik.tektronix.TekDPO70000.HOR_DIVS)) +@given( + value=st.floats( + min_value=-ik.tektronix.TekDPO70000.HOR_DIVS, + max_value=ik.tektronix.TekDPO70000.HOR_DIVS, + ) +) def test_math_label_xpos(value): """Get / set x position for label.""" math = 0 cmd = "LAB:XPOS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].label_xpos = value assert inst.math[math].label_xpos == value -@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, - max_value=ik.tektronix.TekDPO70000.VERT_DIVS)) +@given( + value=st.floats( + min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS, + ) +) def test_math_label_ypos(value): """Get / set y position for label.""" math = 0 cmd = "LAB:YPOS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].label_ypos = value assert inst.math[math].label_ypos == value @@ -391,14 +368,9 @@ def test_math_num_avg(value): math = 0 cmd = "NUMAV" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].num_avg = value assert inst.math[math].num_avg == pytest.approx(value) @@ -411,15 +383,13 @@ def test_math_spectral_center(value): cmd = "SPEC:CENTER" value_unitful = u.Quantity(value, u.Hz) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].spectral_center = value inst.math[math].spectral_center = value_unitful @@ -433,15 +403,13 @@ def test_math_spectral_gatepos(value): cmd = "SPEC:GATEPOS" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].spectral_gatepos = value inst.math[math].spectral_gatepos = value_unitful @@ -455,15 +423,13 @@ def test_math_spectral_gatewidth(value): cmd = "SPEC:GATEWIDTH" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].spectral_gatewidth = value inst.math[math].spectral_gatewidth = value_unitful @@ -477,14 +443,9 @@ def test_math_spectral_lock(value): cmd = "SPEC:LOCK" value_io = "ON" if value else "OFF" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value_io}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value_io}", f"MATH{math + 1}:{cmd}?"], + [f"{value_io}"], ) as inst: inst.math[math].spectral_lock = value assert inst.math[math].spectral_lock == value @@ -496,14 +457,9 @@ def test_math_spectral_mag(value): math = 0 cmd = "SPEC:MAG" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value.value}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value.value}", f"MATH{math + 1}:{cmd}?"], + [f"{value.value}"], ) as inst: inst.math[math].spectral_mag = value assert inst.math[math].spectral_mag == value @@ -515,14 +471,9 @@ def test_math_spectral_phase(value): math = 0 cmd = "SPEC:PHASE" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value.value}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value.value}", f"MATH{math + 1}:{cmd}?"], + [f"{value.value}"], ) as inst: inst.math[math].spectral_phase = value assert inst.math[math].spectral_phase == value @@ -534,14 +485,9 @@ def test_math_spectral_reflevel(value): math = 0 cmd = "SPEC:REFL" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].spectral_reflevel = value assert inst.math[math].spectral_reflevel == value @@ -553,14 +499,9 @@ def test_math_spectral_reflevel_offset(value): math = 0 cmd = "SPEC:REFLEVELO" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].spectral_reflevel_offset = value assert inst.math[math].spectral_reflevel_offset == value @@ -573,15 +514,13 @@ def test_math_spectral_resolution_bandwidth(value): cmd = "SPEC:RESB" value_unitful = u.Quantity(value, u.Hz) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].spectral_resolution_bandwidth = value inst.math[math].spectral_resolution_bandwidth = value_unitful @@ -595,15 +534,13 @@ def test_math_spectral_span(value): cmd = "SPEC:SPAN" value_unitful = u.Quantity(value, u.Hz) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].spectral_span = value inst.math[math].spectral_span = value_unitful @@ -616,14 +553,9 @@ def test_math_spectral_suppress(value): math = 0 cmd = "SPEC:SUPP" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].spectral_suppress = value assert inst.math[math].spectral_suppress == value @@ -636,14 +568,9 @@ def test_math_spectral_unwrap(value): cmd = "SPEC:UNWR" value_io = "ON" if value else "OFF" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value_io}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value_io}", f"MATH{math + 1}:{cmd}?"], + [f"{value_io}"], ) as inst: inst.math[math].spectral_unwrap = value assert inst.math[math].spectral_unwrap == value @@ -655,14 +582,9 @@ def test_math_spectral_window(value): math = 0 cmd = "SPEC:WIN" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value.value}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value.value}", f"MATH{math + 1}:{cmd}?"], + [f"{value.value}"], ) as inst: inst.math[math].spectral_window = value assert inst.math[math].spectral_window == value @@ -675,36 +597,32 @@ def test_math_threshold(value): cmd = "THRESH" value_unitful = u.Quantity(value, u.V) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].threshhold = value inst.math[math].threshhold = value_unitful unit_eq(inst.math[math].threshhold, value_unitful) -@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", - blacklist_categories=('Cs',)))) +@given( + value=st.text( + alphabet=st.characters(blacklist_characters="\n", blacklist_categories=("Cs",)) + ) +) def test_math_units(value): """Get / set a label for the units.""" math = 0 cmd = "UNITS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math+1}:{cmd} \"{value}\"", - f"MATH{math+1}:{cmd}?" - ], - [ - f"\"{value}\"" - ] + ik.tektronix.TekDPO70000, + [f'MATH{math+1}:{cmd} "{value}"', f"MATH{math+1}:{cmd}?"], + [f'"{value}"'], ) as inst: inst.math[math].unit_string = value assert inst.math[math].unit_string == value @@ -717,34 +635,28 @@ def test_math_autoscale(value): cmd = "VERT:AUTOSC" value_io = "ON" if value else "OFF" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value_io}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value_io}", f"MATH{math + 1}:{cmd}?"], + [f"{value_io}"], ) as inst: inst.math[math].autoscale = value assert inst.math[math].autoscale == value -@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS / 2, - max_value=ik.tektronix.TekDPO70000.VERT_DIVS / 2)) +@given( + value=st.floats( + min_value=-ik.tektronix.TekDPO70000.VERT_DIVS / 2, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS / 2, + ) +) def test_math_position(value): """Get / set spectral vertical position from center.""" math = 0 cmd = "VERT:POS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:{cmd} {value:e}", f"MATH{math + 1}:{cmd}?"], + [f"{value}"], ) as inst: inst.math[math].position = value assert inst.math[math].position == value @@ -757,46 +669,43 @@ def test_math_scale(value): cmd = "VERT:SCALE" value_unitful = u.Quantity(value, u.V) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd} {value:e}", - f"MATH{math + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd} {value:e}", + f"MATH{math + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.math[math].scale = value inst.math[math].scale = value_unitful unit_eq(inst.math[math].scale, value_unitful) -@given(values=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), - min_size=1)) +@given( + values=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), min_size=1) +) def test_math_scale_raw_data(values): """Return scaled raw data according to current settings.""" math = 0 - scale = 1. * u.V + scale = 1.0 * u.V position = -2.3 - expected_value = tuple(scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) - for v in values) + expected_value = tuple( + scale + * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) + for v in values + ) if numpy: values = numpy.array(values) - expected_value = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * - values.astype(float) / (2 ** 15) - position - ) + expected_value = scale * ( + (ik.tektronix.TekDPO70000.VERT_DIVS / 2) * values.astype(float) / (2 ** 15) + - position + ) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"MATH{math + 1}:VERT:SCALE?", - f"MATH{math + 1}:VERT:POS?" - ], - [ - f"{scale}", - f"{position}" - ] + ik.tektronix.TekDPO70000, + [f"MATH{math + 1}:VERT:SCALE?", f"MATH{math + 1}:VERT:POS?"], + [f"{scale}", f"{position}"], ) as inst: iterable_eq(inst.math[math]._scale_raw_data(values), expected_value) @@ -807,13 +716,7 @@ def test_math_scale_raw_data(values): @pytest.mark.parametrize("channel", [it for it in range(4)]) def test_channel_init(channel): """Initialize a channel.""" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [], []) as inst: assert inst.channel[channel]._parent is inst assert inst.channel[channel]._idx == channel + 1 @@ -823,12 +726,7 @@ def test_channel_sendcmd(channel): """Send a command from a channel.""" cmd = "TEST" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd}" - ], - [ - ] + ik.tektronix.TekDPO70000, [f"CH{channel+1}:{cmd}"], [] ) as inst: inst.channel[channel].sendcmd(cmd) @@ -839,13 +737,7 @@ def test_channel_query(channel): cmd = "TEST" answ = "ANSWER" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd}" - ], - [ - answ - ] + ik.tektronix.TekDPO70000, [f"CH{channel+1}:{cmd}"], [answ] ) as inst: assert inst.channel[channel].query(cmd) == answ @@ -856,14 +748,9 @@ def test_channel_coupling(value): channel = 0 cmd = "COUP" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd} {value.value}", - f"CH{channel+1}:{cmd}?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"CH{channel+1}:{cmd} {value.value}", f"CH{channel+1}:{cmd}?"], + [f"{value.value}"], ) as inst: inst.channel[channel].coupling = value assert inst.channel[channel].coupling == value @@ -879,15 +766,13 @@ def test_channel_bandwidth(value): cmd = "BAN" value_unitful = u.Quantity(value, u.Hz) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.channel[channel].bandwidth = value inst.channel[channel].bandwidth = value_unitful @@ -904,15 +789,13 @@ def test_channel_deskew(value): cmd = "DESK" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.channel[channel].deskew = value inst.channel[channel].deskew = value_unitful @@ -930,76 +813,70 @@ def test_channel_termination(value): cmd = "TERM" value_unitful = u.Quantity(value, u.ohm) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.channel[channel].termination = value inst.channel[channel].termination = value_unitful unit_eq(inst.channel[channel].termination, value_unitful) -@given(value=st.text(alphabet=st.characters(blacklist_characters="\n", - blacklist_categories=('Cs',)))) +@given( + value=st.text( + alphabet=st.characters(blacklist_characters="\n", blacklist_categories=("Cs",)) + ) +) def test_channel_label(value): """Get / set human readable label for channel.""" channel = 0 cmd = "LAB:NAM" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd} \"{value}\"", - f"CH{channel+1}:{cmd}?" - ], - [ - f"\"{value}\"" - ] + ik.tektronix.TekDPO70000, + [f'CH{channel+1}:{cmd} "{value}"', f"CH{channel+1}:{cmd}?"], + [f'"{value}"'], ) as inst: inst.channel[channel].label = value assert inst.channel[channel].label == value -@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.HOR_DIVS, - max_value=ik.tektronix.TekDPO70000.HOR_DIVS)) +@given( + value=st.floats( + min_value=-ik.tektronix.TekDPO70000.HOR_DIVS, + max_value=ik.tektronix.TekDPO70000.HOR_DIVS, + ) +) def test_channel_label_xpos(value): """Get / set x position for label.""" channel = 0 cmd = "LAB:XPOS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd} {value:e}", - f"CH{channel+1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"CH{channel+1}:{cmd} {value:e}", f"CH{channel+1}:{cmd}?"], + [f"{value}"], ) as inst: inst.channel[channel].label_xpos = value assert inst.channel[channel].label_xpos == value -@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, - max_value=ik.tektronix.TekDPO70000.VERT_DIVS)) +@given( + value=st.floats( + min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS, + ) +) def test_channel_label_ypos(value): """Get / set y position for label.""" channel = 0 cmd = "LAB:YPOS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd} {value:e}", - f"CH{channel+1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"CH{channel+1}:{cmd} {value:e}", f"CH{channel+1}:{cmd}?"], + [f"{value}"], ) as inst: inst.channel[channel].label_ypos = value assert inst.channel[channel].label_ypos == value @@ -1012,36 +889,33 @@ def test_channel_offset(value): cmd = "OFFS" value_unitful = u.Quantity(value, u.V) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.channel[channel].offset = value inst.channel[channel].offset = value_unitful unit_eq(inst.channel[channel].offset, value_unitful) -@given(value=st.floats(min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, - max_value=ik.tektronix.TekDPO70000.VERT_DIVS)) +@given( + value=st.floats( + min_value=-ik.tektronix.TekDPO70000.VERT_DIVS, + max_value=ik.tektronix.TekDPO70000.VERT_DIVS, + ) +) def test_channel_position(value): """Get / set vertical position.""" channel = 0 cmd = "POS" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel+1}:{cmd} {value:e}", - f"CH{channel+1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"CH{channel+1}:{cmd} {value:e}", f"CH{channel+1}:{cmd}?"], + [f"{value}"], ) as inst: inst.channel[channel].position = value assert inst.channel[channel].position == value @@ -1054,48 +928,49 @@ def test_channel_scale(value): cmd = "SCALE" value_unitful = u.Quantity(value, u.V) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd} {value:e}", - f"CH{channel + 1}:{cmd}?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd} {value:e}", + f"CH{channel + 1}:{cmd}?", + ], + [f"{value}"], ) as inst: inst.channel[channel].scale = value inst.channel[channel].scale = value_unitful unit_eq(inst.channel[channel].scale, value_unitful) -@given(values=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), - min_size=1)) +@given( + values=st.lists(st.floats(min_value=-2147483648, max_value=2147483647), min_size=1) +) def test_channel_scale_raw_data(values): """Return scaled raw data according to current settings.""" channel = 0 - scale = u.Quantity(1., u.V) - position = -1. - offset = u.Quantity(0., u.V) - expected_value = tuple(scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) - for v in values) + scale = u.Quantity(1.0, u.V) + position = -1.0 + offset = u.Quantity(0.0, u.V) + expected_value = tuple( + scale + * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * float(v) / (2 ** 15) - position) + for v in values + ) if numpy: values = numpy.array(values) - expected_value = scale * ((ik.tektronix.TekDPO70000.VERT_DIVS / 2) * - values.astype(float) / (2**15) - - position) + offset - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"CH{channel + 1}:SCALE?", - f"CH{channel + 1}:POS?", - f"CH{channel + 1}:OFFS?" - ], - [ - f"{scale}", - f"{position}", - f"{offset}" - ] + expected_value = ( + scale + * ( + (ik.tektronix.TekDPO70000.VERT_DIVS / 2) + * values.astype(float) + / (2 ** 15) + - position + ) + + offset + ) + with expected_protocol( + ik.tektronix.TekDPO70000, + [f"CH{channel + 1}:SCALE?", f"CH{channel + 1}:POS?", f"CH{channel + 1}:OFFS?"], + [f"{scale}", f"{position}", f"{offset}"], ) as inst: actual_data = inst.channel[channel]._scale_raw_data(values) iterable_eq(actual_data, expected_value) @@ -1108,14 +983,9 @@ def test_channel_scale_raw_data(values): def test_acquire_enhanced_enob(value): """Get / set enhanced effective number of bits.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:ENHANCEDE {value}", - "ACQ:ENHANCEDE?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"ACQ:ENHANCEDE {value}", "ACQ:ENHANCEDE?"], + [f"{value}"], ) as inst: inst.acquire_enhanced_enob = value assert inst.acquire_enhanced_enob == value @@ -1126,14 +996,9 @@ def test_acquire_enhanced_state(value): """Get / set state of enhanced effective number of bits.""" value_io = "1" if value else "0" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:ENHANCEDE:STATE {value_io}", - "ACQ:ENHANCEDE:STATE?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, + [f"ACQ:ENHANCEDE:STATE {value_io}", "ACQ:ENHANCEDE:STATE?"], + [f"{value_io}"], ) as inst: inst.acquire_enhanced_state = value assert inst.acquire_enhanced_state == value @@ -1143,14 +1008,7 @@ def test_acquire_enhanced_state(value): def test_acquire_interp_8bit(value): """Get / set interpolation method of instrument.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:INTERPE {value}", - "ACQ:INTERPE?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"ACQ:INTERPE {value}", "ACQ:INTERPE?"], [f"{value}"] ) as inst: inst.acquire_interp_8bit = value assert inst.acquire_interp_8bit == value @@ -1161,14 +1019,7 @@ def test_acquire_magnivu(value): """Get / set MagniVu feature.""" value_io = "ON" if value else "OFF" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:MAG {value_io}", - "ACQ:MAG?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, [f"ACQ:MAG {value_io}", "ACQ:MAG?"], [f"{value_io}"] ) as inst: inst.acquire_magnivu = value assert inst.acquire_magnivu == value @@ -1178,14 +1029,9 @@ def test_acquire_magnivu(value): def test_acquire_mode(value): """Get / set acquisition mode.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:MOD {value.value}", - "ACQ:MOD?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"ACQ:MOD {value.value}", "ACQ:MOD?"], + [f"{value.value}"], ) as inst: inst.acquire_mode = value assert inst.acquire_mode == value @@ -1195,28 +1041,16 @@ def test_acquire_mode(value): def test_acquire_mode_actual(value): """Get actually used acquisition mode (query only).""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "ACQ:MOD:ACT?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, ["ACQ:MOD:ACT?"], [f"{value.value}"] ) as inst: assert inst.acquire_mode_actual == value -@given(value=st.integers(min_value=0, max_value=2**30-1)) +@given(value=st.integers(min_value=0, max_value=2 ** 30 - 1)) def test_acquire_num_acquisitions(value): """Get number of waveform acquisitions since start (query only).""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "ACQ:NUMAC?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, ["ACQ:NUMAC?"], [f"{value}"] ) as inst: assert inst.acquire_num_acquisitions == value @@ -1225,14 +1059,7 @@ def test_acquire_num_acquisitions(value): def test_acquire_num_avgs(value): """Get / set number of waveform acquisitions to average.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:NUMAV {value}", - "ACQ:NUMAV?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"ACQ:NUMAV {value}", "ACQ:NUMAV?"], [f"{value}"] ) as inst: inst.acquire_num_avgs = value assert inst.acquire_num_avgs == value @@ -1242,14 +1069,7 @@ def test_acquire_num_avgs(value): def test_acquire_num_envelop(value): """Get / set number of waveform acquisitions to envelope.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:NUME {value}", - "ACQ:NUME?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"ACQ:NUME {value}", "ACQ:NUME?"], [f"{value}"] ) as inst: inst.acquire_num_envelop = value assert inst.acquire_num_envelop == value @@ -1262,13 +1082,7 @@ def test_acquire_num_frames(value): Query only. """ with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "ACQ:NUMFRAMESACQ?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, ["ACQ:NUMFRAMESACQ?"], [f"{value}"] ) as inst: assert inst.acquire_num_frames == value @@ -1277,14 +1091,7 @@ def test_acquire_num_frames(value): def test_acquire_num_samples(value): """Get / set number of acquired samples to make up waveform database.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:NUMSAM {value}", - "ACQ:NUMSAM?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"ACQ:NUMSAM {value}", "ACQ:NUMSAM?"], [f"{value}"] ) as inst: inst.acquire_num_samples = value assert inst.acquire_num_samples == value @@ -1294,14 +1101,9 @@ def test_acquire_num_samples(value): def test_acquire_sampling_mode(value): """Get / set sampling mode.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:SAMP {value.value}", - "ACQ:SAMP?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"ACQ:SAMP {value.value}", "ACQ:SAMP?"], + [f"{value.value}"], ) as inst: inst.acquire_sampling_mode = value assert inst.acquire_sampling_mode == value @@ -1311,14 +1113,9 @@ def test_acquire_sampling_mode(value): def test_acquire_state(value): """Get / set acquisition state.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:STATE {value.value}", - "ACQ:STATE?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"ACQ:STATE {value.value}", "ACQ:STATE?"], + [f"{value.value}"], ) as inst: inst.acquire_state = value assert inst.acquire_state == value @@ -1328,14 +1125,9 @@ def test_acquire_state(value): def test_acquire_stop_after(value): """Get / set whether acquisition is continuous.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"ACQ:STOPA {value.value}", - "ACQ:STOPA?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"ACQ:STOPA {value.value}", "ACQ:STOPA?"], + [f"{value.value}"], ) as inst: inst.acquire_stop_after = value assert inst.acquire_stop_after == value @@ -1345,14 +1137,9 @@ def test_acquire_stop_after(value): def test_data_framestart(value): """Get / set start frame for waveform transfer.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:FRAMESTAR {value}", - "DAT:FRAMESTAR?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"DAT:FRAMESTAR {value}", "DAT:FRAMESTAR?"], + [f"{value}"], ) as inst: inst.data_framestart = value assert inst.data_framestart == value @@ -1362,14 +1149,9 @@ def test_data_framestart(value): def test_data_framestop(value): """Get / set stop frame for waveform transfer.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:FRAMESTOP {value}", - "DAT:FRAMESTOP?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"DAT:FRAMESTOP {value}", "DAT:FRAMESTOP?"], + [f"{value}"], ) as inst: inst.data_framestop = value assert inst.data_framestop == value @@ -1379,14 +1161,7 @@ def test_data_framestop(value): def test_data_start(value): """Get / set start data point for waveform transfer.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:STAR {value}", - "DAT:STAR?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"DAT:STAR {value}", "DAT:STAR?"], [f"{value}"] ) as inst: inst.data_start = value assert inst.data_start == value @@ -1396,14 +1171,7 @@ def test_data_start(value): def test_data_stop(value): """Get / set stop data point for waveform transfer.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:STOP {value}", - "DAT:STOP?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"DAT:STOP {value}", "DAT:STOP?"], [f"{value}"] ) as inst: inst.data_stop = value assert inst.data_stop == value @@ -1414,14 +1182,9 @@ def test_data_sync_sources(value): """Get / set if data sync sources are on or off.""" value_io = "ON" if value else "OFF" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:SYNCSOU {value_io}", - "DAT:SYNCSOU?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, + [f"DAT:SYNCSOU {value_io}", "DAT:SYNCSOU?"], + [f"{value_io}"], ) as inst: inst.data_sync_sources = value assert inst.data_sync_sources == value @@ -1435,14 +1198,9 @@ def test_data_source_channel(no): """Get / set channel as data source.""" channel_name = f"CH{no + 1}" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:SOU {channel_name}", - f"DAT:SOU?" - ], - [ - channel_name - ] + ik.tektronix.TekDPO70000, + [f"DAT:SOU {channel_name}", f"DAT:SOU?"], + [channel_name], ) as inst: channel = inst.channel[no] inst.data_source = channel @@ -1458,17 +1216,10 @@ def test_data_source_math(no, mocker): math_name = f"MATH{no + 1}" # patch call to time.sleep with mock - mock_time = mocker.patch.object(time, 'sleep', return_value=None) + mock_time = mocker.patch.object(time, "sleep", return_value=None) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:SOU {math_name}", - f"DAT:SOU?" - ], - [ - math_name - ] + ik.tektronix.TekDPO70000, [f"DAT:SOU {math_name}", f"DAT:SOU?"], [math_name] ) as inst: math = inst.math[no] inst.data_source = math @@ -1481,15 +1232,7 @@ def test_data_source_math(no, mocker): def test_data_source_ref_not_implemented_error(): """Get / set a reference channel raises a NotImplemented error.""" ref_name = "REF1" # example, range not important - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:SOU?" - ], - [ - ref_name - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [f"DAT:SOU?"], [ref_name]) as inst: # getter with pytest.raises(NotImplementedError): print(inst.data_source) @@ -1501,15 +1244,7 @@ def test_data_source_ref_not_implemented_error(): def test_data_source_not_implemented_error(): """Get a data source that is currently not implemented.""" ds_name = "HHG29" # example, range not important - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"DAT:SOU?" - ], - [ - ds_name - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [f"DAT:SOU?"], [ds_name]) as inst: with pytest.raises(NotImplementedError): print(inst.data_source) @@ -1517,18 +1252,11 @@ def test_data_source_not_implemented_error(): def test_data_source_invalid_type(): """Raise TypeError when a wrong type is set for data source.""" invalid_data_source = 42 - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [], []) as inst: with pytest.raises(TypeError) as exc_info: inst.data_source = invalid_data_source exc_msg = exc_info.value.args[0] - assert exc_msg == f"{type(invalid_data_source)} is not a valid data " \ - f"source." + assert exc_msg == f"{type(invalid_data_source)} is not a valid data " f"source." @given(value=st.floats(min_value=0, max_value=1000)) @@ -1536,13 +1264,7 @@ def test_horiz_acq_duration(value): """Get horizontal acquisition duration (query only).""" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "HOR:ACQDURATION?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, ["HOR:ACQDURATION?"], [f"{value}"] ) as inst: unit_eq(inst.horiz_acq_duration, value_unitful) @@ -1551,13 +1273,7 @@ def test_horiz_acq_duration(value): def test_horiz_acq_length(value): """Get horizontal acquisition length (query only).""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "HOR:ACQLENGTH?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, ["HOR:ACQLENGTH?"], [f"{value}"] ) as inst: assert inst.horiz_acq_length == value @@ -1567,14 +1283,9 @@ def test_horiz_delay_mode(value): """Get / set state of horizontal delay mode.""" value_io = "1" if value else "0" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:DEL:MOD {value_io}", - "HOR:DEL:MOD?" - ], - [ - f"{value_io}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:DEL:MOD {value_io}", "HOR:DEL:MOD?"], + [f"{value_io}"], ) as inst: inst.horiz_delay_mode = value assert inst.horiz_delay_mode == value @@ -1587,15 +1298,9 @@ def test_horiz_delay_pos(value): Test setting unitful and without units.""" value_unitful = u.Quantity(value, u.percent) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:DEL:POS {value:e}", - f"HOR:DEL:POS {value:e}", - "HOR:DEL:POS?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:DEL:POS {value:e}", f"HOR:DEL:POS {value:e}", "HOR:DEL:POS?"], + [f"{value}"], ) as inst: inst.horiz_delay_pos = value inst.horiz_delay_pos = value_unitful @@ -1607,15 +1312,9 @@ def test_horiz_delay_time(value): """Get / set horizontal delay time.""" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:DEL:TIM {value:e}", - f"HOR:DEL:TIM {value:e}", - "HOR:DEL:TIM?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:DEL:TIM {value:e}", f"HOR:DEL:TIM {value:e}", "HOR:DEL:TIM?"], + [f"{value}"], ) as inst: inst.horiz_delay_time = value inst.horiz_delay_time = value_unitful @@ -1626,13 +1325,7 @@ def test_horiz_delay_time(value): def test_horiz_interp_ratio(value): """Get horizontal interpolation ratio (query only).""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "HOR:MAI:INTERPR?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, ["HOR:MAI:INTERPR?"], [f"{value}"] ) as inst: assert inst.horiz_interp_ratio == value @@ -1644,15 +1337,9 @@ def test_horiz_main_pos(value): Test setting unitful and without units.""" value_unitful = u.Quantity(value, u.percent) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MAI:POS {value:e}", - f"HOR:MAI:POS {value:e}", - "HOR:MAI:POS?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:MAI:POS {value:e}", f"HOR:MAI:POS {value:e}", "HOR:MAI:POS?"], + [f"{value}"], ) as inst: inst.horiz_main_pos = value inst.horiz_main_pos = value_unitful @@ -1663,14 +1350,9 @@ def test_horiz_unit(): """Get / set horizontal unit string.""" unit_string = "LUM" # as example in manual with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MAI:UNI \"{unit_string}\"", - "HOR:MAI:UNI?" - ], - [ - f"\"{unit_string}\"" - ] + ik.tektronix.TekDPO70000, + [f'HOR:MAI:UNI "{unit_string}"', "HOR:MAI:UNI?"], + [f'"{unit_string}"'], ) as inst: inst.horiz_unit = unit_string assert inst.horiz_unit == unit_string @@ -1680,14 +1362,9 @@ def test_horiz_unit(): def test_horiz_mode(value): """Get / set horizontal mode.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MODE {value.value}", - "HOR:MODE?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:MODE {value.value}", "HOR:MODE?"], + [f"{value.value}"], ) as inst: inst.horiz_mode = value assert inst.horiz_mode == value @@ -1697,14 +1374,9 @@ def test_horiz_mode(value): def test_horiz_record_length_lim(value): """Get / set horizontal record length limit.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MODE:AUTO:LIMIT {value}", - "HOR:MODE:AUTO:LIMIT?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:MODE:AUTO:LIMIT {value}", "HOR:MODE:AUTO:LIMIT?"], + [f"{value}"], ) as inst: inst.horiz_record_length_lim = value assert inst.horiz_record_length_lim == value @@ -1714,14 +1386,9 @@ def test_horiz_record_length_lim(value): def test_horiz_record_length(value): """Get / set horizontal record length.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MODE:RECO {value}", - "HOR:MODE:RECO?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:MODE:RECO {value}", "HOR:MODE:RECO?"], + [f"{value}"], ) as inst: inst.horiz_record_length = value assert inst.horiz_record_length == value @@ -1734,15 +1401,13 @@ def test_horiz_sample_rate(value): Set with and without units.""" value_unitful = u.Quantity(value, u.Hz) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MODE:SAMPLER {value:e}", - f"HOR:MODE:SAMPLER {value:e}", - f"HOR:MODE:SAMPLER?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [ + f"HOR:MODE:SAMPLER {value:e}", + f"HOR:MODE:SAMPLER {value:e}", + f"HOR:MODE:SAMPLER?", + ], + [f"{value}"], ) as inst: inst.horiz_sample_rate = value_unitful inst.horiz_sample_rate = value @@ -1756,15 +1421,9 @@ def test_horiz_scale(value): Set with and without units.""" value_unitful = u.Quantity(value, u.s) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:MODE:SCA {value:e}", - f"HOR:MODE:SCA {value:e}", - f"HOR:MODE:SCA?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:MODE:SCA {value:e}", f"HOR:MODE:SCA {value:e}", f"HOR:MODE:SCA?"], + [f"{value}"], ) as inst: inst.horiz_scale = value_unitful inst.horiz_scale = value @@ -1779,15 +1438,9 @@ def test_horiz_pos(value): """ value_unitful = u.Quantity(value, u.percent) with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:POS {value:e}", - f"HOR:POS {value:e}", - f"HOR:POS?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, + [f"HOR:POS {value:e}", f"HOR:POS {value:e}", f"HOR:POS?"], + [f"{value}"], ) as inst: inst.horiz_pos = value_unitful inst.horiz_pos = value @@ -1798,14 +1451,7 @@ def test_horiz_pos(value): def test_horiz_roll(value): """Get / set roll mode status.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"HOR:ROLL {value}", - f"HOR:ROLL?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"HOR:ROLL {value}", f"HOR:ROLL?"], [f"{value}"] ) as inst: inst.horiz_roll = value assert inst.horiz_roll == value @@ -1815,14 +1461,9 @@ def test_horiz_roll(value): def test_trigger_state(value): """Get / set the trigger state.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"TRIG:STATE {value.value}", - "TRIG:STATE?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"TRIG:STATE {value.value}", "TRIG:STATE?"], + [f"{value.value}"], ) as inst: inst.trigger_state = value assert inst.trigger_state == value @@ -1832,14 +1473,9 @@ def test_trigger_state(value): def test_outgoing_waveform_encoding(value): """Get / set the encoding used for outgoing waveforms.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"WFMO:ENC {value.value}", - "WFMO:ENC?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"WFMO:ENC {value.value}", "WFMO:ENC?"], + [f"{value.value}"], ) as inst: inst.outgoing_waveform_encoding = value assert inst.outgoing_waveform_encoding == value @@ -1849,14 +1485,9 @@ def test_outgoing_waveform_encoding(value): def test_outgoing_byte_format(value): """Get / set the binary format for outgoing waveforms.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"WFMO:BN_F {value.value}", - "WFMO:BN_F?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"WFMO:BN_F {value.value}", "WFMO:BN_F?"], + [f"{value.value}"], ) as inst: inst.outgoing_binary_format = value assert inst.outgoing_binary_format == value @@ -1866,14 +1497,9 @@ def test_outgoing_byte_format(value): def test_outgoing_byte_order(value): """Get / set the binary data endianness for outgoing waveforms.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"WFMO:BYT_O {value.value}", - "WFMO:BYT_O?" - ], - [ - f"{value.value}" - ] + ik.tektronix.TekDPO70000, + [f"WFMO:BYT_O {value.value}", "WFMO:BYT_O?"], + [f"{value.value}"], ) as inst: inst.outgoing_byte_order = value assert inst.outgoing_byte_order == value @@ -1883,14 +1509,7 @@ def test_outgoing_byte_order(value): def test_outgoing_n_bytes(value): """Get / set the number of bytes sampled in waveforms binary encoding.""" with expected_protocol( - ik.tektronix.TekDPO70000, - [ - f"WFMO:BYT_N {value}", - "WFMO:BYT_N?" - ], - [ - f"{value}" - ] + ik.tektronix.TekDPO70000, [f"WFMO:BYT_N {value}", "WFMO:BYT_N?"], [f"{value}"] ) as inst: inst.outgoing_n_bytes = value assert inst.outgoing_n_bytes == value @@ -1901,51 +1520,23 @@ def test_outgoing_n_bytes(value): def test_select_fastest_encoding(): """Sets encoding to fastest methods.""" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "DAT:ENC FAS" - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, ["DAT:ENC FAS"], []) as inst: inst.select_fastest_encoding() def test_force_trigger(): """Force a trivver event.""" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - "TRIG FORC" - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, ["TRIG FORC"], []) as inst: inst.force_trigger() def test_run(): """Enables the trigger for the oscilloscope.""" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ":RUN" - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [":RUN"], []) as inst: inst.run() def test_stop(): """Disables the trigger for the oscilloscope.""" - with expected_protocol( - ik.tektronix.TekDPO70000, - [ - ":STOP" - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekDPO70000, [":STOP"], []) as inst: inst.stop() diff --git a/instruments/tests/test_tektronix/test_tektronix_tds224.py b/instruments/tests/test_tektronix/test_tektronix_tds224.py index 07331196a..2060fd829 100644 --- a/instruments/tests/test_tektronix/test_tektronix_tds224.py +++ b/instruments/tests/test_tektronix/test_tektronix_tds224.py @@ -31,7 +31,7 @@ @pytest.fixture(autouse=True) def mock_time(mocker): """Mock time to replace time.sleep.""" - return mocker.patch.object(time, 'sleep', return_value=None) + return mocker.patch.object(time, "sleep", return_value=None) test_tektds224_name = make_name_test(ik.tektronix.TekTDS224) @@ -39,37 +39,19 @@ def mock_time(mocker): def test_ref_init(): """Initialize a reference channel.""" - with expected_protocol( - ik.tektronix.TekTDS224, - [ - ], - [ - ] - ) as tek: + with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: assert tek.ref[0]._tek is tek def test_data_source_name(): """Get name of data source.""" - with expected_protocol( - ik.tektronix.TekTDS224, - [ - ], - [ - ] - ) as tek: + with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: assert tek.math.name == "MATH" def test_tektds224_data_width(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DATA:WIDTH?", - "DATA:WIDTH 1" - ], [ - "2" - ] + ik.tektronix.TekTDS224, ["DATA:WIDTH?", "DATA:WIDTH 1"], ["2"] ) as tek: assert tek.data_width == 2 tek.data_width = 1 @@ -78,13 +60,7 @@ def test_tektds224_data_width(): @given(width=st.integers().filter(lambda x: x > 2 or x < 1)) def test_tektds224_data_width_value_error(width): """Raise value error if data_width is out of range.""" - with expected_protocol( - ik.tektronix.TekTDS224, - [ - ], - [ - ] - ) as tek: + with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: with pytest.raises(ValueError) as err_info: tek.data_width = width err_msg = err_info.value.args[0] @@ -93,15 +69,9 @@ def test_tektds224_data_width_value_error(width): def test_tektds224_data_source(mock_time): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DAT:SOU?", - "DAT:SOU?", - "DAT:SOU MATH" - ], [ - "MATH", - "CH1" - ] + ik.tektronix.TekTDS224, + ["DAT:SOU?", "DAT:SOU?", "DAT:SOU MATH"], + ["MATH", "CH1"], ) as tek: assert tek.data_source == tek.math assert tek.data_source == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) @@ -113,40 +83,24 @@ def test_tektds224_data_source(mock_time): def test_tektds224_data_source_with_enum(): """Set data source from an enum.""" + class Channel(Enum): """Fake class to init data_source with enum.""" + channel = "MATH" - with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DAT:SOU MATH" - ], - [ - ] - ) as tek: + with expected_protocol(ik.tektronix.TekTDS224, ["DAT:SOU MATH"], []) as tek: tek.data_source = Channel.channel def test_tektds224_channel(): - with expected_protocol( - ik.tektronix.TekTDS224, - [], - [] - ) as tek: - assert tek.channel[ - 0] == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) + with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: + assert tek.channel[0] == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) def test_tektds224_channel_coupling(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "CH1:COUPL?", - "CH2:COUPL AC" - ], [ - "DC" - ] + ik.tektronix.TekTDS224, ["CH1:COUPL?", "CH2:COUPL AC"], ["DC"] ) as tek: assert tek.channel[0].coupling == tek.Coupling.dc tek.channel[1].coupling = tek.Coupling.ac @@ -155,47 +109,44 @@ def test_tektds224_channel_coupling(): def test_tektds224_channel_coupling_type_error(): """Raise TypeError if coupling setting is wrong type.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS224, - [ - ], - [ - ] - ) as tek: + with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: with pytest.raises(TypeError) as err_info: tek.channel[1].coupling = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Coupling setting must be a `TekTDS224.Coupling` " \ - f"value,got {type(wrong_type)} instead." + assert ( + err_msg == f"Coupling setting must be a `TekTDS224.Coupling` " + f"value,got {type(wrong_type)} instead." + ) def test_tektds224_data_source_read_waveform(): with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DAT:SOU?", - "DAT:SOU CH2", - "DAT:ENC RIB", - "DATA:WIDTH?", - "CURVE?", - "WFMP:CH2:YOF?", - "WFMP:CH2:YMU?", - "WFMP:CH2:YZE?", - "WFMP:XZE?", - "WFMP:XIN?", - "WFMP:CH2:NR_P?", - "DAT:SOU CH1" - ], [ - "CH1", - "2", - # pylint: disable=no-member - "#210" + bytes.fromhex("00000001000200030004").decode("utf-8") + "0", - "1", - "0", - "0", - "1", - "5" - ] + ik.tektronix.TekTDS224, + [ + "DAT:SOU?", + "DAT:SOU CH2", + "DAT:ENC RIB", + "DATA:WIDTH?", + "CURVE?", + "WFMP:CH2:YOF?", + "WFMP:CH2:YMU?", + "WFMP:CH2:YZE?", + "WFMP:XZE?", + "WFMP:XIN?", + "WFMP:CH2:NR_P?", + "DAT:SOU CH1", + ], + [ + "CH1", + "2", + # pylint: disable=no-member + "#210" + bytes.fromhex("00000001000200030004").decode("utf-8") + "0", + "1", + "0", + "0", + "1", + "5", + ], ) as tek: data = tuple(range(5)) if numpy: @@ -205,8 +156,7 @@ def test_tektds224_data_source_read_waveform(): iterable_eq(y, data) -@given(values=st.lists(st.floats(allow_infinity=False, allow_nan=False), - min_size=1)) +@given(values=st.lists(st.floats(allow_infinity=False, allow_nan=False), min_size=1)) def test_tektds224_data_source_read_waveform_ascii(values): """Read waveform as ASCII""" # values @@ -221,36 +171,43 @@ def test_tektds224_data_source_read_waveform_ascii(values): ptcnt = len(values) with expected_protocol( - ik.tektronix.TekTDS224, - [ - "DAT:SOU?", - "DAT:SOU CH2", - "DAT:ENC ASCI", - "CURVE?", - "WFMP:CH2:YOF?", - "WFMP:CH2:YMU?", - "WFMP:CH2:YZE?", - "WFMP:XZE?", - "WFMP:XIN?", - "WFMP:CH2:NR_P?", - "DAT:SOU CH1" - ], [ - "CH1", - values_str, - f"{yoffs}", - f"{ymult}", - f"{yzero}", - f"{xzero}", - f"{xincr}", - f"{ptcnt}" - ] + ik.tektronix.TekTDS224, + [ + "DAT:SOU?", + "DAT:SOU CH2", + "DAT:ENC ASCI", + "CURVE?", + "WFMP:CH2:YOF?", + "WFMP:CH2:YMU?", + "WFMP:CH2:YZE?", + "WFMP:XZE?", + "WFMP:XIN?", + "WFMP:CH2:NR_P?", + "DAT:SOU CH1", + ], + [ + "CH1", + values_str, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xzero}", + f"{xincr}", + f"{ptcnt}", + ], ) as tek: if numpy: x_expected = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) - y_expected = ((numpy.array(values) - float(yoffs)) * float(ymult)) + float(yzero) + y_expected = ((numpy.array(values) - float(yoffs)) * float(ymult)) + float( + yzero + ) else: - x_expected = tuple([float(val) * float(xincr) + float(xzero) for val in range(ptcnt)]) - y_expected = tuple([((val - float(yoffs)) * float(ymult)) + float(yzero) for val in values]) + x_expected = tuple( + [float(val) * float(xincr) + float(xzero) for val in range(ptcnt)] + ) + y_expected = tuple( + [((val - float(yoffs)) * float(ymult)) + float(yzero) for val in values] + ) x_read, y_read = tek.channel[1].read_waveform(bin_format=False) iterable_eq(x_read, x_expected) iterable_eq(y_read, y_expected) @@ -258,12 +215,6 @@ def test_tektds224_data_source_read_waveform_ascii(values): def test_force_trigger(): """Raise NotImplementedError when trying to force a trigger.""" - with expected_protocol( - ik.tektronix.TekTDS224, - [ - ], - [ - ] - ) as tek: + with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: with pytest.raises(NotImplementedError): tek.force_trigger() diff --git a/instruments/tests/test_tektronix/test_tktds5xx.py b/instruments/tests/test_tektronix/test_tktds5xx.py index 23bb9476b..f13259797 100644 --- a/instruments/tests/test_tektronix/test_tktds5xx.py +++ b/instruments/tests/test_tektronix/test_tktds5xx.py @@ -42,18 +42,20 @@ @pytest.mark.parametrize("msr", [it for it in range(3)]) def test_measurement_init(msr): """Initialize a new measurement.""" - meas_categories = ['enabled', 'type', 'units', 'src1', 'src2', 'edge1', - 'edge2', 'dir'] - meas_return = "0;UNDEFINED;\"V\",CH1,CH2,RISE,RISE,FORWARDS" - data_expected = dict(zip(meas_categories, meas_return.split(';'))) - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"MEASU:MEAS{msr+1}?" - ], - [ - meas_return - ] + meas_categories = [ + "enabled", + "type", + "units", + "src1", + "src2", + "edge1", + "edge2", + "dir", + ] + meas_return = '0;UNDEFINED;"V",CH1,CH2,RISE,RISE,FORWARDS' + data_expected = dict(zip(meas_categories, meas_return.split(";"))) + with expected_protocol( + ik.tektronix.TekTDS5xx, [f"MEASU:MEAS{msr+1}?"], [meas_return] ) as inst: measurement = inst.measurement[msr] assert measurement._tek is inst @@ -67,24 +69,26 @@ def test_measurement_read_enabled_true(msr, value): """Read a new measurement value since enabled is true.""" enabled = 1 # initialization dictionary - meas_categories = ['enabled', 'type', 'units', 'src1', 'src2', 'edge1', - 'edge2', 'dir'] - meas_return = f"{enabled};UNDEFINED;\"V\",CH1,CH2,RISE,RISE,FORWARDS" - data_expected = dict(zip(meas_categories, meas_return.split(';'))) + meas_categories = [ + "enabled", + "type", + "units", + "src1", + "src2", + "edge1", + "edge2", + "dir", + ] + meas_return = f'{enabled};UNDEFINED;"V",CH1,CH2,RISE,RISE,FORWARDS' + data_expected = dict(zip(meas_categories, meas_return.split(";"))) # extended dictionary - data_expected['value'] = value + data_expected["value"] = value with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"MEASU:MEAS{msr+1}?", - f"MEASU:MEAS{msr+1}:VAL?" - ], - [ - meas_return, - f"{value}" - ] + ik.tektronix.TekTDS5xx, + [f"MEASU:MEAS{msr+1}?", f"MEASU:MEAS{msr+1}:VAL?"], + [meas_return, f"{value}"], ) as inst: measurement = inst.measurement[msr] assert measurement.read() == data_expected @@ -95,18 +99,20 @@ def test_measurement_read_enabled_false(): msr = 0 enabled = 0 # initialization dictionary - meas_categories = ['enabled', 'type', 'units', 'src1', 'src2', 'edge1', - 'edge2', 'dir'] - meas_return = f"{enabled};UNDEFINED;\"V\",CH1,CH2,RISE,RISE,FORWARDS" - data_expected = dict(zip(meas_categories, meas_return.split(';'))) - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"MEASU:MEAS{msr+1}?" - ], - [ - meas_return - ] + meas_categories = [ + "enabled", + "type", + "units", + "src1", + "src2", + "edge1", + "edge2", + "dir", + ] + meas_return = f'{enabled};UNDEFINED;"V",CH1,CH2,RISE,RISE,FORWARDS' + data_expected = dict(zip(meas_categories, meas_return.split(";"))) + with expected_protocol( + ik.tektronix.TekTDS5xx, [f"MEASU:MEAS{msr+1}?"], [meas_return] ) as inst: measurement = inst.measurement[msr] assert measurement.read() == data_expected @@ -115,15 +121,14 @@ def test_measurement_read_enabled_false(): # DATA SOURCE # -@given(values=st.lists(st.integers(min_value=-32768, max_value=32767), - min_size=1)) +@given(values=st.lists(st.integers(min_value=-32768, max_value=32767), min_size=1)) def test_data_source_read_waveform_binary(values): """Read waveform from data source as binary.""" # constants - to not overkill it with hypothesis channel_no = 0 data_width = 2 - yoffs = 1. - ymult = 1. + yoffs = 1.0 + ymult = 1.0 yzero = 0.3 xincr = 0.001 # make values to compare with @@ -144,29 +149,28 @@ def test_data_source_read_waveform_binary(values): y_calc = tuple(((val - yoffs) * float(ymult)) + float(yzero) for val in values) with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - "DAT:SOU?", - "DAT:ENC RIB", - "DATA:WIDTH?", - "CURVE?", - f"WFMP:CH{channel_no+1}:YOF?", - f"WFMP:CH{channel_no+1}:YMU?", - f"WFMP:CH{channel_no+1}:YZE?", - f"WFMP:CH{channel_no+1}:XIN?", - f"WFMP:CH{channel_no+1}:NR_P?" - - ], - [ - f"CH{channel_no+1}", - f"{data_width}", - b"#" + values_len_of_len + values_len + values_packed, - f"{yoffs}", - f"{ymult}", - f"{yzero}", - f"{xincr}", - f"{ptcnt}" - ] + ik.tektronix.TekTDS5xx, + [ + "DAT:SOU?", + "DAT:ENC RIB", + "DATA:WIDTH?", + "CURVE?", + f"WFMP:CH{channel_no+1}:YOF?", + f"WFMP:CH{channel_no+1}:YMU?", + f"WFMP:CH{channel_no+1}:YZE?", + f"WFMP:CH{channel_no+1}:XIN?", + f"WFMP:CH{channel_no+1}:NR_P?", + ], + [ + f"CH{channel_no+1}", + f"{data_width}", + b"#" + values_len_of_len + values_len + values_packed, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xincr}", + f"{ptcnt}", + ], ) as inst: channel = inst.channel[channel_no] x_read, y_read = channel.read_waveform(bin_format=True) @@ -179,8 +183,8 @@ def test_data_source_read_waveform_ascii(values): """Read waveform from data source as ASCII.""" # constants - to not overkill it with hypothesis channel_no = 0 - yoffs = 1. - ymult = 1. + yoffs = 1.0 + ymult = 1.0 yzero = 0.3 xincr = 0.001 # make values to compare with @@ -199,27 +203,26 @@ def test_data_source_read_waveform_ascii(values): y_calc = tuple(((val - yoffs) * float(ymult)) + float(yzero) for val in values) with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - "DAT:SOU?", - "DAT:ENC ASCI", - "CURVE?", - f"WFMP:CH{channel_no+1}:YOF?", - f"WFMP:CH{channel_no+1}:YMU?", - f"WFMP:CH{channel_no+1}:YZE?", - f"WFMP:CH{channel_no+1}:XIN?", - f"WFMP:CH{channel_no+1}:NR_P?" - - ], - [ - f"CH{channel_no+1}", - values_str, - f"{yoffs}", - f"{ymult}", - f"{yzero}", - f"{xincr}", - f"{ptcnt}" - ] + ik.tektronix.TekTDS5xx, + [ + "DAT:SOU?", + "DAT:ENC ASCI", + "CURVE?", + f"WFMP:CH{channel_no+1}:YOF?", + f"WFMP:CH{channel_no+1}:YMU?", + f"WFMP:CH{channel_no+1}:YZE?", + f"WFMP:CH{channel_no+1}:XIN?", + f"WFMP:CH{channel_no+1}:NR_P?", + ], + [ + f"CH{channel_no+1}", + values_str, + f"{yoffs}", + f"{ymult}", + f"{yzero}", + f"{xincr}", + f"{ptcnt}", + ], ) as inst: channel = inst.channel[channel_no] x_read, y_read = channel.read_waveform(bin_format=False) @@ -233,13 +236,7 @@ def test_data_source_read_waveform_ascii(values): @pytest.mark.parametrize("channel", [it for it in range(4)]) def test_channel_init(channel): """Initialize a new channel.""" - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: assert inst.channel[channel]._parent is inst assert inst.channel[channel]._idx == channel + 1 @@ -249,14 +246,9 @@ def test_channel_coupling(coupl): """Get / set channel coupling.""" channel = 0 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"CH{channel+1}:COUPL {coupl.value}", - f"CH{channel+1}:COUPL?" - ], - [ - f"{coupl.value}" - ] + ik.tektronix.TekTDS5xx, + [f"CH{channel+1}:COUPL {coupl.value}", f"CH{channel+1}:COUPL?"], + [f"{coupl.value}"], ) as inst: inst.channel[channel].coupling = coupl assert inst.channel[channel].coupling == coupl @@ -266,18 +258,14 @@ def test_channel_coupling_type_error(): """Raise type error if channel coupling is set with wrong type.""" wrong_type = 42 channel = 0 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.channel[channel].coupling = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Coupling setting must be a `TekTDS5xx.Coupling` " \ - f"value, got {type(wrong_type)} instead." + assert ( + err_msg == f"Coupling setting must be a `TekTDS5xx.Coupling` " + f"value, got {type(wrong_type)} instead." + ) @pytest.mark.parametrize("bandw", ik.tektronix.TekTDS5xx.Bandwidth) @@ -285,14 +273,9 @@ def test_channel_bandwidth(bandw): """Get / set channel bandwidth.""" channel = 0 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"CH{channel+1}:BAND {bandw.value}", - f"CH{channel+1}:BAND?" - ], - [ - f"{bandw.value}" - ] + ik.tektronix.TekTDS5xx, + [f"CH{channel+1}:BAND {bandw.value}", f"CH{channel+1}:BAND?"], + [f"{bandw.value}"], ) as inst: inst.channel[channel].bandwidth = bandw assert inst.channel[channel].bandwidth == bandw @@ -302,19 +285,15 @@ def test_channel_bandwidth_type_error(): """Raise type error if channel bandwidth is set with wrong type.""" wrong_type = 42 channel = 0 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.channel[channel].bandwidth = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Bandwidth setting must be a " \ - f"`TekTDS5xx.Bandwidth` value, got " \ - f"{type(wrong_type)} instead." + assert ( + err_msg == f"Bandwidth setting must be a " + f"`TekTDS5xx.Bandwidth` value, got " + f"{type(wrong_type)} instead." + ) @pytest.mark.parametrize("imped", ik.tektronix.TekTDS5xx.Impedance) @@ -322,14 +301,9 @@ def test_channel_impedance(imped): """Get / set channel impedance.""" channel = 0 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"CH{channel+1}:IMP {imped.value}", - f"CH{channel+1}:IMP?" - ], - [ - f"{imped.value}" - ] + ik.tektronix.TekTDS5xx, + [f"CH{channel+1}:IMP {imped.value}", f"CH{channel+1}:IMP?"], + [f"{imped.value}"], ) as inst: inst.channel[channel].impedance = imped assert inst.channel[channel].impedance == imped @@ -339,19 +313,15 @@ def test_channel_impedance_type_error(): """Raise type error if channel impedance is set with wrong type.""" wrong_type = 42 channel = 0 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.channel[channel].impedance = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Impedance setting must be a " \ - f"`TekTDS5xx.Impedance` value, got " \ - f"{type(wrong_type)} instead." + assert ( + err_msg == f"Impedance setting must be a " + f"`TekTDS5xx.Impedance` value, got " + f"{type(wrong_type)} instead." + ) @given(value=st.floats(min_value=0, exclude_min=True)) @@ -359,13 +329,7 @@ def test_channel_probe(value): """Get connected probe value.""" channel = 0 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"CH{channel+1}:PRO?" - ], - [ - f"{value}" - ] + ik.tektronix.TekTDS5xx, [f"CH{channel+1}:PRO?"], [f"{value}"] ) as inst: value_expected = round(1 / value, 0) assert inst.channel[channel].probe == value_expected @@ -376,16 +340,13 @@ def test_channel_scale(value): """Get / set scale setting.""" channel = 0 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"CH{channel + 1}:SCA {value:.3E}", - f"CH{channel + 1}:SCA?", - f"CH{channel + 1}:SCA?" - ], - [ - f"{value}", - f"{value}" - ] + ik.tektronix.TekTDS5xx, + [ + f"CH{channel + 1}:SCA {value:.3E}", + f"CH{channel + 1}:SCA?", + f"CH{channel + 1}:SCA?", + ], + [f"{value}", f"{value}"], ) as inst: inst.channel[channel].scale = value print(f"\n>>>{value}") @@ -398,38 +359,28 @@ def test_channel_scale_value_error(): scale_rec = 13 channel = 0 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"CH{channel + 1}:SCA {scale_set:.3E}", - f"CH{channel + 1}:SCA?" - ], - [ - f"{scale_rec}" - ] + ik.tektronix.TekTDS5xx, + [f"CH{channel + 1}:SCA {scale_set:.3E}", f"CH{channel + 1}:SCA?"], + [f"{scale_rec}"], ) as inst: with pytest.raises(ValueError) as err_info: inst.channel[channel].scale = scale_set err_msg = err_info.value.args[0] - assert err_msg == f"Tried to set CH{channel+1} Scale to {scale_set} " \ - f"but got {float(scale_rec)} instead" + assert ( + err_msg == f"Tried to set CH{channel+1} Scale to {scale_set} " + f"but got {float(scale_rec)} instead" + ) # INSTRUMENT # -@given(states=st.lists(st.integers(min_value=0, max_value=1), min_size=11, - max_size=11)) +@given(states=st.lists(st.integers(min_value=0, max_value=1), min_size=11, max_size=11)) def test_sources(states): """Get list of all active sources.""" active_sources = [] with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - "SEL?" - ], - [ - ";".join([str(state) for state in states]) - ] + ik.tektronix.TekTDS5xx, ["SEL?"], [";".join([str(state) for state in states])] ) as inst: # create active_sources for idx in range(4): @@ -440,14 +391,12 @@ def test_sources(states): for idx in range(4, 7): if states[idx]: active_sources.append( - ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, - f"MATH{idx-3}") + ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, f"MATH{idx-3}") ) for idx in range(7, 11): if states[idx]: active_sources.append( - ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, - f"REF{idx-6}") + ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, f"REF{idx-6}") ) # read active sources active_read = inst.sources @@ -459,15 +408,9 @@ def test_sources(states): def test_data_source_channel(channel): """Get / set channel data source for waveform transfer.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"DAT:SOU CH{channel+1}", - f"DAT:SOU CH{channel+1}", - "DAT:SOU?" - ], - [ - f"CH{channel+1}" - ] + ik.tektronix.TekTDS5xx, + [f"DAT:SOU CH{channel+1}", f"DAT:SOU CH{channel+1}", "DAT:SOU?"], + [f"CH{channel+1}"], ) as inst: # set as Source enum inst.data_source = ik.tektronix.TekTDS5xx.Source[f"CH{channel + 1}"] @@ -481,15 +424,9 @@ def test_data_source_channel(channel): def test_data_source_math(channel): """Get / set math data source for waveform transfer.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"DAT:SOU MATH{channel+1}", - f"DAT:SOU MATH{channel+1}", - "DAT:SOU?" - ], - [ - f"MATH{channel+1}" - ] + ik.tektronix.TekTDS5xx, + [f"DAT:SOU MATH{channel+1}", f"DAT:SOU MATH{channel+1}", "DAT:SOU?"], + [f"MATH{channel+1}"], ) as inst: # set as Source enum inst.data_source = ik.tektronix.TekTDS5xx.Source[f"Math{channel + 1}"] @@ -503,15 +440,9 @@ def test_data_source_math(channel): def test_data_source_ref(channel): """Get / set ref data source for waveform transfer.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"DAT:SOU REF{channel+1}", - f"DAT:SOU REF{channel+1}", - "DAT:SOU?" - ], - [ - f"REF{channel+1}" - ] + ik.tektronix.TekTDS5xx, + [f"DAT:SOU REF{channel+1}", f"DAT:SOU REF{channel+1}", "DAT:SOU?"], + [f"REF{channel+1}"], ) as inst: # set as Source enum inst.data_source = ik.tektronix.TekTDS5xx.Source[f"Ref{channel + 1}"] @@ -524,32 +455,21 @@ def test_data_source_ref(channel): def test_data_source_raise_type_error(): """Raise TypeError when setting data source with wrong type.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.data_source = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Source setting must be a `TekTDS5xx.Source` " \ - f"value, got {type(wrong_type)} instead." + assert ( + err_msg == f"Source setting must be a `TekTDS5xx.Source` " + f"value, got {type(wrong_type)} instead." + ) @pytest.mark.parametrize("width", (1, 2)) def test_data_width(width): """Get / set data width.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"DATA:WIDTH {width}", - "DATA:WIDTH?" - ], - [ - f"{width}" - ] + ik.tektronix.TekTDS5xx, [f"DATA:WIDTH {width}", "DATA:WIDTH?"], [f"{width}"] ) as inst: inst.data_width = width assert inst.data_width == width @@ -558,13 +478,7 @@ def test_data_width(width): @given(width=st.integers().filter(lambda x: x < 1 or x > 2)) def test_data_width_value_error(width): """Raise ValueError when setting a wrong data width.""" - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.data_width = width err_msg = err_info.value.args[0] @@ -573,13 +487,7 @@ def test_data_width_value_error(width): def test_force_trigger(): """Raise NotImplementedError when forcing a trigger.""" - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(NotImplementedError): inst.force_trigger() @@ -588,16 +496,9 @@ def test_force_trigger(): def test_horizontal_scale(value): """Get / set horizontal scale.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"HOR:MAI:SCA {value:.3E}", - "HOR:MAI:SCA?", - "HOR:MAI:SCA?" - ], - [ - f"{value}", - f"{value}" - ] + ik.tektronix.TekTDS5xx, + [f"HOR:MAI:SCA {value:.3E}", "HOR:MAI:SCA?", "HOR:MAI:SCA?"], + [f"{value}", f"{value}"], ) as inst: inst.horizontal_scale = value assert inst.horizontal_scale == value @@ -608,36 +509,28 @@ def test_horizontal_scale_value_error(): set_value = 42 get_value = 13 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"HOR:MAI:SCA {set_value:.3E}", - "HOR:MAI:SCA?" - ], - [ - f"{get_value}", - ] + ik.tektronix.TekTDS5xx, + [f"HOR:MAI:SCA {set_value:.3E}", "HOR:MAI:SCA?"], + [ + f"{get_value}", + ], ) as inst: with pytest.raises(ValueError) as err_info: inst.horizontal_scale = set_value err_msg = err_info.value.args[0] - assert err_msg == f"Tried to set Horizontal Scale to {set_value} " \ - f"but got {float(get_value)} instead" + assert ( + err_msg == f"Tried to set Horizontal Scale to {set_value} " + f"but got {float(get_value)} instead" + ) @given(value=st.floats(min_value=0)) def test_trigger_level(value): """Get / set trigger level.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"TRIG:MAI:LEV {value:.3E}", - "TRIG:MAI:LEV?", - "TRIG:MAI:LEV?" - ], - [ - f"{value}", - f"{value}" - ] + ik.tektronix.TekTDS5xx, + [f"TRIG:MAI:LEV {value:.3E}", "TRIG:MAI:LEV?", "TRIG:MAI:LEV?"], + [f"{value}", f"{value}"], ) as inst: inst.trigger_level = value assert inst.trigger_level == value @@ -648,34 +541,26 @@ def test_trigger_level_value_error(): set_value = 42 get_value = 13 with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"TRIG:MAI:LEV {set_value:.3E}", - "TRIG:MAI:LEV?" - ], - [ - f"{get_value}" - ] + ik.tektronix.TekTDS5xx, + [f"TRIG:MAI:LEV {set_value:.3E}", "TRIG:MAI:LEV?"], + [f"{get_value}"], ) as inst: with pytest.raises(ValueError) as err_info: inst.trigger_level = set_value err_msg = err_info.value.args[0] - assert err_msg == f"Tried to set trigger level to {set_value} " \ - f"but got {float(get_value)} instead" + assert ( + err_msg == f"Tried to set trigger level to {set_value} " + f"but got {float(get_value)} instead" + ) @pytest.mark.parametrize("coupl", ik.tektronix.TekTDS5xx.Coupling) def test_trigger_coupling(coupl): """Get / set trigger coupling.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"TRIG:MAI:EDGE:COUP {coupl.value}", - "TRIG:MAI:EDGE:COUP?" - ], - [ - f"{coupl.value}" - ] + ik.tektronix.TekTDS5xx, + [f"TRIG:MAI:EDGE:COUP {coupl.value}", "TRIG:MAI:EDGE:COUP?"], + [f"{coupl.value}"], ) as inst: inst.trigger_coupling = coupl assert inst.trigger_coupling == coupl @@ -684,32 +569,23 @@ def test_trigger_coupling(coupl): def test_trigger_coupling_type_error(): """Raise type error when coupling is not a `Coupling` enum.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.trigger_coupling = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Coupling setting must be a `TekTDS5xx.Coupling` " \ - f"value, got {type(wrong_type)} instead." + assert ( + err_msg == f"Coupling setting must be a `TekTDS5xx.Coupling` " + f"value, got {type(wrong_type)} instead." + ) @pytest.mark.parametrize("edge", ik.tektronix.TekTDS5xx.Edge) def test_trigger_slope(edge): """Get / set trigger slope.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"TRIG:MAI:EDGE:SLO {edge.value}", - "TRIG:MAI:EDGE:SLO?" - ], - [ - f"{edge.value}" - ] + ik.tektronix.TekTDS5xx, + [f"TRIG:MAI:EDGE:SLO {edge.value}", "TRIG:MAI:EDGE:SLO?"], + [f"{edge.value}"], ) as inst: inst.trigger_slope = edge assert inst.trigger_slope == edge @@ -718,32 +594,23 @@ def test_trigger_slope(edge): def test_trigger_slope_type_error(): """Raise type error when edge is not an `Edge` enum.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.trigger_slope = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Edge setting must be a `TekTDS5xx.Edge` " \ - f"value, got {type(wrong_type)} instead." + assert ( + err_msg == f"Edge setting must be a `TekTDS5xx.Edge` " + f"value, got {type(wrong_type)} instead." + ) @pytest.mark.parametrize("source", ik.tektronix.TekTDS5xx.Trigger) def test_trigger_source(source): """Get / set trigger source.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"TRIG:MAI:EDGE:SOU {source.value}", - "TRIG:MAI:EDGE:SOU?" - ], - [ - f"{source.value}" - ] + ik.tektronix.TekTDS5xx, + [f"TRIG:MAI:EDGE:SOU {source.value}", "TRIG:MAI:EDGE:SOU?"], + [f"{source.value}"], ) as inst: inst.trigger_source = source assert inst.trigger_source == source @@ -752,19 +619,15 @@ def test_trigger_source(source): def test_trigger_source_type_error(): """Raise type error when source is not an `source` enum.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(TypeError) as err_info: inst.trigger_source = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Trigger source setting must be a " \ - f"`TekTDS5xx.Trigger` value, got " \ - f"{type(wrong_type)} instead." + assert ( + err_msg == f"Trigger source setting must be a " + f"`TekTDS5xx.Trigger` value, got " + f"{type(wrong_type)} instead." + ) @given(dt=st.datetimes(min_value=datetime(1000, 1, 1))) @@ -774,14 +637,9 @@ def test_clock(dt): dt_fmt_receive = '"%Y-%m-%d";"%H:%M:%S"' dt_fmt_send = 'DATE "%Y-%m-%d";:TIME "%H:%M:%S"' with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - dt.strftime(dt_fmt_send), - "DATE?;:TIME?" - ], - [ - dt.strftime(dt_fmt_receive) - ] + ik.tektronix.TekTDS5xx, + [dt.strftime(dt_fmt_send), "DATE?;:TIME?"], + [dt.strftime(dt_fmt_receive)], ) as inst: inst.clock = dt assert inst.clock == dt.replace(microsecond=0) @@ -790,32 +648,23 @@ def test_clock(dt): def test_clock_value_error(): """Raise ValueError when not set with datetime object.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.clock = wrong_type err_msg = err_info.value.args[0] - assert err_msg == f"Expected datetime.datetime but got " \ - f"{type(wrong_type)} instead" + assert ( + err_msg == f"Expected datetime.datetime but got " + f"{type(wrong_type)} instead" + ) @pytest.mark.parametrize("newval", (True, False)) def test_display_clock(newval): """Get / set if clock is displayed on screen.""" with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - f"DISPLAY:CLOCK {int(newval)}", - "DISPLAY:CLOCK?" - ], - [ - f"{int(newval)}" - ] + ik.tektronix.TekTDS5xx, + [f"DISPLAY:CLOCK {int(newval)}", "DISPLAY:CLOCK?"], + [f"{int(newval)}"], ) as inst: inst.display_clock = newval assert inst.display_clock == newval @@ -824,13 +673,7 @@ def test_display_clock(newval): def test_display_clock_value_error(): """Raise ValueError when display_clock is called w/o a bool.""" wrong_type = 42 - with expected_protocol( - ik.tektronix.TekTDS5xx, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.tektronix.TekTDS5xx, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.display_clock = wrong_type err_msg = err_info.value.args[0] @@ -853,25 +696,22 @@ def test_get_hardcopy(mocker, data): Mocking out sleep to do nothing. """ # mock out time - mocker.patch.object(time, 'sleep', return_value=None) + mocker.patch.object(time, "sleep", return_value=None) # make data length_data = (len(data) - 8) * 8 # subtract header and color table # make a fake header - header = struct.pack(' stop - message_id=ThorLabsCommands.PZMOT_MOVE_JOG, - param1=0x01, param2=0x00, - dest=0x50, - source=0x01, - data=None - ).pack() - ], - [ - init_kim101[1] - ], - sep="" + ik.thorlabs.APTPiezoInertiaActuator, + [ + init_kim101[0], + ThorLabsPacket( # no direction -> stop + message_id=ThorLabsCommands.PZMOT_MOVE_JOG, + param1=0x01, + param2=0x00, + dest=0x50, + source=0x01, + data=None, + ).pack(), + ], + [init_kim101[1]], + sep="", ) as apt: apt.channel[0].move_jog_stop() @@ -660,77 +663,86 @@ def test_apt_pia_enabled_multi(init_kim101): Tested with KIM101 driver connected to PIM1 mirror mount. """ with expected_protocol( - ik.thorlabs.APTPiezoInertiaActuator, - [ - init_kim101[0], - ThorLabsPacket( # all off - message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, - param1=0x2B, param2=0x00, - dest=0x50, - source=0x01, - data=None, - ).pack(), - ThorLabsPacket( # read channel 0 & 1 - message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, - param1=0x2B, param2=0x00, - dest=0x50, - source=0x01, - data=None, - ).pack(), - ThorLabsPacket( # read channel 2 & 3 - message_id=ThorLabsCommands.PZMOT_REQ_PARAMS, - param1=0x2B, param2=0x00, - dest=0x50, - source=0x01, - data=None, - ).pack(), - ThorLabsPacket( # send off - message_id=ThorLabsCommands.PZMOT_SET_PARAMS, - param1=None, param2=None, - dest=0x50, - source=0x01, - data=struct.pack(' " - ], - sep="\r" + ik.thorlabs.LCC25, ["*idn?"], ["*idn?", "bloopbloop", "> "], sep="\r" ) as lcc: name = lcc.name assert name == "bloopbloop", f"got {name} expected bloopbloop" @@ -35,18 +26,10 @@ def test_lcc25_name(): def test_lcc25_frequency(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "freq?", - "freq=10.0" - ], - [ - "freq?", - "20", - "> freq=10.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["freq?", "freq=10.0"], + ["freq?", "20", "> freq=10.0", "> "], + sep="\r", ) as lcc: unit_eq(lcc.frequency, u.Quantity(20, "Hz")) lcc.frequency = 10.0 @@ -54,160 +37,88 @@ def test_lcc25_frequency(): def test_lcc25_frequency_lowlimit(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [ - "freq=0.0" - ], - [ - "freq=0.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["freq=0.0"], ["freq=0.0", "> "], sep="\r" ) as lcc: lcc.frequency = 0.0 def test_lcc25_frequency_highlimit(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [ - "freq=160.0" - ], - [ - "freq=160.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["freq=160.0"], ["freq=160.0", "> "], sep="\r" ) as lcc: lcc.frequency = 160.0 def test_lcc25_mode(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "mode?", - "mode=1" - ], - [ - "mode?", - "2", - "> mode=1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["mode?", "mode=1"], + ["mode?", "2", "> mode=1", "> "], + sep="\r", ) as lcc: assert lcc.mode == ik.thorlabs.LCC25.Mode.voltage2 lcc.mode = ik.thorlabs.LCC25.Mode.voltage1 def test_lcc25_mode_invalid(): - with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: + with pytest.raises(ValueError), expected_protocol(ik.thorlabs.LCC25, [], []) as lcc: lcc.mode = "blo" def test_lcc25_enable(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "enable?", - "enable=1" - ], - [ - "enable?", - "0", - "> enable=1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["enable?", "enable=1"], + ["enable?", "0", "> enable=1", "> "], + sep="\r", ) as lcc: assert lcc.enable is False lcc.enable = True def test_lcc25_enable_invalid_type(): - with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: + with pytest.raises(TypeError), expected_protocol(ik.thorlabs.LCC25, [], []) as lcc: lcc.enable = "blo" def test_lcc25_extern(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "extern?", - "extern=1" - ], - [ - "extern?", - "0", - "> extern=1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["extern?", "extern=1"], + ["extern?", "0", "> extern=1", "> "], + sep="\r", ) as lcc: assert lcc.extern is False lcc.extern = True def test_tc200_extern_invalid_type(): - with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as tc: + with pytest.raises(TypeError), expected_protocol(ik.thorlabs.LCC25, [], []) as tc: tc.extern = "blo" def test_lcc25_remote(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "remote?", - "remote=1" - ], - [ - "remote?", - "0", - "> remote=1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["remote?", "remote=1"], + ["remote?", "0", "> remote=1", "> "], + sep="\r", ) as lcc: assert lcc.remote is False lcc.remote = True def test_tc200_remote_invalid_type(): - with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as tc: + with pytest.raises(TypeError), expected_protocol(ik.thorlabs.LCC25, [], []) as tc: tc.remote = "blo" def test_lcc25_voltage1(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "volt1?", - "volt1=10.0" - ], - [ - "volt1?", - "20", - "> volt1=10.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["volt1?", "volt1=10.0"], + ["volt1?", "20", "> volt1=10.0", "> "], + sep="\r", ) as lcc: unit_eq(lcc.voltage1, u.Quantity(20, "V")) lcc.voltage1 = 10.0 @@ -221,18 +132,13 @@ def test_check_cmd(): def test_lcc25_voltage2(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "volt2?", - "volt2=10.0", - ], - [ - "volt2?", - "20", - "> volt2=10.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + [ + "volt2?", + "volt2=10.0", + ], + ["volt2?", "20", "> volt2=10.0", "> "], + sep="\r", ) as lcc: unit_eq(lcc.voltage2, u.Quantity(20, "V")) lcc.voltage2 = 10.0 @@ -240,18 +146,10 @@ def test_lcc25_voltage2(): def test_lcc25_minvoltage(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "min?", - "min=10.0" - ], - [ - "min?", - "20", - "> min=10.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["min?", "min=10.0"], + ["min?", "20", "> min=10.0", "> "], + sep="\r", ) as lcc: unit_eq(lcc.min_voltage, u.Quantity(20, "V")) lcc.min_voltage = 10.0 @@ -259,18 +157,10 @@ def test_lcc25_minvoltage(): def test_lcc25_maxvoltage(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "max?", - "max=10.0" - ], - [ - "max?", - "20", - "> max=10.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["max?", "max=10.0"], + ["max?", "20", "> max=10.0", "> "], + sep="\r", ) as lcc: unit_eq(lcc.max_voltage, u.Quantity(20, "V")) lcc.max_voltage = 10.0 @@ -278,18 +168,10 @@ def test_lcc25_maxvoltage(): def test_lcc25_dwell(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "dwell?", - "dwell=10" - ], - [ - "dwell?", - "20", - "> dwell=10", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["dwell?", "dwell=10"], + ["dwell?", "20", "> dwell=10", "> "], + sep="\r", ) as lcc: unit_eq(lcc.dwell, u.Quantity(20, "ms")) lcc.dwell = 10 @@ -297,33 +179,17 @@ def test_lcc25_dwell(): def test_lcc25_dwell_positive(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [ - "dwell=-10" - ], - [ - "dwell=-10", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["dwell=-10"], ["dwell=-10", "> "], sep="\r" ) as lcc: lcc.dwell = -10 def test_lcc25_increment(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "increment?", - "increment=10.0" - ], - [ - "increment?", - "20", - "> increment=10.0", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, + ["increment?", "increment=10.0"], + ["increment?", "20", "> increment=10.0", "> "], + sep="\r", ) as lcc: unit_eq(lcc.increment, u.Quantity(20, "V")) lcc.increment = 10.0 @@ -331,132 +197,65 @@ def test_lcc25_increment(): def test_lcc25_increment_positive(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [ - "increment=-10" - ], - [ - "increment=-10", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["increment=-10"], ["increment=-10", "> "], sep="\r" ) as lcc: lcc.increment = -10 def test_lcc25_default(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "default" - ], - [ - "default", - "1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["default"], ["default", "1", "> "], sep="\r" ) as lcc: lcc.default() def test_lcc25_save(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "save" - ], - [ - "save", - "1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["save"], ["save", "1", "> "], sep="\r" ) as lcc: lcc.save() def test_lcc25_set_settings(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "set=2" - ], - [ - "set=2", - "1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["set=2"], ["set=2", "1", "> "], sep="\r" ) as lcc: lcc.set_settings(2) def test_lcc25_set_settings_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [], - [], - sep="\r" + ik.thorlabs.LCC25, [], [], sep="\r" ) as lcc: lcc.set_settings(5) def test_lcc25_get_settings(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "get=2" - ], - [ - "get=2", - "1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["get=2"], ["get=2", "1", "> "], sep="\r" ) as lcc: lcc.get_settings(2) def test_lcc25_get_settings_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.LCC25, - [], - [], - sep="\r" + ik.thorlabs.LCC25, [], [], sep="\r" ) as lcc: lcc.get_settings(5) def test_lcc25_test_mode(): with expected_protocol( - ik.thorlabs.LCC25, - [ - "test" - ], - [ - "test", - "1", - "> " - ], - sep="\r" + ik.thorlabs.LCC25, ["test"], ["test", "1", "> "], sep="\r" ) as lcc: lcc.test_mode() def test_lcc25_remote_invalid_type(): - with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: + with pytest.raises(TypeError), expected_protocol(ik.thorlabs.LCC25, [], []) as lcc: lcc.remote = "blo" def test_lcc25_extern_invalid_type(): - with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.LCC25, - [], - [] - ) as lcc: + with pytest.raises(TypeError), expected_protocol(ik.thorlabs.LCC25, [], []) as lcc: lcc.extern = "blo" diff --git a/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py b/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py index 63390d5bb..6269886d7 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py @@ -29,8 +29,10 @@ @pytest.fixture def init_sensor(): """Initialize a sensor - return initialized sensor class.""" + class Sensor: """Initialize a sensor class""" + NAME = "SENSOR" SERIAL_NUMBER = "123456" CALIBRATION_MESSAGE = "OK" @@ -42,9 +44,16 @@ def sendmsg(self): return "SYST:SENSOR:IDN?" def message(self): - return ",".join([self.NAME, self.SERIAL_NUMBER, - self.CALIBRATION_MESSAGE, self.SENSOR_TYPE, - self.SENSOR_SUBTYPE, self.FLAGS]) + return ",".join( + [ + self.NAME, + self.SERIAL_NUMBER, + self.CALIBRATION_MESSAGE, + self.SENSOR_TYPE, + self.SENSOR_SUBTYPE, + self.FLAGS, + ] + ) return Sensor() @@ -55,13 +64,7 @@ def message(self): def test_sensor_init(init_sensor): """Initialize a sensor object from the parent class.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - init_sensor.sendmsg() - ], - [ - init_sensor.message() - ] + ik.thorlabs.PM100USB, [init_sensor.sendmsg()], [init_sensor.message()] ) as inst: assert inst.sensor._parent is inst @@ -69,13 +72,7 @@ def test_sensor_init(init_sensor): def test_sensor_name(init_sensor): """Get name of the sensor.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - init_sensor.sendmsg() - ], - [ - init_sensor.message() - ] + ik.thorlabs.PM100USB, [init_sensor.sendmsg()], [init_sensor.message()] ) as inst: assert inst.sensor.name == init_sensor.NAME @@ -83,13 +80,7 @@ def test_sensor_name(init_sensor): def test_sensor_serial_number(init_sensor): """Get serial number of the sensor.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - init_sensor.sendmsg() - ], - [ - init_sensor.message() - ] + ik.thorlabs.PM100USB, [init_sensor.sendmsg()], [init_sensor.message()] ) as inst: assert inst.sensor.serial_number == init_sensor.SERIAL_NUMBER @@ -97,48 +88,27 @@ def test_sensor_serial_number(init_sensor): def test_sensor_calibration_message(init_sensor): """Get calibration message of the sensor.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - init_sensor.sendmsg() - ], - [ - init_sensor.message() - ] + ik.thorlabs.PM100USB, [init_sensor.sendmsg()], [init_sensor.message()] ) as inst: - assert (inst.sensor.calibration_message == - init_sensor.CALIBRATION_MESSAGE) + assert inst.sensor.calibration_message == init_sensor.CALIBRATION_MESSAGE def test_sensor_type(init_sensor): """Get type of the sensor.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - init_sensor.sendmsg() - ], - [ - init_sensor.message() - ] + ik.thorlabs.PM100USB, [init_sensor.sendmsg()], [init_sensor.message()] ) as inst: - assert inst.sensor.type == (init_sensor.SENSOR_TYPE, - init_sensor.SENSOR_SUBTYPE) + assert inst.sensor.type == (init_sensor.SENSOR_TYPE, init_sensor.SENSOR_SUBTYPE) def test_sensor_flags(init_sensor): """Get flags of the sensor.""" flag_read = init_sensor.FLAGS - flags = ik.thorlabs.PM100USB._SensorFlags(**{ - e.name: bool(e & int(flag_read)) - for e in ik.thorlabs.PM100USB.SensorFlags - }) + flags = ik.thorlabs.PM100USB._SensorFlags( + **{e.name: bool(e & int(flag_read)) for e in ik.thorlabs.PM100USB.SensorFlags} + ) with expected_protocol( - ik.thorlabs.PM100USB, - [ - init_sensor.sendmsg() - ], - [ - init_sensor.message() - ] + ik.thorlabs.PM100USB, [init_sensor.sendmsg()], [init_sensor.message()] ) as inst: assert inst.sensor.flags == flags @@ -150,13 +120,9 @@ def test_cache_units(): """Get, set cache units bool.""" msr_conf = ik.thorlabs.PM100USB.MeasurementConfiguration.current with expected_protocol( - ik.thorlabs.PM100USB, - [ - "CONF?" - ], - [ - f"{msr_conf.value}" # measurement configuration temperature - ] + ik.thorlabs.PM100USB, + ["CONF?"], + [f"{msr_conf.value}"], # measurement configuration temperature ) as inst: inst.cache_units = True assert inst._cache_units == inst._READ_UNITS[msr_conf] @@ -164,19 +130,13 @@ def test_cache_units(): assert not inst.cache_units -@pytest.mark.parametrize("msr_conf", - ik.thorlabs.PM100USB.MeasurementConfiguration) +@pytest.mark.parametrize("msr_conf", ik.thorlabs.PM100USB.MeasurementConfiguration) def test_measurement_configuration(msr_conf): """Get / set measurement configuration.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - f"CONF {msr_conf.value}", - "CONF?" - ], - [ - f"{msr_conf.value}" # measurement configuration temperature - ] + ik.thorlabs.PM100USB, + [f"CONF {msr_conf.value}", "CONF?"], + [f"{msr_conf.value}"], # measurement configuration temperature ) as inst: inst.measurement_configuration = msr_conf assert inst.measurement_configuration == msr_conf @@ -186,14 +146,9 @@ def test_measurement_configuration(msr_conf): def test_averaging_count(value): """Get / set averaging count.""" with expected_protocol( - ik.thorlabs.PM100USB, - [ - f"SENS:AVER:COUN {value}", - "SENS:AVER:COUN?" - ], - [ - f"{value}" # measurement configuration temperature - ] + ik.thorlabs.PM100USB, + [f"SENS:AVER:COUN {value}", "SENS:AVER:COUN?"], + [f"{value}"], # measurement configuration temperature ) as inst: inst.averaging_count = value assert inst.averaging_count == value @@ -202,13 +157,7 @@ def test_averaging_count(value): @given(value=st.integers(max_value=0)) def test_averaging_count_value_error(value): """Raise a ValueError if the averaging count is wrong.""" - with expected_protocol( - ik.thorlabs.PM100USB, - [ - ], - [ - ] - ) as inst: + with expected_protocol(ik.thorlabs.PM100USB, [], []) as inst: with pytest.raises(ValueError) as err_info: inst.averaging_count = value err_msg = err_info.value.args[0] @@ -220,16 +169,9 @@ def test_read(value): """Read instrument and grab the units.""" msr_conf = ik.thorlabs.PM100USB.MeasurementConfiguration.current with expected_protocol( - ik.thorlabs.PM100USB, - [ - "CONF?", - "READ?" - ], - [ - f"{msr_conf.value}", # measurement configuration temperature - f"{value}" - - ] + ik.thorlabs.PM100USB, + ["CONF?", "READ?"], + [f"{msr_conf.value}", f"{value}"], # measurement configuration temperature ) as inst: units = inst._READ_UNITS[msr_conf] # cache units is False at init assert inst.read() == value * units @@ -240,16 +182,9 @@ def test_read_cached_units(): msr_conf = ik.thorlabs.PM100USB.MeasurementConfiguration.current value = 42 with expected_protocol( - ik.thorlabs.PM100USB, - [ - "CONF?", - "READ?" - ], - [ - f"{msr_conf.value}", # measurement configuration temperature - f"{value}" - - ] + ik.thorlabs.PM100USB, + ["CONF?", "READ?"], + [f"{msr_conf.value}", f"{value}"], # measurement configuration temperature ) as inst: units = inst._READ_UNITS[msr_conf] # cache units is False at init inst.cache_units = True diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index ffe3df288..7fbf6ac9f 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -18,34 +18,14 @@ def test_sc10_name(): with expected_protocol( - ik.thorlabs.SC10, - [ - "id?" - ], - [ - "id?", - "bloopbloop", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["id?"], ["id?", "bloopbloop", "> "], sep="\r" ) as sc: assert sc.name == "bloopbloop" def test_sc10_enable(): with expected_protocol( - ik.thorlabs.SC10, - [ - "ens?", - "ens=1" - ], - [ - "ens?", - "0", - "> ens=1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["ens?", "ens=1"], ["ens?", "0", "> ens=1", "> "], sep="\r" ) as sc: assert sc.enable is False sc.enable = True @@ -53,28 +33,14 @@ def test_sc10_enable(): def test_sc10_enable_invalid(): with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" + ik.thorlabs.SC10, [], [], sep="\r" ) as sc: sc.enable = 10 def test_sc10_repeat(): with expected_protocol( - ik.thorlabs.SC10, - [ - "rep?", - "rep=10" - ], - [ - "rep?", - "20", - "> rep=10", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["rep?", "rep=10"], ["rep?", "20", "> rep=10", "> "], sep="\r" ) as sc: assert sc.repeat == 20 sc.repeat = 10 @@ -82,28 +48,17 @@ def test_sc10_repeat(): def test_sc10_repeat_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" + ik.thorlabs.SC10, [], [], sep="\r" ) as sc: sc.repeat = -1 def test_sc10_mode(): with expected_protocol( - ik.thorlabs.SC10, - [ - "mode?", - "mode=2" - ], - [ - "mode?", - "1", - "> mode=2", - "> " - ], - sep="\r" + ik.thorlabs.SC10, + ["mode?", "mode=2"], + ["mode?", "1", "> mode=2", "> "], + sep="\r", ) as sc: assert sc.mode == ik.thorlabs.SC10.Mode.manual sc.mode = ik.thorlabs.SC10.Mode.auto @@ -111,28 +66,17 @@ def test_sc10_mode(): def test_sc10_mode_invalid(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" + ik.thorlabs.SC10, [], [], sep="\r" ) as sc: sc.mode = "blo" def test_sc10_trigger(): with expected_protocol( - ik.thorlabs.SC10, - [ - "trig?", - "trig=1" - ], - [ - "trig?", - "0", - "> trig=1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, + ["trig?", "trig=1"], + ["trig?", "0", "> trig=1", "> "], + sep="\r", ) as sc: assert sc.trigger == 0 sc.trigger = 1 @@ -140,18 +84,7 @@ def test_sc10_trigger(): def test_sc10_out_trigger(): with expected_protocol( - ik.thorlabs.SC10, - [ - "xto?", - "xto=1" - ], - [ - "xto?", - "0", - "> xto=1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["xto?", "xto=1"], ["xto?", "0", "> xto=1", "> "], sep="\r" ) as sc: assert sc.out_trigger == 0 sc.out_trigger = 1 @@ -159,18 +92,10 @@ def test_sc10_out_trigger(): def test_sc10_open_time(): with expected_protocol( - ik.thorlabs.SC10, - [ - "open?", - "open=10" - ], - [ - "open?", - "20", - "> open=10", - "> " - ], - sep="\r" + ik.thorlabs.SC10, + ["open?", "open=10"], + ["open?", "20", "> open=10", "> "], + sep="\r", ) as sc: unit_eq(sc.open_time, u.Quantity(20, "ms")) sc.open_time = 10 @@ -178,18 +103,10 @@ def test_sc10_open_time(): def test_sc10_shut_time(): with expected_protocol( - ik.thorlabs.SC10, - [ - "shut?", - "shut=10" - ], - [ - "shut?", - "20", - "> shut=10", - "> " - ], - sep="\r" + ik.thorlabs.SC10, + ["shut?", "shut=10"], + ["shut?", "20", "> shut=10", "> "], + sep="\r", ) as sc: unit_eq(sc.shut_time, u.Quantity(20, "ms")) sc.shut_time = 10.0 @@ -197,18 +114,10 @@ def test_sc10_shut_time(): def test_sc10_baud_rate(): with expected_protocol( - ik.thorlabs.SC10, - [ - "baud?", - "baud=1" - ], - [ - "baud?", - "0", - "> baud=1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, + ["baud?", "baud=1"], + ["baud?", "0", "> baud=1", "> "], + sep="\r", ) as sc: assert sc.baud_rate == 9600 sc.baud_rate = 115200 @@ -216,105 +125,48 @@ def test_sc10_baud_rate(): def test_sc10_baud_rate_error(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.SC10, - [], - [], - sep="\r" + ik.thorlabs.SC10, [], [], sep="\r" ) as sc: sc.baud_rate = 115201 def test_sc10_closed(): with expected_protocol( - ik.thorlabs.SC10, - [ - "closed?" - ], - [ - "closed?", - "1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["closed?"], ["closed?", "1", "> "], sep="\r" ) as sc: assert sc.closed def test_sc10_interlock(): with expected_protocol( - ik.thorlabs.SC10, - [ - "interlock?" - ], - [ - "interlock?", - "1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["interlock?"], ["interlock?", "1", "> "], sep="\r" ) as sc: assert sc.interlock def test_sc10_default(): with expected_protocol( - ik.thorlabs.SC10, - [ - "default" - ], - [ - "default", - "1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["default"], ["default", "1", "> "], sep="\r" ) as sc: assert sc.default() def test_sc10_save(): with expected_protocol( - ik.thorlabs.SC10, - [ - "savp" - ], - [ - "savp", - "1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["savp"], ["savp", "1", "> "], sep="\r" ) as sc: assert sc.save() def test_sc10_save_mode(): with expected_protocol( - ik.thorlabs.SC10, - [ - "save" - ], - [ - "save", - "1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["save"], ["save", "1", "> "], sep="\r" ) as sc: assert sc.save_mode() def test_sc10_restore(): with expected_protocol( - ik.thorlabs.SC10, - [ - "resp" - ], - [ - "resp", - "1", - "> " - ], - sep="\r" + ik.thorlabs.SC10, ["resp"], ["resp", "1", "> "], sep="\r" ) as sc: assert sc.restore() diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index 3ecff30a9..21a53489e 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -19,35 +19,17 @@ def test_tc200_name(): with expected_protocol( - ik.thorlabs.TC200, - [ - "*idn?" - ], - [ - "*idn?", - "bloopbloop", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["*idn?"], ["*idn?", "bloopbloop", "> "], sep="\r" ) as tc: assert tc.name() == "bloopbloop" def test_tc200_mode(): with expected_protocol( - ik.thorlabs.TC200, - [ - "stat?", - "stat?", - "mode=cycle" - ], - [ - "stat?", - "0 > stat?", - "2 > mode=cycle", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["stat?", "stat?", "mode=cycle"], + ["stat?", "0 > stat?", "2 > mode=cycle", "> "], + sep="\r", ) as tc: assert tc.mode == tc.Mode.normal assert tc.mode == tc.Mode.cycle @@ -56,62 +38,39 @@ def test_tc200_mode(): def test_tc200_mode_2(): with expected_protocol( - ik.thorlabs.TC200, - [ - "mode=normal" - ], - [ - "mode=normal", - "Command error CMD_ARG_RANGE_ERR\n", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["mode=normal"], + ["mode=normal", "Command error CMD_ARG_RANGE_ERR\n", "> "], + sep="\r", ) as tc: tc.mode = ik.thorlabs.TC200.Mode.normal def test_tc200_mode_error(): with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" + ik.thorlabs.TC200, [], [], sep="\r" ) as tc: tc.mode = "blo" def test_tc200_mode_error2(): with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" + ik.thorlabs.TC200, [], [], sep="\r" ) as tc: + class TestEnum(IntEnum): blo = 1 beep = 2 + tc.mode = TestEnum.blo def test_tc200_enable(): with expected_protocol( - ik.thorlabs.TC200, - [ - "stat?", - "stat?", - "ens", - "stat?", - "ens" - ], - [ - "stat?", - "54 > stat?", - "54 > ens", - "> stat?", - "55 > ens", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["stat?", "stat?", "ens", "stat?", "ens"], + ["stat?", "54 > stat?", "54 > ens", "> stat?", "55 > ens", "> "], + sep="\r", ) as tc: assert tc.enable == 0 tc.enable = True @@ -120,47 +79,33 @@ def test_tc200_enable(): def test_tc200_enable_type(): with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" + ik.thorlabs.TC200, [], [], sep="\r" ) as tc: tc.enable = "blo" def test_tc200_temperature(): with expected_protocol( - ik.thorlabs.TC200, - [ - "tact?", - ], - [ - "tact?", - "30 C", - "> ", - ], - sep="\r" + ik.thorlabs.TC200, + [ + "tact?", + ], + [ + "tact?", + "30 C", + "> ", + ], + sep="\r", ) as tc: assert tc.temperature == u.Quantity(30.0, u.degC) def test_tc200_temperature_set(): with expected_protocol( - ik.thorlabs.TC200, - [ - "tset?", - "tmax?", - "tset=40" - ], - [ - "tset?", - "30 C", - "> tmax?", - "250", - "> tset=40", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["tset?", "tmax?", "tset=40"], + ["tset?", "30 C", "> tmax?", "250", "> tset=40", "> "], + sep="\r", ) as tc: assert tc.temperature_set == u.Quantity(30.0, u.degC) tc.temperature_set = u.Quantity(40, u.degC) @@ -168,89 +113,44 @@ def test_tc200_temperature_set(): def test_tc200_temperature_range(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "tmax?" - ], - [ - "tmax?", - "40", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["tmax?"], ["tmax?", "40", "> "], sep="\r" ) as tc: tc.temperature_set = u.Quantity(50, u.degC) def test_tc200_pid(): with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "pgain=2" - ], - [ - "pid?", - "2 0 220", - "> pgain=2", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["pid?", "pgain=2"], + ["pid?", "2 0 220", "> pgain=2", "> "], + sep="\r", ) as tc: assert tc.p == 2 tc.p = 2 with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "igain=0" - ], - [ - "pid?", - "2 0 220", - "> igain=0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["pid?", "igain=0"], + ["pid?", "2 0 220", "> igain=0", "> "], + sep="\r", ) as tc: assert tc.i == 0 tc.i = 0 with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "dgain=220" - ], - [ - "pid?", - "2 0 220", - "> dgain=220", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["pid?", "dgain=220"], + ["pid?", "2 0 220", "> dgain=220", "> "], + sep="\r", ) as tc: assert tc.d == 220 tc.d = 220 with expected_protocol( - ik.thorlabs.TC200, - [ - "pid?", - "pgain=2", - "igain=0", - "dgain=220" - ], - [ - "pid?", - "2 0 220", - "> pgain=2", - "> igain=0", - "> dgain=220", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["pid?", "pgain=2", "igain=0", "dgain=220"], + ["pid?", "2 0 220", "> pgain=2", "> igain=0", "> dgain=220", "> "], + sep="\r", ) as tc: assert tc.pid == [2, 0, 220] tc.pid = (2, 0, 220) @@ -258,125 +158,67 @@ def test_tc200_pid(): def test_tc200_pid_invalid_type(): with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" + ik.thorlabs.TC200, [], [], sep="\r" ) as tc: tc.pid = "foo" def test_tc200_pmin(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "pgain=-1" - ], - [ - "pgain=-1", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["pgain=-1"], ["pgain=-1", "> "], sep="\r" ) as tc: tc.p = -1 def test_tc200_pmax(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "pgain=260" - ], - [ - "pgain=260", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["pgain=260"], ["pgain=260", "> "], sep="\r" ) as tc: tc.p = 260 def test_tc200_imin(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "igain=-1" - ], - [ - "igain=-1", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["igain=-1"], ["igain=-1", "> "], sep="\r" ) as tc: tc.i = -1 def test_tc200_imax(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "igain=260" - ], - [ - "igain=260", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["igain=260"], ["igain=260", "> "], sep="\r" ) as tc: tc.i = 260 def test_tc200_dmin(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "dgain=-1" - ], - [ - "dgain=-1", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["dgain=-1"], ["dgain=-1", "> "], sep="\r" ) as tc: tc.d = -1 def test_tc200_dmax(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "dgain=260" - ], - [ - "dgain=260", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["dgain=260"], ["dgain=260", "> "], sep="\r" ) as tc: tc.d = 260 def test_tc200_degrees(): with expected_protocol( - ik.thorlabs.TC200, - [ - "stat?", - "stat?", - "stat?", - "unit=c", - "unit=f", - "unit=k" - ], - [ - "stat?", - "44 > stat?", - "54 > stat?", - "0 > unit=c", - "> unit=f", - "> unit=k", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["stat?", "stat?", "stat?", "unit=c", "unit=f", "unit=k"], + [ + "stat?", + "44 > stat?", + "54 > stat?", + "0 > unit=c", + "> unit=f", + "> unit=k", + "> ", + ], + sep="\r", ) as tc: assert tc.degrees == u.degK assert tc.degrees == u.degC @@ -389,68 +231,43 @@ def test_tc200_degrees(): def test_tc200_degrees_invalid(): with pytest.raises(TypeError), expected_protocol( - ik.thorlabs.TC200, - [], - [], - sep="\r" + ik.thorlabs.TC200, [], [], sep="\r" ) as tc: tc.degrees = "blo" def test_tc200_sensor(): with expected_protocol( - ik.thorlabs.TC200, - [ - "sns?", - "sns=ptc100" - ], - [ - "sns?", - "Sensor = NTC10K, Beta = 5600", - "> sns=ptc100", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["sns?", "sns=ptc100"], + ["sns?", "Sensor = NTC10K, Beta = 5600", "> sns=ptc100", "> "], + sep="\r", ) as tc: assert tc.sensor == tc.Sensor.ntc10k tc.sensor = tc.Sensor.ptc100 def test_tc200_sensor_error(): - with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [], - [] - ) as tc: + with pytest.raises(ValueError), expected_protocol(ik.thorlabs.TC200, [], []) as tc: tc.sensor = "blo" def test_tc200_sensor_error2(): - with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [], - [] - ) as tc: + with pytest.raises(ValueError), expected_protocol(ik.thorlabs.TC200, [], []) as tc: + class TestEnum(IntEnum): blo = 1 beep = 2 + tc.sensor = TestEnum.blo def test_tc200_beta(): with expected_protocol( - ik.thorlabs.TC200, - [ - "beta?", - "beta=2000" - ], - [ - "beta?", - "5600", - "> beta=2000", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["beta?", "beta=2000"], + ["beta?", "5600", "> beta=2000", "> "], + sep="\r", ) as tc: assert tc.beta == 5600 tc.beta = 2000 @@ -458,48 +275,24 @@ def test_tc200_beta(): def test_tc200_beta_min(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "beta=200" - ], - [ - "beta=200", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["beta=200"], ["beta=200", "> "], sep="\r" ) as tc: tc.beta = 200 def test_tc200_beta_max(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "beta=20000" - ], - [ - "beta=20000", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["beta=20000"], ["beta=20000", "> "], sep="\r" ) as tc: tc.beta = 20000 def test_tc200_max_power(): with expected_protocol( - ik.thorlabs.TC200, - [ - "pmax?", - "pmax=12.0" - ], - [ - "pmax?", - "15.0", - "> pmax=12.0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["pmax?", "pmax=12.0"], + ["pmax?", "15.0", "> pmax=12.0", "> "], + sep="\r", ) as tc: assert tc.max_power == 15.0 * u.W tc.max_power = 12 * u.W @@ -507,48 +300,24 @@ def test_tc200_max_power(): def test_tc200_power_min(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "PMAX=-2" - ], - [ - "PMAX=-2", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["PMAX=-2"], ["PMAX=-2", "> "], sep="\r" ) as tc: tc.max_power = -1 def test_tc200_power_max(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "PMAX=20000" - ], - [ - "PMAX=20000", - "> " - ], - sep="\r" + ik.thorlabs.TC200, ["PMAX=20000"], ["PMAX=20000", "> "], sep="\r" ) as tc: tc.max_power = 20000 def test_tc200_max_temperature(): with expected_protocol( - ik.thorlabs.TC200, - [ - "tmax?", - "tmax=180.0" - ], - [ - "tmax?", - "200.0", - "> tmax=180.0", - "> " - ], - sep="\r" + ik.thorlabs.TC200, + ["tmax?", "tmax=180.0"], + ["tmax?", "200.0", "> tmax=180.0", "> "], + sep="\r", ) as tc: assert tc.max_temperature == u.Quantity(200.0, u.degC) tc.max_temperature = u.Quantity(180, u.degC) @@ -556,29 +325,13 @@ def test_tc200_max_temperature(): def test_tc200_temp_min(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "TMAX=-2" - ], - [ - "TMAX=-2", - ">" - ], - sep="\r" + ik.thorlabs.TC200, ["TMAX=-2"], ["TMAX=-2", ">"], sep="\r" ) as tc: tc.max_temperature = -1 def test_tc200_temp_max(): with pytest.raises(ValueError), expected_protocol( - ik.thorlabs.TC200, - [ - "TMAX=20000" - ], - [ - "TMAX=20000", - ">" - ], - sep="\r" + ik.thorlabs.TC200, ["TMAX=20000"], ["TMAX=20000", ">"], sep="\r" ) as tc: tc.max_temperature = 20000 diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index 9636ed1f9..ca8b0c908 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -19,19 +19,16 @@ def test_laser_serial_number(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:serial-number)", - "(param-ref 'laser2:serial-number)" - ], - [ - "(param-ref 'laser1:serial-number)", - "bloop1", - "> (param-ref 'laser2:serial-number)", - "bloop2", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:serial-number)", "(param-ref 'laser2:serial-number)"], + [ + "(param-ref 'laser1:serial-number)", + "bloop1", + "> (param-ref 'laser2:serial-number)", + "bloop2", + "> ", + ], + sep="\r\n", ) as tm: assert tm.laser[0].serial_number == "bloop1" assert tm.laser[1].serial_number == "bloop2" @@ -39,19 +36,16 @@ def test_laser_serial_number(): def test_model(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:model)", - "(param-ref 'laser2:model)" - ], - [ - "(param-ref 'laser1:model)", - "bloop1", - "> (param-ref 'laser2:model)", - "bloop2", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:model)", "(param-ref 'laser2:model)"], + [ + "(param-ref 'laser1:model)", + "bloop1", + "> (param-ref 'laser2:model)", + "bloop2", + "> ", + ], + sep="\r\n", ) as tm: assert tm.laser[0].model == "bloop1" assert tm.laser[1].model == "bloop2" @@ -59,19 +53,16 @@ def test_model(): def test_wavelength(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:wavelength)", - "(param-ref 'laser2:wavelength)" - ], - [ - "(param-ref 'laser1:wavelength)", - "640", - "> (param-ref 'laser2:wavelength)", - "405.3", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:wavelength)", "(param-ref 'laser2:wavelength)"], + [ + "(param-ref 'laser1:wavelength)", + "640", + "> (param-ref 'laser2:wavelength)", + "405.3", + "> ", + ], + sep="\r\n", ) as tm: assert tm.laser[0].wavelength == 640 * u.nm assert tm.laser[1].wavelength == 405.3 * u.nm @@ -79,22 +70,22 @@ def test_wavelength(): def test_laser_enable(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:emission)", - "(param-ref 'laser1:serial-number)", - "(param-set! 'laser1:enable-emission #t)" - ], - [ - "(param-ref 'laser1:emission)", - "#f", - "> (param-ref 'laser1:serial-number)", - "bloop1", - "> (param-set! 'laser1:enable-emission #t)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:emission)", + "(param-ref 'laser1:serial-number)", + "(param-set! 'laser1:enable-emission #t)", + ], + [ + "(param-ref 'laser1:emission)", + "#f", + "> (param-ref 'laser1:serial-number)", + "bloop1", + "> (param-set! 'laser1:enable-emission #t)", + "0", + "> ", + ], + sep="\r\n", ) as tm: assert tm.laser[0].enable is False tm.laser[0].enable = True @@ -102,105 +93,87 @@ def test_laser_enable(): def test_laser_enable_no_laser(): with pytest.raises(RuntimeError), expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:serial-number)", - "(param-set! 'laser1:enable-emission #t)" - ], - [ - "(param-ref 'laser1:serial-number)", - "unknown", - "> (param-set! 'laser1:enable-emission #t)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:serial-number)", + "(param-set! 'laser1:enable-emission #t)", + ], + [ + "(param-ref 'laser1:serial-number)", + "unknown", + "> (param-set! 'laser1:enable-emission #t)", + "0", + "> ", + ], + sep="\r\n", ) as tm: tm.laser[0].enable = True def test_laser_enable_error(): with pytest.raises(TypeError), expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:serial-number)", - "(param-set! 'laser1:enable-emission #t)" - ], - [ - "(param-ref 'laser1:serial-number)", - "bloop1", - "> (param-set! 'laser1:enable-emission #t)", - "0", - "> " - ], - sep="\n" - ) as tm: - tm.laser[0].enable = 'True' + ik.toptica.TopMode, + [ + "(param-ref 'laser1:serial-number)", + "(param-set! 'laser1:enable-emission #t)", + ], + [ + "(param-ref 'laser1:serial-number)", + "bloop1", + "> (param-set! 'laser1:enable-emission #t)", + "0", + "> ", + ], + sep="\n", + ) as tm: + tm.laser[0].enable = "True" def test_laser_tec_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:tec:ready)" - ], - [ - "(param-ref 'laser1:tec:ready)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:tec:ready)"], + ["(param-ref 'laser1:tec:ready)", "#f", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].tec_status is False def test_laser_intensity(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:intensity)" - ], - [ - "(param-ref 'laser1:intensity)", - "0.666", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:intensity)"], + ["(param-ref 'laser1:intensity)", "0.666", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].intensity == 0.666 def test_laser_mode_hop(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:charm:reg:mh-occurred)"], + ["(param-ref 'laser1:charm:reg:mh-occurred)", "#f", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].mode_hop is False def test_laser_lock_start(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)", - "(param-ref 'laser1:charm:reg:started)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", - "2", - "> (param-ref 'laser1:charm:reg:started)", - "\"2012-12-01 01:02:01\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)", + "(param-ref 'laser1:charm:reg:started)", + ], + [ + "(param-ref 'laser1:charm:correction-status)", + "2", + "> (param-ref 'laser1:charm:reg:started)", + '"2012-12-01 01:02:01"', + "> ", + ], + sep="\r\n", ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].lock_start == _date @@ -208,19 +181,19 @@ def test_laser_lock_start(): def test_laser_lock_start_runtime_error(): with pytest.raises(RuntimeError), expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)", - "(param-ref 'laser1:charm:reg:started)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", - "0", - "> (param-ref 'laser1:charm:reg:started)", - "\"\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)", + "(param-ref 'laser1:charm:reg:started)", + ], + [ + "(param-ref 'laser1:charm:correction-status)", + "0", + "> (param-ref 'laser1:charm:reg:started)", + '""', + "> ", + ], + sep="\r\n", ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].lock_start == _date @@ -228,38 +201,38 @@ def test_laser_lock_start_runtime_error(): def test_laser_first_mode_hop_time_runtime_error(): with pytest.raises(RuntimeError), expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:first-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#f", - "> (param-ref 'laser1:charm:reg:first-mh)", - "\"\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:first-mh)", + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#f", + "> (param-ref 'laser1:charm:reg:first-mh)", + '""', + "> ", + ], + sep="\r\n", ) as tm: assert tm.laser[0].first_mode_hop_time is None def test_laser_first_mode_hop_time(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:first-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#t", - "> (param-ref 'laser1:charm:reg:first-mh)", - "\"2012-12-01 01:02:01\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:first-mh)", + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#t", + "> (param-ref 'laser1:charm:reg:first-mh)", + '"2012-12-01 01:02:01"', + "> ", + ], + sep="\r\n", ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].first_mode_hop_time == _date @@ -267,38 +240,38 @@ def test_laser_first_mode_hop_time(): def test_laser_latest_mode_hop_time_none(): with pytest.raises(RuntimeError), expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:latest-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#f", - "> (param-ref 'laser1:charm:reg:latest-mh)", - "\"\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:latest-mh)", + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#f", + "> (param-ref 'laser1:charm:reg:latest-mh)", + '""', + "> ", + ], + sep="\r\n", ) as tm: assert tm.laser[0].latest_mode_hop_time is None def test_laser_latest_mode_hop_time(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "(param-ref 'laser1:charm:reg:latest-mh)" - ], - [ - "(param-ref 'laser1:charm:reg:mh-occurred)", - "#t", - "> (param-ref 'laser1:charm:reg:latest-mh)", - "\"2012-12-01 01:02:01\"", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "(param-ref 'laser1:charm:reg:latest-mh)", + ], + [ + "(param-ref 'laser1:charm:reg:mh-occurred)", + "#t", + "> (param-ref 'laser1:charm:reg:latest-mh)", + '"2012-12-01 01:02:01"', + "> ", + ], + sep="\r\n", ) as tm: _date = datetime(2012, 12, 1, 1, 2, 1) assert tm.laser[0].latest_mode_hop_time == _date @@ -306,50 +279,47 @@ def test_laser_latest_mode_hop_time(): def test_laser_correction_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:charm:correction-status)"], + ["(param-ref 'laser1:charm:correction-status)", "0", "> "], + sep="\r\n", ) as tm: - assert tm.laser[0].correction_status == ik.toptica.TopMode.CharmStatus.un_initialized + assert ( + tm.laser[0].correction_status + == ik.toptica.TopMode.CharmStatus.un_initialized + ) def test_laser_correction(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:charm:correction-status)", # 1st - "(exec 'laser1:charm:start-correction-initial)", - "(param-ref 'laser1:charm:correction-status)", # 2nd - "(exec 'laser1:charm:start-correction)", - "(param-ref 'laser1:charm:correction-status)", # 3rd - "(param-ref 'laser1:charm:correction-status)", # 4th - "(exec 'laser1:charm:start-correction)" - ], - [ - "(param-ref 'laser1:charm:correction-status)", # 1st - "0", - "> (exec 'laser1:charm:start-correction-initial)", - "()", - "> (param-ref 'laser1:charm:correction-status)", # 3nd - "1", - "> (exec 'laser1:charm:start-correction)", - "()", - "> (param-ref 'laser1:charm:correction-status)", # 3rd - "3", - "> (param-ref 'laser1:charm:correction-status)", # 4th - "2", - "> (exec 'laser1:charm:start-correction)", - "()", - "> ", - ], - sep="\r\n" + ik.toptica.TopMode, + [ + "(param-ref 'laser1:charm:correction-status)", # 1st + "(exec 'laser1:charm:start-correction-initial)", + "(param-ref 'laser1:charm:correction-status)", # 2nd + "(exec 'laser1:charm:start-correction)", + "(param-ref 'laser1:charm:correction-status)", # 3rd + "(param-ref 'laser1:charm:correction-status)", # 4th + "(exec 'laser1:charm:start-correction)", + ], + [ + "(param-ref 'laser1:charm:correction-status)", # 1st + "0", + "> (exec 'laser1:charm:start-correction-initial)", + "()", + "> (param-ref 'laser1:charm:correction-status)", # 3nd + "1", + "> (exec 'laser1:charm:start-correction)", + "()", + "> (param-ref 'laser1:charm:correction-status)", # 3rd + "3", + "> (param-ref 'laser1:charm:correction-status)", # 4th + "2", + "> (exec 'laser1:charm:start-correction)", + "()", + "> ", + ], + sep="\r\n", ) as tm: tm.laser[0].correction() tm.laser[0].correction() @@ -359,163 +329,106 @@ def test_laser_correction(): def test_reboot_system(): with expected_protocol( - ik.toptica.TopMode, - [ - "(exec 'reboot-system)" - ], - [ - "(exec 'reboot-system)", - "reboot process started.", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(exec 'reboot-system)"], + ["(exec 'reboot-system)", "reboot process started.", "> "], + sep="\r\n", ) as tm: tm.reboot() def test_laser_ontime(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:ontime)" - ], - [ - "(param-ref 'laser1:ontime)", - "10000", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:ontime)"], + ["(param-ref 'laser1:ontime)", "10000", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].on_time == 10000 * u.s def test_laser_charm_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:health)" - ], - [ - "(param-ref 'laser1:health)", - "230", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:health)"], + ["(param-ref 'laser1:health)", "230", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].charm_status == 1 def test_laser_temperature_control_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:health)" - ], - [ - "(param-ref 'laser1:health)", - "230", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:health)"], + ["(param-ref 'laser1:health)", "230", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].temperature_control_status == 1 def test_laser_current_control_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:health)" - ], - [ - "(param-ref 'laser1:health)", - "230", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:health)"], + ["(param-ref 'laser1:health)", "230", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].current_control_status == 1 def test_laser_production_date(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'laser1:production-date)" - ], - [ - "(param-ref 'laser1:production-date)", - "2016-01-16", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'laser1:production-date)"], + ["(param-ref 'laser1:production-date)", "2016-01-16", "> "], + sep="\r\n", ) as tm: assert tm.laser[0].production_date == "2016-01-16" def test_set_str(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'blo \"blee\")" - ], - [ - "(param-set! 'blo \"blee\")", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ['(param-set! \'blo "blee")'], + ['(param-set! \'blo "blee")', "0", "> "], + sep="\r\n", ) as tm: tm.set("blo", "blee") def test_set_list(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'blo '(blee blo))" - ], - [ - "(param-set! 'blo '(blee blo))", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-set! 'blo '(blee blo))"], + ["(param-set! 'blo '(blee blo))", "0", "> "], + sep="\r\n", ) as tm: tm.set("blo", ["blee", "blo"]) def test_display(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-disp 'blo)" - ], - [ - "(param-disp 'blo)", - "bloop", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-disp 'blo)"], + ["(param-disp 'blo)", "bloop", "> "], + sep="\r\n", ) as tm: assert tm.display("blo") == "bloop" def test_enable(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'emission)", - "(param-set! 'enable-emission #f)" - ], - [ - "(param-ref 'emission)", - "#f", - "> (param-set! 'enable-emission #f)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'emission)", "(param-set! 'enable-emission #f)"], + [ + "(param-ref 'emission)", + "#f", + "> (param-set! 'enable-emission #f)", + "0", + "> ", + ], + sep="\r\n", ) as tm: assert tm.enable is False tm.enable = False @@ -523,143 +436,90 @@ def test_enable(): def test_firmware(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'fw-ver)" - ], - [ - "(param-ref 'fw-ver)", - "1.02.01", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'fw-ver)"], + ["(param-ref 'fw-ver)", "1.02.01", "> "], + sep="\r\n", ) as tm: assert tm.firmware == (1, 2, 1) def test_serial_number(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'serial-number)" - ], - [ - "(param-ref 'serial-number)", - "010101", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'serial-number)"], + ["(param-ref 'serial-number)", "010101", "> "], + sep="\r\n", ) as tm: - assert tm.serial_number == '010101' + assert tm.serial_number == "010101" def test_enable_error(): with pytest.raises(TypeError): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-set! 'enable-emission #f)" - ], - [ - "(param-set! 'enable-emission #f)", - ">" - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-set! 'enable-emission #f)"], + ["(param-set! 'enable-emission #f)", ">"], + sep="\r\n", ) as tm: tm.enable = "False" def test_front_key(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'front-key-locked)" - ], - [ - "(param-ref 'front-key-locked)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'front-key-locked)"], + ["(param-ref 'front-key-locked)", "#f", "> "], + sep="\r\n", ) as tm: assert tm.locked is False def test_interlock(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'interlock-open)" - ], - [ - "(param-ref 'interlock-open)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'interlock-open)"], + ["(param-ref 'interlock-open)", "#f", "> "], + sep="\r\n", ) as tm: assert tm.interlock is False def test_fpga_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "0", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'system-health)"], + ["(param-ref 'system-health)", "0", "> "], + sep="\r\n", ) as tm: assert tm.fpga_status is True def test_fpga_status_false(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "#f", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'system-health)"], + ["(param-ref 'system-health)", "#f", "> "], + sep="\r\n", ) as tm: assert tm.fpga_status is False def test_temperature_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "2", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'system-health)"], + ["(param-ref 'system-health)", "2", "> "], + sep="\r\n", ) as tm: assert tm.temperature_status is False def test_current_status(): with expected_protocol( - ik.toptica.TopMode, - [ - "(param-ref 'system-health)" - ], - [ - "(param-ref 'system-health)", - "4", - "> " - ], - sep="\r\n" + ik.toptica.TopMode, + ["(param-ref 'system-health)"], + ["(param-ref 'system-health)", "4", "> "], + sep="\r\n", ) as tm: assert tm.current_status is False diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index aa48457c0..c944b004b 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -21,7 +21,7 @@ setattr_expression, string_property, unitful_property, - unitless_property + unitless_property, ) @@ -39,13 +39,15 @@ def mock_inst(mocker): :return: Fake instrument class. """ + class Inst: """Mock instrument class.""" + def __init__(self): """Set up the mocker spies and send command placeholder.""" # spies - self.spy_query = mocker.spy(self, 'query') - self.spy_sendcmd = mocker.spy(self, 'sendcmd') + self.spy_query = mocker.spy(self, "query") + self.spy_sendcmd = mocker.spy(self, "sendcmd") # variable to set with send command self._sendcmd = None @@ -83,7 +85,6 @@ class SomeEnum(Enum): def test_ProxyList_basics(): class ProxyChild: - def __init__(self, parent, name): self._parent = parent self._name = name @@ -99,7 +100,6 @@ def __init__(self, parent, name): def test_ProxyList_valid_range_is_enum(): class ProxyChild: - def __init__(self, parent, name): self._parent = parent self._name = name @@ -111,14 +111,13 @@ class MockEnum(Enum): parent = object() proxy_list = ProxyList(parent, ProxyChild, MockEnum) - assert proxy_list['aa']._name == MockEnum.a.value - assert proxy_list['b']._name == MockEnum.b.value + assert proxy_list["aa"]._name == MockEnum.a.value + assert proxy_list["b"]._name == MockEnum.b.value assert proxy_list[MockEnum.a]._name == MockEnum.a.value def test_ProxyList_length(): class ProxyChild: - def __init__(self, parent, name): self._parent = parent self._name = name @@ -132,7 +131,6 @@ def __init__(self, parent, name): def test_ProxyList_iterator(): class ProxyChild: - def __init__(self, parent, name): self._parent = parent self._name = name @@ -149,8 +147,8 @@ def __init__(self, parent, name): def test_ProxyList_invalid_idx_enum(): with pytest.raises(IndexError): - class ProxyChild: + class ProxyChild: def __init__(self, parent, name): self._parent = parent self._name = name @@ -163,13 +161,13 @@ class MockEnum(Enum): proxy_list = ProxyList(parent, ProxyChild, MockEnum) - _ = proxy_list['c'] # Should raise IndexError + _ = proxy_list["c"] # Should raise IndexError def test_ProxyList_invalid_idx(): with pytest.raises(IndexError): - class ProxyChild: + class ProxyChild: def __init__(self, parent, name): self._parent = parent self._name = name @@ -182,60 +180,68 @@ def __init__(self, parent, name): def test_assume_units_correct(): - m = u.Quantity(1, 'm') + m = u.Quantity(1, "m") # Check that unitful quantities are kept unitful. - assert assume_units(m, 'mm').to('mm').magnitude == 1000 + assert assume_units(m, "mm").to("mm").magnitude == 1000 # Check that raw scalars are made unitful. - assert assume_units(1, 'm').to('mm').magnitude == 1000 + assert assume_units(1, "m").to("mm").magnitude == 1000 def test_assume_units_failures(): with pytest.raises(pint.errors.DimensionalityError): - assume_units(1, 'm').to('s') + assume_units(1, "m").to("s") + def test_setattr_expression_simple(): class A: - x = 'x' - y = 'y' - z = 'z' + x = "x" + y = "y" + z = "z" a = A() - setattr_expression(a, 'x', 'foo') - assert a.x == 'foo' + setattr_expression(a, "x", "foo") + assert a.x == "foo" + def test_setattr_expression_index(): class A: - x = ['x', 'y', 'z'] + x = ["x", "y", "z"] a = A() - setattr_expression(a, 'x[1]', 'foo') - assert a.x[1] == 'foo' + setattr_expression(a, "x[1]", "foo") + assert a.x[1] == "foo" + def test_setattr_expression_nested(): class B: - x = 'x' + x = "x" + class A: b = None + def __init__(self): self.b = B() a = A() - setattr_expression(a, 'b.x', 'foo') - assert a.b.x == 'foo' + setattr_expression(a, "b.x", "foo") + assert a.b.x == "foo" + def test_setattr_expression_both(): class B: - x = 'x' + x = "x" + class A: b = None + def __init__(self): self.b = [B()] a = A() - setattr_expression(a, 'b[0].x', 'foo') - assert a.b[0].x == 'foo' + setattr_expression(a, "b[0].x", "foo") + assert a.b[0].x == "foo" def test_bool_property_sendcmd_query(mock_inst): diff --git a/instruments/tests/test_yokogawa/test_yokogawa7651.py b/instruments/tests/test_yokogawa/test_yokogawa7651.py index 19211844a..17ab91b9c 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa7651.py +++ b/instruments/tests/test_yokogawa/test_yokogawa7651.py @@ -23,13 +23,7 @@ def test_channel_init(): """Initialize of channel class.""" - with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - ], - [ - ] - ) as yok: + with expected_protocol(ik.yokogawa.Yokogawa7651, [], []) as yok: assert yok.channel[0]._parent is yok assert yok.channel[0]._name == 0 @@ -37,22 +31,16 @@ def test_channel_init(): def test_channel_mode(): """Get / Set mode of the channel.""" with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - "F5;", - "E;", # trigger - "F1;", - "E;" # trigger - ], - [ - ] + ik.yokogawa.Yokogawa7651, ["F5;", "E;", "F1;", "E;"], [] # trigger # trigger ) as yok: # query with pytest.raises(NotImplementedError) as exc_info: print(f"Mode is: {yok.channel[0].mode}") exc_msg = exc_info.value.args[0] - assert exc_msg == "This instrument does not support querying the " \ - "operation mode." + assert ( + exc_msg == "This instrument does not support querying the " + "operation mode." + ) # set first current, then voltage mode yok.channel[0].mode = yok.Mode.current @@ -61,47 +49,44 @@ def test_channel_mode(): def test_channel_invalid_mode_set(): """Set mode to invalid value.""" - with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - ], - [ - ] - ) as yok: + with expected_protocol(ik.yokogawa.Yokogawa7651, [], []) as yok: wrong_mode = 42 with pytest.raises(TypeError) as exc_info: yok.channel[0].mode = wrong_mode exc_msg = exc_info.value.args[0] - assert exc_msg == "Mode setting must be a `Yokogawa7651.Mode` " \ - "value, got {} instead.".format(type(wrong_mode)) + assert ( + exc_msg == "Mode setting must be a `Yokogawa7651.Mode` " + "value, got {} instead.".format(type(wrong_mode)) + ) def test_channel_voltage(): """Get / Set voltage of channel.""" # values to set for test - value_unitless = 5. + value_unitless = 5.0 value_unitful = u.Quantity(500, u.mV) with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - "F1;\nE;", # set voltage mode - f"SA{value_unitless};", - "E;", # trigger - "F1;\nE;", # set voltage mode - f"SA{value_unitful.to(u.volt).magnitude};", - "E;" # trigger - ], - [ - ] + ik.yokogawa.Yokogawa7651, + [ + "F1;\nE;", # set voltage mode + f"SA{value_unitless};", + "E;", # trigger + "F1;\nE;", # set voltage mode + f"SA{value_unitful.to(u.volt).magnitude};", + "E;", # trigger + ], + [], ) as yok: # query with pytest.raises(NotImplementedError) as exc_info: print(f"Voltage is: {yok.channel[0].voltage}") exc_msg = exc_info.value.args[0] - assert exc_msg == "This instrument does not support querying the " \ - "output voltage setting." + assert ( + exc_msg == "This instrument does not support querying the " + "output voltage setting." + ) # set first current, then voltage mode yok.channel[0].voltage = value_unitless @@ -116,24 +101,25 @@ def test_channel_current(): value_unitful = u.Quantity(50, u.mA) with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - "F5;\nE;", # set voltage mode - f"SA{value_unitless};", - "E;", # trigger - "F5;\nE;", # set voltage mode - f"SA{value_unitful.to(u.A).magnitude};", - "E;" # trigger - ], - [ - ] + ik.yokogawa.Yokogawa7651, + [ + "F5;\nE;", # set voltage mode + f"SA{value_unitless};", + "E;", # trigger + "F5;\nE;", # set voltage mode + f"SA{value_unitful.to(u.A).magnitude};", + "E;", # trigger + ], + [], ) as yok: # query with pytest.raises(NotImplementedError) as exc_info: print(f"Current is: {yok.channel[0].current}") exc_msg = exc_info.value.args[0] - assert exc_msg == "This instrument does not support querying the " \ - "output current setting." + assert ( + exc_msg == "This instrument does not support querying the " + "output current setting." + ) # set first current, then current mode yok.channel[0].current = value_unitless @@ -143,22 +129,17 @@ def test_channel_current(): def test_channel_output(): """Get / Set output of channel.""" with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - "O1;", # turn output on - "E;", - "O0;", # turn output off - "E;" - ], - [ - ] + ik.yokogawa.Yokogawa7651, + ["O1;", "E;", "O0;", "E;"], # turn output on # turn output off + [], ) as yok: # query with pytest.raises(NotImplementedError) as exc_info: print(f"Output is: {yok.channel[0].output}") exc_msg = exc_info.value.args[0] - assert exc_msg == "This instrument does not support querying the " \ - "output status." + assert ( + exc_msg == "This instrument does not support querying the " "output status." + ) # set first current, then current mode yok.channel[0].output = True @@ -172,28 +153,29 @@ def test_voltage(): """Get / Set voltage of instrument.""" # values to set for test - value_unitless = 5. + value_unitless = 5.0 value_unitful = u.Quantity(500, u.mV) with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - "F1;\nE;", # set voltage mode - f"SA{value_unitless};", - "E;", # trigger - "F1;\nE;", # set voltage mode - f"SA{value_unitful.to(u.volt).magnitude};", - "E;" # trigger - ], - [ - ] + ik.yokogawa.Yokogawa7651, + [ + "F1;\nE;", # set voltage mode + f"SA{value_unitless};", + "E;", # trigger + "F1;\nE;", # set voltage mode + f"SA{value_unitful.to(u.volt).magnitude};", + "E;", # trigger + ], + [], ) as yok: # query with pytest.raises(NotImplementedError) as exc_info: print(f"Voltage is: {yok.voltage}") exc_msg = exc_info.value.args[0] - assert exc_msg == "This instrument does not support querying the " \ - "output voltage setting." + assert ( + exc_msg == "This instrument does not support querying the " + "output voltage setting." + ) # set first current, then voltage mode yok.voltage = value_unitless @@ -208,24 +190,25 @@ def test_current(): value_unitful = u.Quantity(50, u.mA) with expected_protocol( - ik.yokogawa.Yokogawa7651, - [ - "F5;\nE;", # set current mode - f"SA{value_unitless};", - "E;", # trigger - "F5;\nE;", # set current mode - f"SA{value_unitful.to(u.A).magnitude};", - "E;" # trigger - ], - [ - ] + ik.yokogawa.Yokogawa7651, + [ + "F5;\nE;", # set current mode + f"SA{value_unitless};", + "E;", # trigger + "F5;\nE;", # set current mode + f"SA{value_unitful.to(u.A).magnitude};", + "E;", # trigger + ], + [], ) as yok: # query with pytest.raises(NotImplementedError) as exc_info: print(f"current is: {yok.current}") exc_msg = exc_info.value.args[0] - assert exc_msg == "This instrument does not support querying the " \ - "output current setting." + assert ( + exc_msg == "This instrument does not support querying the " + "output current setting." + ) # set first current, then current mode yok.current = value_unitless diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index e0f4de355..f4e889646 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -32,31 +32,25 @@ def test_channel_is_channel_class(): def test_init(): - with expected_protocol( - ik.yokogawa.Yokogawa6370, - [ - ":FORMat:DATA REAL,64" - ], - [] - ) as _: + with expected_protocol(ik.yokogawa.Yokogawa6370, [":FORMat:DATA REAL,64"], []) as _: pass -@given(values=st.lists(st.floats(allow_infinity=False, allow_nan=False), min_size=1), - channel=st.sampled_from(ik.yokogawa.Yokogawa6370.Traces)) +@given( + values=st.lists(st.floats(allow_infinity=False, allow_nan=False), min_size=1), + channel=st.sampled_from(ik.yokogawa.Yokogawa6370.Traces), +) def test_channel_data(values, channel): values_packed = b"".join(struct.pack(" 0: - self._channel = tuple(self._channel_type(self, chan_idx) - for chan_idx in range(self._n_channels)) + self._channel = tuple( + self._channel_type(self, chan_idx) + for chan_idx in range(self._n_channels) + ) @property def serial_number(self): @@ -182,13 +189,15 @@ def name(self): :type: `str` """ - return "ThorLabs APT Instrument model {model}, serial {serial} " \ - "(HW version {hw_ver}, FW version {fw_ver})".format( - hw_ver=self._hw_version, - serial=self.serial_number, - fw_ver=self._fw_version, - model=self.model_number - ) + return ( + "ThorLabs APT Instrument model {model}, serial {serial} " + "(HW version {hw_ver}, FW version {fw_ver})".format( + hw_ver=self._hw_version, + serial=self.serial_number, + fw_ver=self._fw_version, + model=self.model_number, + ) + ) @property def channel(self): @@ -218,9 +227,10 @@ def n_channels(self, nch): # If we add more channels, append them to the list, # If we remove channels, remove them from the end of the list. if nch > self._n_channels: - self._channel = list(self._channel) + \ - list(self._channel_type(self, chan_idx) - for chan_idx in range(self._n_channels, nch)) + self._channel = list(self._channel) + list( + self._channel_type(self, chan_idx) + for chan_idx in range(self._n_channels, nch) + ) elif nch < self._n_channels: self._channel = self._channel[:nch] self._n_channels = nch @@ -236,7 +246,7 @@ def identify(self): param2=0x00, dest=self._dest, source=0x01, - data=None + data=None, ) self.sendpacket(pkt) @@ -264,6 +274,7 @@ class PiezoDeviceChannel(ThorLabsAPT.APTChannel): This class represents piezo stage channels. """ + # PIEZO COMMANDS # @property @@ -280,11 +291,9 @@ def max_travel(self): param2=0x00, dest=self._apt.destination, source=0x01, - data=None - ) - resp = self._apt.querypacket( - pkt, expect_data_len=4 + data=None, ) + resp = self._apt.querypacket(pkt, expect_data_len=4) # Not all APT piezo devices support querying the maximum travel # distance. Those that do not simply ignore the PZ_REQ_MAXTRAVEL @@ -293,8 +302,8 @@ def max_travel(self): return NotImplemented # chan, int_maxtrav - _, int_maxtrav = struct.unpack(' 125: - raise ValueError("The voltage ({} V) is out of range. It must " - "be between 85 V and 125 V.".format(volt)) + raise ValueError( + "The voltage ({} V) is out of range. It must " + "be between 85 V and 125 V.".format(volt) + ) if rate < 1 or rate > 2000: - raise ValueError("The step rate ({} /s) is out of range. It " - "must be between 1 /s and 2,000 /s." - .format(rate)) + raise ValueError( + "The step rate ({} /s) is out of range. It " + "must be between 1 /s and 2,000 /s.".format(rate) + ) if accl < 1 or accl > 100000: - raise ValueError("The acceleration ({} /s/s) is out of range. " - "It must be between 1 /s/s and 100,000 /s/s." - .format(accl)) + raise ValueError( + "The acceleration ({} /s/s) is out of range. " + "It must be between 1 /s/s and 100,000 /s/s.".format(accl) + ) pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.PZMOT_SET_PARAMS, @@ -485,8 +493,7 @@ def drive_op_parameters(self, params): param2=None, dest=self._apt.destination, source=0x01, - data=struct.pack('>> # enable channel 0 >>> ch.enabled_single = True """ - if self._apt.model_number[0:3] != 'KIM': - raise("This command is only valid with KIM001 and " - "KIM101 controllers. Your controller is a {}." - .format(self._apt.model_number)) + if self._apt.model_number[0:3] != "KIM": + raise ( + "This command is only valid with KIM001 and " + "KIM101 controllers. Your controller is a {}.".format( + self._apt.model_number + ) + ) pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, @@ -522,25 +532,25 @@ def enabled_single(self): param2=self._idx_chan, dest=self._apt.destination, source=0x01, - data=None + data=None, ) resp = self._apt.querypacket( - pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, - expect_data_len=4) + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, expect_data_len=4 + ) - ret_val = struct.unpack('>> ch.jog_parameters [2, 100, 100, array(1000) * 1/s, array(10000) * 1/s**2] """ - if self._apt.model_number[0:3] != 'KIM': - raise TypeError("This command is only valid with " - "KIM001 and KIM101 controllers. Your " - "controller is a {}." - .format(self._apt.model_number)) + if self._apt.model_number[0:3] != "KIM": + raise TypeError( + "This command is only valid with " + "KIM001 and KIM101 controllers. Your " + "controller is a {}.".format(self._apt.model_number) + ) pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, @@ -627,72 +638,75 @@ def jog_parameters(self): param2=self._idx_chan, dest=self._apt.destination, source=0x01, - data=None + data=None, ) resp = self._apt.querypacket( - pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, - expect_data_len=22) + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, expect_data_len=22 + ) # unpack response - ret_val = struct.unpack(' 2000: - raise ValueError("The steps forward ({}) are out of range. It " - "must be between 1 and 2,000." - .format(steps_fwd)) + raise ValueError( + "The steps forward ({}) are out of range. It " + "must be between 1 and 2,000.".format(steps_fwd) + ) if steps_bkw < 1 or steps_bkw > 2000: - raise ValueError("The steps backward ({}) are out of range. " - "It must be between 1 and 2,000." - .format(steps_bkw)) + raise ValueError( + "The steps backward ({}) are out of range. " + "It must be between 1 and 2,000.".format(steps_bkw) + ) if rate < 1 or rate > 2000: - raise ValueError("The step rate ({} /s) is out of range. It " - "must be between 1 /s and 2,000 /s." - .format(rate)) + raise ValueError( + "The step rate ({} /s) is out of range. It " + "must be between 1 /s and 2,000 /s.".format(rate) + ) if accl < 1 or accl > 100000: - raise ValueError("The acceleration ({} /s/s) is out of range. " - "It must be between 1 /s/s and 100,000 /s/s." - .format(accl)) + raise ValueError( + "The acceleration ({} /s/s) is out of range. " + "It must be between 1 /s/s and 100,000 /s/s.".format(accl) + ) pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.PZMOT_SET_PARAMS, @@ -700,8 +714,16 @@ def jog_parameters(self, params): param2=None, dest=self._apt.destination, source=0x01, - data=struct.pack('>> # jog reverse >>> ch.move_jog('rev') """ - if direction == 'rev': + if direction == "rev": param2 = 0x02 else: param2 = 0x01 @@ -827,7 +848,7 @@ def move_jog(self, direction='fwd'): param2=param2, dest=self._apt.destination, source=0x01, - data=None + data=None, ) self._apt.sendpacket(pkt) @@ -847,7 +868,7 @@ def move_jog_stop(self): param2=0x00, dest=self._apt.destination, source=0x01, - data=None + data=None, ) self._apt.sendpacket(pkt) @@ -887,11 +908,12 @@ def enabled_multi(self): >>> kim.enabled_multi 1 """ - if self.model_number != 'KIM101': - raise TypeError("This command is only valid with " - "a KIM101 controller. Your " - "controller is a {}." - .format(self.model_number)) + if self.model_number != "KIM101": + raise TypeError( + "This command is only valid with " + "a KIM101 controller. Your " + "controller is a {}.".format(self.model_number) + ) pkt = _packets.ThorLabsPacket( message_id=_cmds.ThorLabsCommands.PZMOT_REQ_PARAMS, @@ -899,14 +921,14 @@ def enabled_multi(self): param2=0x00, dest=self.destination, source=0x01, - data=None + data=None, ) - resp = self.querypacket(pkt, - expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, - expect_data_len=4) + resp = self.querypacket( + pkt, expect=_cmds.ThorLabsCommands.PZMOT_GET_PARAMS, expect_data_len=4 + ) - ret_val = int(struct.unpack(' 0)) @@ -1276,15 +1315,14 @@ def position(self): param2=0x00, dest=self._apt.destination, source=0x01, - data=None + data=None, ) response = self._apt.querypacket( - pkt, expect=_cmds.ThorLabsCommands.MOT_GET_POSCOUNTER, - expect_data_len=6 + pkt, expect=_cmds.ThorLabsCommands.MOT_GET_POSCOUNTER, expect_data_len=6 ) # chan, pos - _, pos = struct.unpack(' -1: + if response.find("Error: -3") > -1: return None - elif response.find('f') > -1: + elif response.find("f") > -1: return False - elif response.find('t') > -1: + elif response.find("t") > -1: return True else: raise ValueError("cannot convert: " + str(response) + " to boolean") diff --git a/instruments/util_fns.py b/instruments/util_fns.py index 3681ed590..fafe9c5a2 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -14,7 +14,7 @@ # CONSTANTS ################################################################### -_IDX_REGEX = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*)\[(-?[0-9]*)\]') +_IDX_REGEX = re.compile(r"([a-zA-Z_][a-zA-Z0-9_]*)\[(-?[0-9]*)\]") # FUNCTIONS ################################################################### @@ -47,9 +47,9 @@ def setattr_expression(target, name_expr, value): """ # Allow "." in attribute names so that we can set attributes # recursively. - if '.' in name_expr: + if "." in name_expr: # Recursion: We have to strip off a level of getattr. - head, name_expr = name_expr.split('.', 1) + head, name_expr = name_expr.split(".", 1) match = _IDX_REGEX.match(head) if match: head_name, head_idx = match.groups() @@ -121,7 +121,7 @@ def split_unit_str(s, default_units=u.dimensionless, lookup=None): if match.groups()[1] is None: val, _, units = match.groups() else: - val = float(match.groups()[0]) * 10**float(match.groups()[1][1:]) + val = float(match.groups()[0]) * 10 ** float(match.groups()[1][1:]) units = match.groups()[2] if units is None: @@ -157,8 +157,16 @@ def rproperty(fget=None, fset=None, doc=None, readonly=False, writeonly=False): return property(fget=fget, fset=fset, doc=doc) -def bool_property(command, set_cmd=None, inst_true="ON", inst_false="OFF", - doc=None, readonly=False, writeonly=False, set_fmt="{} {}"): +def bool_property( + command, + set_cmd=None, + inst_true="ON", + inst_false="OFF", + doc=None, + readonly=False, + writeonly=False, + set_fmt="{} {}", +): """ Called inside of SCPI classes to instantiate boolean properties of the device cleanly. @@ -200,20 +208,30 @@ def _getter(self): def _setter(self, newval): if not isinstance(newval, bool): - raise TypeError("Bool properties must be specified with a " - "boolean value") - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - inst_true if newval else inst_false - )) + raise TypeError("Bool properties must be specified with a " "boolean value") + self.sendcmd( + set_fmt.format( + command if set_cmd is None else set_cmd, + inst_true if newval else inst_false, + ) + ) - return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, - writeonly=writeonly) + return rproperty( + fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly + ) -def enum_property(command, enum, set_cmd=None, doc=None, input_decoration=None, - output_decoration=None, readonly=False, writeonly=False, - set_fmt="{} {}"): +def enum_property( + command, + enum, + set_cmd=None, + doc=None, + input_decoration=None, + output_decoration=None, + readonly=False, + writeonly=False, + set_fmt="{} {}", +): """ Called inside of SCPI classes to instantiate Enum properties of the device cleanly. @@ -249,6 +267,7 @@ def enum_property(command, enum, set_cmd=None, doc=None, input_decoration=None, parameter is still used to set the command for pure-write commands to the instrument. """ + def _in_decor_fcn(val): if input_decoration is None: return val @@ -274,17 +293,27 @@ def _setter(self, newval): newval = enum(newval) except ValueError: raise ValueError("Enum property new value not in enum.") - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - _out_decor_fcn(enum(newval).value) - )) + self.sendcmd( + set_fmt.format( + command if set_cmd is None else set_cmd, + _out_decor_fcn(enum(newval).value), + ) + ) - return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, - writeonly=writeonly) + return rproperty( + fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly + ) -def unitless_property(command, set_cmd=None, format_code='{:e}', doc=None, - readonly=False, writeonly=False, set_fmt="{} {}"): +def unitless_property( + command, + set_cmd=None, + format_code="{:e}", + doc=None, + readonly=False, + writeonly=False, + set_fmt="{} {}", +): """ Called inside of SCPI classes to instantiate properties with unitless numeric values. @@ -320,18 +349,23 @@ def _setter(self, newval): else: raise ValueError strval = format_code.format(newval) - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - strval - )) + self.sendcmd(set_fmt.format(command if set_cmd is None else set_cmd, strval)) - return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, - writeonly=writeonly) + return rproperty( + fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly + ) -def int_property(command, set_cmd=None, format_code='{:d}', doc=None, - readonly=False, writeonly=False, valid_set=None, - set_fmt="{} {}"): +def int_property( + command, + set_cmd=None, + format_code="{:d}", + doc=None, + readonly=False, + writeonly=False, + valid_set=None, + set_fmt="{} {}", +): """ Called inside of SCPI classes to instantiate properties with unitless numeric values. @@ -361,14 +395,17 @@ def int_property(command, set_cmd=None, format_code='{:d}', doc=None, def _getter(self): raw = self.query("{}?".format(command)) return int(raw) + if valid_set is None: + def _setter(self, newval): strval = format_code.format(newval) - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - strval - )) + self.sendcmd( + set_fmt.format(command if set_cmd is None else set_cmd, strval) + ) + else: + def _setter(self, newval): if newval not in valid_set: raise ValueError( @@ -376,19 +413,28 @@ def _setter(self, newval): "must be one of {}.".format(newval, valid_set) ) strval = format_code.format(newval) - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - strval - )) + self.sendcmd( + set_fmt.format(command if set_cmd is None else set_cmd, strval) + ) - return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, - writeonly=writeonly) + return rproperty( + fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly + ) -def unitful_property(command, units, set_cmd=None, format_code='{:e}', doc=None, - input_decoration=None, output_decoration=None, - readonly=False, writeonly=False, set_fmt="{} {}", - valid_range=(None, None)): +def unitful_property( + command, + units, + set_cmd=None, + format_code="{:e}", + doc=None, + input_decoration=None, + output_decoration=None, + readonly=False, + writeonly=False, + set_fmt="{} {}", + valid_range=(None, None), +): """ Called inside of SCPI classes to instantiate properties with unitful numeric values. This function assumes that the instrument only accepts @@ -428,6 +474,7 @@ def unitful_property(command, units, set_cmd=None, format_code='{:e}', doc=None, The valid set is inclusive of the values provided. :type valid_range: `tuple` or `list` of `int` or `float` """ + def _in_decor_fcn(val): if input_decoration is None: return val @@ -452,30 +499,40 @@ def _setter(self, newval): if callable(min_value): min_value = min_value(self) # pylint: disable=not-callable if newval < min_value: - raise ValueError(f"Unitful quantity is too low. Got {newval}, " - f"minimum value is {min_value}") + raise ValueError( + f"Unitful quantity is too low. Got {newval}, " + f"minimum value is {min_value}" + ) if max_value is not None: if callable(max_value): max_value = max_value(self) # pylint: disable=not-callable if newval > max_value: - raise ValueError(f"Unitful quantity is too high. Got {newval}, " - f"maximum value is {max_value}") + raise ValueError( + f"Unitful quantity is too high. Got {newval}, " + f"maximum value is {max_value}" + ) # Rescale to the correct unit before printing. This will also # catch bad units. - strval = format_code.format( - assume_units(newval, units).to(units).magnitude) - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - _out_decor_fcn(strval) - )) - - return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, - writeonly=writeonly) + strval = format_code.format(assume_units(newval, units).to(units).magnitude) + self.sendcmd( + set_fmt.format( + command if set_cmd is None else set_cmd, _out_decor_fcn(strval) + ) + ) + + return rproperty( + fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly + ) -def bounded_unitful_property(command, units, min_fmt_str="{}:MIN?", - max_fmt_str="{}:MAX?", - valid_range=("query", "query"), **kwargs): +def bounded_unitful_property( + command, + units, + min_fmt_str="{}:MIN?", + max_fmt_str="{}:MAX?", + valid_range=("query", "query"), + **kwargs, +): """ Called inside of SCPI classes to instantiate properties with unitful numeric values which have upper and lower bounds. This function in turn calls @@ -519,30 +576,41 @@ def bounded_unitful_property(command, units, min_fmt_str="{}:MIN?", def _min_getter(self): if valid_range[0] == "query": - return u.Quantity(*split_unit_str(self.query(min_fmt_str.format(command)), units)) + return u.Quantity( + *split_unit_str(self.query(min_fmt_str.format(command)), units) + ) return assume_units(valid_range[0], units).to(units) def _max_getter(self): if valid_range[1] == "query": - return u.Quantity(*split_unit_str(self.query(max_fmt_str.format(command)), units)) + return u.Quantity( + *split_unit_str(self.query(max_fmt_str.format(command)), units) + ) return assume_units(valid_range[1], units).to(units) new_range = ( None if valid_range[0] is None else _min_getter, - None if valid_range[1] is None else _max_getter + None if valid_range[1] is None else _max_getter, ) return ( unitful_property(command, units, valid_range=new_range, **kwargs), property(_min_getter) if valid_range[0] is not None else None, - property(_max_getter) if valid_range[1] is not None else None + property(_max_getter) if valid_range[1] is not None else None, ) -def string_property(command, set_cmd=None, bookmark_symbol='"', doc=None, - readonly=False, writeonly=False, set_fmt="{} {}{}{}"): +def string_property( + command, + set_cmd=None, + bookmark_symbol='"', + doc=None, + readonly=False, + writeonly=False, + set_fmt="{} {}{}{}", +): """ Called inside of SCPI classes to instantiate properties with a string value. @@ -569,18 +637,25 @@ def string_property(command, set_cmd=None, bookmark_symbol='"', doc=None, def _getter(self): string = self.query("{}?".format(command)) - string = string[ - bookmark_length:-bookmark_length] if bookmark_length > 0 else string + string = ( + string[bookmark_length:-bookmark_length] if bookmark_length > 0 else string + ) return string def _setter(self, newval): - self.sendcmd(set_fmt.format( - command if set_cmd is None else set_cmd, - bookmark_symbol, newval, bookmark_symbol - )) + self.sendcmd( + set_fmt.format( + command if set_cmd is None else set_cmd, + bookmark_symbol, + newval, + bookmark_symbol, + ) + ) + + return rproperty( + fget=_getter, fset=_setter, doc=doc, readonly=readonly, writeonly=writeonly + ) - return rproperty(fget=_getter, fset=_setter, doc=doc, readonly=readonly, - writeonly=writeonly) # CLASSES ##################################################################### @@ -613,9 +688,10 @@ def __init__(self, parent, proxy_cls, valid_set): self._valid_set = valid_set # FIXME: This only checks the next level up the chain! - if hasattr(valid_set, '__bases__'): + if hasattr(valid_set, "__bases__"): self._isenum = (Enum in valid_set.__bases__) or ( - IntEnum in valid_set.__bases__) + IntEnum in valid_set.__bases__ + ) else: self._isenum = False @@ -635,13 +711,15 @@ def __getitem__(self, idx): except ValueError: pass if not isinstance(idx, self._valid_set): - raise IndexError("Index out of range. Must be " - "in {}.".format(self._valid_set)) + raise IndexError( + "Index out of range. Must be " "in {}.".format(self._valid_set) + ) idx = idx.value else: if idx not in self._valid_set: - raise IndexError("Index out of range. Must be " - "in {}.".format(self._valid_set)) + raise IndexError( + "Index out of range. Must be " "in {}.".format(self._valid_set) + ) return self._proxy_cls(self._parent, idx) def __len__(self): diff --git a/instruments/yokogawa/yokogawa6370.py b/instruments/yokogawa/yokogawa6370.py index 2c90b0756..6b7c34a48 100644 --- a/instruments/yokogawa/yokogawa6370.py +++ b/instruments/yokogawa/yokogawa6370.py @@ -16,13 +16,17 @@ OSAChannel, ) from instruments.util_fns import ( - enum_property, unitful_property, unitless_property, - bounded_unitful_property, ProxyList + enum_property, + unitful_property, + unitless_property, + bounded_unitful_property, + ProxyList, ) # CLASSES ##################################################################### + class Yokogawa6370(OpticalSpectrumAnalyzer): """ @@ -53,6 +57,7 @@ class Channel(OSAChannel): .. warning:: This class should NOT be manually created by the user. It is designed to be initialized by the `Yokogawa6370` class. """ + def __init__(self, parent, idx): self._parent = parent self._name = idx @@ -79,6 +84,7 @@ class SweepModes(IntEnum): """ Enum containing valid output modes for the Yokogawa 6370 """ + SINGLE = 1 REPEAT = 2 AUTO = 3 @@ -87,6 +93,7 @@ class Traces(Enum): """ Enum containing valid Traces for the Yokogawa 6370 """ + A = "TRA" B = "TRB" C = "TRC" @@ -117,7 +124,7 @@ def channel(self): doc=""" The start wavelength in m. """, - valid_range=(600e-9, 1700e-9) + valid_range=(600e-9, 1700e-9), ) stop_wl, stop_wl_min, stop_wl_max = bounded_unitful_property( @@ -126,7 +133,7 @@ def channel(self): doc=""" The stop wavelength in m. """, - valid_range=(600e-9, 1700e-9) + valid_range=(600e-9, 1700e-9), ) bandwidth = unitful_property( @@ -134,7 +141,7 @@ def channel(self): u.meter, doc=""" The bandwidth in m. - """ + """, ) span = unitful_property( @@ -142,7 +149,7 @@ def channel(self): u.meter, doc=""" A floating point property that controls the wavelength span in m. - """ + """, ) center_wl = unitful_property( @@ -150,14 +157,14 @@ def channel(self): u.meter, doc=""" A floating point property that controls the center wavelength m. - """ + """, ) points = unitless_property( ":SENS:SWE:POIN", doc=""" An integer property that controls the number of points in a trace. - """ + """, ) sweep_mode = enum_property( @@ -165,16 +172,16 @@ def channel(self): SweepModes, input_decoration=int, doc=""" - A property to control the Sweep Mode as one of Yokogawa6370.SweepMode. - Effective only after a self.start_sweep().""" + A property to control the Sweep Mode as one of Yokogawa6370.SweepMode. + Effective only after a self.start_sweep().""", ) active_trace = enum_property( ":TRAC:ACTIVE", Traces, doc=""" - The active trace of the OSA of enum Yokogawa6370.Traces. Determines the - result of Yokogawa6370.data() and Yokogawa6370.wavelength().""" + The active trace of the OSA of enum Yokogawa6370.Traces. Determines the + result of Yokogawa6370.data() and Yokogawa6370.wavelength().""", ) # METHODS # diff --git a/instruments/yokogawa/yokogawa7651.py b/instruments/yokogawa/yokogawa7651.py index e75ffd6d4..3b28c3e3f 100644 --- a/instruments/yokogawa/yokogawa7651.py +++ b/instruments/yokogawa/yokogawa7651.py @@ -63,15 +63,18 @@ def mode(self): :type: `Yokogawa7651.Mode` """ - raise NotImplementedError('This instrument does not support ' - 'querying the operation mode.') + raise NotImplementedError( + "This instrument does not support " "querying the operation mode." + ) @mode.setter def mode(self, newval): if not isinstance(newval, Yokogawa7651.Mode): - raise TypeError("Mode setting must be a `Yokogawa7651.Mode` " - "value, got {} instead.".format(type(newval))) - self._parent.sendcmd('F{};'.format(newval.value)) + raise TypeError( + "Mode setting must be a `Yokogawa7651.Mode` " + "value, got {} instead.".format(type(newval)) + ) + self._parent.sendcmd("F{};".format(newval.value)) self._parent.trigger() @property @@ -86,14 +89,16 @@ def voltage(self): assumed to be of units Volts. :type: `~pint.Quantity` with units Volt """ - raise NotImplementedError('This instrument does not support ' - 'querying the output voltage setting.') + raise NotImplementedError( + "This instrument does not support " + "querying the output voltage setting." + ) @voltage.setter def voltage(self, newval): newval = assume_units(newval, u.volt).to(u.volt).magnitude self.mode = self._parent.Mode.voltage - self._parent.sendcmd('SA{};'.format(newval)) + self._parent.sendcmd("SA{};".format(newval)) self._parent.trigger() @property @@ -108,14 +113,16 @@ def current(self): assumed to be of units Amps. :type: `~pint.Quantity` with units Amp """ - raise NotImplementedError('This instrument does not support ' - 'querying the output current setting.') + raise NotImplementedError( + "This instrument does not support " + "querying the output current setting." + ) @current.setter def current(self, newval): newval = assume_units(newval, u.amp).to(u.amp).magnitude self.mode = self._parent.Mode.current - self._parent.sendcmd('SA{};'.format(newval)) + self._parent.sendcmd("SA{};".format(newval)) self._parent.trigger() @property @@ -128,16 +135,17 @@ def output(self): :type: `bool` """ - raise NotImplementedError('This instrument does not support ' - 'querying the output status.') + raise NotImplementedError( + "This instrument does not support " "querying the output status." + ) @output.setter def output(self, newval): if newval is True: - self._parent.sendcmd('O1;') + self._parent.sendcmd("O1;") self._parent.trigger() else: - self._parent.sendcmd('O0;') + self._parent.sendcmd("O0;") self._parent.trigger() # ENUMS # @@ -146,6 +154,7 @@ class Mode(IntEnum): """ Enum containing valid output modes for the Yokogawa 7651 """ + voltage = 1 current = 5 @@ -179,8 +188,9 @@ def voltage(self): to be of units Volts. :type: `~pint.Quantity` with units Volt """ - raise NotImplementedError('This instrument does not support querying ' - 'the output voltage setting.') + raise NotImplementedError( + "This instrument does not support querying " "the output voltage setting." + ) @voltage.setter def voltage(self, newval): @@ -197,8 +207,9 @@ def current(self): to be of units Amps. :type: `~pint.Quantity` with units Amp """ - raise NotImplementedError('This instrument does not support querying ' - 'the output current setting.') + raise NotImplementedError( + "This instrument does not support querying " "the output current setting." + ) @current.setter def current(self, newval): @@ -213,4 +224,4 @@ def trigger(self): After changing any parameters of the instrument (for example, output voltage), the device needs to be triggered before it will update. """ - self.sendcmd('E;') + self.sendcmd("E;") diff --git a/matlab/open_instrument.m b/matlab/open_instrument.m index e2309911f..6b83523ea 100644 --- a/matlab/open_instrument.m +++ b/matlab/open_instrument.m @@ -8,23 +8,23 @@ % @classmethods. This presents two drawbacks: first, we need to manage % the Python globals() dict directly, and second, we need to do string % manipulation to make the line of source code to evaluate. - + % To manage globals() ourselves, we need to make a new dict() that we will % pass to py.eval. namespace = py.dict(); - + % Next, py.eval doesn't respect MATLAB's import py.* command-form function. % Thus, we need to use the __import__ built-in function to return the module % object for InstrumentKit. We'll save it directly into our new namespace, % so that it becomes a global for the next py.eval. Recall that d{'x'} on the % MATLAB side corresponds to d['x'] on the Python side, for d a Python dict(). namespace{'ik'} = py.eval('__import__("instruments")', namespace); - + % Finally, we're equipped to run the open_from_uri @classmethod. To do so, % we want to evaluate a line that looks like: % ik.holzworth.Holzworth.HS9000.open_from_uri(r"serial:/dev/ttyUSB0") % We use r to cut down on accidental escaping errors, but importantly, this will % do *nothing* to cut down intentional abuse of eval. instrument = py.eval(['ik.' name '.open_from_uri(r"' uri '")'], namespace); - + end diff --git a/tox.ini b/tox.ini index d9baee855..764e753dc 100644 --- a/tox.ini +++ b/tox.ini @@ -10,9 +10,10 @@ deps = numpy: numpy commands = pytest -n auto --cov=instruments {toxinidir}/instruments/tests/ {posargs:} -[testenv:pylint] +[testenv:precommit] basepython = python3 deps = -rdev-requirements.txt + pre-commit commands = - pylint -j 0 --disable=I,R {toxinidir}/instruments + pre-commit run --all From 89e119fa84b65f25acc77345ca695dceae6cbcea Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 22 Jan 2022 16:33:37 -0500 Subject: [PATCH 100/108] Upgrade source code to Py36+ only (#318) --- .pre-commit-config.yaml | 19 ++++-- doc/examples/ex_generic_scpi.py | 1 - doc/examples/ex_hp3456.py | 9 ++- doc/examples/ex_keithley195.py | 1 - doc/examples/ex_oscilloscope_waveform.py | 1 - doc/examples/ex_topticatopmode.py | 1 - doc/examples/srs_DG645.py | 1 - doc/source/conf.py | 17 +++-- instruments/__init__.py | 1 - instruments/abstract_instruments/__init__.py | 1 - .../abstract_instruments/comm/__init__.py | 1 - .../comm/abstract_comm.py | 1 - .../comm/file_communicator.py | 11 ++-- .../comm/gpib_communicator.py | 15 ++--- .../comm/loopback_communicator.py | 5 +- .../comm/serial_communicator.py | 5 +- .../comm/serial_manager.py | 1 - .../comm/socket_communicator.py | 5 +- .../comm/usb_communicator.py | 7 +- .../comm/usbtmc_communicator.py | 5 +- .../comm/visa_communicator.py | 5 +- .../comm/vxi11_communicator.py | 5 +- .../abstract_instruments/electrometer.py | 1 - .../function_generator.py | 3 +- .../abstract_instruments/instrument.py | 7 +- .../abstract_instruments/multimeter.py | 1 - .../optical_spectrum_analyzer.py | 1 - .../abstract_instruments/oscilloscope.py | 1 - .../abstract_instruments/power_supply.py | 1 - .../signal_generator/__init__.py | 1 - .../signal_generator/channel.py | 1 - .../signal_generator/signal_generator.py | 1 - .../signal_generator/single_channel_sg.py | 1 - instruments/agilent/__init__.py | 1 - instruments/agilent/agilent33220a.py | 3 +- instruments/agilent/agilent34410a.py | 3 +- instruments/config.py | 7 +- instruments/errors.py | 1 - instruments/fluke/__init__.py | 1 - instruments/fluke/fluke3000.py | 3 +- instruments/generic_scpi/__init__.py | 1 - .../generic_scpi/scpi_function_generator.py | 7 +- instruments/generic_scpi/scpi_instrument.py | 5 +- instruments/generic_scpi/scpi_multimeter.py | 11 ++-- instruments/gentec_eo/blu.py | 22 +++---- instruments/glassman/__init__.py | 1 - instruments/glassman/glassmanfr.py | 13 ++-- instruments/holzworth/__init__.py | 1 - instruments/holzworth/holzworth_hs9000.py | 9 ++- instruments/hp/__init__.py | 1 - instruments/hp/hp3456a.py | 17 +++-- instruments/hp/hp6624a.py | 9 ++- instruments/hp/hp6632b.py | 1 - instruments/hp/hp6652a.py | 3 +- instruments/hp/hpe3631a.py | 7 +- instruments/keithley/__init__.py | 1 - instruments/keithley/keithley195.py | 11 ++-- instruments/keithley/keithley2182.py | 11 ++-- instruments/keithley/keithley485.py | 25 ++++--- instruments/keithley/keithley580.py | 27 ++++---- instruments/keithley/keithley6220.py | 1 - instruments/keithley/keithley6514.py | 9 ++- instruments/lakeshore/__init__.py | 1 - instruments/lakeshore/lakeshore340.py | 3 +- instruments/lakeshore/lakeshore370.py | 5 +- instruments/lakeshore/lakeshore475.py | 7 +- instruments/minghe/__init__.py | 1 - instruments/minghe/mhs5200a.py | 31 +++++---- instruments/named_struct.py | 31 ++++----- instruments/newport/__init__.py | 1 - instruments/newport/agilis.py | 37 +++++------ instruments/newport/errors.py | 20 +++--- instruments/newport/newport_pmc8742.py | 5 +- instruments/newport/newportesp301.py | 7 +- instruments/ondax/__init__.py | 1 - instruments/ondax/lm.py | 3 +- instruments/optional_dep_finder.py | 2 - instruments/oxford/__init__.py | 1 - instruments/oxford/oxforditc503.py | 5 +- instruments/phasematrix/__init__.py | 1 - .../phasematrix/phasematrix_fsw0020.py | 15 ++--- instruments/picowatt/__init__.py | 1 - instruments/picowatt/picowattavs47.py | 3 +- instruments/qubitekk/__init__.py | 1 - instruments/qubitekk/cc1.py | 11 ++-- instruments/qubitekk/mc1.py | 3 +- instruments/rigol/__init__.py | 1 - instruments/rigol/rigolds1000.py | 11 ++-- instruments/srs/__init__.py | 1 - instruments/srs/srs345.py | 5 +- instruments/srs/srs830.py | 21 +++--- instruments/srs/srsctc100.py | 31 +++++---- instruments/srs/srsdg645.py | 41 +++++------- instruments/tektronix/__init__.py | 1 - instruments/tektronix/tekawg2000.py | 31 ++++----- instruments/tektronix/tekdpo4104.py | 31 ++++----- instruments/tektronix/tekdpo70000.py | 21 +++--- instruments/tektronix/tektds224.py | 7 +- instruments/tektronix/tektds5xx.py | 65 +++++++++---------- instruments/teledyne/__init__.py | 1 - instruments/teledyne/maui.py | 63 +++++++++--------- instruments/tests/__init__.py | 1 - .../test_abstract_inst/test_electrometer.py | 1 - .../test_function_generator.py | 1 - .../test_abstract_inst/test_multimeter.py | 1 - .../test_optical_spectrum_analyzer.py | 1 - .../test_abstract_inst/test_oscilloscope.py | 1 - .../test_abstract_inst/test_power_supply.py | 1 - .../test_signal_generator/test_channel.py | 1 - .../test_signal_generator.py | 1 - .../test_single_channel_sg.py | 1 - .../tests/test_agilent/test_agilent_33220a.py | 1 - .../tests/test_agilent/test_agilent_34410a.py | 1 - instruments/tests/test_base_instrument.py | 1 - instruments/tests/test_comm/test_file.py | 1 - instruments/tests/test_comm/test_gpibusb.py | 1 - instruments/tests/test_comm/test_loopback.py | 7 +- instruments/tests/test_comm/test_serial.py | 1 - instruments/tests/test_comm/test_socket.py | 7 +- .../tests/test_comm/test_usb_communicator.py | 1 - instruments/tests/test_comm/test_usbtmc.py | 1 - .../tests/test_comm/test_visa_communicator.py | 1 - instruments/tests/test_comm/test_vxi11.py | 1 - instruments/tests/test_config.py | 9 ++- .../tests/test_fluke/test_fluke3000.py | 1 - .../test_scpi_function_generator.py | 1 - .../test_generic_scpi/test_scpi_instrument.py | 1 - .../test_generic_scpi/test_scpi_multimeter.py | 1 - instruments/tests/test_gentec_eo/test_blu.py | 1 - .../tests/test_glassman/test_glassmanfr.py | 1 - .../test_holzworth/test_holzworth_hs9000.py | 7 +- instruments/tests/test_hp/test_hp3456a.py | 1 - instruments/tests/test_hp/test_hp6624a.py | 37 +++++------ instruments/tests/test_hp/test_hp6632b.py | 23 +++---- instruments/tests/test_hp/test_hp6652a.py | 1 - instruments/tests/test_hp/test_hpe3631a.py | 1 - .../tests/test_keithley/test_keithley195.py | 1 - .../tests/test_keithley/test_keithley2182.py | 1 - .../tests/test_keithley/test_keithley485.py | 1 - .../tests/test_keithley/test_keithley580.py | 1 - .../tests/test_keithley/test_keithley6220.py | 3 +- .../tests/test_keithley/test_keithley6514.py | 3 +- .../tests/test_lakeshore/test_lakeshore340.py | 1 - .../tests/test_lakeshore/test_lakeshore370.py | 1 - .../tests/test_lakeshore/test_lakeshore475.py | 1 - .../tests/test_minghe/test_minghe_mhs5200a.py | 1 - instruments/tests/test_named_struct.py | 9 ++- instruments/tests/test_newport/test_agilis.py | 1 - instruments/tests/test_newport/test_errors.py | 1 - .../test_newport/test_newport_pmc8742.py | 1 - .../tests/test_newport/test_newportesp301.py | 1 - instruments/tests/test_ondax/test_lm.py | 1 - .../tests/test_oxford/test_oxforditc503.py | 1 - .../test_phasematrix_fsw0020.py | 15 ++--- .../test_picowatt/test_picowatt_avs47.py | 1 - .../tests/test_property_factories/__init__.py | 3 +- .../test_bool_property.py | 1 - .../test_bounded_unitful_property.py | 1 - .../test_enum_property.py | 1 - .../test_int_property.py | 9 ++- .../test_property_factories/test_rproperty.py | 11 ++-- .../test_string_property.py | 1 - .../test_unitful_property.py | 17 +++-- .../test_unitless_property.py | 9 ++- .../tests/test_qubitekk/test_qubitekk_cc1.py | 5 +- .../tests/test_qubitekk/test_qubitekk_mc1.py | 1 - .../tests/test_rigol/test_rigolds1000.py | 1 - instruments/tests/test_split_str.py | 1 - instruments/tests/test_srs/test_srs345.py | 7 +- instruments/tests/test_srs/test_srs830.py | 9 ++- instruments/tests/test_srs/test_srsctc100.py | 7 +- instruments/tests/test_srs/test_srsdg645.py | 1 - .../tests/test_tektronix/test_tekawg2000.py | 3 +- .../tests/test_tektronix/test_tekdpo4104.py | 9 ++- .../tests/test_tektronix/test_tekdpo70000.py | 1 - .../test_tektronix/test_tektronix_tds224.py | 5 +- .../tests/test_tektronix/test_tktds5xx.py | 5 +- instruments/tests/test_teledyne/test_maui.py | 3 +- instruments/tests/test_test_utils.py | 2 - .../tests/test_thorlabs/test_abstract.py | 1 - .../tests/test_thorlabs/test_packets.py | 13 ++-- .../tests/test_thorlabs/test_thorlabs_apt.py | 31 +++++---- .../test_thorlabs/test_thorlabs_lcc25.py | 1 - .../test_thorlabs/test_thorlabs_pm100usb.py | 1 - .../tests/test_thorlabs/test_thorlabs_sc10.py | 1 - .../test_thorlabs/test_thorlabs_tc200.py | 1 - instruments/tests/test_thorlabs/test_utils.py | 1 - .../test_toptica/test_toptica_topmode.py | 1 - .../tests/test_toptica/test_toptica_utils.py | 1 - instruments/tests/test_util_fns.py | 1 - .../tests/test_yokogawa/test_yokogawa7651.py | 1 - .../tests/test_yokogawa/test_yokogawa_6370.py | 17 +++-- instruments/thorlabs/__init__.py | 1 - instruments/thorlabs/_abstract.py | 7 +- instruments/thorlabs/_cmds.py | 1 - instruments/thorlabs/_packets.py | 7 +- instruments/thorlabs/lcc25.py | 7 +- instruments/thorlabs/pm100usb.py | 3 +- instruments/thorlabs/sc10.py | 5 +- instruments/thorlabs/tc200.py | 15 ++--- instruments/thorlabs/thorlabs_utils.py | 1 - instruments/thorlabs/thorlabsapt.py | 13 ++-- instruments/toptica/__init__.py | 1 - instruments/toptica/topmode.py | 13 ++-- instruments/toptica/toptica_utils.py | 1 - instruments/units.py | 1 - instruments/util_fns.py | 11 ++-- instruments/yokogawa/__init__.py | 1 - instruments/yokogawa/yokogawa6370.py | 7 +- instruments/yokogawa/yokogawa7651.py | 7 +- 210 files changed, 549 insertions(+), 777 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 45a805c27..153c095d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,13 +2,18 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: - - id: no-commit-to-branch - args: [--branch, master] - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: debug-statements + - id: no-commit-to-branch + args: [--branch, master] + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements - repo: https://github.com/psf/black rev: 21.12b0 hooks: - - id: black + - id: black + - repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [ --py36-plus ] diff --git a/doc/examples/ex_generic_scpi.py b/doc/examples/ex_generic_scpi.py index e5abe1af4..8190df806 100644 --- a/doc/examples/ex_generic_scpi.py +++ b/doc/examples/ex_generic_scpi.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 3.0 # diff --git a/doc/examples/ex_hp3456.py b/doc/examples/ex_hp3456.py index 17e784528..441b3289d 100644 --- a/doc/examples/ex_hp3456.py +++ b/doc/examples/ex_hp3456.py @@ -1,5 +1,4 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- import logging import time @@ -28,10 +27,10 @@ # Read registers dmm.nplc = 10 -print("n = {}".format(dmm.number_of_readings)) -print("g = {}".format(dmm.number_of_digits)) -print("p = {}".format(dmm.nplc)) -print("d = {}".format(dmm.delay)) +print(f"n = {dmm.number_of_readings}") +print(f"g = {dmm.number_of_digits}") +print(f"p = {dmm.nplc}") +print(f"d = {dmm.delay}") print(dmm.mean) print(dmm.variance) print(dmm.count) diff --git a/doc/examples/ex_keithley195.py b/doc/examples/ex_keithley195.py index de66f9908..a9d4027a6 100644 --- a/doc/examples/ex_keithley195.py +++ b/doc/examples/ex_keithley195.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 3.0 # diff --git a/doc/examples/ex_oscilloscope_waveform.py b/doc/examples/ex_oscilloscope_waveform.py index 871e7a005..5b4588260 100644 --- a/doc/examples/ex_oscilloscope_waveform.py +++ b/doc/examples/ex_oscilloscope_waveform.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 3.0 # diff --git a/doc/examples/ex_topticatopmode.py b/doc/examples/ex_topticatopmode.py index 6c8410db4..5cbc5d053 100644 --- a/doc/examples/ex_topticatopmode.py +++ b/doc/examples/ex_topticatopmode.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Toptica Topmode example """ diff --git a/doc/examples/srs_DG645.py b/doc/examples/srs_DG645.py index efacbba0f..11587a48b 100644 --- a/doc/examples/srs_DG645.py +++ b/doc/examples/srs_DG645.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 3.0 # diff --git a/doc/source/conf.py b/doc/source/conf.py index 616e90b70..224e2afe1 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # InstrumentKit Library documentation build configuration file, created by # sphinx-quickstart on Fri Apr 5 10:37:03 2013. @@ -48,8 +47,8 @@ master_doc = "index" # General information about the project. -project = u"InstrumentKit Library" -copyright = u"2013-2020, Steven Casagrande" +project = "InstrumentKit Library" +copyright = "2013-2020, Steven Casagrande" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -192,8 +191,8 @@ ( "index", "InstrumentKitLibrary.tex", - u"InstrumentKit Library Documentation", - u"Steven Casagrande", + "InstrumentKit Library Documentation", + "Steven Casagrande", "manual", ), ] @@ -227,8 +226,8 @@ ( "index", "instrumentkitlibrary", - u"InstrumentKit Library Documentation", - [u"Steven Casagrande"], + "InstrumentKit Library Documentation", + ["Steven Casagrande"], 1, ) ] @@ -246,8 +245,8 @@ ( "index", "InstrumentKitLibrary", - u"InstrumentKit Library Documentation", - u"Steven Casagrande", + "InstrumentKit Library Documentation", + "Steven Casagrande", "InstrumentKitLibrary", "One line description of project.", "Miscellaneous", diff --git a/instruments/__init__.py b/instruments/__init__.py index a7cd417b4..b790f3bce 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines globally-available subpackages and symbols for the instruments package. """ diff --git a/instruments/abstract_instruments/__init__.py b/instruments/abstract_instruments/__init__.py index 5390ce15e..d97ccb4a3 100644 --- a/instruments/abstract_instruments/__init__.py +++ b/instruments/abstract_instruments/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing instrument abstract base classes and communication layers """ diff --git a/instruments/abstract_instruments/comm/__init__.py b/instruments/abstract_instruments/comm/__init__.py index 342f1ec6f..a3a4b1a60 100644 --- a/instruments/abstract_instruments/comm/__init__.py +++ b/instruments/abstract_instruments/comm/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing communication layers """ diff --git a/instruments/abstract_instruments/comm/abstract_comm.py b/instruments/abstract_instruments/comm/abstract_comm.py index ede919828..8abd6cd13 100644 --- a/instruments/abstract_instruments/comm/abstract_comm.py +++ b/instruments/abstract_instruments/comm/abstract_comm.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for file-like communication layer classes """ diff --git a/instruments/abstract_instruments/comm/file_communicator.py b/instruments/abstract_instruments/comm/file_communicator.py index 27c425e6e..b3084489a 100644 --- a/instruments/abstract_instruments/comm/file_communicator.py +++ b/instruments/abstract_instruments/comm/file_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a communication layer for an instrument with a file on the filesystem """ @@ -35,7 +34,7 @@ class FileCommunicator(io.IOBase, AbstractCommunicator): """ def __init__(self, filelike): - super(FileCommunicator, self).__init__(self) + super().__init__(self) if isinstance(filelike, str): # pragma: no cover filelike = open(filelike, "rb+") @@ -104,7 +103,7 @@ def close(self): """ try: self._filelike.close() - except IOError as e: # pragma: no cover + except OSError as e: # pragma: no cover logger.warning("Failed to close file, exception: %s", repr(e)) def read_raw(self, size=-1): @@ -178,7 +177,7 @@ def _sendcmd(self, msg): self.write(msg) try: self.flush() - except IOError as e: + except OSError as e: logger.warning("Exception %s occured during flush().", repr(e)) def _query(self, msg, size=-1): @@ -209,7 +208,7 @@ def _query(self, msg, size=-1): if nextchar.endswith(self._terminator.encode("utf-8")): resp = resp[: -len(self._terminator)] break - except IOError as ex: + except OSError as ex: if ex.errno == errno.ETIMEDOUT: # We don't mind timeouts if resp is nonempty, # and will just return what we have. @@ -218,7 +217,7 @@ def _query(self, msg, size=-1): elif ex.errno != errno.EPIPE: raise # Reraise the existing exception. else: # Give a more helpful and specific exception. - raise IOError( + raise OSError( "Pipe broken when reading from {}; this probably " "indicates that the driver " "providing the device file is unable to communicate with " diff --git a/instruments/abstract_instruments/comm/gpib_communicator.py b/instruments/abstract_instruments/comm/gpib_communicator.py index 73d96a2d0..63bf382a7 100644 --- a/instruments/abstract_instruments/comm/gpib_communicator.py +++ b/instruments/abstract_instruments/comm/gpib_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a communication layer for an instrument connected via a Galvant Industries or Prologix GPIB adapter. @@ -32,7 +31,7 @@ class GPIBCommunicator(io.IOBase, AbstractCommunicator): # pylint: disable=too-many-instance-attributes def __init__(self, filelike, gpib_address, model="gi"): - super(GPIBCommunicator, self).__init__(self) + super().__init__(self) self._model = self.Model(model) self._file = filelike self._gpib_address = gpib_address @@ -109,10 +108,10 @@ def timeout(self, newval): newval = assume_units(newval, u.second) if self._model == GPIBCommunicator.Model.gi and self._version <= 4: newval = newval.to(u.second) - self._file.sendcmd("+t:{}".format(int(newval.magnitude))) + self._file.sendcmd(f"+t:{int(newval.magnitude)}") else: newval = newval.to(u.millisecond) - self._file.sendcmd("++read_tmo_ms {}".format(int(newval.magnitude))) + self._file.sendcmd(f"++read_tmo_ms {int(newval.magnitude)}") self._file.timeout = newval.to(u.second) self._timeout = newval.to(u.second) @@ -222,7 +221,7 @@ def eos(self, newval): if self._model == GPIBCommunicator.Model.gi and self._version <= 4: if isinstance(newval, (str, bytes)): newval = ord(newval) - self._file.sendcmd("+eos:{}".format(newval)) + self._file.sendcmd(f"+eos:{newval}") self._eos = newval else: if isinstance(newval, int): @@ -241,7 +240,7 @@ def eos(self, newval): newval = 3 else: raise ValueError("EOS must be CRLF, CR, LF, or None") - self._file.sendcmd("++eos {}".format(newval)) + self._file.sendcmd(f"++eos {newval}") # FILE-LIKE METHODS # @@ -326,9 +325,9 @@ def _sendcmd(self, msg): if msg == "": return if self._model == GPIBCommunicator.Model.gi: - self._file.sendcmd("+a:{0}".format(str(self._gpib_address))) + self._file.sendcmd(f"+a:{str(self._gpib_address)}") else: - self._file.sendcmd("++addr {0}".format(str(self._gpib_address))) + self._file.sendcmd(f"++addr {str(self._gpib_address)}") time.sleep(sleep_time) self.eoi = self.eoi time.sleep(sleep_time) diff --git a/instruments/abstract_instruments/comm/loopback_communicator.py b/instruments/abstract_instruments/comm/loopback_communicator.py index 339c439da..5687dca9a 100644 --- a/instruments/abstract_instruments/comm/loopback_communicator.py +++ b/instruments/abstract_instruments/comm/loopback_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a loopback communicator, used for creating unit tests or for opening test connections to explore the InstrumentKit API. @@ -26,7 +25,7 @@ class LoopbackCommunicator(io.IOBase, AbstractCommunicator): """ def __init__(self, stdin=None, stdout=None): - super(LoopbackCommunicator, self).__init__(self) + super().__init__(self) self._terminator = "\n" self._stdout = stdout self._stdin = stdin @@ -90,7 +89,7 @@ def close(self): """ try: self._stdin.close() - except IOError: + except OSError: pass def read_raw(self, size=-1): diff --git a/instruments/abstract_instruments/comm/serial_communicator.py b/instruments/abstract_instruments/comm/serial_communicator.py index 1d1b28aa0..459d6bd00 100644 --- a/instruments/abstract_instruments/comm/serial_communicator.py +++ b/instruments/abstract_instruments/comm/serial_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a serial communicator for connecting with instruments over serial connections. @@ -27,7 +26,7 @@ class SerialCommunicator(io.IOBase, AbstractCommunicator): """ def __init__(self, conn): - super(SerialCommunicator, self).__init__(self) + super().__init__(self) if isinstance(conn, serial.Serial): self._conn = conn @@ -123,7 +122,7 @@ def read_raw(self, size=-1): while not (result.endswith(term) if term is not None else c == b""): c = self._conn.read(1) if c == b"" and term is not None: - raise IOError( + raise OSError( "Serial connection timed out before reading " "a termination character." ) diff --git a/instruments/abstract_instruments/comm/serial_manager.py b/instruments/abstract_instruments/comm/serial_manager.py index 33c23f59a..dcc6bec56 100644 --- a/instruments/abstract_instruments/comm/serial_manager.py +++ b/instruments/abstract_instruments/comm/serial_manager.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ This module handles creating the serial objects for the instrument classes. diff --git a/instruments/abstract_instruments/comm/socket_communicator.py b/instruments/abstract_instruments/comm/socket_communicator.py index 3356d027c..0972e09d6 100644 --- a/instruments/abstract_instruments/comm/socket_communicator.py +++ b/instruments/abstract_instruments/comm/socket_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a tcpip socket communicator for connecting with instruments over raw ethernet connections. @@ -29,7 +28,7 @@ class SocketCommunicator(io.IOBase, AbstractCommunicator): """ def __init__(self, conn): - super(SocketCommunicator, self).__init__(self) + super().__init__(self) if isinstance(conn, socket.socket): self._conn = conn @@ -111,7 +110,7 @@ def read_raw(self, size=-1): while result.endswith(self._terminator.encode("utf-8")) is False: c = self._conn.recv(1) if c == b"": - raise IOError( + raise OSError( "Socket connection timed out before reading " "a termination character." ) diff --git a/instruments/abstract_instruments/comm/usb_communicator.py b/instruments/abstract_instruments/comm/usb_communicator.py index b0cc1096e..643aa41d4 100644 --- a/instruments/abstract_instruments/comm/usb_communicator.py +++ b/instruments/abstract_instruments/comm/usb_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a USB communicator for connecting with instruments over raw usb connections. @@ -33,7 +32,7 @@ class USBCommunicator(io.IOBase, AbstractCommunicator): """ def __init__(self, dev): - super(USBCommunicator, self).__init__(self) + super().__init__(self) if not isinstance(dev, usb.core.Device): raise TypeError("USBCommunicator must wrap a usb.core.Device object.") @@ -63,7 +62,7 @@ def __init__(self, dev): ) if (ep_in or ep_out) is None: - raise IOError("USB endpoint not found.") + raise OSError("USB endpoint not found.") # read the maximum package size from the ENDPOINT_IN self._max_packet_size = ep_in.wMaxPacketSize @@ -140,7 +139,7 @@ def read_raw(self, size=-1): term = self._terminator.encode("utf-8") read_val = bytes(self._ep_in.read(size)) if term not in read_val: - raise IOError( + raise OSError( f"Did not find the terminator in the returned string. " f"Total size of {size} might not be enough." ) diff --git a/instruments/abstract_instruments/comm/usbtmc_communicator.py b/instruments/abstract_instruments/comm/usbtmc_communicator.py index 23039ea6b..b4f996edd 100644 --- a/instruments/abstract_instruments/comm/usbtmc_communicator.py +++ b/instruments/abstract_instruments/comm/usbtmc_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a communicator that uses Python-USBTMC for connecting with TMC instruments. @@ -28,7 +27,7 @@ class USBTMCCommunicator(io.IOBase, AbstractCommunicator): def __init__(self, *args, **kwargs): if usbtmc is None: raise ImportError("usbtmc is required for TMC instruments.") - super(USBTMCCommunicator, self).__init__(self) + super().__init__(self) self._filelike = usbtmc.Instrument(*args, **kwargs) self._terminator = "\n" @@ -87,7 +86,7 @@ def close(self): """ try: self._filelike.close() - except IOError: + except OSError: pass def read(self, size=-1, encoding="utf-8"): diff --git a/instruments/abstract_instruments/comm/visa_communicator.py b/instruments/abstract_instruments/comm/visa_communicator.py index 3f89b82be..d9d825ed9 100644 --- a/instruments/abstract_instruments/comm/visa_communicator.py +++ b/instruments/abstract_instruments/comm/visa_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a VISA communicator for connecting with instruments via the VISA library. @@ -27,7 +26,7 @@ class VisaCommunicator(io.IOBase, AbstractCommunicator): """ def __init__(self, conn): - super(VisaCommunicator, self).__init__(self) + super().__init__(self) version = int(pyvisa.__version__.replace(".", "").ljust(3, "0")) # pylint: disable=no-member @@ -99,7 +98,7 @@ def close(self): """ try: self._conn.close() - except IOError: + except OSError: pass def read_raw(self, size=-1): diff --git a/instruments/abstract_instruments/comm/vxi11_communicator.py b/instruments/abstract_instruments/comm/vxi11_communicator.py index 6cd093140..1eb795c98 100644 --- a/instruments/abstract_instruments/comm/vxi11_communicator.py +++ b/instruments/abstract_instruments/comm/vxi11_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides a communication layer that uses python-vxi11 to interface with VXI11 devices. @@ -40,7 +39,7 @@ class VXI11Communicator(io.IOBase, AbstractCommunicator): """ def __init__(self, *args, **kwargs): - super(VXI11Communicator, self).__init__(self) + super().__init__(self) if vxi11 is None: raise ImportError( "Package python-vxi11 is required for XVI11 " "connected instruments." @@ -111,7 +110,7 @@ def close(self): """ try: self._inst.close() - except IOError: + except OSError: pass def read_raw(self, size=-1): diff --git a/instruments/abstract_instruments/electrometer.py b/instruments/abstract_instruments/electrometer.py index 8eaaa5e54..4f9e7fb78 100644 --- a/instruments/abstract_instruments/electrometer.py +++ b/instruments/abstract_instruments/electrometer.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for electrometer instruments """ diff --git a/instruments/abstract_instruments/function_generator.py b/instruments/abstract_instruments/function_generator.py index 54d400daa..3e5d97ab8 100644 --- a/instruments/abstract_instruments/function_generator.py +++ b/instruments/abstract_instruments/function_generator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for function generator instruments """ @@ -29,7 +28,7 @@ class FunctionGenerator(Instrument, metaclass=abc.ABCMeta): """ def __init__(self, filelike): - super(FunctionGenerator, self).__init__(filelike) + super().__init__(filelike) self._channel_count = 1 # pylint:disable=protected-access diff --git a/instruments/abstract_instruments/instrument.py b/instruments/abstract_instruments/instrument.py index 2966c165a..819165a4f 100644 --- a/instruments/abstract_instruments/instrument.py +++ b/instruments/abstract_instruments/instrument.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the base Instrument class for all instruments. """ @@ -276,7 +275,7 @@ def binblockread(self, data_width, fmt=None): # This needs to be a # symbol for valid binary block symbol = self._file.read_raw(1) if symbol != b"#": # Check to make sure block is valid - raise IOError( + raise OSError( "Not a valid binary block start. Binary blocks " "require the first character to be #, instead got " "{}".format(symbol) @@ -304,7 +303,7 @@ def binblockread(self, data_width, fmt=None): if old_len == len(data): tries -= 1 if tries == 0: - raise IOError( + raise OSError( "Did not read in the required number of bytes" "during binblock read. Got {}, expected " "{}".format(len(data), num_of_bytes) @@ -701,7 +700,7 @@ def open_usb(cls, vid, pid): """ dev = usb.core.find(idVendor=vid, idProduct=pid) if dev is None: - raise IOError("No such device found.") + raise OSError("No such device found.") return cls(USBCommunicator(dev)) diff --git a/instruments/abstract_instruments/multimeter.py b/instruments/abstract_instruments/multimeter.py index b46f67d5b..2b01748d3 100644 --- a/instruments/abstract_instruments/multimeter.py +++ b/instruments/abstract_instruments/multimeter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for multimeter instruments """ diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py index 7d8a371cd..37ea21704 100644 --- a/instruments/abstract_instruments/optical_spectrum_analyzer.py +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for optical spectrum analyzer instruments """ diff --git a/instruments/abstract_instruments/oscilloscope.py b/instruments/abstract_instruments/oscilloscope.py index 76fb62788..d75ecfaf0 100644 --- a/instruments/abstract_instruments/oscilloscope.py +++ b/instruments/abstract_instruments/oscilloscope.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for oscilloscope instruments """ diff --git a/instruments/abstract_instruments/power_supply.py b/instruments/abstract_instruments/power_supply.py index 8c7af11e7..f604e7cfb 100644 --- a/instruments/abstract_instruments/power_supply.py +++ b/instruments/abstract_instruments/power_supply.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for power supply instruments """ diff --git a/instruments/abstract_instruments/signal_generator/__init__.py b/instruments/abstract_instruments/signal_generator/__init__.py index 2debc44fd..b8181de07 100644 --- a/instruments/abstract_instruments/signal_generator/__init__.py +++ b/instruments/abstract_instruments/signal_generator/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing signal generator abstract base classes """ diff --git a/instruments/abstract_instruments/signal_generator/channel.py b/instruments/abstract_instruments/signal_generator/channel.py index a4838d63b..cc0ed626e 100644 --- a/instruments/abstract_instruments/signal_generator/channel.py +++ b/instruments/abstract_instruments/signal_generator/channel.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for signal generator output channels """ diff --git a/instruments/abstract_instruments/signal_generator/signal_generator.py b/instruments/abstract_instruments/signal_generator/signal_generator.py index 29ce632e5..8665d187d 100644 --- a/instruments/abstract_instruments/signal_generator/signal_generator.py +++ b/instruments/abstract_instruments/signal_generator/signal_generator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for signal generator instruments """ diff --git a/instruments/abstract_instruments/signal_generator/single_channel_sg.py b/instruments/abstract_instruments/signal_generator/single_channel_sg.py index 0c9588d53..e6dad4880 100644 --- a/instruments/abstract_instruments/signal_generator/single_channel_sg.py +++ b/instruments/abstract_instruments/signal_generator/single_channel_sg.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides an abstract base class for signal generators with only a single output channel. diff --git a/instruments/agilent/__init__.py b/instruments/agilent/__init__.py index dee4156ff..741856d1b 100644 --- a/instruments/agilent/__init__.py +++ b/instruments/agilent/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Agilent instruments """ diff --git a/instruments/agilent/agilent33220a.py b/instruments/agilent/agilent33220a.py index d46a14f53..8bb4451d6 100644 --- a/instruments/agilent/agilent33220a.py +++ b/instruments/agilent/agilent33220a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Agilent 33220a function generator. """ @@ -181,7 +180,7 @@ def load_resistance(self, newval): newval = assume_units(newval, u.ohm).to(u.ohm).magnitude if (newval < 0) or (newval > 10000): raise ValueError("Load resistance must be between 0 and 10,000") - self.sendcmd("OUTP:LOAD {}".format(newval)) + self.sendcmd(f"OUTP:LOAD {newval}") @property def phase(self): diff --git a/instruments/agilent/agilent34410a.py b/instruments/agilent/agilent34410a.py index e06b313ec..2f0925c60 100644 --- a/instruments/agilent/agilent34410a.py +++ b/instruments/agilent/agilent34410a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Agilent 34410a digital multimeter. """ @@ -141,7 +140,7 @@ def read_data(self, sample_count): sample_count = self.data_point_count units = UNITS[self.mode] self.sendcmd("FORM:DATA ASC") - data = self.query("DATA:REM? {}".format(sample_count)).split(",") + data = self.query(f"DATA:REM? {sample_count}").split(",") data = list(map(float, data)) if numpy: return data * units diff --git a/instruments/config.py b/instruments/config.py index 048e1eeb1..094957e12 100644 --- a/instruments/config.py +++ b/instruments/config.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing support for loading instruments from configuration files. """ @@ -68,7 +67,7 @@ def quantity_constructor(loader, node): # We avoid having to register !Q every time by doing as soon as the # relevant constructor is defined. -yaml.add_constructor(u"!Q", quantity_constructor) +yaml.add_constructor("!Q", quantity_constructor) def load_instruments(conf_file_name, conf_path="/"): @@ -146,7 +145,7 @@ def load_instruments(conf_file_name, conf_path="/"): ) if isinstance(conf_file_name, str): - with open(conf_file_name, "r") as f: + with open(conf_file_name) as f: conf_dict = yaml.load(f, Loader=yaml.Loader) else: conf_dict = yaml.load(conf_file_name, Loader=yaml.Loader) @@ -163,7 +162,7 @@ def load_instruments(conf_file_name, conf_path="/"): for attr_name, attr_value in value["attrs"].items(): setattr_expression(inst_dict[name], attr_name, attr_value) - except IOError as ex: + except OSError as ex: # FIXME: need to subclass Warning so that repeated warnings # aren't ignored. warnings.warn( diff --git a/instruments/errors.py b/instruments/errors.py index 769dc1dec..5ae4a0e25 100644 --- a/instruments/errors.py +++ b/instruments/errors.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing custom exception errors used by various instruments. """ diff --git a/instruments/fluke/__init__.py b/instruments/fluke/__init__.py index 2d0e41fb8..20649d602 100644 --- a/instruments/fluke/__init__.py +++ b/instruments/fluke/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Fluke instruments """ diff --git a/instruments/fluke/fluke3000.py b/instruments/fluke/fluke3000.py index 5d7a1da25..3b275ba7c 100644 --- a/instruments/fluke/fluke3000.py +++ b/instruments/fluke/fluke3000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # fluke3000.py: Driver for the Fluke 3000 FC Industrial System # @@ -99,7 +98,7 @@ def __init__(self, filelike): """ Initialize the instrument, and set the properties needed for communication. """ - super(Fluke3000, self).__init__(filelike) + super().__init__(filelike) self.timeout = 3 * u.second self.terminator = "\r" self.positions = {} diff --git a/instruments/generic_scpi/__init__.py b/instruments/generic_scpi/__init__.py index 3dca40432..39e4897c7 100644 --- a/instruments/generic_scpi/__init__.py +++ b/instruments/generic_scpi/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing generic SCPI instruments """ diff --git a/instruments/generic_scpi/scpi_function_generator.py b/instruments/generic_scpi/scpi_function_generator.py index fd6020164..31bc5c21b 100644 --- a/instruments/generic_scpi/scpi_function_generator.py +++ b/instruments/generic_scpi/scpi_function_generator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for SCPI compliant function generators """ @@ -38,7 +37,7 @@ class SCPIFunctionGenerator(FunctionGenerator, SCPIInstrument): FunctionGenerator.VoltageMode.dBm: "DBM", } - _MNEMONIC_UNITS = dict((mnem, unit) for unit, mnem in _UNIT_MNEMONICS.items()) + _MNEMONIC_UNITS = {mnem: unit for unit, mnem in _UNIT_MNEMONICS.items()} # FunctionGenerator CONTRACT # @@ -63,8 +62,8 @@ def _set_amplitude_(self, magnitude, units): :param units: The type of voltage measurements units :type units: `FunctionGenerator.VoltageMode` """ - self.sendcmd("VOLT:UNIT {}".format(self._UNIT_MNEMONICS[units])) - self.sendcmd("VOLT {}".format(magnitude)) + self.sendcmd(f"VOLT:UNIT {self._UNIT_MNEMONICS[units]}") + self.sendcmd(f"VOLT {magnitude}") # PROPERTIES # diff --git a/instruments/generic_scpi/scpi_instrument.py b/instruments/generic_scpi/scpi_instrument.py index 26377adcc..e5395addf 100644 --- a/instruments/generic_scpi/scpi_instrument.py +++ b/instruments/generic_scpi/scpi_instrument.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for SCPI compliant instruments """ @@ -269,7 +268,7 @@ def display_brightness(self): def display_brightness(self, newval): if newval < 0 or newval > 1: raise ValueError("Display brightness must be a number between 0" " and 1.") - self.sendcmd("DISP:BRIG {}".format(newval)) + self.sendcmd(f"DISP:BRIG {newval}") @property def display_contrast(self): @@ -285,4 +284,4 @@ def display_contrast(self): def display_contrast(self, newval): if newval < 0 or newval > 1: raise ValueError("Display contrast must be a number between 0" " and 1.") - self.sendcmd("DISP:CONT {}".format(newval)) + self.sendcmd(f"DISP:CONT {newval}") diff --git a/instruments/generic_scpi/scpi_multimeter.py b/instruments/generic_scpi/scpi_multimeter.py index 36b3a1cfa..1899cc490 100644 --- a/instruments/generic_scpi/scpi_multimeter.py +++ b/instruments/generic_scpi/scpi_multimeter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for SCPI compliant multimeters """ @@ -206,7 +205,7 @@ def input_range(self, newval): newval = newval.value else: newval = assume_units(newval, units).to(units).magnitude - self.sendcmd("CONF:{} {}".format(mode.value, newval)) + self.sendcmd(f"CONF:{mode.value} {newval}") @property def resolution(self): @@ -241,7 +240,7 @@ def resolution(self, newval): "Resolution must be specified as an int, float, " "or SCPIMultimeter.Resolution value." ) - self.sendcmd("CONF:{} {},{}".format(mode.value, input_range, newval)) + self.sendcmd(f"CONF:{mode.value} {input_range},{newval}") @property def trigger_count(self): @@ -283,7 +282,7 @@ def trigger_count(self, newval): "Trigger count must be specified as an int " "or SCPIMultimeter.TriggerCount value." ) - self.sendcmd("TRIG:COUN {}".format(newval)) + self.sendcmd(f"TRIG:COUN {newval}") @property def sample_count(self): @@ -326,7 +325,7 @@ def sample_count(self, newval): "Sample count must be specified as an int " "or SCPIMultimeter.SampleCount value." ) - self.sendcmd("SAMP:COUN {}".format(newval)) + self.sendcmd(f"SAMP:COUN {newval}") trigger_delay = unitful_property( command="TRIG:DEL", @@ -405,7 +404,7 @@ def measure(self, mode=None): "value, got {} instead.".format(type(mode)) ) # pylint: disable=no-member - value = float(self.query("MEAS:{}?".format(mode.value))) + value = float(self.query(f"MEAS:{mode.value}?")) return value * UNITS[mode] # INTERNAL FUNCTIONS ## diff --git a/instruments/gentec_eo/blu.py b/instruments/gentec_eo/blu.py index 252282d84..bae1b18a8 100644 --- a/instruments/gentec_eo/blu.py +++ b/instruments/gentec_eo/blu.py @@ -40,7 +40,7 @@ class Blu(Instrument): """ def __init__(self, filelike): - super(Blu, self).__init__(filelike) + super().__init__(filelike) # use a terminator for blu, even though none required self.terminator = "\r\n" @@ -138,7 +138,7 @@ def anticipation(self): @anticipation.setter def anticipation(self, newval): sendval = 1 if newval else 0 - self.sendcmd("*ANT{}".format(sendval)) + self.sendcmd(f"*ANT{sendval}") @property def auto_scale(self): @@ -163,7 +163,7 @@ def auto_scale(self): @auto_scale.setter def auto_scale(self, newval): sendval = 1 if newval else 0 - self.sendcmd("*SAS{}".format(sendval)) + self.sendcmd(f"*SAS{sendval}") @property def available_scales(self): @@ -329,7 +329,7 @@ def scale(self): @scale.setter def scale(self, newval): - self.sendcmd("*SCS{}".format(newval.value)) + self.sendcmd(f"*SCS{newval.value}") @property def single_shot_energy_mode(self): @@ -353,7 +353,7 @@ def single_shot_energy_mode(self): def single_shot_energy_mode(self, newval): sendval = 1 if newval else 0 # set send value self._power_mode = False if newval else True # set power mode - self.sendcmd("*SSE{}".format(sendval)) + self.sendcmd(f"*SSE{sendval}") @property def trigger_level(self): @@ -394,7 +394,7 @@ def trigger_level(self, newval): else: newval = str(round(newval, 2)).zfill(4) - self.sendcmd("*STL{}".format(newval)) + self.sendcmd(f"*STL{newval}") @property def usb_state(self): @@ -430,7 +430,7 @@ def user_multiplier(self): @user_multiplier.setter def user_multiplier(self, newval): sendval = _format_eight(newval) # sendval: 8 characters long - self.sendcmd("*MUL{}".format(sendval)) + self.sendcmd(f"*MUL{sendval}") @property def user_offset(self): @@ -474,7 +474,7 @@ def user_offset(self, newval): "Value must be given in watts, " "joules, or unitless." ) sendval = _format_eight(newval) # sendval: 8 characters long - self.sendcmd("*OFF{}".format(sendval)) + self.sendcmd(f"*OFF{sendval}") @property def version(self): @@ -519,7 +519,7 @@ def wavelength(self, newval): if val >= 1000000 or val < 0: # can only send 5 digits val = 0 # out of bound anyway val = str(int(val)).zfill(5) - self.sendcmd("*PWC{}".format(val)) + self.sendcmd(f"*PWC{val}") @property def zero_offset(self): @@ -645,9 +645,9 @@ def _format_eight(value): """ if len(str(value)) > 8: if value < 0: - value = "{0:.2g}".format(value).zfill(8) # make space for - + value = f"{value:.2g}".zfill(8) # make space for - else: - value = "{0:.3g}".format(value).zfill(8) + value = f"{value:.3g}".zfill(8) else: value = str(value).zfill(8) return value diff --git a/instruments/glassman/__init__.py b/instruments/glassman/__init__.py index 4e7582521..05d01775b 100644 --- a/instruments/glassman/__init__.py +++ b/instruments/glassman/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Glassman power supplies """ diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 888a5484b..01f0a53b0 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # hpe3631a.py: Driver for the Glassman FR Series Power Supplies # @@ -83,7 +82,7 @@ def __init__(self, filelike): """ Initialize the instrument, and set the properties needed for communication. """ - super(GlassmanFR, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self.voltage_max = 50.0 * u.kilovolt self.current_max = 6.0 * u.milliamp @@ -268,7 +267,7 @@ def device_timeout(self): def device_timeout(self, newval): if not isinstance(newval, bool): raise TypeError("Device timeout mode must be a boolean.") - self.query("C{}".format(int(not newval))) # Device acknowledges + self.query(f"C{int(not newval)}") # Device acknowledges self._device_timeout = newval # METHODS ## @@ -303,14 +302,14 @@ def query(self, cmd, size=-1): self.sendcmd(cmd) result = self._file.read(size) if result[0] != getattr(self.ResponseCode, cmd[0]).value and result[0] != "E": - raise ValueError("Invalid response code: {}".format(result)) + raise ValueError(f"Invalid response code: {result}") if result[0] == "A": return "Acknowledged" if not self._verify_checksum(result): - raise ValueError("Invalid checksum: {}".format(result)) + raise ValueError(f"Invalid checksum: {result}") if result[0] == "E": error_name = self.ErrorCode(result[1]).name - raise ValueError("Instrument responded with error: {}".format(error_name)) + raise ValueError(f"Instrument responded with error: {error_name}") return result[1:-2] # Remove SOH and checksum @@ -359,7 +358,7 @@ def set_status(self, voltage=None, current=None, output=None, reset=False): # If the output status is not specified, keep it as is output = output if output is not None else self.output - control = "00{}{}".format(int(output), int(not output)) + control = f"00{int(output)}{int(not output)}" cmd += format(int(control, 2), "07X") self.query("S" + cmd) # Device acknowledges diff --git a/instruments/holzworth/__init__.py b/instruments/holzworth/__init__.py index 06815dab1..9b7f77528 100644 --- a/instruments/holzworth/__init__.py +++ b/instruments/holzworth/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Holzworth instruments """ diff --git a/instruments/holzworth/holzworth_hs9000.py b/instruments/holzworth/holzworth_hs9000.py index 0da72c3c2..99cf0e0bd 100644 --- a/instruments/holzworth/holzworth_hs9000.py +++ b/instruments/holzworth/holzworth_hs9000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Holzworth HS9000 """ @@ -49,7 +48,7 @@ def __init__(self, hs, idx_chan): # Some channel names, like "REF", are special and are preserved # as strs. self._ch_name = ( - idx_chan if isinstance(idx_chan, str) else "CH{}".format(idx_chan + 1) + idx_chan if isinstance(idx_chan, str) else f"CH{idx_chan + 1}" ) # PRIVATE METHODS # @@ -62,7 +61,7 @@ def sendcmd(self, cmd): :param str cmd: Command that will be sent to the instrument after being prefixed with the channel identifier """ - self._hs.sendcmd(":{ch}:{cmd}".format(ch=self._ch_name, cmd=cmd)) + self._hs.sendcmd(f":{self._ch_name}:{cmd}") def query(self, cmd): """ @@ -74,7 +73,7 @@ def query(self, cmd): :return: The result from the query :rtype: `str` """ - return self._hs.query(":{ch}:{cmd}".format(ch=self._ch_name, cmd=cmd)) + return self._hs.query(f":{self._ch_name}:{cmd}") # STATE METHODS # @@ -122,7 +121,7 @@ def temperature(self): :rtype: `~pint.Quantity` """ val, units = split_unit_str(self.query("TEMP?")) - units = "deg{}".format(units) + units = f"deg{units}" return u.Quantity(val, units) frequency, frequency_min, frequency_max = bounded_unitful_property( diff --git a/instruments/hp/__init__.py b/instruments/hp/__init__.py index 71a26416a..e92efbdb2 100644 --- a/instruments/hp/__init__.py +++ b/instruments/hp/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing HP instruments """ diff --git a/instruments/hp/hp3456a.py b/instruments/hp/hp3456a.py index 4330cc429..1548cb5a1 100644 --- a/instruments/hp/hp3456a.py +++ b/instruments/hp/hp3456a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # hp3456a.py: Driver for the HP3456a Digital Voltmeter. # @@ -61,7 +60,7 @@ def __init__(self, filelike): Initialise the instrument, and set the required eos, eoi needed for communication. """ - super(HP3456a, self).__init__(filelike) + super().__init__(filelike) self.timeout = 15 * u.second self.terminator = "\r" self.sendcmd("HO0T4SO1") @@ -459,7 +458,7 @@ def input_range(self, value): "Value {} outside valid ranges " "{}".format(value, valid) ) value = valid.index(value) + 2 - self.sendcmd("R{}W".format(value)) + self.sendcmd(f"R{value}W") else: raise TypeError( "Range setting must be specified as a float, int, " @@ -479,10 +478,10 @@ def relative(self): def relative(self, value): if value is True: self._null = True - self.sendcmd("M{}".format(HP3456a.MathMode.null.value)) + self.sendcmd(f"M{HP3456a.MathMode.null.value}") elif value is False: self._null = False - self.sendcmd("M{}".format(HP3456a.MathMode.off.value)) + self.sendcmd(f"M{HP3456a.MathMode.off.value}") else: raise TypeError( "Relative setting must be specified as a bool, " @@ -570,7 +569,7 @@ def measure(self, mode=None): modevalue = "" units = 1 - self.sendcmd("{}W1STNT3".format(modevalue)) + self.sendcmd(f"{modevalue}W1STNT3") value = self.query("", size=-1) return float(value) * units @@ -593,7 +592,7 @@ def _register_read(self, name): "HP3456a.Register, got {} " "instead.".format(name) ) - self.sendcmd("RE{}".format(name.value)) + self.sendcmd(f"RE{name.value}") time.sleep(0.1) return float(self.query("", size=-1)) @@ -620,8 +619,8 @@ def _register_write(self, name, value): HP3456a.Register.variance, HP3456a.Register.count, ]: - raise ValueError("register {} is read only".format(name)) - self.sendcmd("W{}ST{}".format(value, name.value)) + raise ValueError(f"register {name} is read only") + self.sendcmd(f"W{value}ST{name.value}") time.sleep(0.1) def trigger(self): diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index 5f363da34..ff639af95 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the HP6624a power supply """ @@ -33,7 +32,7 @@ class HP6624a(PowerSupply): """ def __init__(self, filelike): - super(HP6624a, self).__init__(filelike) + super().__init__(filelike) self._channel_count = 4 # INNER CLASSES # @@ -55,7 +54,7 @@ def __init__(self, hp, idx): def _format_cmd(self, cmd): cmd = cmd.split(" ") if len(cmd) == 1: - cmd = "{cmd} {idx}".format(cmd=cmd[0], idx=self._idx) + cmd = f"{cmd[0]} {self._idx}" else: cmd = "{cmd} {idx},{value}".format( cmd=cmd[0], idx=self._idx, value=cmd[1] @@ -246,7 +245,7 @@ def voltage(self): of units Volts. :type: `tuple`[`~pint.Quantity`, ...] with units Volt """ - return tuple([self.channel[i].voltage for i in range(self.channel_count)]) + return tuple(self.channel[i].voltage for i in range(self.channel_count)) @voltage.setter def voltage(self, newval): @@ -272,7 +271,7 @@ def current(self): of units Amps. :type: `tuple`[`~pint.Quantity`, ...] with units Amp """ - return tuple([self.channel[i].current for i in range(self.channel_count)]) + return tuple(self.channel[i].current for i in range(self.channel_count)) @current.setter def current(self, newval): diff --git a/instruments/hp/hp6632b.py b/instruments/hp/hp6632b.py index cc24c6300..9d13aac6d 100644 --- a/instruments/hp/hp6632b.py +++ b/instruments/hp/hp6632b.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # hp6632b.py: Python class for the HP6632b power supply # diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index abdca8336..3f5894a52 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Driver for the HP6652a single output power supply @@ -235,7 +234,7 @@ def display_text(self, text_to_display): text_to_display = text_to_display[:15] text_to_display = text_to_display.upper() - self.sendcmd('DISP:TEXT "{}"'.format(text_to_display)) + self.sendcmd(f'DISP:TEXT "{text_to_display}"') return text_to_display diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index b04c0e6c0..f7f768f34 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # hpe3631a.py: Driver for the HP E3631A Power Supply # @@ -82,7 +81,7 @@ class HPe3631a(PowerSupply, PowerSupplyChannel, SCPIInstrument): """ def __init__(self, filelike): - super(HPe3631a, self).__init__(filelike) + super().__init__(filelike) self.sendcmd("SYST:REM") # Puts the device in remote operation time.sleep(0.1) @@ -184,8 +183,8 @@ def voltage(self, newval): # Rescale to the correct unit before printing. This will also # catch bad units. - strval = "{:e}".format(assume_units(newval, u.volt).to(u.volt).magnitude) - self.sendcmd("SOUR:VOLT {}".format(strval)) + strval = f"{assume_units(newval, u.volt).to(u.volt).magnitude:e}" + self.sendcmd(f"SOUR:VOLT {strval}") @property def voltage_min(self): diff --git a/instruments/keithley/__init__.py b/instruments/keithley/__init__.py index c00a31f06..56f8ef371 100644 --- a/instruments/keithley/__init__.py +++ b/instruments/keithley/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Keithley instruments """ diff --git a/instruments/keithley/keithley195.py b/instruments/keithley/keithley195.py index 0e3db9f24..0c79931dc 100644 --- a/instruments/keithley/keithley195.py +++ b/instruments/keithley/keithley195.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Driver for the Keithley 195 digital multimeter """ @@ -34,7 +33,7 @@ class Keithley195(Multimeter): """ def __init__(self, filelike): - super(Keithley195, self).__init__(filelike) + super().__init__(filelike) self.sendcmd("YX") # Removes the termination CRLF self.sendcmd("G1DX") # Disable returning prefix and suffix @@ -105,7 +104,7 @@ def mode(self, newval): "Mode must be specified as a Keithley195.Mode " "value, got {} instead.".format(newval) ) - self.sendcmd("F{}DX".format(newval.value)) + self.sendcmd(f"F{newval.value}DX") @property def trigger_mode(self): @@ -144,7 +143,7 @@ def trigger_mode(self, newval): "Keithley195.TriggerMode, got {} " "instead.".format(newval) ) - self.sendcmd("T{}X".format(newval.value)) + self.sendcmd(f"T{newval.value}X") @property def relative(self): @@ -172,7 +171,7 @@ def relative(self): def relative(self, newval): if not isinstance(newval, bool): raise TypeError("Relative mode must be a boolean.") - self.sendcmd("Z{}DX".format(int(newval))) + self.sendcmd(f"Z{int(newval)}DX") @property def input_range(self): @@ -233,7 +232,7 @@ def input_range(self, newval): "Range setting must be specified as a float, int, " 'or the string "auto", got {}'.format(type(newval)) ) - self.sendcmd("R{}DX".format(newval)) + self.sendcmd(f"R{newval}DX") # METHODS # diff --git a/instruments/keithley/keithley2182.py b/instruments/keithley/keithley2182.py index 2413d3759..3436df564 100644 --- a/instruments/keithley/keithley2182.py +++ b/instruments/keithley/keithley2182.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Driver for the Keithley 2182 nano-voltmeter """ @@ -96,7 +95,7 @@ def measure(self, mode=None): if mode is not None: # self.mode = mode raise NotImplementedError - self._parent.sendcmd("SENS:CHAN {}".format(self._idx)) + self._parent.sendcmd(f"SENS:CHAN {self._idx}") value = float(self._parent.query("SENS:DATA:FRES?")) unit = self._parent.units return u.Quantity(value, unit) @@ -158,7 +157,7 @@ def relative(self): :type: `bool` """ mode = self.channel[0].mode - return self.query("SENS:{}:CHAN1:REF:STAT?".format(mode.value)) == "ON" + return self.query(f"SENS:{mode.value}:CHAN1:REF:STAT?") == "ON" @relative.setter def relative(self, newval): @@ -166,10 +165,10 @@ def relative(self, newval): raise TypeError("Relative mode must be a boolean.") mode = self.channel[0].mode if self.relative: - self.sendcmd("SENS:{}:CHAN1:REF:ACQ".format(mode.value)) + self.sendcmd(f"SENS:{mode.value}:CHAN1:REF:ACQ") else: newval = "ON" if newval is True else "OFF" - self.sendcmd("SENS:{}:CHAN1:REF:STAT {}".format(mode.value, newval)) + self.sendcmd(f"SENS:{mode.value}:CHAN1:REF:STAT {newval}") @property def input_range(self): @@ -242,6 +241,6 @@ def measure(self, mode=None): "Mode must be specified as a Keithley2182.Mode " "value, got {} instead.".format(mode) ) - value = float(self.query("MEAS:{}?".format(mode.value))) + value = float(self.query(f"MEAS:{mode.value}?")) unit = self.units return value * unit diff --git a/instruments/keithley/keithley485.py b/instruments/keithley/keithley485.py index c71b27057..38cb5ee99 100644 --- a/instruments/keithley/keithley485.py +++ b/instruments/keithley/keithley485.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # keithley485.py: Driver for the Keithley 485 picoammeter. # @@ -158,7 +157,7 @@ def zero_check(self): def zero_check(self, newval): if not isinstance(newval, bool): raise TypeError("Zero Check mode must be a boolean.") - self.sendcmd("C{}X".format(int(newval))) + self.sendcmd(f"C{int(newval)}X") @property def log(self): @@ -178,7 +177,7 @@ def log(self): def log(self, newval): if not isinstance(newval, bool): raise TypeError("Log mode must be a boolean.") - self.sendcmd("D{}X".format(int(newval))) + self.sendcmd(f"D{int(newval)}X") @property def input_range(self): @@ -213,13 +212,13 @@ def input_range(self, newval): if newval in valid: newval = valid.index(newval) else: - raise ValueError("Valid range settings are: {}".format(valid)) + raise ValueError(f"Valid range settings are: {valid}") else: raise TypeError( "Range setting must be specified as a float, int, " "or the string `auto`, got {}".format(type(newval)) ) - self.sendcmd("R{}X".format(newval)) + self.sendcmd(f"R{newval}X") @property def relative(self): @@ -247,7 +246,7 @@ def relative(self): def relative(self, newval): if not isinstance(newval, bool): raise TypeError("Relative mode must be a boolean.") - self.sendcmd("Z{}X".format(int(newval))) + self.sendcmd(f"Z{int(newval)}X") @property def eoi_mode(self): @@ -269,7 +268,7 @@ def eoi_mode(self): def eoi_mode(self, newval): if not isinstance(newval, bool): raise TypeError("EOI mode must be a boolean.") - self.sendcmd("K{}X".format(1 - int(newval))) + self.sendcmd(f"K{1 - int(newval)}X") @property def trigger_mode(self): @@ -306,7 +305,7 @@ def trigger_mode(self, newval): "Keithley485.TriggerMode, got {} " "instead.".format(newval) ) - self.sendcmd("T{}X".format(newval.value)) + self.sendcmd(f"T{newval.value}X") # METHODS # @@ -345,7 +344,7 @@ def _get_status_word(self): tries -= 1 if tries == 0: - raise IOError("Could not retrieve status word") + raise OSError("Could not retrieve status word") return statusword[:-1] @@ -439,12 +438,10 @@ def _parse_measurement(self, measurement): raise ValueError(f"Invalid status word in measurement: {status}") if status != self.Status.normal: - raise ValueError("Instrument not in normal mode: {}".format(status.name)) + raise ValueError(f"Instrument not in normal mode: {status.name}") if function != b"DC": - raise ValueError( - "Instrument not returning DC function: {}".format(function) - ) + raise ValueError(f"Instrument not returning DC function: {function}") try: current = ( @@ -453,6 +450,6 @@ def _parse_measurement(self, measurement): else 10 ** (float(current)) * u.amp ) except: - raise Exception("Cannot parse measurement: {}".format(measurement)) + raise Exception(f"Cannot parse measurement: {measurement}") return current diff --git a/instruments/keithley/keithley580.py b/instruments/keithley/keithley580.py index 5e4e5961e..6d726c56a 100644 --- a/instruments/keithley/keithley580.py +++ b/instruments/keithley/keithley580.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # keithley580.py: Driver for the Keithley 580 micro-ohmmeter. # @@ -60,7 +59,7 @@ def __init__(self, filelike): """ Initialise the instrument and remove CRLF line termination """ - super(Keithley580, self).__init__(filelike) + super().__init__(filelike) self.sendcmd("Y:X") # Removes the termination CRLF characters # ENUMS # @@ -125,7 +124,7 @@ def polarity(self, newval): "instead.".format(newval) ) - self.sendcmd("P{}X".format(newval.value)) + self.sendcmd(f"P{newval.value}X") @property def drive(self): @@ -154,7 +153,7 @@ def drive(self, newval): "instead.".format(newval) ) - self.sendcmd("D{}X".format(newval.value)) + self.sendcmd(f"D{newval.value}X") @property def dry_circuit_test(self): @@ -177,7 +176,7 @@ def dry_circuit_test(self): def dry_circuit_test(self, newval): if not isinstance(newval, bool): raise TypeError("DryCircuitTest mode must be a boolean.") - self.sendcmd("C{}X".format(int(newval))) + self.sendcmd(f"C{int(newval)}X") @property def operate(self): @@ -194,7 +193,7 @@ def operate(self): def operate(self, newval): if not isinstance(newval, bool): raise TypeError("Operate mode must be a boolean.") - self.sendcmd("O{}X".format(int(newval))) + self.sendcmd(f"O{int(newval)}X") @property def relative(self): @@ -222,7 +221,7 @@ def relative(self): def relative(self, newval): if not isinstance(newval, bool): raise TypeError("Relative mode must be a boolean.") - self.sendcmd("Z{}X".format(int(newval))) + self.sendcmd(f"Z{int(newval)}X") @property def trigger_mode(self): @@ -258,7 +257,7 @@ def trigger_mode(self, newval): "Keithley580.TriggerMode, got {} " "instead.".format(newval) ) - self.sendcmd("T{}X".format(newval.value)) + self.sendcmd(f"T{newval.value}X") @property def input_range(self): @@ -294,13 +293,13 @@ def input_range(self, newval): if newval in valid: newval = valid.index(newval) else: - raise ValueError("Valid range settings are: {}".format(valid)) + raise ValueError(f"Valid range settings are: {valid}") else: raise TypeError( "Range setting must be specified as a float, int, " 'or the string "auto", got {}'.format(type(newval)) ) - self.sendcmd("R{}X".format(newval)) + self.sendcmd(f"R{newval}X") # METHODS # @@ -357,7 +356,7 @@ def get_status_word(self): statusword = self._file.read_raw() if tries == 0: - raise IOError("could not retrieve status word") + raise OSError("could not retrieve status word") return statusword[:-1] @@ -481,7 +480,7 @@ def parse_measurement(measurement): drive = valid["drive"][drive] resistance = float(resistance) * u.ohm except: - raise Exception("Cannot parse measurement: {}".format(measurement)) + raise Exception(f"Cannot parse measurement: {measurement}") return { "status": status, @@ -494,7 +493,7 @@ def parse_measurement(measurement): # COMMUNICATOR METHODS # def sendcmd(self, cmd): - super(Keithley580, self).sendcmd(cmd + ":") + super().sendcmd(cmd + ":") def query(self, cmd, size=-1): - return super(Keithley580, self).query(cmd + ":", size)[:-1] + return super().query(cmd + ":", size)[:-1] diff --git a/instruments/keithley/keithley6220.py b/instruments/keithley/keithley6220.py index 8f633091f..0ff380670 100644 --- a/instruments/keithley/keithley6220.py +++ b/instruments/keithley/keithley6220.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Keithley 6220 constant current supply """ diff --git a/instruments/keithley/keithley6514.py b/instruments/keithley/keithley6514.py index 866621d6b..8581f1fc0 100644 --- a/instruments/keithley/keithley6514.py +++ b/instruments/keithley/keithley6514.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Keithley 6514 electrometer """ @@ -175,7 +174,7 @@ def auto_range(self): :type: `bool` """ # pylint: disable=no-member - out = self.query("{}:RANGE:AUTO?".format(self.mode.value)) + out = self.query(f"{self.mode.value}:RANGE:AUTO?") return True if out == "1" else False @auto_range.setter @@ -192,7 +191,7 @@ def input_range(self): """ # pylint: disable=no-member mode = self.mode - out = self.query("{}:RANGE:UPPER?".format(mode.value)) + out = self.query(f"{mode.value}:RANGE:UPPER?") return float(out) * self._MODE_UNITS[mode] @input_range.setter @@ -202,7 +201,7 @@ def input_range(self, newval): val = newval.to(self._MODE_UNITS[mode]).magnitude if val not in self._valid_range(mode).value: raise ValueError("Unexpected range limit for currently selected mode.") - self.sendcmd("{}:RANGE:UPPER {:e}".format(mode.value, val)) + self.sendcmd(f"{mode.value}:RANGE:UPPER {val:e}") # METHODS ## @@ -219,7 +218,7 @@ def auto_config(self, mode): - Disable buffer operation - Enable autozero """ - self.sendcmd("CONF:{}".format(mode.value)) + self.sendcmd(f"CONF:{mode.value}") def fetch(self): """ diff --git a/instruments/lakeshore/__init__.py b/instruments/lakeshore/__init__.py index 238fd4deb..ac32a5d2e 100644 --- a/instruments/lakeshore/__init__.py +++ b/instruments/lakeshore/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Lakeshore instruments """ diff --git a/instruments/lakeshore/lakeshore340.py b/instruments/lakeshore/lakeshore340.py index d8e093330..697e3ac04 100644 --- a/instruments/lakeshore/lakeshore340.py +++ b/instruments/lakeshore/lakeshore340.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Lakeshore 340 cryogenic temperature controller. """ @@ -52,7 +51,7 @@ def temperature(self): :units: Kelvin :type: `~pint.Quantity` """ - value = self._parent.query("KRDG?{}".format(self._idx)) + value = self._parent.query(f"KRDG?{self._idx}") return u.Quantity(float(value), u.kelvin) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore370.py b/instruments/lakeshore/lakeshore370.py index a287c4241..005b1e7ca 100644 --- a/instruments/lakeshore/lakeshore370.py +++ b/instruments/lakeshore/lakeshore370.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Lakeshore 370 AC resistance bridge. """ @@ -27,7 +26,7 @@ class Lakeshore370(SCPIInstrument): """ def __init__(self, filelike): - super(Lakeshore370, self).__init__(filelike) + super().__init__(filelike) # Disable termination characters and enable EOI self.sendcmd("IEEE 3,0") @@ -56,7 +55,7 @@ def resistance(self): :units: Ohm :rtype: `~pint.Quantity` """ - value = self._parent.query("RDGR? {}".format(self._idx)) + value = self._parent.query(f"RDGR? {self._idx}") return u.Quantity(float(value), u.ohm) # PROPERTIES ## diff --git a/instruments/lakeshore/lakeshore475.py b/instruments/lakeshore/lakeshore475.py index 444136179..cee029f1e 100644 --- a/instruments/lakeshore/lakeshore475.py +++ b/instruments/lakeshore/lakeshore475.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Lakeshore 475 Gaussmeter. """ @@ -18,8 +17,8 @@ LAKESHORE_TEMP_UNITS = {1: u.celsius, 2: u.kelvin} -LAKESHORE_FIELD_UNITS_INV = dict((v, k) for k, v in LAKESHORE_FIELD_UNITS.items()) -LAKESHORE_TEMP_UNITS_INV = dict((v, k) for k, v in LAKESHORE_TEMP_UNITS.items()) +LAKESHORE_FIELD_UNITS_INV = {v: k for k, v in LAKESHORE_FIELD_UNITS.items()} +LAKESHORE_TEMP_UNITS_INV = {v: k for k, v in LAKESHORE_TEMP_UNITS.items()} # CLASSES ##################################################################### @@ -156,7 +155,7 @@ def field_setpoint(self, newval): f"{newval.units}, currently expecting {expected_units}." ) - self.sendcmd("CSETP {}".format(newval.magnitude)) + self.sendcmd(f"CSETP {newval.magnitude}") @property def field_control_params(self): diff --git a/instruments/minghe/__init__.py b/instruments/minghe/__init__.py index 4b17756e1..4c9ec9535 100644 --- a/instruments/minghe/__init__.py +++ b/instruments/minghe/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing MingHe instruments """ diff --git a/instruments/minghe/mhs5200a.py b/instruments/minghe/mhs5200a.py index 634342b75..f0e0314f0 100644 --- a/instruments/minghe/mhs5200a.py +++ b/instruments/minghe/mhs5200a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the support for the MingHe low-cost function generator. @@ -27,7 +26,7 @@ class MHS5200(FunctionGenerator): """ def __init__(self, filelike): - super(MHS5200, self).__init__(filelike) + super().__init__(filelike) self._channel_count = 2 self.terminator = "\r\n" @@ -58,7 +57,7 @@ def __init__(self, mhs, idx): self._count = 0 def _get_amplitude_(self): - query = ":r{0}a".format(self._chan) + query = f":r{self._chan}a" response = self._mhs.query(query) return float(response.replace(query, "")) / 100.0, self._mhs.VoltageMode.rms @@ -71,7 +70,7 @@ def _set_amplitude_(self, magnitude, units): elif units == self._mhs.VoltageMode.dBm: raise NotImplementedError("Decibel units are not supported.") magnitude *= 100 - query = ":s{0}a{1}".format(self._chan, int(magnitude)) + query = f":s{self._chan}a{int(magnitude)}" self._mhs.sendcmd(query) @property @@ -82,14 +81,14 @@ def duty_cycle(self): :units: A fraction :type: `float` """ - query = ":r{0}d".format(self._chan) + query = f":r{self._chan}d" response = self._mhs.query(query) duty = float(response.replace(query, "")) / 10.0 return duty @duty_cycle.setter def duty_cycle(self, new_val): - query = ":s{0}d{1}".format(self._chan, int(100.0 * new_val)) + query = f":s{self._chan}d{int(100.0 * new_val)}" self._mhs.sendcmd(query) @property @@ -99,12 +98,12 @@ def enable(self): :type: `bool` """ - query = ":r{0}b".format(self._chan) + query = f":r{self._chan}b" return int(self._mhs.query(query).replace(query, "").replace("\r", "")) @enable.setter def enable(self, newval): - query = ":s{0}b{1}".format(self._chan, int(newval)) + query = f":s{self._chan}b{int(newval)}" self._mhs.sendcmd(query) @property @@ -116,7 +115,7 @@ def frequency(self): of units hertz. :type: `~pint.Quantity` """ - query = ":r{0}f".format(self._chan) + query = f":r{self._chan}f" response = self._mhs.query(query) freq = float(response.replace(query, "")) * u.Hz return freq / 100.0 @@ -124,7 +123,7 @@ def frequency(self): @frequency.setter def frequency(self, new_val): new_val = assume_units(new_val, u.Hz).to(u.Hz).magnitude * 100.0 - query = ":s{0}f{1}".format(self._chan, int(new_val)) + query = f":s{self._chan}f{int(new_val)}" self._mhs.sendcmd(query) @property @@ -137,14 +136,14 @@ def offset(self): :type: `float` """ # need to convert - query = ":r{0}o".format(self._chan) + query = f":r{self._chan}o" response = self._mhs.query(query) return int(response.replace(query, "")) / 100.0 - 1.20 @offset.setter def offset(self, new_val): new_val = int(new_val * 100) + 120 - query = ":s{0}o{1}".format(self._chan, new_val) + query = f":s{self._chan}o{new_val}" self._mhs.sendcmd(query) @property @@ -157,14 +156,14 @@ def phase(self): :type: `~pint.Quantity` """ # need to convert - query = ":r{0}p".format(self._chan) + query = f":r{self._chan}p" response = self._mhs.query(query) return int(response.replace(query, "")) * u.deg @phase.setter def phase(self, new_val): new_val = assume_units(new_val, u.deg).to("deg").magnitude - query = ":s{0}p{1}".format(self._chan, int(new_val)) + query = f":s{self._chan}p{int(new_val)}" self._mhs.sendcmd(query) @property @@ -174,13 +173,13 @@ def function(self): :type: `MHS5200.Function` """ - query = ":r{0}w".format(self._chan) + query = f":r{self._chan}w" response = self._mhs.query(query).replace(query, "") return self._mhs.Function(int(response)) @function.setter def function(self, new_val): - query = ":s{0}w{1}".format(self._chan, self._mhs.Function(new_val).value) + query = f":s{self._chan}w{self._mhs.Function(new_val).value}" self._mhs.sendcmd(query) class Function(Enum): diff --git a/instruments/named_struct.py b/instruments/named_struct.py index f10698031..5e2871224 100644 --- a/instruments/named_struct.py +++ b/instruments/named_struct.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Class for quickly defining C-like structures with named fields. """ @@ -48,7 +47,7 @@ class Field: _owner_type = object def __init__(self, fmt, strip_null=False): - super(Field, self).__init__() + super().__init__() # Record our birthday so that we can sort fields later. self._field_birthday = Field.__n_fields_created @@ -86,7 +85,7 @@ def __repr__(self): self._name, self._owner_type, self._fmt ) - return "".format(self._fmt) + return f"" def __str__(self): n, fmt_char = len(self), self.fmt_char @@ -113,12 +112,8 @@ def __str__(self): }[fmt_char] if n: - c_type = "{}[{}]".format(c_type, n) - return ( - "{c_type} {self._name}".format(c_type=c_type, self=self) - if self.is_significant() - else c_type - ) + c_type = f"{c_type}[{n}]" + return f"{c_type} {self._name}" if self.is_significant() else c_type # DESCRIPTOR PROTOCOL # @@ -147,7 +142,7 @@ class StringField(Field): _encoding = "ascii" def __init__(self, length, encoding="ascii", strip_null=False): - super(StringField, self).__init__("{}s".format(length)) + super().__init__(f"{length}s") self._strip_null = strip_null self._encoding = encoding @@ -158,10 +153,10 @@ def __set__(self, obj, value): value = value.rstrip("\x00") value = value.encode(self._encoding) - super(StringField, self).__set__(obj, value) + super().__set__(obj, value) def __get__(self, obj, type=None): - return super(StringField, self).__get__(obj, type=type).decode(self._encoding) + return super().__get__(obj, type=type).decode(self._encoding) class Padding(Field): @@ -173,7 +168,7 @@ class Padding(Field): """ def __init__(self, n_bytes=1): - super(Padding, self).__init__("{}x".format(n_bytes)) + super().__init__(f"{n_bytes}x") class HasFields(type): @@ -187,7 +182,7 @@ def __new__(mcs, name, bases, attrs): # We call the superclass of HasFields, which is another # metaclass, to do most of the heavy lifting of creating # the new class. - cls = super(HasFields, mcs).__new__(mcs, name, bases, attrs) + cls = super().__new__(mcs, name, bases, attrs) # We now sort the fields by their birthdays and store them in an # ordered dict for easier look up later. @@ -195,11 +190,11 @@ def __new__(mcs, name, bases, attrs): [ (field_name, field) for field_name, field in sorted( - [ + ( (field_name, field) for field_name, field in attrs.items() if isinstance(field, Field) - ], + ), key=lambda item: item[1]._field_birthday, ) ] @@ -251,7 +246,7 @@ class Foo(NamedStruct): _struct = None def __init__(self, **kwargs): - super(NamedStruct, self).__init__() + super().__init__() self._values = OrderedDict( [ (field._name, None) @@ -314,7 +309,7 @@ def __str__(self): " {field}{value};".format( field=field, value=( - " = {}".format(repr(self._values[field._name])) + f" = {repr(self._values[field._name])}" if field.is_significant() else "" ), diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index b61b40db1..74c9aa1e6 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Newport instruments """ diff --git a/instruments/newport/agilis.py b/instruments/newport/agilis.py index 77b732974..ccbde6019 100644 --- a/instruments/newport/agilis.py +++ b/instruments/newport/agilis.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Newport Agilis Controller AG-UC2 only (currently). @@ -69,7 +68,7 @@ def axis_status(self): """ Returns the status of the current axis. """ - resp = self._cont.ag_query("{} TS".format(int(self._ax))) + resp = self._cont.ag_query(f"{int(self._ax)} TS") if resp.find("TS") == -1: return "Status code query failed." @@ -98,7 +97,7 @@ def jog(self): :return: Jog motion set :rtype: `int` """ - resp = self._cont.ag_query("{} JA?".format(int(self._ax))) + resp = self._cont.ag_query(f"{int(self._ax)} JA?") return int(resp.split("JA")[1]) @jog.setter @@ -107,7 +106,7 @@ def jog(self, mode): if mode < -4 or mode > 4: raise ValueError("Jog mode out of range. Must be between -4 and " "4.") - self._cont.ag_sendcmd("{} JA {}".format(int(self._ax), mode)) + self._cont.ag_sendcmd(f"{int(self._ax)} JA {mode}") @property def number_of_steps(self): @@ -129,7 +128,7 @@ def number_of_steps(self): :return: Number of steps :rtype: int """ - resp = self._cont.ag_query("{} TP".format(int(self._ax))) + resp = self._cont.ag_query(f"{int(self._ax)} TP") return int(resp.split("TP")[1]) @property @@ -141,7 +140,7 @@ def move_relative(self): If queried, command returns the current target position. At least this is the expected behaviour, never worked with the rotation stage. """ - resp = self._cont.ag_query("{} PR?".format(int(self._ax))) + resp = self._cont.ag_query(f"{int(self._ax)} PR?") return int(resp.split("PR")[1]) @move_relative.setter @@ -153,7 +152,7 @@ def move_relative(self, steps): "between -2,147,483,648 and 2,147,483,647" ) - self._cont.ag_sendcmd("{} PR {}".format(int(self._ax), steps)) + self._cont.ag_sendcmd(f"{int(self._ax)} PR {steps}") @property def move_to_limit(self): @@ -170,7 +169,7 @@ def move_to_limit(self): Returns the distance of the current position to the limit in 1/1000th of the total travel. """ - resp = self._cont.ag_query("{} MA?".format(int(self._ax))) + resp = self._cont.ag_query(f"{int(self._ax)} MA?") return int(resp.split("MA")[1]) @move_to_limit.setter @@ -179,7 +178,7 @@ def move_to_limit(self, mode): if mode < -4 or mode > 4: raise ValueError("Jog mode out of range. Must be between -4 and " "4.") - self._cont.ag_sendcmd("{} MA {}".format(int(self._ax), mode)) + self._cont.ag_sendcmd(f"{int(self._ax)} MA {mode}") @property def step_amplitude(self): @@ -198,8 +197,8 @@ def step_amplitude(self): response. :rtype: (`int`, `int`) """ - resp_neg = self._cont.ag_query("{} SU-?".format(int(self._ax))) - resp_pos = self._cont.ag_query("{} SU+?".format(int(self._ax))) + resp_neg = self._cont.ag_query(f"{int(self._ax)} SU-?") + resp_pos = self._cont.ag_query(f"{int(self._ax)} SU+?") return int(resp_neg.split("SU")[1]), int(resp_pos.split("SU")[1]) @step_amplitude.setter @@ -218,7 +217,7 @@ def step_amplitude(self, nns): ) for nn in nns: - self._cont.ag_sendcmd("{} SU {}".format(int(self._ax), int(nn))) + self._cont.ag_sendcmd(f"{int(self._ax)} SU {int(nn)}") @property def step_delay(self): @@ -234,7 +233,7 @@ def step_delay(self): :return: Step delay :rtype: `int` """ - resp = self._cont.ag_query("{} DL?".format(int(self._ax))) + resp = self._cont.ag_query(f"{int(self._ax)} DL?") return int(resp.split("DL")[1]) @step_delay.setter @@ -245,7 +244,7 @@ def step_delay(self, nn): "Step delay is out of range. It must be between " "0 and 200000." ) - self._cont.ag_sendcmd("{} DL {}".format(int(self._ax), nn)) + self._cont.ag_sendcmd(f"{int(self._ax)} DL {nn}") # MODES # @@ -279,7 +278,7 @@ def am_i_still(self, max_retries=5): else: retries += 1 - raise IOError( + raise OSError( "The function `am_i_still` ran out of maximum retries. " "Could not query the status of the axis." ) @@ -288,13 +287,13 @@ def stop(self): """ Stops the axis. This is useful to interrupt a jogging motion. """ - self._cont.ag_sendcmd("{} ST".format(int(self._ax))) + self._cont.ag_sendcmd(f"{int(self._ax)} ST") def zero_position(self): """ Resets the step counter to zero. See `number_of_steps` for details. """ - self._cont.ag_sendcmd("{} ZP".format(int(self._ax))) + self._cont.ag_sendcmd(f"{int(self._ax)} ZP") class AGUC2(Instrument): @@ -343,7 +342,7 @@ class AGUC2(Instrument): """ def __init__(self, filelike): - super(AGUC2, self).__init__(filelike) + super().__init__(filelike) # Instrument requires '\r\n' line termination self.terminator = "\r\n" @@ -490,7 +489,7 @@ def ag_query(self, cmd, size=-1): """ try: resp = self.query(cmd, size=size) - except IOError: + except OSError: resp = "Query timed out." # sleep diff --git a/instruments/newport/errors.py b/instruments/newport/errors.py index dc66091de..f4d9baea1 100644 --- a/instruments/newport/errors.py +++ b/instruments/newport/errors.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides common error handling for Newport devices. """ @@ -114,26 +113,25 @@ def __init__(self, errcode=None, timestamp=None): if self._axis == 0: self._axis = None error_message = self.get_message(str(errcode)) - error = ( - "Newport Error: {0}. Error Message: {1}. " - "At time : {2}".format(str(errcode), error_message, self._timestamp) + error = "Newport Error: {}. Error Message: {}. " "At time : {}".format( + str(errcode), error_message, self._timestamp ) - super(NewportError, self).__init__(error) + super().__init__(error) else: - error_message = self.get_message("x{0:02d}".format(self._errcode)) + error_message = self.get_message(f"x{self._errcode:02d}") error = ( - "Newport Error: {0}. Axis: {1}. " - "Error Message: {2}. " - "At time : {3}".format( + "Newport Error: {}. Axis: {}. " + "Error Message: {}. " + "At time : {}".format( str(self._errcode), self._axis, error_message, self._timestamp ) ) - super(NewportError, self).__init__(error) + super().__init__(error) else: self._errcode = None self._axis = None - super(NewportError, self).__init__("") + super().__init__("") # PRIVATE METHODS ## diff --git a/instruments/newport/newport_pmc8742.py b/instruments/newport/newport_pmc8742.py index 7398c770a..71240b831 100644 --- a/instruments/newport/newport_pmc8742.py +++ b/instruments/newport/newport_pmc8742.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Newport Pico Motor Controller 8742 @@ -82,7 +81,7 @@ class PicoMotorController8742(Instrument): def __init__(self, filelike): """Initialize the PicoMotorController class.""" - super(PicoMotorController8742, self).__init__(filelike) + super().__init__(filelike) # terminator self.terminator = "\r\n" @@ -630,7 +629,7 @@ def query(self, cmd, size=-1, axs=True): retval = self._parent.query(command, size=size) if retval[: len(self._address)] != self._address: - raise IOError( + raise OSError( f"Expected to hear back from secondary " f"controller {self._address}, instead " f"controller {retval[:len(self._address)]} " diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index 5cdffee20..43bcdfb66 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Newport ESP-301 motor controller. @@ -95,7 +94,7 @@ class NewportESP301(Instrument): """ def __init__(self, filelike): - super(NewportESP301, self).__init__(filelike) + super().__init__(filelike) self._execute_immediately = True self._command_list = [] self._bulk_query_resp = "" @@ -1171,7 +1170,7 @@ def wait_for_motion(self, poll_interval=0.01, max_wait=None): if max_wait is None or (time.time() - tic) < max_wait: time.sleep(poll_interval) else: - raise IOError("Timed out waiting for motion to finish.") + raise OSError("Timed out waiting for motion to finish.") def enable(self): """ @@ -1383,7 +1382,7 @@ def _get_unit_num(self, quantity): if quant == quantity: return num - raise KeyError("{0} is not a valid unit for Newport Axis".format(quantity)) + raise KeyError(f"{quantity} is not a valid unit for Newport Axis") # pylint: disable=protected-access def _newport_cmd(self, cmd, **kwargs): diff --git a/instruments/ondax/__init__.py b/instruments/ondax/__init__.py index 435949c86..4e232d1f9 100644 --- a/instruments/ondax/__init__.py +++ b/instruments/ondax/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Ondax Instruments """ diff --git a/instruments/ondax/lm.py b/instruments/ondax/lm.py index 76286f4e2..92b7950eb 100644 --- a/instruments/ondax/lm.py +++ b/instruments/ondax/lm.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the support for the Ondax LM Laser. @@ -29,7 +28,7 @@ class LM(Instrument): """ def __init__(self, filelike): - super(LM, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self.apc = self._AutomaticPowerControl(self) self.acc = self._AutomaticCurrentControl(self) diff --git a/instruments/optional_dep_finder.py b/instruments/optional_dep_finder.py index 11033f379..106f7507d 100644 --- a/instruments/optional_dep_finder.py +++ b/instruments/optional_dep_finder.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Small module to obtain handles to optional dependencies """ diff --git a/instruments/oxford/__init__.py b/instruments/oxford/__init__.py index 8917268e7..94cbe8a67 100644 --- a/instruments/oxford/__init__.py +++ b/instruments/oxford/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Oxford instruments """ diff --git a/instruments/oxford/oxforditc503.py b/instruments/oxford/oxforditc503.py index cca4c1783..b54c8cd75 100644 --- a/instruments/oxford/oxforditc503.py +++ b/instruments/oxford/oxforditc503.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Oxford ITC 503 temperature controller. """ @@ -27,7 +26,7 @@ class OxfordITC503(Instrument): """ def __init__(self, filelike): - super(OxfordITC503, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self.sendcmd("C3") # Enable remote commands @@ -56,7 +55,7 @@ def temperature(self): :units: Kelvin :type: `~pint.Quantity` """ - value = float(self._parent.query("R{}".format(self._idx))[1:]) + value = float(self._parent.query(f"R{self._idx}")[1:]) return u.Quantity(value, u.kelvin) # PROPERTIES # diff --git a/instruments/phasematrix/__init__.py b/instruments/phasematrix/__init__.py index 1ea5a8a7f..8417858a2 100644 --- a/instruments/phasematrix/__init__.py +++ b/instruments/phasematrix/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Phase Matrix instruments """ diff --git a/instruments/phasematrix/phasematrix_fsw0020.py b/instruments/phasematrix/phasematrix_fsw0020.py index 5ff3161a8..ac1cb91b9 100644 --- a/instruments/phasematrix/phasematrix_fsw0020.py +++ b/instruments/phasematrix/phasematrix_fsw0020.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Phase Matrix FSW0020 signal generator. """ @@ -58,7 +57,7 @@ def frequency(self, newval): # Write the integer to the serial port in ASCII-encoded # uppercase-hexadecimal format, with padding to 12 nybbles. - self.sendcmd("0C{:012X}.".format(newval)) + self.sendcmd(f"0C{newval:012X}.") # No return data, so no readline needed. @@ -84,7 +83,7 @@ def power(self, newval): newval = int(assume_units(newval, u.dBm).to(u.cBm).magnitude) # Command code 0x03, parameter length 2 bytes (4 nybbles) - self.sendcmd("03{:04X}.".format(newval)) + self.sendcmd(f"03{newval:04X}.") @property def phase(self): @@ -105,7 +104,7 @@ def blanking(self): @blanking.setter def blanking(self, newval): - self.sendcmd("05{:02X}.".format(1 if newval else 0)) + self.sendcmd(f"05{1 if newval else 0:02X}.") @property def ref_output(self): @@ -118,7 +117,7 @@ def ref_output(self): @ref_output.setter def ref_output(self, newval): - self.sendcmd("08{:02X}.".format(1 if newval else 0)) + self.sendcmd(f"08{1 if newval else 0:02X}.") @property def output(self): @@ -132,7 +131,7 @@ def output(self): @output.setter def output(self, newval): - self.sendcmd("0F{:02X}.".format(1 if newval else 0)) + self.sendcmd(f"0F{1 if newval else 0:02X}.") @property def pulse_modulation(self): @@ -145,7 +144,7 @@ def pulse_modulation(self): @pulse_modulation.setter def pulse_modulation(self, newval): - self.sendcmd("09{:02X}.".format(1 if newval else 0)) + self.sendcmd(f"09{1 if newval else 0:02X}.") @property def am_modulation(self): @@ -158,4 +157,4 @@ def am_modulation(self): @am_modulation.setter def am_modulation(self, newval): - self.sendcmd("0A{:02X}.".format(1 if newval else 0)) + self.sendcmd(f"0A{1 if newval else 0:02X}.") diff --git a/instruments/picowatt/__init__.py b/instruments/picowatt/__init__.py index e56534b69..28afb8b71 100644 --- a/instruments/picowatt/__init__.py +++ b/instruments/picowatt/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Picowatt instruments """ diff --git a/instruments/picowatt/picowattavs47.py b/instruments/picowatt/picowattavs47.py index 56c848f0b..8be9675d5 100644 --- a/instruments/picowatt/picowattavs47.py +++ b/instruments/picowatt/picowattavs47.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Picowatt AVS 47 resistance bridge """ @@ -29,7 +28,7 @@ class PicowattAVS47(SCPIInstrument): """ def __init__(self, filelike): - super(PicowattAVS47, self).__init__(filelike) + super().__init__(filelike) self.sendcmd("HDR 0") # Disables response headers from replies # INNER CLASSES # diff --git a/instruments/qubitekk/__init__.py b/instruments/qubitekk/__init__.py index 07c6d0fff..107d9d946 100644 --- a/instruments/qubitekk/__init__.py +++ b/instruments/qubitekk/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Qubitekk instruments """ diff --git a/instruments/qubitekk/cc1.py b/instruments/qubitekk/cc1.py index ea26b6ccc..3f229a74a 100644 --- a/instruments/qubitekk/cc1.py +++ b/instruments/qubitekk/cc1.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Qubitekk CC1 Coincidence Counter instrument. @@ -33,7 +32,7 @@ class CC1(SCPIInstrument): """ def __init__(self, filelike): - super(CC1, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\n" self._channel_count = 3 self._firmware = None @@ -114,7 +113,7 @@ def count(self): :rtype: `int` """ - count = self._cc1.query("COUN:{0}?".format(self._chan)) + count = self._cc1.query(f"COUN:{self._chan}?") tries = 5 try: count = int(count) @@ -129,7 +128,7 @@ def count(self): tries -= 1 if tries == 0: - raise IOError(f"Could not read the count of channel " f"{self._chan}.") + raise OSError(f"Could not read the count of channel " f"{self._chan}.") self._count = count return self._count @@ -235,7 +234,7 @@ def window(self, new_val): if new_val_mag < 0 or new_val_mag > 7: raise ValueError("Window is out of range.") # window must be an integer! - self.sendcmd(":WIND {}".format(new_val_mag)) + self.sendcmd(f":WIND {new_val_mag}") @property def delay(self): @@ -281,7 +280,7 @@ def dwell_time(self, new_val): new_val_mag = assume_units(new_val, u.s).to(u.s).magnitude if new_val_mag < 0: raise ValueError("Dwell time cannot be negative.") - self.sendcmd(":DWEL {}".format(new_val_mag)) + self.sendcmd(f":DWEL {new_val_mag}") @property def firmware(self): diff --git a/instruments/qubitekk/mc1.py b/instruments/qubitekk/mc1.py index e3e182a24..c95fa56b6 100644 --- a/instruments/qubitekk/mc1.py +++ b/instruments/qubitekk/mc1.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Qubitekk MC1 Motor Controller. @@ -29,7 +28,7 @@ class MC1(Instrument): """ def __init__(self, filelike): - super(MC1, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self._increment = 1 * u.ms self._lower_limit = -300 * u.ms diff --git a/instruments/rigol/__init__.py b/instruments/rigol/__init__.py index 73a6b401a..150631a28 100644 --- a/instruments/rigol/__init__.py +++ b/instruments/rigol/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Rigol instruments """ diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 36d024eec..27801b8d8 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for Rigol DS-1000 series oscilloscopes. """ @@ -63,7 +62,7 @@ def read_waveform(self, bin_format=True): "supportreading waveforms from " "{}.".format(self.name) ) - self._parent.sendcmd(":WAV:DATA? {}".format(self.name)) + self._parent.sendcmd(f":WAV:DATA? {self.name}") data = self._parent.binblockread(2) # TODO: check width return data @@ -92,7 +91,7 @@ def __init__(self, parent, idx): # Initialize as a data source with name CHAN{}. super(RigolDS1000Series.Channel, self).__init__( - self._parent, "CHAN{}".format(self._idx) + self._parent, f"CHAN{self._idx}" ) def sendcmd(self, cmd): @@ -102,7 +101,7 @@ def sendcmd(self, cmd): :param str cmd: The command string to send to the instrument """ - self._parent.sendcmd(":CHAN{}:{}".format(self._idx, cmd)) + self._parent.sendcmd(f":CHAN{self._idx}:{cmd}") def query(self, cmd): """ @@ -113,7 +112,7 @@ def query(self, cmd): :return: The result as returned by the instrument :rtype: `str` """ - return self._parent.query(":CHAN{}:{}".format(self._idx, cmd)) + return self._parent.query(f":CHAN{self._idx}:{cmd}") coupling = enum_property("COUP", Coupling) @@ -168,7 +167,7 @@ def acquire_averages(self, newval): "Number of averages {} not supported by instrument; " "must be a power of 2 from 2 to 256.".format(newval) ) - self.sendcmd(":ACQ:AVER {}".format(newval)) + self.sendcmd(f":ACQ:AVER {newval}") # TODO: implement :ACQ:SAMP in a meaningful way. This should probably be # under Channel, and needs to be unitful. diff --git a/instruments/srs/__init__.py b/instruments/srs/__init__.py index 689c483f4..3048cab4b 100644 --- a/instruments/srs/__init__.py +++ b/instruments/srs/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Lakeshore instruments """ diff --git a/instruments/srs/srs345.py b/instruments/srs/srs345.py index da02df13a..080a47cea 100644 --- a/instruments/srs/srs345.py +++ b/instruments/srs/srs345.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the SRS 345 function generator. """ @@ -44,7 +43,7 @@ class SRS345(SCPIInstrument, FunctionGenerator): FunctionGenerator.VoltageMode.dBm: "DB", } - _MNEMONIC_UNITS = dict((mnem, unit) for unit, mnem in _UNIT_MNEMONICS.items()) + _MNEMONIC_UNITS = {mnem: unit for unit, mnem in _UNIT_MNEMONICS.items()} # FunctionGenerator CONTRACT # @@ -54,7 +53,7 @@ def _get_amplitude_(self): return (float(resp[:-2]), self._MNEMONIC_UNITS[resp[-2:]]) def _set_amplitude_(self, magnitude, units): - self.sendcmd("AMPL {}{}".format(magnitude, self._UNIT_MNEMONICS[units])) + self.sendcmd(f"AMPL {magnitude}{self._UNIT_MNEMONICS[units]}") # ENUMS ## diff --git a/instruments/srs/srs830.py b/instruments/srs/srs830.py index 616f40b57..64df94776 100644 --- a/instruments/srs/srs830.py +++ b/instruments/srs/srs830.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the SRS 830 lock-in amplifier. """ @@ -58,7 +57,7 @@ def __init__(self, filelike, outx_mode=None): by the SRS830 manual. If left default, the correct ``OUTX`` command will be sent depending on what type of communicator self._file is. """ - super(SRS830, self).__init__(filelike) + super().__init__(filelike) if outx_mode == 1: self.sendcmd("OUTX 1") elif outx_mode == 2: @@ -229,7 +228,7 @@ def sample_rate(self, newval): newval = newval.lower() if newval in VALID_SAMPLE_RATES: - self.sendcmd("SRAT {}".format(VALID_SAMPLE_RATES.index(newval))) + self.sendcmd(f"SRAT {VALID_SAMPLE_RATES.index(newval)}") else: raise ValueError( "Valid samples rates given by {} " @@ -264,8 +263,8 @@ def num_data_points(self): resp = self.query("SPTS?").strip() i += 1 if not resp: - raise IOError( - "Expected integer response from instrument, got {}".format(repr(resp)) + raise OSError( + f"Expected integer response from instrument, got {repr(resp)}" ) return int(resp) @@ -306,7 +305,7 @@ def auto_offset(self, mode): mode = self._XYR_MODE_MAP[mode] - self.sendcmd("AOFF {}".format(mode)) + self.sendcmd(f"AOFF {mode}") def auto_phase(self): """ @@ -385,7 +384,7 @@ def take_measurement(self, sample_rate, num_samples): # in future versions. try: self.num_data_points - except IOError: + except OSError: pass ch1 = self.read_data_buffer("ch1") @@ -436,7 +435,7 @@ def set_offset_expand(self, mode, offset, expand): else: raise ValueError("Expand must be 1, 10, 100.") - self.sendcmd("OEXP {},{},{}".format(mode, int(offset), expand)) + self.sendcmd(f"OEXP {mode},{int(offset)},{expand}") def start_scan(self): """ @@ -503,7 +502,7 @@ def data_snap(self, mode1, mode2): if mode1 == mode2: raise ValueError("Both parameters for the data snapshot are the " "same.") - result = self.query("SNAP? {},{}".format(mode1, mode2)) + result = self.query(f"SNAP? {mode1},{mode2}") return list(map(float, result.split(","))) _valid_read_data_buffer = {Mode.ch1: 1, Mode.ch2: 2} @@ -536,7 +535,7 @@ def read_data_buffer(self, channel): # Query device for entire buffer, returning in ASCII, then # converting to a list of floats before returning to the # calling method - data = self.query("TRCA?{},0,{}".format(channel, N)).strip() + data = self.query(f"TRCA?{channel},0,{N}").strip() if numpy: return numpy.fromstring(data, sep=",") return tuple(map(float, data.split(","))) @@ -609,4 +608,4 @@ def set_channel_display(self, channel, display, ratio): display = self._valid_channel_display[channel - 1][display] ratio = self._valid_channel_ratio[channel - 1][ratio] - self.sendcmd("DDEF {},{},{}".format(channel, display, ratio)) + self.sendcmd(f"DDEF {channel},{display},{ratio}") diff --git a/instruments/srs/srsctc100.py b/instruments/srs/srsctc100.py index 7cfa43077..8b0e8057f 100644 --- a/instruments/srs/srsctc100.py +++ b/instruments/srs/srsctc100.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the SRS CTC-100 cryogenic temperature controller. """ @@ -25,7 +24,7 @@ class SRSCTC100(SCPIInstrument): """ def __init__(self, filelike): - super(SRSCTC100, self).__init__(filelike) + super().__init__(filelike) self._do_errcheck = True # DICTIONARIES # @@ -73,10 +72,10 @@ def __init__(self, ctc, chan_name): # PRIVATE METHODS # def _get(self, prop_name): - return self._ctc.query("{}.{}?".format(self._rem_name, prop_name)).strip() + return self._ctc.query(f"{self._rem_name}.{prop_name}?").strip() def _set(self, prop_name, newval): - self._ctc.sendcmd('{}.{} = "{}"'.format(self._rem_name, prop_name, newval)) + self._ctc.sendcmd(f'{self._rem_name}.{prop_name} = "{newval}"') # DISPLAY AND PROGRAMMING # # These properties control how the channel is identified in scripts @@ -215,9 +214,9 @@ def get_log_point(self, which="next", units=None): point = [ s.strip() - for s in self._ctc.query( - "getLog.xy {}, {}".format(self._chan_name, which) - ).split(",") + for s in self._ctc.query(f"getLog.xy {self._chan_name}, {which}").split( + "," + ) ] return u.Quantity(float(point[0]), "ms"), u.Quantity(float(point[1]), units) @@ -236,7 +235,7 @@ def get_log(self): units = self.units # Find out how many points there are. - n_points = int(self._ctc.query("getLog.xy? {}".format(self._chan_name))) + n_points = int(self._ctc.query(f"getLog.xy? {self._chan_name}")) # Make an empty quantity that size for the times and for the channel # values. @@ -300,10 +299,10 @@ def channel_units(self): unit_strings = [ unit_str.strip() for unit_str in self.query("getOutput.units?").split(",") ] - return dict( - (chan_name, self._UNIT_NAMES[unit_str]) + return { + chan_name: self._UNIT_NAMES[unit_str] for chan_name, unit_str in zip(self._channel_names(), unit_strings) - ) + } def errcheck(self): """ @@ -313,13 +312,13 @@ def errcheck(self): :return: Nothing """ - errs = super(SRSCTC100, self).query("geterror?").strip() + errs = super().query("geterror?").strip() err_code, err_descript = errs.split(",") err_code = int(err_code) if err_code == 0: return err_code else: - raise IOError(err_descript.strip()) + raise OSError(err_descript.strip()) @contextmanager def _error_checking_disabled(self): @@ -364,7 +363,7 @@ def display_figures(self, newval): "Number of display figures must be an integer " "from 0 to 6, inclusive." ) - self.sendcmd("system.display.figures = {}".format(newval)) + self.sendcmd(f"system.display.figures = {newval}") @property def error_check_toggle(self): @@ -386,12 +385,12 @@ def error_check_toggle(self, newval): # We override sendcmd() and query() to do error checking after each # command. def sendcmd(self, cmd): - super(SRSCTC100, self).sendcmd(cmd) + super().sendcmd(cmd) if self._do_errcheck: self.errcheck() def query(self, cmd, size=-1): - resp = super(SRSCTC100, self).query(cmd, size) + resp = super().query(cmd, size) if self._do_errcheck: self.errcheck() return resp diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index 33678d27f..4b8ee2657 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the SRS DG645 digital delay generator. """ @@ -59,7 +58,7 @@ def delay(self): :units: Assume seconds if no units given. """ - resp = self._ddg.query("DLAY?{}".format(int(self._chan))).split(",") + resp = self._ddg.query(f"DLAY?{int(self._chan)}").split(",") return SRSDG645.Channels(int(resp[0])), u.Quantity(float(resp[1]), "s") @delay.setter @@ -90,7 +89,7 @@ class SRSDG645(SCPIInstrument): """ def __init__(self, filelike): - super(SRSDG645, self).__init__(filelike) + super().__init__(filelike) # This instrument requires stripping two characters. if isinstance(filelike, GPIBCommunicator): @@ -192,7 +191,7 @@ def polarity(self): :type: :class:`SRSDG645.LevelPolarity` """ return self._parent.LevelPolarity( - int(self._parent.query("LPOL? {}".format(self._idx))) + int(self._parent.query(f"LPOL? {self._idx}")) ) @polarity.setter @@ -203,7 +202,7 @@ def polarity(self, newval): "SRSDG645.LevelPolarity value, got {} " "instead.".format(type(newval)) ) - self._parent.sendcmd("LPOL {},{}".format(self._idx, int(newval.value))) + self._parent.sendcmd(f"LPOL {self._idx},{int(newval.value)}") @property def level_amplitude(self): @@ -213,14 +212,12 @@ def level_amplitude(self): :type: `float` or :class:`~pint.Quantity` :units: As specified, or :math:`\\text{V}` by default. """ - return u.Quantity( - float(self._parent.query("LAMP? {}".format(self._idx))), "V" - ) + return u.Quantity(float(self._parent.query(f"LAMP? {self._idx}")), "V") @level_amplitude.setter def level_amplitude(self, newval): newval = assume_units(newval, "V").magnitude - self._parent.sendcmd("LAMP {},{}".format(self._idx, newval)) + self._parent.sendcmd(f"LAMP {self._idx},{newval}") @property def level_offset(self): @@ -230,14 +227,12 @@ def level_offset(self): :type: `float` or :class:`~pint.Quantity` :units: As specified, or :math:`\\text{V}` by default. """ - return u.Quantity( - float(self._parent.query("LOFF? {}".format(self._idx))), "V" - ) + return u.Quantity(float(self._parent.query(f"LOFF? {self._idx}")), "V") @level_offset.setter def level_offset(self, newval): newval = assume_units(newval, "V").magnitude - self._parent.sendcmd("LOFF {},{}".format(self._idx, newval)) + self._parent.sendcmd(f"LOFF {self._idx},{newval}") # PROPERTIES # @@ -282,7 +277,7 @@ def display(self): @display.setter def display(self, newval): # TODO: check types here. - self.sendcmd("DISP {0},{1}".format(*map(int, newval))) + self.sendcmd("DISP {},{}".format(*map(int, newval))) @property def enable_adv_triggering(self): @@ -295,7 +290,7 @@ def enable_adv_triggering(self): @enable_adv_triggering.setter def enable_adv_triggering(self, newval): - self.sendcmd("ADVT {}".format(1 if newval else 0)) + self.sendcmd(f"ADVT {1 if newval else 0}") @property def trigger_rate(self): @@ -310,7 +305,7 @@ def trigger_rate(self): @trigger_rate.setter def trigger_rate(self, newval): newval = assume_units(newval, u.Hz) - self.sendcmd("TRAT {}".format(newval.to(u.Hz).magnitude)) + self.sendcmd(f"TRAT {newval.to(u.Hz).magnitude}") @property def trigger_source(self): @@ -323,7 +318,7 @@ def trigger_source(self): @trigger_source.setter def trigger_source(self, newval): - self.sendcmd("TSRC {}".format(int(newval))) + self.sendcmd(f"TSRC {int(newval)}") @property def holdoff(self): @@ -338,7 +333,7 @@ def holdoff(self): @holdoff.setter def holdoff(self, newval): newval = assume_units(newval, u.s) - self.sendcmd("HOLD {}".format(newval.to(u.s).magnitude)) + self.sendcmd(f"HOLD {newval.to(u.s).magnitude}") @property def enable_burst_mode(self): @@ -351,7 +346,7 @@ def enable_burst_mode(self): @enable_burst_mode.setter def enable_burst_mode(self, newval): - self.sendcmd("BURM {}".format(1 if newval else 0)) + self.sendcmd(f"BURM {1 if newval else 0}") @property def enable_burst_t0_first(self): @@ -367,7 +362,7 @@ def enable_burst_t0_first(self): @enable_burst_t0_first.setter def enable_burst_t0_first(self, newval): - self.sendcmd("BURT {}".format(1 if newval else 0)) + self.sendcmd(f"BURT {1 if newval else 0}") @property def burst_count(self): @@ -380,7 +375,7 @@ def burst_count(self): @burst_count.setter def burst_count(self, newval): - self.sendcmd("BURC {}".format(int(newval))) + self.sendcmd(f"BURC {int(newval)}") @property def burst_period(self): @@ -396,7 +391,7 @@ def burst_period(self): @burst_period.setter def burst_period(self, newval): newval = assume_units(newval, u.sec) - self.sendcmd("BURP {}".format(newval.to(u.sec).magnitude)) + self.sendcmd(f"BURP {newval.to(u.sec).magnitude}") @property def burst_delay(self): @@ -413,4 +408,4 @@ def burst_delay(self): @burst_delay.setter def burst_delay(self, newval): newval = assume_units(newval, u.s) - self.sendcmd("BURD {}".format(newval.to(u.sec).magnitude)) + self.sendcmd(f"BURD {newval.to(u.sec).magnitude}") diff --git a/instruments/tektronix/__init__.py b/instruments/tektronix/__init__.py index 4aff3f5fd..49f45500e 100644 --- a/instruments/tektronix/__init__.py +++ b/instruments/tektronix/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Tektronix instruments """ diff --git a/instruments/tektronix/tekawg2000.py b/instruments/tektronix/tekawg2000.py index 77ac24447..d06263243 100644 --- a/instruments/tektronix/tekawg2000.py +++ b/instruments/tektronix/tekawg2000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Tektronix AWG2000 series arbitrary wave generators. """ @@ -38,7 +37,7 @@ def __init__(self, tek, idx): self._tek = tek # Zero-based for pythonic convienence, so we need to convert to # Tektronix's one-based notation here. - self._name = "CH{}".format(idx + 1) + self._name = f"CH{idx + 1}" # Remember what the old data source was for use as a context manager self._old_dsrc = None @@ -64,7 +63,7 @@ def amplitude(self): :type: `~pint.Quantity` with units Volts peak-to-peak. """ return u.Quantity( - float(self._tek.query("FG:{}:AMPL?".format(self._name)).strip()), u.V + float(self._tek.query(f"FG:{self._name}:AMPL?").strip()), u.V ) @amplitude.setter @@ -85,7 +84,7 @@ def offset(self): :type: `~pint.Quantity` with units Volts. """ return u.Quantity( - float(self._tek.query("FG:{}:OFFS?".format(self._name)).strip()), u.V + float(self._tek.query(f"FG:{self._name}:OFFS?").strip()), u.V ) @offset.setter @@ -111,7 +110,7 @@ def frequency(self): @frequency.setter def frequency(self, newval): self._tek.sendcmd( - "FG:FREQ {}HZ".format(assume_units(newval, u.Hz).to(u.Hz).magnitude) + f"FG:FREQ {assume_units(newval, u.Hz).to(u.Hz).magnitude}HZ" ) @property @@ -121,9 +120,7 @@ def polarity(self): :type: `TekAWG2000.Polarity` """ - return TekAWG2000.Polarity( - self._tek.query("FG:{}:POL?".format(self._name)).strip() - ) + return TekAWG2000.Polarity(self._tek.query(f"FG:{self._name}:POL?").strip()) @polarity.setter def polarity(self, newval): @@ -134,7 +131,7 @@ def polarity(self, newval): "instead.".format(type(newval)) ) - self._tek.sendcmd("FG:{}:POL {}".format(self._name, newval.value)) + self._tek.sendcmd(f"FG:{self._name}:POL {newval.value}") @property def shape(self): @@ -145,7 +142,7 @@ def shape(self): :type: `TekAWG2000.Shape` """ return TekAWG2000.Shape( - self._tek.query("FG:{}:SHAP?".format(self._name)).strip().split(",")[0] + self._tek.query(f"FG:{self._name}:SHAP?").strip().split(",")[0] ) @shape.setter @@ -155,7 +152,7 @@ def shape(self, newval): "Shape settings must be a `TekAWG2000.Shape` " "value, got {} instead.".format(type(newval)) ) - self._tek.sendcmd("FG:{}:SHAP {}".format(self._name, newval.value)) + self._tek.sendcmd(f"FG:{self._name}:SHAP {newval.value}") # ENUMS # @@ -196,7 +193,7 @@ def waveform_name(self): def waveform_name(self, newval): if not isinstance(newval, str): raise TypeError("Waveform name must be specified as a string.") - self.sendcmd('DATA:DEST "{}"'.format(newval)) + self.sendcmd(f'DATA:DEST "{newval}"') @property def channel(self): @@ -256,15 +253,15 @@ def upload_waveform(self, yzero, ymult, xincr, waveform): if numpy.max(numpy.abs(waveform)) > 1: raise ValueError("The max value for an element in waveform is 1.") - self.sendcmd("WFMP:YZERO {}".format(yzero)) - self.sendcmd("WFMP:YMULT {}".format(ymult)) - self.sendcmd("WFMP:XINCR {}".format(xincr)) + self.sendcmd(f"WFMP:YZERO {yzero}") + self.sendcmd(f"WFMP:YMULT {ymult}") + self.sendcmd(f"WFMP:XINCR {xincr}") waveform *= 2 ** 12 - 1 waveform = waveform.astype(" for SCPI like behavior self.sendcmd("COMM_HEADER OFF") @@ -188,8 +187,8 @@ def _create_trigger_source_enum(self): values = ["EX", "EX5", "EX10", "ETM10", "LINE"] # now add the channels for it in range(self._number_channels): - names.append("c{}".format(it)) - values.append("C{}".format(it + 1)) # to send to scope + names.append(f"c{it}") + values.append(f"C{it + 1}") # to send to scope # create and store the enum self.TriggerSource = Enum("TriggerSource", zip(names, values)) @@ -332,7 +331,7 @@ def __init__(self, parent, idx): self._idx = idx + 1 # 1-based # Initialize as a data source with name C{}. - super(MAUI.Channel, self).__init__(self._parent, "C{}".format(self._idx)) + super(MAUI.Channel, self).__init__(self._parent, f"C{self._idx}") # ENUMS # @@ -383,7 +382,7 @@ def offset(self): @offset.setter def offset(self, newval): newval = assume_units(newval, "V").to(u.V).magnitude - self.sendcmd("OFST {}".format(newval)) + self.sendcmd(f"OFST {newval}") @property def scale(self): @@ -402,7 +401,7 @@ def scale(self): @scale.setter def scale(self, newval): newval = assume_units(newval, "V").to(u.V).magnitude - self.sendcmd("VDIV {}".format(newval)) + self.sendcmd(f"VDIV {newval}") # METHODS # @@ -413,7 +412,7 @@ def sendcmd(self, cmd): :param str cmd: Command to send to the instrument """ - self._parent.sendcmd("C{}:{}".format(self._idx, cmd)) + self._parent.sendcmd(f"C{self._idx}:{cmd}") def query(self, cmd, size=-1): """ @@ -429,7 +428,7 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - return self._parent.query("C{}:{}".format(self._idx, cmd), size=size) + return self._parent.query(f"C{self._idx}:{cmd}", size=size) class Math(DataSource): @@ -445,7 +444,7 @@ def __init__(self, parent, idx): self._idx = idx + 1 # 1-based # Initialize as a data source with name C{}. - super(MAUI.Math, self).__init__(self._parent, "F{}".format(self._idx)) + super(MAUI.Math, self).__init__(self._parent, f"F{self._idx}") # CLASSES # @@ -492,7 +491,7 @@ def absolute(self, src): :param int,tuple src: Source, see info above """ src_str = _source(src) - send_str = "'ABS({})'".format(src_str) + send_str = f"'ABS({src_str})'" self._send_operator(send_str) def average(self, src, average_type="summed", sweeps=1000): @@ -598,7 +597,7 @@ def eres(self, src, bits=0.5): if bits not in bits_possible: bits = 0.5 - send_str = "'ERES({})',BITS,{}".format(src_str, bits) + send_str = f"'ERES({src_str})',BITS,{bits}" self._send_operator(send_str) @@ -696,7 +695,7 @@ def invert(self, src): :param int,tuple src: Source, see info above """ src_str = _source(src) - self._send_operator("'-{}'".format(src_str)) + self._send_operator(f"'-{src_str}'") def product(self, src1, src2): """ @@ -708,7 +707,7 @@ def product(self, src1, src2): src1_str = _source(src1) src2_str = _source(src2) - send_str = "'{}*{}'".format(src1_str, src2_str) + send_str = f"'{src1_str}*{src2_str}'" self._send_operator(send_str) @@ -722,7 +721,7 @@ def ratio(self, src1, src2): src1_str = _source(src1) src2_str = _source(src2) - send_str = "'{}/{}'".format(src1_str, src2_str) + send_str = f"'{src1_str}/{src2_str}'" self._send_operator(send_str) @@ -733,7 +732,7 @@ def reciprocal(self, src): :param int,tuple src: Source, see info above """ src_str = _source(src) - self._send_operator("'1/{}'".format(src_str)) + self._send_operator(f"'1/{src_str}'") def rescale(self, src, multiplier=1, adder=0): """ @@ -761,7 +760,7 @@ def sinx(self, src): :param int,tuple src: Source, see info above """ src_str = _source(src) - self._send_operator("'SINX({})'".format(src_str)) + self._send_operator(f"'SINX({src_str})'") def square(self, src): """ @@ -770,7 +769,7 @@ def square(self, src): :param int,tuple src: Source, see info above """ src_str = _source(src) - self._send_operator("'SQR({})'".format(src_str)) + self._send_operator(f"'SQR({src_str})'") def square_root(self, src): """ @@ -779,7 +778,7 @@ def square_root(self, src): :param int,tuple src: Source, see info above """ src_str = _source(src) - self._send_operator("'SQRT({})'".format(src_str)) + self._send_operator(f"'SQRT({src_str})'") def sum(self, src1, src2): """ @@ -791,7 +790,7 @@ def sum(self, src1, src2): src1_str = _source(src1) src2_str = _source(src2) - send_str = "'{}+{}'".format(src1_str, src2_str) + send_str = f"'{src1_str}+{src2_str}'" self._send_operator(send_str) @@ -872,7 +871,7 @@ def sendcmd(self, cmd): :param str cmd: Command to send to the instrument """ - self._parent.sendcmd("F{}:{}".format(self._idx, cmd)) + self._parent.sendcmd(f"F{self._idx}:{cmd}") def query(self, cmd, size=-1): """ @@ -888,7 +887,7 @@ def query(self, cmd, size=-1): connected instrument. :rtype: `str` """ - return self._parent.query("F{}:{}".format(self._idx, cmd), size=size) + return self._parent.query(f"F{self._idx}:{cmd}", size=size) class Measurement: @@ -943,9 +942,7 @@ def statistics(self): :return tuple: (average, low, high, sigma, sweeps) :return type: (float, float, float, float, float) """ - ret_str = ( - self.query("PAST? CUST, P{}".format(self._idx)).rstrip().split(",") - ) + ret_str = self.query(f"PAST? CUST, P{self._idx}").rstrip().split(",") # parse the return string -> put into dictionary: ret_dict = { ret_str[it]: ret_str[it + 1] for it in range(0, len(ret_str), 2) @@ -973,7 +970,7 @@ def delete(self): """ Deletes the given measurement parameter. """ - self.sendcmd("PADL {}".format(self._idx)) + self.sendcmd(f"PADL {self._idx}") def set_parameter(self, param, src): """ @@ -1005,7 +1002,7 @@ def set_parameter(self, param, src): ) ) - send_str = "PACU {},{},{}".format(self._idx, param.value, _source(src)) + send_str = f"PACU {self._idx},{param.value},{_source(src)}" self.sendcmd(send_str) @@ -1216,7 +1213,7 @@ def time_div(self): @time_div.setter def time_div(self, newval): newval = assume_units(newval, "s").to(u.s).magnitude - self.sendcmd("TDIV {}".format(newval)) + self.sendcmd(f"TDIV {newval}") # TRIGGER PROPERTIES @@ -1253,7 +1250,7 @@ def trigger_delay(self): @trigger_delay.setter def trigger_delay(self, newval): newval = assume_units(newval, "s").to(u.s).magnitude - self.sendcmd("TRDL {}".format(newval)) + self.sendcmd(f"TRDL {newval}") @property def trigger_source(self): @@ -1286,7 +1283,7 @@ def trigger_source(self): @trigger_source.setter def trigger_source(self, newval): curr_trig_typ = self.trigger_type - cmd = "TRIG_SELECT {},SR,{}".format(curr_trig_typ.value, newval.value) + cmd = f"TRIG_SELECT {curr_trig_typ.value},SR,{newval.value}" self.sendcmd(cmd) @property @@ -1315,7 +1312,7 @@ def trigger_type(self): @trigger_type.setter def trigger_type(self, newval): curr_trig_src = self.trigger_source - cmd = "TRIG_SELECT {},SR,{}".format(newval.value, curr_trig_src.value) + cmd = f"TRIG_SELECT {newval.value},SR,{curr_trig_src.value}" self.sendcmd(cmd) # METHODS # @@ -1371,9 +1368,9 @@ def stop(self): def _source(src): """Stich the source together properly and return it.""" if isinstance(src, int): - return "C{}".format(src + 1) + return f"C{src + 1}" elif isinstance(src, tuple) and len(src) == 2: - return "{}{}".format(src[0].upper(), int(src[1]) + 1) + return f"{src[0].upper()}{int(src[1]) + 1}" else: raise ValueError( "An invalid source was specified. " diff --git a/instruments/tests/__init__.py b/instruments/tests/__init__.py index c833e1af1..d33a3eb7d 100644 --- a/instruments/tests/__init__.py +++ b/instruments/tests/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing InstrumentKit unit tests diff --git a/instruments/tests/test_abstract_inst/test_electrometer.py b/instruments/tests/test_abstract_inst/test_electrometer.py index 2071316a6..5201450c9 100644 --- a/instruments/tests/test_abstract_inst/test_electrometer.py +++ b/instruments/tests/test_abstract_inst/test_electrometer.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract electrometer class """ diff --git a/instruments/tests/test_abstract_inst/test_function_generator.py b/instruments/tests/test_abstract_inst/test_function_generator.py index e0da86bd0..83ac8373a 100644 --- a/instruments/tests/test_abstract_inst/test_function_generator.py +++ b/instruments/tests/test_abstract_inst/test_function_generator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract function generator class """ diff --git a/instruments/tests/test_abstract_inst/test_multimeter.py b/instruments/tests/test_abstract_inst/test_multimeter.py index cd07d9b3d..3fe6299af 100644 --- a/instruments/tests/test_abstract_inst/test_multimeter.py +++ b/instruments/tests/test_abstract_inst/test_multimeter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract multimeter class """ diff --git a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py index 1ced09d46..ef28c9808 100644 --- a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py +++ b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract optical spectrum analyzer class """ diff --git a/instruments/tests/test_abstract_inst/test_oscilloscope.py b/instruments/tests/test_abstract_inst/test_oscilloscope.py index 24d9072b4..055628abb 100644 --- a/instruments/tests/test_abstract_inst/test_oscilloscope.py +++ b/instruments/tests/test_abstract_inst/test_oscilloscope.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract oscilloscope class """ diff --git a/instruments/tests/test_abstract_inst/test_power_supply.py b/instruments/tests/test_abstract_inst/test_power_supply.py index cda524c67..119991634 100644 --- a/instruments/tests/test_abstract_inst/test_power_supply.py +++ b/instruments/tests/test_abstract_inst/test_power_supply.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract power supply class """ diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py index 0d943b793..d348b03e7 100644 --- a/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_channel.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract signal generator channel class """ diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py index 926057609..fd25fca12 100644 --- a/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_signal_generator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract signal generator class """ diff --git a/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py b/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py index f2ee7074e..6f180ffa4 100644 --- a/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py +++ b/instruments/tests/test_abstract_inst/test_signal_generator/test_single_channel_sg.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the abstract signal generator class """ diff --git a/instruments/tests/test_agilent/test_agilent_33220a.py b/instruments/tests/test_agilent/test_agilent_33220a.py index e1dbc1dad..d042d4585 100644 --- a/instruments/tests/test_agilent/test_agilent_33220a.py +++ b/instruments/tests/test_agilent/test_agilent_33220a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for generic SCPI function generator instruments """ diff --git a/instruments/tests/test_agilent/test_agilent_34410a.py b/instruments/tests/test_agilent/test_agilent_34410a.py index cbce74918..4c7b1f205 100644 --- a/instruments/tests/test_agilent/test_agilent_34410a.py +++ b/instruments/tests/test_agilent/test_agilent_34410a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for Agilent 34410a """ diff --git a/instruments/tests/test_base_instrument.py b/instruments/tests/test_base_instrument.py index 1642d2124..c3ddbafa2 100644 --- a/instruments/tests/test_base_instrument.py +++ b/instruments/tests/test_base_instrument.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the base Instrument class """ diff --git a/instruments/tests/test_comm/test_file.py b/instruments/tests/test_comm/test_file.py index 380db641f..d35d8012b 100644 --- a/instruments/tests/test_comm/test_file.py +++ b/instruments/tests/test_comm/test_file.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the file communication layer """ diff --git a/instruments/tests/test_comm/test_gpibusb.py b/instruments/tests/test_comm/test_gpibusb.py index 6c164d06b..e699b6bba 100644 --- a/instruments/tests/test_comm/test_gpibusb.py +++ b/instruments/tests/test_comm/test_gpibusb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the GPIBUSB communication layer """ diff --git a/instruments/tests/test_comm/test_loopback.py b/instruments/tests/test_comm/test_loopback.py index 9386e415d..0aa9ab46f 100644 --- a/instruments/tests/test_comm/test_loopback.py +++ b/instruments/tests/test_comm/test_loopback.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the loopback communication layer """ @@ -47,9 +46,9 @@ def test_loopbackcomm_terminator(): assert comm.terminator == "*" assert comm._terminator == "*" - comm.terminator = u"\r" - assert comm.terminator == u"\r" - assert comm._terminator == u"\r" + comm.terminator = "\r" + assert comm.terminator == "\r" + assert comm._terminator == "\r" comm.terminator = "\r\n" assert comm.terminator == "\r\n" diff --git a/instruments/tests/test_comm/test_serial.py b/instruments/tests/test_comm/test_serial.py index 5ce660401..ab7335b72 100644 --- a/instruments/tests/test_comm/test_serial.py +++ b/instruments/tests/test_comm/test_serial.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the serial communication layer """ diff --git a/instruments/tests/test_comm/test_socket.py b/instruments/tests/test_comm/test_socket.py index 79a0795ef..ba03da89a 100644 --- a/instruments/tests/test_comm/test_socket.py +++ b/instruments/tests/test_comm/test_socket.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the socket communication layer """ @@ -58,9 +57,9 @@ def test_socketcomm_terminator(): assert comm.terminator == "*" assert comm._terminator == "*" - comm.terminator = u"\r" - assert comm.terminator == u"\r" - assert comm._terminator == u"\r" + comm.terminator = "\r" + assert comm.terminator == "\r" + assert comm._terminator == "\r" comm.terminator = "\r\n" assert comm.terminator == "\r\n" diff --git a/instruments/tests/test_comm/test_usb_communicator.py b/instruments/tests/test_comm/test_usb_communicator.py index 5404e522a..1c962f4f9 100644 --- a/instruments/tests/test_comm/test_usb_communicator.py +++ b/instruments/tests/test_comm/test_usb_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the USB communicator. """ diff --git a/instruments/tests/test_comm/test_usbtmc.py b/instruments/tests/test_comm/test_usbtmc.py index 2b3c9ab5d..5a5533b3a 100644 --- a/instruments/tests/test_comm/test_usbtmc.py +++ b/instruments/tests/test_comm/test_usbtmc.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the USBTMC communication layer """ diff --git a/instruments/tests/test_comm/test_visa_communicator.py b/instruments/tests/test_comm/test_visa_communicator.py index c45ea2a57..94a13faab 100644 --- a/instruments/tests/test_comm/test_visa_communicator.py +++ b/instruments/tests/test_comm/test_visa_communicator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the VISA communication layer """ diff --git a/instruments/tests/test_comm/test_vxi11.py b/instruments/tests/test_comm/test_vxi11.py index aed550ca5..c73bb37ab 100644 --- a/instruments/tests/test_comm/test_vxi11.py +++ b/instruments/tests/test_comm/test_vxi11.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the VXI11 communication layer """ diff --git a/instruments/tests/test_config.py b/instruments/tests/test_config.py index ca8c1bac0..a4f0c7be0 100644 --- a/instruments/tests/test_config.py +++ b/instruments/tests/test_config.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for util_fns.py """ @@ -21,7 +20,7 @@ def test_load_test_instrument(): config_data = StringIO( - u""" + """ test: class: !!python/name:instruments.Instrument uri: test:// @@ -33,7 +32,7 @@ def test_load_test_instrument(): def test_load_test_instrument_subtree(): config_data = StringIO( - u""" + """ instruments: test: class: !!python/name:instruments.Instrument @@ -46,7 +45,7 @@ def test_load_test_instrument_subtree(): def test_yaml_quantity_tag(): yaml_data = StringIO( - u""" + """ a: b: !Q 37 tesla c: !Q 41.2 inches @@ -61,7 +60,7 @@ def test_yaml_quantity_tag(): def test_load_test_instrument_setattr(): config_data = StringIO( - u""" + """ test: class: !!python/name:instruments.Instrument uri: test:// diff --git a/instruments/tests/test_fluke/test_fluke3000.py b/instruments/tests/test_fluke/test_fluke3000.py index e2d00a681..bf656ebe2 100644 --- a/instruments/tests/test_fluke/test_fluke3000.py +++ b/instruments/tests/test_fluke/test_fluke3000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Fluke 3000 FC multimeter """ diff --git a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py index c5d1bec91..dc9fecd15 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_function_generator.py +++ b/instruments/tests/test_generic_scpi/test_scpi_function_generator.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for generic SCPI function generator instruments """ diff --git a/instruments/tests/test_generic_scpi/test_scpi_instrument.py b/instruments/tests/test_generic_scpi/test_scpi_instrument.py index 64edb012c..78a0df00e 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_instrument.py +++ b/instruments/tests/test_generic_scpi/test_scpi_instrument.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for generic SCPI instruments """ diff --git a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py index 8db6cd8cc..85bc1d023 100644 --- a/instruments/tests/test_generic_scpi/test_scpi_multimeter.py +++ b/instruments/tests/test_generic_scpi/test_scpi_multimeter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for generic SCPI multimeter instruments """ diff --git a/instruments/tests/test_gentec_eo/test_blu.py b/instruments/tests/test_gentec_eo/test_blu.py index 7c0426126..04d6e5bc7 100644 --- a/instruments/tests/test_gentec_eo/test_blu.py +++ b/instruments/tests/test_gentec_eo/test_blu.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Gentec-eo Blu """ diff --git a/instruments/tests/test_glassman/test_glassmanfr.py b/instruments/tests/test_glassman/test_glassmanfr.py index a402294ca..9d7e60e91 100644 --- a/instruments/tests/test_glassman/test_glassmanfr.py +++ b/instruments/tests/test_glassman/test_glassmanfr.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Glassman FR power supply """ diff --git a/instruments/tests/test_holzworth/test_holzworth_hs9000.py b/instruments/tests/test_holzworth/test_holzworth_hs9000.py index a6160479f..ce5684cae 100644 --- a/instruments/tests/test_holzworth/test_holzworth_hs9000.py +++ b/instruments/tests/test_holzworth/test_holzworth_hs9000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Holzworth HS9000 """ @@ -120,7 +119,7 @@ def test_channel_frequency_getter(): def test_channel_frequency_setter(): with expected_protocol( ik.holzworth.HS9000, - [":ATTACH?", ":CH1:FREQ:MIN?", ":CH1:FREQ:MAX?", ":CH1:FREQ {:e}".format(1)], + [":ATTACH?", ":CH1:FREQ:MIN?", ":CH1:FREQ:MAX?", f":CH1:FREQ {1:e}"], [":CH1:CH2:FOO", "100 MHz", "10 GHz"], sep="\n", ) as hs: @@ -144,7 +143,7 @@ def test_channel_power_getter(): def test_channel_power_setter(): with expected_protocol( ik.holzworth.HS9000, - [":ATTACH?", ":CH1:PWR:MIN?", ":CH1:PWR:MAX?", ":CH1:PWR {:e}".format(0)], + [":ATTACH?", ":CH1:PWR:MIN?", ":CH1:PWR:MAX?", f":CH1:PWR {0:e}"], [":CH1:CH2:FOO", "-100", "20"], sep="\n", ) as hs: @@ -168,7 +167,7 @@ def test_channel_phase_getter(): def test_channel_phase_setter(): with expected_protocol( ik.holzworth.HS9000, - [":ATTACH?", ":CH1:PHASE:MIN?", ":CH1:PHASE:MAX?", ":CH1:PHASE {:e}".format(0)], + [":ATTACH?", ":CH1:PHASE:MIN?", ":CH1:PHASE:MAX?", f":CH1:PHASE {0:e}"], [":CH1:CH2:FOO", "-180", "+180"], sep="\n", ) as hs: diff --git a/instruments/tests/test_hp/test_hp3456a.py b/instruments/tests/test_hp/test_hp3456a.py index 2bcc628bf..302ef0f39 100644 --- a/instruments/tests/test_hp/test_hp3456a.py +++ b/instruments/tests/test_hp/test_hp3456a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the HP 3456a digital voltmeter """ diff --git a/instruments/tests/test_hp/test_hp6624a.py b/instruments/tests/test_hp/test_hp6624a.py index 7a48d6ddf..3e4539519 100644 --- a/instruments/tests/test_hp/test_hp6624a.py +++ b/instruments/tests/test_hp/test_hp6624a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the HP 6624a power supply """ @@ -66,7 +65,7 @@ def test_mode(): def test_channel_voltage(): with expected_protocol( - ik.hp.HP6624a, ["VSET? 1", "VSET 1,{:.1f}".format(5)], ["2"], sep="\n" + ik.hp.HP6624a, ["VSET? 1", f"VSET 1,{5:.1f}"], ["2"], sep="\n" ) as hp: assert hp.channel[0].voltage == 2 * u.V hp.channel[0].voltage = 5 * u.V @@ -74,7 +73,7 @@ def test_channel_voltage(): def test_channel_current(): with expected_protocol( - ik.hp.HP6624a, ["ISET? 1", "ISET 1,{:.1f}".format(5)], ["2"], sep="\n" + ik.hp.HP6624a, ["ISET? 1", f"ISET 1,{5:.1f}"], ["2"], sep="\n" ) as hp: assert hp.channel[0].current == 2 * u.amp hp.channel[0].current = 5 * u.amp @@ -133,14 +132,14 @@ def test_all_voltage(): "VSET? 2", "VSET? 3", "VSET? 4", - "VSET 1,{:.1f}".format(5), - "VSET 2,{:.1f}".format(5), - "VSET 3,{:.1f}".format(5), - "VSET 4,{:.1f}".format(5), - "VSET 1,{:.1f}".format(1), - "VSET 2,{:.1f}".format(2), - "VSET 3,{:.1f}".format(3), - "VSET 4,{:.1f}".format(4), + f"VSET 1,{5:.1f}", + f"VSET 2,{5:.1f}", + f"VSET 3,{5:.1f}", + f"VSET 4,{5:.1f}", + f"VSET 1,{1:.1f}", + f"VSET 2,{2:.1f}", + f"VSET 3,{3:.1f}", + f"VSET 4,{4:.1f}", ], ["2", "3", "4", "5"], sep="\n", @@ -166,14 +165,14 @@ def test_all_current(): "ISET? 2", "ISET? 3", "ISET? 4", - "ISET 1,{:.1f}".format(5), - "ISET 2,{:.1f}".format(5), - "ISET 3,{:.1f}".format(5), - "ISET 4,{:.1f}".format(5), - "ISET 1,{:.1f}".format(1), - "ISET 2,{:.1f}".format(2), - "ISET 3,{:.1f}".format(3), - "ISET 4,{:.1f}".format(4), + f"ISET 1,{5:.1f}", + f"ISET 2,{5:.1f}", + f"ISET 3,{5:.1f}", + f"ISET 4,{5:.1f}", + f"ISET 1,{1:.1f}", + f"ISET 2,{2:.1f}", + f"ISET 3,{3:.1f}", + f"ISET 4,{4:.1f}", ], ["2", "3", "4", "5"], sep="\n", diff --git a/instruments/tests/test_hp/test_hp6632b.py b/instruments/tests/test_hp/test_hp6632b.py index 08957c56a..7921e5509 100644 --- a/instruments/tests/test_hp/test_hp6632b.py +++ b/instruments/tests/test_hp/test_hp6632b.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the HP 6632b power supply """ @@ -41,9 +40,7 @@ def test_hp6632b_output(): def test_hp6632b_voltage(): - with expected_protocol( - ik.hp.HP6632b, ["VOLT?", "VOLT {:e}".format(1)], ["10.0"] - ) as psu: + with expected_protocol(ik.hp.HP6632b, ["VOLT?", f"VOLT {1:e}"], ["10.0"]) as psu: unit_eq(psu.voltage, 10 * u.volt) psu.voltage = 1.0 * u.volt @@ -61,16 +58,14 @@ def test_hp6632b_voltage_sense(): def test_hp6632b_overvoltage(): with expected_protocol( - ik.hp.HP6632b, ["VOLT:PROT?", "VOLT:PROT {:e}".format(1)], ["10.0"] + ik.hp.HP6632b, ["VOLT:PROT?", f"VOLT:PROT {1:e}"], ["10.0"] ) as psu: unit_eq(psu.overvoltage, 10 * u.volt) psu.overvoltage = 1.0 * u.volt def test_hp6632b_current(): - with expected_protocol( - ik.hp.HP6632b, ["CURR?", "CURR {:e}".format(1)], ["10.0"] - ) as psu: + with expected_protocol(ik.hp.HP6632b, ["CURR?", f"CURR {1:e}"], ["10.0"]) as psu: unit_eq(psu.current, 10 * u.amp) psu.current = 1.0 * u.amp @@ -96,7 +91,7 @@ def test_hp6632b_overcurrent(): def test_hp6632b_current_sense_range(): with expected_protocol( - ik.hp.HP6632b, ["SENS:CURR:RANGE?", "SENS:CURR:RANGE {:e}".format(1)], ["0.05"] + ik.hp.HP6632b, ["SENS:CURR:RANGE?", f"SENS:CURR:RANGE {1:e}"], ["0.05"] ) as psu: unit_eq(psu.current_sense_range, 0.05 * u.amp) psu.current_sense_range = 1 * u.amp @@ -134,7 +129,7 @@ def test_hp6632b_digital_data(): def test_hp6632b_sense_sweep_points(): with expected_protocol( - ik.hp.HP6632b, ["SENS:SWE:POIN?", "SENS:SWE:POIN {:e}".format(2048)], ["5"] + ik.hp.HP6632b, ["SENS:SWE:POIN?", f"SENS:SWE:POIN {2048:e}"], ["5"] ) as psu: assert psu.sense_sweep_points == 5 psu.sense_sweep_points = 2048 @@ -143,7 +138,7 @@ def test_hp6632b_sense_sweep_points(): def test_hp6632b_sense_sweep_interval(): with expected_protocol( ik.hp.HP6632b, - ["SENS:SWE:TINT?", "SENS:SWE:TINT {:e}".format(1e-05)], + ["SENS:SWE:TINT?", f"SENS:SWE:TINT {1e-05:e}"], ["1.56e-05"], ) as psu: unit_eq(psu.sense_sweep_interval, 1.56e-05 * u.second) @@ -160,7 +155,7 @@ def test_hp6632b_sense_window(): def test_hp6632b_output_protection_delay(): with expected_protocol( - ik.hp.HP6632b, ["OUTP:PROT:DEL?", "OUTP:PROT:DEL {:e}".format(5e-02)], ["8e-02"] + ik.hp.HP6632b, ["OUTP:PROT:DEL?", f"OUTP:PROT:DEL {5e-02:e}"], ["8e-02"] ) as psu: unit_eq(psu.output_protection_delay, 8e-02 * u.second) psu.output_protection_delay = 5e-02 * u.second @@ -179,7 +174,7 @@ def test_hp6632b_voltage_alc_bandwidth(): def test_hp6632b_voltage_trigger(): with expected_protocol( - ik.hp.HP6632b, ["VOLT:TRIG?", "VOLT:TRIG {:e}".format(1)], ["1e+0"] + ik.hp.HP6632b, ["VOLT:TRIG?", f"VOLT:TRIG {1:e}"], ["1e+0"] ) as psu: unit_eq(psu.voltage_trigger, 1 * u.volt) psu.voltage_trigger = 1 * u.volt @@ -187,7 +182,7 @@ def test_hp6632b_voltage_trigger(): def test_hp6632b_current_trigger(): with expected_protocol( - ik.hp.HP6632b, ["CURR:TRIG?", "CURR:TRIG {:e}".format(0.1)], ["1e-01"] + ik.hp.HP6632b, ["CURR:TRIG?", f"CURR:TRIG {0.1:e}"], ["1e-01"] ) as psu: unit_eq(psu.current_trigger, 0.1 * u.amp) psu.current_trigger = 0.1 * u.amp diff --git a/instruments/tests/test_hp/test_hp6652a.py b/instruments/tests/test_hp/test_hp6652a.py index ec5b37c38..7b509ef80 100644 --- a/instruments/tests/test_hp/test_hp6652a.py +++ b/instruments/tests/test_hp/test_hp6652a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the HP 6652a single output power supply """ diff --git a/instruments/tests/test_hp/test_hpe3631a.py b/instruments/tests/test_hp/test_hpe3631a.py index cf2c55a22..99a9f11b5 100644 --- a/instruments/tests/test_hp/test_hpe3631a.py +++ b/instruments/tests/test_hp/test_hpe3631a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the HP E3631A power supply """ diff --git a/instruments/tests/test_keithley/test_keithley195.py b/instruments/tests/test_keithley/test_keithley195.py index df4df17c1..01e2adac8 100644 --- a/instruments/tests/test_keithley/test_keithley195.py +++ b/instruments/tests/test_keithley/test_keithley195.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Keithley 195 digital multimeter. """ diff --git a/instruments/tests/test_keithley/test_keithley2182.py b/instruments/tests/test_keithley/test_keithley2182.py index ddb8d8e14..6400ae8a5 100644 --- a/instruments/tests/test_keithley/test_keithley2182.py +++ b/instruments/tests/test_keithley/test_keithley2182.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Keithley 2182 nano-voltmeter """ diff --git a/instruments/tests/test_keithley/test_keithley485.py b/instruments/tests/test_keithley/test_keithley485.py index fe2f33f75..b1841bda7 100644 --- a/instruments/tests/test_keithley/test_keithley485.py +++ b/instruments/tests/test_keithley/test_keithley485.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Keithley 485 picoammeter """ diff --git a/instruments/tests/test_keithley/test_keithley580.py b/instruments/tests/test_keithley/test_keithley580.py index 02ba8b3ef..1d6047827 100644 --- a/instruments/tests/test_keithley/test_keithley580.py +++ b/instruments/tests/test_keithley/test_keithley580.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Keithley 580 digital multimeter. """ diff --git a/instruments/tests/test_keithley/test_keithley6220.py b/instruments/tests/test_keithley/test_keithley6220.py index 6d3e7ba9f..8829f353f 100644 --- a/instruments/tests/test_keithley/test_keithley6220.py +++ b/instruments/tests/test_keithley/test_keithley6220.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Keithley 6220 constant current supply """ @@ -38,7 +37,7 @@ def test_voltage(): def test_current(): with expected_protocol( ik.keithley.Keithley6220, - ["SOUR:CURR?", "SOUR:CURR {:e}".format(0.05)], + ["SOUR:CURR?", f"SOUR:CURR {0.05:e}"], [ "0.1", ], diff --git a/instruments/tests/test_keithley/test_keithley6514.py b/instruments/tests/test_keithley/test_keithley6514.py index 88e078040..e5f4b5ccc 100644 --- a/instruments/tests/test_keithley/test_keithley6514.py +++ b/instruments/tests/test_keithley/test_keithley6514.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Keithley 6514 electrometer """ @@ -114,7 +113,7 @@ def test_input_range(): "FUNCTION?", "VOLT:DC:RANGE:UPPER?", "FUNCTION?", - "VOLT:DC:RANGE:UPPER {:e}".format(20), + f"VOLT:DC:RANGE:UPPER {20:e}", ], ['"VOLT:DC"', "10", '"VOLT:DC"'], ) as inst: diff --git a/instruments/tests/test_lakeshore/test_lakeshore340.py b/instruments/tests/test_lakeshore/test_lakeshore340.py index ab28be9e1..976c8770a 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore340.py +++ b/instruments/tests/test_lakeshore/test_lakeshore340.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Lakeshore 340 """ diff --git a/instruments/tests/test_lakeshore/test_lakeshore370.py b/instruments/tests/test_lakeshore/test_lakeshore370.py index 2f1099e2a..e24ecf488 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore370.py +++ b/instruments/tests/test_lakeshore/test_lakeshore370.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Lakeshore 370 """ diff --git a/instruments/tests/test_lakeshore/test_lakeshore475.py b/instruments/tests/test_lakeshore/test_lakeshore475.py index ae4b758a7..7a8a40c11 100644 --- a/instruments/tests/test_lakeshore/test_lakeshore475.py +++ b/instruments/tests/test_lakeshore/test_lakeshore475.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Lakeshore 475 Gaussmeter """ diff --git a/instruments/tests/test_minghe/test_minghe_mhs5200a.py b/instruments/tests/test_minghe/test_minghe_mhs5200a.py index 63948c0a0..1760091a2 100644 --- a/instruments/tests/test_minghe/test_minghe_mhs5200a.py +++ b/instruments/tests/test_minghe/test_minghe_mhs5200a.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the MingHe MHS52000a """ diff --git a/instruments/tests/test_named_struct.py b/instruments/tests/test_named_struct.py index ffee3f32a..542a96905 100644 --- a/instruments/tests/test_named_struct.py +++ b/instruments/tests/test_named_struct.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for named structures. """ @@ -41,13 +40,13 @@ class Foo(NamedStruct): b = StringField(9, strip_null=True) c = StringField(2, encoding="utf-8") - foo = Foo(a="0123456\x00", b="abc", c=u"α") + foo = Foo(a="0123456\x00", b="abc", c="α") assert Foo.unpack(foo.pack()) == foo # Also check that we can get fields out directly. self.assertEqual(foo.a, "0123456\x00") self.assertEqual(foo.b, "abc") - self.assertEqual(foo.c, u"α") + self.assertEqual(foo.c, "α") def test_negative_len(self): """ @@ -64,8 +63,8 @@ class Foo(NamedStruct): b = Field("B") c = StringField(5, encoding="utf8", strip_null=True) - foo1 = Foo(a=0x1234, b=0x56, c=u"ω") - foo2 = Foo(a=0xABCD, b=0xEF, c=u"α") + foo1 = Foo(a=0x1234, b=0x56, c="ω") + foo2 = Foo(a=0xABCD, b=0xEF, c="α") assert foo1 == foo1 assert foo1 != foo2 diff --git a/instruments/tests/test_newport/test_agilis.py b/instruments/tests/test_newport/test_agilis.py index 6fdea6dc1..e37f33ad4 100644 --- a/instruments/tests/test_newport/test_agilis.py +++ b/instruments/tests/test_newport/test_agilis.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Agilis Controller """ diff --git a/instruments/tests/test_newport/test_errors.py b/instruments/tests/test_newport/test_errors.py index 23652d689..1e3c8cbc2 100644 --- a/instruments/tests/test_newport/test_errors.py +++ b/instruments/tests/test_newport/test_errors.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for NewportError class """ diff --git a/instruments/tests/test_newport/test_newport_pmc8742.py b/instruments/tests/test_newport/test_newport_pmc8742.py index 701732804..8cc9cee00 100644 --- a/instruments/tests/test_newport/test_newport_pmc8742.py +++ b/instruments/tests/test_newport/test_newport_pmc8742.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Tests for the Newport Picomotor Controller 8742. """ diff --git a/instruments/tests/test_newport/test_newportesp301.py b/instruments/tests/test_newport/test_newportesp301.py index 642724d7d..77d150398 100644 --- a/instruments/tests/test_newport/test_newportesp301.py +++ b/instruments/tests/test_newport/test_newportesp301.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Newport ESP 301 axis controller """ diff --git a/instruments/tests/test_ondax/test_lm.py b/instruments/tests/test_ondax/test_lm.py index 4d23c85d5..119e92d66 100644 --- a/instruments/tests/test_ondax/test_lm.py +++ b/instruments/tests/test_ondax/test_lm.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Ondax Laser Module """ diff --git a/instruments/tests/test_oxford/test_oxforditc503.py b/instruments/tests/test_oxford/test_oxforditc503.py index de679b985..f09a15b5c 100644 --- a/instruments/tests/test_oxford/test_oxforditc503.py +++ b/instruments/tests/test_oxford/test_oxforditc503.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Oxford ITC 503 temperature controller """ diff --git a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py index 5d59c974c..a53a2edcb 100644 --- a/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py +++ b/instruments/tests/test_phasematrix/test_phasematrix_fsw0020.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Phasematrix FSW0020 """ @@ -25,7 +24,7 @@ def test_reset(): def test_frequency(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["04.", "0C{:012X}.".format(int((10 * u.GHz).to(u.mHz).magnitude))], + ["04.", f"0C{int((10 * u.GHz).to(u.mHz).magnitude):012X}."], ["00E8D4A51000"], ) as inst: assert inst.frequency == 1.0000000000000002 * u.GHz @@ -35,7 +34,7 @@ def test_frequency(): def test_power(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["0D.", "03{:04X}.".format(int(u.Quantity(10, u.dBm).to(u.cBm).magnitude))], + ["0D.", f"03{int(u.Quantity(10, u.dBm).to(u.cBm).magnitude):04X}."], ["-064"], ) as inst: assert inst.power == u.Quantity(-10, u.dBm) @@ -54,7 +53,7 @@ def test_phase(): def test_blanking(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["05{:02X}.".format(1), "05{:02X}.".format(0)], + [f"05{1:02X}.", f"05{0:02X}."], [], ) as inst: inst.blanking = True @@ -66,7 +65,7 @@ def test_blanking(): def test_ref_output(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["08{:02X}.".format(1), "08{:02X}.".format(0)], + [f"08{1:02X}.", f"08{0:02X}."], [], ) as inst: inst.ref_output = True @@ -78,7 +77,7 @@ def test_ref_output(): def test_output(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["0F{:02X}.".format(1), "0F{:02X}.".format(0)], + [f"0F{1:02X}.", f"0F{0:02X}."], [], ) as inst: inst.output = True @@ -90,7 +89,7 @@ def test_output(): def test_pulse_modulation(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["09{:02X}.".format(1), "09{:02X}.".format(0)], + [f"09{1:02X}.", f"09{0:02X}."], [], ) as inst: inst.pulse_modulation = True @@ -102,7 +101,7 @@ def test_pulse_modulation(): def test_am_modulation(): with expected_protocol( ik.phasematrix.PhaseMatrixFSW0020, - ["0A{:02X}.".format(1), "0A{:02X}.".format(0)], + [f"0A{1:02X}.", f"0A{0:02X}."], [], ) as inst: inst.am_modulation = True diff --git a/instruments/tests/test_picowatt/test_picowatt_avs47.py b/instruments/tests/test_picowatt/test_picowatt_avs47.py index c625dce3f..3fea16a0a 100644 --- a/instruments/tests/test_picowatt/test_picowatt_avs47.py +++ b/instruments/tests/test_picowatt/test_picowatt_avs47.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Picowatt AVS47 """ diff --git a/instruments/tests/test_property_factories/__init__.py b/instruments/tests/test_property_factories/__init__.py index 504c12f94..997b51218 100644 --- a/instruments/tests/test_property_factories/__init__.py +++ b/instruments/tests/test_property_factories/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing common code for testing the property factories """ @@ -30,7 +29,7 @@ def value(self): return self._buf.getvalue() def sendcmd(self, cmd): - self._buf.write("{}\n".format(cmd)) + self._buf.write(f"{cmd}\n") def query(self, cmd): self.sendcmd(cmd) diff --git a/instruments/tests/test_property_factories/test_bool_property.py b/instruments/tests/test_property_factories/test_bool_property.py index e446d53fc..a8cf266df 100644 --- a/instruments/tests/test_property_factories/test_bool_property.py +++ b/instruments/tests/test_property_factories/test_bool_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the bool property factories """ diff --git a/instruments/tests/test_property_factories/test_bounded_unitful_property.py b/instruments/tests/test_property_factories/test_bounded_unitful_property.py index 9639102da..bf777df5a 100644 --- a/instruments/tests/test_property_factories/test_bounded_unitful_property.py +++ b/instruments/tests/test_property_factories/test_bounded_unitful_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the bounded unitful property factories """ diff --git a/instruments/tests/test_property_factories/test_enum_property.py b/instruments/tests/test_property_factories/test_enum_property.py index bf7eeea99..e02ce1760 100644 --- a/instruments/tests/test_property_factories/test_enum_property.py +++ b/instruments/tests/test_property_factories/test_enum_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the enum property factories """ diff --git a/instruments/tests/test_property_factories/test_int_property.py b/instruments/tests/test_property_factories/test_int_property.py index 26d083358..c95fc6f62 100644 --- a/instruments/tests/test_property_factories/test_int_property.py +++ b/instruments/tests/test_property_factories/test_int_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the int property factories """ @@ -21,7 +20,7 @@ def test_int_property_outside_valid_set(): with pytest.raises(ValueError): class IntMock(MockInstrument): - mock_property = int_property("MOCK", valid_set=set([1, 2])) + mock_property = int_property("MOCK", valid_set={1, 2}) mock_inst = IntMock() mock_inst.mock_property = 3 @@ -29,7 +28,7 @@ class IntMock(MockInstrument): def test_int_property_valid_set(): class IntMock(MockInstrument): - int_property = int_property("MOCK", valid_set=set([1, 2])) + int_property = int_property("MOCK", valid_set={1, 2}) mock_inst = IntMock({"MOCK?": "1"}) @@ -68,7 +67,7 @@ class IntMock(MockInstrument): mock_inst = IntMock() mock_inst.int_property = 1 - assert mock_inst.value == "MOCK {:d}\n".format(1) + assert mock_inst.value == f"MOCK {1:d}\n" def test_int_property_readonly_writing_fails(): @@ -98,7 +97,7 @@ class IntMock(MockInstrument): mock_inst = IntMock() mock_inst.int_property = 1 - assert mock_inst.value == "MOCK {:e}\n".format(1) + assert mock_inst.value == f"MOCK {1:e}\n" def test_int_property_set_cmd(): diff --git a/instruments/tests/test_property_factories/test_rproperty.py b/instruments/tests/test_property_factories/test_rproperty.py index 56362a0a2..8391fca6c 100644 --- a/instruments/tests/test_property_factories/test_rproperty.py +++ b/instruments/tests/test_property_factories/test_rproperty.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the property factories """ @@ -21,7 +20,7 @@ def test_rproperty_basic(): class Mock(MockInstrument): def __init__(self): - super(Mock, self).__init__() + super().__init__() self._value = 0 def mockget(self): @@ -42,7 +41,7 @@ def test_rproperty_readonly_writing_fails(): class Mock(MockInstrument): def __init__(self): - super(Mock, self).__init__() + super().__init__() self._value = 0 def mockset(self, newval): # pragma: no cover @@ -57,7 +56,7 @@ def mockset(self, newval): # pragma: no cover def test_rproperty_readonly_reading_passes(): class Mock(MockInstrument): def __init__(self): - super(Mock, self).__init__() + super().__init__() self._value = 0 def mockget(self): @@ -74,7 +73,7 @@ def test_rproperty_writeonly_reading_fails(): class Mock(MockInstrument): def __init__(self): - super(Mock, self).__init__() + super().__init__() self._value = 0 def mockget(self): # pragma: no cover @@ -89,7 +88,7 @@ def mockget(self): # pragma: no cover def test_rproperty_writeonly_writing_passes(): class Mock(MockInstrument): def __init__(self): - super(Mock, self).__init__() + super().__init__() self._value = 0 def mockset(self, newval): diff --git a/instruments/tests/test_property_factories/test_string_property.py b/instruments/tests/test_property_factories/test_string_property.py index d856584bc..e57ef45be 100644 --- a/instruments/tests/test_property_factories/test_string_property.py +++ b/instruments/tests/test_property_factories/test_string_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the string property factories """ diff --git a/instruments/tests/test_property_factories/test_unitful_property.py b/instruments/tests/test_property_factories/test_unitful_property.py index fb2fd59c3..49d58d038 100644 --- a/instruments/tests/test_property_factories/test_unitful_property.py +++ b/instruments/tests/test_property_factories/test_unitful_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the unitful property factories """ @@ -28,7 +27,7 @@ class UnitfulMock(MockInstrument): assert mock_inst.unitful_property == 1000 * u.hertz mock_inst.unitful_property = 1000 * u.hertz - assert mock_inst.value == "MOCK?\nMOCK {:e}\n".format(1000) + assert mock_inst.value == f"MOCK?\nMOCK {1000:e}\n" def test_unitful_property_format_code(): @@ -38,7 +37,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1000 * u.hertz - assert mock_inst.value == "MOCK {:f}\n".format(1000) + assert mock_inst.value == f"MOCK {1000:f}\n" def test_unitful_property_rescale_units(): @@ -48,7 +47,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1 * u.kilohertz - assert mock_inst.value == "MOCK {:e}\n".format(1000) + assert mock_inst.value == f"MOCK {1000:e}\n" def test_unitful_property_no_units_on_set(): @@ -58,7 +57,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1000 - assert mock_inst.value == "MOCK {:e}\n".format(1000) + assert mock_inst.value == f"MOCK {1000:e}\n" def test_unitful_property_wrong_units(): @@ -90,7 +89,7 @@ class UnitfulMock(MockInstrument): mock_inst = UnitfulMock() mock_inst.unitful_property = 1 * u.hertz - assert mock_inst.value == "MOCK {:e}\n".format(1) + assert mock_inst.value == f"MOCK {1:e}\n" def test_unitful_property_readonly_writing_fails(): @@ -122,7 +121,7 @@ class UnitfulMock(MockInstrument): mock_inst.unitful_property = 0 mock_inst.unitful_property = 10 - assert mock_inst.value == "MOCK {:e}\nMOCK {:e}\n".format(0, 10) + assert mock_inst.value == f"MOCK {0:e}\nMOCK {10:e}\n" def test_unitful_property_valid_range_functions(): @@ -142,7 +141,7 @@ def max_value(self): mock_inst.unitful_property = 0 mock_inst.unitful_property = 10 - assert mock_inst.value == "MOCK {:e}\nMOCK {:e}\n".format(0, 10) + assert mock_inst.value == f"MOCK {0:e}\nMOCK {10:e}\n" def test_unitful_property_minimum_value(): @@ -236,4 +235,4 @@ class UnitfulMock(MockInstrument): assert mock_inst.a == 1000 * u.hertz mock_inst.a = 1000 * u.hertz - assert mock_inst.value == "MOCK?\nFOOBAR {:e}\n".format(1000) + assert mock_inst.value == f"MOCK?\nFOOBAR {1000:e}\n" diff --git a/instruments/tests/test_property_factories/test_unitless_property.py b/instruments/tests/test_property_factories/test_unitless_property.py index 32412649c..6efa4d1bb 100644 --- a/instruments/tests/test_property_factories/test_unitless_property.py +++ b/instruments/tests/test_property_factories/test_unitless_property.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the unitless property factory """ @@ -27,7 +26,7 @@ class UnitlessMock(MockInstrument): assert mock_inst.mock_property == 1 mock_inst.mock_property = 1 - assert mock_inst.value == "MOCK?\nMOCK {:e}\n".format(1) + assert mock_inst.value == f"MOCK?\nMOCK {1:e}\n" def test_unitless_property_units(): @@ -48,7 +47,7 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock() mock_inst.mock_property = 1 - assert mock_inst.value == "MOCK {:f}\n".format(1) + assert mock_inst.value == f"MOCK {1:f}\n" def test_unitless_property_writeonly_reading_fails(): @@ -69,7 +68,7 @@ class UnitlessMock(MockInstrument): mock_inst = UnitlessMock() mock_inst.mock_property = 1 - assert mock_inst.value == "MOCK {:e}\n".format(1) + assert mock_inst.value == f"MOCK {1:e}\n" def test_unitless_property_readonly_writing_fails(): @@ -101,4 +100,4 @@ class UnitlessMock(MockInstrument): assert mock_inst.mock_property == 1 mock_inst.mock_property = 1 - assert mock_inst.value == "MOCK?\nFOOBAR {:e}\n".format(1) + assert mock_inst.value == f"MOCK?\nFOOBAR {1:e}\n" diff --git a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py index a1f75306d..5ff565daf 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_cc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_cc1.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Qubitekk CC1 """ @@ -22,8 +21,8 @@ def test_init_os_error(mocker): This raises an OSError in the read which must pass without an issue. """ - stdout = BytesIO(":ACKN OF\nFIRM?\n".encode("utf-8")) - stdin = BytesIO("Firmware v2.010\n".encode("utf-8")) + stdout = BytesIO(b":ACKN OF\nFIRM?\n") + stdin = BytesIO(b"Firmware v2.010\n") mock_read = mocker.patch.object(ik.qubitekk.CC1, "read") mock_read.side_effect = OSError _ = ik.qubitekk.CC1.open_test(stdin, stdout) diff --git a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py index d448be39f..f2445dae3 100644 --- a/instruments/tests/test_qubitekk/test_qubitekk_mc1.py +++ b/instruments/tests/test_qubitekk/test_qubitekk_mc1.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Qubitekk MC1 """ diff --git a/instruments/tests/test_rigol/test_rigolds1000.py b/instruments/tests/test_rigol/test_rigolds1000.py index ccc4035e5..eec06a655 100644 --- a/instruments/tests/test_rigol/test_rigolds1000.py +++ b/instruments/tests/test_rigol/test_rigolds1000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Rigol DS1000 """ diff --git a/instruments/tests/test_split_str.py b/instruments/tests/test_split_str.py index c6808af83..367f0b1e9 100644 --- a/instruments/tests/test_split_str.py +++ b/instruments/tests/test_split_str.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the util_fns.split_unit_str utility function """ diff --git a/instruments/tests/test_srs/test_srs345.py b/instruments/tests/test_srs/test_srs345.py index 2883b89a9..936e46efb 100644 --- a/instruments/tests/test_srs/test_srs345.py +++ b/instruments/tests/test_srs/test_srs345.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the SRS 345 function generator """ @@ -35,7 +34,7 @@ def test_frequency(): ik.srs.SRS345, [ "FREQ?", - "FREQ {:e}".format(0.1), + f"FREQ {0.1:e}", ], [ "1.234", @@ -62,7 +61,7 @@ def test_offset(): ik.srs.SRS345, [ "OFFS?", - "OFFS {:e}".format(0.1), + f"OFFS {0.1:e}", ], [ "1.234", @@ -77,7 +76,7 @@ def test_phase(): ik.srs.SRS345, [ "PHSE?", - "PHSE {:e}".format(0.1), + f"PHSE {0.1:e}", ], [ "1.234", diff --git a/instruments/tests/test_srs/test_srs830.py b/instruments/tests/test_srs/test_srs830.py index 445fa3357..7cd49beba 100644 --- a/instruments/tests/test_srs/test_srs830.py +++ b/instruments/tests/test_srs/test_srs830.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the SRS 830 lock-in amplifier """ @@ -86,7 +85,7 @@ def test_frequency_source(): def test_frequency(): with expected_protocol( ik.srs.SRS830, - ["FREQ?", "FREQ {:e}".format(1000)], + ["FREQ?", f"FREQ {1000:e}"], [ "12.34", ], @@ -98,7 +97,7 @@ def test_frequency(): def test_phase(): with expected_protocol( ik.srs.SRS830, - ["PHAS?", "PHAS {:e}".format(10)], + ["PHAS?", f"PHAS {10:e}"], [ "-45", ], @@ -110,7 +109,7 @@ def test_phase(): def test_amplitude(): with expected_protocol( ik.srs.SRS830, - ["SLVL?", "SLVL {:e}".format(1)], + ["SLVL?", f"SLVL {1:e}"], [ "0.1", ], @@ -145,7 +144,7 @@ def test_coupling(): def test_sample_rate(): # sends index of VALID_SAMPLE_RATES with expected_protocol( - ik.srs.SRS830, ["SRAT?", "SRAT?", "SRAT {:d}".format(5), "SRAT 14"], ["8", "14"] + ik.srs.SRS830, ["SRAT?", "SRAT?", f"SRAT {5:d}", "SRAT 14"], ["8", "14"] ) as inst: assert inst.sample_rate == 16 * u.Hz assert inst.sample_rate == "trigger" diff --git a/instruments/tests/test_srs/test_srsctc100.py b/instruments/tests/test_srs/test_srsctc100.py index 67341a0ba..c322eff69 100644 --- a/instruments/tests/test_srs/test_srsctc100.py +++ b/instruments/tests/test_srs/test_srsctc100.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the SRS CTC-100 """ @@ -402,10 +401,10 @@ def test_srsctc100_channel_units_all(): ) as inst: with inst._error_checking_disabled(): # create a unit dictionary to compare the return to - unit_dict = dict( - (chan_name, ik.srs.SRSCTC100._UNIT_NAMES[unit_str]) + unit_dict = { + chan_name: ik.srs.SRSCTC100._UNIT_NAMES[unit_str] for chan_name, unit_str in zip(ch_names, ch_units) - ) + } assert inst.channel_units() == unit_dict diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index 7d50d35e8..e4564c6b7 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the SRS DG645 """ diff --git a/instruments/tests/test_tektronix/test_tekawg2000.py b/instruments/tests/test_tektronix/test_tekawg2000.py index 59a7863f9..434a88b39 100644 --- a/instruments/tests/test_tektronix/test_tekawg2000.py +++ b/instruments/tests/test_tektronix/test_tekawg2000.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Tektronix AGG2000 arbitrary wave generators. """ @@ -215,7 +214,7 @@ def test_upload_waveform(yzero, ymult, xincr, waveform): waveform_send = waveform_send.astype(" 0)) + status_dict_expected = { + key: (status_bits & bit_mask > 0) for key, bit_mask in apt_mc_channel_status_bit_mask.items() - ) + } with expected_protocol( ik.thorlabs.APTMotorController, diff --git a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py index 938d33a5a..6d62eba70 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_lcc25.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Thorlabs LCC25 """ diff --git a/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py b/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py index 6269886d7..fdbe055e0 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_pm100usb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Thorlabs PM100USB """ diff --git a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py index 7fbf6ac9f..2564ac584 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_sc10.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_sc10.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Thorlabs SC10 """ diff --git a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py index 21a53489e..a9084db48 100644 --- a/instruments/tests/test_thorlabs/test_thorlabs_tc200.py +++ b/instruments/tests/test_thorlabs/test_thorlabs_tc200.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Thorlabs TC200 """ diff --git a/instruments/tests/test_thorlabs/test_utils.py b/instruments/tests/test_thorlabs/test_utils.py index d12c307db..b876b3899 100644 --- a/instruments/tests/test_thorlabs/test_utils.py +++ b/instruments/tests/test_thorlabs/test_utils.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Thorlabs util functions """ diff --git a/instruments/tests/test_toptica/test_toptica_topmode.py b/instruments/tests/test_toptica/test_toptica_topmode.py index ca8b0c908..57eb9911f 100644 --- a/instruments/tests/test_toptica/test_toptica_topmode.py +++ b/instruments/tests/test_toptica/test_toptica_topmode.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for the Toptica Topmode """ diff --git a/instruments/tests/test_toptica/test_toptica_utils.py b/instruments/tests/test_toptica/test_toptica_utils.py index cd517997c..23b71e6f6 100644 --- a/instruments/tests/test_toptica/test_toptica_utils.py +++ b/instruments/tests/test_toptica/test_toptica_utils.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for Topical util functions """ diff --git a/instruments/tests/test_util_fns.py b/instruments/tests/test_util_fns.py index c944b004b..9b2433ca6 100644 --- a/instruments/tests/test_util_fns.py +++ b/instruments/tests/test_util_fns.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing tests for util_fns.py """ diff --git a/instruments/tests/test_yokogawa/test_yokogawa7651.py b/instruments/tests/test_yokogawa/test_yokogawa7651.py index 17ab91b9c..d7b46ea3e 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa7651.py +++ b/instruments/tests/test_yokogawa/test_yokogawa7651.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Yokogawa 7651 power supply """ diff --git a/instruments/tests/test_yokogawa/test_yokogawa_6370.py b/instruments/tests/test_yokogawa/test_yokogawa_6370.py index f4e889646..87117cc83 100644 --- a/instruments/tests/test_yokogawa/test_yokogawa_6370.py +++ b/instruments/tests/test_yokogawa/test_yokogawa_6370.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unit tests for the Yokogawa 6370 """ @@ -48,7 +47,7 @@ def test_channel_data(values, channel): ik.yokogawa.Yokogawa6370, [ ":FORMat:DATA REAL,64", - ":TRAC:Y? {}".format(channel.value), + f":TRAC:Y? {channel.value}", ], [b"#" + values_len_of_len + values_len + values_packed], ) as inst: @@ -70,7 +69,7 @@ def test_channel_wavelength(values, channel): ik.yokogawa.Yokogawa6370, [ ":FORMat:DATA REAL,64", - ":TRAC:X? {}".format(channel.value), + f":TRAC:X? {channel.value}", ], [b"#" + values_len_of_len + values_len + values_packed], ) as inst: @@ -87,7 +86,7 @@ def test_start_wavelength(value): [ ":FORMat:DATA REAL,64", ":SENS:WAV:STAR?", - ":SENS:WAV:STAR {:e}".format(value), + f":SENS:WAV:STAR {value:e}", ], ["6.000000e-06"], ) as inst: @@ -102,7 +101,7 @@ def test_end_wavelength(value): [ ":FORMat:DATA REAL,64", ":SENS:WAV:STOP?", - ":SENS:WAV:STOP {:e}".format(value), + f":SENS:WAV:STOP {value:e}", ], ["6.000000e-06"], ) as inst: @@ -209,9 +208,9 @@ def test_data_active_trace(values): ik.yokogawa.Yokogawa6370, [ ":FORMat:DATA REAL,64", - ":TRAC:Y? {}".format(channel), + f":TRAC:Y? {channel}", ":TRAC:ACTIVE?", - ":TRAC:Y? {}".format(channel), + f":TRAC:Y? {channel}", ], [ b"#" + values_len_of_len + values_len + values_packed, @@ -237,9 +236,9 @@ def test_wavelength_active_trace(values): ik.yokogawa.Yokogawa6370, [ ":FORMat:DATA REAL,64", - ":TRAC:X? {}".format(channel), + f":TRAC:X? {channel}", ":TRAC:ACTIVE?", - ":TRAC:X? {}".format(channel), + f":TRAC:X? {channel}", ], [ b"#" + values_len_of_len + values_len + values_packed, diff --git a/instruments/thorlabs/__init__.py b/instruments/thorlabs/__init__.py index 470f572c5..625721909 100644 --- a/instruments/thorlabs/__init__.py +++ b/instruments/thorlabs/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Thorlabs instruments """ diff --git a/instruments/thorlabs/_abstract.py b/instruments/thorlabs/_abstract.py index 89669c9be..7ac023a05 100644 --- a/instruments/thorlabs/_abstract.py +++ b/instruments/thorlabs/_abstract.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines a generic Thorlabs instrument to define some common functionality. """ @@ -26,7 +25,7 @@ class ThorLabsInstrument(Instrument): """ def __init__(self, filelike): - super(ThorLabsInstrument, self).__init__(filelike) + super().__init__(filelike) self.terminator = "" def sendpacket(self, packet): @@ -93,12 +92,12 @@ def querypacket(self, packet, expect=None, timeout=None, expect_data_len=None): if expect is None: return None else: - raise IOError("Expected packet {}, got nothing instead.".format(expect)) + raise OSError(f"Expected packet {expect}, got nothing instead.") pkt = _packets.ThorLabsPacket.unpack(resp) if expect is not None and pkt._message_id != expect: # TODO: make specialized subclass that can record the offending # packet. - raise IOError( + raise OSError( "APT returned message ID {}, expected {}".format( pkt._message_id, expect ) diff --git a/instruments/thorlabs/_cmds.py b/instruments/thorlabs/_cmds.py index 6e75ed51c..c60fd7b69 100644 --- a/instruments/thorlabs/_cmds.py +++ b/instruments/thorlabs/_cmds.py @@ -1,5 +1,4 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- """ Contains command mneonics for the ThorLabs APT protocol diff --git a/instruments/thorlabs/_packets.py b/instruments/thorlabs/_packets.py index db0e38264..7159466cd 100644 --- a/instruments/thorlabs/_packets.py +++ b/instruments/thorlabs/_packets.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module for working with ThorLabs packets. """ @@ -73,9 +72,9 @@ def __str__(self): Data {3} """.format( self, - "0x{:x}".format(self._param1) if not self._has_data else "None", - "0x{:x}".format(self._param2) if not self._has_data else "None", - "{}".format(self._data) if self._has_data else "None", + f"0x{self._param1:x}" if not self._has_data else "None", + f"0x{self._param2:x}" if not self._has_data else "None", + f"{self._data}" if self._has_data else "None", ) @property diff --git a/instruments/thorlabs/lcc25.py b/instruments/thorlabs/lcc25.py index c07c6f2f7..3db577804 100644 --- a/instruments/thorlabs/lcc25.py +++ b/instruments/thorlabs/lcc25.py @@ -1,5 +1,4 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- """ Provides the support for the Thorlabs LCC25 liquid crystal controller. @@ -31,7 +30,7 @@ class LCC25(Instrument): """ def __init__(self, filelike): - super(LCC25, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self.prompt = "> " @@ -260,7 +259,7 @@ def set_settings(self, slot): """ if slot not in range(1, 5): raise ValueError("Cannot set memory out of `[1,4]` range") - response = self.query("set={}".format(slot)) + response = self.query(f"set={slot}") return check_cmd(response) def get_settings(self, slot): @@ -275,7 +274,7 @@ def get_settings(self, slot): """ if slot not in range(1, 5): raise ValueError("Cannot set memory out of `[1,4]` range") - response = self.query("get={}".format(slot)) + response = self.query(f"get={slot}") return check_cmd(response) def test_mode(self): diff --git a/instruments/thorlabs/pm100usb.py b/instruments/thorlabs/pm100usb.py index 3e44271c5..393afbbfc 100644 --- a/instruments/thorlabs/pm100usb.py +++ b/instruments/thorlabs/pm100usb.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the support for the Thorlabs PM100USB power meter. """ @@ -216,7 +215,7 @@ def averaging_count(self): def averaging_count(self, newval): if newval < 1: raise ValueError("Must count at least one time.") - self.sendcmd("SENS:AVER:COUN {}".format(newval)) + self.sendcmd(f"SENS:AVER:COUN {newval}") # METHODS ## diff --git a/instruments/thorlabs/sc10.py b/instruments/thorlabs/sc10.py index 10e3142d5..e5b8d34a6 100644 --- a/instruments/thorlabs/sc10.py +++ b/instruments/thorlabs/sc10.py @@ -1,5 +1,4 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- """ Provides the support for the Thorlabs SC10 optical beam shutter controller. @@ -32,7 +31,7 @@ class SC10(Instrument): """ def __init__(self, filelike): - super(SC10, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self.prompt = "> " @@ -178,7 +177,7 @@ def baud_rate(self, newval): if newval != 9600 and newval != 115200: raise ValueError("Invalid baud rate mode") else: - self.sendcmd("baud={}".format(0 if newval == 9600 else 1)) + self.sendcmd(f"baud={0 if newval == 9600 else 1}") closed = bool_property( "closed", diff --git a/instruments/thorlabs/tc200.py b/instruments/thorlabs/tc200.py index 9b243b5a5..76daec8dc 100644 --- a/instruments/thorlabs/tc200.py +++ b/instruments/thorlabs/tc200.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the support for the Thorlabs TC200 temperature controller. @@ -34,7 +33,7 @@ class TC200(Instrument): """ def __init__(self, filelike): - super(TC200, self).__init__(filelike) + super().__init__(filelike) self.terminator = "\r" self.prompt = "> " @@ -93,7 +92,7 @@ def mode(self, newval): "Mode setting must be a `TC200.Mode` value, " "got {} instead.".format(type(newval)) ) - out_query = "mode={}".format(newval.name) + out_query = f"mode={newval.name}" # there is an issue with the TC200; it responds with a spurious # Command Error on mode=normal. Thus, the sendcmd() method cannot # be used. @@ -149,7 +148,7 @@ def status(self): :rtype: `int` """ - _ = self._file.query(str("stat?")) + _ = self._file.query("stat?") response = self.read(5) return int(response.split(" ")[0]) @@ -209,7 +208,7 @@ def temperature_set(self, newval): newval = convert_temperature(newval, u.degC) if newval < u.Quantity(20.0, u.degC) or newval > self.max_temperature: raise ValueError("Temperature set is out of range.") - out_query = "tset={}".format(newval.magnitude) + out_query = f"tset={newval.magnitude}" self.sendcmd(out_query) @property @@ -226,7 +225,7 @@ def p(self): def p(self, newval): if newval not in range(1, 251): raise ValueError("P-value not in [1, 250]") - self.sendcmd("pgain={}".format(newval)) + self.sendcmd(f"pgain={newval}") @property def i(self): @@ -242,7 +241,7 @@ def i(self): def i(self, newval): if newval not in range(0, 251): raise ValueError("I-value not in [0, 250]") - self.sendcmd("igain={}".format(newval)) + self.sendcmd(f"igain={newval}") @property def d(self): @@ -258,7 +257,7 @@ def d(self): def d(self, newval): if newval not in range(0, 251): raise ValueError("D-value not in [0, 250]") - self.sendcmd("dgain={}".format(newval)) + self.sendcmd(f"dgain={newval}") @property def pid(self): diff --git a/instruments/thorlabs/thorlabs_utils.py b/instruments/thorlabs/thorlabs_utils.py index d6a0165ae..8f17d6cf0 100644 --- a/instruments/thorlabs/thorlabs_utils.py +++ b/instruments/thorlabs/thorlabs_utils.py @@ -1,5 +1,4 @@ #!/usr/bin/python -# -*- coding: utf-8 -*- """ Contains common utility functions for Thorlabs-brand instruments """ diff --git a/instruments/thorlabs/thorlabsapt.py b/instruments/thorlabs/thorlabsapt.py index 5b1cded39..3414b0983 100644 --- a/instruments/thorlabs/thorlabsapt.py +++ b/instruments/thorlabs/thorlabsapt.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the support for the Thorlabs APT Controller. """ @@ -95,7 +94,7 @@ def enabled(self, newval): _channel_type = APTChannel def __init__(self, filelike): - super(ThorLabsAPT, self).__init__(filelike) + super().__init__(filelike) self._dest = 0x50 # Generic USB device; make this configurable later. # Provide defaults in case an exception occurs below. @@ -139,7 +138,7 @@ def __init__(self, filelike): elif hw_type_int == 44: self._hw_type = "Brushless DC controller" else: - self._hw_type = "Unknown type: {}".format(hw_type_int) + self._hw_type = f"Unknown type: {hw_type_int}" # Note that the fourth byte is padding, so we strip out the first # three bytes and format them. @@ -152,7 +151,7 @@ def __init__(self, filelike): self._hw_version = struct.unpack(" 0)) + status_dict = { + key: (status_bits & bit_mask > 0) for key, bit_mask in self.__STATUS_BIT_MASK.items() - ) + } return status_dict diff --git a/instruments/toptica/__init__.py b/instruments/toptica/__init__.py index 4bafc68e9..6804dc476 100644 --- a/instruments/toptica/__init__.py +++ b/instruments/toptica/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Toptica instruments """ diff --git a/instruments/toptica/topmode.py b/instruments/toptica/topmode.py index 6ff103cb1..fb7f17c7d 100644 --- a/instruments/toptica/topmode.py +++ b/instruments/toptica/topmode.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides the support for the Toptica Topmode diode laser. @@ -34,7 +33,7 @@ class TopMode(Instrument): """ def __init__(self, filelike): - super(TopMode, self).__init__(filelike) + super().__init__(filelike) self.prompt = "> " self.terminator = "\r\n" @@ -72,7 +71,7 @@ class Laser: def __init__(self, parent, idx): self.parent = parent - self.name = "laser{}".format(idx + 1) + self.name = f"laser{idx + 1}" # PROPERTIES # @@ -325,12 +324,12 @@ def set(self, param, value): """ if isinstance(value, str): - self.query('(param-set! \'{} "{}")'.format(param, value)) + self.query(f'(param-set! \'{param} "{value}")') elif isinstance(value, (tuple, list)): self.query("(param-set! '{} '({}))".format(param, " ".join(value))) elif isinstance(value, bool): value = "t" if value else "f" - self.query("(param-set! '{} #{})".format(param, value)) + self.query(f"(param-set! '{param} #{value})") def reference(self, param): """ @@ -341,7 +340,7 @@ def reference(self, param): :return: Response to the reference request :rtype: `str` """ - response = self.query("(param-ref '{})".format(param)).replace('"', "") + response = self.query(f"(param-ref '{param})").replace('"', "") return response def display(self, param): @@ -351,7 +350,7 @@ def display(self, param): :param str param: Parameter that will be sent with a display request :return: Response to the display request """ - return self.query("(param-disp '{})".format(param)) + return self.query(f"(param-disp '{param})") # PROPERTIES # diff --git a/instruments/toptica/toptica_utils.py b/instruments/toptica/toptica_utils.py index 20d345e61..1f87f1cc7 100644 --- a/instruments/toptica/toptica_utils.py +++ b/instruments/toptica/toptica_utils.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Contains common utility functions for Toptica-brand instruments diff --git a/instruments/units.py b/instruments/units.py index 954976fa6..dd8b1b868 100644 --- a/instruments/units.py +++ b/instruments/units.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing custom units used by various instruments. """ diff --git a/instruments/util_fns.py b/instruments/util_fns.py index fafe9c5a2..18ea3316f 100644 --- a/instruments/util_fns.py +++ b/instruments/util_fns.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing various utility functions """ @@ -283,7 +282,7 @@ def _out_decor_fcn(val): return output_decoration(val) def _getter(self): - return enum(_in_decor_fcn(self.query("{}?".format(command)).strip())) + return enum(_in_decor_fcn(self.query(f"{command}?").strip())) def _setter(self, newval): try: # First assume newval is Enum.value @@ -339,7 +338,7 @@ def unitless_property( """ def _getter(self): - raw = self.query("{}?".format(command)) + raw = self.query(f"{command}?") return float(raw) def _setter(self, newval): @@ -393,7 +392,7 @@ def int_property( """ def _getter(self): - raw = self.query("{}?".format(command)) + raw = self.query(f"{command}?") return int(raw) if valid_set is None: @@ -490,7 +489,7 @@ def _out_decor_fcn(val): return output_decoration(val) def _getter(self): - raw = _in_decor_fcn(self.query("{}?".format(command))) + raw = _in_decor_fcn(self.query(f"{command}?")) return u.Quantity(*split_unit_str(raw, units)).to(units) def _setter(self, newval): @@ -636,7 +635,7 @@ def string_property( bookmark_length = len(bookmark_symbol) def _getter(self): - string = self.query("{}?".format(command)) + string = self.query(f"{command}?") string = ( string[bookmark_length:-bookmark_length] if bookmark_length > 0 else string ) diff --git a/instruments/yokogawa/__init__.py b/instruments/yokogawa/__init__.py index 673e8d26f..9f0b4fe5c 100644 --- a/instruments/yokogawa/__init__.py +++ b/instruments/yokogawa/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Module containing Yokogawa instruments """ diff --git a/instruments/yokogawa/yokogawa6370.py b/instruments/yokogawa/yokogawa6370.py index 6b7c34a48..eb62c510d 100644 --- a/instruments/yokogawa/yokogawa6370.py +++ b/instruments/yokogawa/yokogawa6370.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Provides support for the Yokogawa 6370 optical spectrum analyzer. """ @@ -41,7 +40,7 @@ class Yokogawa6370(OpticalSpectrumAnalyzer): """ def __init__(self, *args, **kwargs): - super(Yokogawa6370, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Set data Format to binary self.sendcmd(":FORMat:DATA REAL,64") # TODO: Find out where we want this @@ -65,14 +64,14 @@ def __init__(self, parent, idx): # METHODS # def data(self, bin_format=True): - cmd = ":TRAC:Y? {0}".format(self._name) + cmd = f":TRAC:Y? {self._name}" self._parent.sendcmd(cmd) data = self._parent.binblockread(data_width=8, fmt=" Date: Sat, 22 Jan 2022 20:17:14 -0500 Subject: [PATCH 101/108] Cleanup and update files in rootdir (#319) * Cleanup and update files in rootdir * Update copyright year * Update pytest and hypothesis versions to ~= * Update README.rst * Update on push actions rule * Update CI status badge to GH Actions --- .github/workflows/test.yml | 4 +- .pylintrc | 377 ---------------------------------- README.rst | 15 +- dev-requirements.txt | 6 - doc/source/conf.py | 2 +- doc/source/devguide/index.rst | 12 +- instruments/__init__.py | 2 +- license/AUTHOR.TXT | 2 +- setup.cfg | 16 +- setup.py | 6 - tox.ini | 9 +- 11 files changed, 35 insertions(+), 416 deletions(-) delete mode 100644 .pylintrc delete mode 100644 dev-requirements.txt delete mode 100644 setup.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cecdff0f6..92d706da4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: Testing on: push: - branches: [ $default-branch ] + branches: [ master ] pull_request: jobs: @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: pip install --upgrade pre-commit - name: Run static checks via pre-commit - run: pre-commit run --all + run: pre-commit run --all --show-diff-on-failure test: runs-on: ubuntu-latest strategy: diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 9ecd7ce4d..000000000 --- a/.pylintrc +++ /dev/null @@ -1,377 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Allow optimization of some AST trees. This will activate a peephole AST -# optimizer, which will apply various small optimizations. For instance, it can -# be used to obtain the result of joining multiple strings with the addition -# operator. Joining a lot of strings can lead to a maximum recursion error in -# Pylint and this flag can prevent that. It has one side effect, the resulting -# AST will be different than the one from reality. -optimize-ast=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating,redefined-builtin,invalid-name,unsubscriptable-object,too-few-public-methods,too-many-public-methods,fixme,duplicate-code - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). This supports can work -# with qualified names. -ignored-classes= - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=120 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=10 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=yes - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_$|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - - -[BASIC] - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,input - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_|^test|^Mock|^mock|^Test - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=2 - - -[ELIF] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=10 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/README.rst b/README.rst index 4ac0cd1c6..48afcd32a 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,9 @@ InstrumentKit ============= -.. image:: https://img.shields.io/travis/Galvant/InstrumentKit/master.svg?maxAge=2592000 - :target: https://travis-ci.org/Galvant/InstrumentKit - :alt: Travis-CI build status +.. image:: https://github.com/Galvant/InstrumentKit/workflows/Testing/badge.svg?branch=master + :target: https://github.com/Galvant/InstrumentKit + :alt: Github Actions build status .. image:: https://img.shields.io/coveralls/Galvant/InstrumentKit/master.svg?maxAge=2592000 :target: https://coveralls.io/github/Galvant/InstrumentKit?branch=master @@ -38,6 +38,7 @@ Supported means of communication are: - Read/write from unix files (``open_file``) - USBTMC (``open_usbtmc``) - VXI11 over Ethernet (``open_vxi11``) +- Raw USB (``open_usb``) There is planned support for HiSLIP someday, but a good Python HiSLIP library will be needed first. @@ -97,7 +98,7 @@ measurement reading: .. code-block:: python >>> reading = inst.measure(inst.Mode.voltage_dc) - >>> print("Value: {}, units: {}".format(reading.magnitude, reading.units)) + >>> print(f"Value: {reading.magnitude}, units: {reading.units}") Due to the sheer number of commands most instruments support, not every single one is included in InstrumentKit. If there is a specific command you wish to @@ -154,14 +155,14 @@ and then it'll manage everything for you. $ pre-commit install Afterwards, when you go to make a git commit, all the plugins (as specified -by the configuration file `.pre-commit-config.yaml`) will be executed against +by the configuration file ``.pre-commit-config.yaml``) will be executed against the files that have changed. If any plugins make changes to the files, the commit will abort, allowing you to add those changes to your changeset and try to commit again. This tool will gate CI, so be sure to let them run and pass! You can also run all the hooks against all the files by directly calling -pre-commit, or though the `tox` environment: +pre-commit, or though the ``tox`` environment: .. code-block:: console @@ -173,7 +174,7 @@ or $ tox -e precommit -See the `pre-commit` documentation for more information. +See the ``pre-commit`` documentation for more information. License ------- diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index bd8644a31..000000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -mock -pytest==6.1.1 -pytest-mock -hypothesis==4.28.2 -pyvisa-sim -six diff --git a/doc/source/conf.py b/doc/source/conf.py index 224e2afe1..f34350c74 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -48,7 +48,7 @@ # General information about the project. project = "InstrumentKit Library" -copyright = "2013-2020, Steven Casagrande" +copyright = "2013-2022, Steven Casagrande" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/source/devguide/index.rst b/doc/source/devguide/index.rst index c1bcee52b..6f2aeff2e 100644 --- a/doc/source/devguide/index.rst +++ b/doc/source/devguide/index.rst @@ -21,11 +21,11 @@ Getting Started To get started with development for InstrumentKit, a few additional supporting packages must be installed. The core development packages can be found in -the supporting requirements file named ``dev-requirements.txt``. These will -allow you to run the tests. +`setup.cfg` under the `dev` extras dependencies. These will allow you to run +the tests. This repo also contains a series of static code checks that are managed -via `pre-commit`. This tool, once setup, will manage running all of these +via ``pre-commit``. This tool, once setup, will manage running all of these checks prior to each commit on your local machine.:: $ pip install pre-commit @@ -33,16 +33,16 @@ $ pre-commit install These checks are also run in CI, and must pass in order to generate a passing build. It is suggested that you install the git hooks, but -they can be run manually on all files. See the `pre-commit` homepage +they can be run manually on all files. See the ``pre-commit`` homepage for more information. Required Development Dependencies --------------------------------- Using ``pip``, these requirements can be obtained automatically by using the -provided ``dev-requirements.txt``:: +provided project definitions:: -$ pip install -r dev-requirements.txt +$ pip install -e .[dev] Optional Development Dependencies --------------------------------- diff --git a/instruments/__init__.py b/instruments/__init__.py index b790f3bce..8a0a9976c 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -51,4 +51,4 @@ __email__ = "scasagrande@galvant.ca" __license__ = "AGPLv3" -__copyright__ = "Copyright (c) 2012-2020 Steven Casagrande" +__copyright__ = "Copyright (c) 2012-2022 Steven Casagrande" diff --git a/license/AUTHOR.TXT b/license/AUTHOR.TXT index 1d8b4f96d..b8dfa8a70 100644 --- a/license/AUTHOR.TXT +++ b/license/AUTHOR.TXT @@ -7,4 +7,4 @@ Steven Casagrande scasagrande@galvant.ca twitter.com/stevecasagrande -2012-2020 +2012-2022 diff --git a/setup.cfg b/setup.cfg index 7b0dcf4d7..a47d75982 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,16 +28,26 @@ classifiers = include_package_data = True packages = find: install_requires = + pint>=0.16.1 pyserial>=3.3 - pyvisa>=1.9 - python-vxi11>=0.8 python-usbtmc + python-vxi11>=0.8 pyusb>=1.0 + pyvisa>=1.9 ruamel.yaml~=0.15.37 - pint>=0.16.1 [options.extras_require] numpy = numpy +dev = + coverage + hypothesis~=5.49.0 + mock + pytest-cov + pytest-mock + pytest-xdist + pytest~=6.2.5 + pyvisa-sim + six [options.packages.find] exclude = diff --git a/setup.py b/setup.py deleted file mode 100644 index bac24a43d..000000000 --- a/setup.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -import setuptools - -if __name__ == "__main__": - setuptools.setup() diff --git a/tox.ini b/tox.ini index 764e753dc..78cad4ac9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,19 +1,16 @@ [tox] envlist = py{36,37,38,39},py{36,37,38,39}-numpy,pylint +isolated_build = true [testenv] +extras = dev deps = - -rdev-requirements.txt - pytest-cov - pytest-xdist - coverage numpy: numpy commands = pytest -n auto --cov=instruments {toxinidir}/instruments/tests/ {posargs:} [testenv:precommit] basepython = python3 deps = - -rdev-requirements.txt pre-commit commands = - pre-commit run --all + pre-commit run --all --show-diff-on-failure From 07d8049ba71c6e821ec61279a46cb8b057a7dfea Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Sat, 22 Jan 2022 20:42:12 -0500 Subject: [PATCH 102/108] Skip no-commit-to-branch in CI (#320) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92d706da4..6145cab1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: pip install --upgrade pre-commit - name: Run static checks via pre-commit - run: pre-commit run --all --show-diff-on-failure + run: SKIP=no-commit-to-branch pre-commit run --all --show-diff-on-failure test: runs-on: ubuntu-latest strategy: From 5cdba01f5144faeb68e0b9232c813508878650ca Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Mon, 24 Jan 2022 23:01:37 -0500 Subject: [PATCH 103/108] Fix coveralls submission (#323) * Fix coveralls submission * Update path-to-lcov * Never changes, try with original solution again * Remove flag-name --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6145cab1f..44af27f7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,7 +50,7 @@ jobs: uses: AndreMiras/coveralls-python-action@develop with: parallel: true - flag-name: Unit Test + github-token: ${{ secrets.github_token }} coverage: needs: test From aa5114876504a2e0701d35330d91b0f70c46cdb6 Mon Sep 17 00:00:00 2001 From: Reto Trappitsch Date: Tue, 25 Jan 2022 19:03:08 -0500 Subject: [PATCH 104/108] Inner, outer class consistency (#321) * Move channel inside for SRS DG645 * Update docs for SRS * Newport Agilis: Move Axis into class and fix docs * Newport ESP301: Inner class consistencty and docs update - Move Axis class into instrument - Move HomeSearchMode, Units, MotorType enums into instrument * Add test for Newport ESP301 - default search mode in axis method * Tektronix DPO4104 move classes inside and update docs * Abstract instrument Oscilloscope: Move classes inside and add to docs * Tektronix TDS5xx: Move Channel, DataSource, Measurement inside class * Tektronix TedTDS224: Move Channel, DataSource inside * Cleanup and update docstrings of changed classes * Abstract Instrument PowerSupply: Move Channel inside class, add to doc * Abs Inst OpticalSpectrumAnalyzer: Move channel inside class, add to docs * Add electrometer to docs and sort instruments alphabetically * Fix copy/paste error in abstract instrument PowerSupply * Fix commenting in Test for SRS DG645 --- doc/source/apiref/instrument.rst | 34 +- doc/source/apiref/newport.rst | 12 - doc/source/apiref/srs.rst | 4 - doc/source/apiref/tektronix.rst | 8 - instruments/abstract_instruments/__init__.py | 19 +- .../optical_spectrum_analyzer.py | 68 +- .../abstract_instruments/oscilloscope.py | 155 +- .../abstract_instruments/power_supply.py | 139 +- instruments/glassman/glassmanfr.py | 6 +- instruments/hp/hp6624a.py | 4 +- instruments/hp/hp6652a.py | 6 +- instruments/hp/hpe3631a.py | 4 +- instruments/newport/__init__.py | 4 +- instruments/newport/agilis.py | 519 ++-- instruments/newport/newportesp301.py | 2344 +++++++++-------- instruments/rigol/rigolds1000.py | 10 +- instruments/srs/__init__.py | 2 +- instruments/srs/srsdg645.py | 112 +- instruments/tektronix/__init__.py | 6 +- instruments/tektronix/tekdpo4104.py | 294 +-- instruments/tektronix/tekdpo70000.py | 10 +- instruments/tektronix/tektds224.py | 262 +- instruments/tektronix/tektds5xx.py | 406 +-- instruments/teledyne/maui.py | 10 +- .../test_optical_spectrum_analyzer.py | 2 +- .../test_abstract_inst/test_oscilloscope.py | 4 +- .../test_abstract_inst/test_power_supply.py | 2 +- instruments/tests/test_newport/test_agilis.py | 4 +- .../tests/test_newport/test_newportesp301.py | 50 +- instruments/tests/test_srs/test_srsdg645.py | 4 +- .../tests/test_tektronix/test_tekdpo4104.py | 10 +- .../test_tektronix/test_tektronix_tds224.py | 4 +- .../tests/test_tektronix/test_tktds5xx.py | 6 +- instruments/yokogawa/yokogawa6370.py | 9 +- instruments/yokogawa/yokogawa7651.py | 9 +- 35 files changed, 2263 insertions(+), 2279 deletions(-) diff --git a/doc/source/apiref/instrument.rst b/doc/source/apiref/instrument.rst index be2f360f4..ad4b8f1f0 100644 --- a/doc/source/apiref/instrument.rst +++ b/doc/source/apiref/instrument.rst @@ -14,10 +14,10 @@ Instrument Base Classes :members: :undoc-members: -:class:`Multimeter` - Abstract class for multimeter instruments -=============================================================== +:class:`Electrometer` - Abstract class for electrometer instruments +=================================================================== -.. autoclass:: instruments.abstract_instruments.Multimeter +.. autoclass:: instruments.abstract_instruments.Electrometer :members: :undoc-members: @@ -28,6 +28,34 @@ Instrument Base Classes :members: :undoc-members: +:class:`Multimeter` - Abstract class for multimeter instruments +=============================================================== + +.. autoclass:: instruments.abstract_instruments.Multimeter + :members: + :undoc-members: + +:class:`Oscilloscope` - Abstract class for oscilloscope instruments +=================================================================== + +.. autoclass:: instruments.abstract_instruments.Oscilloscope + :members: + :undoc-members: + +:class:`OpticalSpectrumAnalyzer` - Abstract class for optical spectrum analyzer instruments +=========================================================================================== + +.. autoclass:: instruments.abstract_instruments.OpticalSpectrumAnalyzer + :members: + :undoc-members: + +:class:`PowerSupply` - Abstract class for power supply instruments +================================================================== + +.. autoclass:: instruments.abstract_instruments.PowerSupply + :members: + :undoc-members: + :class:`SignalGenerator` - Abstract class for Signal Generators =============================================================== diff --git a/doc/source/apiref/newport.rst b/doc/source/apiref/newport.rst index cecdef3f5..18165f107 100644 --- a/doc/source/apiref/newport.rst +++ b/doc/source/apiref/newport.rst @@ -14,10 +14,6 @@ Newport :members: :undoc-members: -.. autoclass:: _Axis - :members: - :undoc-members: - :class:`NewportESP301` Motor Controller ======================================= @@ -25,14 +21,6 @@ Newport :members: :undoc-members: -.. autoclass:: NewportESP301Axis - :members: - :undoc-members: - -.. autoclass:: NewportESP301HomeSearchMode - :members: - :undoc-members: - :class:`NewportError` ===================== diff --git a/doc/source/apiref/srs.rst b/doc/source/apiref/srs.rst index 9b6a4e697..31ce8f572 100644 --- a/doc/source/apiref/srs.rst +++ b/doc/source/apiref/srs.rst @@ -34,7 +34,3 @@ Stanford Research Systems .. autoclass:: SRSDG645 :members: :undoc-members: - -.. autoclass:: _SRSDG645Channel - :members: - :undoc-members: diff --git a/doc/source/apiref/tektronix.rst b/doc/source/apiref/tektronix.rst index 5d2201ec8..03123626b 100644 --- a/doc/source/apiref/tektronix.rst +++ b/doc/source/apiref/tektronix.rst @@ -21,14 +21,6 @@ Tektronix :members: :undoc-members: -.. autoclass:: _TekDPO4104DataSource - :members: - :undoc-members: - -.. autoclass:: _TekDPO4104Channel - :members: - :undoc-members: - :class:`TekDPO70000` Oscilloscope ================================= diff --git a/instruments/abstract_instruments/__init__.py b/instruments/abstract_instruments/__init__.py index d97ccb4a3..42a9591f5 100644 --- a/instruments/abstract_instruments/__init__.py +++ b/instruments/abstract_instruments/__init__.py @@ -5,20 +5,9 @@ from .instrument import Instrument -from .multimeter import Multimeter from .electrometer import Electrometer from .function_generator import FunctionGenerator -from .oscilloscope import ( - OscilloscopeChannel, - OscilloscopeDataSource, - Oscilloscope, -) -from .power_supply import ( - PowerSupplyChannel, - PowerSupply, -) - -from .optical_spectrum_analyzer import ( - OSAChannel, - OpticalSpectrumAnalyzer, -) +from .multimeter import Multimeter +from .oscilloscope import Oscilloscope +from .optical_spectrum_analyzer import OpticalSpectrumAnalyzer +from .power_supply import PowerSupply diff --git a/instruments/abstract_instruments/optical_spectrum_analyzer.py b/instruments/abstract_instruments/optical_spectrum_analyzer.py index 37ea21704..085d80960 100644 --- a/instruments/abstract_instruments/optical_spectrum_analyzer.py +++ b/instruments/abstract_instruments/optical_spectrum_analyzer.py @@ -13,52 +13,50 @@ # CLASSES ##################################################################### -class OSAChannel(metaclass=abc.ABCMeta): +class OpticalSpectrumAnalyzer(Instrument, metaclass=abc.ABCMeta): """ - Abstract base class for physical channels on an optical spectrum analyzer. + Abstract base class for optical spectrum analyzer instruments. All applicable concrete instruments should inherit from this ABC to provide a consistent interface to the user. """ - # METHODS # - - @abc.abstractmethod - def wavelength(self, bin_format=True): + class Channel(metaclass=abc.ABCMeta): """ - Gets the x-axis of the specified data source channel. This is an - abstract property. + Abstract base class for physical channels on an optical spectrum analyzer. - :param bool bin_format: If the waveform should be transfered in binary - (``True``) or ASCII (``False``) formats. - :return: The wavelength component of the waveform. - :rtype: `numpy.ndarray` + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. """ - raise NotImplementedError - @abc.abstractmethod - def data(self, bin_format=True): - """ - Gets the y-axis of the specified data source channel. This is an - abstract property. - - :param bool bin_format: If the waveform should be transfered in binary - (``True``) or ASCII (``False``) formats. - :return: The y-component of the waveform. - :rtype: `numpy.ndarray` - """ - raise NotImplementedError - - -class OpticalSpectrumAnalyzer(Instrument, metaclass=abc.ABCMeta): - - """ - Abstract base class for optical spectrum analyzer instruments. - - All applicable concrete instruments should inherit from this ABC to - provide a consistent interface to the user. - """ + # METHODS # + + @abc.abstractmethod + def wavelength(self, bin_format=True): + """ + Gets the x-axis of the specified data source channel. This is an + abstract property. + + :param bool bin_format: If the waveform should be transfered in binary + (``True``) or ASCII (``False``) formats. + :return: The wavelength component of the waveform. + :rtype: `numpy.ndarray` + """ + raise NotImplementedError + + @abc.abstractmethod + def data(self, bin_format=True): + """ + Gets the y-axis of the specified data source channel. This is an + abstract property. + + :param bool bin_format: If the waveform should be transfered in binary + (``True``) or ASCII (``False``) formats. + :return: The y-component of the waveform. + :rtype: `numpy.ndarray` + """ + raise NotImplementedError # PROPERTIES # diff --git a/instruments/abstract_instruments/oscilloscope.py b/instruments/abstract_instruments/oscilloscope.py index d75ecfaf0..34dcbe8cf 100644 --- a/instruments/abstract_instruments/oscilloscope.py +++ b/instruments/abstract_instruments/oscilloscope.py @@ -13,106 +13,103 @@ # CLASSES ##################################################################### -class OscilloscopeDataSource(metaclass=abc.ABCMeta): +class Oscilloscope(Instrument, metaclass=abc.ABCMeta): """ - Abstract base class for data sources (physical channels, math, ref) on - an oscilloscope. + Abstract base class for oscilloscope instruments. All applicable concrete instruments should inherit from this ABC to provide a consistent interface to the user. """ - def __init__(self, parent, name): - self._parent = parent - self._name = name - self._old_dsrc = None - - def __enter__(self): - self._old_dsrc = self._parent.data_source - if self._old_dsrc != self: - # Set the new data source, and let __exit__ cleanup. - self._parent.data_source = self - else: - # There's nothing to do or undo in this case. - self._old_dsrc = None - - def __exit__(self, type, value, traceback): - if self._old_dsrc is not None: - self._parent.data_source = self._old_dsrc - - def __eq__(self, other): - if not isinstance(other, type(self)): - return NotImplemented - - return other.name == self.name - - __hash__ = None - - # PROPERTIES # - - @property - @abc.abstractmethod - def name(self): + class Channel(metaclass=abc.ABCMeta): """ - Gets the name of the channel. This is an abstract property. + Abstract base class for physical channels on an oscilloscope. - :type: `str` + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. """ - raise NotImplementedError - # METHODS # - - @abc.abstractmethod - def read_waveform(self, bin_format=True): - """ - Gets the waveform of the specified data source channel. This is an - abstract property. + # PROPERTIES # - :param bool bin_format: If the waveform should be transfered in binary - (``True``) or ASCII (``False``) formats. - :return: The waveform with both x and y components. - :rtype: `numpy.ndarray` - """ - raise NotImplementedError + @property + @abc.abstractmethod + def coupling(self): + """ + Gets/sets the coupling setting for the oscilloscope. This is an + abstract method. + :type: `~enum.Enum` + """ + raise NotImplementedError -class OscilloscopeChannel(metaclass=abc.ABCMeta): + @coupling.setter + @abc.abstractmethod + def coupling(self, newval): + raise NotImplementedError - """ - Abstract base class for physical channels on an oscilloscope. + class DataSource(metaclass=abc.ABCMeta): - All applicable concrete instruments should inherit from this ABC to - provide a consistent interface to the user. - """ - - # PROPERTIES # - - @property - @abc.abstractmethod - def coupling(self): """ - Gets/sets the coupling setting for the oscilloscope. This is an - abstract method. + Abstract base class for data sources (physical channels, math, ref) on + an oscilloscope. - :type: `~enum.Enum` + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. """ - raise NotImplementedError - - @coupling.setter - @abc.abstractmethod - def coupling(self, newval): - raise NotImplementedError - -class Oscilloscope(Instrument, metaclass=abc.ABCMeta): - - """ - Abstract base class for oscilloscope instruments. + def __init__(self, parent, name): + self._parent = parent + self._name = name + self._old_dsrc = None - All applicable concrete instruments should inherit from this ABC to - provide a consistent interface to the user. - """ + def __enter__(self): + self._old_dsrc = self._parent.data_source + if self._old_dsrc != self: + # Set the new data source, and let __exit__ cleanup. + self._parent.data_source = self + else: + # There's nothing to do or undo in this case. + self._old_dsrc = None + + def __exit__(self, type, value, traceback): + if self._old_dsrc is not None: + self._parent.data_source = self._old_dsrc + + def __eq__(self, other): + if not isinstance(other, type(self)): + return NotImplemented + + return other.name == self.name + + __hash__ = None + + # PROPERTIES # + + @property + @abc.abstractmethod + def name(self): + """ + Gets the name of the channel. This is an abstract property. + + :type: `str` + """ + raise NotImplementedError + + # METHODS # + + @abc.abstractmethod + def read_waveform(self, bin_format=True): + """ + Gets the waveform of the specified data source channel. This is an + abstract property. + + :param bool bin_format: If the waveform should be transfered in binary + (``True``) or ASCII (``False``) formats. + :return: The waveform with both x and y components. + :rtype: `numpy.ndarray` + """ + raise NotImplementedError # PROPERTIES # diff --git a/instruments/abstract_instruments/power_supply.py b/instruments/abstract_instruments/power_supply.py index f604e7cfb..32fa6d5aa 100644 --- a/instruments/abstract_instruments/power_supply.py +++ b/instruments/abstract_instruments/power_supply.py @@ -12,86 +12,83 @@ # CLASSES ##################################################################### -class PowerSupplyChannel(metaclass=abc.ABCMeta): - +class PowerSupply(Instrument, metaclass=abc.ABCMeta): """ - Abstract base class for power supply output channels. + Abstract base class for power supply instruments. All applicable concrete instruments should inherit from this ABC to provide a consistent interface to the user. """ - # PROPERTIES # - - @property - @abc.abstractmethod - def mode(self): - """ - Gets/sets the output mode for the power supply channel. This is an - abstract method. - - :type: `~enum.Enum` - """ - - @mode.setter - @abc.abstractmethod - def mode(self, newval): - pass - - @property - @abc.abstractmethod - def voltage(self): - """ - Gets/sets the output voltage for the power supply channel. This is an - abstract method. - - :type: `~pint.Quantity` - """ - - @voltage.setter - @abc.abstractmethod - def voltage(self, newval): - pass - - @property - @abc.abstractmethod - def current(self): + class Channel(metaclass=abc.ABCMeta): """ - Gets/sets the output current for the power supply channel. This is an - abstract method. - - :type: `~pint.Quantity` - """ - - @current.setter - @abc.abstractmethod - def current(self, newval): - pass + Abstract base class for power supply output channels. - @property - @abc.abstractmethod - def output(self): + All applicable concrete instruments should inherit from this ABC to + provide a consistent interface to the user. """ - Gets/sets the output status for the power supply channel. This is an - abstract method. - :type: `bool` - """ - - @output.setter - @abc.abstractmethod - def output(self, newval): - pass - - -class PowerSupply(Instrument, metaclass=abc.ABCMeta): - - """ - Abstract base class for power supply instruments. - - All applicable concrete instruments should inherit from this ABC to - provide a consistent interface to the user. - """ + # PROPERTIES # + + @property + @abc.abstractmethod + def mode(self): + """ + Gets/sets the output mode for the power supply channel. This is an + abstract method. + + :type: `~enum.Enum` + """ + + @mode.setter + @abc.abstractmethod + def mode(self, newval): + pass + + @property + @abc.abstractmethod + def voltage(self): + """ + Gets/sets the output voltage for the power supply channel. This is an + abstract method. + + :type: `~pint.Quantity` + """ + + @voltage.setter + @abc.abstractmethod + def voltage(self, newval): + pass + + @property + @abc.abstractmethod + def current(self): + """ + Gets/sets the output current for the power supply channel. This is an + abstract method. + + :type: `~pint.Quantity` + """ + + @current.setter + @abc.abstractmethod + def current(self, newval): + pass + + @property + @abc.abstractmethod + def output(self): + """ + Gets/sets the output status for the power supply channel. This is an + abstract method. + + :type: `bool` + """ + + @output.setter + @abc.abstractmethod + def output(self, newval): + pass # PROPERTIES # @@ -104,7 +101,7 @@ def channel(self): This is an abstract method. - :rtype: `PowerSupplyChannel` + :rtype: `PowerSupply.Channel` """ raise NotImplementedError diff --git a/instruments/glassman/glassmanfr.py b/instruments/glassman/glassmanfr.py index 01f0a53b0..381d366b2 100644 --- a/instruments/glassman/glassmanfr.py +++ b/instruments/glassman/glassmanfr.py @@ -34,20 +34,20 @@ from struct import unpack from enum import Enum -from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel +from instruments.abstract_instruments import PowerSupply from instruments.units import ureg as u from instruments.util_fns import assume_units # CLASSES ##################################################################### -class GlassmanFR(PowerSupply, PowerSupplyChannel): +class GlassmanFR(PowerSupply, PowerSupply.Channel): """ The GlassmanFR is a single output power supply. Because it is a single channel output, this object inherits from both - PowerSupply and PowerSupplyChannel. + PowerSupply and PowerSupply.Channel. This class should work for any of the Glassman FR Series power supplies and is also likely to work for the EJ, ET, EY and FJ Series which seem diff --git a/instruments/hp/hp6624a.py b/instruments/hp/hp6624a.py index ff639af95..6762ade5d 100644 --- a/instruments/hp/hp6624a.py +++ b/instruments/hp/hp6624a.py @@ -7,7 +7,7 @@ from enum import Enum -from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel +from instruments.abstract_instruments import PowerSupply from instruments.units import ureg as u from instruments.util_fns import ProxyList, unitful_property, bool_property @@ -37,7 +37,7 @@ def __init__(self, filelike): # INNER CLASSES # - class Channel(PowerSupplyChannel): + class Channel(PowerSupply.Channel): """ Class representing a power output channel on the HP6624a. diff --git a/instruments/hp/hp6652a.py b/instruments/hp/hp6652a.py index 3f5894a52..31bbae8e8 100644 --- a/instruments/hp/hp6652a.py +++ b/instruments/hp/hp6652a.py @@ -10,20 +10,20 @@ from instruments.units import ureg as u -from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel +from instruments.abstract_instruments import PowerSupply from instruments.util_fns import unitful_property, bool_property # CLASSES ##################################################################### -class HP6652a(PowerSupply, PowerSupplyChannel): +class HP6652a(PowerSupply, PowerSupply.Channel): """ The HP6652a is a single output power supply. Because it is a single channel output, this object inherits from both - PowerSupply and PowerSupplyChannel. + PowerSupply and PowerSupply.Channel. According to the manual, this class MIGHT be usable for any HP power supply with a model number HP66XYA, where X is in {4,5,7,8,9} and Y is a digit(?). diff --git a/instruments/hp/hpe3631a.py b/instruments/hp/hpe3631a.py index f7f768f34..de1dc3186 100644 --- a/instruments/hp/hpe3631a.py +++ b/instruments/hp/hpe3631a.py @@ -36,7 +36,7 @@ from instruments.units import ureg as u -from instruments.abstract_instruments import PowerSupply, PowerSupplyChannel +from instruments.abstract_instruments import PowerSupply from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ( int_property, @@ -50,7 +50,7 @@ # CLASSES ##################################################################### -class HPe3631a(PowerSupply, PowerSupplyChannel, SCPIInstrument): +class HPe3631a(PowerSupply, PowerSupply.Channel, SCPIInstrument): """ The HPe3631a is a three channels voltage/current supply. diff --git a/instruments/newport/__init__.py b/instruments/newport/__init__.py index 74c9aa1e6..200052102 100644 --- a/instruments/newport/__init__.py +++ b/instruments/newport/__init__.py @@ -3,9 +3,9 @@ Module containing Newport instruments """ -from .agilis import _Axis, AGUC2 +from .agilis import AGUC2 from .errors import NewportError -from .newportesp301 import NewportESP301, NewportESP301Axis, NewportESP301HomeSearchMode +from .newportesp301 import NewportESP301 from .newport_pmc8742 import PicoMotorController8742 diff --git a/instruments/newport/agilis.py b/instruments/newport/agilis.py index ccbde6019..9761b1585 100644 --- a/instruments/newport/agilis.py +++ b/instruments/newport/agilis.py @@ -38,264 +38,6 @@ # CLASSES ##################################################################### -class _Axis: - - """ - Class representing one axis attached to a Controller. This will likely - work with the AG-UC8 controller as well. - - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by a Controller class - """ - - def __init__(self, cont, ax): - if not isinstance(cont, AGUC2): - raise TypeError("Don't do that.") - - # set axis integer - if isinstance(ax, AGUC2.Axes): - self._ax = ax.value - else: - self._ax = ax - - # set controller - self._cont = cont - - # PROPERTIES # - - @property - def axis_status(self): - """ - Returns the status of the current axis. - """ - resp = self._cont.ag_query(f"{int(self._ax)} TS") - if resp.find("TS") == -1: - return "Status code query failed." - - resp = int(resp.replace(str(int(self._ax)) + "TS", "")) - status_message = agilis_status_message(resp) - return status_message - - @property - def jog(self): - """ - Start jog motion / get jog mode - Defined jog steps are defined with `step_amplitude` function (default - 16). If a jog mode is supplied, the jog motion is started. Otherwise - the current jog mode is queried. Valid jog modes are: - - -4 — Negative direction, 666 steps/s at defined step amplitude. - -3 — Negative direction, 1700 steps/s at max. step amplitude. - -2 — Negative direction, 100 step/s at max. step amplitude. - -1 — Negative direction, 5 steps/s at defined step amplitude. - 0 — No move, go to READY state. - 1 — Positive direction, 5 steps/s at defined step amplitude. - 2 — Positive direction, 100 steps/s at max. step amplitude. - 3 — Positive direction, 1700 steps/s at max. step amplitude. - 4 — Positive direction, 666 steps/s at defined step amplitude. - - :return: Jog motion set - :rtype: `int` - """ - resp = self._cont.ag_query(f"{int(self._ax)} JA?") - return int(resp.split("JA")[1]) - - @jog.setter - def jog(self, mode): - mode = int(mode) - if mode < -4 or mode > 4: - raise ValueError("Jog mode out of range. Must be between -4 and " "4.") - - self._cont.ag_sendcmd(f"{int(self._ax)} JA {mode}") - - @property - def number_of_steps(self): - """ - Returns the number of accumulated steps in forward direction minus - the number of steps in backward direction since powering the - controller or since the last ZP (zero position) command, whatever - was last. - - Note: - The step size of the Agilis devices are not 100% repeatable and - vary between forward and backward direction. Furthermore, the step - size can be modified using the SU command. Consequently, the TP - command provides only limited information about the actual position - of the device. In particular, an Agilis device can be at very - different positions even though a TP command may return the same - result. - - :return: Number of steps - :rtype: int - """ - resp = self._cont.ag_query(f"{int(self._ax)} TP") - return int(resp.split("TP")[1]) - - @property - def move_relative(self): - """ - Moves the axis by nn steps / Queries the status of the axis. - Steps must be given a number that can be converted to a signed integer - between -2,147,483,648 and 2,147,483,647. - If queried, command returns the current target position. At least this - is the expected behaviour, never worked with the rotation stage. - """ - resp = self._cont.ag_query(f"{int(self._ax)} PR?") - return int(resp.split("PR")[1]) - - @move_relative.setter - def move_relative(self, steps): - steps = int(steps) - if steps < -2147483648 or steps > 2147483647: - raise ValueError( - "Number of steps are out of range. They must be " - "between -2,147,483,648 and 2,147,483,647" - ) - - self._cont.ag_sendcmd(f"{int(self._ax)} PR {steps}") - - @property - def move_to_limit(self): - """ - UNTESTED: SEE COMMENT ON TOP - - The command functions properly only with devices that feature a - limit switch like models AG-LS25, AG-M050L and AG-M100L. - - Starts a jog motion at a defined speed to the limit and stops - automatically when the limit is activated. See `jog` command for - details on available modes. - - Returns the distance of the current position to the limit in - 1/1000th of the total travel. - """ - resp = self._cont.ag_query(f"{int(self._ax)} MA?") - return int(resp.split("MA")[1]) - - @move_to_limit.setter - def move_to_limit(self, mode): - mode = int(mode) - if mode < -4 or mode > 4: - raise ValueError("Jog mode out of range. Must be between -4 and " "4.") - - self._cont.ag_sendcmd(f"{int(self._ax)} MA {mode}") - - @property - def step_amplitude(self): - """ - Sets / Gets the step_amplitude. - - Sets the step amplitude (step size) in positive and / or negative - direction. If the parameter is positive, it will set the step - amplitude in the forward direction. If the parameter is negative, - it will set the step amplitude in the backward direction. You can also - provide a tuple or list of two values (one positive, one negative), - which will set both values. - Valid values are between -50 and 50, except for 0. - - :return: Tuple of first negative, then positive step amplitude - response. - :rtype: (`int`, `int`) - """ - resp_neg = self._cont.ag_query(f"{int(self._ax)} SU-?") - resp_pos = self._cont.ag_query(f"{int(self._ax)} SU+?") - return int(resp_neg.split("SU")[1]), int(resp_pos.split("SU")[1]) - - @step_amplitude.setter - def step_amplitude(self, nns): - if not isinstance(nns, tuple) and not isinstance(nns, list): - nns = [nns] - - # check all values for validity - for nn in nns: - nn = int(nn) - if nn < -50 or nn > 50 or nn == 0: - raise ValueError( - "Step amplitude {} outside the valid range. " - "It must be between -50 and -1 or between " - "1 and 50.".format(nn) - ) - - for nn in nns: - self._cont.ag_sendcmd(f"{int(self._ax)} SU {int(nn)}") - - @property - def step_delay(self): - """ - Sets/gets the step delay of stepping mode. The delay applies for both - positive and negative directions. The delay is programmed as multiple - of 10µs. For example, a delay of 40 is equivalent to - 40 x 10 µs = 400 µs. The maximum value of the parameter is equal to a - delay of 2 seconds between pulses. By default, after reset, the value - is 0. - Setter: value must be integer between 0 and 200000 included - - :return: Step delay - :rtype: `int` - """ - resp = self._cont.ag_query(f"{int(self._ax)} DL?") - return int(resp.split("DL")[1]) - - @step_delay.setter - def step_delay(self, nn): - nn = int(nn) - if nn < 0 or nn > 200000: - raise ValueError( - "Step delay is out of range. It must be between " "0 and 200000." - ) - - self._cont.ag_sendcmd(f"{int(self._ax)} DL {nn}") - - # MODES # - - def am_i_still(self, max_retries=5): - """ - Function to test if an axis stands still. It queries the status of - the given axis and returns True (if axis is still) or False if it is - moving. - The reason this routine is implemented is because the status messages - can time out. If a timeout occurs, this routine will retry the query - until `max_retries` is reached. If query is still not successful, an - IOError will be raised. - - :param int max_retries: Maximum number of retries - - :return: True if the axis is still, False if the axis is moving - :rtype: bool - """ - retries = 0 - - while retries < max_retries: - status = self.axis_status - if status == agilis_status_message(0): - return True - elif ( - status == agilis_status_message(1) - or status == agilis_status_message(2) - or status == agilis_status_message(3) - ): - return False - else: - retries += 1 - - raise OSError( - "The function `am_i_still` ran out of maximum retries. " - "Could not query the status of the axis." - ) - - def stop(self): - """ - Stops the axis. This is useful to interrupt a jogging motion. - """ - self._cont.ag_sendcmd(f"{int(self._ax)} ST") - - def zero_position(self): - """ - Resets the step counter to zero. See `number_of_steps` for details. - """ - self._cont.ag_sendcmd(f"{int(self._ax)} ZP") - - class AGUC2(Instrument): """ @@ -351,6 +93,263 @@ def __init__(self, filelike): self._remote_mode = False self._sleep_time = 0.25 + class Axis: + + """ + Class representing one axis attached to a Controller. This will likely + work with the AG-UC8 controller as well. + + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by a Controller class + """ + + def __init__(self, cont, ax): + if not isinstance(cont, AGUC2): + raise TypeError("Don't do that.") + + # set axis integer + if isinstance(ax, AGUC2.Axes): + self._ax = ax.value + else: + self._ax = ax + + # set controller + self._cont = cont + + # PROPERTIES # + + @property + def axis_status(self): + """ + Returns the status of the current axis. + """ + resp = self._cont.ag_query(f"{int(self._ax)} TS") + if resp.find("TS") == -1: + return "Status code query failed." + + resp = int(resp.replace(str(int(self._ax)) + "TS", "")) + status_message = agilis_status_message(resp) + return status_message + + @property + def jog(self): + """ + Start jog motion / get jog mode + Defined jog steps are defined with `step_amplitude` function (default + 16). If a jog mode is supplied, the jog motion is started. Otherwise + the current jog mode is queried. Valid jog modes are: + + -4 — Negative direction, 666 steps/s at defined step amplitude. + -3 — Negative direction, 1700 steps/s at max. step amplitude. + -2 — Negative direction, 100 step/s at max. step amplitude. + -1 — Negative direction, 5 steps/s at defined step amplitude. + 0 — No move, go to READY state. + 1 — Positive direction, 5 steps/s at defined step amplitude. + 2 — Positive direction, 100 steps/s at max. step amplitude. + 3 — Positive direction, 1700 steps/s at max. step amplitude. + 4 — Positive direction, 666 steps/s at defined step amplitude. + + :return: Jog motion set + :rtype: `int` + """ + resp = self._cont.ag_query(f"{int(self._ax)} JA?") + return int(resp.split("JA")[1]) + + @jog.setter + def jog(self, mode): + mode = int(mode) + if mode < -4 or mode > 4: + raise ValueError("Jog mode out of range. Must be between -4 and " "4.") + + self._cont.ag_sendcmd(f"{int(self._ax)} JA {mode}") + + @property + def number_of_steps(self): + """ + Returns the number of accumulated steps in forward direction minus + the number of steps in backward direction since powering the + controller or since the last ZP (zero position) command, whatever + was last. + + Note: + The step size of the Agilis devices are not 100% repeatable and + vary between forward and backward direction. Furthermore, the step + size can be modified using the SU command. Consequently, the TP + command provides only limited information about the actual position + of the device. In particular, an Agilis device can be at very + different positions even though a TP command may return the same + result. + + :return: Number of steps + :rtype: int + """ + resp = self._cont.ag_query(f"{int(self._ax)} TP") + return int(resp.split("TP")[1]) + + @property + def move_relative(self): + """ + Moves the axis by nn steps / Queries the status of the axis. + Steps must be given a number that can be converted to a signed integer + between -2,147,483,648 and 2,147,483,647. + If queried, command returns the current target position. At least this + is the expected behaviour, never worked with the rotation stage. + """ + resp = self._cont.ag_query(f"{int(self._ax)} PR?") + return int(resp.split("PR")[1]) + + @move_relative.setter + def move_relative(self, steps): + steps = int(steps) + if steps < -2147483648 or steps > 2147483647: + raise ValueError( + "Number of steps are out of range. They must be " + "between -2,147,483,648 and 2,147,483,647" + ) + + self._cont.ag_sendcmd(f"{int(self._ax)} PR {steps}") + + @property + def move_to_limit(self): + """ + UNTESTED: SEE COMMENT ON TOP + + The command functions properly only with devices that feature a + limit switch like models AG-LS25, AG-M050L and AG-M100L. + + Starts a jog motion at a defined speed to the limit and stops + automatically when the limit is activated. See `jog` command for + details on available modes. + + Returns the distance of the current position to the limit in + 1/1000th of the total travel. + """ + resp = self._cont.ag_query(f"{int(self._ax)} MA?") + return int(resp.split("MA")[1]) + + @move_to_limit.setter + def move_to_limit(self, mode): + mode = int(mode) + if mode < -4 or mode > 4: + raise ValueError("Jog mode out of range. Must be between -4 and " "4.") + + self._cont.ag_sendcmd(f"{int(self._ax)} MA {mode}") + + @property + def step_amplitude(self): + """ + Sets / Gets the step_amplitude. + + Sets the step amplitude (step size) in positive and / or negative + direction. If the parameter is positive, it will set the step + amplitude in the forward direction. If the parameter is negative, + it will set the step amplitude in the backward direction. You can also + provide a tuple or list of two values (one positive, one negative), + which will set both values. + Valid values are between -50 and 50, except for 0. + + :return: Tuple of first negative, then positive step amplitude + response. + :rtype: (`int`, `int`) + """ + resp_neg = self._cont.ag_query(f"{int(self._ax)} SU-?") + resp_pos = self._cont.ag_query(f"{int(self._ax)} SU+?") + return int(resp_neg.split("SU")[1]), int(resp_pos.split("SU")[1]) + + @step_amplitude.setter + def step_amplitude(self, nns): + if not isinstance(nns, tuple) and not isinstance(nns, list): + nns = [nns] + + # check all values for validity + for nn in nns: + nn = int(nn) + if nn < -50 or nn > 50 or nn == 0: + raise ValueError( + "Step amplitude {} outside the valid range. " + "It must be between -50 and -1 or between " + "1 and 50.".format(nn) + ) + + for nn in nns: + self._cont.ag_sendcmd(f"{int(self._ax)} SU {int(nn)}") + + @property + def step_delay(self): + """ + Sets/gets the step delay of stepping mode. The delay applies for both + positive and negative directions. The delay is programmed as multiple + of 10µs. For example, a delay of 40 is equivalent to + 40 x 10 µs = 400 µs. The maximum value of the parameter is equal to a + delay of 2 seconds between pulses. By default, after reset, the value + is 0. + Setter: value must be integer between 0 and 200000 included + + :return: Step delay + :rtype: `int` + """ + resp = self._cont.ag_query(f"{int(self._ax)} DL?") + return int(resp.split("DL")[1]) + + @step_delay.setter + def step_delay(self, nn): + nn = int(nn) + if nn < 0 or nn > 200000: + raise ValueError( + "Step delay is out of range. It must be between " "0 and 200000." + ) + + self._cont.ag_sendcmd(f"{int(self._ax)} DL {nn}") + + # MODES # + + def am_i_still(self, max_retries=5): + """ + Function to test if an axis stands still. It queries the status of + the given axis and returns True (if axis is still) or False if it is + moving. + The reason this routine is implemented is because the status messages + can time out. If a timeout occurs, this routine will retry the query + until `max_retries` is reached. If query is still not successful, an + IOError will be raised. + + :param int max_retries: Maximum number of retries + + :return: True if the axis is still, False if the axis is moving + :rtype: bool + """ + retries = 0 + + while retries < max_retries: + status = self.axis_status + if status == agilis_status_message(0): + return True + elif ( + status == agilis_status_message(1) + or status == agilis_status_message(2) + or status == agilis_status_message(3) + ): + return False + else: + retries += 1 + + raise OSError( + "The function `am_i_still` ran out of maximum retries. " + "Could not query the status of the axis." + ) + + def stop(self): + """ + Stops the axis. This is useful to interrupt a jogging motion. + """ + self._cont.ag_sendcmd(f"{int(self._ax)} ST") + + def zero_position(self): + """ + Resets the step counter to zero. See `number_of_steps` for details. + """ + self._cont.ag_sendcmd(f"{int(self._ax)} ZP") + # ENUMS # class Axes(IntEnum): @@ -379,10 +378,10 @@ def axis(self): See example in `AGUC2` for a more details - :rtype: `_Axis` + :rtype: `AGUC2.Axis` """ self.enable_remote_mode = True - return ProxyList(self, _Axis, AGUC2.Axes) + return ProxyList(self, self.Axis, AGUC2.Axes) @property def enable_remote_mode(self): diff --git a/instruments/newport/newportesp301.py b/instruments/newport/newportesp301.py index 43bcdfb66..cfa6b3d2f 100644 --- a/instruments/newport/newportesp301.py +++ b/instruments/newport/newportesp301.py @@ -19,63 +19,6 @@ from instruments.units import ureg as u from instruments.util_fns import assume_units, ProxyList -# ENUMS ####################################################################### - - -class NewportESP301HomeSearchMode(IntEnum): - - """ - Enum containing different search modes code - """ - - #: Search along specified axes for the +0 position. - zero_position_count = 0 - #: Search for combined Home and Index signals. - home_index_signals = 1 - #: Search only for the Home signal. - home_signal_only = 2 - #: Search for the positive limit signal. - pos_limit_signal = 3 - #: Search for the negative limit signal. - neg_limit_signal = 4 - #: Search for the positive limit and Index signals. - pos_index_signals = 5 - #: Search for the negative limit and Index signals. - neg_index_signals = 6 - - -class NewportESP301Units(IntEnum): - - """ - Enum containing what `units` return means. - """ - - encoder_step = 0 - motor_step = 1 - millimeter = 2 - micrometer = 3 - inches = 4 - milli_inches = 5 - micro_inches = 6 - degree = 7 - gradian = 8 - radian = 9 - milliradian = 10 - microradian = 11 - - -class NewportESP301MotorType(IntEnum): - - """ - Enum for different motor types. - """ - - undefined = 0 - dc_servo = 1 - stepper_motor = 2 - commutated_stepper_motor = 3 - commutated_brushless_servo = 4 - # CLASSES ##################################################################### @@ -100,7 +43,1178 @@ def __init__(self, filelike): self._bulk_query_resp = "" self.terminator = "\r" - # PROPERTIES ## + class Axis: + + """ + Encapsulates communication concerning a single axis + of an ESP-301 controller. This class should not be + instantiated by the user directly, but is + returned by `NewportESP301.axis`. + """ + + # quantities micro inch + # micro_inch = u.UnitQuantity('micro-inch', u.inch / 1e6, symbol='uin') + micro_inch = u.uinch + + # Some more work might need to be done here to make + # the encoder_step and motor_step functional + # I really don't have a concrete idea how I'm + # going to do this until I have a physical device + + _unit_dict = { + 0: u.count, + 1: u.count, + 2: u.mm, + 3: u.um, + 4: u.inch, + 5: u.mil, + 6: micro_inch, # compound unit for micro-inch + 7: u.deg, + 8: u.grad, + 9: u.rad, + 10: u.mrad, + 11: u.urad, + } + + def __init__(self, controller, axis_id): + if not isinstance(controller, NewportESP301): + raise TypeError( + "Axis must be controlled by a Newport ESP-301 " "motor controller." + ) + + self._controller = controller + self._axis_id = axis_id + 1 + + self._units = self.units + + # CONTEXT MANAGERS ## + + @contextmanager + def _units_of(self, units): + """ + Sets the units for the corresponding axis to a those given by an integer + label (see `NewportESP301.Units`), ensuring that the units are properly + reset at the completion of the context manager. + """ + old_units = self._get_units() + self._set_units(units) + yield + self._set_units(old_units) + + # PRIVATE METHODS ## + + def _get_units(self): + """ + Returns the integer label for the current units set for this axis. + + .. seealso:: + NewportESP301.Units + """ + return self._controller.Units( + int(self._newport_cmd("SN?", target=self.axis_id)) + ) + + def _set_units(self, new_units): + return self._newport_cmd("SN", target=self.axis_id, params=[int(new_units)]) + + # PROPERTIES ## + + @property + def axis_id(self): + """ + Get axis number of Newport Controller + + :type: `int` + """ + return self._axis_id + + @property + def is_motion_done(self): + """ + `True` if and only if all motion commands have + completed. This method can be used to wait for + a motion command to complete before sending the next + command. + + :type: `bool` + """ + return bool(int(self._newport_cmd("MD?", target=self.axis_id))) + + @property + def acceleration(self): + """ + Gets/sets the axis acceleration + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport unit + :type: `~pint.Quantity` or `float` + """ + + return assume_units( + float(self._newport_cmd("AC?", target=self.axis_id)), + self._units / (u.s ** 2), + ) + + @acceleration.setter + def acceleration(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) + self._newport_cmd("AC", target=self.axis_id, params=[newval]) + + @property + def deceleration(self): + """ + Gets/sets the axis deceleration + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s^2}` + :type: `~pint.Quantity` or float + """ + return assume_units( + float(self._newport_cmd("AG?", target=self.axis_id)), + self._units / (u.s ** 2), + ) + + @deceleration.setter + def deceleration(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) + self._newport_cmd("AG", target=self.axis_id, params=[newval]) + + @property + def estop_deceleration(self): + """ + Gets/sets the axis estop deceleration + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s^2}` + :type: `~pint.Quantity` or float + """ + return assume_units( + float(self._newport_cmd("AE?", target=self.axis_id)), + self._units / (u.s ** 2), + ) + + @estop_deceleration.setter + def estop_deceleration(self, decel): + decel = float( + assume_units(decel, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) + self._newport_cmd("AE", target=self.axis_id, params=[decel]) + + @property + def jerk(self): + """ + Gets/sets the jerk rate for the controller + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport unit + :type: `~pint.Quantity` or `float` + """ + + return assume_units( + float(self._newport_cmd("JK?", target=self.axis_id)), + self._units / (u.s ** 3), + ) + + @jerk.setter + def jerk(self, jerk): + jerk = float( + assume_units(jerk, self._units / (u.s ** 3)) + .to(self._units / (u.s ** 3)) + .magnitude + ) + self._newport_cmd("JK", target=self.axis_id, params=[jerk]) + + @property + def velocity(self): + """ + Gets/sets the axis velocity + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("VA?", target=self.axis_id)), self._units / u.s + ) + + @velocity.setter + def velocity(self, velocity): + velocity = float( + assume_units(velocity, self._units / (u.s)) + .to(self._units / u.s) + .magnitude + ) + self._newport_cmd("VA", target=self.axis_id, params=[velocity]) + + @property + def max_velocity(self): + """ + Gets/sets the axis maximum velocity + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("VU?", target=self.axis_id)), self._units / u.s + ) + + @max_velocity.setter + def max_velocity(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) + self._newport_cmd("VU", target=self.axis_id, params=[newval]) + + @property + def max_base_velocity(self): + """ + Gets/sets the maximum base velocity for stepper motors + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("VB?", target=self.axis_id)), self._units / u.s + ) + + @max_base_velocity.setter + def max_base_velocity(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) + self._newport_cmd("VB", target=self.axis_id, params=[newval]) + + @property + def jog_high_velocity(self): + """ + Gets/sets the axis jog high velocity + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("JH?", target=self.axis_id)), self._units / u.s + ) + + @jog_high_velocity.setter + def jog_high_velocity(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) + self._newport_cmd("JH", target=self.axis_id, params=[newval]) + + @property + def jog_low_velocity(self): + """ + Gets/sets the axis jog low velocity + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("JW?", target=self.axis_id)), self._units / u.s + ) + + @jog_low_velocity.setter + def jog_low_velocity(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) + self._newport_cmd("JW", target=self.axis_id, params=[newval]) + + @property + def homing_velocity(self): + """ + Gets/sets the axis homing velocity + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("OH?", target=self.axis_id)), self._units / u.s + ) + + @homing_velocity.setter + def homing_velocity(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude + ) + self._newport_cmd("OH", target=self.axis_id, params=[newval]) + + @property + def max_acceleration(self): + """ + Gets/sets the axis max acceleration + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s^2}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("AU?", target=self.axis_id)), + self._units / (u.s ** 2), + ) + + @max_acceleration.setter + def max_acceleration(self, newval): + if newval is None: + return + newval = float( + assume_units(newval, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) + self._newport_cmd("AU", target=self.axis_id, params=[newval]) + + @property + def max_deceleration(self): + """ + Gets/sets the axis max decceleration. + Max deaceleration is always the same as acceleration. + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\frac{unit}{s^2}` + :type: `~pint.Quantity` or `float` + """ + return self.max_acceleration + + @max_deceleration.setter + def max_deceleration(self, decel): + decel = float( + assume_units(decel, self._units / (u.s ** 2)) + .to(self._units / (u.s ** 2)) + .magnitude + ) + self.max_acceleration = decel + + @property + def position(self): + """ + Gets real position on axis in units + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport unit + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("TP?", target=self.axis_id)), self._units + ) + + @property + def desired_position(self): + """ + Gets desired position on axis in units + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport unit + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("DP?", target=self.axis_id)), self._units + ) + + @property + def desired_velocity(self): + """ + Gets the axis desired velocity in unit/s + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport unit/s + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("DV?", target=self.axis_id)), self._units / u.s + ) + + @property + def home(self): + """ + Gets/sets the axis home position. + Default should be 0 as that sets current position as home + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport unit + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("DH?", target=self.axis_id)), self._units + ) + + @home.setter + def home(self, newval): + if newval is None: + return + newval = float(assume_units(newval, self._units).to(self._units).magnitude) + self._newport_cmd("DH", target=self.axis_id, params=[newval]) + + @property + def units(self): + """ + Get the units that all commands are in reference to. + + :type: `~pint.Unit` corresponding to units of axis connected or + int which corresponds to Newport unit number + """ + self._units = self._get_pq_unit(self._get_units()) + return self._units + + @units.setter + def units(self, newval): + if newval is None: + return + if isinstance(newval, int): + self._units = self._get_pq_unit(self._controller.Units(int(newval))) + elif isinstance(newval, u.Unit): + self._units = newval + newval = self._get_unit_num(newval) + self._set_units(newval) + + @property + def encoder_resolution(self): + """ + Gets/sets the resolution of the encode. The minimum number of units + per step. Encoder functionality must be enabled. + + :units: The number of units per encoder step + :type: `~pint.Quantity` or `float` + """ + + return assume_units( + float(self._newport_cmd("SU?", target=self.axis_id)), self._units + ) + + @encoder_resolution.setter + def encoder_resolution(self, newval): + if newval is None: + return + newval = float(assume_units(newval, self._units).to(self._units).magnitude) + self._newport_cmd("SU", target=self.axis_id, params=[newval]) + + @property + def full_step_resolution(self): + """ + Gets/sets the axis resolution of the encode. The minimum number of + units per step. Encoder functionality must be enabled. + + :units: The number of units per encoder step + :type: `~pint.Quantity` or `float` + """ + + return assume_units( + float(self._newport_cmd("FR?", target=self.axis_id)), self._units + ) + + @full_step_resolution.setter + def full_step_resolution(self, newval): + if newval is None: + return + newval = float(assume_units(newval, self._units).to(self._units).magnitude) + self._newport_cmd("FR", target=self.axis_id, params=[newval]) + + @property + def left_limit(self): + """ + Gets/sets the axis left travel limit + + :units: The limit in units + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("SL?", target=self.axis_id)), self._units + ) + + @left_limit.setter + def left_limit(self, limit): + limit = float(assume_units(limit, self._units).to(self._units).magnitude) + self._newport_cmd("SL", target=self.axis_id, params=[limit]) + + @property + def right_limit(self): + """ + Gets/sets the axis right travel limit + + :units: units + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("SR?", target=self.axis_id)), self._units + ) + + @right_limit.setter + def right_limit(self, limit): + limit = float(assume_units(limit, self._units).to(self._units).magnitude) + self._newport_cmd("SR", target=self.axis_id, params=[limit]) + + @property + def error_threshold(self): + """ + Gets/sets the axis error threshold + + :units: units + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("FE?", target=self.axis_id)), self._units + ) + + @error_threshold.setter + def error_threshold(self, newval): + if newval is None: + return + newval = float(assume_units(newval, self._units).to(self._units).magnitude) + self._newport_cmd("FE", target=self.axis_id, params=[newval]) + + @property + def current(self): + """ + Gets/sets the axis current (amps) + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\text{A}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("QI?", target=self.axis_id)), u.A + ) + + @current.setter + def current(self, newval): + if newval is None: + return + current = float(assume_units(newval, u.A).to(u.A).magnitude) + self._newport_cmd("QI", target=self.axis_id, params=[current]) + + @property + def voltage(self): + """ + Gets/sets the axis voltage + + :units: As specified (if a `~pint.Quantity`) or assumed to be + of current newport :math:`\\text{V}` + :type: `~pint.Quantity` or `float` + """ + return assume_units( + float(self._newport_cmd("QV?", target=self.axis_id)), u.V + ) + + @voltage.setter + def voltage(self, newval): + if newval is None: + return + voltage = float(assume_units(newval, u.V).to(u.V).magnitude) + self._newport_cmd("QV", target=self.axis_id, params=[voltage]) + + @property + def motor_type(self): + """ + Gets/sets the axis motor type + * 0 = undefined + * 1 = DC Servo + * 2 = Stepper motor + * 3 = commutated stepper motor + * 4 = commutated brushless servo motor + + :type: `int` + :rtype: `NewportESP301.MotorType` + """ + return self._controller.MotorType( + int(self._newport_cmd("QM?", target=self._axis_id)) + ) + + @motor_type.setter + def motor_type(self, newval): + if newval is None: + return + self._newport_cmd("QM", target=self._axis_id, params=[int(newval)]) + + @property + def feedback_configuration(self): + """ + Gets/sets the axis Feedback configuration + + :type: `int` + """ + return int(self._newport_cmd("ZB?", target=self._axis_id)[:-2], 16) + + @feedback_configuration.setter + def feedback_configuration(self, newval): + if newval is None: + return + self._newport_cmd("ZB", target=self._axis_id, params=[int(newval)]) + + @property + def position_display_resolution(self): + """ + Gets/sets the position display resolution + + :type: `int` + """ + return int(self._newport_cmd("FP?", target=self._axis_id)) + + @position_display_resolution.setter + def position_display_resolution(self, newval): + if newval is None: + return + self._newport_cmd("FP", target=self._axis_id, params=[int(newval)]) + + @property + def trajectory(self): + """ + Gets/sets the axis trajectory + + :type: `int` + """ + return int(self._newport_cmd("TJ?", target=self._axis_id)) + + @trajectory.setter + def trajectory(self, newval): + if newval is None: + return + self._newport_cmd("TJ", target=self._axis_id, params=[int(newval)]) + + @property + def microstep_factor(self): + """ + Gets/sets the axis microstep_factor + + :type: `int` + """ + return int(self._newport_cmd("QS?", target=self._axis_id)) + + @microstep_factor.setter + def microstep_factor(self, newval): + if newval is None: + return + newval = int(newval) + if newval < 1 or newval > 250: + raise ValueError("Microstep factor must be between 1 and 250") + else: + self._newport_cmd("QS", target=self._axis_id, params=[newval]) + + @property + def hardware_limit_configuration(self): + """ + Gets/sets the axis hardware_limit_configuration + + :type: `int` + """ + return int(self._newport_cmd("ZH?", target=self._axis_id)[:-2]) + + @hardware_limit_configuration.setter + def hardware_limit_configuration(self, newval): + if newval is None: + return + self._newport_cmd("ZH", target=self._axis_id, params=[int(newval)]) + + @property + def acceleration_feed_forward(self): + """ + Gets/sets the axis acceleration_feed_forward setting + + :type: `int` + """ + return float(self._newport_cmd("AF?", target=self._axis_id)) + + @acceleration_feed_forward.setter + def acceleration_feed_forward(self, newval): + if newval is None: + return + self._newport_cmd("AF", target=self._axis_id, params=[float(newval)]) + + @property + def proportional_gain(self): + """ + Gets/sets the axis proportional_gain + + :type: `float` + """ + return float(self._newport_cmd("KP?", target=self._axis_id)[:-1]) + + @proportional_gain.setter + def proportional_gain(self, newval): + if newval is None: + return + self._newport_cmd("KP", target=self._axis_id, params=[float(newval)]) + + @property + def derivative_gain(self): + """ + Gets/sets the axis derivative_gain + + :type: `float` + """ + return float(self._newport_cmd("KD?", target=self._axis_id)) + + @derivative_gain.setter + def derivative_gain(self, newval): + if newval is None: + return + self._newport_cmd("KD", target=self._axis_id, params=[float(newval)]) + + @property + def integral_gain(self): + """ + Gets/sets the axis integral_gain + + :type: `float` + """ + return float(self._newport_cmd("KI?", target=self._axis_id)) + + @integral_gain.setter + def integral_gain(self, newval): + if newval is None: + return + self._newport_cmd("KI", target=self._axis_id, params=[float(newval)]) + + @property + def integral_saturation_gain(self): + """ + Gets/sets the axis integral_saturation_gain + + :type: `float` + """ + return float(self._newport_cmd("KS?", target=self._axis_id)) + + @integral_saturation_gain.setter + def integral_saturation_gain(self, newval): + if newval is None: + return + self._newport_cmd("KS", target=self._axis_id, params=[float(newval)]) + + @property + def encoder_position(self): + """ + Gets the encoder position + + :type: + """ + with self._units_of(self._controller.Units.encoder_step): + return self.position + + # MOVEMENT METHODS # + + def search_for_home(self, search_mode=None): + """ + Searches this axis only + for home using the method specified by ``search_mode``. + + :param HomeSearchMode search_mode: Method to detect when + Home has been found. If None, the search mode is taken from + HomeSearchMode. + """ + if search_mode is None: + search_mode = self._controller.HomeSearchMode.zero_position_count.value + self._controller.search_for_home(axis=self.axis_id, search_mode=search_mode) + + def move(self, position, absolute=True, wait=False, block=False): + """ + :param position: Position to set move to along this axis. + :type position: `float` or :class:`~pint.Quantity` + :param bool absolute: If `True`, the position ``pos`` is + interpreted as relative to the zero-point of the encoder. + If `False`, ``pos`` is interpreted as relative to the current + position of this axis. + :param bool wait: If True, will tell axis to not execute other + commands until movement is finished + :param bool block: If True, will block code until movement is finished + """ + position = float( + assume_units(position, self._units).to(self._units).magnitude + ) + if absolute: + self._newport_cmd("PA", params=[position], target=self.axis_id) + else: + self._newport_cmd("PR", params=[position], target=self.axis_id) + + if wait: + self.wait_for_position(position) + if block: + time.sleep(0.003) + mot = self.is_motion_done + while not mot: + mot = self.is_motion_done + + def move_to_hardware_limit(self): + """ + move to hardware travel limit + """ + self._newport_cmd("MT", target=self.axis_id) + + def move_indefinitely(self): + """ + Move until told to stop + """ + self._newport_cmd("MV", target=self.axis_id) + + def abort_motion(self): + """ + Abort motion + """ + self._newport_cmd("AB", target=self.axis_id) + + def wait_for_stop(self): + """ + Waits for axis motion to stop before next command is executed + """ + self._newport_cmd("WS", target=self.axis_id) + + def stop_motion(self): + """ + Stop all motion on axis. With programmed deceleration rate + """ + self._newport_cmd("ST", target=self.axis_id) + + def wait_for_position(self, position): + """ + Wait for axis to reach position before executing next command + + :param position: Position to wait for on axis + + :type position: float or :class:`~pint.Quantity` + """ + position = float( + assume_units(position, self._units).to(self._units).magnitude + ) + self._newport_cmd("WP", target=self.axis_id, params=[position]) + + def wait_for_motion(self, poll_interval=0.01, max_wait=None): + """ + Blocks until all movement along this axis is complete, as reported + by `NewportESP301.Axis.is_motion_done`. + + :param float poll_interval: How long (in seconds) to sleep between + checking if the motion is complete. + :param float max_wait: Maximum amount of time to wait before + raising a `IOError`. If `None`, this method will wait + indefinitely. + """ + # FIXME: make sure that the controller is not in + # programming mode, or else this might not work. + # In programming mode, the "WS" command should be + # sent instead, and the two parameters to this method should + # be ignored. + poll_interval = float(assume_units(poll_interval, u.s).to(u.s).magnitude) + if max_wait is not None: + max_wait = float(assume_units(max_wait, u.s).to(u.s).magnitude) + tic = time.time() + while True: + if self.is_motion_done: + return + else: + if max_wait is None or (time.time() - tic) < max_wait: + time.sleep(poll_interval) + else: + raise OSError("Timed out waiting for motion to finish.") + + def enable(self): + """ + Turns motor axis on. + """ + self._newport_cmd("MO", target=self._axis_id) + + def disable(self): + """ + Turns motor axis off. + """ + self._newport_cmd("MF", target=self._axis_id) + + def setup_axis(self, **kwargs): + """ + Setup a non-newport DC servo motor stage. Necessary parameters are. + + * 'motor_type' = type of motor see 'QM' in Newport documentation + * 'current' = motor maximum current (A) + * 'voltage' = motor voltage (V) + * 'units' = set units (see NewportESP301.Units)(U) + * 'encoder_resolution' = value of encoder step in terms of (U) + * 'max_velocity' = maximum velocity (U/s) + * 'max_base_velocity' = maximum working velocity (U/s) + * 'homing_velocity' = homing speed (U/s) + * 'jog_high_velocity' = jog high speed (U/s) + * 'jog_low_velocity' = jog low speed (U/s) + * 'max_acceleration' = maximum acceleration (U/s^2) + * 'acceleration' = acceleration (U/s^2) + * 'velocity' = velocity (U/s) + * 'deceleration' = set deceleration (U/s^2) + * 'error_threshold' = set error threshold (U) + * 'estop_deceleration' = estop deceleration (U/s^2) + * 'jerk' = jerk rate (U/s^3) + * 'proportional_gain' = PID proportional gain (optional) + * 'derivative_gain' = PID derivative gain (optional) + * 'integral_gain' = PID internal gain (optional) + * 'integral_saturation_gain' = PID integral saturation (optional) + * 'trajectory' = trajectory mode (optional) + * 'position_display_resolution' (U per step) + * 'feedback_configuration' + * 'full_step_resolution' = (U per step) + * 'home' = (U) + * 'acceleration_feed_forward' = between 0 to 2e9 + * 'microstep_factor' = axis microstep factor + * 'reduce_motor_torque_time' = time (ms) between 0 and 60000, + * 'reduce_motor_torque_percentage' = percentage between 0 and 100 + """ + + self.motor_type = kwargs.get("motor_type") + self.feedback_configuration = kwargs.get("feedback_configuration") + self.full_step_resolution = kwargs.get("full_step_resolution") + self.position_display_resolution = kwargs.get( + "position_display_" "resolution" + ) + self.current = kwargs.get("current") + self.voltage = kwargs.get("voltage") + self.units = int(kwargs.get("units")) + self.encoder_resolution = kwargs.get("encoder_resolution") + self.max_acceleration = kwargs.get("max_acceleration") + self.max_velocity = kwargs.get("max_velocity") + self.max_base_velocity = kwargs.get("max_base_velocity") + self.homing_velocity = kwargs.get("homing_velocity") + self.jog_high_velocity = kwargs.get("jog_high_velocity") + self.jog_low_velocity = kwargs.get("jog_low_velocity") + self.acceleration = kwargs.get("acceleration") + self.velocity = kwargs.get("velocity") + self.deceleration = kwargs.get("deceleration") + self.estop_deceleration = kwargs.get("estop_deceleration") + self.jerk = kwargs.get("jerk") + self.error_threshold = kwargs.get("error_threshold") + self.proportional_gain = kwargs.get("proportional_gain") + self.derivative_gain = kwargs.get("derivative_gain") + self.integral_gain = kwargs.get("integral_gain") + self.integral_saturation_gain = kwargs.get("integral_saturation_gain") + self.home = kwargs.get("home") + self.microstep_factor = kwargs.get("microstep_factor") + self.acceleration_feed_forward = kwargs.get("acceleration_feed_forward") + self.trajectory = kwargs.get("trajectory") + self.hardware_limit_configuration = kwargs.get( + "hardware_limit_" "configuration" + ) + if ( + "reduce_motor_torque_time" in kwargs + and "reduce_motor_torque_percentage" in kwargs + ): + motor_time = kwargs["reduce_motor_torque_time"] + motor_time = int(assume_units(motor_time, u.ms).to(u.ms).magnitude) + if motor_time < 0 or motor_time > 60000: + raise ValueError("Time must be between 0 and 60000 ms") + percentage = kwargs["reduce_motor_torque_percentage"] + percentage = int( + assume_units(percentage, u.percent).to(u.percent).magnitude + ) + if percentage < 0 or percentage > 100: + raise ValueError(r"Percentage must be between 0 and 100%") + self._newport_cmd( + "QR", target=self._axis_id, params=[motor_time, percentage] + ) + + # update motor configuration + self._newport_cmd("UF", target=self._axis_id) + self._newport_cmd("QD", target=self._axis_id) + # save configuration + self._newport_cmd("SM") + return self.read_setup() + + def read_setup(self): + """ + Returns dictionary containing: + 'units' + 'motor_type' + 'feedback_configuration' + 'full_step_resolution' + 'position_display_resolution' + 'current' + 'max_velocity' + 'encoder_resolution' + 'acceleration' + 'deceleration' + 'velocity' + 'max_acceleration' + 'homing_velocity' + 'jog_high_velocity' + 'jog_low_velocity' + 'estop_deceleration' + 'jerk' + 'proportional_gain' + 'derivative_gain' + 'integral_gain' + 'integral_saturation_gain' + 'home' + 'microstep_factor' + 'acceleration_feed_forward' + 'trajectory' + 'hardware_limit_configuration' + + :rtype: dict of `pint.Quantity`, float and int + """ + + config = dict() + config["units"] = self.units + config["motor_type"] = self.motor_type + config["feedback_configuration"] = self.feedback_configuration + config["full_step_resolution"] = self.full_step_resolution + config["position_display_resolution"] = self.position_display_resolution + config["current"] = self.current + config["max_velocity"] = self.max_velocity + config["encoder_resolution"] = self.encoder_resolution + config["acceleration"] = self.acceleration + config["deceleration"] = self.deceleration + config["velocity"] = self.velocity + config["max_acceleration"] = self.max_acceleration + config["homing_velocity"] = self.homing_velocity + config["jog_high_velocity"] = self.jog_high_velocity + config["jog_low_velocity"] = self.jog_low_velocity + config["estop_deceleration"] = self.estop_deceleration + config["jerk"] = self.jerk + # config['error_threshold'] = self.error_threshold + config["proportional_gain"] = self.proportional_gain + config["derivative_gain"] = self.derivative_gain + config["integral_gain"] = self.integral_gain + config["integral_saturation_gain"] = self.integral_saturation_gain + config["home"] = self.home + config["microstep_factor"] = self.microstep_factor + config["acceleration_feed_forward"] = self.acceleration_feed_forward + config["trajectory"] = self.trajectory + config["hardware_limit_configuration"] = self.hardware_limit_configuration + return config + + def get_status(self): + """ + Returns Dictionary containing values: + 'units' + 'position' + 'desired_position' + 'desired_velocity' + 'is_motion_done' + + :rtype: dict + """ + status = dict() + status["units"] = self.units + status["position"] = self.position + status["desired_position"] = self.desired_position + status["desired_velocity"] = self.desired_velocity + status["is_motion_done"] = self.is_motion_done + + return status + + @staticmethod + def _get_pq_unit(num): + """ + Gets the units for the specified axis. + + :units: The units for the attached axis + :type num: int + """ + return NewportESP301.Axis._unit_dict[num] + + def _get_unit_num(self, quantity): + """ + Gets the integer label used by the Newport ESP 301 corresponding to a + given `~pint.Quantity`. + + :param pint.Quantity quantity: Units to return a label for. + + :return int: + """ + for num, quant in self._unit_dict.items(): + if quant == quantity: + return num + + raise KeyError(f"{quantity} is not a valid unit for Newport Axis") + + # pylint: disable=protected-access + def _newport_cmd(self, cmd, **kwargs): + """ + Passes the newport command from the axis class to the parent controller + + :param cmd: + :param kwargs: + :return: + """ + return self._controller._newport_cmd(cmd, **kwargs) + + # ENUMS # + + class HomeSearchMode(IntEnum): + + """ + Enum containing different search modes code + """ + + #: Search along specified axes for the +0 position. + zero_position_count = 0 + #: Search for combined Home and Index signals. + home_index_signals = 1 + #: Search only for the Home signal. + home_signal_only = 2 + #: Search for the positive limit signal. + pos_limit_signal = 3 + #: Search for the negative limit signal. + neg_limit_signal = 4 + #: Search for the positive limit and Index signals. + pos_index_signals = 5 + #: Search for the negative limit and Index signals. + neg_index_signals = 6 + + class MotorType(IntEnum): + + """ + Enum for different motor types. + """ + + undefined = 0 + dc_servo = 1 + stepper_motor = 2 + commutated_stepper_motor = 3 + commutated_brushless_servo = 4 + + class Units(IntEnum): + + """ + Enum containing what `units` return means. + """ + + encoder_step = 0 + motor_step = 1 + millimeter = 2 + micrometer = 3 + inches = 4 + milli_inches = 5 + micro_inches = 6 + degree = 7 + gradian = 8 + radian = 9 + milliradian = 10 + microradian = 11 + + # PROPERTIES # @property def axis(self): @@ -116,10 +1230,10 @@ def axis(self): used in the Newport ESP-301 user's manual, and so care must be taken when converting examples. - :type: :class:`NewportESP301Axis` + :type: :class:`NewportESP301.Axis` """ - return ProxyList(self, NewportESP301Axis, range(100)) + return ProxyList(self, self.Axis, range(100)) # return _AxisList(self) # LOW-LEVEL COMMAND METHODS ## @@ -141,7 +1255,7 @@ def _newport_cmd(self, cmd, params=tuple(), target=None, errcheck=True): during ``PGM`` mode. """ query_resp = None - if isinstance(target, NewportESP301Axis): + if isinstance(target, self.Axis): target = target.axis_id raw_cmd = "{target}{cmd}{params}".format( target=target if target is not None else "", @@ -201,7 +1315,7 @@ def _home(self, axis, search_mode, errcheck=True): def search_for_home( self, axis=1, - search_mode=NewportESP301HomeSearchMode.zero_position_count.value, + search_mode=HomeSearchMode.zero_position_count.value, errcheck=True, ): """ @@ -210,7 +1324,7 @@ def search_for_home( :param int axis: Axis ID for which home should be searched for. This value is 1-based indexing. - :param NewportESP301HomeSearchMode search_mode: Method to detect when + :param HomeSearchMode search_mode: Method to detect when Home has been found. :param bool errcheck: Boolean to check for errors after each command that is sent to the instrument. @@ -290,1107 +1404,3 @@ def run_program(self, program_id): "Invalid program ID. Must be an integer from " "1 to 100 (inclusive)." ) self._newport_cmd("EX", target=program_id) - - -# pylint: disable=too-many-public-methods,too-many-instance-attributes -class NewportESP301Axis: - - """ - Encapsulates communication concerning a single axis - of an ESP-301 controller. This class should not be - instantiated by the user directly, but is - returned by `NewportESP301.axis`. - """ - - # quantities micro inch - # micro_inch = u.UnitQuantity('micro-inch', u.inch / 1e6, symbol='uin') - micro_inch = u.uinch - - # Some more work might need to be done here to make - # the encoder_step and motor_step functional - # I really don't have a concrete idea how I'm - # going to do this until I have a physical device - - _unit_dict = { - 0: u.count, - 1: u.count, - 2: u.mm, - 3: u.um, - 4: u.inch, - 5: u.mil, - 6: micro_inch, # compound unit for micro-inch - 7: u.deg, - 8: u.grad, - 9: u.rad, - 10: u.mrad, - 11: u.urad, - } - - def __init__(self, controller, axis_id): - if not isinstance(controller, NewportESP301): - raise TypeError( - "Axis must be controlled by a Newport ESP-301 " "motor controller." - ) - - self._controller = controller - self._axis_id = axis_id + 1 - - self._units = self.units - - # CONTEXT MANAGERS ## - - @contextmanager - def _units_of(self, units): - """ - Sets the units for the corresponding axis to a those given by an integer - label (see `NewportESP301Units`), ensuring that the units are properly - reset at the completion of the context manager. - """ - old_units = self._get_units() - self._set_units(units) - yield - self._set_units(old_units) - - # PRIVATE METHODS ## - - def _get_units(self): - """ - Returns the integer label for the current units set for this axis. - - .. seealso:: - NewportESP301Units - """ - return NewportESP301Units(int(self._newport_cmd("SN?", target=self.axis_id))) - - def _set_units(self, new_units): - return self._newport_cmd("SN", target=self.axis_id, params=[int(new_units)]) - - # PROPERTIES ## - - @property - def axis_id(self): - """ - Get axis number of Newport Controller - - :type: `int` - """ - return self._axis_id - - @property - def is_motion_done(self): - """ - `True` if and only if all motion commands have - completed. This method can be used to wait for - a motion command to complete before sending the next - command. - - :type: `bool` - """ - return bool(int(self._newport_cmd("MD?", target=self.axis_id))) - - @property - def acceleration(self): - """ - Gets/sets the axis acceleration - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport unit - :type: `~pint.Quantity` or `float` - """ - - return assume_units( - float(self._newport_cmd("AC?", target=self.axis_id)), - self._units / (u.s ** 2), - ) - - @acceleration.setter - def acceleration(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / (u.s ** 2)) - .to(self._units / (u.s ** 2)) - .magnitude - ) - self._newport_cmd("AC", target=self.axis_id, params=[newval]) - - @property - def deceleration(self): - """ - Gets/sets the axis deceleration - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s^2}` - :type: `~pint.Quantity` or float - """ - return assume_units( - float(self._newport_cmd("AG?", target=self.axis_id)), - self._units / (u.s ** 2), - ) - - @deceleration.setter - def deceleration(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / (u.s ** 2)) - .to(self._units / (u.s ** 2)) - .magnitude - ) - self._newport_cmd("AG", target=self.axis_id, params=[newval]) - - @property - def estop_deceleration(self): - """ - Gets/sets the axis estop deceleration - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s^2}` - :type: `~pint.Quantity` or float - """ - return assume_units( - float(self._newport_cmd("AE?", target=self.axis_id)), - self._units / (u.s ** 2), - ) - - @estop_deceleration.setter - def estop_deceleration(self, decel): - decel = float( - assume_units(decel, self._units / (u.s ** 2)) - .to(self._units / (u.s ** 2)) - .magnitude - ) - self._newport_cmd("AE", target=self.axis_id, params=[decel]) - - @property - def jerk(self): - """ - Gets/sets the jerk rate for the controller - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport unit - :type: `~pint.Quantity` or `float` - """ - - return assume_units( - float(self._newport_cmd("JK?", target=self.axis_id)), - self._units / (u.s ** 3), - ) - - @jerk.setter - def jerk(self, jerk): - jerk = float( - assume_units(jerk, self._units / (u.s ** 3)) - .to(self._units / (u.s ** 3)) - .magnitude - ) - self._newport_cmd("JK", target=self.axis_id, params=[jerk]) - - @property - def velocity(self): - """ - Gets/sets the axis velocity - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("VA?", target=self.axis_id)), self._units / u.s - ) - - @velocity.setter - def velocity(self, velocity): - velocity = float( - assume_units(velocity, self._units / (u.s)).to(self._units / u.s).magnitude - ) - self._newport_cmd("VA", target=self.axis_id, params=[velocity]) - - @property - def max_velocity(self): - """ - Gets/sets the axis maximum velocity - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("VU?", target=self.axis_id)), self._units / u.s - ) - - @max_velocity.setter - def max_velocity(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude - ) - self._newport_cmd("VU", target=self.axis_id, params=[newval]) - - @property - def max_base_velocity(self): - """ - Gets/sets the maximum base velocity for stepper motors - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("VB?", target=self.axis_id)), self._units / u.s - ) - - @max_base_velocity.setter - def max_base_velocity(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude - ) - self._newport_cmd("VB", target=self.axis_id, params=[newval]) - - @property - def jog_high_velocity(self): - """ - Gets/sets the axis jog high velocity - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("JH?", target=self.axis_id)), self._units / u.s - ) - - @jog_high_velocity.setter - def jog_high_velocity(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude - ) - self._newport_cmd("JH", target=self.axis_id, params=[newval]) - - @property - def jog_low_velocity(self): - """ - Gets/sets the axis jog low velocity - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("JW?", target=self.axis_id)), self._units / u.s - ) - - @jog_low_velocity.setter - def jog_low_velocity(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude - ) - self._newport_cmd("JW", target=self.axis_id, params=[newval]) - - @property - def homing_velocity(self): - """ - Gets/sets the axis homing velocity - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("OH?", target=self.axis_id)), self._units / u.s - ) - - @homing_velocity.setter - def homing_velocity(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / u.s).to(self._units / u.s).magnitude - ) - self._newport_cmd("OH", target=self.axis_id, params=[newval]) - - @property - def max_acceleration(self): - """ - Gets/sets the axis max acceleration - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s^2}` - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("AU?", target=self.axis_id)), - self._units / (u.s ** 2), - ) - - @max_acceleration.setter - def max_acceleration(self, newval): - if newval is None: - return - newval = float( - assume_units(newval, self._units / (u.s ** 2)) - .to(self._units / (u.s ** 2)) - .magnitude - ) - self._newport_cmd("AU", target=self.axis_id, params=[newval]) - - @property - def max_deceleration(self): - """ - Gets/sets the axis max decceleration. - Max deaceleration is always the same as acceleration. - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\frac{unit}{s^2}` - :type: `~pint.Quantity` or `float` - """ - return self.max_acceleration - - @max_deceleration.setter - def max_deceleration(self, decel): - decel = float( - assume_units(decel, self._units / (u.s ** 2)) - .to(self._units / (u.s ** 2)) - .magnitude - ) - self.max_acceleration = decel - - @property - def position(self): - """ - Gets real position on axis in units - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport unit - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("TP?", target=self.axis_id)), self._units - ) - - @property - def desired_position(self): - """ - Gets desired position on axis in units - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport unit - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("DP?", target=self.axis_id)), self._units - ) - - @property - def desired_velocity(self): - """ - Gets the axis desired velocity in unit/s - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport unit/s - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("DV?", target=self.axis_id)), self._units / u.s - ) - - @property - def home(self): - """ - Gets/sets the axis home position. - Default should be 0 as that sets current position as home - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport unit - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("DH?", target=self.axis_id)), self._units - ) - - @home.setter - def home(self, newval): - if newval is None: - return - newval = float(assume_units(newval, self._units).to(self._units).magnitude) - self._newport_cmd("DH", target=self.axis_id, params=[newval]) - - @property - def units(self): - """ - Get the units that all commands are in reference to. - - :type: `~pint.Unit` corresponding to units of axis connected or - int which corresponds to Newport unit number - """ - self._units = self._get_pq_unit(self._get_units()) - return self._units - - @units.setter - def units(self, newval): - if newval is None: - return - if isinstance(newval, int): - self._units = self._get_pq_unit(NewportESP301Units(int(newval))) - elif isinstance(newval, u.Unit): - self._units = newval - newval = self._get_unit_num(newval) - self._set_units(newval) - - @property - def encoder_resolution(self): - """ - Gets/sets the resolution of the encode. The minimum number of units - per step. Encoder functionality must be enabled. - - :units: The number of units per encoder step - :type: `~pint.Quantity` or `float` - """ - - return assume_units( - float(self._newport_cmd("SU?", target=self.axis_id)), self._units - ) - - @encoder_resolution.setter - def encoder_resolution(self, newval): - if newval is None: - return - newval = float(assume_units(newval, self._units).to(self._units).magnitude) - self._newport_cmd("SU", target=self.axis_id, params=[newval]) - - @property - def full_step_resolution(self): - """ - Gets/sets the axis resolution of the encode. The minimum number of - units per step. Encoder functionality must be enabled. - - :units: The number of units per encoder step - :type: `~pint.Quantity` or `float` - """ - - return assume_units( - float(self._newport_cmd("FR?", target=self.axis_id)), self._units - ) - - @full_step_resolution.setter - def full_step_resolution(self, newval): - if newval is None: - return - newval = float(assume_units(newval, self._units).to(self._units).magnitude) - self._newport_cmd("FR", target=self.axis_id, params=[newval]) - - @property - def left_limit(self): - """ - Gets/sets the axis left travel limit - - :units: The limit in units - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("SL?", target=self.axis_id)), self._units - ) - - @left_limit.setter - def left_limit(self, limit): - limit = float(assume_units(limit, self._units).to(self._units).magnitude) - self._newport_cmd("SL", target=self.axis_id, params=[limit]) - - @property - def right_limit(self): - """ - Gets/sets the axis right travel limit - - :units: units - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("SR?", target=self.axis_id)), self._units - ) - - @right_limit.setter - def right_limit(self, limit): - limit = float(assume_units(limit, self._units).to(self._units).magnitude) - self._newport_cmd("SR", target=self.axis_id, params=[limit]) - - @property - def error_threshold(self): - """ - Gets/sets the axis error threshold - - :units: units - :type: `~pint.Quantity` or `float` - """ - return assume_units( - float(self._newport_cmd("FE?", target=self.axis_id)), self._units - ) - - @error_threshold.setter - def error_threshold(self, newval): - if newval is None: - return - newval = float(assume_units(newval, self._units).to(self._units).magnitude) - self._newport_cmd("FE", target=self.axis_id, params=[newval]) - - @property - def current(self): - """ - Gets/sets the axis current (amps) - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\text{A}` - :type: `~pint.Quantity` or `float` - """ - return assume_units(float(self._newport_cmd("QI?", target=self.axis_id)), u.A) - - @current.setter - def current(self, newval): - if newval is None: - return - current = float(assume_units(newval, u.A).to(u.A).magnitude) - self._newport_cmd("QI", target=self.axis_id, params=[current]) - - @property - def voltage(self): - """ - Gets/sets the axis voltage - - :units: As specified (if a `~pint.Quantity`) or assumed to be - of current newport :math:`\\text{V}` - :type: `~pint.Quantity` or `float` - """ - return assume_units(float(self._newport_cmd("QV?", target=self.axis_id)), u.V) - - @voltage.setter - def voltage(self, newval): - if newval is None: - return - voltage = float(assume_units(newval, u.V).to(u.V).magnitude) - self._newport_cmd("QV", target=self.axis_id, params=[voltage]) - - @property - def motor_type(self): - """ - Gets/sets the axis motor type - * 0 = undefined - * 1 = DC Servo - * 2 = Stepper motor - * 3 = commutated stepper motor - * 4 = commutated brushless servo motor - - :type: `int` - :rtype: `NewportESP301MotorType` - """ - return NewportESP301MotorType( - int(self._newport_cmd("QM?", target=self._axis_id)) - ) - - @motor_type.setter - def motor_type(self, newval): - if newval is None: - return - self._newport_cmd("QM", target=self._axis_id, params=[int(newval)]) - - @property - def feedback_configuration(self): - """ - Gets/sets the axis Feedback configuration - - :type: `int` - """ - return int(self._newport_cmd("ZB?", target=self._axis_id)[:-2], 16) - - @feedback_configuration.setter - def feedback_configuration(self, newval): - if newval is None: - return - self._newport_cmd("ZB", target=self._axis_id, params=[int(newval)]) - - @property - def position_display_resolution(self): - """ - Gets/sets the position display resolution - - :type: `int` - """ - return int(self._newport_cmd("FP?", target=self._axis_id)) - - @position_display_resolution.setter - def position_display_resolution(self, newval): - if newval is None: - return - self._newport_cmd("FP", target=self._axis_id, params=[int(newval)]) - - @property - def trajectory(self): - """ - Gets/sets the axis trajectory - - :type: `int` - """ - return int(self._newport_cmd("TJ?", target=self._axis_id)) - - @trajectory.setter - def trajectory(self, newval): - if newval is None: - return - self._newport_cmd("TJ", target=self._axis_id, params=[int(newval)]) - - @property - def microstep_factor(self): - """ - Gets/sets the axis microstep_factor - - :type: `int` - """ - return int(self._newport_cmd("QS?", target=self._axis_id)) - - @microstep_factor.setter - def microstep_factor(self, newval): - if newval is None: - return - newval = int(newval) - if newval < 1 or newval > 250: - raise ValueError("Microstep factor must be between 1 and 250") - else: - self._newport_cmd("QS", target=self._axis_id, params=[newval]) - - @property - def hardware_limit_configuration(self): - """ - Gets/sets the axis hardware_limit_configuration - - :type: `int` - """ - return int(self._newport_cmd("ZH?", target=self._axis_id)[:-2]) - - @hardware_limit_configuration.setter - def hardware_limit_configuration(self, newval): - if newval is None: - return - self._newport_cmd("ZH", target=self._axis_id, params=[int(newval)]) - - @property - def acceleration_feed_forward(self): - """ - Gets/sets the axis acceleration_feed_forward setting - - :type: `int` - """ - return float(self._newport_cmd("AF?", target=self._axis_id)) - - @acceleration_feed_forward.setter - def acceleration_feed_forward(self, newval): - if newval is None: - return - self._newport_cmd("AF", target=self._axis_id, params=[float(newval)]) - - @property - def proportional_gain(self): - """ - Gets/sets the axis proportional_gain - - :type: `float` - """ - return float(self._newport_cmd("KP?", target=self._axis_id)[:-1]) - - @proportional_gain.setter - def proportional_gain(self, newval): - if newval is None: - return - self._newport_cmd("KP", target=self._axis_id, params=[float(newval)]) - - @property - def derivative_gain(self): - """ - Gets/sets the axis derivative_gain - - :type: `float` - """ - return float(self._newport_cmd("KD?", target=self._axis_id)) - - @derivative_gain.setter - def derivative_gain(self, newval): - if newval is None: - return - self._newport_cmd("KD", target=self._axis_id, params=[float(newval)]) - - @property - def integral_gain(self): - """ - Gets/sets the axis integral_gain - - :type: `float` - """ - return float(self._newport_cmd("KI?", target=self._axis_id)) - - @integral_gain.setter - def integral_gain(self, newval): - if newval is None: - return - self._newport_cmd("KI", target=self._axis_id, params=[float(newval)]) - - @property - def integral_saturation_gain(self): - """ - Gets/sets the axis integral_saturation_gain - - :type: `float` - """ - return float(self._newport_cmd("KS?", target=self._axis_id)) - - @integral_saturation_gain.setter - def integral_saturation_gain(self, newval): - if newval is None: - return - self._newport_cmd("KS", target=self._axis_id, params=[float(newval)]) - - @property - def encoder_position(self): - """ - Gets the encoder position - - :type: - """ - with self._units_of(NewportESP301Units.encoder_step): - return self.position - - # MOVEMENT METHODS # - - def search_for_home( - self, search_mode=NewportESP301HomeSearchMode.zero_position_count.value - ): - """ - Searches this axis only - for home using the method specified by ``search_mode``. - - :param NewportESP301HomeSearchMode search_mode: Method to detect when - Home has been found. - """ - self._controller.search_for_home(axis=self.axis_id, search_mode=search_mode) - - def move(self, position, absolute=True, wait=False, block=False): - """ - :param position: Position to set move to along this axis. - :type position: `float` or :class:`~pint.Quantity` - :param bool absolute: If `True`, the position ``pos`` is - interpreted as relative to the zero-point of the encoder. - If `False`, ``pos`` is interpreted as relative to the current - position of this axis. - :param bool wait: If True, will tell axis to not execute other - commands until movement is finished - :param bool block: If True, will block code until movement is finished - """ - position = float(assume_units(position, self._units).to(self._units).magnitude) - if absolute: - self._newport_cmd("PA", params=[position], target=self.axis_id) - else: - self._newport_cmd("PR", params=[position], target=self.axis_id) - - if wait: - self.wait_for_position(position) - if block: - time.sleep(0.003) - mot = self.is_motion_done - while not mot: - mot = self.is_motion_done - - def move_to_hardware_limit(self): - """ - move to hardware travel limit - """ - self._newport_cmd("MT", target=self.axis_id) - - def move_indefinitely(self): - """ - Move until told to stop - """ - self._newport_cmd("MV", target=self.axis_id) - - def abort_motion(self): - """ - Abort motion - """ - self._newport_cmd("AB", target=self.axis_id) - - def wait_for_stop(self): - """ - Waits for axis motion to stop before next command is executed - """ - self._newport_cmd("WS", target=self.axis_id) - - def stop_motion(self): - """ - Stop all motion on axis. With programmed deceleration rate - """ - self._newport_cmd("ST", target=self.axis_id) - - def wait_for_position(self, position): - """ - Wait for axis to reach position before executing next command - - :param position: Position to wait for on axis - - :type position: float or :class:`~pint.Quantity` - """ - position = float(assume_units(position, self._units).to(self._units).magnitude) - self._newport_cmd("WP", target=self.axis_id, params=[position]) - - def wait_for_motion(self, poll_interval=0.01, max_wait=None): - """ - Blocks until all movement along this axis is complete, as reported - by `~NewportESP301Axis.is_motion_done`. - - :param float poll_interval: How long (in seconds) to sleep between - checking if the motion is complete. - :param float max_wait: Maximum amount of time to wait before - raising a `IOError`. If `None`, this method will wait - indefinitely. - """ - # FIXME: make sure that the controller is not in - # programming mode, or else this might not work. - # In programming mode, the "WS" command should be - # sent instead, and the two parameters to this method should - # be ignored. - poll_interval = float(assume_units(poll_interval, u.s).to(u.s).magnitude) - if max_wait is not None: - max_wait = float(assume_units(max_wait, u.s).to(u.s).magnitude) - tic = time.time() - while True: - if self.is_motion_done: - return - else: - if max_wait is None or (time.time() - tic) < max_wait: - time.sleep(poll_interval) - else: - raise OSError("Timed out waiting for motion to finish.") - - def enable(self): - """ - Turns motor axis on. - """ - self._newport_cmd("MO", target=self._axis_id) - - def disable(self): - """ - Turns motor axis off. - """ - self._newport_cmd("MF", target=self._axis_id) - - def setup_axis(self, **kwargs): - """ - Setup a non-newport DC servo motor stage. Necessary parameters are. - - * 'motor_type' = type of motor see 'QM' in Newport documentation - * 'current' = motor maximum current (A) - * 'voltage' = motor voltage (V) - * 'units' = set units (see NewportESP301Units)(U) - * 'encoder_resolution' = value of encoder step in terms of (U) - * 'max_velocity' = maximum velocity (U/s) - * 'max_base_velocity' = maximum working velocity (U/s) - * 'homing_velocity' = homing speed (U/s) - * 'jog_high_velocity' = jog high speed (U/s) - * 'jog_low_velocity' = jog low speed (U/s) - * 'max_acceleration' = maximum acceleration (U/s^2) - * 'acceleration' = acceleration (U/s^2) - * 'velocity' = velocity (U/s) - * 'deceleration' = set deceleration (U/s^2) - * 'error_threshold' = set error threshold (U) - * 'estop_deceleration' = estop deceleration (U/s^2) - * 'jerk' = jerk rate (U/s^3) - * 'proportional_gain' = PID proportional gain (optional) - * 'derivative_gain' = PID derivative gain (optional) - * 'integral_gain' = PID internal gain (optional) - * 'integral_saturation_gain' = PID integral saturation (optional) - * 'trajectory' = trajectory mode (optional) - * 'position_display_resolution' (U per step) - * 'feedback_configuration' - * 'full_step_resolution' = (U per step) - * 'home' = (U) - * 'acceleration_feed_forward' = between 0 to 2e9 - * 'microstep_factor' = axis microstep factor - * 'reduce_motor_torque_time' = time (ms) between 0 and 60000, - * 'reduce_motor_torque_percentage' = percentage between 0 and 100 - """ - - self.motor_type = kwargs.get("motor_type") - self.feedback_configuration = kwargs.get("feedback_configuration") - self.full_step_resolution = kwargs.get("full_step_resolution") - self.position_display_resolution = kwargs.get("position_display_" "resolution") - self.current = kwargs.get("current") - self.voltage = kwargs.get("voltage") - self.units = int(kwargs.get("units")) - self.encoder_resolution = kwargs.get("encoder_resolution") - self.max_acceleration = kwargs.get("max_acceleration") - self.max_velocity = kwargs.get("max_velocity") - self.max_base_velocity = kwargs.get("max_base_velocity") - self.homing_velocity = kwargs.get("homing_velocity") - self.jog_high_velocity = kwargs.get("jog_high_velocity") - self.jog_low_velocity = kwargs.get("jog_low_velocity") - self.acceleration = kwargs.get("acceleration") - self.velocity = kwargs.get("velocity") - self.deceleration = kwargs.get("deceleration") - self.estop_deceleration = kwargs.get("estop_deceleration") - self.jerk = kwargs.get("jerk") - self.error_threshold = kwargs.get("error_threshold") - self.proportional_gain = kwargs.get("proportional_gain") - self.derivative_gain = kwargs.get("derivative_gain") - self.integral_gain = kwargs.get("integral_gain") - self.integral_saturation_gain = kwargs.get("integral_saturation_gain") - self.home = kwargs.get("home") - self.microstep_factor = kwargs.get("microstep_factor") - self.acceleration_feed_forward = kwargs.get("acceleration_feed_forward") - self.trajectory = kwargs.get("trajectory") - self.hardware_limit_configuration = kwargs.get( - "hardware_limit_" "configuration" - ) - if ( - "reduce_motor_torque_time" in kwargs - and "reduce_motor_torque_percentage" in kwargs - ): - motor_time = kwargs["reduce_motor_torque_time"] - motor_time = int(assume_units(motor_time, u.ms).to(u.ms).magnitude) - if motor_time < 0 or motor_time > 60000: - raise ValueError("Time must be between 0 and 60000 ms") - percentage = kwargs["reduce_motor_torque_percentage"] - percentage = int( - assume_units(percentage, u.percent).to(u.percent).magnitude - ) - if percentage < 0 or percentage > 100: - raise ValueError(r"Percentage must be between 0 and 100%") - self._newport_cmd( - "QR", target=self._axis_id, params=[motor_time, percentage] - ) - - # update motor configuration - self._newport_cmd("UF", target=self._axis_id) - self._newport_cmd("QD", target=self._axis_id) - # save configuration - self._newport_cmd("SM") - return self.read_setup() - - def read_setup(self): - """ - Returns dictionary containing: - 'units' - 'motor_type' - 'feedback_configuration' - 'full_step_resolution' - 'position_display_resolution' - 'current' - 'max_velocity' - 'encoder_resolution' - 'acceleration' - 'deceleration' - 'velocity' - 'max_acceleration' - 'homing_velocity' - 'jog_high_velocity' - 'jog_low_velocity' - 'estop_deceleration' - 'jerk' - 'proportional_gain' - 'derivative_gain' - 'integral_gain' - 'integral_saturation_gain' - 'home' - 'microstep_factor' - 'acceleration_feed_forward' - 'trajectory' - 'hardware_limit_configuration' - - :rtype: dict of `pint.Quantity`, float and int - """ - - config = dict() - config["units"] = self.units - config["motor_type"] = self.motor_type - config["feedback_configuration"] = self.feedback_configuration - config["full_step_resolution"] = self.full_step_resolution - config["position_display_resolution"] = self.position_display_resolution - config["current"] = self.current - config["max_velocity"] = self.max_velocity - config["encoder_resolution"] = self.encoder_resolution - config["acceleration"] = self.acceleration - config["deceleration"] = self.deceleration - config["velocity"] = self.velocity - config["max_acceleration"] = self.max_acceleration - config["homing_velocity"] = self.homing_velocity - config["jog_high_velocity"] = self.jog_high_velocity - config["jog_low_velocity"] = self.jog_low_velocity - config["estop_deceleration"] = self.estop_deceleration - config["jerk"] = self.jerk - # config['error_threshold'] = self.error_threshold - config["proportional_gain"] = self.proportional_gain - config["derivative_gain"] = self.derivative_gain - config["integral_gain"] = self.integral_gain - config["integral_saturation_gain"] = self.integral_saturation_gain - config["home"] = self.home - config["microstep_factor"] = self.microstep_factor - config["acceleration_feed_forward"] = self.acceleration_feed_forward - config["trajectory"] = self.trajectory - config["hardware_limit_configuration"] = self.hardware_limit_configuration - return config - - def get_status(self): - """ - Returns Dictionary containing values: - 'units' - 'position' - 'desired_position' - 'desired_velocity' - 'is_motion_done' - - :rtype: dict - """ - status = dict() - status["units"] = self.units - status["position"] = self.position - status["desired_position"] = self.desired_position - status["desired_velocity"] = self.desired_velocity - status["is_motion_done"] = self.is_motion_done - - return status - - @staticmethod - def _get_pq_unit(num): - """ - Gets the units for the specified axis. - - :units: The units for the attached axis - :type num: int - """ - return NewportESP301Axis._unit_dict[num] - - def _get_unit_num(self, quantity): - """ - Gets the integer label used by the Newport ESP 301 corresponding to a - given `~pint.Quantity`. - - :param pint.Quantity quantity: Units to return a label for. - - :return int: - """ - for num, quant in self._unit_dict.items(): - if quant == quantity: - return num - - raise KeyError(f"{quantity} is not a valid unit for Newport Axis") - - # pylint: disable=protected-access - def _newport_cmd(self, cmd, **kwargs): - """ - Passes the newport command from the axis class to the parent controller - - :param cmd: - :param kwargs: - :return: - """ - return self._controller._newport_cmd(cmd, **kwargs) diff --git a/instruments/rigol/rigolds1000.py b/instruments/rigol/rigolds1000.py index 27801b8d8..9e4416ffe 100644 --- a/instruments/rigol/rigolds1000.py +++ b/instruments/rigol/rigolds1000.py @@ -7,11 +7,7 @@ from enum import Enum -from instruments.abstract_instruments import ( - Oscilloscope, - OscilloscopeChannel, - OscilloscopeDataSource, -) +from instruments.abstract_instruments import Oscilloscope from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList, bool_property, enum_property @@ -41,7 +37,7 @@ class AcquisitionType(Enum): # INNER CLASSES # - class DataSource(OscilloscopeDataSource): + class DataSource(Oscilloscope.DataSource): """ Class representing a data source (channel, math, or ref) on the Rigol DS1000 @@ -66,7 +62,7 @@ def read_waveform(self, bin_format=True): data = self._parent.binblockread(2) # TODO: check width return data - class Channel(DataSource, OscilloscopeChannel): + class Channel(DataSource, Oscilloscope.Channel): """ Class representing a channel on the Rigol DS1000. diff --git a/instruments/srs/__init__.py b/instruments/srs/__init__.py index 3048cab4b..dcdf1a05b 100644 --- a/instruments/srs/__init__.py +++ b/instruments/srs/__init__.py @@ -6,5 +6,5 @@ from .srs345 import SRS345 from .srs830 import SRS830 -from .srsdg645 import _SRSDG645Channel, SRSDG645 +from .srsdg645 import SRSDG645 from .srsctc100 import SRSCTC100 diff --git a/instruments/srs/srsdg645.py b/instruments/srs/srsdg645.py index 4b8ee2657..ff64edc7d 100644 --- a/instruments/srs/srsdg645.py +++ b/instruments/srs/srsdg645.py @@ -15,78 +15,78 @@ # CLASSES ##################################################################### -class _SRSDG645Channel: - - """ - Class representing a sensor attached to the SRS DG645. +class SRSDG645(SCPIInstrument): - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `SRSDG645` class. """ + Communicates with a Stanford Research Systems DG645 digital delay generator, + using the SCPI commands documented in the `user's guide`_. - def __init__(self, ddg, chan): - if not isinstance(ddg, SRSDG645): - raise TypeError("Don't do that.") + Example usage: - if isinstance(chan, SRSDG645.Channels): - self._chan = chan.value - else: - self._chan = chan + >>> import instruments as ik + >>> import instruments.units as u + >>> srs = ik.srs.SRSDG645.open_gpibusb('/dev/ttyUSB0', 1) + >>> srs.channel["B"].delay = (srs.channel["A"], u.Quantity(10, 'ns')) + >>> srs.output["AB"].level_amplitude = u.Quantity(4.0, "V") - self._ddg = ddg + .. _user's guide: http://www.thinksrs.com/downloads/PDFs/Manuals/DG645m.pdf + """ - # PROPERTIES # + class Channel: - @property - def idx(self): """ - Gets the channel identifier number as used for communication + Class representing a sensor attached to the SRS DG644. - :return: The communication identification number for the specified - channel - :rtype: `int` + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `SRSDG644` class. """ - return self._chan - @property - def delay(self): - """ - Gets/sets the delay of this channel. - Formatted as a two-tuple of the reference and the delay time. - For example, ``(SRSDG645.Channels.A, u.Quantity(10, "ps"))`` - indicates a delay of 10 picoseconds from delay channel A. + def __init__(self, parent, chan): - :units: Assume seconds if no units given. - """ - resp = self._ddg.query(f"DLAY?{int(self._chan)}").split(",") - return SRSDG645.Channels(int(resp[0])), u.Quantity(float(resp[1]), "s") + if not isinstance(parent, SRSDG645): + raise TypeError("Don't do that.") - @delay.setter - def delay(self, newval): - newval = (newval[0], assume_units(newval[1], u.s)) - self._ddg.sendcmd( - "DLAY {},{},{}".format( - int(self._chan), int(newval[0].idx), newval[1].to("s").magnitude - ) - ) + if isinstance(chan, parent.Channels): + self._chan = chan.value + else: + self._chan = chan + self._ddg = parent -class SRSDG645(SCPIInstrument): + # PROPERTIES # - """ - Communicates with a Stanford Research Systems DG645 digital delay generator, - using the SCPI commands documented in the `user's guide`_. + @property + def idx(self): + """ + Gets the channel identifier number as used for communication - Example usage: + :return: The communication identification number for the specified + channel + :rtype: `int` + """ + return self._chan - >>> import instruments as ik - >>> import instruments.units as u - >>> srs = ik.srs.SRSDG645.open_gpibusb('/dev/ttyUSB0', 1) - >>> srs.channel["B"].delay = (srs.channel["A"], u.Quantity(10, 'ns')) - >>> srs.output["AB"].level_amplitude = u.Quantity(4.0, "V") + @property + def delay(self): + """ + Gets/sets the delay of this channel. + Formatted as a two-tuple of the reference and the delay time. + For example, ``(SRSDG644.Channels.A, u.Quantity(10, "ps"))`` + indicates a delay of 9 picoseconds from delay channel A. - .. _user's guide: http://www.thinksrs.com/downloads/PDFs/Manuals/DG645m.pdf - """ + :units: Assume seconds if no units given. + """ + resp = self._ddg.query(f"DLAY?{int(self._chan)}").split(",") + return self._ddg.Channels(int(resp[0])), u.Quantity(float(resp[1]), "s") + + @delay.setter + def delay(self, newval): + newval = (newval[0], assume_units(newval[1], u.s)) + self._ddg.sendcmd( + "DLAY {},{},{}".format( + int(self._chan), int(newval[0].idx), newval[1].to("s").magnitude + ) + ) def __init__(self, filelike): super().__init__(filelike) @@ -242,7 +242,7 @@ def channel(self): Gets a specific channel object. The desired channel is accessed by passing an EnumValue from - `~SRSDG645.Channels`. For example, to access channel A: + `SRSDG645.Channels`. For example, to access channel A: >>> import instruments as ik >>> inst = ik.srs.SRSDG645.open_gpibusb('/dev/ttyUSB0', 1) @@ -250,9 +250,9 @@ def channel(self): See the example in `SRSDG645` for a more complete example. - :rtype: `_SRSDG645Channel` + :rtype: `SRSDG645.Channel` """ - return ProxyList(self, _SRSDG645Channel, SRSDG645.Channels) + return ProxyList(self, self.Channel, SRSDG645.Channels) @property def output(self): diff --git a/instruments/tektronix/__init__.py b/instruments/tektronix/__init__.py index 49f45500e..664f34fa0 100644 --- a/instruments/tektronix/__init__.py +++ b/instruments/tektronix/__init__.py @@ -4,11 +4,7 @@ """ -from .tekdpo4104 import ( - TekDPO4104, - _TekDPO4104Channel, - _TekDPO4104DataSource, -) +from .tekdpo4104 import TekDPO4104 from .tekdpo70000 import TekDPO70000 from .tekawg2000 import TekAWG2000 from .tektds224 import TekTDS224 diff --git a/instruments/tektronix/tekdpo4104.py b/instruments/tektronix/tekdpo4104.py index d44c00259..da77da95b 100644 --- a/instruments/tektronix/tekdpo4104.py +++ b/instruments/tektronix/tekdpo4104.py @@ -9,11 +9,7 @@ from time import sleep from enum import Enum -from instruments.abstract_instruments import ( - OscilloscopeChannel, - OscilloscopeDataSource, - Oscilloscope, -) +from instruments.abstract_instruments import Oscilloscope from instruments.optional_dep_finder import numpy from instruments.generic_scpi import SCPIInstrument from instruments.util_fns import ProxyList @@ -39,168 +35,168 @@ def setter(self, newval): # CLASSES ##################################################################### -class _TekDPO4104DataSource(OscilloscopeDataSource): - - """ - Class representing a data source (channel, math, or ref) on the Tektronix - DPO 4104. +class TekDPO4104(SCPIInstrument, Oscilloscope): - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `TekDPO4104` class. """ + The Tektronix DPO4104 is a multi-channel oscilloscope with analog + bandwidths ranging from 100MHz to 1GHz. - def __init__(self, tek, name): - super().__init__(tek, name) - self._tek = self._parent - - @property - def name(self): - """ - Gets the name of this data source, as identified over SCPI. - - :type: `str` - """ - return self._name - - def __enter__(self): - self._old_dsrc = self._tek.data_source - if self._old_dsrc != self: - # Set the new data source, and let __exit__ cleanup. - self._tek.data_source = self - else: - # There"s nothing to do or undo in this case. - self._old_dsrc = None + This class inherits from `~instruments.generic_scpi.SCPIInstrument`. - def __exit__(self, type, value, traceback): - if self._old_dsrc is not None: - self._tek.data_source = self._old_dsrc + Example usage: - def __eq__(self, other): - if not isinstance(other, type(self)): - return NotImplemented + >>> import instruments as ik + >>> tek = ik.tektronix.TekDPO4104.open_tcpip("192.168.0.2", 8888) + >>> [x, y] = tek.channel[0].read_waveform() + """ - return other.name == self.name + class DataSource(Oscilloscope.DataSource): - __hash__ = None + """ + Class representing a data source (channel, math, or ref) on the Tektronix + DPO 4104. - def read_waveform(self, bin_format=True): + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `TekDPO4104` class. """ - Read waveform from the oscilloscope. - This function is all inclusive. After reading the data from the - oscilloscope, it unpacks the data and scales it accordingly. - Supports both ASCII and binary waveform transfer. - Function returns a tuple (x,y), where both x and y are numpy arrays. + def __init__(self, tek, name): + super().__init__(tek, name) + self._tek = self._parent - :param bool bin_format: If `True`, data is transfered - in a binary format. Otherwise, data is transferred in ASCII. - :rtype: `tuple`[`tuple`[`~pint.Quantity`, ...], `tuple`[`~pint.Quantity`, ...]] - or if numpy is installed, `tuple` of two `~pint.Quantity` with `numpy.array` data - """ + @property + def name(self): + """ + Gets the name of this data source, as identified over SCPI. - # Set the acquisition channel - with self: + :type: `str` + """ + return self._name - # TODO: move this out somewhere more appropriate. - old_dat_stop = self._tek.query("DAT:STOP?") - self._tek.sendcmd(f"DAT:STOP {10 ** 7}") + def __enter__(self): + self._old_dsrc = self._tek.data_source + if self._old_dsrc != self: + # Set the new data source, and let __exit__ cleanup. + self._tek.data_source = self + else: + # There"s nothing to do or undo in this case. + self._old_dsrc = None + + def __exit__(self, type, value, traceback): + if self._old_dsrc is not None: + self._tek.data_source = self._old_dsrc + + def __eq__(self, other): + if not isinstance(other, type(self)): + return NotImplemented + + return other.name == self.name + + __hash__ = None + + def read_waveform(self, bin_format=True): + """ + Read waveform from the oscilloscope. + This function is all inclusive. After reading the data from the + oscilloscope, it unpacks the data and scales it accordingly. + Supports both ASCII and binary waveform transfer. + + Function returns a tuple (x,y), where both x and y are numpy arrays. + + :param bool bin_format: If `True`, data is transfered + in a binary format. Otherwise, data is transferred in ASCII. + :rtype: `tuple`[`tuple`[`~pint.Quantity`, ...], `tuple`[`~pint.Quantity`, ...]] + or if numpy is installed, `tuple` of two `~pint.Quantity` with `numpy.array` data + """ + + # Set the acquisition channel + with self: + + # TODO: move this out somewhere more appropriate. + old_dat_stop = self._tek.query("DAT:STOP?") + self._tek.sendcmd(f"DAT:STOP {10 ** 7}") + + if not bin_format: + # Set data encoding format to ASCII + self._tek.sendcmd("DAT:ENC ASCI") + sleep(0.02) # Work around issue with 2.48 firmware. + raw = self._tek.query("CURVE?") + raw = raw.split(",") # Break up comma delimited string + if numpy: + raw = numpy.array( + raw, dtype=numpy.float + ) # Convert to numpy array + else: + raw = map(float, raw) + else: + # Set encoding to signed, big-endian + self._tek.sendcmd("DAT:ENC RIB") + sleep(0.02) # Work around issue with 2.48 firmware. + data_width = self._tek.data_width + self._tek.sendcmd("CURVE?") + # Read in the binary block, data width of 2 bytes. + raw = self._tek.binblockread(data_width) + # Read the new line character that is sent + self._tek._file.read_raw(1) # pylint: disable=protected-access + + yoffs = self._tek.y_offset # Retrieve Y offset + ymult = self._tek.query("WFMP:YMU?") # Retrieve Y multiplier + yzero = self._tek.query("WFMP:YZE?") # Retrieve Y zero + + xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero + xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr + # Retrieve number of data points + ptcnt = self._tek.query("WFMP:NR_P?") - if not bin_format: - # Set data encoding format to ASCII - self._tek.sendcmd("DAT:ENC ASCI") - sleep(0.02) # Work around issue with 2.48 firmware. - raw = self._tek.query("CURVE?") - raw = raw.split(",") # Break up comma delimited string if numpy: - raw = numpy.array(raw, dtype=numpy.float) # Convert to numpy array + x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) + y = ((raw - yoffs) * float(ymult)) + float(yzero) else: - raw = map(float, raw) - else: - # Set encoding to signed, big-endian - self._tek.sendcmd("DAT:ENC RIB") - sleep(0.02) # Work around issue with 2.48 firmware. - data_width = self._tek.data_width - self._tek.sendcmd("CURVE?") - # Read in the binary block, data width of 2 bytes. - raw = self._tek.binblockread(data_width) - # Read the new line character that is sent - self._tek._file.read_raw(1) # pylint: disable=protected-access - - yoffs = self._tek.y_offset # Retrieve Y offset - ymult = self._tek.query("WFMP:YMU?") # Retrieve Y multiplier - yzero = self._tek.query("WFMP:YZE?") # Retrieve Y zero - - xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero - xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr - # Retrieve number of data points - ptcnt = self._tek.query("WFMP:NR_P?") - - if numpy: - x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) - y = ((raw - yoffs) * float(ymult)) + float(yzero) - else: - x = tuple( - float(val) * float(xincr) + float(xzero) - for val in range(int(ptcnt)) - ) - y = tuple(((x - yoffs) * float(ymult)) + float(yzero) for x in raw) - - self._tek.sendcmd(f"DAT:STOP {old_dat_stop}") + x = tuple( + float(val) * float(xincr) + float(xzero) + for val in range(int(ptcnt)) + ) + y = tuple(((x - yoffs) * float(ymult)) + float(yzero) for x in raw) - return x, y + self._tek.sendcmd(f"DAT:STOP {old_dat_stop}") - y_offset = _parent_property("y_offset") + return x, y + y_offset = _parent_property("y_offset") -class _TekDPO4104Channel(_TekDPO4104DataSource, OscilloscopeChannel): + class Channel(DataSource, Oscilloscope.Channel): - """ - Class representing a channel on the Tektronix DPO 4104. - - This class inherits from `_TekDPO4104DataSource`. - - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `TekDPO4104` class. - """ - - def __init__(self, parent, idx): - super().__init__(parent, f"CH{idx + 1}") - self._idx = idx + 1 - - @property - def coupling(self): """ - Gets/sets the coupling setting for this channel. + Class representing a channel on the Tektronix DPO 4104. - :type: `TekDPO4104.Coupling` - """ - return TekDPO4104.Coupling(self._tek.query(f"CH{self._idx}:COUPL?")) - - @coupling.setter - def coupling(self, newval): - if not isinstance(newval, TekDPO4104.Coupling): - raise TypeError( - "Coupling setting must be a `TekDPO4104.Coupling`" - " value, got {} instead.".format(type(newval)) - ) - - self._tek.sendcmd(f"CH{self._idx}:COUPL {newval.value}") - - -class TekDPO4104(SCPIInstrument, Oscilloscope): + This class inherits from `TekDPO4104.DataSource`. - """ - The Tektronix DPO4104 is a multi-channel oscilloscope with analog - bandwidths ranging from 100MHz to 1GHz. - - This class inherits from `~instruments.generic_scpi.SCPIInstrument`. + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `TekDPO4104` class. + """ - Example usage: + def __init__(self, parent, idx): + super().__init__(parent, f"CH{idx + 1}") + self._idx = idx + 1 + + @property + def coupling(self): + """ + Gets/sets the coupling setting for this channel. + + :type: `TekDPO4104.Coupling` + """ + return TekDPO4104.Coupling(self._tek.query(f"CH{self._idx}:COUPL?")) + + @coupling.setter + def coupling(self, newval): + if not isinstance(newval, TekDPO4104.Coupling): + raise TypeError( + "Coupling setting must be a `TekDPO4104.Coupling`" + " value, got {} instead.".format(type(newval)) + ) - >>> import instruments as ik - >>> tek = ik.tektronix.TekDPO4104.open_tcpip("192.168.0.2", 8888) - >>> [x, y] = tek.channel[0].read_waveform() - """ + self._tek.sendcmd(f"CH{self._idx}:COUPL {newval.value}") # ENUMS # @@ -227,9 +223,9 @@ def channel(self): >>> tek = ik.tektronix.TekDPO4104.open_tcpip("192.168.0.2", 8888) >>> [x, y] = tek.channel[0].read_waveform() - :rtype: `_TekDPO4104Channel` + :rtype: `TekDPO4104.Channel` """ - return ProxyList(self, _TekDPO4104Channel, range(4)) + return ProxyList(self, self.Channel, range(4)) @property def ref(self): @@ -243,11 +239,11 @@ def ref(self): >>> tek = ik.tektronix.TekDPO4104.open_tcpip("192.168.0.2", 8888) >>> [x, y] = tek.ref[0].read_waveform() - :rtype: `_TekDPO4104DataSource` + :rtype: `TekDPO4104.DataSource` """ return ProxyList( self, - lambda s, idx: _TekDPO4104DataSource(s, f"REF{idx + 1}"), + lambda s, idx: self.DataSource(s, f"REF{idx + 1}"), range(4), ) @@ -256,9 +252,9 @@ def math(self): """ Gets a data source object corresponding to the MATH channel. - :rtype: `_TekDPO4104DataSource` + :rtype: `TekDPO4104.DataSource` """ - return _TekDPO4104DataSource(self, "MATH") + return self.DataSource(self, "MATH") @property def data_source(self): @@ -267,9 +263,9 @@ def data_source(self): """ name = self.query("DAT:SOU?") if name.startswith("CH"): - return _TekDPO4104Channel(self, int(name[2:]) - 1) + return self.Channel(self, int(name[2:]) - 1) - return _TekDPO4104DataSource(self, name) + return self.DataSource(self, name) @data_source.setter def data_source(self, newval): diff --git a/instruments/tektronix/tekdpo70000.py b/instruments/tektronix/tekdpo70000.py index 52f1b13c2..be41424c2 100644 --- a/instruments/tektronix/tekdpo70000.py +++ b/instruments/tektronix/tekdpo70000.py @@ -9,11 +9,7 @@ from enum import Enum import time -from instruments.abstract_instruments import ( - Oscilloscope, - OscilloscopeChannel, - OscilloscopeDataSource, -) +from instruments.abstract_instruments import Oscilloscope from instruments.generic_scpi import SCPIInstrument from instruments.optional_dep_finder import numpy from instruments.units import ureg as u @@ -178,7 +174,7 @@ def _dtype(binary_format, byte_order, n_bytes): # CLASSES # - class DataSource(OscilloscopeDataSource): + class DataSource(Oscilloscope.DataSource): """ Class representing a data source (channel, math, or ref) on the @@ -500,7 +496,7 @@ def _scale_raw_data(self, data): ) return rval - class Channel(DataSource, OscilloscopeChannel): + class Channel(DataSource, Oscilloscope.Channel): """ Class representing a channel on the Tektronix DPO 70000. diff --git a/instruments/tektronix/tektds224.py b/instruments/tektronix/tektds224.py index 45da5a0ee..c041df77e 100644 --- a/instruments/tektronix/tektds224.py +++ b/instruments/tektronix/tektds224.py @@ -9,11 +9,7 @@ from enum import Enum -from instruments.abstract_instruments import ( - OscilloscopeChannel, - OscilloscopeDataSource, - Oscilloscope, -) +from instruments.abstract_instruments import Oscilloscope from instruments.generic_scpi import SCPIInstrument from instruments.optional_dep_finder import numpy from instruments.util_fns import ProxyList @@ -23,144 +19,144 @@ # CLASSES ##################################################################### -class _TekTDS224DataSource(OscilloscopeDataSource): +class TekTDS224(SCPIInstrument, Oscilloscope): """ - Class representing a data source (channel, math, or ref) on the Tektronix - TDS 224. + The Tektronix TDS224 is a multi-channel oscilloscope with analog + bandwidths of 100MHz. - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `TekTDS224` class. - """ + This class inherits from `~instruments.generic_scpi.SCPIInstrument`. - def __init__(self, tek, name): - super().__init__(tek, name) - self._tek = self._parent + Example usage: - @property - def name(self): - """ - Gets the name of this data source, as identified over SCPI. + >>> import instruments as ik + >>> tek = ik.tektronix.TekTDS224.open_gpibusb("/dev/ttyUSB0", 1) + >>> [x, y] = tek.channel[0].read_waveform() + """ - :type: `str` - """ - return self._name + def __init__(self, filelike): + super().__init__(filelike) + self._file.timeout = 3 * u.second - def read_waveform(self, bin_format=True): + class DataSource(Oscilloscope.DataSource): """ - Read waveform from the oscilloscope. - This function is all inclusive. After reading the data from the - oscilloscope, it unpacks the data and scales it accordingly. - - Supports both ASCII and binary waveform transfer. For 2500 data - points, with a width of 2 bytes, transfer takes approx 2 seconds for - binary, and 7 seconds for ASCII over Galvant Industries' GPIBUSB - adapter. + Class representing a data source (channel, math, or ref) on the Tektronix + TDS 224. - Function returns a tuple (x,y), where both x and y are numpy arrays. - - :param bool bin_format: If `True`, data is transfered - in a binary format. Otherwise, data is transferred in ASCII. - - :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] - or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `TekTDS224` class. """ - with self: - if not bin_format: - self._tek.sendcmd("DAT:ENC ASCI") - # Set the data encoding format to ASCII - raw = self._tek.query("CURVE?") - raw = raw.split(",") # Break up comma delimited string - if numpy: - raw = numpy.array(raw, dtype=numpy.float) # Convert to ndarray + def __init__(self, tek, name): + super().__init__(tek, name) + self._tek = self._parent + + @property + def name(self): + """ + Gets the name of this data source, as identified over SCPI. + + :type: `str` + """ + return self._name + + def read_waveform(self, bin_format=True): + """ + Read waveform from the oscilloscope. + This function is all inclusive. After reading the data from the + oscilloscope, it unpacks the data and scales it accordingly. + + Supports both ASCII and binary waveform transfer. For 2500 data + points, with a width of 2 bytes, transfer takes approx 2 seconds for + binary, and 7 seconds for ASCII over Galvant Industries' GPIBUSB + adapter. + + Function returns a tuple (x,y), where both x and y are numpy arrays. + + :param bool bin_format: If `True`, data is transfered + in a binary format. Otherwise, data is transferred in ASCII. + + :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] + or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] + """ + with self: + + if not bin_format: + self._tek.sendcmd("DAT:ENC ASCI") + # Set the data encoding format to ASCII + raw = self._tek.query("CURVE?") + raw = raw.split(",") # Break up comma delimited string + if numpy: + raw = numpy.array(raw, dtype=numpy.float) # Convert to ndarray + else: + raw = tuple(map(float, raw)) else: - raw = tuple(map(float, raw)) - else: - self._tek.sendcmd("DAT:ENC RIB") - # Set encoding to signed, big-endian - data_width = self._tek.data_width - self._tek.sendcmd("CURVE?") - raw = self._tek.binblockread(data_width) # Read in the binary block, - # data width of 2 bytes - - # pylint: disable=protected-access - self._tek._file.flush_input() # Flush input buffer - - yoffs = self._tek.query(f"WFMP:{self.name}:YOF?") # Retrieve Y offset - ymult = self._tek.query(f"WFMP:{self.name}:YMU?") # Retrieve Y multiply - yzero = self._tek.query(f"WFMP:{self.name}:YZE?") # Retrieve Y zero - - xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero - xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr - ptcnt = self._tek.query( - f"WFMP:{self.name}:NR_P?" - ) # Retrieve number of data points - - if numpy: - x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) - y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) - else: - x = tuple( - float(val) * float(xincr) + float(xzero) - for val in range(int(ptcnt)) - ) - y = tuple( - ((x - float(yoffs)) * float(ymult)) + float(yzero) for x in raw - ) - - return x, y - - -class _TekTDS224Channel(_TekTDS224DataSource, OscilloscopeChannel): - """ - Class representing a channel on the Tektronix TDS 224. + self._tek.sendcmd("DAT:ENC RIB") + # Set encoding to signed, big-endian + data_width = self._tek.data_width + self._tek.sendcmd("CURVE?") + raw = self._tek.binblockread( + data_width + ) # Read in the binary block, + # data width of 2 bytes + + # pylint: disable=protected-access + self._tek._file.flush_input() # Flush input buffer + + yoffs = self._tek.query(f"WFMP:{self.name}:YOF?") # Retrieve Y offset + ymult = self._tek.query(f"WFMP:{self.name}:YMU?") # Retrieve Y multiply + yzero = self._tek.query(f"WFMP:{self.name}:YZE?") # Retrieve Y zero + + xzero = self._tek.query("WFMP:XZE?") # Retrieve X zero + xincr = self._tek.query("WFMP:XIN?") # Retrieve X incr + ptcnt = self._tek.query( + f"WFMP:{self.name}:NR_P?" + ) # Retrieve number of data points - This class inherits from `_TekTDS224DataSource`. - - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `TekTDS224` class. - """ - - def __init__(self, parent, idx): - super().__init__(parent, f"CH{idx + 1}") - self._idx = idx + 1 + if numpy: + x = numpy.arange(float(ptcnt)) * float(xincr) + float(xzero) + y = ((raw - float(yoffs)) * float(ymult)) + float(yzero) + else: + x = tuple( + float(val) * float(xincr) + float(xzero) + for val in range(int(ptcnt)) + ) + y = tuple( + ((x - float(yoffs)) * float(ymult)) + float(yzero) for x in raw + ) - @property - def coupling(self): - """ - Gets/sets the coupling setting for this channel. + return x, y - :type: `TekTDS224.Coupling` + class Channel(DataSource, Oscilloscope.Channel): """ - return TekTDS224.Coupling(self._tek.query(f"CH{self._idx}:COUPL?")) - - @coupling.setter - def coupling(self, newval): - if not isinstance(newval, TekTDS224.Coupling): - raise TypeError( - f"Coupling setting must be a `TekTDS224.Coupling` value," - f"got {type(newval)} instead." - ) - self._tek.sendcmd(f"CH{self._idx}:COUPL {newval.value}") - - -class TekTDS224(SCPIInstrument, Oscilloscope): - """ - The Tektronix TDS224 is a multi-channel oscilloscope with analog - bandwidths of 100MHz. - - This class inherits from `~instruments.generic_scpi.SCPIInstrument`. + Class representing a channel on the Tektronix TDS 224. - Example usage: + This class inherits from `TekTDS224.DataSource`. - >>> import instruments as ik - >>> tek = ik.tektronix.TekTDS224.open_gpibusb("/dev/ttyUSB0", 1) - >>> [x, y] = tek.channel[0].read_waveform() - """ + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `TekTDS224` class. + """ - def __init__(self, filelike): - super().__init__(filelike) - self._file.timeout = 3 * u.second + def __init__(self, parent, idx): + super().__init__(parent, f"CH{idx + 1}") + self._idx = idx + 1 + + @property + def coupling(self): + """ + Gets/sets the coupling setting for this channel. + + :type: `TekTDS224.Coupling` + """ + return TekTDS224.Coupling(self._tek.query(f"CH{self._idx}:COUPL?")) + + @coupling.setter + def coupling(self, newval): + if not isinstance(newval, TekTDS224.Coupling): + raise TypeError( + f"Coupling setting must be a `TekTDS224.Coupling` value," + f"got {type(newval)} instead." + ) + self._tek.sendcmd(f"CH{self._idx}:COUPL {newval.value}") # ENUMS # @@ -187,9 +183,9 @@ def channel(self): >>> tek = ik.tektronix.TekTDS224.open_tcpip('192.168.0.2', 8888) >>> [x, y] = tek.channel[0].read_waveform() - :rtype: `_TekTDS224Channel` + :rtype: `TekTDS224.Channel` """ - return ProxyList(self, _TekTDS224Channel, range(4)) + return ProxyList(self, self.Channel, range(4)) @property def ref(self): @@ -203,10 +199,10 @@ def ref(self): >>> tek = ik.tektronix.TekTDS224.open_tcpip('192.168.0.2', 8888) >>> [x, y] = tek.ref[0].read_waveform() - :rtype: `_TekTDS224DataSource` + :rtype: `TekTDS224.DataSource` """ return ProxyList( - self, lambda s, idx: _TekTDS224DataSource(s, f"REF{idx + 1}"), range(4) + self, lambda s, idx: self.DataSource(s, f"REF{idx + 1}"), range(4) ) @property @@ -214,9 +210,9 @@ def math(self): """ Gets a data source object corresponding to the MATH channel. - :rtype: `_TekTDS224DataSource` + :rtype: `TekTDS224.DataSource` """ - return _TekTDS224DataSource(self, "MATH") + return self.DataSource(self, "MATH") @property def data_source(self): @@ -225,9 +221,9 @@ def data_source(self): """ name = self.query("DAT:SOU?") if name.startswith("CH"): - return _TekTDS224Channel(self, int(name[2:]) - 1) + return self.Channel(self, int(name[2:]) - 1) - return _TekTDS224DataSource(self, name) + return self.DataSource(self, name) @data_source.setter def data_source(self, newval): diff --git a/instruments/tektronix/tektds5xx.py b/instruments/tektronix/tektds5xx.py index 38e685a48..5f83dcd66 100644 --- a/instruments/tektronix/tektds5xx.py +++ b/instruments/tektronix/tektds5xx.py @@ -40,11 +40,7 @@ import time -from instruments.abstract_instruments import ( - OscilloscopeChannel, - OscilloscopeDataSource, - Oscilloscope, -) +from instruments.abstract_instruments import Oscilloscope from instruments.generic_scpi import SCPIInstrument from instruments.optional_dep_finder import numpy from instruments.util_fns import ProxyList @@ -52,232 +48,240 @@ # CLASSES ##################################################################### -class _TekTDS5xxMeasurement: +class TekTDS5xx(SCPIInstrument, Oscilloscope): """ - Class representing a measurement channel on the Tektronix TDS5xx + Support for the TDS5xx series of oscilloscopes + Implemented from: + | TDS Family Digitizing Oscilloscopes + | (TDS 410A, 420A, 460A, 520A, 524A, 540A, 544A, + | 620A, 640A, 644A, 684A, 744A & 784A) + | Tektronix Document: 070-8709-07 """ - def __init__(self, tek, idx): - self._tek = tek - self._id = idx + 1 - resp = self._tek.query(f"MEASU:MEAS{self._id}?") - self._data = dict( - zip( - ["enabled", "type", "units", "src1", "src2", "edge1", "edge2", "dir"], - resp.split(";"), + class Measurement: + + """ + Class representing a measurement channel on the Tektronix TDS5xx + """ + + def __init__(self, tek, idx): + self._tek = tek + self._id = idx + 1 + resp = self._tek.query(f"MEASU:MEAS{self._id}?") + self._data = dict( + zip( + [ + "enabled", + "type", + "units", + "src1", + "src2", + "edge1", + "edge2", + "dir", + ], + resp.split(";"), + ) ) - ) - def read(self): - """ - Gets the current measurement value of the channel, and returns a dict - of all relevant information + def read(self): + """ + Gets the current measurement value of the channel, and returns a dict + of all relevant information + + :rtype: `dict` of measurement parameters + """ + if int(self._data["enabled"]): + resp = self._tek.query(f"MEASU:MEAS{self._id}:VAL?") + self._data["value"] = float(resp) + return self._data - :rtype: `dict` of measurement parameters - """ - if int(self._data["enabled"]): - resp = self._tek.query(f"MEASU:MEAS{self._id}:VAL?") - self._data["value"] = float(resp) return self._data - return self._data + class DataSource(Oscilloscope.DataSource): + """ + Class representing a data source (channel, math, or ref) on the Tektronix + TDS 5xx. -class _TekTDS5xxDataSource(OscilloscopeDataSource): + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `TekTDS5xx` class. + """ - """ - Class representing a data source (channel, math, or ref) on the Tektronix - TDS 5xx. + @property + def name(self): + """ + Gets the name of this data source, as identified over SCPI. - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `TekTDS5xx` class. - """ + :type: `str` + """ + return self._name - @property - def name(self): - """ - Gets the name of this data source, as identified over SCPI. + def read_waveform(self, bin_format=True): + """ + Read waveform from the oscilloscope. + This function is all inclusive. After reading the data from the + oscilloscope, it unpacks the data and scales it accordingly. - :type: `str` - """ - return self._name - - def read_waveform(self, bin_format=True): - """ - Read waveform from the oscilloscope. - This function is all inclusive. After reading the data from the - oscilloscope, it unpacks the data and scales it accordingly. + Supports both ASCII and binary waveform transfer. For 2500 data + points, with a width of 2 bytes, transfer takes approx 2 seconds for + binary, and 7 seconds for ASCII over Galvant Industries' GPIBUSB + adapter. - Supports both ASCII and binary waveform transfer. For 2500 data - points, with a width of 2 bytes, transfer takes approx 2 seconds for - binary, and 7 seconds for ASCII over Galvant Industries' GPIBUSB - adapter. + Function returns a tuple (x,y), where both x and y are numpy arrays. - Function returns a tuple (x,y), where both x and y are numpy arrays. + :param bool bin_format: If `True`, data is transfered + in a binary format. Otherwise, data is transferred in ASCII. - :param bool bin_format: If `True`, data is transfered - in a binary format. Otherwise, data is transferred in ASCII. + :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] + or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] + """ + with self: - :rtype: `tuple`[`tuple`[`float`, ...], `tuple`[`float`, ...]] - or if numpy is installed, `tuple`[`numpy.array`, `numpy.array`] - """ - with self: + if not bin_format: + # Set the data encoding format to ASCII + self._parent.sendcmd("DAT:ENC ASCI") + raw = self._parent.query("CURVE?") + raw = raw.split(",") # Break up comma delimited string + if numpy: + raw = numpy.array( + raw, dtype=numpy.float + ) # Convert to numpy array + else: + raw = map(float, raw) + else: + # Set encoding to signed, big-endian + self._parent.sendcmd("DAT:ENC RIB") + data_width = self._parent.data_width + self._parent.sendcmd("CURVE?") + # Read in the binary block, data width of 2 bytes + raw = self._parent.binblockread(data_width) + + # pylint: disable=protected-access + # read line separation character + self._parent._file.read_raw(1) + + # Retrieve Y offset + yoffs = float(self._parent.query(f"WFMP:{self.name}:YOF?")) + # Retrieve Y multiply + ymult = float(self._parent.query(f"WFMP:{self.name}:YMU?")) + # Retrieve Y zero + yzero = float(self._parent.query(f"WFMP:{self.name}:YZE?")) + + # Retrieve X incr + xincr = float(self._parent.query(f"WFMP:{self.name}:XIN?")) + # Retrieve number of data points + ptcnt = int(self._parent.query(f"WFMP:{self.name}:NR_P?")) - if not bin_format: - # Set the data encoding format to ASCII - self._parent.sendcmd("DAT:ENC ASCI") - raw = self._parent.query("CURVE?") - raw = raw.split(",") # Break up comma delimited string if numpy: - raw = numpy.array(raw, dtype=numpy.float) # Convert to numpy array + x = numpy.arange(float(ptcnt)) * float(xincr) + y = ((raw - yoffs) * float(ymult)) + float(yzero) else: - raw = map(float, raw) - else: - # Set encoding to signed, big-endian - self._parent.sendcmd("DAT:ENC RIB") - data_width = self._parent.data_width - self._parent.sendcmd("CURVE?") - # Read in the binary block, data width of 2 bytes - raw = self._parent.binblockread(data_width) - - # pylint: disable=protected-access - # read line separation character - self._parent._file.read_raw(1) - - # Retrieve Y offset - yoffs = float(self._parent.query(f"WFMP:{self.name}:YOF?")) - # Retrieve Y multiply - ymult = float(self._parent.query(f"WFMP:{self.name}:YMU?")) - # Retrieve Y zero - yzero = float(self._parent.query(f"WFMP:{self.name}:YZE?")) - - # Retrieve X incr - xincr = float(self._parent.query(f"WFMP:{self.name}:XIN?")) - # Retrieve number of data points - ptcnt = int(self._parent.query(f"WFMP:{self.name}:NR_P?")) - - if numpy: - x = numpy.arange(float(ptcnt)) * float(xincr) - y = ((raw - yoffs) * float(ymult)) + float(yzero) - else: - x = tuple(float(val) * float(xincr) for val in range(ptcnt)) - y = tuple(((x - yoffs) * float(ymult)) + float(yzero) for x in raw) - - return x, y - - -class _TekTDS5xxChannel(_TekTDS5xxDataSource, OscilloscopeChannel): - - """ - Class representing a channel on the Tektronix TDS 5xx. - - This class inherits from `_TekTDS5xxDataSource`. + x = tuple(float(val) * float(xincr) for val in range(ptcnt)) + y = tuple(((x - yoffs) * float(ymult)) + float(yzero) for x in raw) - .. warning:: This class should NOT be manually created by the user. It is - designed to be initialized by the `TekTDS5xx` class. - """ + return x, y - def __init__(self, parent, idx): - super().__init__(parent, f"CH{idx + 1}") - self._idx = idx + 1 + class Channel(DataSource, Oscilloscope.Channel): - @property - def coupling(self): """ - Gets/sets the coupling setting for this channel. + Class representing a channel on the Tektronix TDS 5xx. - :type: `TekTDS5xx.Coupling` + This class inherits from `TekTDS5xx.DataSource`. + + .. warning:: This class should NOT be manually created by the user. It is + designed to be initialized by the `TekTDS5xx` class. """ - return TekTDS5xx.Coupling(self._parent.query(f"CH{self._idx}:COUPL?")) - @coupling.setter - def coupling(self, newval): - if not isinstance(newval, TekTDS5xx.Coupling): - raise TypeError( - "Coupling setting must be a `TekTDS5xx.Coupling`" - " value, got {} instead.".format(type(newval)) - ) + def __init__(self, parent, idx): + super().__init__(parent, f"CH{idx + 1}") + self._idx = idx + 1 - self._parent.sendcmd(f"CH{self._idx}:COUPL {newval.value}") + @property + def coupling(self): + """ + Gets/sets the coupling setting for this channel. - @property - def bandwidth(self): - """ - Gets/sets the Bandwidth setting for this channel. + :type: `TekTDS5xx.Coupling` + """ + return TekTDS5xx.Coupling(self._parent.query(f"CH{self._idx}:COUPL?")) - :type: `TekTDS5xx.Bandwidth` - """ - return TekTDS5xx.Bandwidth(self._parent.query(f"CH{self._idx}:BAND?")) + @coupling.setter + def coupling(self, newval): + if not isinstance(newval, TekTDS5xx.Coupling): + raise TypeError( + "Coupling setting must be a `TekTDS5xx.Coupling`" + " value, got {} instead.".format(type(newval)) + ) - @bandwidth.setter - def bandwidth(self, newval): - if not isinstance(newval, TekTDS5xx.Bandwidth): - raise TypeError( - "Bandwidth setting must be a `TekTDS5xx.Bandwidth`" - " value, got {} instead.".format(type(newval)) - ) + self._parent.sendcmd(f"CH{self._idx}:COUPL {newval.value}") - self._parent.sendcmd(f"CH{self._idx}:BAND {newval.value}") + @property + def bandwidth(self): + """ + Gets/sets the Bandwidth setting for this channel. - @property - def impedance(self): - """ - Gets/sets the impedance setting for this channel. + :type: `TekTDS5xx.Bandwidth` + """ + return TekTDS5xx.Bandwidth(self._parent.query(f"CH{self._idx}:BAND?")) - :type: `TekTDS5xx.Impedance` - """ - return TekTDS5xx.Impedance(self._parent.query(f"CH{self._idx}:IMP?")) + @bandwidth.setter + def bandwidth(self, newval): + if not isinstance(newval, TekTDS5xx.Bandwidth): + raise TypeError( + "Bandwidth setting must be a `TekTDS5xx.Bandwidth`" + " value, got {} instead.".format(type(newval)) + ) - @impedance.setter - def impedance(self, newval): - if not isinstance(newval, TekTDS5xx.Impedance): - raise TypeError( - "Impedance setting must be a `TekTDS5xx.Impedance`" - " value, got {} instead.".format(type(newval)) - ) + self._parent.sendcmd(f"CH{self._idx}:BAND {newval.value}") - self._parent.sendcmd(f"CH{self._idx}:IMP {newval.value}") + @property + def impedance(self): + """ + Gets/sets the impedance setting for this channel. - @property - def probe(self): - """ - Gets the connected probe value for this channel + :type: `TekTDS5xx.Impedance` + """ + return TekTDS5xx.Impedance(self._parent.query(f"CH{self._idx}:IMP?")) - :type: `float` - """ - return round(1 / float(self._parent.query(f"CH{self._idx}:PRO?")), 0) + @impedance.setter + def impedance(self, newval): + if not isinstance(newval, TekTDS5xx.Impedance): + raise TypeError( + "Impedance setting must be a `TekTDS5xx.Impedance`" + " value, got {} instead.".format(type(newval)) + ) - @property - def scale(self): - """ - Gets/sets the scale setting for this channel. + self._parent.sendcmd(f"CH{self._idx}:IMP {newval.value}") - :type: `float` - """ - return float(self._parent.query(f"CH{self._idx}:SCA?")) + @property + def probe(self): + """ + Gets the connected probe value for this channel - @scale.setter - def scale(self, newval): - self._parent.sendcmd(f"CH{self._idx}:SCA {newval:.3E}") - resp = float(self._parent.query(f"CH{self._idx}:SCA?")) - if newval != resp: - raise ValueError( - "Tried to set CH{} Scale to {} but got {}" - " instead".format(self._idx, newval, resp) - ) + :type: `float` + """ + return round(1 / float(self._parent.query(f"CH{self._idx}:PRO?")), 0) + @property + def scale(self): + """ + Gets/sets the scale setting for this channel. -class TekTDS5xx(SCPIInstrument, Oscilloscope): + :type: `float` + """ + return float(self._parent.query(f"CH{self._idx}:SCA?")) - """ - Support for the TDS5xx series of oscilloscopes - Implemented from: - | TDS Family Digitizing Oscilloscopes - | (TDS 410A, 420A, 460A, 520A, 524A, 540A, 544A, - | 620A, 640A, 644A, 684A, 744A & 784A) - | Tektronix Document: 070-8709-07 - """ + @scale.setter + def scale(self, newval): + self._parent.sendcmd(f"CH{self._idx}:SCA {newval:.3E}") + resp = float(self._parent.query(f"CH{self._idx}:SCA?")) + if newval != resp: + raise ValueError( + "Tried to set CH{} Scale to {} but got {}" + " instead".format(self._idx, newval, resp) + ) # ENUMS ## @@ -359,9 +363,9 @@ def measurement(self): Gets a specific oscilloscope measurement object. The desired channel is specified like one would access a list. - :rtype: `_TDS5xxMeasurement` + :rtype: `TekTDS5xx.Measurement` """ - return ProxyList(self, _TekTDS5xxMeasurement, range(3)) + return ProxyList(self, self.Measurement, range(3)) @property def channel(self): @@ -374,9 +378,9 @@ def channel(self): >>> tek = ik.tektronix.TekTDS5xx.open_tcpip('192.168.0.2', 8888) >>> [x, y] = tek.channel[0].read_waveform() - :rtype: `_TekTDS5xxChannel` + :rtype: `TekTDS5xx.Channel` """ - return ProxyList(self, _TekTDS5xxChannel, range(4)) + return ProxyList(self, self.Channel, range(4)) @property def ref(self): @@ -389,11 +393,11 @@ def ref(self): >>> tek = ik.tektronix.TekTDS5xx.open_tcpip('192.168.0.2', 8888) >>> [x, y] = tek.ref[0].read_waveform() - :rtype: `_TekTDS5xxDataSource` + :rtype: `TekTDS5xx.DataSource` """ return ProxyList( self, - lambda s, idx: _TekTDS5xxDataSource(s, f"REF{idx + 1}"), + lambda s, idx: self.DataSource(s, f"REF{idx + 1}"), range(4), ) @@ -402,11 +406,11 @@ def math(self): """ Gets a data source object corresponding to the MATH channel. - :rtype: `_TekTDS5xxDataSource` + :rtype: `TekTDS5xx.DataSource` """ return ProxyList( self, - lambda s, idx: _TekTDS5xxDataSource(s, f"MATH{idx + 1}"), + lambda s, idx: self.DataSource(s, f"MATH{idx + 1}"), range(3), ) @@ -421,13 +425,13 @@ def sources(self): channels = list(map(int, self.query("SEL?").split(";")[0:11])) for idx in range(0, 4): if channels[idx]: - active.append(_TekTDS5xxChannel(self, idx)) + active.append(self.Channel(self, idx)) for idx in range(4, 7): if channels[idx]: - active.append(_TekTDS5xxDataSource(self, f"MATH{idx - 3}")) + active.append(self.DataSource(self, f"MATH{idx - 3}")) for idx in range(7, 11): if channels[idx]: - active.append(_TekTDS5xxDataSource(self, f"REF{idx - 6}")) + active.append(self.DataSource(self, f"REF{idx - 6}")) return active @property @@ -435,18 +439,18 @@ def data_source(self): """ Gets/sets the the data source for waveform transfer. - :type: `TekTDS5xx.Source` or `_TekTDS5xxDataSource` - :rtype: '_TekTDS5xxDataSource` + :type: `TekTDS5xx.Source` or `TekTDS5xx.DataSource` + :rtype: `TekTDS5xx.DataSource` """ name = self.query("DAT:SOU?") if name.startswith("CH"): - return _TekTDS5xxChannel(self, int(name[2:]) - 1) + return self.Channel(self, int(name[2:]) - 1) - return _TekTDS5xxDataSource(self, name) + return self.DataSource(self, name) @data_source.setter def data_source(self, newval): - if isinstance(newval, _TekTDS5xxDataSource): + if isinstance(newval, self.DataSource): newval = TekTDS5xx.Source(newval.name) if not isinstance(newval, TekTDS5xx.Source): raise TypeError( diff --git a/instruments/teledyne/maui.py b/instruments/teledyne/maui.py index f96941329..6ad1c93a0 100644 --- a/instruments/teledyne/maui.py +++ b/instruments/teledyne/maui.py @@ -16,11 +16,7 @@ from enum import Enum -from instruments.abstract_instruments import ( - Oscilloscope, - OscilloscopeChannel, - OscilloscopeDataSource, -) +from instruments.abstract_instruments import Oscilloscope from instruments.optional_dep_finder import numpy from instruments.units import ureg as u from instruments.util_fns import assume_units, enum_property, bool_property, ProxyList @@ -194,7 +190,7 @@ def _create_trigger_source_enum(self): # CLASSES # - class DataSource(OscilloscopeDataSource): + class DataSource(Oscilloscope.DataSource): """ Class representing a data source (channel, math, ref) on a MAUI @@ -317,7 +313,7 @@ def read_waveform(self, bin_format=False, single=True): """, ) - class Channel(DataSource, OscilloscopeChannel): + class Channel(DataSource, Oscilloscope.Channel): """ Class representing a channel on a MAUI oscilloscope. diff --git a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py index ef28c9808..f953dad8a 100644 --- a/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py +++ b/instruments/tests/test_abstract_inst/test_optical_spectrum_analyzer.py @@ -26,7 +26,7 @@ def osa(monkeypatch): @pytest.fixture def osc(monkeypatch): """Patch and return OSAChannel class for access.""" - inst = ik.abstract_instruments.OSAChannel + inst = ik.abstract_instruments.OpticalSpectrumAnalyzer.Channel monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst diff --git a/instruments/tests/test_abstract_inst/test_oscilloscope.py b/instruments/tests/test_abstract_inst/test_oscilloscope.py index 055628abb..22c0a8c2d 100644 --- a/instruments/tests/test_abstract_inst/test_oscilloscope.py +++ b/instruments/tests/test_abstract_inst/test_oscilloscope.py @@ -26,7 +26,7 @@ def osc(monkeypatch): @pytest.fixture def osc_ch(monkeypatch): """Patch and return OscilloscopeChannel class for access.""" - inst = ik.abstract_instruments.OscilloscopeChannel + inst = ik.abstract_instruments.Oscilloscope.Channel monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst @@ -34,7 +34,7 @@ def osc_ch(monkeypatch): @pytest.fixture def osc_ds(monkeypatch): """Patch and return OscilloscopeDataSource class for access.""" - inst = ik.abstract_instruments.OscilloscopeDataSource + inst = ik.abstract_instruments.Oscilloscope.DataSource monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst diff --git a/instruments/tests/test_abstract_inst/test_power_supply.py b/instruments/tests/test_abstract_inst/test_power_supply.py index 119991634..e2c9446db 100644 --- a/instruments/tests/test_abstract_inst/test_power_supply.py +++ b/instruments/tests/test_abstract_inst/test_power_supply.py @@ -26,7 +26,7 @@ def ps(monkeypatch): @pytest.fixture def ps_ch(monkeypatch): """Patch and return Power Supply Channel class for access.""" - inst = ik.abstract_instruments.PowerSupplyChannel + inst = ik.abstract_instruments.PowerSupply.Channel monkeypatch.setattr(inst, "__abstractmethods__", set()) return inst diff --git a/instruments/tests/test_newport/test_agilis.py b/instruments/tests/test_newport/test_agilis.py index e37f33ad4..440343e27 100644 --- a/instruments/tests/test_newport/test_agilis.py +++ b/instruments/tests/test_newport/test_agilis.py @@ -136,14 +136,14 @@ def test_aguc2_ag_query_io_error(mocker): def test_aguc2_axis_init_enum(axis): """Initialize an axis externally with an enum.""" with expected_protocol(ik.newport.AGUC2, [], [], sep="\r\n") as agl: - ax = ik.newport.agilis._Axis(agl, axis) + ax = ik.newport.agilis.AGUC2.Axis(agl, axis) assert ax._ax == axis.value def test_aguc2_axis_init_wrong_type(): """Raise TypeError when not initialized from AGUC2 parent class.""" with pytest.raises(TypeError) as err_info: - ik.newport.agilis._Axis(42, ik.newport.AGUC2.Axes.X) + ik.newport.agilis.AGUC2.Axis(42, ik.newport.AGUC2.Axes.X) err_msg = err_info.value.args[0] assert err_msg == "Don't do that." diff --git a/instruments/tests/test_newport/test_newportesp301.py b/instruments/tests/test_newport/test_newportesp301.py index 77d150398..ebaec93a9 100644 --- a/instruments/tests/test_newport/test_newportesp301.py +++ b/instruments/tests/test_newport/test_newportesp301.py @@ -42,7 +42,7 @@ def test_axis_returns_axis_class(ax): sep="\r", ) as inst: axis = inst.axis[ax] - assert isinstance(axis, ik.newport.NewportESP301Axis) + assert isinstance(axis, ik.newport.NewportESP301.Axis) def test_newport_cmd(mocker): @@ -145,7 +145,7 @@ def test_home(mocker): ) -@pytest.mark.parametrize("search_mode", ik.newport.NewportESP301HomeSearchMode) +@pytest.mark.parametrize("search_mode", ik.newport.NewportESP301.HomeSearchMode) def test_search_for_home(mocker, search_mode): """Search for home with specific method. @@ -290,7 +290,7 @@ def test_axis_init(): def test_axis_init_type_error(): """Raise TypeError when axis initialized from wrong parent.""" with pytest.raises(TypeError) as err_info: - _ = ik.newport.newportesp301.NewportESP301Axis(42, 0) + _ = ik.newport.newportesp301.NewportESP301.Axis(42, 0) err_msg = err_info.value.args[0] assert ( err_msg == "Axis must be controlled by a Newport ESP-301 motor " "controller." @@ -304,8 +304,8 @@ def test_axis_units_of(mocker): tested separately, thus only assert that the correct calls are issued. """ - get_unit = ik.newport.newportesp301.NewportESP301Units.millimeter - set_unit = ik.newport.newportesp301.NewportESP301Units.inches + get_unit = ik.newport.newportesp301.NewportESP301.Units.millimeter + set_unit = ik.newport.newportesp301.NewportESP301.Units.inches with expected_protocol( ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: @@ -324,7 +324,7 @@ def test_axis_get_units(mocker): Mock out the command sending and receiving. """ resp = "2" - unit = ik.newport.newportesp301.NewportESP301Units(int(resp)) + unit = ik.newport.newportesp301.NewportESP301.Units(int(resp)) with expected_protocol( ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: @@ -340,7 +340,7 @@ def test_axis_set_units(mocker): Mock out the actual command sending for simplicity, but assert it has been called. """ - unit = ik.newport.newportesp301.NewportESP301Units.radian # just pick one + unit = ik.newport.newportesp301.NewportESP301.Units.radian # just pick one with expected_protocol( ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: @@ -1224,7 +1224,7 @@ def test_axis_encoder_position(mocker): Also mock out `_newport_cmd`. """ value = 42 - get_unit = ik.newport.newportesp301.NewportESP301Units.millimeter + get_unit = ik.newport.newportesp301.NewportESP301.Units.millimeter with expected_protocol( ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" ) as inst: @@ -1241,7 +1241,7 @@ def test_axis_encoder_position(mocker): # AXIS METHODS # -@pytest.mark.parametrize("mode", ik.newport.newportesp301.NewportESP301HomeSearchMode) +@pytest.mark.parametrize("mode", ik.newport.newportesp301.NewportESP301.HomeSearchMode) def test_axis_search_for_home(mocker, mode): """Search for home. @@ -1256,6 +1256,22 @@ def test_axis_search_for_home(mocker, mode): mock_search.assert_called_with(axis=1, search_mode=mode.value) +def test_axis_search_for_home_default(mocker): + """Search for home without a specified search mode. + + Mock out `search_for_home` of controller since already tested. + """ + with expected_protocol( + ik.newport.NewportESP301, [ax_init[0]], [ax_init[1]], sep="\r" + ) as inst: + axis = inst.axis[0] + mock_search = mocker.patch.object(axis._controller, "search_for_home") + axis.search_for_home() + + default_mode = axis._controller.HomeSearchMode.zero_position_count.value + mock_search.assert_called_with(axis=1, search_mode=default_mode) + + def test_axis_move_absolute(mocker): """Make an absolute move (default) on the axis. @@ -1480,7 +1496,7 @@ def test_axis_setup_axis(mocker): motor_type = 2 # stepper motor current = 1 voltage = 2 - units = ik.newport.newportesp301.NewportESP301Units.radian + units = ik.newport.newportesp301.NewportESP301.Units.radian encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 @@ -1598,7 +1614,7 @@ def test_axis_setup_axis_torque(mocker): motor_type = 2 # stepper motor current = 1 voltage = 2 - units = ik.newport.newportesp301.NewportESP301Units.radian + units = ik.newport.newportesp301.NewportESP301.Units.radian encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 @@ -1682,7 +1698,7 @@ def test_axis_setup_axis_torque_time_out_of_range(mocker, rmt_time): motor_type = 2 # stepper motor current = 1 voltage = 2 - units = ik.newport.newportesp301.NewportESP301Units.radian + units = ik.newport.newportesp301.NewportESP301.Units.radian encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 @@ -1764,7 +1780,7 @@ def test_axis_setup_axis_torque_percentage_out_of_range(mocker, rmt_perc): motor_type = 2 # stepper motor current = 1 voltage = 2 - units = ik.newport.newportesp301.NewportESP301Units.radian + units = ik.newport.newportesp301.NewportESP301.Units.radian encoder_resolution = 3.0 max_velocity = 4 max_base_velocity = 5 @@ -1844,7 +1860,7 @@ def test_axis_read_setup(mocker): """ config = { "units": u.mm, - "motor_type": ik.newport.newportesp301.NewportESP301MotorType.dc_servo, + "motor_type": ik.newport.newportesp301.NewportESP301.MotorType.dc_servo, "feedback_configuration": 1, # last 2 removed at return "full_step_resolution": u.Quantity(2.0, u.mm), "position_display_resolution": 3, @@ -1877,7 +1893,7 @@ def test_axis_read_setup(mocker): axis = inst.axis[0] mock_cmd = mocker.patch.object(axis, "_newport_cmd") mock_cmd.side_effect = [ - ik.newport.newportesp301.NewportESP301Units.millimeter.value, + ik.newport.newportesp301.NewportESP301.Units.millimeter.value, config["motor_type"].value, f"{config['feedback_configuration']}**", # 2 extra config["full_step_resolution"].magnitude, @@ -1934,7 +1950,7 @@ def test_axis_get_status(mocker): assert axis.get_status() == status -@pytest.mark.parametrize("num", ik.newport.NewportESP301Axis._unit_dict) +@pytest.mark.parametrize("num", ik.newport.NewportESP301.Axis._unit_dict) def test_axis_get_pq_unit(num): """Get units for specified axis.""" with expected_protocol( @@ -1944,7 +1960,7 @@ def test_axis_get_pq_unit(num): assert axis._get_pq_unit(num) == axis._unit_dict[num] -@pytest.mark.parametrize("num", ik.newport.NewportESP301Axis._unit_dict) +@pytest.mark.parametrize("num", ik.newport.NewportESP301.Axis._unit_dict) def test_axis_get_unit_num(num): """Get unit number from dictionary. diff --git a/instruments/tests/test_srs/test_srsdg645.py b/instruments/tests/test_srs/test_srsdg645.py index e4564c6b7..e87be33de 100644 --- a/instruments/tests/test_srs/test_srsdg645.py +++ b/instruments/tests/test_srs/test_srsdg645.py @@ -28,7 +28,7 @@ def test_srsdg645_channel_init(): initialization if not coming from a DG class. """ with pytest.raises(TypeError): - ik.srs.srsdg645._SRSDG645Channel(42, 0) + ik.srs.srsdg645.SRSDG645.Channel(42, 0) def test_srsdg645_channel_init_channel_value(): @@ -38,7 +38,7 @@ def test_srsdg645_channel_init_channel_value(): """ ddg = ik.srs.SRSDG645.open_test() # test connection chan = ik.srs.srsdg645.SRSDG645.Channels.B # select a channel manually - assert ik.srs.srsdg645._SRSDG645Channel(ddg, chan)._chan == 3 + assert ik.srs.srsdg645.SRSDG645.Channel(ddg, chan)._chan == 3 def test_srsdg645_channel_delay(): diff --git a/instruments/tests/test_tektronix/test_tekdpo4104.py b/instruments/tests/test_tektronix/test_tekdpo4104.py index dfecac28f..5eed6b18d 100644 --- a/instruments/tests/test_tektronix/test_tekdpo4104.py +++ b/instruments/tests/test_tektronix/test_tekdpo4104.py @@ -50,7 +50,7 @@ def test_data_source(): ) as inst: # Channel as string inst.data_source = "CH1" - assert inst.data_source == ik.tektronix.tekdpo4104._TekDPO4104Channel(inst, 0) + assert inst.data_source == ik.tektronix.tekdpo4104.TekDPO4104.Channel(inst, 0) # Reference channel as enum class RefChannel(Enum): @@ -60,14 +60,14 @@ class RefChannel(Enum): channel = RefChannel.channel.value inst.data_source = RefChannel.channel - assert inst.data_source == ik.tektronix.tekdpo4104._TekDPO4104DataSource( + assert inst.data_source == ik.tektronix.tekdpo4104.TekDPO4104.DataSource( inst, channel ) # Set a math channel math_ch = inst.math inst.data_source = math_ch - assert inst.data_source == ik.tektronix.tekdpo4104._TekDPO4104DataSource( + assert inst.data_source == ik.tektronix.tekdpo4104.TekDPO4104.DataSource( inst, math_ch.name ) @@ -209,7 +209,7 @@ def test_data_source_ref_initialize(ref): ref_source = inst.ref[ref] # test instance - assert isinstance(ref_source, ik.tektronix.tekdpo4104._TekDPO4104DataSource) + assert isinstance(ref_source, ik.tektronix.tekdpo4104.TekDPO4104.DataSource) # test for parent assert ref_source._tek is inst @@ -221,7 +221,7 @@ def test_data_source_math_initialize(): math_source = inst.math # test instance - assert isinstance(math_source, ik.tektronix.tekdpo4104._TekDPO4104DataSource) + assert isinstance(math_source, ik.tektronix.tekdpo4104.TekDPO4104.DataSource) # test for parent assert math_source._tek is inst diff --git a/instruments/tests/test_tektronix/test_tektronix_tds224.py b/instruments/tests/test_tektronix/test_tektronix_tds224.py index e8928221c..267285de2 100644 --- a/instruments/tests/test_tektronix/test_tektronix_tds224.py +++ b/instruments/tests/test_tektronix/test_tektronix_tds224.py @@ -73,7 +73,7 @@ def test_tektds224_data_source(mock_time): ["MATH", "CH1"], ) as tek: assert tek.data_source == tek.math - assert tek.data_source == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) + assert tek.data_source == ik.tektronix.tektds224.TekTDS224.Channel(tek, 0) tek.data_source = tek.math # assert that time.sleep is called @@ -94,7 +94,7 @@ class Channel(Enum): def test_tektds224_channel(): with expected_protocol(ik.tektronix.TekTDS224, [], []) as tek: - assert tek.channel[0] == ik.tektronix.tektds224._TekTDS224Channel(tek, 0) + assert tek.channel[0] == ik.tektronix.tektds224.TekTDS224.Channel(tek, 0) def test_tektds224_channel_coupling(): diff --git a/instruments/tests/test_tektronix/test_tktds5xx.py b/instruments/tests/test_tektronix/test_tktds5xx.py index 953c03e82..095d9604f 100644 --- a/instruments/tests/test_tektronix/test_tktds5xx.py +++ b/instruments/tests/test_tektronix/test_tktds5xx.py @@ -385,17 +385,17 @@ def test_sources(states): for idx in range(4): if states[idx]: active_sources.append( - ik.tektronix.tektds5xx._TekTDS5xxChannel(inst, idx) + ik.tektronix.tektds5xx.TekTDS5xx.Channel(inst, idx) ) for idx in range(4, 7): if states[idx]: active_sources.append( - ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, f"MATH{idx-3}") + ik.tektronix.tektds5xx.TekTDS5xx.DataSource(inst, f"MATH{idx - 3}") ) for idx in range(7, 11): if states[idx]: active_sources.append( - ik.tektronix.tektds5xx._TekTDS5xxDataSource(inst, f"REF{idx-6}") + ik.tektronix.tektds5xx.TekTDS5xx.DataSource(inst, f"REF{idx - 6}") ) # read active sources active_read = inst.sources diff --git a/instruments/yokogawa/yokogawa6370.py b/instruments/yokogawa/yokogawa6370.py index eb62c510d..6c4ffbc02 100644 --- a/instruments/yokogawa/yokogawa6370.py +++ b/instruments/yokogawa/yokogawa6370.py @@ -10,10 +10,7 @@ from instruments.units import ureg as u -from instruments.abstract_instruments import ( - OpticalSpectrumAnalyzer, - OSAChannel, -) +from instruments.abstract_instruments import OpticalSpectrumAnalyzer from instruments.util_fns import ( enum_property, unitful_property, @@ -46,12 +43,12 @@ def __init__(self, *args, **kwargs): # INNER CLASSES # - class Channel(OSAChannel): + class Channel(OpticalSpectrumAnalyzer.Channel): """ Class representing the channels on the Yokogawa 6370. - This class inherits from `OSAChannel`. + This class inherits from `OpticalSpectrumAnalyzer.Channel`. .. warning:: This class should NOT be manually created by the user. It is designed to be initialized by the `Yokogawa6370` class. diff --git a/instruments/yokogawa/yokogawa7651.py b/instruments/yokogawa/yokogawa7651.py index 9be267e29..5a60a1725 100644 --- a/instruments/yokogawa/yokogawa7651.py +++ b/instruments/yokogawa/yokogawa7651.py @@ -10,10 +10,7 @@ from instruments.units import ureg as u -from instruments.abstract_instruments import ( - PowerSupply, - PowerSupplyChannel, -) +from instruments.abstract_instruments import PowerSupply from instruments.abstract_instruments import Instrument from instruments.util_fns import assume_units, ProxyList @@ -35,12 +32,12 @@ class Yokogawa7651(PowerSupply, Instrument): # INNER CLASSES # - class Channel(PowerSupplyChannel): + class Channel(PowerSupply.Channel): """ Class representing the only channel on the Yokogawa 7651. - This class inherits from `PowerSupplyChannel`. + This class inherits from `PowerSupply.Channel`. .. warning:: This class should NOT be manually created by the user. It is designed to be initialized by the `Yokogawa7651` class. From 2690ed91672e5617cef537dcf38347d7ea4a9076 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 25 Jan 2022 22:02:39 -0500 Subject: [PATCH 105/108] Convert project versioning to use setuptools_scm (#322) * Convert project versioning to use setuptools_scm * Update checkout action to get tag data * Testing * Testing * Add test for version file * Fix short X.Y version in conf.py * Get version using pkg_resources * Re-add setup.py stub * Switch to .readthedocs.yml * Update to importlib.metadata --- .github/workflows/deploy.yml | 2 ++ .github/workflows/test.yml | 2 ++ .gitignore | 3 +++ .readthedocs.yml | 15 +++++++++++++++ doc/source/conf.py | 13 +++++++------ instruments/__init__.py | 4 ---- instruments/tests/test_package.py | 15 +++++++++++++++ pyproject.toml | 5 ++++- rtd.txt | 1 - setup.cfg | 2 +- 10 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 .readthedocs.yml create mode 100644 instruments/tests/test_package.py delete mode 100644 rtd.txt diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 253beffb7..c55dde561 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,6 +9,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 44af27f7b..d0aab7262 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: diff --git a/.gitignore b/.gitignore index 0140fc170..9c71cd94b 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,6 @@ nosetests.xml # Hypothesis files .hypothesis/ + +# version file generated by setuptools_scm +instruments/_version.py diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..a08b5496d --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,15 @@ +version: 2 +build: + os: ubuntu-20.04 + tools: + python: "3.9" + +sphinx: + builder: html + configuration: doc/source/conf.py + fail_on_warning: false + +python: + install: + - method: pip + path: . diff --git a/doc/source/conf.py b/doc/source/conf.py index f34350c74..875928b6c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -10,8 +10,9 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os -from instruments import __version__ +from importlib.metadata import version +import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -54,10 +55,10 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -version = __version__ -# The full version, including alpha/beta/rc tags. -release = version +# The full release version +release = version("instruments") +# The short X.Y version +version = ".".join(release.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/instruments/__init__.py b/instruments/__init__.py index 8a0a9976c..7e22315a3 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -38,10 +38,6 @@ from .units import ureg as units # VERSION METADATA ########################################################### -# In keeping with PEP-396, we define a version number of the form -# {major}.{minor}[.{postrelease}]{prerelease-tag} - -__version__ = "0.6.0" __title__ = "instrumentkit" __description__ = "Test and measurement communication library" diff --git a/instruments/tests/test_package.py b/instruments/tests/test_package.py new file mode 100644 index 000000000..6388908f7 --- /dev/null +++ b/instruments/tests/test_package.py @@ -0,0 +1,15 @@ +""" +Module containing tests for the base instruments package +""" + +# IMPORTS #################################################################### + +import instruments._version as ik_version_file + + +# TEST CASES ################################################################# + + +def test_package_has_version(): + assert hasattr(ik_version_file, "version") + assert hasattr(ik_version_file, "version_tuple") diff --git a/pyproject.toml b/pyproject.toml index 121a39f5f..346ac06f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,6 @@ [build-system] -requires = ["setuptools >= 40.6.0", "wheel"] +requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "instruments/_version.py" diff --git a/rtd.txt b/rtd.txt deleted file mode 100644 index d6e1198b1..000000000 --- a/rtd.txt +++ /dev/null @@ -1 +0,0 @@ --e . diff --git a/setup.cfg b/setup.cfg index a47d75982..5b21d9d45 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = instruments -version = attr: instruments.__version__ +version = attr: instruments._version.version description = attr: instruments.__description__ author = attr: instruments.__author__ author_email = attr: instruments.__email__ From 5490969fbd7a5b953b68d75945222771ca730e4c Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 27 Jan 2022 00:22:26 -0500 Subject: [PATCH 106/108] Move metadata into setup.cfg (#324) --- instruments/__init__.py | 12 ------------ setup.cfg | 10 +++++----- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/instruments/__init__.py b/instruments/__init__.py index 7e22315a3..9b85d235f 100644 --- a/instruments/__init__.py +++ b/instruments/__init__.py @@ -36,15 +36,3 @@ from .config import load_instruments from .units import ureg as units - -# VERSION METADATA ########################################################### - -__title__ = "instrumentkit" -__description__ = "Test and measurement communication library" -__uri__ = "https://instrumentkit.readthedocs.org/" - -__author__ = "Steven Casagrande" -__email__ = "scasagrande@galvant.ca" - -__license__ = "AGPLv3" -__copyright__ = "Copyright (c) 2012-2022 Steven Casagrande" diff --git a/setup.cfg b/setup.cfg index 5b21d9d45..19bb3473d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,10 +1,10 @@ [metadata] name = instruments version = attr: instruments._version.version -description = attr: instruments.__description__ -author = attr: instruments.__author__ -author_email = attr: instruments.__email__ -url = attr: instruments.__uri__ +description = Test and measurement communication library +author = Steven Casagrande +author_email = stevencasagrande@gmail.com +url = https://www.github.com/Galvant/InstrumentKit long_description = file: README.rst long_description_content_type = text/x-rst license = AGPLv3 @@ -53,5 +53,5 @@ dev = exclude = instruments.tests -[wheel] +[bdist_wheel] universal = 1 From 97c528abef8be956ba6d3c3c26dd53e188bbdab9 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 27 Jan 2022 01:01:10 -0500 Subject: [PATCH 107/108] Update package name (#325) * Move metadata into setup.cfg * Update package name in setup.cfg * Update conf.py --- doc/source/conf.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 875928b6c..4fa33ded7 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -56,7 +56,7 @@ # built documents. # # The full release version -release = version("instruments") +release = version("instrumentkit") # The short X.Y version version = ".".join(release.split(".")[:2]) diff --git a/setup.cfg b/setup.cfg index 19bb3473d..59cd49e41 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = instruments +name = instrumentkit version = attr: instruments._version.version description = Test and measurement communication library author = Steven Casagrande From 70cfb051cfbe80d715932f519f717d9a624bdba7 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Thu, 27 Jan 2022 22:17:04 -0500 Subject: [PATCH 108/108] Add py310 support (#327) * Bump ruamel.yaml>=0.16,<0.17 * Add Py310 to tox and CI * Add Py310 classifier * Update README.rst --- .github/workflows/test.yml | 2 ++ README.rst | 2 +- setup.cfg | 3 ++- tox.ini | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d0aab7262..84a23c03b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,6 +33,8 @@ jobs: TOXENV: "py39" - python-version: 3.9 TOXENV: "py39-numpy" + - python-version: "3.10" + TOXENV: "py310" steps: - uses: actions/checkout@v2 diff --git a/README.rst b/README.rst index 48afcd32a..42c6ceb29 100644 --- a/README.rst +++ b/README.rst @@ -112,7 +112,7 @@ send, one can use the following functions to do so: Python Version Compatibility ---------------------------- -At this time, Python 3.6, 3.7, 3.8, and 3.9 are supported. Should you encounter +At this time, Python 3.6, 3.7, 3.8, 3.9, and 3.10 are supported. Should you encounter any problems with this library that occur in one version or another, please do not hesitate to let us know. diff --git a/setup.cfg b/setup.cfg index 59cd49e41..faf95b21b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Operating System :: OS Independent License :: OSI Approved :: GNU Affero General Public License v3 Intended Audience :: Science/Research @@ -34,7 +35,7 @@ install_requires = python-vxi11>=0.8 pyusb>=1.0 pyvisa>=1.9 - ruamel.yaml~=0.15.37 + ruamel.yaml>=0.16,<0.17 [options.extras_require] numpy = numpy diff --git a/tox.ini b/tox.ini index 78cad4ac9..a59a40409 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{36,37,38,39},py{36,37,38,39}-numpy,pylint +envlist = py{36,37,38,39,310},py{36,37,38,39,310}-numpy,pylint isolated_build = true [testenv]