From 2f98da8695d3ff3291f25729f211e829a341b4fc Mon Sep 17 00:00:00 2001 From: Jasper-Harvey0 Date: Fri, 26 Jul 2024 11:40:54 +1000 Subject: [PATCH 1/3] Add proof of concept changes --- src/fixate/drivers/dmm/keithley_6500.py | 84 ++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/src/fixate/drivers/dmm/keithley_6500.py b/src/fixate/drivers/dmm/keithley_6500.py index b6c0e6db..597e2df4 100644 --- a/src/fixate/drivers/dmm/keithley_6500.py +++ b/src/fixate/drivers/dmm/keithley_6500.py @@ -2,6 +2,45 @@ 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% + 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 + } class Keithley6500(DMM): @@ -41,6 +80,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 +187,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,16 +285,47 @@ 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): + """ + Translates the range value given to a mode function for the DMM + 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 a 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 +339,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): From 4c0ff242e5fb67b0abc9ad6c5e619dcbad76caaf Mon Sep 17 00:00:00 2001 From: Jasper-Harvey0 Date: Fri, 26 Jul 2024 13:07:36 +1000 Subject: [PATCH 2/3] Update description for _select_range() --- src/fixate/drivers/dmm/keithley_6500.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fixate/drivers/dmm/keithley_6500.py b/src/fixate/drivers/dmm/keithley_6500.py index 597e2df4..237efca7 100644 --- a/src/fixate/drivers/dmm/keithley_6500.py +++ b/src/fixate/drivers/dmm/keithley_6500.py @@ -301,8 +301,7 @@ def _set_measurement_mode(self, mode, _range=None, suffix=None): def _select_range(self, value): """ - Translates the range value given to a mode function for the DMM - selects the appropriate range for the DMM to measure "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) From b540fb17b44d3c17f356902750ef96851b7947f2 Mon Sep 17 00:00:00 2001 From: Jasper-Harvey0 Date: Fri, 26 Jul 2024 13:18:41 +1000 Subject: [PATCH 3/3] Wording + Black --- src/fixate/drivers/dmm/keithley_6500.py | 49 ++++++++++++++----------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/fixate/drivers/dmm/keithley_6500.py b/src/fixate/drivers/dmm/keithley_6500.py index 237efca7..011cd279 100644 --- a/src/fixate/drivers/dmm/keithley_6500.py +++ b/src/fixate/drivers/dmm/keithley_6500.py @@ -7,23 +7,26 @@ 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% - 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. + # 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_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. + 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 + 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? @@ -33,14 +36,15 @@ class DMMRanges: "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. + "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 + "diode": diode, } + # fmt: on class Keithley6500(DMM): @@ -80,7 +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.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 @@ -187,7 +191,7 @@ def reset(self): self._write("*RST") self._CLEAN_UP_FLAG = False self._is_error() - self.instrument.clear() # Clear buffer after reset + self.instrument.clear() # Clear buffer after reset def __enter__(self): return self @@ -285,7 +289,7 @@ def _set_measurement_mode(self, mode, _range=None, suffix=None): :param _range: :return: """ - self.mode = mode # Update the mode. + self.mode = mode # Update the mode. self._manual_trigger = False self.range = self._select_range(_range) @@ -298,23 +302,25 @@ def _set_measurement_mode(self, mode, _range=None, suffix=None): 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 a range. Return None if this is the case. + # 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 + 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}'") + raise ParameterError( + f"Requested range '{value}' is too large for mode '{self.mode}'" + ) def _get_ranges(self): """ @@ -322,9 +328,8 @@ def _get_ranges(self): """ 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) @@ -340,13 +345,13 @@ def voltage_dc(self, _range=None, auto_impedance=False): 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 + _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 + _range = 10 # 10A range will use the 10A port self._set_measurement_mode("current_dc", _range) def resistance(self, _range=None):