Ejemplo n.º 1
0
    def __init__(self, instrument_name, **kwargs):
        super().__init__(instrument_name, **kwargs)

        self._output_channels = {
            f'ch{ch}': Channel(instrument_name=self.instrument_name(),
                              name=f'ch{ch}', id=ch, output=True)
            for ch in self.instrument.channel_idxs}

        self._pxi_channels = {
            f'pxi{k}': Channel(instrument_name=self.instrument_name(),
                               name=f'pxi{k}', id=4000 + k,
                               input_trigger=True, output=True, input=True)
            for k in range(self.instrument.n_triggers)}

        self._channels = {
            **self._output_channels,
            **self._pxi_channels,
            'trig_in': Channel(instrument_name=self.instrument_name(),
                               name='trig_in', input_trigger=True,
                               input_TTL=(0, 5.0)),
            'trig_out': Channel(instrument_name=self.instrument_name(),
                                name='trig_out', output_TTL=(0, 3.3))}

        self.pulse_implementations = [
            # TODO fix sinepulseimplementation by using pulse_to_waveform_sequence
            SinePulseImplementation(
                pulse_requirements=[('frequency', {'min': 0, 'max': 200e6}),
                                    ('amplitude', {'max': 1.5})]),
            AWGPulseImplementation(
                pulse_requirements=[]),
            CombinationPulseImplementation(
                pulse_requirements=[]),
            DCPulseImplementation(
                pulse_requirements=[('amplitude', {'min': -1.5, 'max': 1.5})]),
            DCRampPulseImplementation(
                pulse_requirements=[]),
            TriggerPulseImplementation(
                pulse_requirements=[]),
            MarkerPulseImplementation(
                pulse_requirements=[])
        ]

        self.add_parameter('channel_selection',
                           vals=vals.Lists(),
                           get_cmd=self._get_active_channel_names)

        self.add_parameter('default_sampling_rates', set_cmd=None,
                           initial_value=[500e6] * len(self.instrument.channel_idxs))

        self.add_parameter('trigger_mode',
                           set_cmd=None,
                           initial_value='software',
                           vals=vals.Enum('none', 'hardware', 'software'),
                           docstring='Selects the method to run through the AWG queue.')

        self.trigger_thread = None
        self.waveforms = None
        self.waveform_queue = None
        self.started = False
