def __init__(self,
                 station,
                 sweepparams=None,
                 sweepranges=None,
                 minstrument=None,
                 nplots=None,
                 Naverage=10,
                 resolution=(96, 96),
                 sample_rate='default',
                 diff_dir=None,
                 verbose=1,
                 dorun=True,
                 show_controls=True,
                 add_ppt=True,
                 crosshair=False,
                 averaging=True,
                 name=None,
                 mouse_click_callback=None,
                 videomode_processor=None):
        """ Tool for fast acquisition of charge stability diagram

        The acquisition and post-processing of data is performed by a VideoModeProcessor.

        The class assumes that the station has a station.digitizer and a station.awg.

        Args:
            station (QCoDeS station): reference for the instruments
            sweepparams (list or dict or str): parameters to sweep
            sweepranges (list): list of sweep ranges
            minstrument (object): specification of the measurement instrument
            crosshair (bool): Enable crosshair in plots
            videomode_processor (None or VideoModeProcessor): Processor to use for measurement and post-processing
            mouse_click_callback (None or function): if None then update scan position with callback

        """
        if VideoMode.videomode_class_index == 0:
            # create instance of QApplication
            _ = pyqtgraph.mkQApp()
        VideoMode.videomode_class_index += 1
        self.videomode_index = VideoMode.videomode_class_index

        self.station = station
        self.verbose = verbose
        self.sweepparams = sweepparams
        if isinstance(sweepranges, numbers.Number):
            sweepranges = [sweepranges]
        self.sweepranges = sweepranges

        self.virtual_awg = getattr(station, ' virtual_awg', None)

        self.Naverage = Parameter('Naverage',
                                  get_cmd=self._get_Naverage,
                                  set_cmd=self._set_Naverage,
                                  vals=Numbers(1, 1023))

        # set core VideoMode properties
        self.diff_dir = diff_dir
        self.smoothing = 0
        self.datalock = threading.Lock()
        self.datafunction_result = None
        self.update_sleep = 1e-5
        self.diffsigma = 1
        self.diff = True
        self.laplace = False
        self.idx = 0
        self.name = ''
        self.maxidx = None
        self.datafunction = None
        self.alldata = None
        self._averaging_enabled = None
        self.fps = qtt.pgeometry.fps_t(nn=6)

        if videomode_processor is None:
            add_sawtooth_videomode_processor(self, sweepparams, sweepranges,
                                             resolution, sample_rate,
                                             minstrument)
            # Setup the GUI
            if nplots is None:
                nplots = len(self.channels)
        else:
            self.videomode_processor = videomode_processor

        self.set_videomode_name(name)

        self.nplots = nplots
        self.window_title = "%s: nplots %d" % (self.name, self.nplots)

        self._create_gui(nplots, show_controls, add_ppt, crosshair, Naverage,
                         averaging)

        self.Naverage(Naverage)

        if mouse_click_callback is None:
            mouse_click_callback = self._update_position
        for p in self.lp:
            p.sigMouseClicked.connect(mouse_click_callback)

        if dorun:
            self.run()
Exemple #2
0
def _param_set():
    p = Parameter('simple_setter_parameter', set_cmd=None, get_cmd=None)
    return p
Exemple #3
0
def test_setting_initial_value_delegate_parameter():
    value = 10
    p = Parameter('testparam', set_cmd=None, get_cmd=None)
    d = DelegateParameter('test_delegate_parameter', p, initial_value=value)
    assert p.cache.get(get_if_invalid=False) == value
    assert d.cache.get(get_if_invalid=False) == value
