We use the ziqc.MFLI instrument driver for the MFLI Lock-In amplifier.

import numpy as np
import matplotlib.pyplot as plt

import qcodes as qc
import zhinst.qcodes as ziqc

mfli = ziqc.MFLI("mf1", "dev3337", interface="pcie", host="")
Successfully connected to data server at api version: 6
Successfully connected to device DEV3337 on interface PCIE
Connected to: Zurich Instruments MFLI (serial:dev3337, firmware:65939) in 0.72s


Access the MFLI’s nodetree:

print([k for k in mfli.submodules.keys()])
print([k for k in mfli.parameters.keys()])
['daq', 'sweeper', 'stats', 'oscs', 'demods', 'extrefs', 'triggers', 'status', 'dios', 'auxins', 'system', 'sigins', 'currins', 'sigouts', 'mods', 'pids', 'features', 'auxouts', 'tu', 'imps']
['IDN', 'clockbase']

The Parameter sample of the demodulators uses the getSample(...) method of the ziPython API to read the latest sample of the demodulator.




⚠️ Note that this parameter only returns a single sample. There is no guarantee for any precise time-relation between consecutive samples. For proper (triggered) data acquisition of signals aligned to a precise time-grid, please use the Data Acquisition Module (see below).

The Data Acquisition Module of the MFLI

The MFLI comes with a Data Acquisition Module for precise (triggered) measurements. For more details on the Data Acquisition Module please see the according example.

print([k for k in mfli.daq.parameters.keys()])
['awgcontrol', 'bandwidth', 'bitmask', 'bits', 'buffercount', 'buffersize', 'clearhistory', 'count', 'delay', 'device', 'duration', 'edge', 'enable', 'endless', 'eventcount_mode', 'fft_absolute', 'fft_window', 'findlevel', 'flags', 'forcetrigger', 'grid_cols', 'grid_direction', 'grid_mode', 'grid_overwrite', 'grid_repetitions', 'grid_rowrepetition', 'grid_rows', 'grid_waterfall', 'historylength', 'holdoff_count', 'holdoff_time', 'hysteresis', 'level', 'preview', 'pulse_max', 'pulse_min', 'refreshrate', 'save_csvlocale', 'save_csvseparator', 'save_directory', 'save_fileformat', 'save_filename', 'save_save', 'save_saveonread', 'spectrum_autobandwidth', 'spectrum_enable', 'spectrum_frequencyspan', 'spectrum_overlapped', 'triggered', 'triggernode', 'type']

List available sources of streaming signals …


… and different signal types for a given source.

['x', 'y', 'r', 'xiy', 'theta', 'frequency', 'auxin0', 'auxin1', 'dio']

Use the method signals_add(..) to specify which signals should be acquired during a measurement. The return value gives the exact node path that will be subscribed to before the measurement.

demod_x = mfli.daq.signals_add("demod0", "x")
demod_y = mfli.daq.signals_add("demod0", "y")

# which signal nodes have been added?
['/dev3337/demods/0/sample.x.avg', '/dev3337/demods/0/sample.y.avg']

Use trigger type, grid columns, grid rows and other parameters to configure the measurement you want.


The measurement routine is started using the measure(...) method. At first, the mdoule will subscribe to the signal nodes that were previously added to the measurement. Then the measurement is started. When it is finished, the results will be added to the results property of the daq Module.

subscribed to: /dev3337/demods/0/sample.x.avg
subscribed to: /dev3337/demods/0/sample.y.avg
Progress: 0.0%
Progress: 1.0%
Progress: 7.0%
Progress: 15.0%
Progress: 23.0%
Progress: 34.0%
Progress: 42.0%
Progress: 49.0%
Progress: 56.0%
Progress: 64.0%
Progress: 72.0%
Progress: 80.0%
Progress: 89.0%
Progress: 98.0%

The results property is a dictionary with the exact signal node as its keys. The return value from signals_add(..) can be used to access the result of that signal. The value in the dictionary is a DAQResult object that contains all measurement information such as the measurement values, the time- or frequency-axis as well as meta-information.

