diff --git a/src/fixate/drivers/dmm/keithley_6500.py b/src/fixate/drivers/dmm/keithley_6500.py index b6c0e6db..011cd279 100644 --- a/src/fixate/drivers/dmm/keithley_6500.py +++ b/src/fixate/drivers/dmm/keithley_6500.py @@ -2,6 +2,49 @@ from fixate.core.exceptions import InstrumentError, ParameterError from fixate.drivers.dmm.helper import DMM import time +from dataclasses import dataclass +from logging import getLogger + +logger = getLogger(__name__) + + +@dataclass +class DMMRanges: + """ + Class to store DMM range definitions. These are taken from the DMM User Manual / Specifications. + """ + + # Overrange is 20% on all ranges except 1000 VDC which is 1% + # fmt: off + current_dc = ( 10e-6, 100e-6, 1e-3, 10e-3, 100e-3, 1, 3, 10,) # Not all of these ranges are available. Modified to match Fluke DMM in some cases. + current_ac = (100e-3, 1e-3, 10e-3, 100e-3, 1, 3, 10) + voltage_dc = (0.1, 1, 10, 100, 1000) + voltage_ac = (100e-3, 1, 10, 100, 750) + resistance = (1, 10, 100, 1e3, 10e3, 100e3, 1e6, 10e6, 100e6) + temperature = () # Empty. No ranges for temperature + frequency = ( 300e-3,) # No adjustable range for frequency. Just put maximum range here. + period = (3.3e-6,) # No adjustable range for period. Just put maximum range here. + continuity = (1e3,) # No selectable range for continuity. Put maximum range here. + capacitance = (1e-9, 10e-9, 100e-9, 1e-6, 10e-6, 100e-6) + diode = (10,) # No selectable range for diode. Default is 10V + + # Helper to map a mode to a range + # Note: This means we have to keep self._modes and this dictionary in sync. Maybe there is a better way to do this? + mode_to_range = { + "current_dc": current_dc, + "current_ac": current_ac, + "voltage_dc": voltage_dc, + "voltage_ac": voltage_ac, + "resistance": resistance, + "fresistance": resistance, # Four wire resistance uses the same ranges as two wire + "temperature": temperature, # Not currently implemented in the driver. + "frequency": frequency, + "period": period, + "continuity": continuity, + "capacitance": capacitance, + "diode": diode, + } + # fmt: on class Keithley6500(DMM): @@ -41,6 +84,7 @@ def __init__(self, instrument, *args, **kwargs): "diode": "DIOD", } + self.range = None # Currently selected range. Can be None if the mode does not have a range. self._init_string = "" # Unchanging # Adapted for different DMM behaviour @@ -147,6 +191,7 @@ def reset(self): self._write("*RST") self._CLEAN_UP_FLAG = False self._is_error() + self.instrument.clear() # Clear buffer after reset def __enter__(self): return self @@ -244,17 +289,48 @@ def _set_measurement_mode(self, mode, _range=None, suffix=None): :param _range: :return: """ - self.mode = mode + self.mode = mode # Update the mode. self._manual_trigger = False + self.range = self._select_range(_range) + mode_str = f"SENS:FUNC '{self._modes[self._mode]}'" - if _range is not None: - mode_str += f"; :SENS:{self._modes[self._mode]}:RANGE {_range}" + if self.range is not None: + # Don't error when range is None. This is valid in some modes. + mode_str += f"; :SENS:{self._modes[self._mode]}:RANGE {self.range}" if suffix is not None: mode_str += suffix self._write(mode_str) self._write(f":COUN {self.samples}") self._is_error() + def _select_range(self, value): + """ + Selects the appropriate range for the DMM to measure "value" + + return: Range value to set on the DMM + raise: ParameterError if the range is not valid for the mode (over range) + """ + # Some modes don't have adjustable range. Return None if this is the case. + if value is None: + return None + + ranges = self._get_ranges() # Get ranges for the current mode + for i in ranges: + if abs(value) <= i: + return i + raise ParameterError( + f"Requested range '{value}' is too large for mode '{self.mode}'" + ) + + def _get_ranges(self): + """ + Returns a tuple of available ranges for the current mode + """ + if self.mode is None: + raise InstrumentError("DMM mode is not set. Cannot return range") + + return DMMRanges.mode_to_range[self.mode] + def voltage_ac(self, _range=None): self._set_measurement_mode("voltage_ac", _range) @@ -267,9 +343,15 @@ def voltage_dc(self, _range=None, auto_impedance=False): self._set_measurement_mode("voltage_dc", _range, suffix=command) def current_ac(self, _range=None): + if _range >= 400e-3: + # Modify the range to match the Fluke DMM port ranges + _range = 10 # 10A range will use the 10A port self._set_measurement_mode("current_ac", _range) def current_dc(self, _range=None): + if _range >= 400e-3: + # Modify the range to match the Fluke DMM port ranges + _range = 10 # 10A range will use the 10A port self._set_measurement_mode("current_dc", _range) def resistance(self, _range=None):