Exemple #4
0
class Measure(Metadatable):
    """
    Create a DataSet from a single (non-looped) set of actions.

    Args:
        *actions (any): sequence of actions to perform. Any action that is
            valid in a ``Loop`` can be used here. If an action is a gettable
            ``Parameter``, its output will be included in the DataSet.
            Scalars returned by an action will be saved as length-1 arrays,
            with a dummy setpoint for consistency with other DataSets.
    """
    dummy_parameter = Parameter(name='single',
                                label='Single Measurement',
                                set_cmd=None, get_cmd=None)
    active_measurement = None

    def __init__(self, *actions):
        super().__init__()
        self._dummyLoop = Loop(self.dummy_parameter[[0]]).each(*actions)

    def run_temp(self, **kwargs):
        """
        Wrapper to run this measurement as a temporary data set
        """
        return self.run(quiet=True, location=False, **kwargs)

    def get_data_set(self, *args, **kwargs):
        return self._dummyLoop.get_data_set(*args, **kwargs)

    def run(self, *args, use_threads=False, quiet=False, station=None,
            set_active=True, save_metadata=True, **kwargs):
        """
        Run the actions in this measurement and return their data as a DataSet

        Args:
            quiet (Optional[bool]): Set True to not print anything except
                errors. Default False.

            station (Optional[Station]): the ``Station`` this measurement
                pertains to. Defaults to ``Station.default`` if one is defined.
                Only used to supply metadata.

            use_threads (Optional[bool]): whether to parallelize ``get``
                operations using threads. Default False.

            Other kwargs are passed along to data_set.new_data. The key ones
            are:

            location (Optional[Union[str, False]]): the location of the
                DataSet, a string whose meaning depends on formatter and io,
                or False to only keep in memory. May be a callable to provide
                automatic locations. If omitted, will use the default
                DataSet.location_provider

            name (Optional[str]): if location is default or another provider
                function, name is a string to add to location to make it more
                readable/meaningful to users

            formatter (Optional[Formatter]): knows how to read and write the
                file format. Default can be set in DataSet.default_formatter

            io (Optional[io_manager]): knows how to connect to the storage
                (disk vs cloud etc)

        returns:
            a DataSet object containing the results of the measurement
        """

        data_set = self._dummyLoop.get_data_set(*args, **kwargs)

        # set the DataSet to local for now so we don't save it, since
        # we're going to massage it afterward
        # Keep location as private attribute
        data_set._location = data_set.location
        data_set.location = False

        # run the measurement as if it were a Loop
        self._dummyLoop.run(use_threads=use_threads,
                            station=station, quiet=True, set_active=set_active)

        # look for arrays that are unnecessarily nested, and un-nest them
        all_unnested = True
        for array in data_set.arrays.values():
            if not hasattr(array, 'ndim') or array.ndim == 1:
                if array.is_setpoint:
                    dummy_setpoint = array
                else:
                    # we've found a scalar - so keep the dummy setpoint
                    all_unnested = False
            else:
                # The original return was an array, so take off the extra dim.
                # (This ensures the outer dim length was 1, otherwise this
                # will raise a ValueError.)
                array.ndarray.shape = array.ndarray.shape[1:]

                # TODO: DataArray.shape masks ndarray.shape, and a user *could*
                # change it, thinking they were reshaping the underlying array,
                # but this would a) not actually reach the ndarray right now,
                # and b) if it *did* and the array was reshaped, this array
                # would be out of sync with its setpoint arrays, so bad things
                # would happen. So we probably want some safeguards in place
                # against this
                array.shape = array.ndarray.shape

                array.set_arrays = array.set_arrays[1:]

                array.init_data()

        # Do we still need the dummy setpoint array at all?
        if all_unnested:
            del data_set.arrays[dummy_setpoint.array_id]
            if hasattr(data_set, 'action_id_map'):
                del data_set.action_id_map[dummy_setpoint.action_indices]

        # now put back in the DataSet location and save it
        data_set.location = data_set._location
        data_set.write()

        if save_metadata:
            # metadata: ActiveLoop already provides station snapshot, but also
            # puts in a 'loop' section that we need to replace with 'measurement'
            # but we use the info from 'loop' to ensure consistency and avoid
            # duplication.
            LOOP_SNAPSHOT_KEYS = ['ts_start', 'ts_end', 'use_threads']
            data_set.add_metadata({'measurement': {
                k: data_set.metadata['loop'][k] for k in LOOP_SNAPSHOT_KEYS
            }})
            del data_set.metadata['loop']

            # actions are included in self.snapshot() rather than in
            # LOOP_SNAPSHOT_KEYS because they are useful if someone just
            # wants a local snapshot of the Measure object
            data_set.add_metadata({'measurement': self.snapshot()})

            data_set.save_metadata()

        if not quiet:
            print(repr(data_set))
            print(datetime.now().strftime('acquired at %Y-%m-%d %H:%M:%S'))

        return data_set

    def snapshot_base(self, update=False):
        return {
            '__class__': full_class(self),
            'actions': _actions_snapshot(self._dummyLoop.actions, update)
        }