Ejemplo n.º 2
0
    def __init__(
        self,
        parent: InstrumentBase,
        dac_instrument: DACInterface,
        channel_id: int,
        layout_id: int,
        name: str = "nanotune_gate",
        label: str = "nanotune gate",
        line_type: str = "dc_current",
        safety_range: Tuple[float, float] = (-3, 0),
        use_ramp: bool = True,
        ramp_rate: float = 0.1,
        max_jump: float = 0.05,
        delay: float = 0.001,
        fast_readout_source: Optional[InstrumentChannel] = None,
        **kwargs,
    ) -> None:
        """

        Args:
            dac_instrument (Optional[Parameter]):
            line_type (Optional[str]):
            min_v (Optional[float]):
            max_v (Optional[float]):
            use_ramp (Optional[bool]):
            ramp_rate (Optional[float]):
            max_jump (Optional[float]):
            delay (Optional[float]): Delay in between setting and measuring
                another parameter

        """
        assert issubclass(dac_instrument.__class__, DACInterface)

        super().__init__(parent, name)

        self._dac_instrument = dac_instrument
        self._dac_channel = self._dac_instrument.nt_channels[channel_id]

        super().add_parameter(
            name="channel_id",
            label="instrument channel id",
            set_cmd=None,
            get_cmd=None,
            initial_value=channel_id,
            vals=vals.Ints(0),
        )

        super().add_parameter(
            name="dc_voltage",
            label=f"{label} dc voltage",
            set_cmd=self.set_dc_voltage,
            get_cmd=self._dac_channel.get_dc_voltage,
            vals=vals.Numbers(*safety_range),
        )

        self.has_ramp = self._dac_channel.supports_hardware_ramp

        super().add_parameter(
            name="label",
            label="gate label",
            set_cmd=self._dac_channel.set_label,
            get_cmd=self._dac_channel.get_label,
            initial_value=label,
            vals=vals.Strings(),
        )

        qc_safety_range = (safety_range[0] - 0.01, safety_range[1] + 0.01)

        super().add_parameter(
            name="safety_range",
            label=f"{label} DC voltage safety range",
            set_cmd=self.set_safety_range,
            get_cmd=self.get_safety_range,
            initial_value=list(safety_range),
            vals=vals.Lists(),
        )

        super().add_parameter(
            name="inter_delay",
            label=f"{label} inter delay",
            set_cmd=self._dac_channel.set_inter_delay,
            get_cmd=self._dac_channel.get_inter_delay,
            initial_value=delay,
            vals=vals.Numbers(),
        )

        super().add_parameter(
            name="post_delay",
            label=label + " post delay",
            set_cmd=self._dac_channel.set_post_delay,
            get_cmd=self._dac_channel.get_post_delay,
            initial_value=delay,
            vals=vals.Numbers(),
        )

        super().add_parameter(
            name="max_jump",
            label=f"{label} maximum voltage jump",
            set_cmd=self.set_max_jump,
            get_cmd=self.get_max_jump,
            initial_value=max_jump,
            vals=vals.Numbers(),
        )

        super().add_parameter(
            name="step",
            label=f"{label} voltage step",
            set_cmd=self._dac_channel.set_step,
            get_cmd=self._dac_channel.get_step,
            initial_value=max_jump,
            vals=vals.Numbers(),
        )

        super().add_parameter(
            name="ramp_rate",
            label=f"{label} ramp rate",
            set_cmd=self._dac_channel.set_ramp_rate,
            get_cmd=self._dac_channel.get_ramp_rate,
            initial_value=ramp_rate,
            vals=vals.Numbers(),
        )

        super().add_parameter(
            name="use_ramp",
            label=f"{label} ramp setting",
            set_cmd=self.set_ramp,
            get_cmd=self.get_ramp,
            initial_value=use_ramp,
            vals=vals.Bool(),
        )

        super().add_parameter(
            name="relay_state",
            label=f"{label} DAC channel relay state",
            set_cmd=self._dac_channel.set_relay_state,
            get_cmd=self._dac_channel.get_relay_state,
            initial_value=self._dac_channel.get_relay_state(),
            vals=vals.Strings(),
        )

        super().add_parameter(
            name="layout_id",
            label=f"{label} device layout id",
            set_cmd=None,
            get_cmd=None,
            initial_value=layout_id,
            vals=vals.Ints(0),
        )

        super().add_parameter(
            name="current_valid_range",
            label=f"{label} current valid range",
            set_cmd=self.set_current_valid_range,
            get_cmd=self.get_current_valid_range,
            initial_value=[],
            vals=vals.Lists(),
        )

        super().add_parameter(
            name="transition_voltage",
            label=f"{label} transition voltage",
            set_cmd=self.set_transition_voltage,
            get_cmd=self.compute_transition_voltage,
            initial_value=0,
            vals=vals.Numbers(),
        )

        super().add_parameter(
            name="amplitude",
            label=f"{label} sawtooth amplitude",
            set_cmd=self._dac_channel.set_amplitude,
            get_cmd=self._dac_channel.get_amplitude,
            initial_value=0,
            vals=vals.Numbers(-0.5, 0.5),
        )

        super().add_parameter(
            name="offset",
            label=f"{label} sawtooth offset",
            set_cmd=self._set_offset,
            get_cmd=self._dac_channel.get_offset,
            initial_value=0,
            vals=vals.Numbers(*qc_safety_range),
        )

        super().add_parameter(
            name="frequency",
            label=f"{label} sawtooth frequency",
            set_cmd=self._dac_channel.set_frequency,
            get_cmd=self._dac_channel.get_frequency,
            initial_value=0,
            vals=vals.Numbers(),
        )
