diff --git a/ivi/agilent/__init__.py b/ivi/agilent/__init__.py index 83039b19..44a9d769 100644 --- a/ivi/agilent/__init__.py +++ b/ivi/agilent/__init__.py @@ -121,6 +121,9 @@ from .agilentMSO7052B import agilentMSO7052B from .agilentMSO7054B import agilentMSO7054B from .agilentMSO7104B import agilentMSO7104B +# Infiniium 9000A +from .agilentMSO9104A import agilentMSO9104A +from .agilentMSO9064A import agilentMSO9064A # Infiniium 90000A from .agilentDSO90254A import agilentDSO90254A from .agilentDSO90404A import agilentDSO90404A @@ -153,7 +156,9 @@ from .agilentMSOX92504A import agilentMSOX92504A from .agilentMSOX92804A import agilentMSOX92804A from .agilentMSOX93204A import agilentMSOX93204A - +# # Infinium S series +# from .agilentDSOS204A import agilentDSOS204A +# from .agilentDSOS804A import agilentDSOS804A # Spectrum Analyzers # 859xA series from .agilent8590A import agilent8590A diff --git a/ivi/agilent/agilent9000.py b/ivi/agilent/agilent9000.py new file mode 100644 index 00000000..a455d951 --- /dev/null +++ b/ivi/agilent/agilent9000.py @@ -0,0 +1,353 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2016 Alex Forencich + +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 .agilentBaseInfiniium import * + +AcquisitionModeMapping = { + 'etim': ('normal', 'equivalent_time'), + 'rtim': ('normal', 'real_time'), + 'pdet': ('peak_detect', 'real_time'), + 'hres': ('high_resolution', 'real_time'), + 'segm': ('normal', 'segmented'), + 'segp': ('peak_detect', 'segmented'), + 'segh': ('high_resolution', 'segmented') +} +AcquisitionType = set(['normal', 'peak_detect', 'high_resolution']) +VerticalCoupling = set(['dc']) +ScreenshotImageFormatMapping = { + 'tif': 'tif', + 'tiff': 'tif', + 'bmp': 'bmp', + 'bmp24': 'bmp', + 'png': 'png', + 'png24': 'png', + 'jpg': 'jpg', + 'jpeg': 'jpg', + 'gif': 'gif'} +SampleMode = set(['real_time', 'equivalent_time', 'segmented']) + +class agilent9000(agilentBaseInfiniium): + "Agilent Infiniium 9000A series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + super(agilent9000, self).__init__(*args, **kwargs) + + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 4e9 + + self._horizontal_divisions = 10 + self._vertical_divisions = 8 + + self._display_color_grade = False + + self._identity_description = "Agilent Infiniium 9000A series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO9104A', 'MSO9064A'] + + self._add_property('channels[].common_mode', + self._get_channel_common_mode, + self._set_channel_common_mode, + None, + ivi.Doc(""" + Turns on/off common mode for the channel. Channels 2 and 4 may form a + common mode channel and channels 1 and 3 may form a common mode channel. + """)) + self._add_property('channels[].differential', + self._get_channel_differential, + self._set_channel_differential, + None, + ivi.Doc(""" + Turns on/off differential mode for the channel. Channels 2 and 4 may form + a differential channel and channels 1 and 3 may form a differential + channel. + """)) + self._add_property('channels[].differential_skew', + self._get_channel_differential_skew, + self._set_channel_differential_skew, + None, + ivi.Doc(""" + Specifies the skew that is applied to the differential or common mode pair + of channels. Units are seconds. + """)) + self._add_property('channels[].display_auto', + self._get_channel_display_auto, + self._set_channel_display_auto, + None, + ivi.Doc(""" + Sets the differential and common mode display scale and offset to track + the acquisition scale and offset. + """)) + self._add_property('channels[].display_offset', + self._get_channel_display_offset, + self._set_channel_display_offset, + None, + ivi.Doc(""" + Sets the displayed offset of the selected channel. Setting this parameter + disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_range', + self._get_channel_display_range, + self._set_channel_display_range, + None, + ivi.Doc(""" + Sets the full scale vertical range of the selected channel. Setting this + parameter disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_scale', + self._get_channel_display_scale, + self._set_channel_display_scale, + None, + ivi.Doc(""" + Sets the displayed scale of the selected channel per division. Setting + this parameter disables display_auto. Units are volts. + """)) + + self._init_channels() + + + def _utility_error_query(self): + error_code = 0 + error_message = "No error" + if not self._driver_operation_simulate: + error_code = self._ask(":system:error?") + error_code = int(error_code) + if error_code != 0: + error_message = "Unknown" + return (error_code, error_message) + + def _init_channels(self): + try: + super(agilent9000, self)._init_channels() + except AttributeError: + pass + + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + for i in range(self._analog_channel_count): + self._channel_common_mode.append(False) + self._channel_differential.append(False) + self._channel_differential_skew.append(0) + self._channel_display_auto.append(True) + self._channel_display_offset.append(0.0) + self._channel_display_range.append(1.0) + self._channel_display_scale.append(0.1) + + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in ScreenshotImageFormatMapping: + raise ivi.ValueNotSupportedException() + + format = ScreenshotImageFormatMapping[format] + + self._write(":display:data? %s, screen, on, %s" % (format, 'invert' if invert else 'normal')) + + return self._read_ieee_block() + + def _get_channel_common_mode(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_common_mode[index] = bool(int(self._ask(":%s:commonmode?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_common_mode[index] + + def _set_channel_common_mode(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:commonmode %d" % (self._channel_name[index], int(value))) + self._channel_common_mode[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential[index] = bool(int(self._ask(":%s:differential?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_differential[index] + + def _set_channel_differential(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:differential %d" % (self._channel_name[index], int(value))) + self._channel_differential[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential_skew(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential_skew[index] = float(self._ask(":%s:differential:skew?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_differential_skew[index] + + def _set_channel_differential_skew(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:differential:skew %e" % (self._channel_name[index], value)) + self._channel_differential_skew[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_auto(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_auto[index] = bool(int(self._ask(":%s:display:auto?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_display_auto[index] + + def _set_channel_display_auto(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:display:auto %d" % (self._channel_name[index], int(value))) + self._channel_display_auto[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_offset(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_offset[index] = float(self._ask(":%s:display:offset?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_offset[index] + + def _set_channel_display_offset(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:offset %e" % (self._channel_name[index], value)) + self._channel_display_offset[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_range(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_range[index] = float(self._ask(":%s:display:range?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_range[index] + + def _set_channel_display_range(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:range %e" % (self._channel_name[index], value)) + self._channel_display_range[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_scale(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_scale[index] = float(self._ask(":%s:display:scale?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_scale[index] + + def _set_channel_display_scale(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:scale %e" % (self._channel_name[index], value)) + self._channel_display_scale[index] = value + self._set_cache_valid(index=index) + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return list() + + if sys.byteorder == 'little': + self._write(":waveform:byteorder lsbfirst") + else: + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format word") + self._write(":waveform:streaming on") + self._write(":waveform:source %s" % self._channel_name[index]) + + # Read preamble + + pre = self._ask(":waveform:preamble?").split(',') + + format = int(pre[0]) + type = int(pre[1]) + points = int(pre[2]) + count = int(pre[3]) + xincrement = float(pre[4]) + xorigin = float(pre[5]) + xreference = int(float(pre[6])) + yincrement = float(pre[7]) + yorigin = float(pre[8]) + yreference = int(float(pre[9])) + + # if type == 1: + # raise scope.InvalidAcquisitionTypeException() + + if format != 2: + raise UnexpectedResponseException() + + # Read waveform data + raw_data = self._ask_for_ieee_block(":waveform:data?") + + # Split out points and convert to time and voltage pairs + y_data = array.array('h', raw_data[0:points*2]) + + data = [(((i - xreference) * xincrement) + xorigin, float('nan') if y == 31232 else ((y - yreference) * yincrement) + yorigin) for i, y in enumerate(y_data)] + + return data + + def _measurement_read_waveform(self, index, maximum_time): + return self._measurement_fetch_waveform(index) + + def _measurement_initiate(self): + if not self._driver_operation_simulate: + self._write(":acquire:complete 100") + self._write(":digitize") + self._set_cache_valid(False, 'trigger_continuous') + diff --git a/ivi/agilent/agilentBaseInfiniium.py b/ivi/agilent/agilentBaseInfiniium.py index 1dcf6c6f..ba3b503e 100644 --- a/ivi/agilent/agilentBaseInfiniium.py +++ b/ivi/agilent/agilentBaseInfiniium.py @@ -48,10 +48,15 @@ 'jpeg': 'jpg', 'gif': 'gif'} SampleMode = set(['real_time', 'equivalent_time', 'segmented']) +TimebaseReferenceMapping = { + 'left': 'left', + 'center': 'cent', + 'right': 'righ', + 'percent': 'perc'} class agilentBaseInfiniium(agilentBaseScope): "Agilent Infiniium series IVI oscilloscope driver" - + def __init__(self, *args, **kwargs): self.__dict__.setdefault('_instrument_id', '') self._analog_channel_name = list() @@ -66,37 +71,37 @@ def __init__(self, *args, **kwargs): self._channel_display_offset = list() self._channel_display_range = list() self._channel_display_scale = list() - + super(agilentBaseInfiniium, self).__init__(*args, **kwargs) - + self._analog_channel_name = list() self._analog_channel_count = 4 self._digital_channel_name = list() self._digital_channel_count = 16 self._channel_count = self._analog_channel_count + self._digital_channel_count self._bandwidth = 13e9 - + self._horizontal_divisions = 10 self._vertical_divisions = 8 self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping self._display_color_grade = False - + self._identity_description = "Agilent Infiniium series IVI oscilloscope driver" - self._identity_supported_instrument_models = ['DSO90254A','DSO90404A','DSO90604A', - 'DSO90804A','DSO91204A','DSO91304A','DSOX91304A','DSOX91604A','DSOX92004A', - 'DSOX92504A','DSOX92804A','DSOX93204A','DSA90254A','DSA90404A','DSA90604A', - 'DSA90804A','DSA91204A','DSA91304A','DSAX91304A','DSAX91604A','DSAX92004A', - 'DSAX92504A','DSAX92804A','DSAX93204A','MSOX91304A','MSOX91604A','MSOX92004A', - 'MSOX92504A','MSOX92804A','MSOX93204A'] - + self._identity_supported_instrument_models = ['MSO9104A', 'MSO9064A','DSO90254A', + 'DSO90404A','DSO90604A', 'DSO90804A','DSO91204A','DSO91304A','DSOX91304A', + 'DSOX91604A','DSOX92004A', 'DSOX92504A','DSOX92804A','DSOX93204A','DSA90254A', + 'DSA90404A','DSA90604A', 'DSA90804A','DSA91204A','DSA91304A','DSAX91304A', + 'DSAX91604A','DSAX92004A', 'DSAX92504A','DSAX92804A','DSAX93204A','MSOX91304A', + 'MSOX91604A','MSOX92004A', 'MSOX92504A','MSOX92804A','MSOX93204A'] + self._add_property('display.color_grade', self._get_display_color_grade, self._set_display_color_grade, None, ivi.Doc(""" Controls color grade persistance. - + When in the color grade persistance mode, all waveforms are mapped into a database and shown with different colors representing varying number of hits in a pixel. Vector display mode is disabled when color grade is @@ -108,7 +113,7 @@ def __init__(self, *args, **kwargs): Returns the range of hits represented by each color. Fourteen values are returned, representing the minimum and maximum count for each of seven colors. The values are returned in the following order: - + * White minimum value * White maximum value * Yellow minimum value @@ -124,10 +129,18 @@ def __init__(self, *args, **kwargs): * Green minimum value * Green maximum value """)) - + self._add_property('acquisition.averaging_enabled', + self._get_acquisition_averaging_enabled, + self._set_acquisition_averaging_enabled, + None, + ivi.Doc(""" + The acquisition averaging control allows Infiniium to acquire + waveform data from several acquisitions and then average them all together. + When enabled, the channel data is averaged before being displayed. + """)) self._init_channels() - - + + def _utility_error_query(self): error_code = 0 error_message = "No error" @@ -137,68 +150,92 @@ def _utility_error_query(self): if error_code != 0: error_message = "Unknown" return (error_code, error_message) - + def _init_channels(self): try: super(agilentBaseInfiniium, self)._init_channels() except AttributeError: pass - + # currently no additional parameters - - + + def _display_fetch_screenshot(self, format='png', invert=False): if self._driver_operation_simulate: return b'' - + if format not in self._display_screenshot_image_format_mapping: raise ivi.ValueNotSupportedException() - + format = self._display_screenshot_image_format_mapping[format] - + self._write(":display:data? %s, screen, on, %s" % (format, 'invert' if invert else 'normal')) - + return self._read_ieee_block() - + def _get_display_vectors(self): if not self._driver_operation_simulate and not self._get_cache_valid(): self._display_vectors = bool(int(self._ask(":display:connect?"))) self._set_cache_valid() return self._display_vectors - + def _set_display_vectors(self, value): value = bool(value) if not self._driver_operation_simulate: self._write(":display:connect %d" % int(value)) self._display_vectors = value self._set_cache_valid() - + def _get_display_color_grade(self): if not self._driver_operation_simulate and not self._get_cache_valid(): self._display_color_grade = bool(int(self._ask(":display:cgrade?"))) self._set_cache_valid() return self._display_color_grade - + def _set_display_color_grade(self, value): value = bool(value) if not self._driver_operation_simulate: self._write(":display:cgrade %d" % int(value)) self._display_color_grade = value self._set_cache_valid() - + def _fetch_display_color_grade_levels(self): if self._driver_operation_simulate(): return [0]*14 - + lst = self._ask(":display:cgrade:levels?").split(',') return [int(x) for x in lst] - + + def _get_channel_coupling(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_coupling[index] = self._ask(":%s:input?" % self._channel_name[index]).lower() + self._set_cache_valid(index=index) + return self._channel_coupling[index] + + def _set_channel_coupling(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + if value not in VerticalCoupling: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:input %s" % (self._channel_name[index], value)) + self._channel_coupling[index] = value + self._set_cache_valid(index=index) + def _get_channel_input_impedance(self, index): index = ivi.get_index(self._analog_channel_name, index) - # fixed - self._channel_input_impedance[index] = 50 + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:input?" % self._channel_name[index]).lower() + if val == 'dc': + val == '1M' + elif val == 'DC50': + val == '50' + elif val == 'AC': + val == '1M' + self._channel_coupling[index] = val + self._set_cache_valid(index=index) return self._channel_input_impedance[index] - + def _set_channel_input_impedance(self, index, value): value = float(value) index = ivi.get_index(self._analog_channel_name, index) @@ -206,24 +243,113 @@ def _set_channel_input_impedance(self, index, value): raise Exception('Invalid impedance selection') self._channel_input_impedance[index] = value self._set_cache_valid(index=index) - + + def _get_timebase_reference(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":timebase:reference?").lower() + self._timebase_reference = [k for k,v in TimebaseReferenceMapping.items() if v==value][0] + if self._timebase_reference == 'percent': + self._timebase_reference = float(self._ask(":timebase:reference:percent?")) + self._set_cache_valid() + return self._timebase_reference + + def _get_trigger_coupling(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_coupling = self._ask(":trigger:edge:coupling?") + return self._trigger_coupling + + def _set_trigger_coupling(self, value): + if value not in TriggerCouplingMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:coupling %s" % value) + self._trigger_coupling = value + self._set_cache_valid() + + def _get_trigger_source(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:edge:source?").lower() + # TODO process value + self._trigger_source = value + self._set_cache_valid() + return self._trigger_source + + def _set_trigger_source(self, value): + if hasattr(value, 'name'): + value = value.name + value = str(value) + # if value not in self._channel_name: + # raise ivi.UnknownPhysicalNameException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:source %s" % value) + self._trigger_source = value + self._set_cache_valid() + + def _get_trigger_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:mode?").lower() + self._trigger_type = value + self._set_cache_valid() + return self._trigger_type + + def _set_trigger_type(self, value): + if value not in TriggerTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:mode %s" % TriggerTypeMapping[value]) + if value == 'ac_line': + self._write(":trigger:source line") + if value == 'glitch': + qual = self._ask(":trigger:glitch:qualifier?").lower() + if qual not in GlitchConditionMapping.values(): + self._write(":trigger:glitch:qualifier %s" % GlitchConditionMapping[self._trigger_glitch_condition]) + if value == 'width': + self._write(":trigger:glitch:qualifier range") + self._trigger_type = value + self._set_cache_valid() + + def _get_acquisition_averaging_enabled(self): + if not self._driver_operation_simulate: + value = self._ask(":acquire:average?") + self._acquisition_averaging_enabled = value + return self._acquisition_averaging_enabled + + def _set_acquisition_averaging_enabled(self, value): + if not self._driver_operation_simulate: + self._write(":acquire:average %s" % value) + self._acquisition_averaging_enabled = value + + def _get_acquisition_number_of_averages(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_averages = int(self._ask(":acquire:average:count?")) + self._set_cache_valid() + return self._acquisition_number_of_averages + + def _set_acquisition_number_of_averages(self, value): + if value < 1 or value > 65536: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":acquire:average:count %d" % value) + self._acquisition_number_of_averages = value + self._set_cache_valid() + def _measurement_fetch_waveform(self, index): index = ivi.get_index(self._channel_name, index) - + if self._driver_operation_simulate: return list() - + if sys.byteorder == 'little': self._write(":waveform:byteorder lsbfirst") else: self._write(":waveform:byteorder msbfirst") self._write(":waveform:format word") self._write(":waveform:source %s" % self._channel_name[index]) - + # Read preamble - + pre = self._ask(":waveform:preamble?").split(',') - + format = int(pre[0]) type = int(pre[1]) points = int(pre[2]) @@ -234,26 +360,26 @@ def _measurement_fetch_waveform(self, index): yincrement = float(pre[7]) yorigin = float(pre[8]) yreference = int(float(pre[9])) - + #if type == 1: # raise scope.InvalidAcquisitionTypeException() - + if format != 2: raise UnexpectedResponseException() - + # Read waveform data raw_data = self._ask_for_ieee_block(":waveform:data?") - + # Split out points and convert to time and voltage pairs y_data = array.array('h', raw_data[0:points*2]) - + data = [(((i - xreference) * xincrement) + xorigin, float('nan') if y == 31232 else ((y - yreference) * yincrement) + yorigin) for i, y in enumerate(y_data)] - + return data - + def _measurement_read_waveform(self, index, maximum_time): return self._measurement_fetch_waveform(index) - + def _measurement_initiate(self): if not self._driver_operation_simulate: self._write(":acquire:complete 100") @@ -297,18 +423,28 @@ def _set_acquisition_mode(self, t, value): def _get_acquisition_type(self): self._get_acquisition_mode() return self._acquisition_type - + def _set_acquisition_type(self, value): if value not in AcquisitionType: raise ivi.ValueNotSupportedException() self._set_acquisition_mode('type', value) - + def _get_acquisition_sample_mode(self): self._get_acquisition_mode() return self._acquisition_sample_mode - + def _set_acquisition_sample_mode(self, value): if value not in SampleMode: raise ivi.ValueNotSupportedException() self._set_acquisition_mode('mode', value) + def _get_acquisition_number_of_points_minimum(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_points_minimum = int(self._ask(":ACQuire:POINts:ANALog?")) + return self._acquisition_number_of_points_minimum + + def _set_acquisition_number_of_points_minimum(self, value): + if not self._driver_operation_simulate: + self._write(":ACQuire:POINts:ANALog %s" % value) + self._acquisition_number_of_points_minimum = value + self._set_cache_valid() \ No newline at end of file diff --git a/ivi/agilent/agilentDSOS804A.py b/ivi/agilent/agilentDSOS804A.py new file mode 100644 index 00000000..38984995 --- /dev/null +++ b/ivi/agilent/agilentDSOS804A.py @@ -0,0 +1,44 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2014 Alex Forencich + +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 .agilents import * + +class agilentDSOS804A(agilents): + "KeySight InfiniiVision DSOS804A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DSOS804A') + + super(agilentDSOS804A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 8e9 + + self._init_channels() + + \ No newline at end of file diff --git a/ivi/agilent/agilentMSO9064A.py b/ivi/agilent/agilentMSO9064A.py new file mode 100644 index 00000000..9efc63c5 --- /dev/null +++ b/ivi/agilent/agilentMSO9064A.py @@ -0,0 +1,95 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2016 Alex Forencich + +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 .agilent9000 import * + +class agilentMSO9064A(agilent9000): + "Agilent Infiniium MSO9064A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + cls = 'IviScope' + grp = 'Base' + + super(agilentMSO9064A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 6e8 + + self._init_channels() + self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) + self._add_property('acquisition.analog_sample_rate', + self._get_acquisition_analog_sample_rate, + self._set_acquisition_analog_sample_rate, + None, + ivi.Doc(""" + Returns or sets the effective sample rate of the acquired analog waveform using the + current configuration. The units are samples per second. + """, cls, grp, '4.2.10')) + + def _get_acquisition_analog_sample_rate(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition__analog_sample_rate = self._ask(":acquire:srate:analog?") + self._set_cache_valid() + return self._acquisition__analog_sample_rate + + def _set_acquisition_analog_sample_rate(self, value): + value = float(value) + self._acquisition_analog_sample_rate = value + + def _measurement_fetch_waveform_digital(self, index): + raw_data = [] + + if self._driver_operation_simulate: + return list() + + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format ascii") + self._write(":waveform:source %s" % index) + + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + xinc = float(pre[4]) + xorg = float(pre[5]) + xref = int(float(pre[6])) + +# if format != 0: +# raise UnexpectedResponseException() + + # Read waveform data + raw_data.append(self._ask(':WAVeform:DATA?')) + + # convert string of hex values to list of hex strings + data_list = raw_data[0].split(",") + + # convert to times + data = [((((k-xref)*xinc) + xorg), e) for k,e in enumerate(data_list)] + + return data + + diff --git a/ivi/agilent/agilentMSO9104A.py b/ivi/agilent/agilentMSO9104A.py new file mode 100644 index 00000000..69e609df --- /dev/null +++ b/ivi/agilent/agilentMSO9104A.py @@ -0,0 +1,94 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2016 Alex Forencich + +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 .agilent9000 import * + +class agilentMSO9104A(agilent9000): + "Agilent Infiniium MSO9104A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'MSO9104A') + + super(agilentMSO9104A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + + self._init_channels() + self._add_method('measurement.fetch_waveform_digital', self._measurement_fetch_waveform_digital, ivi.Doc("""description goes here""", cls, grp, '4.3.13')) + self._add_property('acquisition.analog_sample_rate', + self._get_acquisition_analog_sample_rate, + self._set_acquisition_analog_sample_rate, + None, + ivi.Doc(""" + Returns or sets the effective sample rate of the acquired analog waveform using the + current configuration. The units are samples per second. + """, cls, grp, '4.2.10')) + + def _get_acquisition_analog_sample_rate(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition__analog_sample_rate = self._ask(":acquire:srate:analog?") + self._set_cache_valid() + return self._acquisition__analog_sample_rate + + def _set_acquisition_analog_sample_rate(self, value): + value = float(value) + self._acquisition_analog_sample_rate = value + + def _measurement_fetch_waveform_digital(self, index): + raw_data = [] + + if self._driver_operation_simulate: + return list() + + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format ascii") + self._write(":waveform:source %s" % index) + + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + xinc = float(pre[4]) + xorg = float(pre[5]) + xref = int(float(pre[6])) + +# if format != 0: +# raise UnexpectedResponseException() + + # Read waveform data + raw_data.append(self._ask(':WAVeform:DATA?')) + + # convert string of hex values to list of hex strings + data_list = raw_data[0].split(",") + + # convert to times + data = [((((k-xref)*xinc) + xorg), e) for k,e in enumerate(data_list)] + + return data + + diff --git a/ivi/agilent/agilents.py b/ivi/agilent/agilents.py new file mode 100644 index 00000000..619b1bd4 --- /dev/null +++ b/ivi/agilent/agilents.py @@ -0,0 +1,379 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2012-2014 Alex Forencich + +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 .agilentBaseInfiniium import * + +AcquisitionModeMapping = { + 'etim': ('normal', 'equivalent_time'), + 'rtim': ('normal', 'real_time'), + 'pdet': ('peak_detect', 'real_time'), + 'hres': ('high_resolution', 'real_time'), + 'segm': ('normal', 'segmented'), + 'segp': ('peak_detect', 'segmented'), + 'segh': ('high_resolution', 'segmented') +} +AcquisitionType = set(['normal', 'peak_detect', 'high_resolution']) +VerticalCoupling = set(['dc']) +ScreenshotImageFormatMapping = { + 'tif': 'tif', + 'tiff': 'tif', + 'bmp': 'bmp', + 'bmp24': 'bmp', + 'png': 'png', + 'png24': 'png', + 'jpg': 'jpg', + 'jpeg': 'jpg', + 'gif': 'gif'} +SampleMode = set(['real_time', 'equivalent_time', 'segmented']) + +class agilents(agilentBaseInfiniium): + "Agilent Infiniium S series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + super(agilents, self).__init__(*args, **kwargs) + + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 0.5e9 + + self._horizontal_divisions = 10 + self._vertical_divisions = 8 + + self._display_color_grade = False + + self._identity_description = "KeySight Infiniium S series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DSOS054A','DSOS104A','DSOS204A','DSOS254A','DSOS404A','DSOS604A', + 'DSOS804A','MSOS054A','MSOS104A','MSOS204A','MSOS254A','MSOS404A','MSOS604A','MSOS804A'] + + self._add_property('channels[].common_mode', + self._get_channel_common_mode, + self._set_channel_common_mode, + None, + ivi.Doc(""" + Turns on/off common mode for the channel. Channels 2 and 4 may form a + common mode channel and channels 1 and 3 may form a common mode channel. + """)) + self._add_property('channels[].differential', + self._get_channel_differential, + self._set_channel_differential, + None, + ivi.Doc(""" + Turns on/off differential mode for the channel. Channels 2 and 4 may form + a differential channel and channels 1 and 3 may form a differential + channel. + """)) + self._add_property('channels[].differential_skew', + self._get_channel_differential_skew, + self._set_channel_differential_skew, + None, + ivi.Doc(""" + Specifies the skew that is applied to the differential or common mode pair + of channels. Units are seconds. + """)) + self._add_property('channels[].display_auto', + self._get_channel_display_auto, + self._set_channel_display_auto, + None, + ivi.Doc(""" + Sets the differential and common mode display scale and offset to track + the acquisition scale and offset. + """)) + self._add_property('channels[].display_offset', + self._get_channel_display_offset, + self._set_channel_display_offset, + None, + ivi.Doc(""" + Sets the displayed offset of the selected channel. Setting this parameter + disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_range', + self._get_channel_display_range, + self._set_channel_display_range, + None, + ivi.Doc(""" + Sets the full scale vertical range of the selected channel. Setting this + parameter disables display_auto. Units are volts. + """)) + self._add_property('channels[].display_scale', + self._get_channel_display_scale, + self._set_channel_display_scale, + None, + ivi.Doc(""" + Sets the displayed scale of the selected channel per division. Setting + this parameter disables display_auto. Units are volts. + """)) + + self._init_channels() + + + def _utility_error_query(self): + error_code = 0 + error_message = "No error" + if not self._driver_operation_simulate: + error_code = self._ask(":system:error?") + error_code = int(error_code) + if error_code != 0: + error_message = "Unknown" + return (error_code, error_message) + + def _init_channels(self): + try: + super(agilents, self)._init_channels() + except AttributeError: + pass + + self._channel_common_mode = list() + self._channel_differential = list() + self._channel_differential_skew = list() + self._channel_display_auto = list() + self._channel_display_offset = list() + self._channel_display_range = list() + self._channel_display_scale = list() + + for i in range(self._analog_channel_count): + self._channel_common_mode.append(False) + self._channel_differential.append(False) + self._channel_differential_skew.append(0) + self._channel_display_auto.append(True) + self._channel_display_offset.append(0.0) + self._channel_display_range.append(1.0) + self._channel_display_scale.append(0.1) + + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in ScreenshotImageFormatMapping: + raise ivi.ValueNotSupportedException() + + format = ScreenshotImageFormatMapping[format] + + self._write(":display:data? %s, screen, on, %s" % (format, 'invert' if invert else 'normal')) + + return self._read_ieee_block() + + def _get_channel_common_mode(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_common_mode[index] = bool(int(self._ask(":%s:commonmode?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_common_mode[index] + + def _set_channel_common_mode(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:commonmode %d" % (self._channel_name[index], int(value))) + self._channel_common_mode[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential[index] = bool(int(self._ask(":%s:differential?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_differential[index] + + def _set_channel_differential(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:differential %d" % (self._channel_name[index], int(value))) + self._channel_differential[index] = value + self._set_cache_valid(index=index) + + def _get_channel_differential_skew(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_differential_skew[index] = float(self._ask(":%s:differential:skew?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_differential_skew[index] + + def _set_channel_differential_skew(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:differential:skew %e" % (self._channel_name[index], value)) + self._channel_differential_skew[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_auto(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_auto[index] = bool(int(self._ask(":%s:display:auto?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_display_auto[index] + + def _set_channel_display_auto(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:display:auto %d" % (self._channel_name[index], int(value))) + self._channel_display_auto[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_offset(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_offset[index] = float(self._ask(":%s:display:offset?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_offset[index] + + def _set_channel_display_offset(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:offset %e" % (self._channel_name[index], value)) + self._channel_display_offset[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_range(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_range[index] = float(self._ask(":%s:display:range?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_range[index] + + def _set_channel_display_range(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:range %e" % (self._channel_name[index], value)) + self._channel_display_range[index] = value + self._set_cache_valid(index=index) + + def _get_channel_display_scale(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_display_scale[index] = float(self._ask(":%s:display:scale?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_display_scale[index] + + def _set_channel_display_scale(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:display:scale %e" % (self._channel_name[index], value)) + self._channel_display_scale[index] = value + self._set_cache_valid(index=index) + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return list() + + self._write(":waveform:byteorder msbfirst") + self._write(":waveform:format word") + self._write(":waveform:streaming on") + self._write(":waveform:source %s" % self._channel_name[index]) + + # Read preamble + + pre = self._ask(":waveform:preamble?").split(',') + + format = int(pre[0]) + type = int(pre[1]) + points = int(pre[2]) + count = int(pre[3]) + xincrement = float(pre[4]) + xorigin = float(pre[5]) + xreference = int(float(pre[6])) + yincrement = float(pre[7]) + yorigin = float(pre[8]) + yreference = int(float(pre[9])) + + if type == 1: + raise scope.InvalidAcquisitionTypeException() + + if format != 2: + raise UnexpectedResponseException() + + self._write(":waveform:data?") + + # Read waveform data + raw_data = self._read_ieee_block() + + # Split out points and convert to time and voltage pairs + + data = list() + for i in range(points): + x = ((i - xreference) * xincrement) + xorigin + + yval = struct.unpack(">h", raw_data[i*2:i*2+2])[0] + + if yval == 31232: + # hole value + y = float('nan') + else: + y = ((yval - yreference) * yincrement) + yorigin + + data.append((x, y)) + + return data + + def _measurement_read_waveform(self, index, maximum_time): + return self._measurement_fetch_waveform(index) + + def _measurement_initiate(self): + if not self._driver_operation_simulate: + self._write(":acquire:complete 100") + self._write(":digitize") + self._set_cache_valid(False, 'trigger_continuous') + + def _set_working_directory(self,value): + if not self._driver_operation_simulate: + self._write(":DISK:CDIRECTORY %s" % '\"'+value+'\"') + + def _get_pwd(self): + if not self._driver_operation_simulate: + return self._ask(":DISK:PWD?") + + def _save_waveform(self,filename,source,filtype='BIN',header="ON"): + if not self._driver_operation_simulate: + self._write(":DISK:SAVE:WAVEFORM %s" % 'CHANnel'+str(source)+',\"'+filename+'\",'+filtype+','+header) + + def _set_save_waveform_all(self): + if not self._driver_operation_simulate: + self._write(":DISK:SEGMented ALL") diff --git a/ivi/ivi.py b/ivi/ivi.py index e4a8756b..f2fea3b8 100644 --- a/ivi/ivi.py +++ b/ivi/ivi.py @@ -144,36 +144,36 @@ def __init__(self): d.setdefault('_props', dict()) d.setdefault('_docs', dict()) d.setdefault('_locked', False) - + def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None): "Add a managed property" d = object.__getattribute__(self, '__dict__') d['_props'][name] = (fget, fset, fdel) d['_docs'][name] = doc d[name] = None - + def _add_method(self, name, f=None, doc=None): "Add a managed method" d = object.__getattribute__(self, '__dict__') d['_docs'][name] = doc d[name] = f - + def _del_property(self, name): "Remove managed property or method" d = object.__getattribute__(self, '__dict__') del d['_props'][name] del d['_docs'][name] del d[name] - + def _lock(self, lock=True): "Set lock state to prevent creation or deletion of unmanaged members" d = object.__getattribute__(self, '__dict__') d['_locked'] = lock - + def _unlock(self): "Unlock object to allow creation or deletion of unmanaged members, equivalent to _lock(False)" self._lock(False) - + def __getattribute__(self, name): if name == '__dict__': return object.__getattribute__(self, name) @@ -186,7 +186,7 @@ def __getattribute__(self, name): raise AttributeError("unreadable attribute") return f() return object.__getattribute__(self, name) - + def __setattr__(self, name, value): d = object.__getattribute__(self, '__dict__') d.setdefault('_props', dict()) @@ -200,7 +200,7 @@ def __setattr__(self, name, value): if name not in d and self._locked: raise AttributeError("locked") object.__setattr__(self, name, value) - + def __delattr__(self, name): d = object.__getattribute__(self, '__dict__') d.setdefault('_props', dict()) @@ -214,7 +214,7 @@ def __delattr__(self, name): if name not in d and self._locked: raise AttributeError("locked") object.__delattr__(self, name) - + class IndexedPropertyCollection(object): "A building block to create hierarchical trees of methods and properties with an index that is converted to a parameter" @@ -224,7 +224,7 @@ def __init__(self): self._indicies = list() self._indicies_dict = dict() self._objs = list() - + def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None, props = None, docs = None): "Add a managed property" if props is None: @@ -245,7 +245,7 @@ def _add_property(self, name, fget=None, fset=None, fdel=None, doc=None, props = else: props[n] = (fget, fset, fdel) docs[n] = doc - + def _add_method(self, name, f=None, doc=None, props = None, docs = None): "Add a managed method" if props is None: @@ -266,15 +266,15 @@ def _add_method(self, name, f=None, doc=None, props = None, docs = None): else: props[n] = f docs[n] = doc - + def _add_sub_property(self, sub, name, fget=None, fset=None, fdel=None, doc=None): "Add a sub-property (equivalent to _add_property('sub.name', ...))" self._add_property(sub+'.'+name, fget, fset, fdel, doc) - + def _add_sub_method(self, sub, name, f=None, doc=None): "Add a sub-method (equivalent to _add_method('sub.name', ...))" self._add_method(sub+'.'+name, f, doc) - + def _del_property(self, name): "Delete property" l = name.split('.',1) @@ -286,7 +286,7 @@ def _del_property(self, name): else: del self._props[name] del self._docs[name] - + def _build_obj(self, props, docs, i): "Build a tree of PropertyCollection objects with the proper index associations" obj = PropertyCollection() @@ -307,7 +307,7 @@ def _build_obj(self, props, docs, i): obj._add_method(n, partial(itm, i), doc) obj._lock() return obj - + def _set_list(self, l): "Set a list of allowable indicies as an associative array" self._indicies = list(l) @@ -315,7 +315,7 @@ def _set_list(self, l): self._objs = list() for i in range(len(self._indicies)): self._objs.append(self._build_obj(self._props, self._docs, i)) - + def __getitem__(self, key): if type(key) is slice: return self._objs[key] @@ -324,10 +324,10 @@ def __getitem__(self, key): def __iter__(self): return self._objs.__iter__() - + def __len__(self): return len(self._indicies) - + def count(self): return len(self._indicies) @@ -396,7 +396,7 @@ def __init__(self, doc = '', cls = '', grp = '', section = '', name = ''): self.cls = cls self.grp = grp self.section = section - + def render(self): txt = '.. attribute:: ' + self.name + '\n\n' if self.cls != '': @@ -406,7 +406,7 @@ def render(self): txt += '\n'.join(' ' + x for x in self.doc.splitlines()) txt += '\n' return txt - + def __str__(self): return self.doc @@ -496,7 +496,7 @@ def build_ieee_block(data): # ex: #800002000 prefixes 2000 data bytes return str('#8%08d' % len(data)).encode('utf-8') + data - + def decode_ieee_block(data): "Decode IEEE block" # IEEE block binary data is prefixed with #lnnnnnnnn @@ -505,20 +505,20 @@ def decode_ieee_block(data): # ex: #800002000 prefixes 2000 data bytes if len(data) == 0: return b'' - + ind = 0 c = '#'.encode('utf-8') while data[ind:ind+1] != c: ind += 1 - + ind += 1 l = int(data[ind:ind+1]) ind += 1 - + if (l > 0): num = int(data[ind:ind+l].decode('utf-8')) ind += l - + return data[ind:ind+num] else: return data[ind:] @@ -546,10 +546,10 @@ def get_sig(sig): y = np.array(sig[:,1]) else: raise Exception('Unknown argument') - + if len(x) != len(y): raise Exception('Signals must be the same length!') - + return x, y @@ -587,13 +587,13 @@ def trim_doc(docstring): def doc(obj=None, itm=None, docs=None, prefix=None): """Python IVI documentation generator""" st = "" - + # add a dot to prefix when needed if prefix is None or len(prefix) == 0: prefix = '' elif not prefix[-1] == '.': prefix += '.' - + # if something passed in docs, iterate over it if docs is not None: for n in sorted(docs.keys()): @@ -604,44 +604,44 @@ def doc(obj=None, itm=None, docs=None, prefix=None): else: # print leaf (method or property) st += prefix + n + "\n" - + return st - + if itm is not None: # split off first component before the dot l = itm.split('.',1) n = l[0] r = '' - + # remove brackets k = n.find('[') if k > 0: n = n[:k] - + # if there is more left, need to recurse if len(l) > 1: r = l[1] - + # hand off to parent if type(obj) == dict and n in obj: return doc(obj[n], r, prefix=prefix+n) - + elif n in obj.__dict__: return doc(obj.__dict__[n], r, prefix=prefix+n) - + elif hasattr(obj, '_docs') and n in obj._docs: d = obj._docs[n] if type(d) == dict: return doc(d, r, prefix=prefix+n) - + else: - + d = None - + # return documentation if present if type(obj) == dict and n in obj: d = obj[n] - + elif hasattr(obj, '_docs') and n in obj._docs: d = obj._docs[n] @@ -652,31 +652,31 @@ def doc(obj=None, itm=None, docs=None, prefix=None): return d elif type(d) == str: return trim_doc(d) - + return "error" - - + + if hasattr(obj, '__dict__'): # if obj has __dict__, iterate over it for n in sorted(obj.__dict__.keys()): o = obj.__dict__[n] - + # add brackets for indexed property collections extra = '' if type(o) == IndexedPropertyCollection: extra = '[]' - + if n == '_docs': # process documentation dict st += doc(docs=o, prefix=prefix) elif hasattr(o, '_docs'): # process object that contains a documentation dict st += doc(o, prefix=prefix+n) - + # if we got something, return it if len(st) > 0: return st - + return "error" def help(obj=None, itm=None, complete=False, indent=0): @@ -686,7 +686,7 @@ def help(obj=None, itm=None, complete=False, indent=0): l = sorted(filter(None, l)) for m in l: d = doc(obj, m) - + if type(d) == Doc: print(d.render()) if type(d) == str: @@ -704,7 +704,7 @@ def help(obj=None, itm=None, complete=False, indent=0): print(trim_doc(""" Using Python IVI help --------------------- - + Use the help method to get documentation on IVI methods and properties. The IVI help system is a little different from the built-in Python help system. Here are some examples on how to use it correctly: @@ -755,10 +755,10 @@ def help(obj=None, itm=None, complete=False, indent=0): class DriverOperation(IviContainer): "Inherent IVI methods for driver operation" - + def __init__(self, *args, **kwargs): super(DriverOperation, self).__init__(*args, **kwargs) - + self._driver_operation_cache = True self._driver_operation_driver_setup = "" self._driver_operation_interchange_check = False @@ -768,10 +768,10 @@ def __init__(self, *args, **kwargs): self._driver_operation_record_coercions = False self._driver_operation_io_resource_descriptor = "" self._driver_operation_simulate = False - + self._driver_operation_interchange_warnings = list() self._driver_operation_coercion_records = list() - + self._add_property('driver_operation.cache', self._get_driver_operation_cache, self._set_driver_operation_cache, @@ -781,7 +781,7 @@ def __init__(self, *args, **kwargs): specific driver keeps track of the current instrument settings so that it can avoid sending redundant commands to the instrument. If False, the specific driver does not cache the value of attributes. - + The default value is True. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize a specific driver, the user can override this value by specifying a value in @@ -799,7 +799,7 @@ def __init__(self, *args, **kwargs): passes in the OptionString parameter of the Initialize function. Refer to Section 6.14, Initialize, for the restrictions on the format of the driver setup string. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -816,7 +816,7 @@ def __init__(self, *args, **kwargs): Get Next Interchange Warning, Section 6.2, Clear Interchange Warnings, and Section 6.18, Reset Interchange Check, for more information. If False, the specific driver does not perform interchangeability checking. - + If the user opens an instrument session through an IVI class driver and the Interchange Check attribute is enabled, the IVI class driver may perform additional interchangeability checking. The IVI class driver @@ -824,7 +824,7 @@ def __init__(self, *args, **kwargs): The user can retrieve both class driver interchangeability warnings and specific driver interchangeability warnings by calling the Get Next Interchange Warning function on the class driver session. - + If the IVI specific driver does not implement interchangeability checking, the specific driver returns the Value Not Supported error when the user attempts to set the Interchange Check attribute to True. If the specific @@ -833,7 +833,7 @@ def __init__(self, *args, **kwargs): accepts True as a valid value for the Interchange Check attribute even if the class driver does not implement interchangeability checking capabilities of its own. - + The default value is False. If the user opens an instrument session through an IVI class driver or initializes an IVI specific driver with a logical name, the user can override this value in the IVI configuration @@ -851,7 +851,7 @@ def __init__(self, *args, **kwargs): not pass a logical name, then this attribute returns an empty string. Refer to IVI-3.5: Configuration Server Specification for restrictions on the format of IVI logical names. - + The string that this attribute returns contains a maximum of 256 characters including the NULL character. """) @@ -867,7 +867,7 @@ def __init__(self, *args, **kwargs): program, the user can set this attribute to False to disable status checking and maximize performance. The user specifies this value for the entire IVI driver session. - + The default value is False. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize an IVI specific driver, the user can override this value by specifying a value in @@ -883,7 +883,7 @@ def __init__(self, *args, **kwargs): If True, the IVI specific driver validates attribute values and function parameters. If False, the IVI specific driver does not validate attribute values and function parameters. - + If range check is enabled, the specific driver validates the parameter values that users pass to driver functions. Validating attribute values and function parameters is useful for debugging. After validating the @@ -904,17 +904,17 @@ def __init__(self, *args, **kwargs): makes for ViInt32 and ViReal64 attributes. If False, the IVI specific driver does not keep a list of the value coercions it makes for ViInt32 and ViReal64 attributes. - + If the Record Value Coercions attribute is enabled, the specific driver maintains a record of each coercion. The user calls the Get Next Coercion Record function to extract and delete the oldest coercion record from the list. Refer to Section 6.10, Get Next Coercion Record, for more information. - + If the IVI specific driver does not implement coercion recording, the specific driver returns the Value Not Supported error when the user attempts to set the Record Value Coercions attribute to True. - + The default value is False. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize a IVI specific driver, the user can override this value by specifying a value in @@ -932,7 +932,7 @@ def __init__(self, *args, **kwargs): configuration store or by passing a resource descriptor to the Initialize function of the specific driver. Refer to Section 6.14, Initialize, for the restrictions on the contents of the resource descriptor string. - + The string that this attribute returns contains a maximum of 256 characters including the NULL character. """) @@ -944,11 +944,11 @@ def __init__(self, *args, **kwargs): If True, the IVI specific driver simulates instrument driver I/O operations. If False, the IVI specific driver communicates directly with the instrument. - + If simulation is enabled, the specific driver functions do not perform instrument I/O. For output parameters that represent instrument data, the specific driver functions return simulated values. - + The default value is False. When the user opens an instrument session through an IVI class driver or uses a logical name to initialize an IVI specific driver, the user can override this value by specifying a value in @@ -961,11 +961,11 @@ def __init__(self, *args, **kwargs): """ This function clears the list of interchangeability warnings that the IVI specific driver maintains. - + When this function is called on an IVI class driver session, the function clears the list of interchangeability warnings that the class driver and the specific driver maintain. - + Refer to the Interchange Check attribute for more information on interchangeability checking. """) @@ -978,32 +978,32 @@ def __init__(self, *args, **kwargs): associated with the IVI session. It retrieves and clears the oldest instance in which the specific driver coerced a value the user specified to another value. - + The function returns an empty string in the CoercionRecord parameter if no coercion records remain for the session. - + The coercion record string shall contain the following information: - + * The name of the attribute that was coerced. This can be the generic name, the COM property name, or the C defined constant. * If the attribute applies to a repeated capability, the name of the virtual or physical repeated capability identifier. * The value that the user specified for the attribute. * The value to which the attribute was coerced. - + A recommended format for the coercion record string is as follows:: - + " Attribute " + + [" on " + ] + " was coerced from " + + " to " + - + . - + And example coercion record string is as follows:: - + Attribute TKTDS500_ATTR_VERTICAL_RANGE on channel ch1 was coerced from 9.0 to 10.0. - + """) self._add_method('driver_operation.get_next_interchange_warning', self._driver_operation_get_next_interchange_warning, @@ -1014,16 +1014,16 @@ def __init__(self, *args, **kwargs): session. It retrieves and clears the oldest interchangeability warning from the list. Interchangeability warnings indicate that using the application with a different instrument might cause different behavior. - + When this function is called on an IVI class driver session, it may return interchangeability warnings generated by the IVI class driver as well as interchangeability warnings generated by the IVI specific driver. The IVI class driver determines the relative order in which the IVI class driver warnings are returned in relation to the IVI specific driver warnings. - + The function returns an empty string in the InterchangeWarning parameter if no interchangeability warnings remain for the session. - + Refer to the Interchange Check attribute for more information on interchangeability checking. """) @@ -1040,7 +1040,7 @@ class driver determines the relative order in which the IVI class driver specific driver so that specific driver functions that execute prior to calling this function have no effect on whether future calls to the specific driver generate interchangeability warnings. - + When developing a complex test system that consists of multiple test modules, it is generally a good idea to design the test modules so that they can run in any order. To do so requires ensuring that each test @@ -1051,7 +1051,7 @@ class driver determines the relative order in which the IVI class driver different order, the behavior of the instrument and therefore the entire test module is likely to change. This change in behavior is generally instrument specific and represents an interchangeability problem. - + Users can use this function to test for such cases. By calling this function at the beginning of a test module, users can determine whether the test module has dependencies on the operation of previously executed @@ -1061,80 +1061,80 @@ class driver determines the relative order in which the IVI class driver does not completely configure the instrument and that the user is likely to experience different behavior if the user changes the execution order of the test modules or if the user changes instruments. - + Note: This function does not clear interchangeability warnings from the list of interchangeability warnings. To guarantee that the Get Next Interchange Warning function returns interchangeability warnings that occur only after the program calls function, the user must clear the list of interchangeability warnings by calling the Clear Interchange Warnings function. - + Refer to the Interchange Check attribute for more information on interchangeability checking. """) - - + + def _get_driver_operation_cache(self): return self._driver_operation_cache - + def _set_driver_operation_cache(self, value): self._driver_operation_cache = bool(value) - + def _get_driver_operation_driver_setup(self): return self._driver_operation_driver_setup - + def _get_driver_operation_interchange_check(self): return self._driver_operation_interchange_check - + def _set_driver_operation_interchange_check(self, value): self._driver_operation_interchange_check = bool(value) - + def _get_driver_operation_logical_name(self): return self._driver_operation_logical_name - + def _get_driver_operation_query_instrument_status(self): return self._driver_operation_query_instrument_status - + def _set_driver_operation_query_instrument_status(self, value): self._driver_operation_query_instrument_status = bool(value) - + def _get_driver_operation_range_check(self): return self._driver_operation_range_check - + def _set_driver_operation_range_check(self, value): self._driver_operation_range_check = bool(value) - + def _get_driver_operation_record_coercions(self): return self._driver_operation_record_coercions - + def _set_driver_operation_record_coercions(self, value): self._driver_operation_record_coercions = bool(value) - + def _get_driver_operation_io_resource_descriptor(self): return self._driver_operation_io_resource_descriptor - + def _get_driver_operation_simulate(self): return self._driver_operation_simulate - + def _set_driver_operation_simulate(self, value): value = bool(value) if self._driver_operation_simulate and not value: raise SimulationStateException() self._driver_operation_simulate = value - + def _driver_operation_clear_interchange_warnings(self): self._driver_operation_interchange_warnings = list() - + def _driver_operation_get_next_coercion_record(self): if len(self._driver_operation_coercion_records) > 0: return self._driver_operation_coercion_records.pop() return "" - + def _driver_operation_get_next_interchange_warning(self): if len(self._driver_operation_interchange_warnings) > 0: return self._driver_operation_interchange_warnings.pop() return "" - + def _driver_operation_invalidate_all_attributes(self): pass @@ -1147,7 +1147,7 @@ class DriverIdentity(IviContainer): def __init__(self, *args, **kwargs): super(DriverIdentity, self).__init__(*args, **kwargs) - + self._identity_description = "Base IVI Driver" self._identity_identifier = "" self._identity_revision = "" @@ -1159,14 +1159,14 @@ def __init__(self, *args, **kwargs): self._identity_specification_minor_version = 0 self._identity_supported_instrument_models = list() self.__dict__.setdefault('_identity_group_capabilities', list()) - + self._add_property('identity.description', self._get_identity_description, None, None, """ Returns a brief description of the IVI software component. - + The string that this attribute returns has no maximum size. """) self._add_property('identity.identifier', @@ -1186,7 +1186,7 @@ def __init__(self, *args, **kwargs): Returns version information about the IVI software component. Refer to Section 3.1.2.2, Additional Compliance Rules for Revision String Attributes, for additional rules regarding this attribute. - + The string that this attribute returns has no maximum size. """) self._add_property('identity.vendor', @@ -1195,7 +1195,7 @@ def __init__(self, *args, **kwargs): None, """ Returns the name of the vendor that supplies the IVI software component. - + The string that this attribute returns has no maximum size. """) self._add_property('identity.instrument_manufacturer', @@ -1207,7 +1207,7 @@ def __init__(self, *args, **kwargs): driver returns the value it queries from the instrument as the value of this attribute or a string indicating that it cannot query the instrument identity. - + In some cases, it is not possible for the specific driver to query the manufacturer of the instrument. This can occur when the Simulate attribute is set to True or if the instrument is not capable of returning the @@ -1217,7 +1217,7 @@ def __init__(self, *args, **kwargs): attribute. If the instrument is not capable of returning the manufacturer and the Simulate attribute is set to False, the specific driver returns "Cannot query from instrument" as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1229,7 +1229,7 @@ def __init__(self, *args, **kwargs): Returns the model number or name of the physical instrument. The IVI specific driver returns the value it queries from the instrument or a string indicating that it cannot query the instrument identity. - + In some cases, it is not possible for the specific driver to query the model of the instrument. This can occur when the Simulate attribute is set to True or if the instrument is not capable of returning the model. @@ -1239,7 +1239,7 @@ def __init__(self, *args, **kwargs): If the instrument is not capable of returning the model and the Simulate attribute is set to False, the specific driver returns "Cannot query from instrument" as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1253,7 +1253,7 @@ def __init__(self, *args, **kwargs): returns the value it queries from the instrument as the value of this attribute or a string indicating that it cannot query the instrument identity. - + In some cases, it is not possible for the specific driver to query the firmware revision of the instrument. This can occur when the Simulate attribute is set to True or if the instrument is not capable of returning @@ -1264,7 +1264,7 @@ def __init__(self, *args, **kwargs): firmware version and the Simulate attribute is set to False, the specific driver returns "Cannot query from instrument" as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1276,7 +1276,7 @@ def __init__(self, *args, **kwargs): Returns the major version number of the class specification in accordance with which the IVI software component was developed. The value is a positive integer value. - + If the software component is not compliant with a class specification, the software component returns zero as the value of this attribute. """) @@ -1288,7 +1288,7 @@ def __init__(self, *args, **kwargs): Returns the minor version number of the class specification in accordance with which the IVI software component was developed. The value is a positive integer value. - + If the software component is not compliant with a class specification, the software component returns zero as the value of this attribute. """) @@ -1301,11 +1301,11 @@ def __init__(self, *args, **kwargs): the IVI specific driver is compatible. The string has no white space except possibly embedded in the instrument model names. An example of a string that this attribute might return is "TKTDS3012,TKTDS3014,TKTDS3016". - + It is not necessary for the string to include the abbreviation for the manufacturer if it is the same for all models. In the example above, it is valid for the attribute to return the string "TDS3012,TDS3014,TDS3016". - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1319,10 +1319,10 @@ def __init__(self, *args, **kwargs): capability group names that the IVI class specifications define. The string has no white space except for white space that might be embedded in a capability group name. - + If the IVI specific driver does not comply with an IVI class specification, the specific driver returns an empty string as the value of this attribute. - + The string that this attribute returns does not have a predefined maximum length. """) @@ -1333,7 +1333,7 @@ def __init__(self, *args, **kwargs): driver implements. The items in the list are capability group names that the IVI class specifications define. The list is returned as a list of strings. - + If the IVI specific driver does not comply with an IVI class specification, the specific driver returns an array with zero elements. """) @@ -1344,54 +1344,54 @@ def __init__(self, *args, **kwargs): driver is compatible. The list is returned as a list of strings. For example, this attribute might return the strings "TKTDS3012", "TKTDS3014", and "TKTDS3016" . - + It is not necessary for the string to include the abbreviation for the manufacturer if it is the same for all models. In the example above, it is valid for the attribute to return the strings "TDS3012", "TDS3014", and "TDS3016". """) - - + + def _add_group_capability(self, name): self.__dict__.setdefault('_identity_group_capabilities', list()) self._identity_group_capabilities.insert(0, name) - + def _get_identity_description(self): return self._identity_description - + def _get_identity_identifier(self): return self._identity_identifier - + def _get_identity_revision(self): return self._identity_revision - + def _get_identity_vendor(self): return self._identity_vendor - + def _get_identity_instrument_manufacturer(self): return self._identity_instrument_manufacturer - + def _get_identity_instrument_model(self): return self._identity_instrument_model - + def _get_identity_instrument_firmware_revision(self): return self._identity_instrument_firmware_revision - + def _get_identity_specification_major_version(self): return self._identity_specification_major_version - + def _get_identity_specification_minor_version(self): return self._identity_specification_minor_version - + def _get_identity_supported_instrument_models(self): return ",".join(self._identity_supported_instrument_models) - + def _get_identity_group_capabilities(self): return ",".join(self._identity_group_capabilities) - + def _identity_get_group_capabilities(self): return self._identity_group_capabilities - + def _identity_get_supported_instrument_models(self): return self._identity_supported_instrument_models @@ -1401,7 +1401,7 @@ class DriverUtility(IviContainer): def __init__(self, *args, **kwargs): super(DriverUtility, self).__init__(*args, **kwargs) - + self._add_method('utility.disable', self._utility_disable, """ @@ -1413,7 +1413,7 @@ def __init__(self, *args, **kwargs): perform the other operations that the Reset operation performs such as configuring the instrument options on which the IVI specific driver depends. For some instruments, the disable function may do nothing. - + The IVI class specifications define the exact behavior of this function for each instrument class. Refer to the IVI class specifications for more information on the behavior of this function. @@ -1422,16 +1422,16 @@ def __init__(self, *args, **kwargs): self._utility_error_query, """ Queries the instrument and returns instrument specific error information. - + Generally, the user calls this function after another function in the IVI driver returns the Instrument Status error. The IVI specific driver returns the Instrument Status error when the instrument indicates that it encountered an error and its error queue is not empty. Error Query extracts an error out of the instrument's error queue. - + For instruments that have status registers but no error queue, the IVI specific driver emulates an error queue in software. - + The method returns a tuple containing the error code and error message. """) self._add_method('utility.lock_object', @@ -1442,7 +1442,7 @@ def __init__(self, *args, **kwargs): have released their locks or for the length of time specified by the maximum time parameter, whichever come first. The type of lock obtained depends upon the parameters passed to the specific driver constructor. - + The user can use Lock Session with IVI specific drivers to protect a section of code that requires exclusive access to the instrument. This occurs when the user takes multiple actions that affect the instrument @@ -1450,11 +1450,11 @@ def __init__(self, *args, **kwargs): the instrument state until all the actions execute. For example, if the user sets various instrument attributes and then triggers a measurement, the user must ensure no other execution thread modifies the attribute - values until the user finishes taking the measurement. - + values until the user finishes taking the measurement. + It is important to note that this lock is not related to I/O locks such as the VISA resource locking mechanism. - + The user can safely make nested calls to Lock Session within the same thread. To completely unlock the session, the user must balance each call to Lock Session with a call to Unlock Session. Calls to Lock Session must @@ -1465,13 +1465,13 @@ def __init__(self, *args, **kwargs): self._utility_reset, """ This function performs the following actions: - + * Places the instrument in a known state. In an IEEE 488.2 instrument, the Reset function sends the command string ``*RST`` to the instrument. * Configures instrument options on which the IVI specific driver depends. A specific driver might enable or disable headers or enable binary mode for waveform transfers. - + The user can either call the Reset function separately or specify that it be called from the Initialize function. The Initialize function performs additional operations after performing the reset operation to place the @@ -1485,7 +1485,7 @@ def __init__(self, *args, **kwargs): The Reset With Defaults function performs the same operations that the Reset function performs and then performs the following additional operations in the specified order: - + * Disables the class extension capability groups that the IVI specific driver implements. * If the class specification with which the IVI specific driver is @@ -1495,7 +1495,7 @@ def __init__(self, *args, **kwargs): * Configures the initial settings for the specific driver and instrument based on the information retrieved from the IVI configuration store when the instrument driver session was initialized. - + Notice that the Initialize function also performs these functions. To place the instrument and the IVI specific driver in the exact same state that they attain when the user calls the Initialize function, the user @@ -1507,44 +1507,44 @@ def __init__(self, *args, **kwargs): Causes the instrument to perform a self test. Self Test waits for the instrument to complete the test. It then queries the instrument for the results of the self test and returns the results to the user. - + If the instrument passes the self test, this function returns the tuple:: - + (0, 'Self test passed') - + Otherwise, the function returns a tuple of the result code and message. """) self._add_method('utility.unlock_object', self._utility_unlock_object, """ This function releases a lock that the Lock Session function acquires. - + Refer to Lock Session for additional information on IVI session locks. """) - - + + def _utility_disable(self): pass - + def _utility_error_query(self): error_code = 0 error_message = "No error" return (error_code, error_message) - + def _utility_lock_object(self): pass - + def _utility_reset(self): pass - + def _utility_reset_with_defaults(self): self.utility_reset() - + def _utility_self_test(self): code = 0 message = "Self test passed" return (code, message) - + def _utility_unlock_object(self): pass @@ -1559,25 +1559,25 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw 'interchange_check', 'driver_setup', 'prefer_pyvisa'): if k in kwargs: kw[k] = kwargs.pop(k) - + self._interface = None self._initialized = False self.__dict__.setdefault('_instrument_id', '') self._cache_valid = dict() - + super(Driver, self).__init__(*args, **kwargs) - + self._add_method('initialize', self._initialize, """ The user must call the Initialize function prior to calling other IVI driver functions that access the instrument. The Initialize function is called automatically by the constructor if a resource string is passed as - the first argument to the constructor. - + the first argument to the constructor. + If simulation is disabled when the user calls the Initialize function, the function performs the following actions: - + * Opens and configures an I/O session to the instrument. * If the user passes True for the IdQuery parameter, the function queries the instrument for its ID and verifies that the IVI specific driver @@ -1588,7 +1588,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw instrument in a known state. In an IEEE 488.2 instrument, the function sends the command string "*RST" to the instrument. If the instrument cannot perform a reset, the IVI specific driver returns the Reset Not - Supported warning. + Supported warning. * Configures instrument options on which the IVI specific driver depends. For example, a specific driver might enable or disable headers or enable binary mode for waveform transfers. @@ -1600,12 +1600,12 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw the attributes to the values that the class specification defines. 3. If the ResourceName parameter is a logical name, the IVI specific driver configures the initial settings for the specific driver and - instrument based on the configuration of the logical name in the IVI + instrument based on the configuration of the logical name in the IVI configuration store. - + If simulation is enabled when the user calls the Initialize function, the function performs the following actions: - + * If the user passes True for the IdQuery parameter and the instrument cannot return its ID, the IVI specific driver returns the ID Query Not Supported warning. @@ -1615,7 +1615,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw * If the ResourceName parameter is a logical name, the IVI specific driver configures the initial settings for the specific driver based on the configuration of the logical name in the IVI configuration store. - + Some instrument driver operations require or take into account information from the IVI configuration store. Examples of such information are virtual repeated capability name mappings and the value of certain inherent @@ -1626,17 +1626,17 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw Section 3.2.3, Instantiating the Right Configuration Store From Software Modules, of IVI-3.5: Configuration Server Specification for details on how to correctly instantiate the configuration store. - + The ResourceName parameter must contain either a logical name that is defined in the IVI configuration store or an instrument specific string that identifies the I/O address of the instrument, such as a VISA resource descriptor string. Refer to IVI-3.5: Configuration Server Specification for restrictions on the format of IVI logical names. Refer to the VXIplug&play specifications for the grammar of VISA resource descriptor - strings. - + strings. + Example resource strings:: - + 'TCPIP::10.0.0.1::INSTR' 'TCPIP0::10.0.0.1::INSTR' 'TCPIP::10.0.0.1::gpib,5::INSTR' @@ -1653,7 +1653,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw 'ASRL::COM1,9600,8n1::INSTR' 'ASRL::/dev/ttyUSB0,9600::INSTR' 'ASRL::/dev/ttyUSB0,9600,8n1::INSTR' - + The user can use additional parameters to specify the initial values of certain IVI inherent attributes for the session. The following table lists the inherent attributes that the user can set through these named @@ -1661,7 +1661,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw attributes. If the user does not specify the initial value of an inherent attribute, the initial value of the attribute depends on the value of the ResourceName parameter: - + * If the ResourceName parameter contains an IVI logical name, the IVI specific driver configures the initial settings based on the configuration of the logical name in the IVI configuration store. @@ -1669,12 +1669,12 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw identifies the I/O address of the instrument, the IVI specific driver sets inherent attributes to their default initial values. The following table shows the default initial value for each attribute. - + The following table lists the IVI inherent attributes that the user can set, their default initial values, and the name that represents each attribute. These options are passed to the initialize function or the - constructor as key-value pairs. - + constructor as key-value pairs. + +-------------------------+----------------------+---------------------+ | Attribute | Default Inital Value | Options String Name | +=========================+======================+=====================+ @@ -1694,7 +1694,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw +-------------------------+----------------------+---------------------+ | Prefer PyVISA | False | prefer_pyvisa | +-------------------------+----------------------+---------------------+ - + Each IVI specific driver defines it own meaning and valid values for the Driver Setup attribute. Many specific drivers ignore the value of the Driver Setup attribute. Other specific drivers use the Driver Setup string @@ -1702,7 +1702,7 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw if a specific driver supports a family of instrument models, the driver can use the Driver Setup attribute to allow the user to specify a particular instrument model to simulate. - + If the user attempts to initialize the instrument a second time without first calling the Close function, the Initialize function returns the Already Initialized error. @@ -1717,13 +1717,13 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw the Initialize function successfully executes, this attribute returns False. After the Initialize function successfully executes and prior to the execution of the Close function, this attribute returns True. After - the Close function executes, this attribute returns False. - + the Close function executes, this attribute returns False. + The Initialized attribute is one of the few IVI specific driver attributes that can be accessed while the specific driver is not in the initialized state. All the attributes of an IVI specific driver that can be accessed while the specific driver is not in the initialized state are listed below. - + * Component Class Spec Major Version * Component Class Spec Minor Version * Component Description @@ -1739,10 +1739,10 @@ def __init__(self, resource = None, id_query = False, reset = False, *args, **kw """ When the user finishes using a Python IVI driver, the user should call either the Close method or __del__. Note that __del__ will call close - automatically. - + automatically. + This function also does the following: - + * Prevents the user from calling other functions in the driver that access the instrument until the user calls the Initialize function again. @@ -1917,32 +1917,33 @@ def _close(self): def _get_initialized(self): "Returnes initialization state of driver" return self._initialized - + def _get_cache_tag(self, tag=None, skip=1): if tag is None: stack = inspect.stack() start = 0 + skip if len(stack) < start + 1: return '' - tag = stack[start][3] - + tag = stack[start][3] + if tag[0:4] == "_get": tag = tag[4:] if tag[0:4] == "_set": tag = tag[4:] if tag[0] == "_": tag = tag[1:] - + return tag def _get_cache_valid(self, tag=None, index=-1, skip_disable=False): - if not skip_disable and not self._driver_operation_cache: - return False - tag = self._get_cache_tag(tag, 2) - if index >= 0: - tag = tag + '_%d' % index - try: - return self._cache_valid[tag] - except KeyError: - self._cache_valid[tag] = False - return False + # if not skip_disable and not self._driver_operation_cache: + # return False + # tag = self._get_cache_tag(tag, 2) + # if index >= 0: + # tag = tag + '_%d' % index + # try: + # return self._cache_valid[tag] + # except KeyError: + # self._cache_valid[tag] = False + # return False + return False #always return false to ensure physical instr is queried def _set_cache_valid(self, valid=True, tag=None, index=-1): tag = self._get_cache_tag(tag, 2) @@ -1961,7 +1962,7 @@ def _write_raw(self, data): if not self._initialized or self._interface is None: raise NotInitializedException() self._interface.write_raw(data) - + def _read_raw(self, num=-1): "Read binary data from instrument" if self._driver_operation_simulate: @@ -1970,7 +1971,7 @@ def _read_raw(self, num=-1): if not self._initialized or self._interface is None: raise NotInitializedException() return self._interface.read_raw(num) - + def _ask_raw(self, data, num=-1): "Write then read binary data" if self._driver_operation_simulate: @@ -1984,7 +1985,7 @@ def _ask_raw(self, data, num=-1): # if interface does not implement ask_raw, emulate it self._write_raw(data) return self._read_raw(num) - + def _write(self, data, encoding = 'utf-8'): "Write string to instrument" if self._driver_operation_simulate: @@ -2002,7 +2003,7 @@ def _write(self, data, encoding = 'utf-8'): return self._write_raw(str(data).encode(encoding)) - + def _read(self, num=-1, encoding = 'utf-8'): "Read string from instrument" if self._driver_operation_simulate: @@ -2014,7 +2015,7 @@ def _read(self, num=-1, encoding = 'utf-8'): return self._interface.read(num, encoding) except AttributeError: return self._read_raw(num).decode(encoding).rstrip('\r\n') - + def _ask(self, data, num=-1, encoding = 'utf-8'): "Write then read string" if self._driver_operation_simulate: @@ -2035,11 +2036,11 @@ def _ask(self, data, num=-1, encoding = 'utf-8'): self._write(data, encoding) return self._read(num, encoding) - + def _ask_for_values(self, msg, delim=',', converter=float, array=True): ''' write then read a list or array of data - + Parameters -------------- msg : str @@ -2049,8 +2050,8 @@ def _ask_for_values(self, msg, delim=',', converter=float, array=True): converter : type a datatype used to typecase the elements in the returned list array: bool - convert the output to a numpy array - + convert the output to a numpy array + ''' s = self._ask(msg) s_split = s.split(delim) @@ -2058,7 +2059,7 @@ def _ask_for_values(self, msg, delim=',', converter=float, array=True): if array: out = np.array(out) return out - + def _read_stb(self): "Read status byte" if self._driver_operation_simulate: @@ -2070,7 +2071,7 @@ def _read_stb(self): return self._interface.read_stb() except (AttributeError, NotImplementedError): return int(self._ask("*STB?")) - + def _trigger(self): "Device trigger" if self._driver_operation_simulate: @@ -2081,7 +2082,7 @@ def _trigger(self): self._interface.trigger() except (AttributeError, NotImplementedError): self._write("*TRG") - + def _clear(self): "Device clear" if self._driver_operation_simulate: @@ -2092,7 +2093,7 @@ def _clear(self): return self._interface.clear() except (AttributeError, NotImplementedError): self._write("*CLS") - + def _remote(self): "Device set remote" if self._driver_operation_simulate: @@ -2100,7 +2101,7 @@ def _remote(self): if not self._initialized or self._interface is None: raise NotInitializedException() return self._interface.remote() - + def _local(self): "Device set local" if self._driver_operation_simulate: @@ -2108,7 +2109,7 @@ def _local(self): if not self._initialized or self._interface is None: raise NotInitializedException() return self._interface.local() - + def _read_ieee_block(self): "Read IEEE block" # IEEE block binary data is prefixed with #lnnnnnnnn @@ -2132,7 +2133,7 @@ def _read_ieee_block(self): raw_data = self._read_raw() return raw_data - + def _ask_for_ieee_block(self, data, encoding = 'utf-8'): "Write string then read IEEE block" self._write(data, encoding) @@ -2144,33 +2145,33 @@ def _write_ieee_block(self, data, prefix = None, encoding = 'utf-8'): # where l is length of n and n is the # length of the data # ex: #800002000 prefixes 2000 data bytes - + block = b'' - + if type(prefix) == str: block = prefix.encode(encoding) elif type(prefix) == bytes: block = prefix - + block = block + build_ieee_block(data) - + self._write_raw(block) - + def doc(self, obj=None, itm=None, docs=None, prefix=None): """Python IVI documentation generator""" - + # need an obj, if none specified, use self if obj is None: obj = self - + # if first arg is a string, put in itm and use self for obj if type(obj) == str: itm = obj obj = self - + return doc(obj, itm, docs, prefix) - + def help(self, itm=None, complete=False, indent=0): """Python IVI help system""" return help(self, itm, complete, indent) - + diff --git a/ivi/rigol/__init__.py b/ivi/rigol/__init__.py index 3ea5b2a7..72df6af0 100644 --- a/ivi/rigol/__init__.py +++ b/ivi/rigol/__init__.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,51 @@ """ +# Oscilloscopes +# DS1000Z +from .rigolDS1054Z import rigolDS1054Z +from .rigolDS1074Z import rigolDS1074Z +from .rigolDS1074ZPlus import rigolDS1074ZPlus +from .rigolDS1104Z import rigolDS1104Z +from .rigolMSO1074Z import rigolMSO1074Z +from .rigolMSO1104Z import rigolMSO1104Z +# DS2000A +from .rigolDS2072A import rigolDS2072A +from .rigolDS2102A import rigolDS2102A +from .rigolDS2202A import rigolDS2202A +from .rigolDS2302A import rigolDS2302A +from .rigolMSO2072A import rigolMSO2072A +from .rigolMSO2102A import rigolMSO2102A +from .rigolMSO2202A import rigolMSO2202A +from .rigolMSO2302A import rigolMSO2302A +# DS4000 +from .rigolDS4012 import rigolDS4012 +from .rigolDS4014 import rigolDS4014 +from .rigolDS4022 import rigolDS4022 +from .rigolDS4024 import rigolDS4024 +from .rigolDS4032 import rigolDS4032 +from .rigolDS4034 import rigolDS4034 +from .rigolDS4052 import rigolDS4052 +from .rigolDS4054 import rigolDS4054 +from .rigolMSO4012 import rigolMSO4012 +from .rigolMSO4014 import rigolMSO4014 +from .rigolMSO4022 import rigolMSO4022 +from .rigolMSO4024 import rigolMSO4024 +from .rigolMSO4032 import rigolMSO4032 +from .rigolMSO4034 import rigolMSO4034 +from .rigolMSO4052 import rigolMSO4052 +from .rigolMSO4054 import rigolMSO4054 +# MSO5000 +from .rigolMSO5072 import rigolMSO5072 +from .rigolMSO5074 import rigolMSO5074 +# MSO7000 +from .rigolDS7014 import rigolDS7014 +from .rigolMSO7014 import rigolMSO7014 +# MSO8000 +from .rigolMSO8064 import rigolMSO8064 + + + # DC Power Supplies # DP800 from .rigolDP831A import rigolDP831A diff --git a/ivi/rigol/rigolBaseDCPwr.py b/ivi/rigol/rigolBaseDCPwr.py index fe600eb2..421f9785 100644 --- a/ivi/rigol/rigolBaseDCPwr.py +++ b/ivi/rigol/rigolBaseDCPwr.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ivi/rigol/rigolBaseScope.py b/ivi/rigol/rigolBaseScope.py new file mode 100644 index 00000000..4a631fc1 --- /dev/null +++ b/ivi/rigol/rigolBaseScope.py @@ -0,0 +1,1274 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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. + +""" + +import array +import math +import time + +from .. import ivi +from .. import scope +from .. import scpi +from .. import extra + +AcquisitionTypeMapping = { + 'normal': 'norm', + 'peak_detect': 'peak', + 'high_resolution': 'hres', + 'average': 'aver'} +VerticalCoupling = set(['ac', 'dc', 'gnd']) +TriggerTypeMapping = { + 'edge': 'edge', + 'tv': 'video', + 'width': 'puls', + 'glitch': 'puls', + 'runt': 'runt', + #'immediate': '', + 'ac_line': 'edge', + 'window': 'wind', + 'nth_edge': 'nedg', + 'pattern': 'patt', + 'delay': 'del', + 'timeout': 'tim', + 'duration': 'dur', + 'setup_hold': 'shold', + 'rs232': 'rs232', + 'i2c': 'iic', + 'spi': 'spi'} +TriggerCouplingMapping = { + 'ac': ('ac', 0), + 'dc': ('dc', 0), + 'hf_reject': ('hfr', 0), + 'lf_reject': ('lfr', 0), + 'noise_reject': ('dc', 1), + 'ac_noise_reject': ('ac', 1), + 'hf_noise_reject': ('hfr', 1), + 'lf_noise_reject': ('lfr', 1)} +TVTriggerEventMapping = { + 'field1': 'oddf', + 'field2': 'even', + 'any_line': 'alin', + 'line_number': 'line'} +TVTriggerFormatMapping = { + 'ntsc': 'ntsc', + 'pal': 'pals', + 'secam': 'pals', + 'p480': '480p', + 'p576': '576p'} +PolarityMapping = {'positive': 'pos', + 'negative': 'neg'} +GlitchConditionMapping = {'less_than': 'less', + 'greater_than': 'gre'} +WidthConditionMapping = {'within': ''} +SlopeMapping = { + 'positive': 'pos', + 'negative': 'neg', + 'either': 'rfal'} +MeasurementFunctionMapping = { + 'rise_time': 'RTIME', + 'fall_time': 'FTIME', + 'frequency': 'FREQUENCY', + 'period': 'period', + 'voltage_rms': 'vrms', + 'voltage_peak_to_peak': 'vpp', + 'voltage_max': 'vmax', + 'voltage_min': 'vmin', + 'voltage_high': 'vtop', + 'voltage_low': 'vbase', + 'voltage_average': 'vavg', + 'width_negative': 'nwidth', + 'width_positive': 'pwidth', + 'duty_cycle_positive': 'pduty', + 'duty_cycle_negative': 'nduty', + 'amplitude': 'vamp', + 'voltage_cycle_rms': 'pvrms', + 'voltage_cycle_average': 'vbase', + 'overshoot': 'overshoot', + 'preshoot': 'preshoot'} + #'ratio': 'vratio', + #'phase': 'rphase', + #'delay': 'rdelay'} +MeasurementFunctionMappingDigital = { + 'frequency': 'frequency', + 'period': 'period',} +ScreenshotImageFormatMapping = { + 'tif': 'tiff', + 'tiff': 'tiff', + 'bmp': 'bmp24', + 'bmp24': 'bmp24', + 'bmp8': 'bmp8', + 'png': 'png', + 'png24': 'png', + 'jpg': 'jpeg', + 'jpeg': 'jpeg'} +TimebaseModeMapping = { + 'main': 'main', + 'window': 'window', + 'xy': 'xy', + 'roll': 'roll'} +TriggerModifierMapping = {'none': 'norm', 'auto': 'auto'} + +class rigolBaseScope(scpi.common.IdnCommand, scpi.common.ErrorQuery, scpi.common.Reset, + scpi.common.SelfTest, scpi.common.Memory, + scope.Base, scope.TVTrigger, scope.GlitchTrigger, scope.WidthTrigger, + scope.AcLineTrigger, scope.WaveformMeasurement, + scope.ContinuousAcquisition, scope.AverageAcquisition, + scope.TriggerModifier, scope.AutoSetup, + extra.common.SystemSetup, extra.common.Screenshot, + ivi.Driver): + "Rigol generic IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + cls = 'IviScope' + grp = 'Base' + self.__dict__.setdefault('_instrument_id', '') + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_invert = list() + self._channel_bw_limit = list() + + super(rigolBaseScope, self).__init__(*args, **kwargs) + + self._self_test_delay = 0 + self._memory_size = 10 + + self._analog_channel_name = list() + self._analog_channel_count = 4 + self._digital_channel_name = list() + self._digital_channel_count = 16 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 1e9 + self._bandwidth_limit = {'20M': 20e6} + self._max_averages = 1024 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._timebase_mode = 'main' + self._timebase_position = 0.0 + self._timebase_range = 1e-3 + self._timebase_scale = 100e-6 + self._timebase_window_position = 0.0 + self._timebase_window_range = 5e-6 + self._timebase_window_scale = 500e-9 + self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping + self._display_vectors = True + + self._identity_description = "Rigol generic IVI oscilloscope driver" + self._identity_identifier = "" + self._identity_revision = "" + self._identity_vendor = "" + self._identity_instrument_manufacturer = "Rigol Technologies" + self._identity_instrument_model = "" + self._identity_instrument_firmware_revision = "" + self._identity_specification_major_version = 4 + self._identity_specification_minor_version = 1 + self._identity_supported_instrument_models = ['DS1054Z', 'DS1074ZPlus', 'DS1104Z', 'DS1074Plus', 'MSO1074Z', + 'MSO1104Z', 'DS2072A', 'DS2074A', 'DS2104A', 'DS2204A', 'DS2304A', 'MSO2072A', 'MSO2074A', + 'MSO2104A', 'MSO2204A', 'MSO2304A', 'MSO5072', 'MSO5074', 'DS7014', 'MSO7014', 'MSO8064'] + + self._add_property('channels[].invert', + self._get_channel_invert, + self._set_channel_invert, + None, + ivi.Doc(""" + Selects whether or not to invert the channel. + """)) + self._add_property('channels[].label', + self._get_channel_label, + self._set_channel_label, + None, + ivi.Doc(""" + Sets the channel label. Setting a channel label also adds the label to + the nonvolatile label list. + """)) + self._add_property('channels[].probe_skew', + self._get_channel_probe_skew, + self._set_channel_probe_skew, + None, + ivi.Doc(""" + Specifies the channel-to-channel skew factor for the channel. Each analog + channel can be adjusted + or - 100 ns for a total of 200 ns difference + between channels. This can be used to compensate for differences in cable + delay. Units are seconds. + """)) + self._add_property('channels[].scale', + self._get_channel_scale, + self._set_channel_scale, + None, + ivi.Doc(""" + Specifies the vertical scale, or units per division, of the channel. Units + are volts. + """)) + self._add_property('timebase.mode', + self._get_timebase_mode, + self._set_timebase_mode, + None, + ivi.Doc(""" + Sets the current time base. There are four time base modes: + + * 'main': normal timebase + * 'window': zoomed or delayed timebase + * 'xy': channels are plotted against each other, no timebase + * 'roll': data moves continuously from left to right + """)) + self._add_property('timebase.position', + self._get_timebase_position, + self._set_timebase_position, + None, + ivi.Doc(""" + Sets the time interval between the trigger event and the display reference + point on the screen. The display reference point is either left, right, or + center and is set with the timebase.reference property. The maximum + position value depends on the time/division settings. + """)) + self._add_property('timebase.range', + self._get_timebase_range, + self._set_timebase_range, + None, + ivi.Doc(""" + Sets the full-scale horizontal time in seconds for the main window. The + range is 10 times the current time-per-division setting. + """)) + self._add_property('timebase.scale', + self._get_timebase_scale, + self._set_timebase_scale, + None, + ivi.Doc(""" + Sets the horizontal scale or units per division for the main window. + """)) + self._add_property('timebase.window.position', + self._get_timebase_window_position, + self._set_timebase_window_position, + None, + ivi.Doc(""" + Sets the horizontal position in the zoomed (delayed) view of the main + sweep. The main sweep range and the main sweep horizontal position + determine the range for this command. The value for this command must + keep the zoomed view window within the main sweep range. + """)) + self._add_property('timebase.window.range', + self._get_timebase_window_range, + self._set_timebase_window_range, + None, + ivi.Doc(""" + Sets the fullscale horizontal time in seconds for the zoomed (delayed) + window. The range is 10 times the current zoomed view window seconds per + division setting. The main sweep range determines the range for this + command. The maximum value is one half of the timebase.range value. + """)) + self._add_property('timebase.window.scale', + self._get_timebase_window_scale, + self._set_timebase_window_scale, + None, + ivi.Doc(""" + Sets the zoomed (delayed) window horizontal scale (seconds/division). The + main sweep scale determines the range for this command. The maximum value + is one half of the timebase.scale value. + """)) + self._add_property('display.vectors', + self._get_display_vectors, + self._set_display_vectors, + None, + ivi.Doc(""" + When enabled, draws a line between consecutive waveform data points. + """)) + self._add_method('display.clear', + self._display_clear, + ivi.Doc(""" + Clears the display and resets all associated measurements. If the + oscilloscope is stopped, all currently displayed data is erased. If the + oscilloscope is running, all the data in active channels and functions is + erased; however, new data is displayed on the next acquisition. + """)) + self._add_property('acquisition.analog_sample_rate', + self._get_acquisition_analog_sample_rate, + None, + ivi.Doc(""" + Returns or sets the effective sample rate of the acquired analog waveform using the + current configuration. The units are samples per second. + """,)) + + self._init_channels() + + def _initialize(self, resource = None, id_query = False, reset = False, **keywargs): + "Opens an I/O session to the instrument." + + self._channel_count = self._analog_channel_count + self._digital_channel_count + + super(rigolBaseScope, 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_channels(self): + super(rigolBaseScope, self)._init_channels() + + self._channel_name = list() + self._channel_label = list() + self._channel_probe_skew = list() + self._channel_invert = list() + self._channel_scale = list() + self._channel_bw_limit = list() + + self._analog_channel_name = list() + for i in range(self._analog_channel_count): + self._channel_name.append("chan%d" % (i+1)) + self._channel_label.append("%d" % (i+1)) + self._analog_channel_name.append("chan%d" % (i+1)) + self._channel_probe_skew.append(0) + self._channel_invert.append(False) + self._channel_scale.append(1.0) + self._channel_bw_limit.append(False) + + # digital channels + self._digital_channel_name = list() + if (self._digital_channel_count > 0): + for i in range(self._digital_channel_count): + self._channel_name.append("d%d" % i) + self._channel_label.append("D%d" % i) + self._digital_channel_name.append("d%d" % i) + + for i in range(self._analog_channel_count, self._channel_count): + self._channel_input_impedance[i] = 100000 + self._channel_input_frequency_max[i] = 1e9 + self._channel_probe_attenuation[i] = 1 + self._channel_coupling[i] = 'dc' + self._channel_offset[i] = 0 + self._channel_range[i] = 1 + + self._channel_count = self._analog_channel_count + self._digital_channel_count + self.channels._set_list(self._channel_name) + + def _system_fetch_setup(self): + if self._driver_operation_simulate: + return b'' + + self._write(":system:setup?") + + data = ivi.decode_ieee_block(self._read_raw()) + + return data + + def _system_load_setup(self, data): + if self._driver_operation_simulate: + return + + self._write_ieee_block(data, ':system:setup ') + + self.driver_operation.invalidate_all_attributes() + + def _display_fetch_screenshot(self, format='png', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + format = self._display_screenshot_image_format_mapping[format] + + self._write(":display:data? on, %d, %s" % (int(invert), format)) + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_timebase_mode(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + if int(self._ask(":timebase:delay:enable?")): + self._timebase_mode = "window" + else: + value = self._ask(":timebase:mode?").lower() + self._timebase_mode = [k for k,v in TimebaseModeMapping.items() if v==value][0] + self._set_cache_valid() + return self._timebase_mode + + def _set_timebase_mode(self, value): + if value not in TimebaseModeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if value == 'window': + self._write("timebase:mode main") + self._write("timebase:delay:enable 1") + else: + self._write("timebase:delay:enable 0") + self._write(":timebase:mode %s" % TimebaseModeMapping[value]) + self._timebase_mode = value + self._set_cache_valid() + + def _get_timebase_position(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_position = float(self._ask(":timebase:main:offset?")) + self._set_cache_valid() + return self._timebase_position + + def _set_timebase_position(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:main:offset %e" % value) + self._timebase_position = value + self._set_cache_valid() + self._set_cache_valid(False, 'timebase_window_position') + + def _get_timebase_range(self): + return self._get_timebase_scale() * self._horizontal_divisions + + def _set_timebase_range(self, value): + value = float(value) + self._set_timebase_scale(value / self._horizontal_divisions) + + def _get_timebase_scale(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_scale = float(self._ask(":timebase:main:scale?")) + self._timebase_range = self._timebase_scale * self._horizontal_divisions + self._set_cache_valid() + return self._timebase_scale + + def _set_timebase_scale(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:main:scale %e" % value) + self._timebase_scale = value + self._timebase_range = value * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(False, 'timebase_window_scale') + + def _get_timebase_window_position(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_window_position = float(self._ask(":timebase:delay:offset?")) + self._set_cache_valid() + return self._timebase_window_position + + def _set_timebase_window_position(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:delay:offset %e" % value) + self._timebase_window_position = value + self._set_cache_valid() + + def _get_timebase_window_range(self): + return self._get_timebase_window_scale() * self._horizontal_divisions + + def _set_timebase_window_range(self, value): + value = float(value) + self._set_timebase_window_scale(value / self._horizontal_divisions) + + def _get_timebase_window_scale(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._timebase_window_scale = float(self._ask(":timebase:delay:scale?")) + self._timebase_window_range = self._timebase_window_scale * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(True, 'timebase_window_range') + return self._timebase_window_scale + + def _set_timebase_window_scale(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:delay:scale %e" % value) + self._timebase_window_scale = value + self._timebase_window_range = value * self._horizontal_divisions + self._set_cache_valid() + self._set_cache_valid(True, 'timebase_window_range') + + def _get_display_vectors(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._display_vectors = self._ask(":display:type?").lower() == 'vect' + self._set_cache_valid() + return self._display_vectors + + def _set_display_vectors(self, value): + value = bool(value) + if not self._driver_operation_simulate: + self._write(":display:type %s" % ('vectors' if value else 'dots')) + self._display_vectors = value + self._set_cache_valid() + + def _display_clear(self): + if not self._driver_operation_simulate: + self._write(":display:clear") + + def _get_acquisition_start_time(self): + pos = 0 + if not self._driver_operation_simulate and not self._get_cache_valid(): + pos = float(self._ask(":timebase:main:offset?")) + self._set_cache_valid() + self._acquisition_start_time = pos - self._get_acquisition_time_per_record() / 2 + return self._acquisition_start_time + + def _set_acquisition_start_time(self, value): + value = float(value) + value = value + self._get_acquisition_time_per_record() / 2 + if not self._driver_operation_simulate: + self._write(":timebase:main:offset %e" % value) + self._acquisition_start_time = value + self._set_cache_valid() + + def _get_acquisition_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":acquire:type?").lower() + self._acquisition_type = [k for k,v in AcquisitionTypeMapping.items() if v==value][0] + self._set_cache_valid() + return self._acquisition_type + + def _set_acquisition_type(self, value): + if value not in AcquisitionTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":acquire:type %s" % AcquisitionTypeMapping[value]) + self._acquisition_type = value + self._set_cache_valid() + + def _get_acquisition_number_of_points_minimum(self): + if not self._driver_operation_simulate: + value = self._ask(":acquire:mdepth?") + if value == 'AUTO': + self._acquisition_number_of_points_minimum = value + else: + self._acquisition_number_of_points_minimum = float(value) + return self._acquisition_number_of_points_minimum + + def _set_acquisition_number_of_points_minimum(self, value): + if value == 'AUTO': + if not self._driver_operation_simulate: + self._write(":acquire:mdepth %s" % value) + else: + value = int(value) + if not self._driver_operation_simulate: + self._write(":acquire:mdepth %d" % value) + self._acquisition_number_of_points_minimum = value + + def _get_acquisition_record_length(self): + if not self._driver_operation_simulate: + val = self._ask(":acquire:mdepth?") + if val == "AUTO": + self._acquisition_record_length = val + else: + self._acquisition_record_length =float(val) + return self._acquisition_record_length + + def _get_acquisition_time_per_record(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_time_per_record = float(self._ask(":timebase:scale?")) * self._horizontal_divisions + self._set_cache_valid() + return self._acquisition_time_per_record + + def _set_acquisition_time_per_record(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":timebase:scale %e" % (value / self._horizontal_divisions)) + self._acquisition_time_per_record = value + self._set_cache_valid() + self._set_cache_valid(False, 'acquisition_start_time') + + def _get_acquisition_analog_sample_rate(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition__analog_sample_rate = self._ask(":acquire:srate?") + self._set_cache_valid() + return self._acquisition__analog_sample_rate + + def _get_channel_label(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_label[index] = self._ask(":%s:label?" % self._channel_name[index]).strip('"') + self._set_cache_valid(index=index) + return self._channel_label[index] + + def _set_channel_label(self, index, value): + value = str(value) + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate: + self._write(":%s:label \"%s\"" % (self._channel_name[index], value)) + self._channel_label[index] = value + self._set_cache_valid(index=index) + + def _get_channel_enabled(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_enabled[index] = bool(int(self._ask(":%s:display?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_enabled[index] + + def _set_channel_enabled(self, index, value): + value = bool(value) + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate: + self._write(":%s:display %d" % (self._channel_name[index], int(value))) + self._channel_enabled[index] = value + self._set_cache_valid(index=index) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 1000000: + raise Exception('Invalid impedance selection') + self._channel_input_impedance[index] = value + + def _get_channel_input_frequency_max(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + value = self._ask(":%s:bwlimit?" % self._channel_name[index]).upper() + if value == 'OFF': + self._channel_input_frequency_max[index] = self._bandwidth + else: + self._channel_input_frequency_max[index] = self._bandwidth_limit[value] + return self._channel_input_frequency_max[index] + + def _set_channel_input_frequency_max(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate: + limit = 'OFF' + bw = self._bandwidth + + for l, b in self._bandwidth_limit.items(): + if b < bw and b > value: + limit, bw = l, b + + value = bw + self._write(":%s:bwlimit %s" % (self._channel_name[index], limit)) + self._channel_input_frequency_max[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %e" % (self._channel_name[index], value)) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') + + def _get_channel_probe_skew(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_skew[index] = float(self._ask(":%s:tcal?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_skew[index] + + def _set_channel_probe_skew(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:tcal %e" % (self._channel_name[index], value)) + self._channel_probe_skew[index] = value + self._set_cache_valid(index=index) + + def _get_channel_invert(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_invert[index] = bool(int(self._ask(":%s:invert?" % self._channel_name[index]))) + self._set_cache_valid(index=index) + return self._channel_invert[index] + + def _set_channel_invert(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:invert %d" % (self._channel_name[index], value)) + self._channel_invert[index] = value + self._set_cache_valid(index=index) + + def _get_channel_coupling(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_coupling[index] = self._ask(":%s:coupling?" % self._channel_name[index]).lower() + self._set_cache_valid(index=index) + return self._channel_coupling[index] + + def _set_channel_coupling(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + if value not in VerticalCoupling: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:coupling %s" % (self._channel_name[index], value)) + self._channel_coupling[index] = value + self._set_cache_valid(index=index) + + def _get_channel_offset(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate: + self._channel_offset[index] = float(self._ask(":%s:offset?" % self._channel_name[index])) + return self._channel_offset[index] + + def _set_channel_offset(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:offset %e" % (self._channel_name[index], value)) + self._channel_offset[index] = value + + def _get_channel_range(self, index): + index = ivi.get_index(self._channel_name, index) + return self._get_channel_scale(index) * self._vertical_divisions + + def _set_channel_range(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + self._set_channel_scale(index, value / self._vertical_divisions) + + def _get_channel_scale(self, index): + index = ivi.get_index(self._channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_scale[index] = float(self._ask(":%s:scale?" % self._channel_name[index])) + self._channel_range[index] = self._channel_scale[index] * self._vertical_divisions + self._set_cache_valid(index=index) + self._set_cache_valid(True, "channel_range", index) + return self._channel_scale[index] + + def _set_channel_scale(self, index, value): + index = ivi.get_index(self._channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:scale %e" % (self._channel_name[index], value)) + self._channel_scale[index] = value + self._channel_range[index] = value * self._vertical_divisions + self._set_cache_valid(index=index) + self._set_cache_valid(True, "channel_range", index) + self._set_cache_valid(False, "channel_offset", index) + + def _get_measurement_status(self): + return self._measurement_status + + def _get_trigger_coupling(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + cpl = self._ask(":trigger:coupling?").lower() + noise = int(self._ask(":trigger:nreject?")) + for k in TriggerCouplingMapping: + if (cpl, noise) == TriggerCouplingMapping[k]: + self._trigger_coupling = k + return self._trigger_coupling + + def _set_trigger_coupling(self, value): + if value not in TriggerCouplingMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + cpl, noise = TriggerCouplingMapping[value] + self._write(":trigger:coupling %s" % cpl) + self._write(":trigger:nreject %d" % noise) + self._trigger_coupling = value + self._set_cache_valid() + + def _get_trigger_holdoff(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_holdoff = float(self._ask(":trigger:holdoff?")) + self._set_cache_valid() + return self._trigger_holdoff + + def _set_trigger_holdoff(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:holdoff %e" % value) + self._trigger_holdoff = value + self._set_cache_valid() + + def _get_trigger_level(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_level = float(self._ask(":trigger:edge:level?")) + self._set_cache_valid() + return self._trigger_level + + def _set_trigger_level(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:edge:level %e" % value) + self._trigger_level = value + self._set_cache_valid() + + def _get_trigger_edge_slope(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:edge:slope?").lower() + self._trigger_edge_slope = [k for k,v in SlopeMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_edge_slope + + def _set_trigger_edge_slope(self, value): + if value not in SlopeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:slope %s" % SlopeMapping[value]) + self._trigger_edge_slope = value + self._set_cache_valid() + + def _get_trigger_source(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:edge:source?").lower() + # TODO process value + self._trigger_source = value + self._set_cache_valid() + return self._trigger_source + + def _set_trigger_source(self, value): + if hasattr(value, 'name'): + value = value.name + value = str(value) + if value not in self._channel_name: + raise ivi.UnknownPhysicalNameException() + if not self._driver_operation_simulate: + self._write(":trigger:edge:source %s" % value) + self._trigger_source = value + self._set_cache_valid() + + def _get_trigger_type(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:mode?").lower() + if value == 'edge': + src = self._ask(":trigger:edge:source?").lower() + if src == 'ac': + value = 'ac_line' + elif value == 'puls': + qual = self._ask(":trigger:pulse:when?").lower() + if qual in ('pgl', 'ngl'): + value = 'width' + else: + value = 'glitch' + else: + value = [k for k,v in TriggerTypeMapping.items() if v==value][0] + self._trigger_type = value + self._set_cache_valid() + return self._trigger_type + + def _set_trigger_type(self, value): + if value not in TriggerTypeMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:mode %s" % TriggerTypeMapping[value]) + if value == 'ac_line': + self._write(":trigger:edge:source ac") + if value == 'glitch': + if self._get_trigger_glitch_condition() == 'greater_than': + if self._get_trigger_width_polarity() == 'positive': + self._write(":trigger:pulse:when pgr") + else: + self._write(":trigger:pulse:when ngr") + else: + if self._get_trigger_width_polarity() == 'positive': + self._write(":trigger:pulse:when ples") + else: + self._write(":trigger:pulse:when nles") + if value == 'width': + if self._get_trigger_width_polarity() == 'positive': + self._write(":trigger:pulse:when pgl") + else: + self._write(":trigger:pulse:when ngl") + self._trigger_type = value + self._set_cache_valid() + + def _measurement_abort(self): + pass + + def _get_trigger_tv_trigger_event(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:video:mode?").lower() + # may need processing + self._trigger_tv_trigger_event = [k for k,v in TVTriggerEventMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_tv_trigger_event + + def _set_trigger_tv_trigger_event(self, value): + if value not in TVTriggerEvent: + raise ivi.ValueNotSupportedException() + # may need processing + if not self._driver_operation_simulate: + self._write(":trigger:video:mode %s" % TVTriggerEventMapping[value]) + self._trigger_tv_trigger_event = value + self._set_cache_valid() + + def _get_trigger_tv_line_number(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = int(self._ask(":trigger:video:line?")) + # may need processing + self._trigger_tv_line_number = value + self._set_cache_valid() + return self._trigger_tv_line_number + + def _set_trigger_tv_line_number(self, value): + value = int(value) + # may need processing + if not self._driver_operation_simulate: + self._write(":trigger:video:line %e" % value) + self._trigger_tv_line_number = value + self._set_cache_valid() + + def _get_trigger_tv_polarity(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:video:polarity?").lower() + self._trigger_tv_polarity = [k for k,v in PolarityMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_tv_polarity + + def _set_trigger_tv_polarity(self, value): + if value not in PolarityMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:video:polarity %s" % PolarityMapping[value]) + self._trigger_tv_polarity = value + self._set_cache_valid() + + def _get_trigger_tv_signal_format(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:video:standard?").lower() + self._trigger_tv_signal_format = [k for k,v in TVTriggerFormatMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_tv_signal_format + + def _set_trigger_tv_signal_format(self, value): + if value not in TVTriggerFormatMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:video:standard %s" % TVTriggerFormatMapping[value]) + self._trigger_tv_signal_format = value + self._set_cache_valid() + + def _get_trigger_glitch_condition(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:pulse:when?").lower() + if value in ('pgr', 'ngr'): + self._trigger_glitch_condition = 'greater_than' + self._set_cache_valid() + elif value in ('ples', 'nles'): + self._trigger_glitch_condition = 'less_than' + self._set_cache_valid() + return self._trigger_glitch_condition + + def _set_trigger_glitch_condition(self, value): + if value not in GlitchConditionMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if self._get_trigger_glitch_polarity() == 'positive': + self._write(":trigger:pulse:when %s" % ('pgr' if value == 'greater_than' else 'ples')) + else: + self._write(":trigger:pulse:when %s" % ('ngr' if value == 'greater_than' else 'nles')) + self._trigger_glitch_condition = value + self._set_cache_valid() + + def _get_trigger_glitch_polarity(self): + return self._get_trigger_width_polarity() + + def _set_trigger_glitch_polarity(self, value): + self._set_trigger_width_polarity(value) + + def _get_trigger_glitch_width(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_glitch_width = float(self._ask(":trigger:pulse:width?")) + self._set_cache_valid() + return self._trigger_glitch_width + + def _set_trigger_glitch_width(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:pulse:width %e" % value) + self._trigger_glitch_width = value + self._set_cache_valid() + + def _get_trigger_width_condition(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = 'within' + return self._trigger_width_condition + + def _set_trigger_width_condition(self, value): + if value not in WidthConditionMapping: + raise ivi.ValueNotSupportedException() + self._trigger_width_condition = value + self._set_cache_valid() + + def _get_trigger_width_threshold_high(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_width_threshold_high = float(self._ask(":trigger:pulse:uwidth?")) + self._set_cache_valid() + return self._trigger_width_threshold_high + + def _set_trigger_width_threshold_high(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:pulse:uwidth %e" % value) + self._trigger_width_threshold_high = value + self._set_cache_valid() + + def _get_trigger_width_threshold_low(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_width_threshold_low = float(self._ask(":trigger:pulse:lwidth?")) + self._set_cache_valid() + return self._trigger_width_threshold_low + + def _set_trigger_width_threshold_low(self, value): + value = float(value) + if not self._driver_operation_simulate: + self._write(":trigger:pulse:lwidth %e" % value) + self._trigger_width_threshold_low = value + self._set_cache_valid() + + def _get_trigger_width_polarity(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:pulse:when?").lower() + self._trigger_width_polarity = 'positive' if value.lower() in ('pgr', 'ples', 'pgl') else 'negative' + self._set_cache_valid() + return self._trigger_width_polarity + + def _set_trigger_width_polarity(self, value): + if value not in PolarityMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if self._get_trigger_type() == 'glitch': + if self._get_trigger_glitch_condition() == 'greater_than': + if value == 'positive': + self._write(":trigger:pulse:when pgr") + else: + self._write(":trigger:pulse:when ngr") + else: + if value == 'positive': + self._write(":trigger:pulse:when ples") + else: + self._write(":trigger:pulse:when nles") + if self._get_trigger_type() == 'width': + if value == 'positive': + self._write(":trigger:pulse:when pgl") + else: + self._write(":trigger:pulse:when ngl") + self._trigger_width_polarity = value + self._set_cache_valid() + + def _get_trigger_ac_line_slope(self): + return self._get_trigger_edge_slope() + + def _set_trigger_ac_line_slope(self, value): + self._set_trigger_edge_slope(value) + + def _measurement_fetch_waveform(self, index): + index = ivi.get_index(self._channel_name, index) + + if self._driver_operation_simulate: + return ivi.TraceYT() + + self._write(":waveform:source %s" % self._channel_name[index]) + self._write(":waveform:format byte") + + trace = ivi.TraceYT() + + # Read preamble + pre = self._ask(":waveform:preamble?").split(',') + + acq_format = int(pre[0]) + acq_type = int(pre[1]) + points = int(pre[2]) + trace.average_count = int(pre[3]) + trace.x_increment = float(pre[4]) + trace.x_origin = float(pre[5]) + trace.x_reference = int(float(pre[6])) + trace.y_increment = float(pre[7]) + trace.y_origin = 0.0 + trace.y_reference = int(float(pre[9]) + float(pre[8])) + + if acq_format != 0: + raise UnexpectedResponseException() + + # Read waveform data + data = bytearray() + + for offset in range(1, points+1, 250000): + self._write(":waveform:start %d" % offset) + self._write(":waveform:stop %d" % min(points, offset+249999)) + self._write(":waveform:data?") + raw_data = self._read_raw() + data.extend(ivi.decode_ieee_block(raw_data)) + + # Store in trace object + trace.y_raw = array.array('B', data[0:points]) + + return trace + + def _measurement_read_waveform(self, index, maximum_time): + return self._measurement_fetch_waveform(index) + + def _measurement_initiate(self): + if not self._driver_operation_simulate: + self._write(":single") + self._set_cache_valid(False, 'trigger_continuous') + + def _get_reference_level_high(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._reference_level_high = float(self._ask(":measure:setup:max?")) + self._set_cache_valid() + return self._reference_level_high + + def _set_reference_level_high(self, value): + value = float(value) + if value < 7: value = 7 + if value > 95: value = 95 + if not self._driver_operation_simulate: + self._write(":measure:setup:max %e" % value) + self._reference_level_high = value + self._set_cache_valid() + + def _get_reference_level_low(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._reference_level_low = float(self._ask(":measure:setup:min?")) + self._set_cache_valid() + return self._reference_level_low + + def _set_reference_level_low(self, value): + value = float(value) + if value < 5: value = 5 + if value > 93: value = 93 + if not self._driver_operation_simulate: + self._write(":measure:setup:min %e" % value) + self._reference_level_low = value + self._set_cache_valid() + + def _get_reference_level_middle(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._reference_level_middle = float(self._ask(":measure:setup:mid?")) + self._set_cache_valid() + return self._reference_level_middle + + def _set_reference_level_middle(self, value): + value = float(value) + if value < 6: value = 6 + if value > 94: value = 94 + if not self._driver_operation_simulate: + self._write(":measure:setup:mid %e" % value) + self._reference_level_middle = value + self._set_cache_valid() + + def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): + index = ivi.get_index(self._channel_name, index) + print 'measurement_function ', measurement_function + print 'index is ', index + if index < self._analog_channel_count: + if measurement_function not in MeasurementFunctionMapping: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMapping[measurement_function] + print 'func is ', func + else: + if measurement_function not in MeasurementFunctionMappingDigital: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMappingDigital[measurement_function] + if not self._driver_operation_simulate: + # l = func.split(' ') + # l[0] = l[0] + '?' + # if len(l) > 1: + # l[-1] = l[-1] + ',' + # func = ' '.join(l) + query = ":measure:item? %s, %s" % (func, self._channel_name[index]) + if measurement_function in ['phase', 'delay']: + ref_index = ivi.get_index(self._channel_name, ref_channel) + query += ", %s" % self._channel_name[ref_index] + meas = self._ask(query) + if meas == 'measure error!': + return meas + else: + return float(meas) + return 0 + + def _measurement_read_waveform_measurement(self, index, measurement_function, maximum_time): + return self._measurement_fetch_waveform_measurement(index, measurement_function) + + def _get_trigger_continuous(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._trigger_continuous = self._ask(":trigger:sweep?").lower() != 'sing' + self._set_cache_valid() + return self._trigger_continuous + + def _set_trigger_continuous(self, value): + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s" % ('run' if value else 'stop')) + self._trigger_continuous = value + self._set_cache_valid() + + def _get_acquisition_number_of_averages(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + self._acquisition_number_of_averages = int(self._ask(":acquire:averages?")) + self._set_cache_valid() + return self._acquisition_number_of_averages + + def _set_acquisition_number_of_averages(self, value): + if value < 2 or value > self._max_averages: + raise ivi.OutOfRangeException() + value = 2**round(math.log(value, 2)) + if not self._driver_operation_simulate: + self._write(":acquire:averages %d" % value) + self._acquisition_number_of_averages = value + self._set_cache_valid() + + def _get_trigger_modifier(self): + if not self._driver_operation_simulate and not self._get_cache_valid(): + value = self._ask(":trigger:sweep?").lower() + if value == 'sing': + self._trigger_modifier = 'none' + else: + self._trigger_modifier = [k for k,v in TriggerModifierMapping.items() if v==value][0] + self._set_cache_valid() + return self._trigger_modifier + + def _set_trigger_modifier(self, value): + if value not in TriggerModifierMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":trigger:sweep %s" % TriggerModifierMapping[value]) + self._trigger_modifier = value + self._set_cache_valid() + + def _measurement_auto_setup(self): + if not self._driver_operation_simulate: + self._write(":autoset") + diff --git a/ivi/rigol/rigolBaseWG.py b/ivi/rigol/rigolBaseWG.py new file mode 100644 index 00000000..1d7f5ed8 --- /dev/null +++ b/ivi/rigol/rigolBaseWG.py @@ -0,0 +1,343 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2016 Alex Forencich + +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. + +""" + +import numpy as np +import struct + +from .. import ivi +from .. import fgen + +OutputMode = set(['function', 'arbitrary']) +OperationMode = set(['continuous']) +StandardWaveformMapping = { + 'sin': 'sin', + 'squ': 'squ', + 'ramp': 'ramp', + 'dc': 'dc', + 'puls': 'puls', + 'nois': 'nois', + 'sinc': 'sinc', + 'exprise': 'exprise', + 'expfall': 'expfall', + 'ecg': 'ecg', + 'gaussian': 'gaussian', + 'lorentz': 'lorentz', + 'haversine': 'haversine', + } + +class rigolBaseWG(fgen.Base, fgen.StdFunc, fgen.ArbWfm, fgen.ArbFrequency, + fgen.ArbChannelWfm): + "Rigol Oscilloscope WG option IVI waveform generator driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1074Z') + + self._output_standard_waveform_pulse_width = list() + self._output_standard_waveform_symmetry = list() + self._output_noise_enabled = list() + self._output_noise_percent = list() + + super(rigolBaseWG, self).__init__(*args, **kwargs) + + # WG option + self._output_count = 2 + self._arbitrary_sample_rate = 0 + self._arbitrary_waveform_number_waveforms_max = 0 + self._arbitrary_waveform_size_max = 131072 + self._arbitrary_waveform_size_min = 2 + self._arbitrary_waveform_quantum = 1 + + self._add_property('outputs[].standard_waveform.symmetry', + self._get_output_standard_waveform_symmetry, + self._set_output_standard_waveform_symmetry, + None, + """ + Specifies the symmetry for a ramp or triangle waveform. This attribute + affects function generator behavior only when the Waveform attribute is + set to Waveform Triangle, Ramp Up, or Ramp Down. The value is expressed + as a percentage. + """) + + + self._identity_description = "Rigol Oscilloscope WG option IVI function generator driver" + self._identity_supported_instrument_models = ['DS1074Z', 'DS1104Z', 'DS1074ZPlus', + 'DS1104ZPlus', 'MSO1074Z', 'MSO1104Z', 'DS2072A', 'MSO2072A', 'MSO5072', 'MSO5074', + 'DS7014', 'MSO7014', 'MSO8064'] + + self._init_outputs() + + def _init_outputs(self): + try: + super(rigolBaseWG, self)._init_outputs() + except AttributeError: + pass + self._output_name = list() + self._source_name = list() + self._output_operation_mode = list() + self._output_enabled = list() + self._output_impedance = list() + self._output_mode = list() + self._output_reference_clock_source = list() + self._output_standard_waveform_pulse_width = list() + self._output_standard_waveform_ramp_symmetry = list() + self._output_noise_enabled = list() + self._output_noise_percent = list() + for i in range(self._output_count): + if self._output_count == 1: + self._output_name.append("output") + else: + self._output_name.append("output%d" % (i+1)) + self._output_operation_mode.append('continuous') + self._output_enabled.append(False) + self._output_impedance.append(50) + self._output_mode.append('function') + self._output_reference_clock_source.append('internal') + self._output_standard_waveform_pulse_width.append(100e-6) + self._output_standard_waveform_symmetry.append(50.0) + self._output_noise_enabled.append(False) + self._output_noise_percent.append(10.0) + + #create source channels + for i in range(self._output_count): + if self._output_count == 1: + self._source_name.append("source") + else: + self._source_name.append("source%d" % (i+1)) + + self.outputs._set_list(self._output_name) + + + # AFG option + def _get_output_enabled(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s?" % (self._output_name[index])) + self._output_enabled[index] = bool(int(resp)) + self._set_cache_valid(index=index) + return self._output_enabled[index] + + def _set_output_enabled(self, index, value): + index = ivi.get_index(self._output_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s %d" % (self._output_name[index], value)) + self._output_enabled[index] = value + self._set_cache_valid(index=index) + + + def _get_output_impedance(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._output_name[index]) + if val == 'OMEG': + self._output_impedance[index] = "HighZ" + elif val == 'FIFT': + self._output_impedance[index] = "50Ohms" + self._set_cache_valid(index=index) + return self._output_impedance[index] + + def _set_output_impedance(self, index, value): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate: + if value == "HighZ": + self._write(":%s:impedance OMEG" % self._output_name[index]) + elif value == "50Ohms": + self._write(":%s:impedance FIFTY" % self._output_name[index]) + self._output_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_output_settings(self, index): + index = ivi.get_index(self._output_name, index) + self._output_settings = {} + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:apply?" % self._source_name[index]) + settings = resp.split(',') + wtype = settings[0].lower() + wtype = [k for k,v in StandardWaveformMapping.items() if v==wtype][0] + self._output_settings["wtype"] = wtype + self._output_settings["amplitude"] = settings[2] + self._output_settings["offset"] = settings[3] + if wtype != 'noise': + self._output_settings["freq"] = settings[1] + self._output_settings["startphase"] = settings[4] + self._set_cache_valid(index=index) + return self._output_settings + + def _set_output_settings(self, index, wtype, amplitude, offset, freq = "", startphase = ""): + #fix source index + index = ivi.get_index(self._output_name, index) + wtype = StandardWaveformMapping[wtype] + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + if wtype == "NOISE": + self._write(":%s:APPLY:NOISE %s, %s" % (self._source_name[index], amplitude, offset)) + else: + self._write(":%s:APPLY:%s %s, %s, %s, %s" % (self._source_name[index], wtype, freq, amplitude, offset, startphase)) + self._set_cache_valid(index=index) + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_frequency(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:frequency?" % self._source_name[index]) + self._output_standard_waveform_frequency[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_frequency[index] + + def _set_output_standard_waveform_frequency(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.1 or value > 50e6: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:frequency %e" % (self._source_name[index], value)) + self._output_standard_waveform_frequency[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_start_phase(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:phase?" % self._source_name[index]) + self._output_standard_waveform_start_phase[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_start_phase[index] + + def _set_output_standard_waveform_start_phase(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < -180.0 or value > 180.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:phase %e" % (self._source_name[index], value)) + self._output_standard_waveform_start_phase[index] = value + self._set_cache_valid(index=index) + + + def _get_output_standard_waveform_waveform(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function?" % self._source_name[index]).lower() + if resp == 'arbitrary': + resp = 'sine' + resp = [k for k,v in StandardWaveformMapping.items() if v==resp][0] + if resp == 'ramp_up': + if self._get_output_standard_waveform_symmetry(index) <= 10.0: + resp = 'ramp_down' + elif self._get_output_standard_waveform_symmetry(index) >= 90.0: + resp = 'ramp_up' + else: + resp = 'triangle' + self._output_standard_waveform_waveform[index] = resp + self._set_cache_valid(index=index) + return self._output_standard_waveform_waveform[index] + + def _set_output_standard_waveform_waveform(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in StandardWaveformMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:function %s" % (self._source_name[index], StandardWaveformMapping[value])) + if value == 'triangle': + if self._get_output_standard_waveform_symmetry(index) <= 10.0 or self._get_output_standard_waveform_symmetry(index) >= 90: + self._set_output_standard_waveform_symmetry(index, 50.0) + elif value == 'ramp_up': + self._set_output_standard_waveform_symmetry(index, 100.0) + elif value == 'ramp_down': + self._set_output_standard_waveform_symmetry(index, 0.0) + self._output_standard_waveform_waveform[index] = value + self._set_cache_valid(index=index) + self._output_mode[index] = 'function' + self._set_cache_valid(True, 'output_mode', index=index) + + def _get_output_standard_waveform_symmetry(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function:ramp:symmetry?" % self._source_name[index]) + self._output_standard_waveform_symmetry[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_symmetry[index] + + def _set_output_standard_waveform_symmetry(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.0 or value > 100.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:function:ramp:symmetry %e" % (self._source_name[index], value)) + self._output_standard_waveform_symmetry[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_amplitude(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage?" % self._source_name[index]) + self._output_standard_waveform_amplitude[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_amplitude[index] + + def _set_output_standard_waveform_amplitude(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.01 or value > 5.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:voltage %e" % (self._source_name[index], value)) + self._output_standard_waveform_amplitude[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_dc_offset(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage:offset?" % self._source_name[index]) + self._output_standard_waveform_dc_offset[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_dc_offset[index] + + def _set_output_standard_waveform_dc_offset(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:voltage:offset %e" % (self._source_name[index], value)) + self._output_standard_waveform_dc_offset[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_duty_cycle_high(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:pulse:dcycle?" % self._source_name[index]) + self._output_standard_waveform_duty_cycle_high[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_duty_cycle_high[index] + + def _set_output_standard_waveform_duty_cycle_high(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + print value + if value < 10.0 or value > 90.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:pulse:dcycle %e" % (self._source_name[index], value)) + self._output_standard_waveform_duty_cycle_high[index] = value + self._set_cache_valid(index=index) diff --git a/ivi/rigol/rigolDM3068Agilent.py b/ivi/rigol/rigolDM3068Agilent.py index 01a7df28..29d515b1 100644 --- a/ivi/rigol/rigolDM3068Agilent.py +++ b/ivi/rigol/rigolDM3068Agilent.py @@ -2,8 +2,8 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2012-2016 Alex Forencich -Copyright (c) 2015-2016 Rikard Lindstrom +Copyright (c) 2012-2017 Alex Forencich +Copyright (c) 2015-2017 Rikard Lindstrom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -318,4 +318,4 @@ def _set_thermistor_resistance(self, value): self._write("temp:tran:ther:type %g" % value) value = float(value) - self._thermistor_resistance = value + self._thermistor_resistance = value \ No newline at end of file diff --git a/ivi/rigol/rigolDP1000.py b/ivi/rigol/rigolDP1000.py index 83a2f352..5cd0e305 100644 --- a/ivi/rigol/rigolDP1000.py +++ b/ivi/rigol/rigolDP1000.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -66,3 +66,4 @@ def __init__(self, *args, **kwargs): + diff --git a/ivi/rigol/rigolDP1116A.py b/ivi/rigol/rigolDP1116A.py index 8491ea11..de3c174b 100644 --- a/ivi/rigol/rigolDP1116A.py +++ b/ivi/rigol/rigolDP1116A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -49,6 +49,4 @@ def __init__(self, *args, **kwargs): } ] - self._init_outputs() - - + self._init_outputs() \ No newline at end of file diff --git a/ivi/rigol/rigolDP1308A.py b/ivi/rigol/rigolDP1308A.py index 6cab72e9..f68cd095 100644 --- a/ivi/rigol/rigolDP1308A.py +++ b/ivi/rigol/rigolDP1308A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDP800.py b/ivi/rigol/rigolDP800.py index cfa33a6e..f95a6ffb 100644 --- a/ivi/rigol/rigolDP800.py +++ b/ivi/rigol/rigolDP800.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ivi/rigol/rigolDP831A.py b/ivi/rigol/rigolDP831A.py index 839663ad..c2d11209 100644 --- a/ivi/rigol/rigolDP831A.py +++ b/ivi/rigol/rigolDP831A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDP832.py b/ivi/rigol/rigolDP832.py index 7e68b690..4638443c 100644 --- a/ivi/rigol/rigolDP832.py +++ b/ivi/rigol/rigolDP832.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDP832A.py b/ivi/rigol/rigolDP832A.py index b043d2c1..9380500a 100644 --- a/ivi/rigol/rigolDP832A.py +++ b/ivi/rigol/rigolDP832A.py @@ -2,7 +2,7 @@ Python Interchangeable Virtual Instrument Library -Copyright (c) 2013-2016 Alex Forencich +Copyright (c) 2013-2017 Alex Forencich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,4 +68,4 @@ def __init__(self, *args, **kwargs): self._init_outputs() - + \ No newline at end of file diff --git a/ivi/rigol/rigolDS1000Z.py b/ivi/rigol/rigolDS1000Z.py new file mode 100644 index 00000000..7312b9e5 --- /dev/null +++ b/ivi/rigol/rigolDS1000Z.py @@ -0,0 +1,54 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * +from .rigolDSSource import * + +class rigolDS1000Z(rigolBaseScope, rigolDSSource): + "Rigol DS1000Z series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS1000Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + self._max_averages = 1024 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + # Internal source + self._output_count = 2 + + self._identity_description = "Rigol DS1000Z series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS1054Z', 'DS1074Z', 'DS1074ZPlus', 'DS1104Z', 'MSO1074Z', 'MSO1104Z'] + + self._init_channels() + self._init_outputs() diff --git a/ivi/rigol/rigolDS1054Z.py b/ivi/rigol/rigolDS1054Z.py new file mode 100644 index 00000000..629f6655 --- /dev/null +++ b/ivi/rigol/rigolDS1054Z.py @@ -0,0 +1,45 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS1000Z import * + +class rigolDS1054Z(rigolDS1000Z): + "Rigol DS1054Z IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1054Z') + + super(rigolDS1054Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 50e6 + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._init_channels() + diff --git a/ivi/rigol/rigolDS1074Z.py b/ivi/rigol/rigolDS1074Z.py new file mode 100644 index 00000000..34a286e4 --- /dev/null +++ b/ivi/rigol/rigolDS1074Z.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS1000Z import * + +class rigolDS1074Z(rigolDS1000Z): + "Rigol DS1074Z IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1074Z') + + super(rigolDS1074Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/rigol/rigolDS1074ZPlus.py b/ivi/rigol/rigolDS1074ZPlus.py new file mode 100644 index 00000000..b35e00de --- /dev/null +++ b/ivi/rigol/rigolDS1074ZPlus.py @@ -0,0 +1,42 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS1000Z import * + +class rigolDS1074ZPlus(rigolDS1000Z): + "Rigol DS1074ZPlus IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1074ZPlus') + + super(rigolDS1074ZPlus, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + + self._init_channels() diff --git a/ivi/rigol/rigolDS1104Z.py b/ivi/rigol/rigolDS1104Z.py new file mode 100644 index 00000000..bfc07376 --- /dev/null +++ b/ivi/rigol/rigolDS1104Z.py @@ -0,0 +1,44 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS1000Z import * + +class rigolDS1104Z(rigolDS1000Z): + "Rigol DS1104Z IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS1104Z') + + super(rigolDS1104Z, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + + self._init_channels() + + \ No newline at end of file diff --git a/ivi/rigol/rigolDS2000.py b/ivi/rigol/rigolDS2000.py new file mode 100644 index 00000000..fcef2a5f --- /dev/null +++ b/ivi/rigol/rigolDS2000.py @@ -0,0 +1,120 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * +from .rigolDSSource import * + +ScreenshotImageFormatMapping = { + 'bmp': 'bmp', + 'bmp24': 'bmp24'} + +class rigolDS2000A(rigolBaseScope, rigolDSSource): + "Rigol DS2000A series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + super(rigolDS2000A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 14 + self._vertical_divisions = 8 + + # Internal source + self._output_count = 2 + + self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping + + self._identity_description = "Rigol DS2000A series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS2074A', 'DS2104A', 'DS2204A', + 'DS2304A', 'MSO2074A', 'MSO2104A', 'MSO2204A', 'MSO2304A'] + + self._init_channels() + self._init_outputs() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') + + def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): + print 'index is ', index + index = ivi.get_index(self._channel_name, index) + if index < self._analog_channel_count: + if measurement_function not in MeasurementFunctionMapping: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMapping[measurement_function] + else: + if measurement_function not in MeasurementFunctionMappingDigital: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMappingDigital[measurement_function] + if not self._driver_operation_simulate: + # l = func.split(' ') + # l[0] = l[0] + '?' + # if len(l) > 1: + # l[-1] = l[-1] + ',' + # func = ' '.join(l) + query = ":measure:%s? %s" % (func, self._channel_name[index]) + print 'query is ', query + if measurement_function in ['phase', 'delay']: + ref_index = ivi.get_index(self._channel_name, ref_channel) + query += ", %s" % self._channel_name[ref_index] + meas = self._ask(query) + if meas == 'measure error!': + return meas + else: + return float(meas) + return 0 \ No newline at end of file diff --git a/ivi/rigol/rigolDS2000A.py b/ivi/rigol/rigolDS2000A.py new file mode 100644 index 00000000..19cc900e --- /dev/null +++ b/ivi/rigol/rigolDS2000A.py @@ -0,0 +1,118 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * +from .rigolDSSource import * + +ScreenshotImageFormatMapping = { + 'bmp': 'bmp', + 'bmp24': 'bmp24'} + +class rigolDS2000A(rigolBaseScope, rigolDSSource): + "Rigol DS2000A series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + super(rigolDS2000A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 16 + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 14 + self._vertical_divisions = 8 + + # Internal source + self._output_count = 2 + + self._display_screenshot_image_format_mapping = ScreenshotImageFormatMapping + + self._identity_description = "Rigol DS2000A series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS2074A', 'DS2104A', 'DS2204A', + 'DS2304A', 'MSO2074A', 'MSO2104A', 'MSO2204A', 'MSO2304A'] + + self._init_channels() + self._init_outputs() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') + + def _measurement_fetch_waveform_measurement(self, index, measurement_function, ref_channel = None): + index = ivi.get_index(self._channel_name, index) + if index < self._analog_channel_count: + if measurement_function not in MeasurementFunctionMapping: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMapping[measurement_function] + else: + if measurement_function not in MeasurementFunctionMappingDigital: + raise ivi.ValueNotSupportedException() + func = MeasurementFunctionMappingDigital[measurement_function] + if not self._driver_operation_simulate: + # l = func.split(' ') + # l[0] = l[0] + '?' + # if len(l) > 1: + # l[-1] = l[-1] + ',' + # func = ' '.join(l) + query = ":measure:%s? %s" % (func, self._channel_name[index]) + if measurement_function in ['phase', 'delay']: + ref_index = ivi.get_index(self._channel_name, ref_channel) + query += ", %s" % self._channel_name[ref_index] + meas = self._ask(query) + if meas == 'measure error!': + return meas + else: + return float(meas) + return 0 \ No newline at end of file diff --git a/ivi/rigol/rigolDS2072A.py b/ivi/rigol/rigolDS2072A.py new file mode 100644 index 00000000..0dc000c3 --- /dev/null +++ b/ivi/rigol/rigolDS2072A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS2000A import * + +class rigolDS2072A(rigolDS2000A): + "Rigol DS2072A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2072A') + + super(rigolDS2072A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 70e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS2102A.py b/ivi/rigol/rigolDS2102A.py new file mode 100644 index 00000000..9a0519e8 --- /dev/null +++ b/ivi/rigol/rigolDS2102A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS2000A import * + +class rigolDS2102A(rigolDS2000A): + "Rigol DS2102A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2102A') + + super(rigolDS2102A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS2202A.py b/ivi/rigol/rigolDS2202A.py new file mode 100644 index 00000000..e1d72644 --- /dev/null +++ b/ivi/rigol/rigolDS2202A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS2000A import * + +class rigolDS2202A(rigolDS2000A): + "Rigol DS2202A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2202A') + + super(rigolDS2202A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS2302A.py b/ivi/rigol/rigolDS2302A.py new file mode 100644 index 00000000..3b3567fd --- /dev/null +++ b/ivi/rigol/rigolDS2302A.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS2000A import * + +class rigolDS2302A(rigolDS2000A): + "Rigol DS2302A IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS2302A') + + super(rigolDS2302A, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4000.py b/ivi/rigol/rigolDS4000.py new file mode 100644 index 00000000..ff9f7f9d --- /dev/null +++ b/ivi/rigol/rigolDS4000.py @@ -0,0 +1,107 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * + +class rigolDS4000(rigolBaseScope): + "Rigol DS4000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS4000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS4000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS4012', 'DS4014', 'DS4022', + 'DS4024', 'DS4032', 'DS4034', 'DS4052', 'DS4054', 'MSO4012', 'MSO4014', + 'MSO4022', 'MSO4024', 'MSO4032', 'MSO4034', 'MSO4052', 'MSO4054'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS4012.py b/ivi/rigol/rigolDS4012.py new file mode 100644 index 00000000..be3cbfe7 --- /dev/null +++ b/ivi/rigol/rigolDS4012.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4012(rigolDS4000): + "Rigol DS4012 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4012') + + super(rigolDS4012, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4014.py b/ivi/rigol/rigolDS4014.py new file mode 100644 index 00000000..f22e94c6 --- /dev/null +++ b/ivi/rigol/rigolDS4014.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4014(rigolDS4000): + "Rigol DS4014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4014') + + super(rigolDS4014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4022.py b/ivi/rigol/rigolDS4022.py new file mode 100644 index 00000000..0a3a86f4 --- /dev/null +++ b/ivi/rigol/rigolDS4022.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4022(rigolDS4000): + "Rigol DS4022 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4022') + + super(rigolDS4022, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4024.py b/ivi/rigol/rigolDS4024.py new file mode 100644 index 00000000..72065b85 --- /dev/null +++ b/ivi/rigol/rigolDS4024.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4024(rigolDS4000): + "Rigol DS4024 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4024') + + super(rigolDS4024, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 200e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4032.py b/ivi/rigol/rigolDS4032.py new file mode 100644 index 00000000..ef26bb49 --- /dev/null +++ b/ivi/rigol/rigolDS4032.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4032(rigolDS4000): + "Rigol DS4032 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4032') + + super(rigolDS4032, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4034.py b/ivi/rigol/rigolDS4034.py new file mode 100644 index 00000000..1e2ac60f --- /dev/null +++ b/ivi/rigol/rigolDS4034.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4034(rigolDS4000): + "Rigol DS4034 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4034') + + super(rigolDS4034, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 300e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4052.py b/ivi/rigol/rigolDS4052.py new file mode 100644 index 00000000..9ede3e23 --- /dev/null +++ b/ivi/rigol/rigolDS4052.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4052(rigolDS4000): + "Rigol DS4052 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4052') + + super(rigolDS4052, self).__init__(*args, **kwargs) + + self._analog_channel_count = 2 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS4054.py b/ivi/rigol/rigolDS4054.py new file mode 100644 index 00000000..b20bfc95 --- /dev/null +++ b/ivi/rigol/rigolDS4054.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS4000 import * + +class rigolDS4054(rigolDS4000): + "Rigol DS4054 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS4054') + + super(rigolDS4054, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS5000.py b/ivi/rigol/rigolDS5000.py new file mode 100644 index 00000000..0cebdc24 --- /dev/null +++ b/ivi/rigol/rigolDS5000.py @@ -0,0 +1,110 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * +from .rigolBaseWG import * + + +class rigolDS5000(rigolBaseScope, rigolBaseWG): + "Rigol DS5000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS5000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS5000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO5072', 'MSO5074', 'MSO5102', + 'MSO5104', 'MSO5204', 'MSO5354'] + + self._init_channels() + self._init_outputs() + + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS7000.py b/ivi/rigol/rigolDS7000.py new file mode 100644 index 00000000..128ae611 --- /dev/null +++ b/ivi/rigol/rigolDS7000.py @@ -0,0 +1,105 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * + +class rigolDS7000(rigolBaseScope): + "Rigol DS7000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS7000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS7000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['DS7014', 'MSO7014'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDS7014.py b/ivi/rigol/rigolDS7014.py new file mode 100644 index 00000000..5db1cad7 --- /dev/null +++ b/ivi/rigol/rigolDS7014.py @@ -0,0 +1,43 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolDS7000 import * + +class rigolDS7014(rigolDS7000): + "Rigol DS7014 IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', 'DS7014') + + super(rigolDS7014, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 0 + self._channel_count = self._analog_channel_count + self._digital_channel_count + self._bandwidth = 100e6 + self._bandwidth_limit = {'20M': 20e6} + + self._init_channels() diff --git a/ivi/rigol/rigolDS8000.py b/ivi/rigol/rigolDS8000.py new file mode 100644 index 00000000..03e39acb --- /dev/null +++ b/ivi/rigol/rigolDS8000.py @@ -0,0 +1,105 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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 .rigolBaseScope import * + +class rigolDS8000(rigolBaseScope): + "Rigol DS8000 series IVI oscilloscope driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + super(rigolDS8000, self).__init__(*args, **kwargs) + + self._analog_channel_count = 4 + self._digital_channel_count = 16 + self._bandwidth = 500e6 + self._bandwidth_limit = {'20M': 20e6, '100M': 100e6} + self._max_averages = 8192 + + self._horizontal_divisions = 12 + self._vertical_divisions = 8 + + self._identity_description = "Rigol DS8000 series IVI oscilloscope driver" + self._identity_supported_instrument_models = ['MSO8064'] + + self._init_channels() + + def _display_fetch_screenshot(self, format='bmp', invert=False): + if self._driver_operation_simulate: + return b'' + + if format not in self._display_screenshot_image_format_mapping: + raise ivi.ValueNotSupportedException() + + self._write(":display:data?") + + data = self._read_raw() + + return ivi.decode_ieee_block(data) + + def _get_channel_input_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:impedance?" % self._channel_name[index]) + if val == 'OMEG': + self._channel_input_impedance[index] = 1000000 + elif val == 'FIFT': + self._channel_input_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._channel_input_impedance[index] + + def _set_channel_input_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:impedance omeg" % self._channel_name[index]) + elif value == 50: + self._write(":%s:impedance fifty" % self._channel_name[index]) + self._channel_input_impedance[index] = value + self._set_cache_valid(index=index) + + def _get_channel_probe_attenuation(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + self._channel_probe_attenuation[index] = float(self._ask(":%s:probe?" % self._channel_name[index])) + self._set_cache_valid(index=index) + return self._channel_probe_attenuation[index] + + def _set_channel_probe_attenuation(self, index, value): + index = ivi.get_index(self._analog_channel_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:probe %s" % (self._channel_name[index], ("%f" %value).rstrip('0').rstrip('.'))) + self._channel_probe_attenuation[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'channel_offset', index) + self._set_cache_valid(False, 'channel_scale', index) + self._set_cache_valid(False, 'channel_range', index) + self._set_cache_valid(False, 'trigger_level') diff --git a/ivi/rigol/rigolDSSource.py b/ivi/rigol/rigolDSSource.py new file mode 100644 index 00000000..aac0c549 --- /dev/null +++ b/ivi/rigol/rigolDSSource.py @@ -0,0 +1,435 @@ +""" + +Python Interchangeable Virtual Instrument Library + +Copyright (c) 2017 Alex Forencich + +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. + +""" + +import numpy as np +import struct + +from .. import ivi +from .. import fgen + +OutputMode = set(['function', 'arbitrary']) +OperationMode = set(['continuous']) +StandardWaveformMapping = { + 'sine': 'sin', + 'square': 'squ', + 'triangle': 'ramp', + 'ramp_up': 'ramp', + 'ramp_down': 'ramp', + 'dc': 'dc', + 'pulse': 'puls', + 'noise': 'nois', + 'sinc': 'sinc', + 'exprise': 'expr', + 'expfall': 'expf', + 'cardiac': 'ecg', + 'gaussian': 'gaus', + 'lorentz': 'lor', + 'haversine': 'hav' + } + +class rigolDSSource(fgen.Base, fgen.StdFunc, fgen.ArbWfm, fgen.ArbFrequency, + fgen.ArbChannelWfm): + "Rigol DSO internal source IVI function generator driver" + + def __init__(self, *args, **kwargs): + self.__dict__.setdefault('_instrument_id', '') + + self._output_standard_waveform_symmetry = list() + + super(rigolDSSource, self).__init__(*args, **kwargs) + + # Internal source + self._output_count = 2 + self._arbitrary_sample_rate = 0 + self._arbitrary_waveform_number_waveforms_max = 0 + self._arbitrary_waveform_size_max = 16384 + self._arbitrary_waveform_size_min = 2 + self._arbitrary_waveform_quantum = 1 + + self._add_property('outputs[].standard_waveform.symmetry', + self._get_output_standard_waveform_symmetry, + self._set_output_standard_waveform_symmetry, + None, + """ + Specifies the symmetry for a ramp or triangle waveform. This attribute + affects function generator behavior only when the Waveform attribute is + set to Waveform Triangle, Ramp Up, or Ramp Down. The value is expressed + as a percentage. + """) + + self._identity_description = "Rigol DSO internal source IVI function generator driver" + self._identity_supported_instrument_models = [] + + self._init_outputs() + + def _init_outputs(self): + try: + super(rigolDSSource, self)._init_outputs() + except AttributeError: + pass + self._output_name = list() + self._output_operation_mode = list() + self._output_enabled = list() + self._output_impedance = list() + self._output_mode = list() + self._output_reference_clock_source = list() + self._output_standard_waveform_ramp_symmetry = list() + for i in range(self._output_count): + self._output_name.append("source%d" % (i+1)) + self._output_operation_mode.append('continuous') + self._output_enabled.append(False) + self._output_impedance.append(50) + self._output_mode.append('function') + self._output_reference_clock_source.append('internal') + self._output_standard_waveform_symmetry.append(50.0) + + self.outputs._set_list(self._output_name) + + # AFG option + def _get_output_operation_mode(self, index): + index = ivi.get_index(self._output_name, index) + return self._output_operation_mode[index] + + def _set_output_operation_mode(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in OperationMode: + raise ivi.ValueNotSupportedException() + self._output_operation_mode[index] = value + + def _get_output_enabled(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:output:state?" % self._output_name[index]) + self._output_enabled[index] = resp == 'ON' + self._set_cache_valid(index=index) + return self._output_enabled[index] + + def _set_output_enabled(self, index, value): + index = ivi.get_index(self._output_name, index) + value = bool(value) + if not self._driver_operation_simulate: + self._write(":%s:output:state %d" % (self._output_name[index], value)) + self._output_enabled[index] = value + self._set_cache_valid(index=index) + + def _get_output_impedance(self, index): + index = ivi.get_index(self._analog_channel_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + val = self._ask(":%s:output:impedance?" % self._output_name[index]) + if val == 'HIGHZ': + self._output_impedance[index] = 1000000 + elif val == 'FIF': + self._output_impedance[index] = 50 + self._set_cache_valid(index=index) + return self._output_impedance[index] + + def _set_output_impedance(self, index, value): + value = float(value) + index = ivi.get_index(self._analog_channel_name, index) + if value != 50 and value != 1000000: + raise Exception('Invalid impedance selection') + if not self._driver_operation_simulate: + if value == 1000000: + self._write(":%s:output:impedance highz" % self._output_name[index]) + elif value == 50: + self._write(":%s:output:impedance fifty" % self._output_name[index]) + self._output_impedance[index] = value + self._set_cache_valid(index=index) + self._set_cache_valid(False, 'output_standard_waveform_amplitude', index) + + def _get_output_mode(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function?" % self._output_name[index]).lower() + if resp == 'ext': + self._output_mode[index] = 'arbitrary' + else: + self._output_mode[index] = 'function' + self._set_cache_valid(index=index) + return self._output_mode[index] + + def _set_output_mode(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in OutputMode: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + if value == 'arbitrary': + self._write(":%s:function ext" % self._output_name[index]) + else: + if self._get_cache_valid('output_standard_waveform_waveform', index=index): + self._set_output_standard_waveform_waveform(index, self._output_standard_waveform_waveform[index]) + else: + self._set_output_standard_waveform_waveform(index, 'sine') + self._output_mode[index] = value + self._set_cache_valid(index=index) + + def _get_output_reference_clock_source(self, index): + index = ivi.get_index(self._output_name, index) + return self._output_reference_clock_source[index] + + def _set_output_reference_clock_source(self, index, value): + index = ivi.get_index(self._output_name, index) + value = 'internal' + self._output_reference_clock_source[index] = value + + def abort_generation(self): + pass + + def initiate_generation(self): + pass + + def _get_output_standard_waveform_amplitude(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage:amplitude?" % self._output_name[index]) + self._output_standard_waveform_amplitude[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_amplitude[index] + + def _set_output_standard_waveform_amplitude(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.01 or value > 5.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:voltage:amplitude %e" % (self._output_name[index], value)) + self._output_standard_waveform_amplitude[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_dc_offset(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:voltage:offset?" % self._output_name[index]) + self._output_standard_waveform_dc_offset[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_dc_offset[index] + + def _set_output_standard_waveform_dc_offset(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if not self._driver_operation_simulate: + self._write(":%s:voltage:offset %e" % (self._output_name[index], value)) + self._output_standard_waveform_dc_offset[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_duty_cycle_high(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:pulse:dcycle?" % self._output_name[index]) + self._output_standard_waveform_duty_cycle_high[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_duty_cycle_high[index] + + def _set_output_standard_waveform_duty_cycle_high(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 10.0 or value > 90.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:pulse:dcycle %e" % (self._output_name[index], value)) + self._output_standard_waveform_duty_cycle_high[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_symmetry(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function:ramp:symmetry?" % self._output_name[index]) + self._output_standard_waveform_symmetry[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_symmetry[index] + + def _set_output_standard_waveform_symmetry(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.0 or value > 100.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:function:ramp:symmetry %e" % (self._output_name[index], value)) + self._output_standard_waveform_symmetry[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_start_phase(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:phase?" % self._output_name[index]) + self._output_standard_waveform_start_phase[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_start_phase[index] + + def _set_output_standard_waveform_start_phase(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) % 360 + if value < 0 or value > 360.0: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:phase %e" % (self._output_name[index], value)) + self._output_standard_waveform_start_phase[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_frequency(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:frequency?" % self._output_name[index]) + self._output_standard_waveform_frequency[index] = float(resp) + self._set_cache_valid(index=index) + return self._output_standard_waveform_frequency[index] + + def _set_output_standard_waveform_frequency(self, index, value): + index = ivi.get_index(self._output_name, index) + value = float(value) + if value < 0.1 or value > 25e6: + raise ivi.OutOfRangeException() + if not self._driver_operation_simulate: + self._write(":%s:frequency %e" % (self._output_name[index], value)) + self._output_standard_waveform_frequency[index] = value + self._set_cache_valid(index=index) + + def _get_output_standard_waveform_waveform(self, index): + index = ivi.get_index(self._output_name, index) + if not self._driver_operation_simulate and not self._get_cache_valid(index=index): + resp = self._ask(":%s:function?" % self._output_name[index]).lower() + if resp == 'arbitrary': + resp = 'sine' + resp = [k for k,v in StandardWaveformMapping.items() if v==resp][0] + if resp == 'ramp_up': + if self._get_output_standard_waveform_symmetry(index) <= 10.0: + resp = 'ramp_down' + elif self._get_output_standard_waveform_symmetry(index) >= 90.0: + resp = 'ramp_up' + else: + resp = 'triangle' + self._output_standard_waveform_waveform[index] = resp + self._set_cache_valid(index=index) + return self._output_standard_waveform_waveform[index] + + def _set_output_standard_waveform_waveform(self, index, value): + index = ivi.get_index(self._output_name, index) + if value not in StandardWaveformMapping: + raise ivi.ValueNotSupportedException() + if not self._driver_operation_simulate: + self._write(":%s:function %s" % (self._output_name[index], StandardWaveformMapping[value])) + if value == 'triangle': + if self._get_output_standard_waveform_symmetry(index) <= 10.0 or self._get_output_standard_waveform_symmetry(index) >= 90: + self._set_output_standard_waveform_symmetry(index, 50.0) + elif value == 'ramp_up': + self._set_output_standard_waveform_symmetry(index, 100.0) + elif value == 'ramp_down': + self._set_output_standard_waveform_symmetry(index, 0.0) + self._output_standard_waveform_waveform[index] = value + self._set_cache_valid(index=index) + self._output_mode[index] = 'function' + self._set_cache_valid(True, 'output_mode', index=index) + + def _get_output_arbitrary_gain(self, index): + return self._get_output_standard_waveform_amplitude(index) + + def _set_output_arbitrary_gain(self, index, value): + self._set_output_standard_waveform_amplitude(index, value) + + def _get_output_arbitrary_offset(self, index): + return self._get_output_standard_waveform_dc_offset(index) + + def _set_output_arbitrary_offset(self, index, value): + self._set_output_standard_waveform_dc_offset(index, value) + + def _get_output_arbitrary_waveform(self, index): + index = ivi.get_index(self._output_name, index) + return self._output_arbitrary_waveform[index] + + def _set_output_arbitrary_waveform(self, index, value): + index = ivi.get_index(self._output_name, index) + value = str(value) + self._output_arbitrary_waveform[index] = value + + def _get_arbitrary_sample_rate(self): + return self._arbitrary_sample_rate + + def _set_arbitrary_sample_rate(self, value): + value = float(value) + self._arbitrary_sample_rate = value + + def _get_arbitrary_waveform_number_waveforms_max(self): + return self._arbitrary_waveform_number_waveforms_max + + def _get_arbitrary_waveform_size_max(self): + return self._arbitrary_waveform_size_max + + def _get_arbitrary_waveform_size_min(self): + return self._arbitrary_waveform_size_min + + def _get_arbitrary_waveform_quantum(self): + return self._arbitrary_waveform_quantum + + def _arbitrary_waveform_clear(self, handle): + pass + + def _arbitrary_waveform_configure(self, index, handle, gain, offset): + self._set_output_arbitrary_waveform(index, handle) + self._set_output_arbitrary_gain(index, gain) + self._set_output_arbitrary_offset(index, offset) + + def _arbitrary_waveform_create(self, data): + return "handle" + + def _get_output_arbitrary_frequency(self, index): + return self._get_output_standard_waveform_frequency(index) + + def _set_output_arbitrary_frequency(self, index, value): + self._set_output_standard_waveform_frequency(index, value) + + def _arbitrary_waveform_create_channel_waveform(self, index, data): + y = None + x = None + if type(data) == list and type(data[0]) == float: + # list + y = array(data) + elif type(data) == np.ndarray and len(data.shape) == 1: + # 1D array + y = data + elif type(data) == np.ndarray and len(data.shape) == 2 and data.shape[0] == 1: + # 2D array, hieght 1 + y = data[0] + elif type(data) == np.ndarray and len(data.shape) == 2 and data.shape[1] == 1: + # 2D array, width 1 + y = data[:,0] + else: + x, y = ivi.get_sig(data) + + if len(y) % self._arbitrary_waveform_quantum != 0: + raise ivi.ValueNotSupportedException() + + # clip on [-1,1] and rescale to [0,1] + yc = (y.clip(-1, 1)+1)/2 + + # scale to 14 bits + yb = np.rint(yc * ((1 << 14)-1)).astype(int) & 0x00003fff + + raw_data = yb.astype('