Exemple #5
0
def _param():
    p = Parameter('simple_parameter', set_cmd=None, get_cmd=lambda: 1)
    return p
Exemple #6
0
 def setUp(self):
     self.parameter = Parameter(name='foobar',
                                set_cmd=None,
                                get_cmd=None,
                                set_parser=lambda x: int(round(x)),
                                vals=vals.PermissiveInts(0))
Exemple #7
0
 def setUp(self):
     self.p1 = Parameter('P1', initial_value=1, get_cmd=None, set_cmd=None)
Exemple #8
0
 def test_parameter_in_node_snapshot(self):
     node = ParameterNode()
     node.p = Parameter()
     self.assertEqual(node.snapshot()['parameters']['p'], node.p.snapshot())
Exemple #9
0
 def test_parameter_in_node_simplified_snapshot(self):
     node = ParameterNode(simplify_snapshot=True)
     node.p = Parameter(initial_value=42)
     self.assertEqual(node.snapshot()['p'], 42)
Exemple #10
0
 def __init__(self, **kwargs):
     super().__init__(**kwargs)
     self.p = Parameter()
     self.value = 1
Exemple #11
0
 def __init__(self, **kwargs):
     Parameter.__init__(self, **kwargs)
     ParameterNode.__init__(self, use_as_attributes=True, **kwargs)
Exemple #12
0
 def test_sweep_parameter_node(self):
     node = ParameterNode(use_as_attributes=True)
     node.param1 = Parameter(set_cmd=None)
     sweep_values_node = node.sweep('param1', 0, 10, step=1)
     sweep_values_parameter = node['param1'].sweep(0, 10, step=1)
     self.assertEqual(list(sweep_values_node), list(sweep_values_parameter))
def test_bad_validator():
    with pytest.raises(TypeError):
        Parameter('p', vals=[1, 2, 3])
Exemple #14
0
def test_get_latest_no_get():
    """
    Test that get_latest on a parameter that does not have get is handled
    correctly.
    """
    local_parameter = Parameter('test_param', set_cmd=None, get_cmd=False)
    # The parameter does not have a get method.
    with pytest.raises(AttributeError):
        local_parameter.get()
    # get_latest will fail as get cannot be called and no cache
    # is available
    with pytest.raises(RuntimeError):
        local_parameter.get_latest()
    value = 1
    local_parameter.set(value)
    assert local_parameter.get_latest() == value

    local_parameter2 = Parameter('test_param2',
                                 set_cmd=None,
                                 get_cmd=False,
                                 initial_value=value)
    with pytest.raises(AttributeError):
        local_parameter2.get()
    assert local_parameter2.get_latest() == value
