Qcodes example with Swabian Instruments Time Tagger

[1]:
from qcodes_contrib_drivers.drivers.SwabianInstruments.Swabian_Instruments_Time_Tagger import TimeTagger

The driver wraps the TimeTagger python API provided by Swabian Instruments (download here). As the API is very object-oriented, the QCoDeS driver is fairly dynamic in that, for each new measurement type, a new InstrumentChannel of the corresponding type is added to the main instrument. These channels have parameters which correspond to the instantiation arguments of the API objects, as well as gettable parameters which correspond to the measured data.

For more information on the driver design, see the SwabianInstruments/Swabian_Instruments_Time_Tagger.py module docstring.

[2]:
tagger = TimeTagger('tagger')
Connected to: Swabian Instruments Time Tagger 20 (serial:1234567AB8, firmware:TT-20, FW4, OK 3.1) in 1.40s

Measurements

The tagger instrument has submodules (channel lists) for each type of measurement or virtual channel that is implemented and which are at first empty:

[3]:
tagger.submodules
[3]:
{'synchronized_measurements': <TimeTaggerSynchronizedMeasurements: tagger_synchronized_measurements of TimeTagger: tagger>,
 'correlation_measurements': ChannelList(<TimeTagger: tagger>, CorrelationMeasurement, []),
 'combiner_virtual_channels': ChannelList(<TimeTagger: tagger>, CombinerVirtualChannel, []),
 'count_rate_measurements': ChannelList(<TimeTagger: tagger>, CountRateMeasurement, []),
 'histogram_log_bins_measurements': ChannelList(<TimeTagger: tagger>, HistogramLogBinsMeasurement, []),
 'counter_measurements': ChannelList(<TimeTagger: tagger>, CounterMeasurement, []),
 'coincidence_virtual_channels': ChannelList(<TimeTagger: tagger>, CoincidenceVirtualChannel, [])}

To add for example a new Correlation measurement, use the automatically generated helper method add_correlation_measurement, which adds a CorrelationMeasurement channel to the correlation_measurements channel list.

[4]:
correlation = tagger.add_correlation_measurement()
correlation
[4]:
<CorrelationMeasurement: tagger_correlation_1 of TimeTagger: tagger>

At first, the API object is not yet instantiated because arguments are missing:

[5]:
try:
    correlation.api
except RuntimeError as err:
    print(err)
The following parameters need to be initialized first: channels

Instead, we must provide the arguments by setting the parameters (note that units are the same as the API driver, so usually picoseconds):

[6]:
correlation.channels([1, 2])
correlation.binwidth(1)
correlation.n_bins(4_000_000)
correlation.api
[6]:
<TimeTagger.Correlation; proxy of <Swig Object of type 'Correlation *' at 0x00000260E600A630> >

We can now start measuring (we switch on the test signal to have a meaningful output):

[7]:
tagger.set_test_signal([1, 2], state=True)

correlation.start_for(5 * 10 ** 12, clear=True)
correlation.wait_until_finished()

# The capture_duration parameter is inherited by all Measurement classes
print(correlation.capture_duration())  # ps
correlation.data_normalized()
5000000000000
[7]:
array([0., 0., 0., ..., 0., 0., 0.])

Once a measurement is not needed anymore, it may also be removed from the tagger object:

[8]:
tagger.correlation_measurements.remove(correlation)

Virtual Channels

The second type of API object that is implemented are virtual channels. These are objects that behave like a physical channel (i.e., they return time stamps of clicks), but can process events from multiple physical channels (such as aggregation or coincidence counting).

To reproduce the API documentation’s example, do the following:

[9]:
tagger.set_test_signal([1, 2], True)

vc = tagger.add_combiner_virtual_channel()
vc.channels([1, 2])

rate = tagger.add_count_rate_measurement()
rate.channels([1, 2, vc.get_channel()])

rate.start_for(int(1e12), clear=True)
rate.wait_until_finished()

print(rate.data())
[ 815477.  815477. 1630954.]

Synchronized measurements

The TimeTagger driver also implements the SynchronizedMeasurements functionality of the API. This is an object that helps to synchronize multiple different measurements using the same physical tagger. It can either be used by passing the virtual tagger return by its get_tagger() method to the measurement, or by calling the register_measurement() and unregister_measurement() methods:

[10]:
tagger.synchronized_measurements
[10]:
<TimeTaggerSynchronizedMeasurements: tagger_synchronized_measurements of TimeTagger: tagger>
[11]:
sync_tagger = tagger.synchronized_measurements.api_tagger

sync_correlation = tagger.add_correlation_measurement(api_tagger=sync_tagger)
sync_correlation.channels([1, 2])

sync_count_rate = tagger.add_count_rate_measurement(api_tagger=sync_tagger)
sync_count_rate.channels([1, 2])

Measurements are then controlled by the SynchronizedMeasurements object.

Note: Currently, the api property of the measurements and virtual channels needs to be accessed once before using the measurement control methods of the SynchronizedMeasurements object. This is because the Time Tagger API object is only instantiated once the api cached property is accessed (as parameters need to be initialized for this).

[12]:
sync_correlation.api
sync_count_rate.api
[12]:
<TimeTagger.Countrate; proxy of <Swig Object of type 'Countrate *' at 0x00000260FDF97180> >
[13]:
tagger.synchronized_measurements.start_for(int(1e12), clear=True)
tagger.synchronized_measurements.wait_until_finished()

print(sync_count_rate.data())
[815269. 815269.]

Or, equivalently (note that parameters need to be initialized):

[14]:
correlation = tagger.add_correlation_measurement()
correlation.channels([-1, -2])  # falling flank

count_rate = tagger.add_count_rate_measurement()
count_rate.channels([-1, -2])

tagger.synchronized_measurements.register_measurement(correlation)
tagger.synchronized_measurements.register_measurement(count_rate)
# Perform measurement as usual
tagger.synchronized_measurements.unregister_measurement(correlation)
tagger.synchronized_measurements.unregister_measurement(count_rate)

Using this syntax, the api property is automatically accessed by the register_measurement method and the above note does not apply.

[15]:
# Convenience methods to clear all channel lists
tagger.remove_all_measurements()
tagger.remove_all_virtual_channels()
tagger.close()