Source code for qtt.instrument_drivers.virtual_awg

# -*- coding: utf-8 -*-
"""
Created on Wed Aug 31 13:04:09 2016

@author: diepencjv
"""

# %%
import numpy as np
import scipy.signal
import logging
import warnings

import qcodes
from qcodes import Instrument
from qcodes.plots.pyqtgraph import QtPlot
from qcodes.data.data_array import DataArray
import qtt
import qtt.utilities.tools

logger = logging.getLogger(__name__)
# %%


[docs]class virtual_awg(Instrument): """ Attributes: _awgs (list): handles to instruments awg_map (dict) hardware (Instrument): contains AWG to plunger values corr (float): unknown delay_FPGA (float): time delay of signals going through fridge """ def __init__(self, name, instruments=[], awg_map=None, hardware=None, verbose=1, **kwargs): super().__init__(name, **kwargs) logger.info('initialize virtual_awg %s' % name) self._awgs = instruments self.awg_map = awg_map self.hardware = hardware self.verbose = verbose self.delay_FPGA = 2.0e-6 # should depend on filterboxes self.corr = .0 # legacy code, specific for FPGA board not used any more self.maxdatapts = 16e6 # This used to be set to the fpga maximum, but that maximum should not be handled here self.awg_seq = None if len(self._awgs) == 0 and self.verbose: print('no physical AWGs connected') elif len(self._awgs) == 1: self.awg_cont = self._awgs[0] self.awg_cont.set('run_mode', 'CONT') elif len(self._awgs) == 2 and 'awg_mk' in self.awg_map: self.awg_cont = self._awgs[self.awg_map['awg_mk'][0]] self.awg_cont.set('run_mode', 'CONT') self.awg_seq = self._awgs[(self.awg_map['awg_mk'][0] + 1) % 2] self._set_seq_mode(self.awg_seq) self.delay_AWG = self.hardware.parameters['delay_AWG'].get() else: raise Exception( 'Configuration of AWGs not supported by virtual_awg instrument') self.AWG_clock = 1e8 self.ch_amp = 4.0 for awg in self._awgs: awg.set('clock_freq', self.AWG_clock) awg.delete_all_waveforms_from_list() for i in range(1, 5): awg.set('ch%s_amp' % i, self.ch_amp) def _set_seq_mode(self, a): a.set('run_mode', 'SEQ') a.sequence_length.set(1) a.set_sqel_trigger_wait(1, 0)
[docs] def get_idn(self): ''' Overrule because the default VISA command does not work ''' IDN = {'vendor': 'QuTech', 'model': 'virtual_awg', 'serial': None, 'firmware': None} return IDN
[docs] def awg_gate(self, gate): """ Return true of the gate can be controlled by the awg Args: gate () """ if gate is None: return False if isinstance(gate, dict): # vector scan, assume we can do it fast if all components are fast return np.all([self.awg_gate(g) for g in gate]) if self.awg_map is None: return False if gate in self.awg_map: return True else: return False
[docs] def stop(self, verbose=0): ''' Stops all AWGs and turns of all channels ''' for awg in self._awgs: awg.stop() for i in range(1, 5): awg.set('ch%d_state' % i, 0) if verbose: print('Stopped AWGs')
[docs] def sweep_init(self, waveforms, period=1e-3, delete=True, samp_freq=None): ''' Send waveform(s) to gate(s) Arguments: waveforms (dict): the waveforms with the gates as keys period (float): period of the waveform in seconds Returns: sweep_info (dict): the keys are tuples of the awgs and channels to activate Example: -------- >> sweep_info = sweep_init(waveforms) ''' sweepgates = [g for g in waveforms] if delete: for awg in self._awgs: awg.delete_all_waveforms_from_list() awgs = [self._awgs[self.awg_map[g][0]] for g in sweepgates] if 'fpga_mk' in self.awg_map: marker_info = self.awg_map['fpga_mk'] marker_delay = self.delay_FPGA marker_name = 'fpga_mk' elif 'm4i_mk' in self.awg_map: marker_info = self.awg_map['m4i_mk'] if samp_freq is not None: pretrigger_period = 16 / samp_freq else: pretrigger_period = 0 marker_delay = self.delay_FPGA + pretrigger_period marker_name = 'm4i_mk' awgs.append(self._awgs[marker_info[0]]) sweep_info = dict() wave_len = len(waveforms[sweepgates[0]]['wave']) for g in sweepgates: sweep_info[self.awg_map[g]] = dict() sweep_info[self.awg_map[g]]['waveform'] = waveforms[g]['wave'] sweep_info[self.awg_map[g]]['marker1'] = np.zeros(wave_len) sweep_info[self.awg_map[g]]['marker2'] = np.zeros(wave_len) if 'name' in waveforms[g]: sweep_info[self.awg_map[g]]['name'] = waveforms[g]['name'] else: sweep_info[self.awg_map[g]]['name'] = 'waveform_%s' % g if marker_info[:2] == self.awg_map[g]: sweep_info[marker_info[:2]]['delay'] = marker_delay # marker points marker_points = np.zeros(wave_len) marker_points[int(marker_delay * self.AWG_clock):(int(marker_delay * self.AWG_clock) + wave_len // 20)] = 1.0 if marker_info[:2] not in sweep_info: sweep_info[marker_info[:2]] = dict() sweep_info[marker_info[:2]]['waveform'] = np.zeros(wave_len) sweep_info[marker_info[:2]]['marker1'] = np.zeros(wave_len) sweep_info[marker_info[:2]]['marker2'] = np.zeros(wave_len) for g in sweepgates: marker_name += '_%s' % g sweep_info[marker_info[:2]]['name'] = marker_name sweep_info[marker_info[:2]]['delay'] = marker_delay sweep_info[marker_info[:2]]['marker%d' % marker_info[2]] = marker_points self._awgs[marker_info[0]].set( 'ch%i_m%i_low' % (marker_info[1], marker_info[2]), 0) self._awgs[marker_info[0]].set( 'ch%i_m%i_high' % (marker_info[1], marker_info[2]), 2.6) # awg marker if getattr(self, 'awg_seq', None) is not None: awg_info = self.awg_map['awg_mk'] if awg_info[:2] not in sweep_info: awgs.append(self._awgs[awg_info[0]]) sweep_info[awg_info[:2]] = dict() sweep_info[awg_info[:2]]['waveform'] = np.zeros(wave_len) sweep_info[awg_info[:2]]['marker1'] = np.zeros(wave_len) sweep_info[awg_info[:2]]['marker2'] = np.zeros(wave_len) sweep_info[awg_info[:2]]['name'] = 'awg_mk' awg_marker = np.zeros(wave_len) awg_marker[0:wave_len // 20] = 1 awg_marker = np.roll( awg_marker, wave_len - int(self.delay_AWG * self.AWG_clock)) sweep_info[awg_info[:2]]['marker%d' % self.awg_map['awg_mk'][2]] = awg_marker self._awgs[awg_info[0]].set( 'ch%i_m%i_low' % (awg_info[1], awg_info[2]), 0) self._awgs[awg_info[0]].set( 'ch%i_m%i_high' % (awg_info[1], awg_info[2]), 2.6) # send waveforms if delete: for sweep in sweep_info: try: self._awgs[sweep[0]].send_waveform_to_list(sweep_info[sweep]['waveform'], sweep_info[ sweep]['marker1'], sweep_info[sweep]['marker2'], sweep_info[sweep]['name']) except Exception as ex: print(ex) print('sweep_info[sweep][waveform] %s' % (sweep_info[sweep]['waveform'].shape,)) print('sweep_info[sweep][marker1] %s' % (sweep_info[sweep]['marker1'].shape,)) print('sweep_info[sweep][marker2] %s' % (sweep_info[sweep]['marker2'].shape,)) return sweep_info
[docs] def sweep_run(self, sweep_info): ''' Activate AWG(s) and channel(s) for the sweep(s). Arguments: sweep_info (dict): the keys are tuples of the awgs and channels to activate ''' for sweep in sweep_info: if hasattr(self, 'awg_seq') and self._awgs[sweep[0]] == self.awg_seq: self._awgs[sweep[0]].set_sqel_waveform( sweep_info[sweep]['name'], sweep[1], 1) self._awgs[sweep[0]].set_sqel_loopcnt_to_inf(1) self._awgs[sweep[0]].set_sqel_event_jump_target_index( sweep[1], 1) self._awgs[sweep[0]].set_sqel_event_jump_type(1, 'IND') else: self._awgs[sweep[0]].set( 'ch%i_waveform' % sweep[1], sweep_info[sweep]['name']) for sweep in sweep_info: self._awgs[sweep[0]].set('ch%i_state' % sweep[1], 1) awgnrs = set([sweep[0] for sweep in sweep_info]) for nr in awgnrs: self._awgs[nr].run()
[docs] def make_sawtooth(self, sweeprange, period, width=.95, repetitionnr=1, start_zero=False): '''Make a sawtooth with a decline width determined by width. Not yet scaled with awg_to_plunger value. Arguments: sweeprange (float): the range of voltages to sweep over period (float): the period of the triangular signal Returns: wave_raw (array): raw data which represents the waveform ''' samplerate = 1. / self.AWG_clock tt = np.arange(0, period * repetitionnr + samplerate, samplerate) v_wave = float(sweeprange / ((self.ch_amp / 2.0))) wave_raw = (v_wave / 2) * scipy.signal.sawtooth(2 * np.pi * tt / period, width=width) # idx_zero = np.argmin(np.abs(wave_raw)) # wave_raw = np.roll(wave_raw, wave_raw.size-idx_zero) if start_zero: o = int((wave_raw.size) * (1 - width) / 2) wave_raw = np.roll(wave_raw, o) return wave_raw
[docs] def make_pulses(self, voltages, waittimes, reps=1, filtercutoff=None, mvrange=None): """Make a pulse sequence with custom voltage levels and wait times at each level. Arguments: voltages (list of floats): voltage levels to be applied in the sequence waittimes (list of floats): duration of each pulse in the sequence reps (int): number of times to repeat the pulse sequence in the waveform filtercutoff (float): cutoff frequency of a 1st order butterworth filter to make the pulse steps smoother Returns: wave_raw (array): raw data which represents the waveform """ if len(waittimes) != len(voltages): raise Exception('Number of voltage levels must be equal to the number of wait times') samples = [int(x * self.AWG_clock) for x in waittimes] if mvrange is None: mvrange = [max(voltages), min(voltages)] v_wave = float((mvrange[0] - mvrange[1]) / self.ch_amp) v_prop = [2 * ((x - mvrange[1]) / (mvrange[0] - mvrange[1])) - 1 for x in voltages] wave_raw = np.concatenate([x * v_wave * np.ones(y) for x, y in zip(v_prop, samples)]) if filtercutoff is not None: b, a = scipy.signal.butter(1, 0.5 * filtercutoff / self.AWG_clock, btype='low', analog=False, output='ba') wave_raw = scipy.signal.filtfilt(b, a, wave_raw) wave_raw = np.tile(wave_raw, reps) return wave_raw
[docs] def check_frequency_waveform(self, period, width): """ Check whether a sawtooth waveform with specified period can be generated """ old_sr = self.AWG_clock new_sr = 5 / (period * (1 - width)) if (new_sr) > old_sr: warnings.warn('awg sampling frequency %.1f MHz is too low for signal requested (sr %.1f [MHz], period %.1f [ms])' % ( old_sr / 1e6, new_sr / 1e6, 1e3 * period), UserWarning) return new_sr
[docs] def sweep_gate(self, gate, sweeprange, period, width=.95, wave_name=None, delete=True): ''' Send a sawtooth signal with the AWG to a gate to sweep. Also send a marker to the measurement instrument. Args: gate (string): the name of the gate to sweep sweeprange (float): the range of voltages to sweep over period (float): the period of the triangular signal Returns: waveform (dict): The waveform being send with the AWG. sweep_info (dict): the keys are tuples of the awgs and channels to activate Example: >>> waveform, sweep_info = sweep_gate('P1',sweeprange=60,period=1e-3) ''' self.check_frequency_waveform(period, width) self.check_amplitude(gate, sweeprange) start_zero = True waveform = dict() wave_raw = self.make_sawtooth(sweeprange, period, width, start_zero=start_zero) awg_to_plunger = self.hardware.parameters['awg_to_%s' % gate].get() wave = wave_raw / awg_to_plunger waveform[gate] = dict() waveform[gate]['wave'] = wave if wave_name is None: waveform[gate]['name'] = 'sweep_%s' % gate else: waveform[gate]['name'] = wave_name sweep_info = self.sweep_init(waveform, period, delete) self.sweep_run(sweep_info) waveform['width'] = width waveform['start_zero'] = start_zero waveform['sweeprange'] = sweeprange waveform['samplerate'] = 1 / self.AWG_clock waveform['period'] = period for channels in sweep_info: if 'delay' in sweep_info[channels]: waveform['markerdelay'] = sweep_info[channels]['delay'] return waveform, sweep_info
[docs] def sweep_gate_virt(self, gate_comb, sweeprange, period, width=.95, delete=True): ''' Send a sawtooth signal with the AWG to a linear combination of gates to sweep. Also send a marker to the measurement instrument. Arguments: gate_comb (dict): the gates to sweep and the coefficients as values sweeprange (float): the range of voltages to sweep over period (float): the period of the triangular signal Returns: waveform (dict): The waveform being send with the AWG. sweep_info (dict): the keys are tuples of the awgs and channels to activate ''' self.check_frequency_waveform(period, width) waveform = dict() for g in gate_comb: self.check_amplitude(g, gate_comb[g] * sweeprange) for g in gate_comb: wave_raw = self.make_sawtooth(sweeprange, period, width) awg_to_plunger = self.hardware.parameters['awg_to_%s' % g].get() wave = wave_raw * gate_comb[g] / awg_to_plunger waveform[g] = dict() waveform[g]['wave'] = wave waveform[g]['name'] = 'sweep_%s' % g sweep_info = self.sweep_init(waveform, period, delete) self.sweep_run(sweep_info) waveform['width'] = width waveform['sweeprange'] = sweeprange waveform['samplerate'] = 1 / self.AWG_clock waveform['period'] = period for channels in sweep_info: if 'delay' in sweep_info[channels]: waveform['markerdelay'] = sweep_info[channels]['delay'] return waveform, sweep_info
[docs] def sweepandpulse_gate(self, sweepdata, pulsedata, wave_name=None, delete=True, shift_zero=True): ''' Makes and outputs a waveform which overlays a sawtooth signal to sweep a gate, with a pulse sequence. A marker is sent to the measurement instrument at the start of the waveform. IMPORTANT: The function offsets the voltages values so that the last point is 0 V on all gates (i.e. it centers the pulse sequence on the last point) Args: sweepdata (dict): inputs for the sawtooth (gate, sweeprange, period, width). See sweep_gate for more info. pulsedata (dict): inputs for the pulse sequence (gate_voltages, waittimes). See pulse_gates for more info. Returns: waveform (dict): The waveform being sent with the AWG. sweep_info (dict): the keys are tuples of the awgs and channels to activate ''' sweepgate = sweepdata['gate'] sweeprange = sweepdata['sweeprange'] period = sweepdata['period'] width = sweepdata.get('width', 0.95) gate_voltages = pulsedata['gate_voltages'].copy() if shift_zero: for g in gate_voltages: gate_voltages[g] = [x - gate_voltages[g][-1] for x in gate_voltages[g]] waittimes = pulsedata['waittimes'] filtercutoff = pulsedata.get('filtercutoff', None) pulsesamp = [int(round(x * self.AWG_clock)) for x in waittimes] sawsamp = int(round(period * width * self.AWG_clock)) pulsereps = int(np.ceil(self.AWG_clock * period * width / sum(pulsesamp))) allvoltages = np.concatenate([v for v in gate_voltages.values()]) mvrange = [max(allvoltages), min(allvoltages)] self.check_frequency_waveform(period, width) waveform = dict() wave_sweep = self.make_sawtooth(sweeprange, period, width) for g in gate_voltages: self.check_amplitude(g, sweeprange + (mvrange[0] - mvrange[1])) for g in gate_voltages: wave_raw = self.make_pulses(gate_voltages[g], waittimes, reps=pulsereps, filtercutoff=filtercutoff, mvrange=mvrange) wave_raw = wave_raw[:sawsamp] wave_raw = np.pad(wave_raw, (0, len(wave_sweep) - len(wave_raw)), 'edge') if sweepgate == g: wave_raw += wave_sweep awg_to_plunger = self.hardware.parameters['awg_to_%s' % g].get() wave = wave_raw / awg_to_plunger waveform[g] = dict() waveform[g]['wave'] = wave if wave_name is None: waveform[g]['name'] = 'sweepandpulse_%s' % g else: waveform[g]['name'] = wave_name sweep_info = self.sweep_init(waveform, period, delete) self.sweep_run(sweep_info) waveform['width'] = width waveform['sweeprange'] = sweeprange waveform['samplerate'] = 1 / self.AWG_clock waveform['period'] = period waveform['pulse_voltages'] = gate_voltages waveform['pulse_waittimes'] = waittimes for channels in sweep_info: if 'delay' in sweep_info[channels]: waveform['markerdelay'] = sweep_info[channels]['delay'] return waveform, sweep_info
[docs] def sweep_process(self, data, waveform, Naverage=1, direction='forwards', start_offset=1): """ Process the data returned by reading out based on the shape of the sawtooth send with the AWG. Args: data (list or Nxk array): the data (N is the number of samples) waveform (dict): contains the wave and the sawtooth width Naverage (int): number of times the signal was averaged direction (string): option to use backwards signal i.o. forwards Returns: data_processed (array): The data after dropping part of it. Example: >> data_processed = sweep_process(data, waveform, 25) """ width = waveform['width'] if isinstance(data, list): data = np.array(data) if direction == 'forwards': end = int(np.floor(width * data.shape[0] - 1)) data_processed = data[start_offset:end] elif direction == 'backwards': begin = int(np.ceil(width * data.shape[0] + 1)) data_processed = data[begin:] data_processed = data_processed[::-1] data_processed = np.array(data_processed) / Naverage return data_processed
[docs] def sweep_2D(self, samp_freq, sweepgates, sweepranges, resolution, width=.95, comp=None, delete=True): ''' Send sawtooth signals to the sweepgates which effectively do a 2D scan. The first sweepgate is the fast changing gate (on the horizontal axis). Arguments: samp_freq (float): sampling frequency of the measurement instrument in Hertz. sweepgates (list): two strings with names of gates to sweep sweepranges (list): two floats for sweepranges in milliVolts Returns: waveform (dict): The waveforms being send with the AWG. sweep_info (dict): the keys are tuples of the awgs and channels to activate ''' # JP: I think FPGA exceptions should not be handled by awg # if resolution[0] * resolution[1] > self.maxdatapts: # raise Exception('resolution is set higher than FPGA memory allows') if self.corr != 0: raise Exception('please do not use the .corr setting any more') error_corr = resolution[0] * self.corr period_horz = resolution[0] / samp_freq + error_corr period_vert = resolution[1] * period_horz self.check_frequency_waveform(period_horz, width) for g, r in zip(sweepgates, sweepranges): self.check_amplitude(g, r) waveform = dict() # horizontal waveform wave_horz_raw = self.make_sawtooth( sweepranges[0], period_horz, repetitionnr=resolution[1]) awg_to_plunger_horz = self.hardware.parameters[ 'awg_to_%s' % sweepgates[0]].get() wave_horz = wave_horz_raw / awg_to_plunger_horz waveform[sweepgates[0]] = dict() waveform[sweepgates[0]]['wave'] = wave_horz waveform[sweepgates[0]]['name'] = 'sweep_2D_horz_%s' % sweepgates[0] # vertical waveform wave_vert_raw = self.make_sawtooth(sweepranges[1], period_vert) awg_to_plunger_vert = self.hardware.parameters[ 'awg_to_%s' % sweepgates[1]].get() wave_vert = wave_vert_raw / awg_to_plunger_vert waveform[sweepgates[1]] = dict() waveform[sweepgates[1]]['wave'] = wave_vert waveform[sweepgates[1]]['name'] = 'sweep_2D_vert_%s' % sweepgates[1] if comp is not None: for g in comp: if g not in sweepgates: waveform[g] = dict() waveform[g]['wave'] = comp[g]['vert'] * \ wave_vert + comp[g]['horz'] * wave_horz waveform[g]['name'] = 'sweep_2D_comp_%s' % g else: raise Exception('Can not compensate a sweepgate') sweep_info = self.sweep_init(waveform, period=period_vert, delete=delete, samp_freq=samp_freq) self.sweep_run(sweep_info) waveform['width_horz'] = width waveform['sweeprange_horz'] = sweepranges[0] waveform['width_vert'] = width waveform['sweeprange_vert'] = sweepranges[1] waveform['resolution'] = resolution waveform['samplerate'] = 1 / self.AWG_clock waveform['period'] = period_vert waveform['period_horz'] = period_horz for channels in sweep_info: if 'delay' in sweep_info[channels]: waveform['markerdelay'] = sweep_info[channels]['delay'] return waveform, sweep_info
[docs] def sweep_2D_virt(self, samp_freq, gates_horz, gates_vert, sweepranges, resolution, width=.95, delete=True): ''' Send sawtooth signals to the linear combinations of gates set by gates_horz and gates_vert which effectively do a 2D scan of two virtual gates. The horizontal direction is the direction where the AWG signal is changing fastest. It is the first element in the resolution and sweepranges. Arguments: samp_freq (float): sampling frequency of the measurement instrument in Hertz. gates_horz (dict): the gates for the horizontal direction and their coefficients gates_vert (dict): the gates for the vertical direction and their coefficients sweepranges (list): two floats for sweepranges in milliVolts resolution (list): two ints for numbers of pixels Returns: waveform (dict): The waveforms being send with the AWG. sweep_info (dict): the keys are tuples of the awgs and channels to activate ''' # JP: I think FPGA exceptions should not be handled by awg # if resolution[0] * resolution[1] > self.maxdatapts: # raise Exception('resolution is set higher than memory allows') error_corr = resolution[0] * self.corr period_horz = resolution[0] / samp_freq + error_corr period_vert = resolution[1] * period_horz new_sr = self.check_frequency_waveform(period_horz, width) # self.reset_AWG(new_sr) waveform = dict() # horizontal virtual gate for g in gates_horz: self.check_amplitude(g, sweepranges[0] * gates_horz[g]) for g in gates_horz: wave_raw = self.make_sawtooth(sweepranges[0], period_horz, repetitionnr=resolution[1]) awg_to_plunger = self.hardware.parameters['awg_to_%s' % g].get() wave = wave_raw * gates_horz[g] / awg_to_plunger waveform[g] = dict() waveform[g]['wave'] = wave waveform[g]['name'] = 'sweep_2D_virt_%s' % g # vertical virtual gate for g in gates_vert: self.check_amplitude(g, sweepranges[1] * gates_vert[g]) for g in gates_vert: wave_raw = self.make_sawtooth(sweepranges[1], period_vert) awg_to_plunger = self.hardware.parameters['awg_to_%s' % g].get() wave = wave_raw * gates_vert[g] / awg_to_plunger if g in waveform: waveform[g]['wave'] = waveform[g]['wave'] + wave else: waveform[g] = dict() waveform[g]['wave'] = wave waveform[g]['name'] = 'sweep_2D_virt_%s' % g # TODO: Implement compensation of sensing dot plunger sweep_info = self.sweep_init(waveform, period=period_vert, delete=delete, samp_freq=samp_freq) self.sweep_run(sweep_info) waveform['width_horz'] = width waveform['sweeprange_horz'] = sweepranges[0] waveform['width_vert'] = width waveform['sweeprange_vert'] = sweepranges[1] waveform['resolution'] = resolution waveform['samplerate'] = 1 / self.AWG_clock waveform['period'] = period_vert waveform['period_horz'] = period_horz for channels in sweep_info: if 'delay' in sweep_info[channels]: waveform['markerdelay'] = sweep_info[channels]['delay'] return waveform, sweep_info
[docs] def sweep_2D_process(self, data, waveform, diff_dir=None): ''' Process data from sweep_2D Arguments: data (list): the raw measured data waveform (dict): The waveforms that was sent with the AWG. Returns: data_processed (list): the processed data ''' width_horz = waveform['width_horz'] width_vert = waveform['width_vert'] resolution = waveform['resolution'] # split up the fpga data in chunks of horizontal sweeps chunks_ch1 = [data[x:x + resolution[0]] for x in range(0, len(data), resolution[0])] chunks_ch1 = [chunks_ch1[i][1:int(width_horz * len(chunks_ch1[i]))] for i in range(0, len(chunks_ch1))] data_processed = chunks_ch1[:int(width_vert * len(chunks_ch1))] if diff_dir is not None: data_processed = qtt.utilities.tools.diffImageSmooth(data_processed, dy=diff_dir, sigma=1) return data_processed
[docs] def pulse_gates(self, gate_voltages, waittimes, reps=1, filtercutoff=None, reset_to_zero=False, delete=True): ''' Send a pulse sequence with the AWG that can span over any gate space. Sends a marker to measurement instrument at the start of the sequence. Only works with physical gates. Arguments: gate_voltages (dict): keys are gates to apply the sequence to, and values are arrays with the voltage levels to be applied in the sequence waittimes (list of floats): duration of each pulse in the sequence reset_to_zero (bool): if True, the function offsets the voltages values so that the last point is 0V on all gates (i.e. it centers the pulse sequence on the last point). Returns: waveform (dict): The waveform being send with the AWG. sweep_info (dict): the keys are tuples of the awgs and channels to activate ''' period = sum(waittimes) if reset_to_zero: for g in gate_voltages: gate_voltages[g] = [x - gate_voltages[g][-1] for x in gate_voltages[g]] allvoltages = np.concatenate([v for v in gate_voltages.values()]) mvrange = [max(allvoltages), min(allvoltages)] waveform = dict() for g in gate_voltages: wave_raw = self.make_pulses(gate_voltages[g], waittimes, reps=reps, filtercutoff=filtercutoff, mvrange=mvrange) awg_to_plunger = self.hardware.parameters['awg_to_%s' % g].get() wave = wave_raw / awg_to_plunger waveform[g] = dict() waveform[g]['wave'] = wave waveform[g]['name'] = 'pulses_%s' % g sweep_info = self.sweep_init(waveform, period, delete) self.sweep_run(sweep_info) waveform['voltages'] = gate_voltages waveform['samplerate'] = 1 / self.AWG_clock waveform['waittimes'] = waittimes for channels in sweep_info: if 'delay' in sweep_info[channels]: waveform['markerdelay'] = sweep_info[channels]['delay'] return waveform, sweep_info
[docs] def reset_AWG(self, clock=1e8): """ Reset AWG to videomode and scanfast """ self.AWG_clock = clock for a in self._awgs: a.clock_freq.set(clock) a.trigger_mode.set('CONT') a.trigger_source.set('INT') for ii in range(1, 5): f = getattr(a, 'ch%d_amp' % ii) val = f() if val != 4.0: warnings.warn('AWG channel %d output not at 4.0 V' % ii) if self.awg_seq is not None: self._set_seq_mode(self.awg_seq)
[docs] def set_amplitude(self, amplitude): """ Set the AWG peak-to-peak amplitude for all channels Args: amplitude (float): peak-to-peak amplitude (V) """ if amplitude < 0.02: warnings.warn('Trying to set AWG amplitude too low, setting it to minimum (20mV)') amplitude = 0.02 elif amplitude > 4.5: warnings.warn('Trying to set AWG amplitude too high, setting it to maximum (4.5V)') amplitude = 4.5 # tektronics 5014 has precision of 1mV self.ch_amp = round(amplitude, 3) for awg in self._awgs: for i in range(1, 5): awg.set('ch%s_amp' % i, self.ch_amp)
[docs] def check_amplitude(self, gate, mvrange): """ Calculates the lowest allowable AWG peak-to-peak amplitude based on the ranges to be applied to the gates. If the AWG amplitude is too low, it gives a warning and increases the amplitude. Args: gate (str): name of the gate to check mvrange (float): voltage range, in mV, that the gate needs to reach """ min_amp = mvrange / self.hardware.parameters['awg_to_%s' % gate].get() if min_amp > 4: raise(Exception('Sweep range of gate %s is larger than maximum allowed by the AWG' % gate)) if self.ch_amp < min_amp: min_amp = np.ceil(min_amp * 10) / 10 self.set_amplitude(min_amp) warnings.warn('AWG amplitude too low for this range, setting to %.1f' % min_amp)
# %%
[docs]def plot_wave_raw(wave_raw, samplerate=None, station=None): ''' Plot the raw wave Arguments: wave_raw (array): raw data which represents the waveform Returns: plot (QtPlot): the plot showing the data ''' if samplerate is None: if station is None: raise Exception('There is no station') samplerate = 1 / station.awg.getattr('AWG_clock') else: samplerate = samplerate horz_var = np.arange(0, len(wave_raw) * samplerate, samplerate) x = DataArray(name='time(s)', label='time (s)', preset_data=horz_var, is_setpoint=True) y = DataArray( label='sweep value (mV)', preset_data=wave_raw, set_arrays=(x,)) plot = QtPlot(x, y) return plot
[docs]def sweep_2D_process(data, waveform, diff_dir=None): ''' Process data from sweep_2D Arguments: data (list): the raw measured data waveform (dict): The waveforms that was sent with the AWG. Returns: data_processed (list): the processed data ''' width_horz = waveform['width_horz'] width_vert = waveform['width_vert'] resolution = waveform['resolution'] # split up the fpga data in chunks of horizontal sweeps chunks_ch1 = [data[x:x + resolution[0]] for x in range(0, len(data), resolution[0])] chunks_ch1 = [chunks_ch1[i][1:int(width_horz * len(chunks_ch1[i]))] for i in range(0, len(chunks_ch1))] data_processed = chunks_ch1[:int(width_vert * len(chunks_ch1))] if diff_dir is not None: data_processed = qtt.utilities.tools.diffImageSmooth(data_processed, dy=diff_dir, sigma=1) return data_processed