Beispiel #1
0
    def get_raw(self) -> Tuple[Tuple[float, ...], Tuple[float, ...]]:
        measurement_mode = self.instrument.get_measurement_mode()
        if len(measurement_mode['channels']) != 2:
            raise ValueError('Two measurement channels are needed, one for '
                             'gate current and other for source drain '
                             'current.')

        smu = self.instrument.by_channel[measurement_mode['channels'][0]]

        if not smu.setup_fnc_already_run:
            raise Exception(f'Sweep setup has not yet been run successfully '
                            f'on {smu.full_name}')

        delay_time = smu.iv_sweep.step_delay()
        if smu._average_coefficient < 0:
            # negative coefficient means nplc and positive means just
            # averaging
            nplc = 128 * abs(smu._average_coefficient)
            power_line_time_period = 1 / smu.power_line_frequency
            calculated_time = 2 * nplc * power_line_time_period
        else:
            calculated_time = smu._average_coefficient * \
                              delay_time
        num_steps = smu.iv_sweep.sweep_steps()
        estimated_timeout = max(delay_time, calculated_time) * num_steps
        new_timeout = estimated_timeout * self._fudge

        format_and_mode = self.instrument.get_response_format_and_mode()
        fmt_format = format_and_mode['format']
        fmt_mode = format_and_mode['mode']
        try:
            self.root_instrument.write(MessageBuilder().fmt(1, 1).message)
            with self.root_instrument.timeout.set_to(new_timeout):
                raw_data = self.instrument.ask(MessageBuilder().xe().message)
                parsed_data = fmt_response_base_parser(raw_data)
        finally:
            self.root_instrument.write(MessageBuilder().fmt(fmt_format,
                                                            fmt_mode).message)

        self.param1 = _FMTResponse(
            *[parsed_data[i][::3] for i in range(0, 4)])
        self.param2 = _FMTResponse(
            *[parsed_data[i][1::3] for i in range(0, 4)])
        self.source_voltage = _FMTResponse(
            *[parsed_data[i][2::3] for i in range(0, 4)])

        self.shapes = ((len(self.source_voltage.value),),) * 2
        self.setpoints = ((self.source_voltage.value,),) * 2

        convert_dummy_val_to_nan(self.param1)
        convert_dummy_val_to_nan(self.param2)

        return self.param1.value, self.param2.value
Beispiel #2
0
def test_convert_dummy_val_to_nan():
    status = ['C', 'V', 'N', 'V', 'N', 'N']
    value = [0, 199.999e99, 1, 199.999e99, 2, 3]
    channel = [1, 1, 1, 1, 1, 1]
    param_type = ['V', 'V', 'V', 'V', 'V', 'V']
    param = _FMTResponse(value, status, channel, param_type)
    convert_dummy_val_to_nan(param)
    assert math.isnan(param.value[1])
    assert math.isnan(param.value[3])
    def __init__(self, name: str, instrument: B1517A, **kwargs: Any):
        super().__init__(name,
                         names=tuple(['param1', 'param2']),
                         units=tuple(['A', 'A']),
                         labels=tuple(['Param1 Current', 'Param2 Current']),
                         shapes=((1, ), ) * 2,
                         setpoint_names=(('Voltage', ), ) * 2,
                         setpoint_labels=(('Voltage', ), ) * 2,
                         setpoint_units=(('V', ), ) * 2,
                         instrument=instrument,
                         **kwargs)

        self.instrument: B1517A
        self.root_instrument: KeysightB1500

        self.param1 = _FMTResponse(None, None, None, None)
        self.param2 = _FMTResponse(None, None, None, None)
        self.source_voltage = _FMTResponse(None, None, None, None)
        self._fudge: float = 1.5