<zhinst.toolkit.control.drivers.base.daq.DAQResult object at 0x000002409DA3E3C8>

path:        /dev3337/demods/0/sample.x.avg
value:       (100, 100)
time:        (100,)
<zhinst.toolkit.control.drivers.base.daq.DAQResult object at 0x000002409DA3E400>

path:        /dev3337/demods/0/sample.y.avg
value:       (100, 100)
time:        (100,)
import matplotlib.pyplot as plt

result_x = mfli.daq.results[demod_x]

plt.imshow(result_x.value, extent=[result_x.time[0], result_x.time[-1], 0, 100], aspect="auto")

The Sweeper Module of the MFLI

The MFLI also features a Sweeper Module. For more details on the Sweeper Module please see the according example. The module has all of the following Parameters:

print([k for k in mfli.sweeper.parameters.keys()])
['averaging_sample', 'averaging_tc', 'averaging_time', 'awgcontrol', 'bandwidth', 'bandwidthcontrol', 'bandwidthoverlap', 'clearhistory', 'device', 'endless', 'gridnode', 'historylength', 'loopcount', 'maxbandwidth', 'omegasuppression', 'order', 'phaseunwrap', 'remainingtime', 'samplecount', 'save_csvlocale', 'save_csvseparator', 'save_directory', 'save_fileformat', 'save_filename', 'save_save', 'save_saveonread', 'scan', 'settling_inaccuracy', 'settling_tc', 'settling_time', 'sincfilter', 'start', 'stop', 'xmapping']

As for the Data Acquisition Module the available signal sources can be shown with signals_list(...). They can differ depending on the options installed in the instrument.


The Parameters that are available for sweeping can be listed with sweep_parameter_list().

As for the Data Acquisition Module the available signal sources can be shown with signals_list(...). They can differ depending on the options installed in the instrument.

The device parameters that are available for sweeping can be listed with sweep_parameter_list():


A typical frequency sweep from 1-10 kHz and 100 steps is configured as follows. We add the signal from the first demodulator (‘demod0’) to the measurement.

# turn singal output on

# prepare a frequency sweep

# add a singal source
demod = mfli.sweeper.signals_add("demod0")
set sweep parameter to 'frequency': 'oscs/0/freq'

The measurement is simply started with the measure() method:

# perform measurement
subscribed to: /dev3337/demods/0/sample
Sweeping oscs/0/freq from 1000.0 to 10000.0
Progress: 0.0%
Progress: 4.0%
Progress: 10.0%
Progress: 15.0%
Progress: 20.0%
Progress: 25.0%
Progress: 30.0%
Progress: 35.0%
Progress: 40.0%
Progress: 45.0%
Progress: 50.0%
Progress: 55.0%
Progress: 60.0%
Progress: 65.0%
Progress: 70.0%
Progress: 76.0%
Progress: 81.0%
Progress: 86.0%
Progress: 90.0%
Progress: 95.0%

The results are stored in the results dictionary with the exact node path as the key. The value is a DAQResults object from the zhinst-toolkit. It contains all the data of the demodulator that has been returned by the Python API.

result = mfli.sweeper.results[demod]
<zhinst.toolkit.control.drivers.base.sweeper.SweeperResult object at 0x000002409DEC8518>

 - header
 - timestamp
 - samplecount
 - flags
 - sampleformat
 - sweepmode
 - bandwidthmode
 - auxin0
 - auxin0pwr
 - auxin0stddev
 - auxin1
 - auxin1pwr
 - auxin1stddev
 - bandwidth
 - frequency
 - frequencypwr
 - frequencystddev
 - grid
 - phase
 - phasepwr
 - phasestddev
 - r
 - rpwr
 - rstddev
 - settling
 - tc
 - tcmeas
 - x
 - xpwr
 - xstddev
 - y
 - ypwr
 - ystddev
 - count
 - nexttimestamp
 - settimestamp
import matplotlib.pyplot as plt

plt.plot(result.frequency, result.x)