Exemple #15
0
    def testLoopCombinedParameterInside(self, npoints, npoints_outer,
                                        x_start_stop, y_start_stop,
                                        z_start_stop):
        x_set = np.linspace(x_start_stop[0], x_start_stop[1], npoints_outer)
        y_set = np.linspace(y_start_stop[0], y_start_stop[1], npoints)
        z_set = np.linspace(z_start_stop[0], z_start_stop[1], npoints)

        setpoints = np.hstack((y_set.reshape(npoints,
                                             1), z_set.reshape(npoints, 1)))

        parameters = [
            Parameter(name, get_cmd=None, set_cmd=None)
            for name in ["X", "Y", "Z"]
        ]
        sweep_values = combine(parameters[1], parameters[2],
                               name="combined").sweep(setpoints)

        def ataskfunc():
            a = 1 + 1

        def btaskfunc():
            b = 1 + 2

        atask = Task(ataskfunc)
        btask = Task(btaskfunc)

        def wrapper():
            counter = 0

            def inner():
                nonlocal counter
                counter += 1
                return counter

            return inner

        self.dmm.voltage.get = wrapper()
        loop = Loop(
            parameters[0].sweep(x_start_stop[0],
                                x_start_stop[1],
                                num=npoints_outer)).loop(sweep_values).each(
                                    self.dmm.voltage, atask,
                                    self.dmm.somethingelse, self.dmm.voltage,
                                    btask)
        data = loop.run(quiet=True)
        np.testing.assert_array_equal(data.arrays['X_set'].ndarray, x_set)
        np.testing.assert_array_equal(
            data.arrays['Y'].ndarray,
            np.repeat(y_set.reshape(1, npoints), npoints_outer, axis=0))
        np.testing.assert_array_equal(
            data.arrays['Z'].ndarray,
            np.repeat(z_set.reshape(1, npoints), npoints_outer, axis=0))

        np.testing.assert_array_equal(
            data.arrays['dmm_voltage_0'].ndarray,
            np.arange(1, npoints * npoints_outer * 2,
                      2).reshape(npoints_outer, npoints))
        np.testing.assert_array_equal(
            data.arrays['dmm_voltage_3'].ndarray,
            np.arange(2, npoints * npoints_outer * 2 + 1,
                      2).reshape(npoints_outer, npoints))
        np.testing.assert_array_equal(data.arrays['dmm_somethingelse'].ndarray,
                                      np.ones((npoints_outer, npoints)))
Exemple #16
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.p = Parameter()
Exemple #17
0
# Criar um database
initialise_or_create_database_at("~/teste.db")

exp = load_or_create_experiment(experiment_name='osc realtime intro 2',
                                sample_name="osc realtime 1")


def calculateGain():
    Y = osc.ch3.wavesample()
    n_mean = int(len(Y) / 2)
    value = 2 * np.mean(Y[n_mean:])
    value = 10 * np.log(np.power(value, 2))
    return value


gain = Parameter('gain', label='gain', unit='dB', get_cmd=calculateGain)

# Medida de fato
meas = Measurement(exp=exp, station=station)

meas.register_parameter(PSG1.freq)
meas.register_parameter(gain, setpoints=[PSG1.freq])

with meas.run(write_in_background=True) as datasaver:
    for aFreq in np.linspace(1, 500, 500):
        time.sleep(1)
        PSG1.freq(aFreq)
        PSG2.freq(aFreq)
        datasaver.add_result((gain, gain()), (PSG1.freq, PSG1.freq()))
Exemple #18
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.val = 42
     self.param1 = Parameter()
Exemple #19
0
 def test_no_name(self):
     with self.assertRaises(TypeError):
         Parameter()
Exemple #20
0
    def test_full_names(self):
        for instrument in self.blank_instruments:
            # no instrument
            p = Parameter(name='simple')
            p._instrument = instrument
            self.assertEqual(p.full_names, None)

            p = Parameter(names=['a', 'b'])
            p._instrument = instrument
            self.assertEqual(p.full_names, ['a', 'b'])

        p = Parameter(name='simple')
        p._instrument = self.named_instrument
        self.assertEqual(p.full_names, None)

        p = Parameter(names=['penn', 'teller'])
        p._instrument = self.named_instrument
        self.assertEqual(p.full_names, ['astro_penn', 'astro_teller'])
Exemple #21
0
def test_no_name():
    with pytest.raises(TypeError):
        Parameter()
def test_get_shape_for_parameter_from_len(loop_shape):
    a = Parameter(name='a', initial_cache_value=10)
    shape = detect_shape_of_measurement((a,), loop_shape)
    assert shape == {'a': tuple(loop_shape)}