Beispiel #4
0
    def get_raw(self) -> Tuple[Tuple[float, ...], ...]:
        measurement_mode = self.instrument.get_measurement_mode()
        channels = measurement_mode['channels']
        n_channels = len(channels)

        if n_channels < 1:
            raise ValueError('At least one measurement channel is needed for '
                             'an IV sweep.')

        if (len(self.names) != n_channels or len(self.units) != n_channels
                or len(self.labels) != n_channels
                or len(self.shapes) != n_channels):
            raise ValueError(
                f"The number of `.names` ({len(self.names)}), "
                f"`.units` ({len(self.units)}), `.labels` ("
                f"{len(self.labels)}), or `.shapes` ({len(self.shapes)}) "
                f"of the {self.full_name} parameter "
                f"does not match the number of channels expected for the IV "
                f"sweep measurement, which is {n_channels}. One must set "
                f"enough names, units, and labels for all the channels that "
                f"are to be measured.")

        smu = self.instrument.by_channel[channels[0]]

        if not smu.setup_fnc_already_run:
            raise Exception(f'Sweep setup has not yet been run successfully '
                            f'on {smu.full_name}')

        delay_time = smu.iv_sweep.step_delay()
        if smu._average_coefficient < 0:
            # negative coefficient means nplc and positive means just
            # averaging, see B1517A.set_average_samples_for_high_speed_adc
            # for more info
            nplc = 128 * abs(smu._average_coefficient)
            power_line_time_period = 1 / smu.power_line_frequency
            calculated_time = 2 * nplc * power_line_time_period
        else:
            calculated_time = smu._average_coefficient * delay_time
        num_steps = smu.iv_sweep.sweep_steps()
        estimated_timeout = max(delay_time, calculated_time) * num_steps
        new_timeout = estimated_timeout * self._fudge

        format_and_mode = self.instrument.get_response_format_and_mode()
        fmt_format = format_and_mode['format']
        fmt_mode = format_and_mode['mode']
        try:
            self.root_instrument.write(MessageBuilder().fmt(1, 1).message)
            with self.root_instrument.timeout.set_to(new_timeout):
                raw_data = self.instrument.ask(MessageBuilder().xe().message)
        finally:
            self.root_instrument.write(MessageBuilder().fmt(
                fmt_format, fmt_mode).message)

        parsed_data = fmt_response_base_parser(raw_data)

        # The `4` comes from the len(_FMTResponse(None, None, None, None)),
        # the _FMTResponse tuple declares these items that the instrument
        # gives for each data point
        n_items_per_data_point = 4

        # sourced voltage values are also returned, hence the `+1`
        n_all_data_channels = n_channels + 1

        for channel_index in range(n_channels):
            parsed_data_items = [
                parsed_data[i][channel_index::n_all_data_channels]
                for i in range(0, n_items_per_data_point)
            ]
            single_channel_data = _FMTResponse(*parsed_data_items)
            convert_dummy_val_to_nan(single_channel_data)

            # Store the results to `.param#` attributes for convenient access
            # to all the data, e.g. status of each value in the arrays
            setattr(self, f"param{channel_index+1}", single_channel_data)

        channel_values_to_return = tuple(
            getattr(self, f"param{n + 1}").value for n in range(n_channels))

        source_voltage_index = n_channels
        parsed_source_voltage_items = [
            parsed_data[i][source_voltage_index::n_all_data_channels]
            for i in range(0, n_items_per_data_point)
        ]
        self.source_voltage = _FMTResponse(*parsed_source_voltage_items)

        self.shapes = ((len(self.source_voltage.value), ), ) * n_channels
        self.setpoints = ((self.source_voltage.value, ), ) * n_channels

        return channel_values_to_return
Beispiel #5
0
    def set_names_labels_and_units(
            self,
            names: Optional[Sequence[str]] = None,
            labels: Optional[Sequence[str]] = None,
            units: Optional[Sequence[str]] = None) -> None:
        """
        Set names, labels, and units of the measured parts of the MultiParameter.

        If units are not provided, "A" will be used because this parameter
        measures currents.

        If labels are not provided, names will be used.

        If names are not provided, ``param#`` will be used as names; the number
        of those names will be the same as the number of measured channels
        that ``B1500.get_measurement_mode`` method returns. Note that it is
        possible to not provide names and provide labels at the same time.
        In case, neither names nor labels are provided, the labels will be
        generated as ``Param# Current``.

        The number of provided names, labels, and units must be the same.
        Moreover, that number has to be equal to the number of channels
        that ``B1500.get_measurement_mode`` method returns. It is
        recommended to set measurement mode and number of channels first,
        and only then call this method to provide names/labels/units.

        The name/label/unit of the setpoint of this parameter will also be
        updated to defaults dictated by the
        ``set_setpoint_name_label_and_unit`` method.

        Note that ``.shapes`` of this parameter will also be updated to
        be in sync with the number of names.
        """
        measurement_mode = self.instrument.get_measurement_mode()
        channels = measurement_mode['channels']

        if names is None:
            names = [f"param{n+1}" for n in range(len(channels))]
            if labels is None:
                labels = [
                    f"Param{n + 1} Current" for n in range(len(channels))
                ]

        if labels is None:
            labels = tuple(names)

        if units is None:
            units = ['A'] * len(names)

        if len(labels) != len(names) or len(units) != len(names):
            raise ValueError(
                f"If provided, the number of names, labels, and units must be "
                f"the same, instead got {len(names)} names, {len(labels)} "
                f"labels, {len(units)} units.")

        if len(names) != len(channels):
            raise ValueError(
                f"The number of names ({len(names)}) does not match the number "
                f"of channels expected for the IV sweep measurement, "
                f"which is {len(channels)}. Please, when providing names, "
                f"provide them for every channel.")

        self.names = tuple(names)
        self.labels = tuple(labels)
        self.units = tuple(units)

        for n in range(len(channels)):
            setattr(self, f"param{n+1}", _FMTResponse(None, None, None, None))

        self.shapes = ((1, ), ) * len(self.names)

        self.set_setpoint_name_label_and_unit()