Ejemplo n.º 3
0
    def __init__(self, instrument_name, max_amplitude=1.5, **kwargs):
        assert max_amplitude <= 1.5

        super().__init__(instrument_name, **kwargs)

        self._output_channels = {
            f"ch{k}": Channel(instrument_name=self.instrument_name(),
                              name=f"ch{k}",
                              id=k,
                              output=True)
            for k in [1, 2]
        }

        # TODO add marker outputs
        self._channels = {
            **self._output_channels,
            "trig_in":
            Channel(
                instrument_name=self.instrument_name(),
                name="trig_in",
                input_trigger=True,
            ),
            "event_in":
            Channel(
                instrument_name=self.instrument_name(),
                name="event_in",
                input_trigger=True,
            ),
            "sync":
            Channel(instrument_name=self.instrument_name(),
                    name="sync",
                    output=True),
        }

        self.pulse_implementations = [
            DCPulseImplementation(pulse_requirements=[
                ("amplitude", {
                    "max": max_amplitude
                }),
                ("duration", {
                    "min": 100e-9
                }),
            ]),
            SinePulseImplementation(pulse_requirements=[
                ("frequency", {
                    "min": -1.5e9,
                    "max": 1.5e9
                }),
                ("amplitude", {
                    "min": 0,
                    "max": max_amplitude
                }),
                ("duration", {
                    "min": 100e-9
                }),
            ]),
            FrequencyRampPulseImplementation(pulse_requirements=[
                ("frequency_start", {
                    "min": -1.5e9,
                    "max": 1.5e9
                }),
                ("frequency_stop", {
                    "min": -1.5e9,
                    "max": 1.5e9
                }),
                ("amplitude", {
                    "min": 0,
                    "max": max_amplitude
                }),
                ("duration", {
                    "min": 100e-9
                }),
            ]),
        ]

        self.add_parameter(
            "trigger_in_duration",
            parameter_class=ManualParameter,
            unit="s",
            initial_value=1e-6,
        )
        self.add_parameter(
            "active_channels",
            parameter_class=ManualParameter,
            initial_value=[],
            vals=vals.Lists(vals.Strings()),
        )

        self.instrument.ch1.clear_waveforms()
        self.instrument.ch2.clear_waveforms()

        self.waveforms = {}  # List of waveform arrays for each channel
        # Optional initial waveform for each channel. Used to set the first point
        # to equal the last voltage of the final pulse (see docstring for details)
        self.waveforms_initial = {}
        self.sequences = {}  # List of sequence instructions for each channel
        # offsets list of actual programmed sample points versus expected points
        self.point_offsets = {}
        self.max_point_offsets = {}  # Maximum absolute sample point offset
        self.point = {
        }  # Current sample point, incremented as sequence is programmed
        # Maximum tolerable absolute point offset before raising a warning
        self.point_offset_limit = 100

        # Add parameters that are not set via setup
        self.additional_settings = ParameterNode()
        for instrument_channel in self.instrument.channels:
            channel = ParameterNode(instrument_channel.name)
            setattr(self.additional_settings, instrument_channel.name, channel)

            channel.output_coupling = instrument_channel.output_coupling
            channel.sample_rate = instrument_channel.sample_rate
