def test_convert_legacy_version_to_supported_version(): legacy_verstr = "a.1.4" assert ii.convert_legacy_version_to_supported_version(legacy_verstr) == "65.1.4" legacy_verstr = "10.4.7" assert ii.convert_legacy_version_to_supported_version(legacy_verstr) == "10.4.7" legacy_verstr = "C.2.1" assert ii.convert_legacy_version_to_supported_version(legacy_verstr) == "67.2.1"
def __init__(self, name: str, address: str, terminator: str = '\n', **kwargs: Any): """ Create an instance of the instrument. Args: name: Name of the instrument instance address: Visa-resolvable instrument address. """ super().__init__(name, address, terminator=terminator, **kwargs) idn = self.IDN.get() self.has_firmware_a_02_10_or_above = version.parse( convert_legacy_version_to_supported_version( idn["firmware"])) >= version.parse( convert_legacy_version_to_supported_version("A.02.10")) self.has_option_001 = '001' in self._options() self._dc_bias_v_level_range: Union[Numbers, Enum] if self.has_option_001: self._v_level_range = Numbers(0, 20) self._i_level_range = Numbers(0, 0.1) self._imp_range = Enum(0.1, 1, 10, 100, 300, 1000, 3000, 10000, 30000, 100000) self._dc_bias_v_level_range = Numbers(-40, 40) else: self._v_level_range = Numbers(0, 2) self._i_level_range = Numbers(0, 0.02) self._imp_range = Enum(1, 10, 100, 300, 1000, 3000, 10000, 30000, 100000) self._dc_bias_v_level_range = Enum(0, 1.5, 2) self._measurement_pair = MeasurementPair( "CPD", ("capacitance", "dissipation_factor"), ("F", "")) self.add_parameter( "frequency", get_cmd=":FREQuency?", set_cmd=":FREQuency {}", get_parser=float, unit="Hz", vals=Numbers(20, 2E6), docstring="Gets and sets the frequency for normal measurement.") self.add_parameter( "current_level", get_cmd=self._get_current_level, set_cmd=self._set_current_level, unit="A", vals=self._i_level_range, docstring="Gets and sets the current level for measurement signal." ) self.add_parameter( "voltage_level", get_cmd=self._get_voltage_level, set_cmd=self._set_voltage_level, unit="V", vals=self._v_level_range, docstring="Gets and sets the AC bias voltage level for measurement " "signal.") self.add_parameter("measurement_function", get_cmd=":FUNCtion:IMPedance?", set_cmd=self._set_measurement) self.add_parameter( "range", get_cmd=":FUNCtion:IMPedance:RANGe?", set_cmd=self._set_range, unit='Ohm', vals=self._imp_range, docstring="Selects the impedance measurement range, also turns " "the auto range function OFF.") self.add_parameter( "imp_autorange_enabled", get_cmd=":FUNCtion:IMPedance:RANGe:AUTO?", set_cmd=":FUNCtion:IMPedance:RANGe:AUTO {}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="Enables the auto-range for impedance measurement.") self.add_parameter( "dc_bias_enabled", get_cmd=":BIAS:STATe?", set_cmd=":BIAS:STATe {}", vals=Bool(), val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="Enables DC bias. DC bias is automatically turned " "off after recalling the state from memory.") self.add_parameter( "dc_bias_voltage_level", get_cmd=":BIAS:VOLTage:LEVel?", set_cmd=":BIAS:VOLTage:LEVel {}", get_parser=float, unit="V", vals=self._dc_bias_v_level_range, docstring="Sets the DC bias voltage. Setting does not " "implicitly turn the DC bias ON.") self.add_parameter("meas_time_mode", val_mapping={ "short": "SHOR", "medium": "MED", "long": "LONG" }, parameter_class=GroupParameter) self.add_parameter("averaging_rate", vals=Ints(1, 256), parameter_class=GroupParameter, get_parser=int, docstring="Averaging rate for the measurement.") self._aperture_group = Group( [self.meas_time_mode, self.averaging_rate], set_cmd=":APERture {meas_time_mode},{averaging_rate}", get_cmd=":APERture?") if self.has_firmware_a_02_10_or_above: self.add_parameter( "dc_bias_autorange_enabled", get_cmd=":BIAS:RANGe:AUTO?", set_cmd=":BIAS:RANGe:AUTO {}", vals=Bool(), val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="Enables DC Bias range AUTO setting. When DC bias " "range is fixed (not AUTO), '#' is displayed in " "the BIAS field of the display.") self.add_parameter( "signal_mode", initial_value=None, vals=Enum("Voltage", "Current", None), parameter_class=ManualParameter, docstring="This parameter tracks the signal mode which is being " "set.") self.add_submodule("_correction", Correction4980A(self, "correction")) self._set_signal_mode_on_driver_initialization() self.connect_message()
def __init__(self, name: str, address: str, silent: bool = False, **kwargs: Any): """ Create an instance of the instrument. Args: name: Name used by QCoDeS. Appears in the DataSet address: Visa-resolvable instrument address. silent: If True, the connect_message of the instrument is supressed. Default: False """ super().__init__(name, address, terminator='\n', **kwargs) idn = self.IDN.get() self.model = idn['model'] self.is_34465A_34470A = self.model in ['34465A', '34470A'] self.is_34410A_34411A = self.model in ['34410A', '34411A'] #################################### # Instrument specifications options = self._options() self.has_DIG = self.is_34465A_34470A and ( "DIG" in options or version.parse(convert_legacy_version_to_supported_version("A.03")) <= version.parse( convert_legacy_version_to_supported_version(idn["firmware"]))) # Note that the firmware version check is still needed because # ``_options`` (the ``*OPT?`` command) returns 'DIG' option for # firmware 3.0 only if it has been purchased before self.has_MEM = self.is_34465A_34470A and 'MEM' in options PLCs = { '34410A': [0.006, 0.02, 0.06, 0.2, 1, 2, 10, 100], '34411A': [0.001, 0.002, 0.006, 0.02, 0.06, 0.2, 1, 2, 10, 100], '34460A': [0.02, 0.2, 1, 10, 100], '34461A': [0.02, 0.2, 1, 10, 100], '34465A': [0.02, 0.06, 0.2, 1, 10, 100], '34470A': [0.02, 0.06, 0.2, 1, 10, 100] } if self.has_DIG: PLCs['34465A'] = [0.001, 0.002, 0.006] + PLCs['34465A'] PLCs['34470A'] = [0.001, 0.002, 0.006] + PLCs['34470A'] # The resolution factor order matches the order of PLCs res_factors = { '34410A': [6e-6, 3e-6, 1.5e-6, 0.7e-6, 0.3e-6, 0.2e-6, 0.1e-6, 0.03e-6], '34411A': [ 30e-6, 15e-5, 6e-6, 3e-6, 1.5e-6, 0.7e-6, 0.3e-6, 0.2e-6, 0.1e-6, 0.03e-6 ], '34460A': [300e-6, 100e-6, 30e-6, 10e-6, 3e-6], '34461A': [100e-6, 10e-6, 3e-6, 1e-6, 0.3e-6], '34465A': [3e-6, 1.5e-6, 0.7e-6, 0.3e-6, 0.1e-6, 0.03e-6], '34470A': [1e-6, 0.5e-6, 0.3e-6, 0.1e-6, 0.03e-6, 0.01e-6] } if self.has_DIG: res_factors['34465A'] = [30e-6, 15e-6, 6e-6 ] + res_factors['34465A'] res_factors['34470A'] = [30e-6, 10e-6, 3e-6 ] + res_factors['34470A'] self._resolution_factors = res_factors[self.model] self.ranges = [10**n for n in range(-1, 4)] # 100 m to 1 k self.NPLC_list = PLCs[self.model] #################################### # PARAMETERS # this is the "master" parameter that determines whether the DMM is # a voltmeter, an ampmeter, etc. self.add_parameter('sense_function', label="Instrument sense function", get_cmd="SENSe:FUNCtion?", set_cmd="SENSe:FUNCtion {}", val_mapping={ "DC Voltage": '"VOLT"', "AC Voltage": '"VOLT:AC"', "DC Current": '"CURR"', "AC Current": '"CURR:AC"', "2 Wire Resistance": '"RES"', "4 Wire Resistance": '"FRES"' }) self.add_parameter('line_frequency', get_cmd='SYSTem:LFRequency?', get_parser=int, set_cmd=False, label='Line Frequency', unit='Hz', docstring=('The frequency of the power line where ' 'the instrument is plugged')) self.add_parameter('NPLC', get_cmd='SENSe:VOLTage:DC:NPLC?', get_parser=float, set_cmd=self._set_NPLC, vals=vals.Enum(*self.NPLC_list), label='Integration time', unit='NPLC', docstring=textwrap.dedent("""\ Sets the integration time in number of power line cycles (PLC) for DC voltage and ratio measurements. Integration time is the period that the instrument's analog-to-digital (A/D) converter samples the input signal for a measurement. A longer integration time gives better measurement resolution but slower measurement speed. Only integration times of 1, 10, or 100 PLC provide normal mode (line frequency noise) rejection. Setting the integration time also sets the measurement resolution.""")) self.add_parameter('range', get_cmd='SENSe:VOLTage:DC:RANGe?', get_parser=float, set_cmd='SENSe:VOLTage:DC:RANGe {:f}', vals=vals.Enum(*self.ranges)) self.add_parameter('resolution', get_cmd='SENSe:VOLTage:DC:RESolution?', get_parser=float, set_cmd=self._set_resolution, label='Resolution', unit='V', vals=vals.MultiType(vals.Numbers(0), vals.Enum('MIN', 'MAX', 'DEF')), docstring=textwrap.dedent("""\ Selects the measurement resolution for DC voltage and ratio measurements. The resolution is specified in the same units as the selected measurement function, not in number of digits. You can also specify MIN (best resolution) or MAX (worst resolution). To achieve normal mode (line frequency noise) rejection, use a resolution that corresponds to an integration time that is an integral number of power line cycles. Refer to "Resolution Table" or "Range, Resolution and NPLC" sections of the instrument's manual for the available ranges for the resolution values.""")) self.add_parameter('autorange', label='Autorange', set_cmd='SENSe:VOLTage:DC:RANGe:AUTO {}', get_cmd='SENSe:VOLTage:DC:RANGe:AUTO?', val_mapping={ 'ON': 1, 'OFF': 0 }, vals=vals.Enum('ON', 'OFF')) self.add_parameter('autozero', label='Autozero', set_cmd='SENSe:VOLTage:DC:ZERO:AUTO {}', get_cmd='SENSe:VOLTage:DC:ZERO:AUTO?', val_mapping={ 'ON': 1, 'OFF': 0, 'ONCE': 'ONCE' }, vals=vals.Enum('ON', 'OFF', 'ONCE'), docstring=textwrap.dedent("""\ Disables or enables the autozero mode for DC voltage and ratio measurements. ON: the DMM internally measures the offset following each measurement. It then subtracts that measurement from the preceding reading. This prevents offset voltages present on the DMM’s input circuitry from affecting measurement accuracy. OFF: the instrument uses the last measured zero measurement and subtracts it from each measurement. It takes a new zero measurement each time you change the function, range or integration time. ONCE: the instrument takes one zero measurement and sets autozero OFF. The zero measurement taken is used for all subsequent measurements until the next change to the function, range or integration time. If the specified integration time is less than 1 PLC, the zero measurement is taken at 1 PLC to optimize noise rejection. Subsequent measurements are taken at the specified fast (< 1 PLC) integration time.""")) #################################### # Aperture parameters if self.is_34465A_34470A or self.is_34410A_34411A: # Define the extreme aperture time values for the 34410A, 34411A, # 34465A and 34470A. The upper limits for 34410A and 34411A in the # case of a 60Hz line frequency are just calculated by multiplying # the respective limit with 50/60. utility_freq = self.line_frequency() if utility_freq == 50: apt_times = { '34410A': [100e-6, 1], '34411A': [20e-6, 1], '34465A': [0.3e-3, 2], '34470A': [0.3e-3, 2] } elif utility_freq == 60: apt_times = { '34410A': [100e-6, 0.83], '34411A': [20e-6, 0.83], '34465A': [0.3e-3, 1.67], '34470A': [0.3e-3, 1.67] } if self.has_DIG: apt_times['34465A'][0] = 20e-6 apt_times['34470A'][0] = 20e-6 self.add_parameter('aperture_mode', label='Aperture mode', set_cmd='SENSe:VOLTage:DC:APERture:ENABled {}', get_cmd='SENSe:VOLTage:DC:APERture:ENABled?', val_mapping={ 'ON': 1, 'OFF': 0 }, vals=vals.Enum('ON', 'OFF'), docstring=textwrap.dedent("""\ Enables the setting of integration time in seconds (called aperture time) for DC voltage measurements. If aperture time mode is disabled (default), the integration time is set in PLC (power-line cycles).""")) self.add_parameter('aperture_time', label='Aperture time', set_cmd=self._set_apt_time, get_cmd='SENSe:VOLTage:DC:APERture?', get_parser=float, vals=vals.Numbers(*apt_times[self.model]), docstring=textwrap.dedent("""\ Specifies the integration time in seconds (called aperture time) with 2 µs resolution for DC voltage measurements. Use this command for precise control of the DMM's integration time. Use `NPLC` for better power-line noise rejection characteristics (NPLC > 1). Setting the aperture time automatically enables the aperture mode.""")) #################################### # Submodules self.add_submodule('display', Display(self, 'display')) self.add_submodule('trigger', Trigger(self, 'trigger')) self.add_submodule('sample', Sample(self, 'sample')) #################################### # Measurement Parameters # snapshot_get is disabled for each of these to prevent rapid mode # changes on initialization or snapshot update, however the cached # (last read) value will still be stored in the snapshot. self.add_parameter('volt', get_cmd=partial(self._get_parameter, "DC Voltage"), label='Voltage', unit='V', snapshot_get=False) self.add_parameter('curr', get_cmd=partial(self._get_parameter, "DC Current"), label='Current', unit='A', snapshot_get=False) self.add_parameter('ac_volt', get_cmd=partial(self._get_parameter, "AC Voltage"), label='AC Voltage', unit='V', snapshot_get=False) self.add_parameter('ac_curr', get_cmd=partial(self._get_parameter, "AC Current"), label='AC Current', unit='A', snapshot_get=False) self.add_parameter('res', get_cmd=partial(self._get_parameter, "2 Wire Resistance"), label='Resistance', unit='Ohms', snapshot_get=False) self.add_parameter('four_wire_res', get_cmd=partial(self._get_parameter, "4 Wire Resistance"), label='Resistance', unit='Ohms', snapshot_get=False) ##################################### # Time trace parameters self.add_parameter('timetrace_npts', label='Time trace number of points', initial_value=500, get_cmd=None, set_cmd=None, vals=vals.Ints(1)) self.add_parameter('timetrace_dt', label='Time trace time interval', unit='s', initial_value=1e-1, get_cmd=None, set_cmd=None, vals=vals.Numbers(0)) self.add_parameter('time_axis', label='Time', unit='s', snapshot_value=False, vals=vals.Arrays(shape=(self.timetrace_npts, )), parameter_class=TimeAxis) self.add_parameter('timetrace', vals=vals.Arrays(shape=(self.timetrace_npts, )), setpoints=(self.time_axis, ), parameter_class=TimeTrace) #################################### # Connect message if not silent: self.connect_message()