Exemple #23
0
def test_perform_actual_upgrade_6_to_newest_add_new_data():
    """
    Insert new runs on top of existing runs upgraded and verify that they
    get the correct captured_run_id and captured_counter
    """
    from qcodes.dataset.measurements import Measurement
    from qcodes.instrument.parameter import Parameter
    import numpy as np

    fixpath = os.path.join(fixturepath, 'db_files', 'version6')

    db_file = 'some_runs.db'
    dbname_old = os.path.join(fixpath, db_file)

    if not os.path.exists(dbname_old):
        pytest.skip("No db-file fixtures found. You can generate test db-files"
                    " using the scripts in the "
                    "https://github.com/QCoDeS/qcodes_generate_test_db/ repo")

    with temporarily_copied_DB(dbname_old, debug=False, version=6) as conn:
        assert isinstance(conn, ConnectionPlus)
        perform_db_upgrade(conn)
        assert get_user_version(conn) >= 7
        no_of_runs_query = "SELECT max(run_id) FROM runs"
        no_of_runs = one(atomic_transaction(conn, no_of_runs_query),
                         'max(run_id)')

        # Now let's insert new runs and ensure that they also get
        # captured_run_id assigned.
        params = []
        for n in range(5):
            params.append(
                Parameter(f'p{n}',
                          label=f'Parameter {n}',
                          unit=f'unit {n}',
                          set_cmd=None,
                          get_cmd=None))

        # Set up an experiment
        exp = new_experiment('some-exp', 'some-sample', conn=conn)
        meas = Measurement(exp=exp)
        meas.register_parameter(params[0])
        meas.register_parameter(params[1])
        meas.register_parameter(params[2], basis=(params[0], ))
        meas.register_parameter(params[3], basis=(params[1], ))
        meas.register_parameter(params[4], setpoints=(params[2], params[3]))

        # Make a number of identical runs
        for _ in range(10):
            with meas.run() as datasaver:
                for x in np.random.rand(10):
                    for y in np.random.rand(10):
                        z = np.random.rand()
                        datasaver.add_result((params[0], 0), (params[1], 1),
                                             (params[2], x), (params[3], y),
                                             (params[4], z))

        no_of_runs_new = one(atomic_transaction(conn, no_of_runs_query),
                             'max(run_id)')
        assert no_of_runs_new == 20

        # check that run_id is equivalent to captured_run_id for new
        # runs
        for run_id in range(no_of_runs, no_of_runs_new + 1):
            ds1 = load_by_id(run_id, conn)
            ds2 = load_by_run_spec(captured_run_id=run_id, conn=conn)

            assert ds1.the_same_dataset_as(ds2)

            assert ds1.run_id == run_id
            assert ds1.run_id == ds1.captured_run_id
            assert ds2.run_id == run_id
            assert ds2.run_id == ds2.captured_run_id

        # we are creating a new experiment into a db with one exp so:
        exp_id = 2

        # check that counter is equivalent to captured_counter for new
        # runs
        for counter in range(1, no_of_runs_new - no_of_runs + 1):
            ds1 = load_by_counter(counter, exp_id, conn)
            # giving only the counter is not unique since we have 2 experiments
            with pytest.raises(NameError,
                               match="More than one"
                               " matching dataset"):
                load_by_run_spec(captured_counter=counter, conn=conn)
            # however we can supply counter and experiment
            ds2 = load_by_run_spec(captured_counter=counter,
                                   experiment_name='some-exp',
                                   conn=conn)

            assert ds1.the_same_dataset_as(ds2)
            assert ds1.counter == counter
            assert ds1.counter == ds1.captured_counter
            assert ds2.counter == counter
            assert ds2.counter == ds2.captured_counter
def test_get_shape_for_parameter_from_sequence(loop_shape, range_func):
    a = Parameter(name='a', initial_cache_value=10)
    loop_sequence = tuple(range_func(x) for x in loop_shape)
    shape = detect_shape_of_measurement((a,), loop_sequence)
    assert shape == {'a': tuple(loop_shape)}
Exemple #25
0
def _param_complex():
    p = Parameter('simple_complex_parameter',
                  set_cmd=None,
                  get_cmd=lambda: 1 + 1j,
                  vals=validators.ComplexNumbers())
    return p
