From e9165ffac8e027024497613ce1d8b3b39884f53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20L=C3=A5ngbacka?= Date: Fri, 25 Aug 2017 15:01:12 +0200 Subject: [PATCH 1/7] Driver for Keithley 2280S power supply --- README.md | 1 + ivi/keithley/__init__.py | 29 +++++ ivi/keithley/keithley2280S.py | 196 ++++++++++++++++++++++++++++++++++ setup.py | 1 + 4 files changed, 227 insertions(+) create mode 100644 ivi/keithley/__init__.py create mode 100644 ivi/keithley/keithley2280S.py diff --git a/README.md b/README.md index 36c2180e..6a67c58f 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Instrument standard from the [IVI foundation](http://www.ivifoundation.org/). * Chroma 62000P series * Rigol DP800 series * Rigol DP1000 series + * Keithley (Tektronix) 2280S series * Tektronix PS2520G/PS2521G * RF Power Meters (pwrmeter): * Agilent 436A diff --git a/ivi/keithley/__init__.py b/ivi/keithley/__init__.py new file mode 100644 index 00000000..a64334a0 --- /dev/null +++ b/ivi/keithley/__init__.py @@ -0,0 +1,29 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Jonas Långbacka, Acconeer Ab + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +# DC Power Supplies +from .keithley2280S import keithley2280S +#from .keithley2280S import keithley2280S_60_3 \ No newline at end of file diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py new file mode 100644 index 00000000..7951d081 --- /dev/null +++ b/ivi/keithley/keithley2280S.py @@ -0,0 +1,196 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Acconeer Ab + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" +from .. import ivi +from .. import scpi +from .. import dcpwr +#from .. import extra + +TrackingType = set(['floating']) +TriggerSourceMapping = { + 'immediate': 'imm', + 'external': 'ext', + 'manual': 'man'} +MeasurementType = set(['voltage', 'current', 'concurrent']) + +class keithley2280S(scpi.dcpwr.Base, scpi.dcpwr.SoftwareTrigger, + dcpwr.Measurement, dcpwr.Trigger): + "Keithley (Tektronix) 2280S series precision measurement DC supply driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'keithley2280S') + + super(keithley2280S, self).__init__(*args, **kwargs) + + self._output_count = 1 + + self._output_spec = [ + { + 'range': { + 'P32V': (32.0, 6.0) + }, + 'ovp_max': 32.0, + 'voltage_max': 32.0, + 'current_max': 6.0 + } + ] + + self._memory_size = 2500 + self._memory_offset = 0 + + self._output_trigger_delay = list() + + self._identity_description = "Keithley (Tektronix) 2280S series precision measurement DC supply driver" + self._identity_identifier = "" + self._identity_revision = "" + self._identity_vendor = "" + self._identity_instrument_manufacturer = "Keithley (Tektronix)" + self._identity_instrument_model = "" + self._identity_instrument_firmware_revision = "" + self._identity_specification_major_version = 3 + self._identity_specification_minor_version = 0 + self._identity_supported_instrument_models = ['2280S-32-6', '2280S-60-3'] + + self._init_outputs() + +# def _initialize(self, resource = None, id_query = False, reset = False, **keywargs): +# "Opens an I/O session to the instrument." +# +# super(keithley2280S, self)._initialize(resource, id_query, reset, **keywargs) +# +# # interface clear +# if not self._driver_operation_simulate: +# self._clear() +# +# # check ID +# if id_query and not self._driver_operation_simulate: +# id = self.identity.instrument_model +# id_check = self._instrument_id +# id_short = id[:len(id_check)] +# if id_short != id_check: +# raise Exception("Instrument ID mismatch, expecting %s, got %s", id_check, id_short) +# +# # reset +# if reset: +# self.utility_reset() + + def _utility_disable(self): + pass + + def _utility_lock_object(self): + pass + + def _utility_unlock_object(self): + pass + +# def _init_outputs(self): +# try: +# super(keithley2280S, self)._init_outputs() +# except AttributeError: +# pass +# +# self._output_current_limit = list() +# self._output_current_limit_behavior = list() +# self._output_enabled = list() +# self._output_ovp_enabled = list() +# self._output_ovp_limit = list() +# self._output_voltage_level = list() +# self._output_trigger_source = list() +# self._output_trigger_delay = list() +# for i in range(self._output_count): +# self._output_current_limit.append(0) +# self._output_current_limit_behavior.append('regulate') +# self._output_enabled.append(False) +# self._output_ovp_enabled.append(True) +# self._output_ovp_limit.append(0) +# self._output_voltage_level.append(0) +# self._output_trigger_source.append('bus') +# self._output_trigger_delay.append(0) + + + def _get_output_current_limit_behavior(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._output_current_limit_behavior[index] = 'regulate' + self._set_cache_valid(index=index) + return self._output_current_limit_behavior[index] + + def _set_output_current_limit_behavior(self, index, value): + raise ivi.ValueNotSupportedException() + + def _get_output_ovp_enabled(self, index): + # Alwayas enabled by default + raise ivi.ValueNotSupportedException() + + def _set_output_ovp_enabled(self, index, value): + # Alwayas enabled by default + raise ivi.ValueNotSupportedException() + + def _output_configure_range(self, index, range_type, range_val): + # Voltage and current can be set in any range supported by the instrument and hence doesn't have a range command + raise ivi.ValueNotSupportedException() + + def _output_reset_output_protection(self): + if not self._driver_operation_simulate: + self._write("output:protection:clear") + + def _get_output_trigger_source(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask("trigger:source?").lower() + self._output_trigger_source[index] = [k for k,v in TriggerSourceMapping.items() if v==value][0] + return self._output_trigger_source[index] + + def _set_output_trigger_source(self, index, value): + index = ivi.get_index(self._output_name, index) + value = str(value) + if value not in TriggerSourceMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write("trigger:source %s" % TriggerSourceMapping[value]) + self._output_trigger_source[index] = value + self._set_cache_valid(index=index) + + def _trigger_abort(self): + if not self._driver_operation_simulate: + self._write("abort") + + def _trigger_initiate(self): + if not self._driver_operation_simulate: + self._write("initiate") + + def _output_measure(self, index, type): + index = ivi.get_index(self._output_name, index) + if type not in MeasurementType: + raise ivi.ValueNotSupportedException() + if type == 'voltage': + if not self._driver_operation_simulate: + return float(self._ask("measure:voltage?").split(',')[1][:-1]) + if type == 'current': + return float(self._ask("measure:current?").split(',')[0][:-1]) + elif type == 'concurrent': # Measure both current and voltage at the same time + value = self._ask("measure:concurrent?").split(',')[0:2] + return [float(v[:-1]) for v in value] + return 0 \ No newline at end of file diff --git a/setup.py b/setup.py index e9484085..b938d80c 100644 --- a/setup.py +++ b/setup.py @@ -66,6 +66,7 @@ def run_tests(self): 'ivi.dicon', 'ivi.ics', 'ivi.jdsu', + 'ivi.keithley', 'ivi.lecroy', 'ivi.rigol', 'ivi.tektronix', From a11766ff160e2e51894c052ce76304246688076b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20L=C3=A5ngbacka?= Date: Fri, 8 Sep 2017 19:09:32 +0200 Subject: [PATCH 2/7] Added more instrument functionality --- ivi/__init__.py | 1 + ivi/interface/pyvisa.py | 3 + ivi/keithley/keithley2280S.py | 161 +++++++++++++++++++++++++++++++--- setup.py | 2 +- 4 files changed, 152 insertions(+), 15 deletions(-) diff --git a/ivi/__init__.py b/ivi/__init__.py index 82efcc87..b70cc52e 100644 --- a/ivi/__init__.py +++ b/ivi/__init__.py @@ -50,6 +50,7 @@ "colby", "ics", "jdsu", + "keithley", "lecroy", "rigol", "tektronix", diff --git a/ivi/interface/pyvisa.py b/ivi/interface/pyvisa.py index 29f0712e..23e6b36d 100644 --- a/ivi/interface/pyvisa.py +++ b/ivi/interface/pyvisa.py @@ -34,6 +34,9 @@ # New style PyVISA visa_rm = visa.ResourceManager() visa_instrument_opener = visa_rm.open_resource + except OSError: + visa_rm = visa.ResourceManager('@py') + visa_instrument_opener = visa_rm.open_resource except AttributeError: # Old style PyVISA visa_instrument_opener = visa.instrument diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py index 7951d081..ad2154ed 100644 --- a/ivi/keithley/keithley2280S.py +++ b/ivi/keithley/keithley2280S.py @@ -26,17 +26,25 @@ from .. import ivi from .. import scpi from .. import dcpwr -#from .. import extra +from .. import extra TrackingType = set(['floating']) TriggerSourceMapping = { 'immediate': 'imm', 'external': 'ext', 'manual': 'man'} +RangeType = set(['current']) MeasurementType = set(['voltage', 'current', 'concurrent']) +MeasurementFunctionMapping = { + 'minimum': 'min', + 'maximum': 'max', + 'average': 'mean', + 'peak_to_peak': 'pkpk', + 'standard_deviation': 'sdev'} class keithley2280S(scpi.dcpwr.Base, scpi.dcpwr.SoftwareTrigger, - dcpwr.Measurement, dcpwr.Trigger): + dcpwr.Measurement, + extra.dcpwr.OCP): "Keithley (Tektronix) 2280S series precision measurement DC supply driver" def __init__(self, *args, **kwargs): @@ -51,7 +59,8 @@ def __init__(self, *args, **kwargs): 'range': { 'P32V': (32.0, 6.0) }, - 'ovp_max': 32.0, + 'ovp_max': 33.0, + 'ocp_max': 6.1, 'voltage_max': 32.0, 'current_max': 6.0 } @@ -72,6 +81,24 @@ def __init__(self, *args, **kwargs): self._identity_specification_major_version = 3 self._identity_specification_minor_version = 0 self._identity_supported_instrument_models = ['2280S-32-6', '2280S-60-3'] + + self._add_property('outputs[].trigger_count', + self._get_output_trigger_count, + self._set_output_trigger_count) + + self._add_property('outputs[].trigger_sample_count', + self._get_output_trigger_sample_count, + self._set_output_trigger_sample_count) + + self._add_method('trigger.initiate', + self._trigger_initiate) + + self._add_property('outputs[].trigger_source', + self._get_output_trigger_source, + self._set_output_trigger_source) + + self._add_method('outputs[].configure_measurement', + self._output_configure_measurement) self._init_outputs() @@ -142,15 +169,52 @@ def _set_output_current_limit_behavior(self, index, value): def _get_output_ovp_enabled(self, index): # Alwayas enabled by default - raise ivi.ValueNotSupportedException() + return True def _set_output_ovp_enabled(self, index, value): # Alwayas enabled by default raise ivi.ValueNotSupportedException() - def _output_configure_range(self, index, range_type, range_val): - # Voltage and current can be set in any range supported by the instrument and hence doesn't have a range command - raise ivi.ValueNotSupportedException() + def _get_output_ovp_limit(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._output_ovp_limit[index] = float(self._ask("source:voltage:protection:level?")) + self._set_cache_valid(index=index) + return self._output_ovp_limit[index] + + def _set_output_ovp_limit(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if abs(value) > self._output_spec[index]['ovp_max']: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write("source:voltage:protection:level %f" % float(value)) + self._output_ovp_limit[index] = value + self._set_cache_valid(index=index) + + def _get_output_ocp_limit(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._output_ocp_limit[index] = float(self._ask("source:current:protection:level?")) + self._set_cache_valid(index=index) + return self._output_ocp_limit[index] + + def _set_output_ocp_limit(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if abs(value) > self._output_spec[index]['ocp_max']: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write("source:current:protection:level %f" % float(value)) + self._output_ocp_limit[index] = value + self._set_cache_valid(index=index) + + def _output_configure_range(self, index, range_type, range_value): + if range_type not in RangeType: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write("sense:current:range %s" % range_value) + return def _output_reset_output_protection(self): if not self._driver_operation_simulate: @@ -173,6 +237,30 @@ def _set_output_trigger_source(self, index, value): self._output_trigger_source[index] = value self._set_cache_valid(index=index) + def _get_output_trigger_count(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + int(self._ask("trigger:count?")) + + def _set_output_trigger_count(self, index, value): + index = ivi.get_index(self._output_name, index) + if (value < 0) or (value > 2500): + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write("trigger:count %d" % value) + + def _get_output_trigger_sample_count(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + int(self._ask("trigger:sample:count?")) + + def _set_output_trigger_sample_count(self, index, value): + index = ivi.get_index(self._output_name, index) + if (value < 0) or (value > 2500): + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write("trigger:sample:count %d" % value) + def _trigger_abort(self): if not self._driver_operation_simulate: self._write("abort") @@ -181,16 +269,61 @@ def _trigger_initiate(self): if not self._driver_operation_simulate: self._write("initiate") + + def _output_measure(self, index, type): index = ivi.get_index(self._output_name, index) - if type not in MeasurementType: + if type not in set(['voltage', 'current']): raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write("FORM:ELEM \"READ\"") if type == 'voltage': if not self._driver_operation_simulate: - return float(self._ask("measure:voltage?").split(',')[1][:-1]) + return float(self._ask("measure:voltage?")) if type == 'current': - return float(self._ask("measure:current?").split(',')[0][:-1]) - elif type == 'concurrent': # Measure both current and voltage at the same time - value = self._ask("measure:concurrent?").split(',')[0:2] - return [float(v[:-1]) for v in value] - return 0 \ No newline at end of file + return float(self._ask("measure:current?")) + return 0 + + def _output_configure_measurement(self, index, type, measurement_count=1, NPLC=1, measurement_digits=6): + index = ivi.get_index(self._output_name, index) + if (measurement_count > 2500) or (measurement_count < 0): + raise ivi.OutOfRangeException('Maximum buffer length is 2500') + if (NPLC < 0.002) or (NPLC > 12): + raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 6): + raise ivi.OutOfRangeException('Number of measurement digits must be between 4 and 6') + if type not in MeasurementType: + raise ivi.ValueNotSupportedException() + + self._set_output_trigger_sample_count = measurement_count + self._write(":sense:"+type+":NPLC %f" % NPLC) + self._write(":sense:"+type+":digits %d" % measurement_digits) + + if not self._driver_operation_simulate: + # extend buffer memory size if measurement_count exceeds buffer size + if int(self._ask(":trace:points?")) < measurement_count: + self._write(":trace:points %d" % measurement_count) + if type == 'voltage': + self._write("sense:function \"voltage\"") + if type == 'current': + self._write("sense:function \"current\"") + elif type == 'concurrent': # Measure both current and voltage at the same time + self._write("sense:function \"concurrent\"") + + +# def _output_measure_statistics(self, index, type): +# index = ivi.get_index(self._output_name, index) +# if type not in MeasurementType: +# raise ivi.ValueNotSupportedException() +# if type == 'voltage': +# if not self._driver_operation_simulate: +# self._write("calculate2:function \"voltage\"") +# return [float(v) for v in self._ask("measure:voltage?").split(',')] +# if type == 'current': +# if not self._driver_operation_simulate: +# self._write("calculate2:function \"current\"") +# return float(self._ask("measure:current?").split(',')[0][:-1]) +# elif type == 'concurrent': # Measure both current and voltage at the same time +# if not self._driver_operation_simulate: +# self._write("calculate2:function concurrent") +# return 0 \ No newline at end of file diff --git a/setup.py b/setup.py index b938d80c..9213d5f7 100644 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ def run_tests(self): 'ivi.dicon', 'ivi.ics', 'ivi.jdsu', - 'ivi.keithley', + 'ivi.keithley', 'ivi.lecroy', 'ivi.rigol', 'ivi.tektronix', From e0bba93d3ad00939bca6b888ab6d4a4f9c577488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20L=C3=A5ngbacka?= Date: Tue, 12 Sep 2017 20:27:22 +0200 Subject: [PATCH 3/7] Add ADC auto-zero functionality for faster sampling rates. --- ivi/keithley/keithley2280S.py | 137 +++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 50 deletions(-) diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py index ad2154ed..2dbbd6f7 100644 --- a/ivi/keithley/keithley2280S.py +++ b/ivi/keithley/keithley2280S.py @@ -65,12 +65,17 @@ def __init__(self, *args, **kwargs): 'current_max': 6.0 } ] + + self._output_auto_zero = True self._memory_size = 2500 self._memory_offset = 0 - + + self._power_line_frequency = 50 + self._number_of_power_line_cycles = 1 + self._output_trigger_delay = list() - + self._identity_description = "Keithley (Tektronix) 2280S series precision measurement DC supply driver" self._identity_identifier = "" self._identity_revision = "" @@ -81,7 +86,11 @@ def __init__(self, *args, **kwargs): self._identity_specification_major_version = 3 self._identity_specification_minor_version = 0 self._identity_supported_instrument_models = ['2280S-32-6', '2280S-60-3'] - + + self._add_property('outputs[].auto_zero', + self._get_output_auto_zero, + self._set_output_auto_zero) + self._add_property('outputs[].trigger_count', self._get_output_trigger_count, self._set_output_trigger_count) @@ -93,6 +102,9 @@ def __init__(self, *args, **kwargs): self._add_method('trigger.initiate', self._trigger_initiate) + self._add_method('trigger.abort', + self._trigger_abort) + self._add_property('outputs[].trigger_source', self._get_output_trigger_source, self._set_output_trigger_source) @@ -102,26 +114,31 @@ def __init__(self, *args, **kwargs): self._init_outputs() -# def _initialize(self, resource = None, id_query = False, reset = False, **keywargs): -# "Opens an I/O session to the instrument." -# -# super(keithley2280S, self)._initialize(resource, id_query, reset, **keywargs) -# -# # interface clear -# if not self._driver_operation_simulate: -# self._clear() -# -# # check ID -# if id_query and not self._driver_operation_simulate: -# id = self.identity.instrument_model -# id_check = self._instrument_id -# id_short = id[:len(id_check)] -# if id_short != id_check: -# raise Exception("Instrument ID mismatch, expecting %s, got %s", id_check, id_short) -# -# # reset -# if reset: -# self.utility_reset() + def _initialize(self, resource=None, id_query=False, reset=False, power_line_frequency=50, **keywargs): + "Opens an I/O session to the instrument." + + super(keithley2280S, self)._initialize(resource, id_query, reset, **keywargs) + + # interface clear + if not self._driver_operation_simulate: + self._clear() + + # check ID + if id_query and not self._driver_operation_simulate: + id = self.identity.instrument_model + id_check = self._instrument_id + id_short = id[:len(id_check)] + if id_short != id_check: + raise Exception("Instrument ID mismatch, expecting %s, got %s", id_check, id_short) + + # reset + if reset: + self.utility_reset() + + if power_line_frequency not in (50,60): + raise Exception("Incorrect power line frequency, expecting 50/60 Hz, got %s", power_line_frequency) + else: + self._power_line_frequency = power_line_frequency def _utility_disable(self): pass @@ -132,30 +149,43 @@ def _utility_lock_object(self): def _utility_unlock_object(self): pass -# def _init_outputs(self): -# try: -# super(keithley2280S, self)._init_outputs() -# except AttributeError: -# pass -# -# self._output_current_limit = list() -# self._output_current_limit_behavior = list() -# self._output_enabled = list() -# self._output_ovp_enabled = list() -# self._output_ovp_limit = list() -# self._output_voltage_level = list() -# self._output_trigger_source = list() -# self._output_trigger_delay = list() -# for i in range(self._output_count): -# self._output_current_limit.append(0) -# self._output_current_limit_behavior.append('regulate') -# self._output_enabled.append(False) -# self._output_ovp_enabled.append(True) -# self._output_ovp_limit.append(0) -# self._output_voltage_level.append(0) -# self._output_trigger_source.append('bus') -# self._output_trigger_delay.append(0) + def _init_outputs(self): + try: + super(keithley2280S, self)._init_outputs() + except AttributeError: + pass + + self._output_current_limit = list() + self._output_current_limit_behavior = list() + self._output_enabled = list() + self._output_ovp_enabled = list() + self._output_ovp_limit = list() + self._output_voltage_level = list() + self._output_trigger_source = list() + self._output_trigger_delay = list() + for i in range(self._output_count): + self._output_current_limit.append(0) + self._output_current_limit_behavior.append('regulate') + self._output_enabled.append(False) + self._output_ovp_enabled.append(True) + self._output_ovp_limit.append(0) + self._output_voltage_level.append(0) + self._output_trigger_source.append('bus') + self._output_trigger_delay.append(0) + + def _get_output_auto_zero(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_auto_zero = (self._ask("system:azero:state?") == '1') + self._set_cache_valid(index=index) + return self._output_auto_zero + def _set_output_auto_zero(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("system:azero:state %s" % ('off', 'on')[value]) + self._set_cache_valid(index=index) + self._output_auto_zero = value def _get_output_current_limit_behavior(self, index): index = ivi.get_index(self._output_name, index) @@ -284,11 +314,13 @@ def _output_measure(self, index, type): return float(self._ask("measure:current?")) return 0 - def _output_configure_measurement(self, index, type, measurement_count=1, NPLC=1, measurement_digits=6): + def _output_configure_measurement(self, index, type, measurement_count=1, NPLC=1, measurement_digits=6, auto_zero=True): index = ivi.get_index(self._output_name, index) if (measurement_count > 2500) or (measurement_count < 0): raise ivi.OutOfRangeException('Maximum buffer length is 2500') - if (NPLC < 0.002) or (NPLC > 12): + if (self._power_line_frequency == 50) and ((NPLC < 0.002) or (NPLC > 12)): + raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 15)): raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 6): raise ivi.OutOfRangeException('Number of measurement digits must be between 4 and 6') @@ -296,8 +328,13 @@ def _output_configure_measurement(self, index, type, measurement_count=1, NPLC=1 raise ivi.ValueNotSupportedException() self._set_output_trigger_sample_count = measurement_count - self._write(":sense:"+type+":NPLC %f" % NPLC) - self._write(":sense:"+type+":digits %d" % measurement_digits) + self._number_of_power_line_cycles = NPLC + self._output_auto_zero = auto_zero + if not self._driver_operation_simulate: + self._write(":sense:"+type+":NPLC %f" % NPLC) + self._write(":sense:"+type+":digits %d" % measurement_digits) + self._write(":system:azero:state %s" % ('off', 'on')[auto_zero]) + self._set_cache_valid() if not self._driver_operation_simulate: # extend buffer memory size if measurement_count exceeds buffer size From 322edfdeb53d361b72f5202f775eb5c863030057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20L=C3=A5ngbacka?= Date: Thu, 14 Sep 2017 16:44:07 +0200 Subject: [PATCH 4/7] Add more measurement functionality. Make all functions support multichannel instruments. --- ivi/keithley/keithley2280S.py | 209 +++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 67 deletions(-) diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py index 2dbbd6f7..16d5e507 100644 --- a/ivi/keithley/keithley2280S.py +++ b/ivi/keithley/keithley2280S.py @@ -33,14 +33,18 @@ 'immediate': 'imm', 'external': 'ext', 'manual': 'man'} -RangeType = set(['current']) +RangeType = set(['voltage','current']) MeasurementType = set(['voltage', 'current', 'concurrent']) -MeasurementFunctionMapping = { - 'minimum': 'min', - 'maximum': 'max', - 'average': 'mean', - 'peak_to_peak': 'pkpk', - 'standard_deviation': 'sdev'} +MeasurementTypeMapping = { + 'voltage': "volt:dc", + 'current': "curr:dc", + 'concurrent': "conc:dc"} +#MeasurementFunctionMapping = { +# 'minimum': 'min', +# 'maximum': 'max', +# 'average': 'mean', +# 'peak_to_peak': 'pkpk', +# 'standard_deviation': 'sdev'} class keithley2280S(scpi.dcpwr.Base, scpi.dcpwr.SoftwareTrigger, dcpwr.Measurement, @@ -66,13 +70,10 @@ def __init__(self, *args, **kwargs): } ] - self._output_auto_zero = True self._memory_size = 2500 self._memory_offset = 0 - self._power_line_frequency = 50 - self._number_of_power_line_cycles = 1 self._output_trigger_delay = list() @@ -91,6 +92,14 @@ def __init__(self, *args, **kwargs): self._get_output_auto_zero, self._set_output_auto_zero) + self._add_property('outputs[].number_of_digits', + self._get_output_number_of_digits, + self._set_output_number_of_digits) + + self._add_property('outputs[].number_of_power_line_cycles', + self._get_output_number_of_power_line_cycles, + self._set_output_number_of_power_line_cycles) + self._add_property('outputs[].trigger_count', self._get_output_trigger_count, self._set_output_trigger_count) @@ -109,12 +118,20 @@ def __init__(self, *args, **kwargs): self._get_output_trigger_source, self._set_output_trigger_source) + self._add_property('outputs[].measurement_type', + self._get_output_measurement_type, + self._set_output_measurement_type) + + self._add_property('outputs[].measurement_range', + self._get_output_measurement_range, + self._set_output_measurement_range) + self._add_method('outputs[].configure_measurement', - self._output_configure_measurement) + self._output_configure_measurement) self._init_outputs() - def _initialize(self, resource=None, id_query=False, reset=False, power_line_frequency=50, **keywargs): + def _initialize(self, resource=None, id_query=False, reset=False, **keywargs): "Opens an I/O session to the instrument." super(keithley2280S, self)._initialize(resource, id_query, reset, **keywargs) @@ -135,10 +152,6 @@ def _initialize(self, resource=None, id_query=False, reset=False, power_line_fre if reset: self.utility_reset() - if power_line_frequency not in (50,60): - raise Exception("Incorrect power line frequency, expecting 50/60 Hz, got %s", power_line_frequency) - else: - self._power_line_frequency = power_line_frequency def _utility_disable(self): pass @@ -163,6 +176,13 @@ def _init_outputs(self): self._output_voltage_level = list() self._output_trigger_source = list() self._output_trigger_delay = list() + self._output_trigger_count = list() + self._output_trigger_sample_count = list() + self._output_number_of_power_line_cycles = list() + self._output_number_of_digits = list() + self._output_measurement_type = list() + self._output_measurement_range = list() + self._output_auto_zero = list() for i in range(self._output_count): self._output_current_limit.append(0) self._output_current_limit_behavior.append('regulate') @@ -172,21 +192,63 @@ def _init_outputs(self): self._output_voltage_level.append(0) self._output_trigger_source.append('bus') self._output_trigger_delay.append(0) + self._output_trigger_count.append(1) + self._output_trigger_sample_count.append(1) + self._output_number_of_power_line_cycles.append(1) + self._output_number_of_digits.append(6) + self._output_measurement_type.append('concurrent') + self._output_measurement_range.append(0.01) + self._output_auto_zero.append(True) + def _get_output_auto_zero(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: - self._output_auto_zero = (self._ask("system:azero:state?") == '1') + self._output_auto_zero = (self._ask("system:azero%d:state?" % (index+1)) == '1') self._set_cache_valid(index=index) return self._output_auto_zero def _set_output_auto_zero(self, index, value): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: - self._write("system:azero:state %s" % ('off', 'on')[value]) + self._write("system:azero%d:state %s" % (index+1,('off', 'on')[value])) self._set_cache_valid(index=index) self._output_auto_zero = value + def _get_output_number_of_power_line_cycles(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + # Need to write initialized measurement type for the operation to be processed + self._output_number_of_power_line_cycles = self._ask(":sense%d:%s:nplcycles?" % (index+1, self._output_measurement_type[index])) + self._set_cache_valid(index=index) + return self._output_number_of_power_line_cycles + + def _set_output_number_of_power_line_cycles(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + # Need to write initialized measurement type for the operation to be processed + self._write(":sense%d:%s:nplcycles %f" % (index+1, self._output_measurement_type[index], float(value))) + self._set_cache_valid(index=index) + self._output_number_of_power_line_cycles = value + + def _get_output_number_of_digits(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + # Need to write initialized measurement type for the operation to be processed + self._output_number_of_digits = self._ask(":sense%d:%s:digits?" % (index+1, self._output_measurement_type[index])) + self._set_cache_valid(index=index) + return self._output_number_of_digits + + def _set_output_number_of_digits(self, index, value): + index = ivi.get_index(self._output_name, index) + if (value < 4) or (value > 6): + raise ivi.OutOfRangeException('Number of measurement digits must be between 4 and 6') + if not self._driver_operation_simulate: + # Need to write initialized measurement type for the operation to be processed + self._write(":sense%d:%s:digits %d" % (index+1, self._output_measurement_type[index], value)) + self._set_cache_valid(index=index) + self._output_number_of_power_line_cycles = value + def _get_output_current_limit_behavior(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): @@ -208,7 +270,7 @@ def _set_output_ovp_enabled(self, index, value): def _get_output_ovp_limit(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): - self._output_ovp_limit[index] = float(self._ask("source:voltage:protection:level?")) + self._output_ovp_limit[index] = float(self._ask("source%d:voltage:protection:level?" % (index+1))) self._set_cache_valid(index=index) return self._output_ovp_limit[index] @@ -218,14 +280,14 @@ def _set_output_ovp_limit(self, index, value): if abs(value) > self._output_spec[index]['ovp_max']: raise ivi.OutOfRangeException() if not self._driver_operation_simulate: - self._write("source:voltage:protection:level %f" % float(value)) + self._write("source%d:voltage:protection:level %f" % (index+1, float(value))) self._output_ovp_limit[index] = value self._set_cache_valid(index=index) def _get_output_ocp_limit(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): - self._output_ocp_limit[index] = float(self._ask("source:current:protection:level?")) + self._output_ocp_limit[index] = float(self._ask("source%d:current:protection:level?" % (index+1))) self._set_cache_valid(index=index) return self._output_ocp_limit[index] @@ -235,17 +297,10 @@ def _set_output_ocp_limit(self, index, value): if abs(value) > self._output_spec[index]['ocp_max']: raise ivi.OutOfRangeException() if not self._driver_operation_simulate: - self._write("source:current:protection:level %f" % float(value)) + self._write("source%d:current:protection:level %f" % (index+1,float(value))) self._output_ocp_limit[index] = value self._set_cache_valid(index=index) - def _output_configure_range(self, index, range_type, range_value): - if range_type not in RangeType: - raise ivi.ValueNotSupportedException() - if not self._driver_operation_simulate: - self._write("sense:current:range %s" % range_value) - return - def _output_reset_output_protection(self): if not self._driver_operation_simulate: self._write("output:protection:clear") @@ -253,7 +308,7 @@ def _output_reset_output_protection(self): def _get_output_trigger_source(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(): - value = self._ask("trigger:source?").lower() + value = self._ask("trigger:sequence%d:source?" % (index+1)).lower() self._output_trigger_source[index] = [k for k,v in TriggerSourceMapping.items() if v==value][0] return self._output_trigger_source[index] @@ -263,43 +318,79 @@ def _set_output_trigger_source(self, index, value): if value not in TriggerSourceMapping: raise ivi.ValueNotSupportedException() if not self._driver_operation_simulate: - self._write("trigger:source %s" % TriggerSourceMapping[value]) - self._output_trigger_source[index] = value - self._set_cache_valid(index=index) + self._write("trigger:sequence%d:source %s" % (index+1, TriggerSourceMapping[value])) + self._output_trigger_source = value + self._set_cache_valid() def _get_output_trigger_count(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: - int(self._ask("trigger:count?")) + self._output_trigger_count[index] = int(self._ask("trigger:sequence%d:count?" % (index+1))) + self._set_cache_valid() + return self._output_trigger_count[index] + def _set_output_trigger_count(self, index, value): index = ivi.get_index(self._output_name, index) if (value < 0) or (value > 2500): raise ivi.OutOfRangeException() if not self._driver_operation_simulate: - self._write("trigger:count %d" % value) + self._write("trigger:sequence%d:count %d" % (index+1, value)) + self._output_trigger_count[index] = value + self._set_cache_valid(index=index) def _get_output_trigger_sample_count(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: - int(self._ask("trigger:sample:count?")) + self._output_trigger_sample_count[index] = int(self._ask("trigger:sequence%d:sample:count?" % (index+1))) + self._set_cache_valid(index=index) + return self._output_trigger_sample_count[index] def _set_output_trigger_sample_count(self, index, value): index = ivi.get_index(self._output_name, index) if (value < 0) or (value > 2500): raise ivi.OutOfRangeException() if not self._driver_operation_simulate: - self._write("trigger:sample:count %d" % value) + self._write("trigger:sequence%d:sample:count %d" % (index+1, value)) + self._output_trigger_sample_count[index] = value + self._set_cache_valid(index=index) def _trigger_abort(self): if not self._driver_operation_simulate: - self._write("abort") + self._write("abort") # TODO: output dependent trigger abort def _trigger_initiate(self): if not self._driver_operation_simulate: - self._write("initiate") + self._write("initiate") #TODO: Output dependent trigger initiate + def _get_output_measurement_type(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + value = self._ask("sense%d:function?" % (index+1)).lower()[1:-1] + self._output_measurement_type[index] = [k for k,v in MeasurementTypeMapping.items() if v==value][0] + self._set_cache_valid(index=index) + return self._output_measurement_type[index] + def _set_output_measurement_type(self, index, type): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("sense%d:function \"%s\"" % (index+1, type)) + self._output_measurement_type[index] = type + self._set_cache_valid(index=index) + + def _get_output_measurement_range(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_measurement_range[index] = float(self._ask("sense%d:%s:range?" % (index+1, self._output_measurement_type[index]))) #TODO: add auto range + self._set_cache_valid(index=index) + return self._output_measurement_range[index] + + def _set_output_measurement_range(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("sense%d:%s:range %s" % (index+1, self._output_measurement_type[index], value)) #TODO: add auto range + self._output_measurement_range[index] = value + self._set_cache_valid(index=index) def _output_measure(self, index, type): index = ivi.get_index(self._output_name, index) @@ -314,40 +405,24 @@ def _output_measure(self, index, type): return float(self._ask("measure:current?")) return 0 - def _output_configure_measurement(self, index, type, measurement_count=1, NPLC=1, measurement_digits=6, auto_zero=True): + def _output_configure_measurement(self, index, type, sample_count=1, NPLC=1, measurement_digits=6, measurement_range=0.01, auto_zero=True): index = ivi.get_index(self._output_name, index) - if (measurement_count > 2500) or (measurement_count < 0): - raise ivi.OutOfRangeException('Maximum buffer length is 2500') - if (self._power_line_frequency == 50) and ((NPLC < 0.002) or (NPLC > 12)): - raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 15)): - raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 6): - raise ivi.OutOfRangeException('Number of measurement digits must be between 4 and 6') if type not in MeasurementType: raise ivi.ValueNotSupportedException() - - self._set_output_trigger_sample_count = measurement_count - self._number_of_power_line_cycles = NPLC - self._output_auto_zero = auto_zero - if not self._driver_operation_simulate: - self._write(":sense:"+type+":NPLC %f" % NPLC) - self._write(":sense:"+type+":digits %d" % measurement_digits) - self._write(":system:azero:state %s" % ('off', 'on')[auto_zero]) - self._set_cache_valid() if not self._driver_operation_simulate: - # extend buffer memory size if measurement_count exceeds buffer size - if int(self._ask(":trace:points?")) < measurement_count: - self._write(":trace:points %d" % measurement_count) - if type == 'voltage': - self._write("sense:function \"voltage\"") - if type == 'current': - self._write("sense:function \"current\"") - elif type == 'concurrent': # Measure both current and voltage at the same time - self._write("sense:function \"concurrent\"") + # extend buffer memory size if sample_count exceeds configured buffer size + if int(self._ask(":trace:points?")) < sample_count: + self._write(":trace:points %d" % sample_count) + + self._set_output_measurement_type(index, type) + self._set_output_measurement_range(index, measurement_range) + self._set_output_number_of_digits(index, measurement_digits) + self._set_output_trigger_sample_count(index, sample_count) + self._set_output_number_of_power_line_cycles(index, NPLC) + self._set_output_auto_zero(index, auto_zero) + - # def _output_measure_statistics(self, index, type): # index = ivi.get_index(self._output_name, index) # if type not in MeasurementType: From 646d01163b2bdb3d51586b24c9aedc474a12ed3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20L=C3=A5ngbacka?= Date: Sun, 24 Sep 2017 19:20:37 +0200 Subject: [PATCH 5/7] Add more measurement functionality --- ivi/keithley/keithley2280S.py | 182 ++++++++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 42 deletions(-) diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py index 16d5e507..a8f1f9ad 100644 --- a/ivi/keithley/keithley2280S.py +++ b/ivi/keithley/keithley2280S.py @@ -34,11 +34,19 @@ 'external': 'ext', 'manual': 'man'} RangeType = set(['voltage','current']) -MeasurementType = set(['voltage', 'current', 'concurrent']) MeasurementTypeMapping = { 'voltage': "volt:dc", 'current': "curr:dc", 'concurrent': "conc:dc"} +FetchBufferDataTypeMapping = { + 'measurement_function': 'reading', + 'voltage': 'source', + 'unit': 'unit', + 'date': 'dateä', + 'time': 'tstamp', + 'relative_time_seconds': 'relative', + 'relative_time': 'rstamp', + 'budder_index': 'rnumber'} #MeasurementFunctionMapping = { # 'minimum': 'min', # 'maximum': 'max', @@ -88,9 +96,9 @@ def __init__(self, *args, **kwargs): self._identity_specification_minor_version = 0 self._identity_supported_instrument_models = ['2280S-32-6', '2280S-60-3'] - self._add_property('outputs[].auto_zero', - self._get_output_auto_zero, - self._set_output_auto_zero) + self._add_property('outputs[].adc_auto_zero', + self._get_output_adc_autozero, + self._set_output_adc_autozero) self._add_property('outputs[].number_of_digits', self._get_output_number_of_digits, @@ -108,6 +116,10 @@ def __init__(self, *args, **kwargs): self._get_output_trigger_sample_count, self._set_output_trigger_sample_count) + self._add_property('outputs[].trigger_continuous', + self._get_output_trigger_continuous, + self._set_output_trigger_continuous) + self._add_method('trigger.initiate', self._trigger_initiate) @@ -129,6 +141,23 @@ def __init__(self, *args, **kwargs): self._add_method('outputs[].configure_measurement', self._output_configure_measurement) + self._add_method('outputs[].fetch_measurement', + self._output_fetch_measurement) + + self._add_method('outputs[].clear_buffer', + self._output_clear_buffer) + + + self._add_property('outputs[].auto_clear_buffer', + self._get_output_auto_clear_buffer, + self._set_output_auto_clear_buffer) + + self._add_method('system.query_power_line_frequeny', + self._system_query_power_line_frequency, + ivi.Doc(""" + Get power line frequency (either 50 Hz or 60 Hz). + """)) + self._init_outputs() def _initialize(self, resource=None, id_query=False, reset=False, **keywargs): @@ -139,6 +168,7 @@ def _initialize(self, resource=None, id_query=False, reset=False, **keywargs): # interface clear if not self._driver_operation_simulate: self._clear() + self._system_query_power_line_frequency() # check ID if id_query and not self._driver_operation_simulate: @@ -147,6 +177,7 @@ def _initialize(self, resource=None, id_query=False, reset=False, **keywargs): id_short = id[:len(id_check)] if id_short != id_check: raise Exception("Instrument ID mismatch, expecting %s, got %s", id_check, id_short) + # reset if reset: @@ -177,12 +208,14 @@ def _init_outputs(self): self._output_trigger_source = list() self._output_trigger_delay = list() self._output_trigger_count = list() + self._output_trigger_continuous = list() self._output_trigger_sample_count = list() self._output_number_of_power_line_cycles = list() self._output_number_of_digits = list() self._output_measurement_type = list() self._output_measurement_range = list() - self._output_auto_zero = list() + self._output_adc_autozero = list() + self._output_auto_clear_buffer = list() for i in range(self._output_count): self._output_current_limit.append(0) self._output_current_limit_behavior.append('regulate') @@ -193,49 +226,57 @@ def _init_outputs(self): self._output_trigger_source.append('bus') self._output_trigger_delay.append(0) self._output_trigger_count.append(1) + self._output_trigger_continuous.append(True) self._output_trigger_sample_count.append(1) self._output_number_of_power_line_cycles.append(1) self._output_number_of_digits.append(6) self._output_measurement_type.append('concurrent') self._output_measurement_range.append(0.01) - self._output_auto_zero.append(True) + self._output_adc_autozero.append(True) + self._output_auto_clear_buffer.append(True) - def _get_output_auto_zero(self, index): + def _get_output_adc_autozero(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: - self._output_auto_zero = (self._ask("system:azero%d:state?" % (index+1)) == '1') + self._output_adc_autozero = (self._ask("system:azero%d:state?" % (index+1)) == '1') self._set_cache_valid(index=index) return self._output_auto_zero - def _set_output_auto_zero(self, index, value): + def _set_output_adc_autozero(self, index, value): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: self._write("system:azero%d:state %s" % (index+1,('off', 'on')[value])) self._set_cache_valid(index=index) - self._output_auto_zero = value + self._output_adc_autozero = value def _get_output_number_of_power_line_cycles(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: # Need to write initialized measurement type for the operation to be processed - self._output_number_of_power_line_cycles = self._ask(":sense%d:%s:nplcycles?" % (index+1, self._output_measurement_type[index])) + self._output_number_of_power_line_cycles[index] = self._ask(":sense%d:%s:nplcycles?" % (index+1, self._output_measurement_type[index])) self._set_cache_valid(index=index) return self._output_number_of_power_line_cycles def _set_output_number_of_power_line_cycles(self, index, value): index = ivi.get_index(self._output_name, index) + if self._system_power_line_frequency == 50: + if value < 0.002 or value > 15: + raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 < NPLC < 15 for 50 Hz power line frequency') + if self._system_power_line_frequency == 60: + if value < 0.002 or value > 12: + raise ivi.OutOfRangeException('Number of power line cycles (NPLC) must be 0.002 < NPLC < 12 for 60 Hz power line frequency') if not self._driver_operation_simulate: # Need to write initialized measurement type for the operation to be processed self._write(":sense%d:%s:nplcycles %f" % (index+1, self._output_measurement_type[index], float(value))) self._set_cache_valid(index=index) - self._output_number_of_power_line_cycles = value + self._output_number_of_power_line_cycles[index] = value def _get_output_number_of_digits(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: # Need to write initialized measurement type for the operation to be processed - self._output_number_of_digits = self._ask(":sense%d:%s:digits?" % (index+1, self._output_measurement_type[index])) + self._output_number_of_digits[index] = self._ask(":sense%d:%s:digits?" % (index+1, self._output_measurement_type[index])) self._set_cache_valid(index=index) return self._output_number_of_digits @@ -247,7 +288,7 @@ def _set_output_number_of_digits(self, index, value): # Need to write initialized measurement type for the operation to be processed self._write(":sense%d:%s:digits %d" % (index+1, self._output_measurement_type[index], value)) self._set_cache_valid(index=index) - self._output_number_of_power_line_cycles = value + self._output_number_of_power_line_cycles[index] = value def _get_output_current_limit_behavior(self, index): index = ivi.get_index(self._output_name, index) @@ -355,6 +396,20 @@ def _set_output_trigger_sample_count(self, index, value): self._output_trigger_sample_count[index] = value self._set_cache_valid(index=index) + def _get_output_trigger_continuous(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_continuous[index] = self._ask("initiate%d:continuous?" % (index+1)) == '1' + self._set_cache_valid() + return self._output_trigger_continuous[index] + + def _set_output_trigger_continuous(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("initiate%d:continuous %s" % (index+1, ('off', 'on')[value])) + self._output_trigger_continuous[index] = value + self._set_cache_valid(index=index) + def _trigger_abort(self): if not self._driver_operation_simulate: self._write("abort") # TODO: output dependent trigger abort @@ -392,6 +447,25 @@ def _set_output_measurement_range(self, index, value): self._output_measurement_range[index] = value self._set_cache_valid(index=index) + def _get_output_auto_clear_buffer(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_auto_clear_buffer[index] = (self._ask("trace%d:clear:auto?" % (index+1)) == '1') + self._set_cache_valid(index=index) + return self._output_auto_clear_buffer[index] + + def _set_output_auto_clear_buffer(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:clear:auto %s" % (index+1, ('off', 'on')[value])) + self._output_auto_clear_buffer[index] = value + self._set_cache_valid(index=index) + + def _output_clear_buffer(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:clear" % (index+1)) + def _output_measure(self, index, type): index = ivi.get_index(self._output_name, index) if type not in set(['voltage', 'current']): @@ -405,37 +479,61 @@ def _output_measure(self, index, type): return float(self._ask("measure:current?")) return 0 - def _output_configure_measurement(self, index, type, sample_count=1, NPLC=1, measurement_digits=6, measurement_range=0.01, auto_zero=True): + def _output_configure_measurement(self, index, type, sample_count=None, trigger_continuous=None, NPLC=None, measurement_digits=None, measurement_range=None, adc_autozero=None, auto_clear_buffer=None): index = ivi.get_index(self._output_name, index) - if type not in MeasurementType: + if type not in MeasurementTypeMapping.keys(): raise ivi.ValueNotSupportedException() + self._set_output_measurement_type(index, type) + + if sample_count is not None: + self._set_output_trigger_sample_count(index, sample_count) + if trigger_continuous is not None: + self._set_output_trigger_continuous(index, trigger_continuous) + if NPLC is not None: + self._set_output_number_of_power_line_cycles(index, NPLC) + if measurement_digits is not None: + self._set_output_number_of_digits(index, measurement_digits) + if measurement_range is not None: + self._set_output_measurement_range(index, measurement_range) + if adc_autozero is not None: + self._set_output_adc_autozero(index, adc_autozero) + if auto_clear_buffer is not None: + self._set_output_auto_clear_buffer(index, auto_clear_buffer) if not self._driver_operation_simulate: # extend buffer memory size if sample_count exceeds configured buffer size - if int(self._ask(":trace:points?")) < sample_count: - self._write(":trace:points %d" % sample_count) + if sample_count is not None: + if int(self._ask(":trace:points?")) < sample_count: + self._write(":trace:points %d" % sample_count) - self._set_output_measurement_type(index, type) - self._set_output_measurement_range(index, measurement_range) - self._set_output_number_of_digits(index, measurement_digits) - self._set_output_trigger_sample_count(index, sample_count) - self._set_output_number_of_power_line_cycles(index, NPLC) - self._set_output_auto_zero(index, auto_zero) - - -# def _output_measure_statistics(self, index, type): -# index = ivi.get_index(self._output_name, index) -# if type not in MeasurementType: -# raise ivi.ValueNotSupportedException() -# if type == 'voltage': -# if not self._driver_operation_simulate: -# self._write("calculate2:function \"voltage\"") -# return [float(v) for v in self._ask("measure:voltage?").split(',')] -# if type == 'current': -# if not self._driver_operation_simulate: -# self._write("calculate2:function \"current\"") -# return float(self._ask("measure:current?").split(',')[0][:-1]) -# elif type == 'concurrent': # Measure both current and voltage at the same time -# if not self._driver_operation_simulate: -# self._write("calculate2:function concurrent") -# return 0 \ No newline at end of file + + def _output_fetch_measurement(self, index, type, sample_count=None): + index = ivi.get_index(self._output_name, index) + # type can be multiple elements so need to check that all are valid and construct a composite SCPI string + if type not in FetchBufferDataTypeMapping.keys(): + raise ivi.ValueNotSupportedException() + + type_string = FetchBufferDataTypeMapping[type] + + if not self._driver_operation_simulate: + if sample_count is None: + sample_count = self._get_output_trigger_sample_count(index) + + buffer_data = [] + # if number of samples is less than or equal to 100, all data can be fetched in one operation + if sample_count < 100: + buffer_data = self._ask("trace%d:data? %s" % (index, type_string)) + else: + # if number of samples is more than 100, fetch in chunks of 100 samples + for i in range(sample_count // 100): + buffer_data.append(self._ask("trace:data:select? %d,%d,%s" % (100*i-99,i*100, type_string))) + j = i + sample_count % 100 + buffer_data.append(self._ask("trace:data:select? %d,%d,%s" % (100*j-99,j*100, type_string))) + + return buffer_data.split(',') + + + def _system_query_power_line_frequency(self): + if not self._driver_operation_simulate: + self._system_power_line_frequency = int(self._ask("system:lfr?")) + return self._system_power_line_frequency \ No newline at end of file From d1d7cc63b51ae75886aa4086c9abca4fc303b007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20L=C3=A5ngbacka?= Date: Mon, 9 Oct 2017 17:52:20 +0200 Subject: [PATCH 6/7] Add more trigger functionality --- ivi/keithley/keithley2280S.py | 265 ++++++++++++++++++++++++++++------ 1 file changed, 217 insertions(+), 48 deletions(-) diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py index a8f1f9ad..01cdd930 100644 --- a/ivi/keithley/keithley2280S.py +++ b/ivi/keithley/keithley2280S.py @@ -38,15 +38,18 @@ 'voltage': "volt:dc", 'current': "curr:dc", 'concurrent': "conc:dc"} -FetchBufferDataTypeMapping = { - 'measurement_function': 'reading', - 'voltage': 'source', - 'unit': 'unit', - 'date': 'dateä', - 'time': 'tstamp', - 'relative_time_seconds': 'relative', - 'relative_time': 'rstamp', - 'budder_index': 'rnumber'} +BufferDataTypeMapping = { + 'current': ['read', float], + 'voltage': ['sour', float], + 'unit': ['unit', str], + 'mode': ['mode', str], + 'date': ['date', str], + 'time': ['time', str], + 'time_stamp': ['tstamp', str], + 'relative_time_seconds': ['rel', float], + 'relative_time': ['rstamp', str], + 'buffer_index': ['rnumber', int]} +TriggerDirection = set(['rise', 'fall']) #MeasurementFunctionMapping = { # 'minimum': 'min', # 'maximum': 'max', @@ -65,7 +68,7 @@ def __init__(self, *args, **kwargs): super(keithley2280S, self).__init__(*args, **kwargs) self._output_count = 1 - + self._output_spec = [ { 'range': { @@ -78,11 +81,9 @@ def __init__(self, *args, **kwargs): } ] - self._memory_size = 2500 self._memory_offset = 0 - self._output_trigger_delay = list() self._identity_description = "Keithley (Tektronix) 2280S series precision measurement DC supply driver" @@ -108,10 +109,38 @@ def __init__(self, *args, **kwargs): self._get_output_number_of_power_line_cycles, self._set_output_number_of_power_line_cycles) + self._add_property('outputs[].trace_points', + self._get_output_trace_points, + self._set_output_trace_points) + self._add_property('outputs[].trigger_count', self._get_output_trigger_count, self._set_output_trigger_count) + self._add_property('outputs[].trigger_current_level', + self._get_output_trigger_current_level, + self._set_output_trigger_current_level) + + self._add_property('outputs[].trigger_voltage_level', + self._get_output_trigger_voltage_level, + self._set_output_trigger_voltage_level) + + self._add_property('outputs[].trigger_current_direction', + self._get_output_trigger_current_direction, + self._set_output_trigger_current_direction) + + self._add_property('outputs[].trigger_voltage_direction', + self._get_output_trigger_voltage_direction, + self._set_output_trigger_voltage_direction) + + self._add_property('outputs[].trigger_current_state', + self._get_output_trigger_current_state, + self._set_output_trigger_current_state) + + self._add_property('outputs[].trigger_voltage_state', + self._get_output_trigger_voltage_state, + self._set_output_trigger_voltage_state) + self._add_property('outputs[].trigger_sample_count', self._get_output_trigger_sample_count, self._set_output_trigger_sample_count) @@ -139,15 +168,23 @@ def __init__(self, *args, **kwargs): self._set_output_measurement_range) self._add_method('outputs[].configure_measurement', - self._output_configure_measurement) + self._output_configure_measurement, + ivi.Doc(""" + Configure measurement parameters such as measurement type (e.g. source voltage, + current, time stamp, ...) number of samples, resolution, sampling interval in + terms of number of power line cycles (NPLC), measurement range, trigger options. + """)) self._add_method('outputs[].fetch_measurement', - self._output_fetch_measurement) + self._output_fetch_measurement, + ivi.Doc(""" + Fetch measurement data from buffer memory. Either fetch all measurement data or + a specific interval. + """)) self._add_method('outputs[].clear_buffer', self._output_clear_buffer) - self._add_property('outputs[].auto_clear_buffer', self._get_output_auto_clear_buffer, self._set_output_auto_clear_buffer) @@ -177,12 +214,10 @@ def _initialize(self, resource=None, id_query=False, reset=False, **keywargs): id_short = id[:len(id_check)] if id_short != id_check: raise Exception("Instrument ID mismatch, expecting %s, got %s", id_check, id_short) - # reset if reset: self.utility_reset() - def _utility_disable(self): pass @@ -205,10 +240,17 @@ def _init_outputs(self): self._output_ovp_enabled = list() self._output_ovp_limit = list() self._output_voltage_level = list() + self._output_trace_points = list() self._output_trigger_source = list() self._output_trigger_delay = list() self._output_trigger_count = list() self._output_trigger_continuous = list() + self._output_trigger_current_level = list() + self._output_trigger_current_direction = list() + self._output_trigger_current_state = list() + self._output_trigger_voltage_level = list() + self._output_trigger_voltage_direction = list() + self._output_trigger_voltage_state = list() self._output_trigger_sample_count = list() self._output_number_of_power_line_cycles = list() self._output_number_of_digits = list() @@ -222,11 +264,18 @@ def _init_outputs(self): self._output_enabled.append(False) self._output_ovp_enabled.append(True) self._output_ovp_limit.append(0) + self._output_trace_points.append(100) self._output_voltage_level.append(0) self._output_trigger_source.append('bus') self._output_trigger_delay.append(0) self._output_trigger_count.append(1) self._output_trigger_continuous.append(True) + self._output_trigger_current_level.append(0.0) + self._output_trigger_current_direction.append('rise') + self._output_trigger_current_state.append(False) + self._output_trigger_voltage_level.append(0.0) + self._output_trigger_voltage_direction.append('rise') + self._output_trigger_voltage_state.append(False) self._output_trigger_sample_count.append(1) self._output_number_of_power_line_cycles.append(1) self._output_number_of_digits.append(6) @@ -235,7 +284,6 @@ def _init_outputs(self): self._output_adc_autozero.append(True) self._output_auto_clear_buffer.append(True) - def _get_output_adc_autozero(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: @@ -303,7 +351,7 @@ def _set_output_current_limit_behavior(self, index, value): def _get_output_ovp_enabled(self, index): # Alwayas enabled by default return True - + def _set_output_ovp_enabled(self, index, value): # Alwayas enabled by default raise ivi.ValueNotSupportedException() @@ -324,7 +372,7 @@ def _set_output_ovp_limit(self, index, value): self._write("source%d:voltage:protection:level %f" % (index+1, float(value))) self._output_ovp_limit[index] = value self._set_cache_valid(index=index) - + def _get_output_ocp_limit(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(index=index): @@ -346,13 +394,115 @@ def _output_reset_output_protection(self): if not self._driver_operation_simulate: self._write("output:protection:clear") + def _get_output_trace_points(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trace_points[index] = int(self._ask("trace%d:points?" % (index+1))) + self._get_cache_valid() + return self._output_trace_points[index] + + def _set_output_trace_points(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:points %d" % (index+1, int(value))) + self._output_trace_points[index] = int(value) + self._set_cache_valid() + + def _get_output_trigger_current_direction(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_current_direction[index] = self._ask("trace%d:trigger:current:direction?" % (index+1)) + self._get_cache_valid() + return self._output_trigger_current_direction[index] + + def _set_output_trigger_current_direction(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in TriggerDirection: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write("trace%d:trigger:current:direction %s" % (index+1, value)) + self._output_trigger_current_direction[index] = value.lower() + self._set_cache_valid() + + def _get_output_trigger_current_level(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_current_state[index] = float(self._ask("trace%d:trigger:current:level?" % (index+1))) + self._get_cache_valid() + return self._output_trigger_current_level[index] + + def _set_output_trigger_current_level(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:trigger:current:level %f" % (index+1, float(value))) + self._output_trigger_current_level[index] = float(value) + self._set_cache_valid() + + def _get_output_trigger_current_state(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_current_state[index] = bool(int(self._ask("trace%d:trigger:current:state?" % (index+1)))) + self._get_cache_valid() + return self._output_trigger_current_state[index] + + def _set_output_trigger_current_state(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:trigger:current:state %d" % (index+1, (0, 1)[value])) + self._output_trigger_current_state[index] = value + self._set_cache_valid() + + def _get_output_trigger_voltage_direction(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_voltage_direction[index] = self._ask("trace%d:trigger:voltage:direction?" % (index+1)) + self._get_cache_valid() + return self._output_trigger_voltage_direction[index] + + def _set_output_trigger_voltage_direction(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in TriggerDirection: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write("trace%d:trigger:voltage:direction %s" % (index+1, value)) + self._output_trigger_voltage_level[index] = value.lower() + self._set_cache_valid() + + def _get_output_trigger_voltage_state(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_voltage_state[index] = bool(int(self._ask("trace%d:trigger:voltage:state?" % (index+1)))) + self._get_cache_valid() + return self._output_trigger_voltage_state[index] + + def _set_output_trigger_voltage_state(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:trigger:voltage:state %d" % (index+1, (0, 1)[value])) + self._output_trigger_voltage_state[index] = value + self._set_cache_valid() + + def _get_output_trigger_voltage_level(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._output_trigger_voltage_state[index] = float(self._ask("trace%d:trigger:voltage:level?" % (index+1))) + self._get_cache_valid() + return self._output_trigger_voltage_level[index] + + def _set_output_trigger_voltage_level(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + self._write("trace%d:trigger:voltage:level %f" % (index+1, float(value))) + self._output_trigger_voltage_level[index] = float(value) + self._set_cache_valid() + def _get_output_trigger_source(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate and not self._get_cache_valid(): value = self._ask("trigger:sequence%d:source?" % (index+1)).lower() self._output_trigger_source[index] = [k for k,v in TriggerSourceMapping.items() if v==value][0] return self._output_trigger_source[index] - + def _set_output_trigger_source(self, index, value): index = ivi.get_index(self._output_name, index) value = str(value) @@ -369,7 +519,6 @@ def _get_output_trigger_count(self, index): self._output_trigger_count[index] = int(self._ask("trigger:sequence%d:count?" % (index+1))) self._set_cache_valid() return self._output_trigger_count[index] - def _set_output_trigger_count(self, index, value): index = ivi.get_index(self._output_name, index) @@ -413,7 +562,7 @@ def _set_output_trigger_continuous(self, index, value): def _trigger_abort(self): if not self._driver_operation_simulate: self._write("abort") # TODO: output dependent trigger abort - + def _trigger_initiate(self): if not self._driver_operation_simulate: self._write("initiate") #TODO: Output dependent trigger initiate @@ -460,7 +609,7 @@ def _set_output_auto_clear_buffer(self, index, value): self._write("trace%d:clear:auto %s" % (index+1, ('off', 'on')[value])) self._output_auto_clear_buffer[index] = value self._set_cache_valid(index=index) - + def _output_clear_buffer(self, index): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: @@ -471,7 +620,7 @@ def _output_measure(self, index, type): if type not in set(['voltage', 'current']): raise ivi.ValueNotSupportedException() if not self._driver_operation_simulate: - self._write("FORM:ELEM \"READ\"") + self._write("form:elem \"read\"") if type == 'voltage': if not self._driver_operation_simulate: return float(self._ask("measure:voltage?")) @@ -479,7 +628,8 @@ def _output_measure(self, index, type): return float(self._ask("measure:current?")) return 0 - def _output_configure_measurement(self, index, type, sample_count=None, trigger_continuous=None, NPLC=None, measurement_digits=None, measurement_range=None, adc_autozero=None, auto_clear_buffer=None): + def _output_configure_measurement(self, index, type, sample_count=None, trigger_continuous=None, NPLC=None, + measurement_digits=None, measurement_range=None, adc_autozero=None, auto_clear_buffer=None): index = ivi.get_index(self._output_name, index) if type not in MeasurementTypeMapping.keys(): raise ivi.ValueNotSupportedException() @@ -504,34 +654,53 @@ def _output_configure_measurement(self, index, type, sample_count=None, trigger_ # extend buffer memory size if sample_count exceeds configured buffer size if sample_count is not None: if int(self._ask(":trace:points?")) < sample_count: - self._write(":trace:points %d" % sample_count) - + self._set_output_trace_points(index, sample_count) - def _output_fetch_measurement(self, index, type, sample_count=None): + def _output_fetch_measurement(self, index, measurement_type, buffer_range=None): index = ivi.get_index(self._output_name, index) - # type can be multiple elements so need to check that all are valid and construct a composite SCPI string - if type not in FetchBufferDataTypeMapping.keys(): + # type can be multiple elements so need to check that all are valid + if type(measurement_type) in (tuple, list): + for t in measurement_type: + if t not in BufferDataTypeMapping: + raise ivi.ValueNotSupportedException() + elif type(measurement_type) is str: + if measurement_type not in BufferDataTypeMapping: raise ivi.ValueNotSupportedException() - - type_string = FetchBufferDataTypeMapping[type] - - if not self._driver_operation_simulate: - if sample_count is None: - sample_count = self._get_output_trigger_sample_count(index) - - buffer_data = [] - # if number of samples is less than or equal to 100, all data can be fetched in one operation - if sample_count < 100: - buffer_data = self._ask("trace%d:data? %s" % (index, type_string)) + # make measurement_type a list so that we do not loop through the characters + measurement_type = [measurement_type] + else: + raise ivi.InvalidOptionValueException() + + # Check that buffer_range is a valid tuple or list + if buffer_range is not None: + if type(buffer_range) not in (tuple, list): + raise ivi.ValueNotSupportedException("buffer_range must be tuple or list of length 2") + if buffer_range[0] > self._memory_size[index]: + raise ivi.ValueNotSupportedException("buffer_range buffer size is %d" % self._memory_size) + + buffer_data = [] + for m in measurement_type: + buffer_data_temp = [] + measurement_scpi_string = BufferDataTypeMapping[m][0] + parse_buffer_data = BufferDataTypeMapping[m][1] + + # If no buffer_range was supplied, return all measurement data + if buffer_range is None: + buffer_data_temp = self._ask("trace:data? \"%s\"" % measurement_scpi_string).split(',') + + # return only requested buffer_range else: - # if number of samples is more than 100, fetch in chunks of 100 samples - for i in range(sample_count // 100): - buffer_data.append(self._ask("trace:data:select? %d,%d,%s" % (100*i-99,i*100, type_string))) - j = i + sample_count % 100 - buffer_data.append(self._ask("trace:data:select? %d,%d,%s" % (100*j-99,j*100, type_string))) + buffer_data_temp = self._ask("trace:data:sel? %d, %d, \"%s\"" % + (buffer_range[0], buffer_range[1], measurement_scpi_string)).split(',') + buffer_data_temp = [parse_buffer_data(data) for data in buffer_data_temp] - return buffer_data.split(',') + # If multiple data types where requested, return a list of measurement sequences + if len(measurement_type) > 1: + buffer_data.append(buffer_data_temp) + else: + return buffer_data_temp + return buffer_data def _system_query_power_line_frequency(self): if not self._driver_operation_simulate: From 5f165f858e59068432e79f7f03683d2bc83e325b Mon Sep 17 00:00:00 2001 From: JonasLang Date: Wed, 28 Mar 2018 16:44:09 +0200 Subject: [PATCH 7/7] Change _output_fetch_measurement() so that all data is fetched in a single instrument call --- ivi/keithley/keithley2280S.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/ivi/keithley/keithley2280S.py b/ivi/keithley/keithley2280S.py index 01cdd930..aaf0849b 100644 --- a/ivi/keithley/keithley2280S.py +++ b/ivi/keithley/keithley2280S.py @@ -404,6 +404,8 @@ def _get_output_trace_points(self, index): def _set_output_trace_points(self, index, value): index = ivi.get_index(self._output_name, index) if not self._driver_operation_simulate: + if value > 2500: + raise ivi.OutOfRangeException() self._write("trace%d:points %d" % (index+1, int(value))) self._output_trace_points[index] = int(value) self._set_cache_valid() @@ -675,31 +677,30 @@ def _output_fetch_measurement(self, index, measurement_type, buffer_range=None): if buffer_range is not None: if type(buffer_range) not in (tuple, list): raise ivi.ValueNotSupportedException("buffer_range must be tuple or list of length 2") - if buffer_range[0] > self._memory_size[index]: + if buffer_range[1] > self._memory_size: raise ivi.ValueNotSupportedException("buffer_range buffer size is %d" % self._memory_size) - buffer_data = [] + scpi_string = '' for m in measurement_type: - buffer_data_temp = [] - measurement_scpi_string = BufferDataTypeMapping[m][0] - parse_buffer_data = BufferDataTypeMapping[m][1] - - # If no buffer_range was supplied, return all measurement data - if buffer_range is None: - buffer_data_temp = self._ask("trace:data? \"%s\"" % measurement_scpi_string).split(',') + scpi_string += BufferDataTypeMapping[m][0] + ',' + if buffer_range is None: + scpi_string = "trace:data? \"%s\"" % scpi_string[:-1] # Add scpi command and remove last comma + else: + scpi_string = "trace:data:sel? %d, %d, \"%s\"" % (buffer_range[0], buffer_range[1], scpi_string[-1]) - # return only requested buffer_range - else: - buffer_data_temp = self._ask("trace:data:sel? %d, %d, \"%s\"" % - (buffer_range[0], buffer_range[1], measurement_scpi_string)).split(',') - buffer_data_temp = [parse_buffer_data(data) for data in buffer_data_temp] + buffer_data = [] + # Fetch data + buffer_raw_data = self._ask(scpi_string).split(',') + i = 0 + for m in measurement_type: + buffer_data_i = [BufferDataTypeMapping[m][1](value) for value in buffer_raw_data[i::len(measurement_type)]] + i += 1 # If multiple data types where requested, return a list of measurement sequences if len(measurement_type) > 1: - buffer_data.append(buffer_data_temp) + buffer_data.append(buffer_data_i) else: - return buffer_data_temp - + return buffer_data_i return buffer_data def _system_query_power_line_frequency(self):