Ejemplo n.º 4
0
    def __init__(self, parent, name, id, **kwargs):
        super().__init__(parent, name, **kwargs)

        self.id = id

        self.write = self.parent.write
        self.visa_handle = self.parent.visa_handle

        self.add_parameter(
            "continuous_run_mode",
            get_cmd="INITIATE:CONTINUOUS?",
            set_cmd="INITIATE:CONTINUOUS {}",
            val_mapping={
                True: "ON",
                False: "OFF"
            },
            docstring=
            "Choose to run in continuous mode (1) or triggered and gated mode (0)"
            "Continuous run mode repeats a waveform indefinitely without"
            "requiring a trigger.",
        )

        self.add_parameter(
            "continuous_armed",
            get_cmd="INITIATE:CONTINUOUS:ENABLE?",
            set_cmd="INITIATE:CONTINUOUS:ENABLE {}",
            val_mapping={
                True: "ARM",
                False: "SELF"
            },
            docstring="Whether continuous mode should wait to be armed:\n"
            "\tWhen False, waveforms are straight away generated as "
            "soon as the output is on. "
            "\tWhen True, the AWG remains idle until an enable signal "
            "is received. During idling, the output depends on the "
            "output function.",
        )

        self.add_parameter(
            "sample_rate",
            get_cmd="FREQUENCY:RASTER?",
            set_cmd="FREQUENCY:RASTER {:.8f}",
            get_parser=float,
            vals=vals.Numbers(10e6, 4.2e9),
            docstring="Set the sample rate of the arbitrary waveforms. Has no "
            "effect on standard waveforms",
        )

        self.add_parameter(
            "frequency_standard_waveforms",
            unit="Hz",
            get_cmd="FREQUENCY?",
            set_cmd="FREQUENCY {:.8f}",
            get_parser=float,
            vals=vals.Numbers(10e-3, 250e6),
            docstring="Set the frequency (Hz) of standard waveforms",
        )

        self.add_parameter(
            "function_mode",
            get_cmd="FUNCTION:MODE?",
            set_cmd="FUNCTION:MODE {}",
            vals=EnumVisa("FIXed", "USER", "SEQuenced", "ASEQuenced",
                          "MODulated", "PULSe"),
            docstring="Run mode defines the type of waveform that is "
            "available. Possible run modes are:\n"
            "\tfixed: standard waveform shapes.\n"
            "\tuser: arbitrary waveform shapes.\n"
            "\tsequenced: sequence of arbitrary waveforms.\n"
            "\tasequenced: advanced sequence of arbitrary "
            "waveforms.\n"
            "\tmodulated: modulated (standard) waveforms.\n"
            "\tpulse: digital pulse function",
        )

        self.add_parameter(
            "output_coupling",
            get_cmd="OUTPUT:COUPLING?",
            set_cmd="OUTPUT:COUPLING {}",
            vals=vals.Enum("DC", "DAC", "AC"),
            docstring="Possible output couplings are:\n"
            "\tDC: optimized for pulses at high amplitudes.\n"
            "\tDAC: optimized for bandwidth but low amplitude.\n"
            "\tAC: optimized for bandwidth.\n"
            "Note that DC and DAC use the DC output, and AC uses "
            "the AC output. DC and DAC couplings also allow "
            "control of amplitude and offset",
        )

        self.add_parameter(
            "output",
            get_cmd="OUTPUT?",
            set_cmd="OUTPUT {}",
            vals=EnumVisa("ON", "OFF"),
        )

        self.add_parameter(
            "sync",
            get_cmd="OUTPUT:SYNC?",
            set_cmd="OUTPUT:SYNC {}",
            vals=EnumVisa("ON", "OFF"),
        )

        self.add_parameter(
            "power",
            unit="dBm",
            get_cmd="POWER?",
            set_cmd="POWER {:.8f}",
            get_parser=float,
            vals=vals.Numbers(-5, 5),  # Might be -5 to 5, manual is unclear
            docstring=
            "Set output power (dBm) from AC output path (50-ohm matched)",
        )

        self.add_parameter(
            "voltage_DAC",
            unit="V",
            get_cmd="VOLTAGE:DAC?",
            set_cmd="VOLTAGE:DAC {:.8f}",
            get_parser=float,
            vals=vals.Numbers(50e-3, 2),
            docstring="Waveform amplitude (V) when routed through the DAC path",
        )

        self.add_parameter(
            "voltage_DC",
            unit="V",
            get_cmd="VOLTAGE?",
            set_cmd="VOLTAGE {:.8f}",
            get_parser=float,
            vals=vals.Numbers(50e-3, 2),
            docstring="Waveform amplitude (V) when routed through the DC path",
        )

        self.add_parameter(
            "voltage_offset",
            unit="V",
            get_cmd="VOLTAGE:OFFSET?",
            set_cmd="VOLTAGE:OFFSET {:.8f}",
            get_parser=float,
            vals=vals.Numbers(-1.5, 1.5),
            docstring="Voltage offset (V) when routed through DAC or DC path",
        )

        self.add_parameter(
            "output_modulation",
            get_cmd="MODulation:TYPE?",
            set_cmd="MODulation:TYPE {}",
            vals=EnumVisa("OFF", "AM", "FM", "SWEEP"),
        )

        # Trigger parameters
        self.add_parameter(
            "trigger_input",
            set_cmd="TRIGGER:{}",
            vals=vals.Enum("ECL", "TTL"),
            docstring="Set trigger input. Possible inputs are:\n"
            "\tECL: negative signal at fixed -1.3V.\n"
            "\tTTL: variable trigger_level",
        )

        self.add_parameter(
            "trigger_source",
            get_cmd="TRIGGER:SOURCE:ADVANCE?",
            set_cmd="TRIGGER:SOURCE:ADVANCE {}",
            vals=EnumVisa("EXTernal", "BUS", "TIMer", "EVENt"),
            docstring="Possible trigger sources are:\n"
            "\texternal: Use TRIG IN port exclusively.\n"
            "\tbus: Use remote commands exclusively.\n"
            "\ttimer: Use internal trigger generator exclusively.\n"
            "\tevent: Use EVENT IN port exclusively.",
        )

        self.add_parameter(
            "trigger_mode",
            get_cmd="TRIGGER:MODE?",
            set_cmd="TRIGGER:MODE {}",
            vals=EnumVisa("NORMal", "OVERride"),
            docstring="Possible trigger modes are:\n"
            "\tnormal:  the first trigger activates the output and "
            "consecutive triggers are ignored for the duration of "
            "the output waveform.\n"
            "\toverride:  the first trigger activates  the output "
            "and consecutive triggers  restart the output waveform, "
            "regardless if the current waveform has been completed "
            "or not.",
        )

        self.add_parameter(
            "trigger_timer_mode",
            get_cmd="TRIGGER:TIMER:MODE?",
            set_cmd="TRIGGER:TIMER:MODE {}",
            vals=EnumVisa("TIME", "DELay"),
            docstring="Possible modes of internal trigger are:\n"
            "\ttime: Perform trigger at fixed time interval.\n"
            "\tdelay: Perform trigger at fixed time intervals after "
            "previous waveform finished.",
        )

        self.add_parameter(
            "trigger_timer_time",
            get_cmd="TRIGGER:TIMER:TIME?",
            set_cmd="TRIGGER:TIMER:TIME {:f}",
            get_parser=float,
            unit="s",
            vals=vals.Numbers(100e-9, 20),
            docstring=
            "Triggering period (s) when in internal trigger timer mode")

        self.add_parameter(
            "trigger_timer_delay",
            get_cmd="TRIGGER:TIMER:DELAY?",
            set_cmd="TRIGGER:TIMER:DELAY {:d}",
            get_parser=int,
            unit="1/sample rate",
            vals=vals.Multiples(min_value=152, max_value=8000000, divisor=8),
            docstring="Delay of the internal trigger generator.")

        self.add_parameter(
            "trigger_level",
            get_cmd="TRIGGER:LEVEL?",
            set_cmd="TRIGGER:LEVEL {:.4f}",
            get_parser=float,
            vals=vals.Numbers(-5, 5),
            unit="V",
            docstring="Trigger level when trigger_mode is TTL",
        )

        self.add_parameter(
            "trigger_slope",
            get_cmd="TRIGGER:SLOPE?",
            set_cmd="TRIGGER:SLOPE {}",
            vals=EnumVisa("POSitive", "NEGative", "EITher"),
        )

        self.add_parameter(
            "trigger_delay",
            get_cmd="TRIGGER:DELAY?",
            set_cmd="TRIGGER:DELAY {:.4f}",
            get_parser=float,
            unit="1/sample rate",
            vals=vals.Multiples(min_value=0, max_value=8000000, divisor=8),
            docstring="Delay between receiving an external trigger, and "
            "outputting the first waveform")

        self.add_parameter(
            "burst_count",
            get_cmd="TRIGGER:COUNT?",
            set_cmd="TRIGGER:COUNT {}",
            set_parser=int,
            get_parser=int,
            vals=vals.Numbers(1, 1048576),
            docstring="Perform number of waveform cycles after trigger.",
        )

        # Waveform parameters
        self.add_parameter("uploaded_waveforms",
                           set_cmd=None,
                           initial_value=[],
                           vals=vals.Lists(),
                           docstring="List of uploaded waveforms",
                           snapshot_value=False)

        self.add_parameter(
            "waveform_timing",
            get_cmd="TRACE:SELECT:TIMING?",
            set_cmd="TRACE:SELECT:TIMING {}",
            vals=EnumVisa("COHerent", "IMMediate"),
            docstring="Possible waveform timings are:\n"
            "\tcoherent: finish current waveform before next.\n"
            "\timmediate: immediately skip to next waveform",
        )

        # Sequence parameters
        self.add_parameter("uploaded_sequence",
                           parameter_class=ManualParameter,
                           vals=vals.Iterables())

        self.add_parameter(
            "sequence_mode",
            get_cmd="SEQUENCE:ADVANCE?",
            set_cmd="SEQUENCE:ADVANCE {}",
            vals=EnumVisa("AUTOmatic", "ONCE", "STEPped"),
            docstring="Possible sequence modes are:\n"
            "\tautomatic: Automatically continue to next waveform, "
            "and repeat sequence from start when finished. When "
            "encountering jump command, wait for event input "
            "before continuing.\n"
            "\tonce: Automatically continue to next waveform. Stop "
            "when sequence completed.\n"
            "\tstepped: wait for event input after each waveform. "
            "Repeat sequence when completed.",
        )

        self.add_parameter(
            "sequence_once_count",
            get_cmd="SEQUENCE:ONCE:COUNT?",
            set_cmd="SEQUENCE:ONCE:COUNT {}",
            set_parser=int,
            get_parser=int,
            vals=vals.Numbers(1, 1048575),
            docstring="Determines the number of times a waveform sequence will"
            "be repeated.",
        )

        self.add_parameter(
            "sequence_jump",
            get_cmd="SEQUENCE:JUMP?",
            set_cmd="SEQUENCE:JUMP {}",
            vals=EnumVisa("BUS", "EVENt"),
            docstring="Determines the trigger source that will cause the "
            "sequence to advance after a jump bit. "
            "Possible jump modes are:\n"
            "\tbus: only advance after a remote trigger command.\n"
            "\tevent: only advance after an event input.",
        )

        self.add_parameter(
            "sequence_select_source",
            get_cmd="SEQUENCE:SELECT:SOURCE?",
            set_cmd="SEUQENCE:SELECT:SOURCE {}",
            vals=EnumVisa("BUS", "EXTernal"),
            docstring="Possible sources that can select active sequence:\n"
            "\tbus: sequence switches when remote command is "
            "called.\n"
            "\texternal: rear panel connector can dynamically "
            "choose "
            "next sequence.",
        )

        self.add_parameter(
            "sequence_select_timing",
            get_cmd="SEQUENCE:SELECT:TIMING?",
            set_cmd="SEQUENCE:SELECT:TIMING {}",
            vals=EnumVisa("COHerent", "IMMediate"),
            docstring="Possible ways in which the generator transitions from "
            "sequence to sequence:\n"
            "c\toherent: transition once current sequence is done.\n"
            "\timmediate: abort current sequence and progress to "
            "next",
        )

        self.add_parameter(
            "sequence_pre_step",
            get_cmd="SEQUENCE:PREStep?",
            set_cmd="SEQUENCE:PREStep {}",
            vals=EnumVisa("WAVE", "DC"),
            docstring="Choose to play a blank DC segment while waiting for an "
            "event signal to initiate or continue a sequence.",
        )

        # Functions
        self.add_function(
            "enable",
            call_cmd=f"INST {self.id};ENABLE",
            docstring="An immediate and unconditional generation of the "
            "selected output waveform. Must be armed and in "
            "continuous mode.",
        )
        self.add_function(
            "abort",
            call_cmd=f"INST {self.id};ABORT",
            docstring="An immediate and unconditional termination of the "
            "output waveform.",
        )

        self.add_function(
            "trigger",
            call_cmd=f"INST {self.id};TRIGGER",
            docstring="Perform software trigger",
        )

        self.add_function("on",
                          call_cmd=f"INST {self.id};OUTPUT ON",
                          docstring="Turn output on")

        self.add_function("off",
                          call_cmd=f"INST {self.id};OUTPUT OFF",
                          docstring="Turn output off")

        # Query current values for all parameters
        for param_name, param in self.parameters.items():
            param()