Exemple #26
0
    def __init__(self, name, dll_path, **kwargs):
        super().__init__(name, **kwargs)

        # Add .NET assembly to pythonnet
        # This allows importing its functions
        clr.System.Reflection.Assembly.LoadFile(dll_path)
        from clr import ActiveTechnologies

        self._api = ActiveTechnologies.Instruments.AWG4000.Control

        ### Instrument constants (set here since api is only defined above)
        self._trigger_sources = {
            "stop": self._api.TriggerSource.Stop,
            "start": self._api.TriggerSource.Start,
            "event_marker": self._api.TriggerSource.Event_Marker,
            "dc_trigger_in": self._api.TriggerSource.DCTriggerIN,
            "fp_trigger_in": self._api.TriggerSource.FPTriggerIN,
        }
        self._trigger_sensitivity_edges = {
            "rising": self._api.SensitivityEdge.RisingEdge,
            "falling": self._api.SensitivityEdge.RisingEdge,
        }
        self._trigger_actions = {
            "start": self._api.TriggerAction.TriggerStart,
            "stop": self._api.TriggerAction.TriggerStop,
            "ignore": self._api.TriggerAction.TriggerIgnore,
        }

        ### Get device object
        self._device = self._api.DeviceSet().DeviceList[0]

        ### Initialize device and channels
        self.initialize()

        ### Add channels containing their own parameters/functions
        for channel_id in range(1, 5):
            channel = AWGChannel(
                self,
                name=f"ch{channel_id}",
                id=channel_id,
                channel_api=self._device.GetChannel(channel_id - 1),
            )
            setattr(self, f"ch{channel_id}", channel)

        self.channels = ChannelList(
            parent=self,
            name="channels",
            chan_type=AWGChannel,
            chan_list=[self.ch1, self.ch2, self.ch3, self.ch4],
        )
        self.add_submodule("channels", self.channels)

        ### Add channel pair settings
        self.left_frequency_interpolation = Parameter(
            set_cmd=self._device.PairLeft.SetFrequencyInterpolation,
            vals=vals.Enum(1, 2, 4),
        )
        self.right_frequency_interpolation = Parameter(
            set_cmd=self._device.PairRight.SetFrequencyInterpolation,
            vals=vals.Enum(1, 2, 4),
        )

        # TODO Need to implement frequency interpolation for channel pairs
        # self.add_parameter('frequency_interpolation',
        #                    label='DAC frequency interpolation factor',
        #                    vals=vals.Enum(1, 2, 4))

        self.trigger_sensitivity_edge = Parameter(
            initial_value="rising",
            label="Trigger sensitivity edge for in/out",
            set_cmd=None,
            vals=vals.Enum("rising", "falling"),
        )

        self.trigger_action = Parameter(
            initial_value="start",
            label="Trigger action",
            set_cmd=None,
            vals=vals.Enum("start", "stop", "ignore"),
        )
Exemple #27
0
def test_delegate_cache_pristine_if_not_set():
    p = Parameter('test')
    d = DelegateParameter('delegate', p)
    gotten_delegate_cache = d.cache.get(get_if_invalid=False)
    assert gotten_delegate_cache is None
def parameters():
    n_points_1 = Parameter('n_points_1', set_cmd=None, vals=vals.Ints())
    n_points_2 = Parameter('n_points_2', set_cmd=None, vals=vals.Ints())
    n_points_3 = Parameter('n_points_3', set_cmd=None, vals=vals.Ints())

    n_points_1.set(10)
    n_points_2.set(20)
    n_points_3.set(15)

    setpoints_1 = Parameter('setpoints_1',
                            get_cmd=lambda: np.arange(n_points_1()),
                            vals=vals.Arrays(shape=(n_points_1, )))
    setpoints_2 = Parameter('setpoints_2',
                            get_cmd=lambda: np.arange(n_points_2()),
                            vals=vals.Arrays(shape=(n_points_2, )))
    setpoints_3 = Parameter('setpoints_3',
                            get_cmd=lambda: np.arange(n_points_3()),
                            vals=vals.Arrays(shape=(n_points_3, )))
    yield (n_points_1, n_points_2, n_points_3, setpoints_1, setpoints_2,
           setpoints_3)
Exemple #29
0
 def test_bad_validator(self):
     with self.assertRaises(TypeError):
         Parameter('p', vals=[1, 2, 3])
