Skip to content
This repository has been archived by the owner on Dec 15, 2023. It is now read-only.

Commit

Permalink
[DEM-943] Feature UHFLI integration (#573)
Browse files Browse the repository at this point in the history
* [DEM-943] Moved number of averages to scope reader acquire.
* [DEM-943] Updated processing example.
* [DEM-943] Added channel limit setter.
* [DEM-943] Added combined example. Changed to old processing.
* [DEM-943] Added segment scope reader tests.
* [DEM-943] Removed needed ScopeReader name in scans.
* [DEM-943] Unit scope reader.
* [DEM-943] Updated stimulus functions.
* [DEM-943] Changed default arguments.
* [DEM-943] Resolved review comments.
* [DEM-943] Changed output marker and sweep initial waiting time.
* [DEM-943] Decorated property not supported (python/mypy#1362).
* [DEM-943] Enable external clock UHFLI.
* [DEM-943] Map misses setting first channel.
* [DEM-943] Data is one element longer?
* [DEM-943] Addressed review comments.
  • Loading branch information
CumulonimbusCalvus authored Jun 26, 2019
1 parent 09d891f commit beaedca
Show file tree
Hide file tree
Showing 15 changed files with 669 additions and 152 deletions.
1 change: 1 addition & 0 deletions docs/notebooks/unfinished/HDAWG8_dev8048.dat

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/notebooks/unfinished/UHFLI_dev2338.dat

Large diffs are not rendered by default.

230 changes: 230 additions & 0 deletions docs/notebooks/unfinished/example_UHFLI_measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
#%% Importing the modules and defining functions
import os
from typing import Optional, Tuple, Dict

from matplotlib import pyplot as plt
from qcodes import Instrument, ManualParameter, Station
from qcodes.instrument_drivers.ZI.ZIHDAWG8 import WARNING_ANY
from qcodes.utils.validators import Numbers
from qilib.configuration_helper import InstrumentAdapterFactory

from qtt.instrument_drivers.virtual_instruments import VirtualIVVI
from qtt.instrument_drivers.virtualAwg.virtual_awg import VirtualAwg
from qtt.measurements.acquisition import UHFLIScopeReader, UHFLIStimulus, load_configuration
from qtt.measurements.scans import scan1D, scanjob_t
from qtt.measurements.videomode import VideoMode


def update_stimulus(is_enabled: bool, signal_output: int = 1, signal_input: Optional[int] = None,
amplitude: float = 0.1, oscillator: int = 1) -> None:
""" Sets the enabled status of a demodulator and connects an oscillator to a demodulator.
After that sets for a signal output, the amplitude and enabled status and finally to of the UHFLI.
Note that each oscillator and demodulator is connected one-to-one in this function.
Also the output signal and demodulator input signal is connected one-to-one.
Args:
is_enabled: True to enable and False to disable.
signal_output: One of the two outputs on the device.
signal_input: One of the two inputs on the device.
amplitude: Amplitude in volts, allowed values are 0.0 - 1.5 V.
demodulator: Which demodulator used to connect to the signal output to.
"""
demodulator = oscillator
if not signal_input:
signal_input = signal_output

stimulus.connect_oscillator_to_demodulator(oscillator, demodulator)
stimulus.set_demodulator_signal_input(demodulator, signal_input)
stimulus.set_demodulation_enabled(demodulator, is_enabled)

stimulus.set_signal_output_amplitude(signal_output, demodulator, amplitude)
stimulus.set_signal_output_enabled(signal_output, demodulator, is_enabled)
stimulus.set_output_enabled(signal_output, is_enabled)


def set_stimulus_oscillator_frequency(frequency: float, oscillator: int = 1) -> None:
""" Sets the frequency of an oscillator in the UHFLI.
Note this function only works with the UHFLI multifrequency option enabled (MF option).
Args:
frequency: The set frequency of the oscillator in Hz.
oscillator: The oscillator (1 - 8) to set the frequency on.
"""
stimulus.set_oscillator_frequency(oscillator, frequency)


def update_scope(period: Optional[float] = None, sample_rate: Optional[float] = 27e3,
input_channels : Optional[Dict[str, int]] = None,
input_ranges: Optional[Tuple[float, float]] = (1.0, 1.0),
limits: Optional[Tuple[float, float]] = (0.0, 1.0),
trigger_enabled: Optional[bool] = False, trigger_level: Optional[float] = 0.5):
""" Updates the settings of the UHFLI scope module needed for readout.
Args:
period: The measuring period of the acquisition in seconds.
sample_rate: The sample rate of the acquisition device in samples per second.
input_channels: A dictionary containing the signal names and input channels, e.g. {'Demod 1 R': 1}.
input_ranges: The gain in Volt of the analog input amplifier for both input channels.
limits: The limits in Volt of the scope full scale range.
trigger_enabled: Will enable the external triggering on 'Trigger input 1' if True.
trigger_level: The level of the trigger in Volt.
"""
if period is not None:
scope_reader.period = period

if input_channels is None:
input_channels = {'Demod 1 R': 1}

nearest_sample_rate = scope_reader.get_nearest_sample_rate(sample_rate)
scope_reader.sample_rate = nearest_sample_rate

scope_reader.input_range = input_ranges
unique_channels = set(input_channels.values())
scope_reader.enabled_channels = unique_channels

[scope_reader.set_channel_limits(channel, *limits) for channel in unique_channels]
[scope_reader.set_input_signal(channel, attribute) for (attribute, channel) in input_channels.items()]

scope_reader.trigger_enabled = trigger_enabled
scope_reader.trigger_channel = 'Trig Input 1'
scope_reader.trigger_level = trigger_level
scope_reader.trigger_slope = 'Rise'
scope_reader.trigger_delay = 0


# Settings

class HardwareSettings(Instrument):

def __init__(self, name='settings'):
super().__init__(name)
awg_gates = {'P1': (0, 0), 'P2': (0, 1)}
awg_markers = {'m4i_mk': (0, 0, 0)}
self.awg_map = {**awg_gates, **awg_markers}

for awg_gate in self.awg_map:
parameter_name = 'awg_to_{}'.format(awg_gate)
parameter_label = '{} (factor)'.format(parameter_name)
self.add_parameter(parameter_name, parameter_class=ManualParameter,
initial_value=1000, label=parameter_label, vals=Numbers(1, 1000))


working_directory = "<work_directory>\\qtt\\docs\\notebooks\\unfinished"

# Lock-in Amplifier

uhfli_id = 'dev2338'
file_path_UHFLI = os.path.join(working_directory, f'UHFLI_{uhfli_id}.dat')
uhfli_configuration = load_configuration(file_path_UHFLI)

stimulus = UHFLIStimulus(uhfli_id)
stimulus.initialize(uhfli_configuration)
scope_reader = UHFLIScopeReader(uhfli_id)

scope_reader.adapter.instrument.external_clock_enabled('ON')

# AWG

hdawg8_id = 'dev8048'
file_path_HDAWG8 = os.path.join(working_directory, f'HDAWG8_{hdawg8_id}.dat')
hdawg8_configuration = load_configuration(file_path_HDAWG8)

awg_adapter = InstrumentAdapterFactory.get_instrument_adapter('ZIHDAWG8InstrumentAdapter', hdawg8_id)
awg_adapter.apply(hdawg8_configuration)

awg_adapter.instrument.warnings_as_errors.append(WARNING_ANY)

external_clock = 1
awg_adapter.instrument.system_clocks_referenceclock_source(external_clock)

grouping_1x8 = 2
awg_adapter.instrument.set_channel_grouping(grouping_1x8)

output1_marker1 = 4
awg_adapter.instrument.triggers_out_0_source(output1_marker1)

awg_sampling_rate_586KHz = 12
awg_adapter.instrument.awgs_0_time(awg_sampling_rate_586KHz)


# Virtual AWG

settings = HardwareSettings()
virtual_awg = VirtualAwg([awg_adapter.instrument], settings)

marker_delay = 16e-6
virtual_awg.digitizer_marker_delay(marker_delay)

marker_uptime = 1e-2
virtual_awg.digitizer_marker_uptime(marker_uptime)


# Station

gates = VirtualIVVI('gates', gates=['P1', 'P2'], model=None)
station = Station(virtual_awg, scope_reader.adapter.instrument, awg_adapter.instrument, gates)

#%% Enable the stimulus

demodulator = 1
signal_output = 1
oscillator = demodulator

update_stimulus(is_enabled=True, signal_output=signal_output, amplitude=0.1, oscillator=oscillator)

#%% Sensing a resonance

signal_input = signal_output

scanjob = scanjob_t(wait_time_startscan=1)
scanjob.add_sweep(param=stimulus.set_oscillator_frequency(oscillator), start=140e6, end=180e6, step=0.2e6)
scanjob.add_minstrument([scope_reader.acquire_single_sample(demodulator, 'R', partial=True),
scope_reader.acquire_single_sample(demodulator, 'x', partial=True),
scope_reader.acquire_single_sample(demodulator, 'y', partial=True)])

data_set = scan1D(station, scanjob)

plt.clf()
plt.plot(data_set.arrays[f'oscillator{demodulator}_freq'], data_set.arrays[f'demod{demodulator}_R'])
plt.plot(data_set.arrays[f'oscillator{demodulator}_freq'], data_set.arrays[f'demod{demodulator}_x'])
plt.plot(data_set.arrays[f'oscillator{demodulator}_freq'], data_set.arrays[f'demod{demodulator}_y'])
plt.show()


#%% 1D readout

period = 1.0
frequency = 154.8e6

update_scope(period)
set_stimulus_oscillator_frequency(frequency)

scope_reader.start_acquisition()
data_arrays = scope_reader.acquire(number_of_averages=1)
scope_reader.stop_acquisition()

plt.clf()
plt.plot(data_arrays[0].set_arrays[0], data_arrays[0])
plt.show()


#%% 2D video mode

sample_rate = 220e3

update_scope(sample_rate = sample_rate, trigger_enabled = True)

resolution = [96, 96]
sweep_gates = [{'P1': 1}, {'P2': 1}]
sweep_ranges = [1000, 1000]
scope = (scope_reader, [1], ['Demod 1 R'])

vm = VideoMode(station, sweep_gates, sweep_ranges, minstrument=scope, resolution=resolution)
vm.updatebg()


#%% Disable the stimulus

update_stimulus(is_enabled=False, signal_output=signal_output, oscillator=oscillator)
30 changes: 17 additions & 13 deletions docs/notebooks/unfinished/example_process_sawtooth_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,54 @@

# DUMMY DATASET CREATION

pixels_x = 100
upwards_edge_pixels_x = 90
pixels_x = 96
upwards_edge_pixels_x = 94

pixels_y = 50
upwards_edge_pixels_y = 45
pixels_y = 96
upwards_edge_pixels_y = 94

sample_rate = 21e7 # samples per seconds.
sample_rate = 21e3 # samples per seconds.
period = pixels_x * pixels_y / sample_rate # seconds

time = np.linspace(0, period, np.rint(period * sample_rate))
set_array = DataArray('ScopeTime', 'Time', unit='seconds', is_setpoint=True, preset_data=time)

processing = 'center'
width = [upwards_edge_pixels_x/pixels_x, upwards_edge_pixels_y/pixels_y]
resolution = [pixels_x, pixels_y]


def create_dummy_data_array(width: float, sawteeth_count: int, channel_index: int = 1, trace_number: int = 1):
def create_dummy_data_array(width: float, processing: str, sawteeth_count: int, channel_index: int = 1,
trace_number: int = 1) -> DataArray:
identifier = 'ScopeTrace_{:03d}'.format(trace_number)
label = 'Channel_{}'.format(channel_index)
scope_data = sawtooth(2 * np.pi * sawteeth_count * time / period, width)
scope_data = sawtooth(2 * np.pi * (sawteeth_count * time / period), width)
offset = {'left': 1 - width, 'center': (1 - width /2), 'right': 0}
scope_data = np.roll(scope_data, int(offset[processing] * len(scope_data)))
return DataArray(identifier, label, preset_data=scope_data, set_arrays=[set_array])


data_set = DataSet()
data_set.user_data = {'resolution': resolution, 'width': width}
data_set.user_data = {'resolution': resolution, 'width': width, 'processing': processing}

data_array_x = create_dummy_data_array(width=width[0], sawteeth_count=1, channel_index=1, trace_number=1)
data_array_x = create_dummy_data_array(width=width[0], processing=processing, sawteeth_count=1, channel_index=1, trace_number=1)
data_set.add_array(data_array_x)

data_array_y = create_dummy_data_array(width=width[1], sawteeth_count=resolution[1], channel_index=2, trace_number=2)
data_array_y = create_dummy_data_array(width=width[1], processing=processing, sawteeth_count=resolution[1], channel_index=2, trace_number=2)
data_set.add_array(data_array_y)


## EXAMPLE PRCCESSING 2D SAWTOOTH
# EXAMPLE PRCCESSING 2D SAWTOOTH

signal_processor = SignalProcessorRunner()
signal_processor.add_signal_processor(ProcessSawtooth2D())
processed_data_set = signal_processor.run(data_set)


## PLOTTING
# PLOTTING

color_cycler = cycle('bgrcmk')


def plot_1D_dataset(data_set, label_x, label_y, figure_number=100):
plt.figure(figure_number)
plt.clf()
Expand Down
1 change: 0 additions & 1 deletion docs/notebooks/unfinished/uhfli.dat

This file was deleted.

1 change: 1 addition & 0 deletions src/qtt/instrument_drivers/virtualAwg/virtual_awg.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ def sequence_gates(self, sequences, do_upload=True):
continue

sequence_data = Sequencer.get_data(sequence, sampling_rate)
sequence_data = sequence_data[:-1]
if not marker_number:
awg_to_gate = self._settings.parameters['awg_to_{}'.format(gate_name)].get()
scaling_ratio = 1 / (awg_to_gate * gain_factor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ def start_acquisition(self) -> None:
"""

@abstractmethod
def acquire(self, number_of_records: int, timeout: float) -> List[DataArray]:
def acquire(self, number_of_averages: int, timeout: float = 30) -> List[DataArray]:
""" Reads raw-data from the acquisition device.
This method should be called after initialising and starting the acquisition.
Args:
number_of_records: The number of records collected.
number_of_averages: The number of averages taken during acquiring.
timeout: The maximum period in seconds to acquire records.
Returns:
Expand Down
Loading

0 comments on commit beaedca

Please sign in to comment.