Ejemplo n.º 5
0
    def __init__(self, parent: Instrument, name, aid):
        """
        Args:
            parent: The ANC300 instance to which the channel is to be attached
            name: The name of the channel
            aid: The axis id (slot number) of the module
        """
        super().__init__(parent, name)
        self.aid = aid

        serial_no = self.ask('getser {:d}'.format(aid))
        self.model = serial_no.split('-')[0][:-1]

        if self.model in ('ANM300', 'NULL'):
            filter_mapping = {16: '16', 160: '160', 'off': 'off'}
        elif self.model == 'ANM200':
            filter_mapping = {1.6: "1.6", 16: '16', 160: '160',
                              1600: '1600', 'off': 'off'}
        elif self.model == 'ANM150':
            filter_mapping = None
        else:
            raise ValueError('Module model {!s} '
                             'is not supported.'.format(self.model))

        # TODO(ThibaudRuelle) add soft limits, initial values if adequate

        # TODO(Thibaud Ruelle) ans_parser = self._parent.ans_parser
        # leads to error in add_parameter at instantiation time

        # TODO(Thibaud Ruelle) use set_cmd = False instead of vals= ?

        def ans_parser(name, ans, unit=None, parser=str):
            """
            Parse "{name} = {value} ({unit})" type answers from ANC300.

            Args:
                name: The expected name
                ans: The answer from the instrument
                unit: The expected unit(s). String of list of strings. Defaults to None.
                parser: Function to use to parse the value.

            Returns parser(value).
            """
            ans = ans.strip().replace('=', ' ')
            ansparts = ans.split()
            ans_name, ans_val = ansparts[:2]

            if type(unit) == str or unit is None:
                unit = [unit,]

            if ans_val == '?':
                return None

            try:
                ans_unit = ansparts[2]
            except IndexError:
                ans_unit = None

            if ans_name != name:
                raise ValueError('Expected value name {!r}, '
                                 'received {!r}.'.format(name, ans_name))
            if not ans_unit in unit:
                raise ValueError('Expected value unit {!r}, '
                                 'received {!r}.'.format(unit, ans_unit))

            return parser(ans_val)

        # general parameters

        self.add_parameter('serial_no',
                           get_cmd='getser {}'.format(self.aid),
                           vals=vals.Strings())  # unnecessary when #651 in pip

        self.add_parameter('mode',
                           get_cmd='getm {}'.format(self.aid),
                           get_parser=partial(ans_parser, 'mode'),
                           set_cmd='setm {} {{}}'.format(self.aid),
                           label='Axis mode',
                           vals=vals.Enum('gnd', 'inp', 'cap',
                                          'stp', 'off', 'stp+', 'stp-'))

        self.add_parameter('output_voltage',
                           get_cmd='geto {}'.format(self.aid),
                           get_parser=partial(ans_parser, 'voltage',
                                              unit='V', parser=float),
                           label='Output voltage',
                           unit='V')

        self.add_parameter('capacitance',
                           get_cmd='getc {}'.format(self.aid),
                           get_parser=partial(ans_parser, 'capacitance',
                                              unit='nF', parser=float),
                           label='Capacitance',
                           unit='nF')

        self.add_function('update_capacitance',
                          call_cmd='setm {} cap'.format(self.aid))

        self.add_function('wait_capacitance_updated',
                          call_cmd='stop {}'.format(self.aid))

        # scanning parameters

        if self.model in ('ANM300', 'ANM200', 'NULL'):
            self.add_parameter('offset_voltage',
                            get_cmd='geta {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'voltage',
                                                unit='V', parser=float),
                            set_cmd='seta {} {{:.6f}}'.format(self.aid),
                            label='Offset voltage',
                            unit='V',
                            vals=vals.Numbers(0, 150))

            self.add_parameter('ac_in',
                            get_cmd='getaci {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'acin'),
                            set_cmd='setaci {} {{!s}}'.format(self.aid),
                            label='AC input status',
                            val_mapping={True: 'on', False: 'off'})

            self.add_parameter('dc_in',
                            get_cmd='getdci {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'dcin'),
                            set_cmd='setdci {} {{!s}}'.format(self.aid),
                            label='DC input status',
                            val_mapping={True: 'on', False: 'off'})

            if filter_mapping:
                self.add_parameter('outp_filter',
                                get_cmd='getfil {}'.format(self.aid),
                                get_parser=partial(ans_parser, 'filter'),
                                set_cmd='setfil {} {{!s}}'.format(self.aid),
                                label='Output low-pass filter',
                                unit='Hz',
                                val_mapping=filter_mapping)

        # stepping parameters

        if self.model in ('ANM300', 'ANM150', 'NULL'):
            self.add_parameter('step_frequency',
                            get_cmd='getf {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'frequency',
                                                unit=['Hz','H'], parser=int),
                            set_cmd='setf {} {{:.0f}}'.format(self.aid),
                            label='Stepping frequency',
                            unit='Hz',
                            vals=vals.Ints(1, 10000))

            self.add_parameter('step_amplitude',
                            get_cmd='getv {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'voltage',
                                                unit='V', parser=float),
                            set_cmd='setv {} {{:.6f}}'.format(self.aid),
                            label='Stepping amplitude',
                            unit='V',
                            vals=vals.Numbers(0, 150))

            # TODO(Thibaud Ruelle): define acceptable vals properly for patterns
            self.add_parameter('step_up_pattern',
                            get_cmd='getpu {}'.format(self.aid),
                            get_parser=lambda s: [int(u) for u in
                                                    s.split('\r\n')],
                            set_cmd='setpu {} {{!s}}'.format(self.aid),
                            set_parser=lambda l: " ".join([str(u) for u in l]),
                            vals=vals.Lists())  # unnecessary when #651 in pip

            self.add_parameter('step_down_pattern',
                            get_cmd='getpd {}'.format(self.aid),
                            get_parser=lambda s: [int(u) for u in
                                                    s.split('\r\n')],
                            set_cmd='setpd {} {{!s}}'.format(self.aid),
                            set_parser=lambda l: " ".join([str(u) for u in l]),
                            vals=vals.Lists())  # unnecessary when #651 in pip

            self.add_function('step_up_single',
                            call_cmd='stepu {}'.format(self.aid))

            self.add_function('step_up_cont',
                            call_cmd='stepu {} c'.format(self.aid))

            self.add_function('step_up',
                            call_cmd='stepu {} {{:d}}'.format(self.aid),
                            args=[vals.Ints(min_value=1)])

            self.add_function('step_down_single',
                            call_cmd='stepd {}'.format(self.aid))

            self.add_function('step_down_cont',
                            call_cmd='stepu {} c'.format(self.aid))

            self.add_function('step_down',
                            call_cmd='stepd {} {{:d}}'.format(self.aid),
                            args=[vals.Ints(min_value=1)])

            self.add_function('stop',
                            call_cmd='stop {}'.format(self.aid))

            def wait_steps_end(self):
                old_timeout = self.root_instrument.timeout()
                try:
                    self.root_instrument.timeout.set(3600)
                    self.write_raw(f'stepw {self.aid}')
                finally:
                    self.root_instrument.timeout.set(old_timeout)

            self.wait_steps_end = types.MethodType(wait_steps_end, self) # be sure to import types

            # TODO(Thibaud Ruelle): test for range before adding param
            self.add_parameter('trigger_up_pin',
                            get_cmd='gettu {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'trigger'),
                            set_cmd='settu {} {{!s}}'.format(self.aid),
                            label='Input trigger up pin',
                            val_mapping={i: str(i) for i in
                                            ['off', *range(1, 15)]})

            self.add_parameter('trigger_down_pin',
                            get_cmd='gettd {}'.format(self.aid),
                            get_parser=partial(ans_parser, 'trigger'),
                            set_cmd='settd {} {{!s}}'.format(self.aid),
                            label='Input trigger down pin',
                            val_mapping={i: str(i) for i in
                                            ['off', *range(1, 15)]})