def do2Dconductance(outer_param: Parameter,
                    outer_start: Union[float, int],
                    outer_stop: Union[float, int],
                    outer_npts: int,
                    inner_param: Parameter,
                    inner_start: Union[float, int],
                    inner_stop: Union[float, int],
                    inner_npts: int,
                    lockin: SR830_T3,
                    delay: Optional[float]=None):
    """
    Function to perform a sped-up 2D conductance measurement

    Args:
        outer_param: The outer loop voltage parameter
        outer_start: The outer loop start voltage
        outer_stop: The outer loop stop voltage
        outer_npts: The number of points in the outer loop
        inner_param: The inner loop voltage parameter
        inner_start: The inner loop start voltage
        inner_stop: The inner loop stop voltage
        inner_npts: The number of points in the inner loop
        lockin: The lock-in amplifier to use
        delay: Delay to wait after setting inner parameter before triggering lockin.
          If None will use default delay, otherwise used the supplied.
    """
    station = qc.Station.default

    sr = lockin

    # Validate the instruments
    if sr.name not in station.components:
        raise KeyError('Unknown lock-in! Refusing to proceed until the '
                       'lock-in has been added to the station.')
    if (outer_param._instrument.name not in station.components and
        outer_param._instrument._parent.name not in station.components):
        raise KeyError('Unknown instrument for outer parameter. '
                       'Please add that instrument to the station.')
    if (inner_param._instrument.name not in station.components and
        inner_param._instrument._parent.name not in station.components):
        raise KeyError('Unknown instrument for inner parameter. '
                       'Please add that instrument to the station.')

    tau = sr.time_constant()
    min_delay = 0.002  # what's the physics behind this number?
    if delay is None:
        delay = tau + min_delay
    # Prepare for the first iteration
    # Some of these things have to be repeated during the loop
    sr.buffer_reset()
    sr.buffer_start()
    #sr.buffer_trig_mode('ON')
    sr.buffer_SR('Trigger')
    sr.conductance.shape = (inner_npts,)
    sr.conductance.setpoint_names = (inner_param.name,)
    sr.conductance.setpoint_labels = (inner_param.label,)
    sr.conductance.setpoint_units = ('V',)
    sr.conductance.setpoints = (tuple(np.linspace(inner_start,
                                                  inner_stop,
                                                  inner_npts)),)

    def trigger():
        sleep(delay)
        sr.send_trigger()

    def prepare_buffer():
        # here it should be okay to call ch1_databuffer... I think...
        sr.ch1_databuffer.prepare_buffer_readout()
        # For the dataset/plotting, put in the correct setpoints
        sr.conductance.setpoint_names = (inner_param.name,)
        sr.conductance.setpoint_labels = (inner_param.label,)
        sr.conductance.setpoint_units = ('V',)
        sr.conductance.setpoints = (tuple(np.linspace(inner_start,
                                                      inner_stop,
                                                      inner_npts)),)

    def start_buffer():
        sr.buffer_start()
        sr.conductance.shape = (inner_npts,)  # This is something

    def reset_buffer():
        sr.buffer_reset()

    trig_task = qc.Task(trigger)
    reset_task = qc.Task(reset_buffer)
    start_task = qc.Task(start_buffer)
    inner_loop = qc.Loop(inner_param.sweep(inner_start,
                                           inner_stop,
                                           num=inner_npts)).each(trig_task)
    outer_loop = qc.Loop(outer_param.sweep(outer_start,
                                           outer_stop,
                                           num=outer_npts)).each(start_task,
                                                                 inner_loop,
                                                                 sr.conductance,
                                                                 reset_task)

    set_params = ((inner_param, inner_start, inner_stop),
                  (outer_param, outer_start, outer_stop))
    meas_params = (sr.conductance,)
    prepare_buffer()
    qdac = None
    # ensure that any waveform generator is unbound from the qdac channels that we step if
    # we are stepping the qdac
    if isinstance(inner_param._instrument, QDacch):
        qdacch = inner_param._instrument
        qdacch.slope('Inf')
    if isinstance(outer_param._instrument, QDacch):
        qdacch = outer_param._instrument
        qdacch.slope('Inf')
    if qdac:
        qdac.fast_voltage_set(True)  # now that we have unbound the function generators
                                     # we don't need to do it in the loop
        qdac.voltage_set_dont_wait(False)  # this is un safe and highly experimental
    plot, data = _do_measurement(outer_loop, set_params, meas_params, do_plots=True)
    return plot, data