Skip to content
Snippets Groups Projects
Commit b2de0761 authored by Stephan's avatar Stephan
Browse files

added hvi sequencing

parent 5618ac2d
No related branches found
No related tags found
No related merge requests found
File added
File added
......@@ -47,8 +47,8 @@ class pulselib:
self.awg_markers_to_location = []
self.channel_delays = dict()
for i in self.channels:
channel_delays[i] = 0
for i in self.awg_channels:
self.channel_delays[i] = 0
self.delays = []
self.convertion_matrix= []
......@@ -68,7 +68,7 @@ class pulselib:
self.awg.add_awg('AWG4',awg4)
self.sequencer = sequencer(self.awg, self.segments_bin)
self.sequencer = sequencer(self.awg, self.channel_delays, self.segments_bin)
def add_channel_delay(self, delays):
'''
......@@ -78,13 +78,13 @@ class pulselib:
taken as an extentsion point.
Args:
delays: dict, e.g. {'P1':20, 'P2:16'} delay P1 with 20 ns and P2 with 16 ns
delays: dict, e.g. {'P1':20, 'P2':16} delay P1 with 20 ns and P2 with 16 ns
Returns:
0/Error
'''
for i in delay.items():
if i[0] in self.channels:
for i in delays.items():
if i[0] in self.awg_channels:
self.channel_delays[i[0]] = i[1]
else:
raise ValueError("Channel delay error: Channel '{}' does not exist. Please provide valid input".format(i[0]))
......@@ -101,6 +101,12 @@ class pulselib:
return self.segments_bin.get(name)
def add_sequence(self,name,seq):
'''
name: name for the sequence, if name already present, it will be overwritten.
seq: list of list,
e.g. [ ['name segment 1' (str), number of times to play (int), prescale (int)] ]
prescale (default 0, see keysight manual) (not all awg's will support this).
'''
self.sequencer.add_sequence(name, seq)
def start_sequence(self, name):
......@@ -158,16 +164,18 @@ class segment_bin():
class sequencer():
def __init__(self, awg_system, segment_bin):
def __init__(self, awg_system, channel_delays, segment_bin):
self.awg = awg_system
self.segment_bin = segment_bin
self.channels = segment_bin.channels
self.channel_delays = channel_delays
self.sequences = dict()
def add_sequence(self, name, sequence):
self.sequences[name] = sequence
def start_sequence(self, name):
self.get_sequence_upload_data(name)
self.awg.upload(self.sequences[name], self.get_sequence_upload_data(name))
self.awg.start()
......@@ -182,35 +190,122 @@ class sequencer():
number of times to play
uniqueness -> segment is reusable?
identifiers for marking differnt locations in the ram of the awg.
'''
upload_data = dict()
# put in a getter to make sure there is no error -- it exists...
seq = self.sequences[name]
for i in self.channels:
for chan in self.channels:
sequence_data_single_channel = []
for k in seq:
input_data = {'segment':k[0], 'ntimes':k[1]}
unique = getattr(self.segment_bin.get_segment(k[0]), i).unique
input_data['unique'] = unique
# Make unique uuid's for each segment
if unique == True:
input_data['identifier'] = [uuid.uuid4() for i in range(k[1])]
num_elements = len(seq)
for k in range(len(seq)):
segment_play_info = seq[k]
# if starting segment or finishing segment, here there should be added the delay info.
pre_delay, post_delay = (0,0)
if k == 0:
pre_delay = self.get_pre_delay(chan)
if k == len(seq)-1:
post_delay = self.get_post_delay(chan)
if pre_delay!=0 or post_delay!=0:
rep = segment_play_info[1]
segment_play_info[1] = 1
input_data = self.generate_input_data(segment_play_info, chan, pre_delay, post_delay)
sequence_data_single_channel.append(input_data)
sequence_data_single_channel.append(input_data)
# If only one, go to next segment in the sequence.
if rep == 1 :
continue
else:
segment_play_info[1] = rep -1
upload_data[i] = sequence_data_single_channel
sequence_data_single_channel.append(self.generate_input_data(segment_play_info, chan))
upload_data[chan] = sequence_data_single_channel
return upload_data
def generate_input_data(self, segment_play_info, channel, pre_delay=0, post_delay=0):
'''
function that will generate a dict that defines the input data, this will contain all the neccesary info to upload the segment.
returns:
dict with sequence info for a cerain channel (for parameters see the code).
'''
input_data = {'segment': segment_play_info[0],
'segment_name': self.make_segment_name(segment_play_info[0], pre_delay, post_delay),
'ntimes': segment_play_info[1],
'prescaler': segment_play_info[2],
'pre_delay': pre_delay,
'post_delay': post_delay}
unique = getattr(self.segment_bin.get_segment(segment_play_info[0]), channel).unique
input_data['unique'] = unique
# Make unique uuid's for each segment
if unique == True:
input_data['identifier'] = [uuid.uuid4() for i in range(segment_play_info[1])]
return input_data
def make_segment_name(self, segment, pre_delay, post_delay):
'''
function that makes the name of the segment that is delayed.
Note that if the delay is 0 there should be no new segment name.
'''
segment_name = segment
if pre_delay!=0 or post_delay!= 0:
segment_name = segment + '_' + str(pre_delay) + '_' + str(post_delay)
return segment_name
def calculate_total_channel_delay(self):
'''
function for calculating how many ns time there is a delay in between the channels.
Also support for negative delays...
returns:
tot_delay (the total delay)
max_delay (hight amount of the delay)
'''
delays = np.array( list(self.channel_delays.values()))
tot_delay = np.max(delays) - np.min(delays)
return tot_delay, np.max(delays)
def get_pre_delay(self, channel):
'''
get the of ns that a channel needs to be pushed forward/backward.
returns
pre-delay : number of points that need to be pushed in from of the segment
'''
tot_delay, max_delay = self.calculate_total_channel_delay()
delay = self.channel_delays[channel]
return max_delay- delay
def get_post_delay(self, channel):
'''
get the of ns that a channel needs to be pushed forward/backward.
returns
post-delay: number of points that need to be pushed after the segment
'''
tot_delay, max_delay = self.calculate_total_channel_delay()
delay = self.channel_delays[channel]
return delay - (tot_delay - max_delay)
p = pulselib()
p.add_channel_delay({'B2':0,})
seg = p.mk_segment('INIT')
seg2 = p.mk_segment('Manip')
seg3 = p.mk_segment('Readout')
seg.B0.add_block(2,5,-1)
seg.B0.add_pulse([[20,0],[30,0.5], [30,0]])
seg.B0.add_block(40,70,1)
seg.B0.add_pulse([[70,0],
......@@ -219,10 +314,12 @@ seg.B0.add_pulse([[70,0],
[150,0]])
# append functions?
seg.P1.add_block(2,5,-1)
seg.P1.add_pulse([[100,0.5]
,[800,0.5],
[1400,0]])
seg.B2.add_block(2,5,-1)
seg.B2.add_pulse([[20,0],[30,0.5], [30,0]])
seg.B2.add_block(40,70,1)
seg.B2.add_pulse([[70,0],
......@@ -230,8 +327,8 @@ seg.B2.add_pulse([[70,0],
[150,0.5],
[150,0]])
seg.B4.add_block(1,10,1)
seg.B4.add_block(2,5,1)
# seg.B4.add_block(2,10,1)
# seg.M2.wait(50)
# seg.M2.plot_sequence()
# seg.B0.repeat(20)
......@@ -261,7 +358,7 @@ seg2.B0.wait(2000)
# seg3.B5.wait(2000)
p.show_sequences()
SEQ = [['INIT', 1, 0], ['Manip', 1, 0]]
SEQ = [['INIT', 1, 0], ['Manip', 1, 0], ['Manip', 1, 0]]
p.add_sequence('mysequence', SEQ)
......
import numpy as np
import datetime
from copy import deepcopy
from copy import deepcopy, copy
import matplotlib.pyplot as plt
import qcodes.instrument_drivers.Keysight.SD_common.SD_AWG as keysight_awg
import qcodes.instrument_drivers.Keysight.SD_common.SD_DIG as keysight_dig
import sys
sys.path.append("C:/Program Files (x86)/Keysight/SD1/Libraries/Python/")
import keysightSD1
class keysight_AWG():
def __init__(self, segment_bin, channel_locations,channels, channel_delays):
self.awg = dict()
......@@ -19,6 +24,9 @@ class keysight_AWG():
self.vpp_max = 3 #Volt
# init HVI object
self.HVI = keysightSD1.SD_HVI()
# setting for the amount of voltage you can be off from the optimal setting for a channels
# e.g. when you are suppose to input
self.voltage_tolerance = 0.2
......@@ -40,6 +48,9 @@ class keysight_AWG():
self.maxmem = 1e9
# General data
self.n_rep = 0 # number of times to repeat the sequence (0 is infinite).
self.length_sequence = 1
@property
def allocatable_mem(self):
......@@ -65,10 +76,10 @@ class keysight_AWG():
self.flush_queues()
# step 1 collect vmin and vmax data, check if segments are intialized (e.g. have at least one pulse):
for i in sequence_data_raw:
segment_name = i[0]
if self.segment_bin.used(segment_name) == False:
raise ValueError("Empty segment provided .. (segment name: '{}')".format(segment_name))
v_min_max = self.segment_bin.get_segment(segment_name).Vmin_max_data
segment_id = i[0]
if self.segment_bin.used(segment_id) == False:
raise ValueError("Empty segment provided .. (segment name: '{}')".format(segment_id))
v_min_max = self.segment_bin.get_segment(segment_id).Vmin_max_data
self.adjust_vmin_vmax_data(v_min_max)
# step 2 calculate Vpp/Voff needed for each channel + assign the voltages to each channel.
......@@ -81,45 +92,29 @@ class keysight_AWG():
mem_needed[i] = 0
for chan, sequence_data in sequence_data_processed.items():
# loop trough all elements in the sequence and check how much data is needed.
t = 0
# Add artificial delay data points.
mem_needed[self.channel_locations[chan][0]] += tot_channel_delay
# Check if segments can be reused as expected (if channell has a delay, first and segment cannot be repeated. )
num_items = len(sequence_data)
k = 0
for i in sequence_data:
segment_name = i['segment']
segment_id = i['segment']
segment_name = i['segment_name']
repetitions= i['ntimes']
unique = i['unique']
pre_delay= i['pre_delay']
post_delay = i['post_delay']
if k == 0 or k == num_items-1:
segment_name_delayed += '_' + str(self.channel_delays[chan])
# Check if stuff in the memory, if present, needs to be updated.
if segment_name_delayed in self.segmentdata[chan] and unique == False:
if self.segment_bin.get_segment(segment_name_delayed).last_mod <= self.segmentdata[chan][segment_name_delayed]['last_edit']:
repetitions -= 1
else:
if unique == True:
mem_needed[self.channel_locations[chan][0]] += self.segment_bin.get_segment(segment_name_delayed).total_time
else:
mem_needed[self.channel_locations[chan][0]] += self.segment_bin.get_segment(segment_name_delayed).total_time
# Check if stuff in the memory, if present, needs to be updated.
if segment_name in self.segmentdata[chan] and unique == False:
if self.segment_bin.get_segment(segment_name).last_mod <= self.segmentdata[chan][segment_name]['last_edit']:
continue
# Check if the segment is in the memory and still up to date (if not, go on.)
if self.segment_in_mem(segment_id, segment_name, chan) and unique == False:
continue
if unique == True:
mem_needed[self.channel_locations[chan][0]] += self.segment_bin.get_segment(segment_name).total_time * repetitions
mem_needed[self.channel_locations[chan][0]] += pre_delay + post_delay + self.segment_bin.get_segment(segment_id).total_time * repetitions
else:
mem_needed[self.channel_locations[chan][0]] += self.segment_bin.get_segment(segment_name).total_time
mem_needed[self.channel_locations[chan][0]] += pre_delay + post_delay + self.segment_bin.get_segment(segment_id).total_time
# for i in self.awg:
# print("memory needed for awg {} is {} points.".format(i,mem_needed[i]))
# If memory full, clear (if one is full it is very likely all others are also full, so we will just clear everything.)
for i in self.awg:
......@@ -130,29 +125,32 @@ class keysight_AWG():
# step 4 upload the sequences to the awg.
for chan, sequence_data in sequence_data_processed.items():
# Upload here sequences.
# plt.figure()
# Keep counting time of the segments. This is important for IQ data.
time = 0
for my_segment in sequence_data:
segment_name = my_segment['segment']
segment_id = my_segment['segment']
segment_name = my_segment['segment_name']
repetitions= my_segment['ntimes']
unique = my_segment['unique']
# Check if we need to skip the upload.
if segment_name in self.segmentdata[chan] and unique == False:
if self.segment_bin.get_segment(segment_name).last_mod <= self.segmentdata[chan][segment_name]['last_edit']:
continue
pre_delay= my_segment['pre_delay']
post_delay = my_segment['post_delay']
# Check if the segment is in the memory and still up to date (if not, go on.)
if self.segment_in_mem(segment_id, segment_name, chan) and unique == False:
continue
if unique == False:
points = self.get_and_upload_waveform(chan,segment_name, time)
points = self.get_and_upload_waveform(chan,my_segment, time)
time += points
time += points*repetitions
else:
for uuid in range(repetitions):
# my_segment['identifier'] = list with unique id's
points = self.get_and_upload_waveform(chan,segment_name, time, my_segment['identifier'][uuid])
points = self.get_and_upload_waveform(chan,my_segment, time, my_segment['identifier'][uuid])
time += points
print(self.vpp_data)
self.length_sequence = time
# step 5 make the queue in the AWG.
for chan, sequence_data in sequence_data_processed.items():
......@@ -173,11 +171,11 @@ class keysight_AWG():
trigger_mode = 0
start_delay = 0
cycles = 1
prescaler = 0
prescaler = segmentdata['prescaler']
self.awg[awg_name].awg_queue_waveform(awg_number,seg_num,trigger_mode,start_delay,cycles,prescaler)
else:
seg_num = self.segmentdata[chan][segmentdata['segment']]['mem_pointer']
seg_num = self.segmentdata[chan][segmentdata['segment_name']]['mem_pointer']
if first_element == True:
trigger_mode = 1
first_element = False
......@@ -186,24 +184,47 @@ class keysight_AWG():
start_delay = 0
cycles = segmentdata['ntimes']
prescaler = 0
prescaler = segmentdata['prescaler']
self.awg[awg_name].awg_queue_waveform(awg_number,seg_num,trigger_mode,start_delay,cycles,prescaler)
def get_and_upload_waveform(self, channel, segment_name, time, uuid=None):
def segment_in_mem(self, seg_id, seg_name, channel):
'''
function that checks is certain segment in already present in the memory of the awg
input:
1 list item from a sequence element.
Returns:
True/False
'''
if seg_name in self.segmentdata[channel]:
if self.segment_bin.get_segment(seg_id).last_mod <= self.segmentdata[channel][seg_name]['last_edit']:
return True
return False
def get_and_upload_waveform(self, channel, segment_info, time, uuid=None):
'''
get the wavform for channel with the name segment_name.
The waveform occurs at time time in the sequence.
This function also adds the waveform to segmentdata variable
'''
segment_data = self.segment_bin.get_segment(segment_name).get_waveform(channel, self.vpp_data, time, np.float32)
segment_id = segment_info['segment']
segment_name = segment_info['segment_name']
pre_delay= segment_info['pre_delay']
post_delay = segment_info['post_delay']
# point data of the segment (array to be uploaded).
segment_data = self.segment_bin.get_segment(segment_id).get_waveform(channel, self.vpp_data, time, pre_delay, post_delay, np.float32)
wfv = keysight_awg.SD_AWG.new_waveform_from_double(0, segment_data)
awg_name = self.channel_locations[channel][0]
seg_number = self.get_new_segment_number(channel)
self.awg[awg_name].load_waveform(wfv, seg_number)
# print("plotting {}, {}".format(channel, segment_name))
# print("plotting {}, {}".format(channel, segment_id))
# plt.plot(segment_data)
last_mod = self.segment_bin.get_segment(segment_name).last_mod
last_mod = self.segment_bin.get_segment(segment_id).last_mod
# upload data
if uuid is None:
self.segmentdata[channel][segment_name] = dict()
......@@ -301,12 +322,44 @@ class keysight_AWG():
Triggering is done via the PXI triggers to make sure the that the system works correctly.
'''
# use a awg to send the trigger (does not matter which one)(here the first defined channel)
for chan_name, chan_data in self.channel_locations.items():
self.awg[chan_data[0]].awg_start(chan_data[1])
# Launch the right HVI instance
# Launch the right HVI instance, set right parameters.
self.HVI.stop()
self.HVI.open("C:/V2_code/HVI/For_loop_single_sequence.HVI")
self.HVI.assignHardwareWithIndexAndSlot(0,0,2)
self.HVI.assignHardwareWithIndexAndSlot(1,0,3)
self.HVI.assignHardwareWithIndexAndSlot(2,0,4)
self.HVI.assignHardwareWithIndexAndSlot(3,0,5)
# Length of the sequence
self.HVI.writeIntegerConstantWithIndex(0, "length_sequence", int(self.length_sequence/10 + 1))
self.HVI.writeIntegerConstantWithIndex(1, "length_sequence", int(self.length_sequence/10 + 1))
self.HVI.writeIntegerConstantWithIndex(2, "length_sequence", int(self.length_sequence/10 + 1))
self.HVI.writeIntegerConstantWithIndex(3, "length_sequence", int(self.length_sequence/10 + 1))
# number of repetitions
nrep = self.n_rep
if nrep == 0:
nrep = 1
self.HVI.writeIntegerConstantWithIndex(0, "n_rep", nrep)
self.HVI.writeIntegerConstantWithIndex(1, "n_rep", nrep)
self.HVI.writeIntegerConstantWithIndex(2, "n_rep", nrep)
self.HVI.writeIntegerConstantWithIndex(3, "n_rep", nrep)
# Inifinite looping
step = 1
if self.n_rep == 0:
step = 0
self.HVI.writeIntegerConstantWithIndex(0, "step", step)
self.HVI.writeIntegerConstantWithIndex(1, "step", step)
self.HVI.writeIntegerConstantWithIndex(2, "step", step)
self.HVI.writeIntegerConstantWithIndex(3, "step", step)
self.HVI.compile()
self.HVI.load()
self.HVI.start()
def add_awg(self, name, awg):
'''
......@@ -371,7 +424,7 @@ class keysight_AWG():
tot_delay (the total delay)
'''
delays = np.array( self.channel_delays.values())
delays = np.array( list(self.channel_delays.values()))
tot_delay = np.max(delays) - np.min(delays)
return tot_delay
......@@ -14,7 +14,7 @@ class segment_container():
def __init__(self, name, channels):
self.channels = channels
self.name = name
self.waveform_cache = None
self._Vmin_max_data = dict()
for i in self.channels:
......@@ -50,10 +50,10 @@ class segment_container():
@property
def Vmin_max_data(self):
if self.prev_upload < self.last_mod:
self.prep4upload()
for i in range(len(self.channels)):
self._Vmin_max_data[self.channels[i]]['v_min'] = np.min(self.waveform_cache[i,:])
self._Vmin_max_data[self.channels[i]]['v_max'] = np.max(self.waveform_cache[i,:])
self._Vmin_max_data[self.channels[i]]['v_min'] = getattr(self,self.channels[i]).v_min
self._Vmin_max_data[self.channels[i]]['v_max'] = getattr(self,self.channels[i]).v_max
return self._Vmin_max_data
......@@ -77,34 +77,49 @@ class segment_container():
getattr(self, i).starttime = maxtime
def prep4upload(self):
# make waveform (in chache) (only if needed)
t_tot = self.total_time
# def prep4upload(self):
# # make waveform (in chache) (only if needed)
# t_tot = self.total_time
if self.prev_upload < self.last_mod or self.waveform_cache is None:
self.waveform_cache = np.empty([len(self.channels), int(t_tot)])
# if self.prev_upload < self.last_mod or self.waveform_cache is None:
# self.waveform_cache = np.empty([len(self.channels), int(t_tot)])
for i in range(len(self.channels)):
self.waveform_cache[i,:] = getattr(self, self.channels[i]).get_sequence(t_tot)
# for i in range(len(self.channels)):
# self.waveform_cache[i,:] = getattr(self, self.channels[i]).get_segment(t_tot)
def get_waveform(self, channel, Vpp_data, sequenc_time, return_type = np.double):
# get waforms for required channels. For global Vpp, Voffset settings (per channel) and expected data type
self.prep4upload()
def get_waveform(self, channel, Vpp_data, sequence_time, pre_delay=0, post_delay = 0, return_type = np.double):
'''
function to get the raw data of a waveform,
inputs:
channel: channel name of the waveform you want
Vpp_data: contains peak to peak voltage and offset for each channel
sequence time: efffective time in the sequence when the segment starts, this can be important for when using mulitple segments with IQ modulation.
pre_delay: extra offset in from of the waveform (start at negative time) (for a certain channel, as defined in channel delays)
post_delay: time gets appended to the waveform (for a certain channel)
return type: type of the wavefrom (must be numpy compatible). Here number between -1 and 1.
returns:
waveform as a numpy array with the specified data type.
'''
# self.prep4upload()
upload_data = np.empty([int(self.total_time)], dtype = return_type)
# upload_data = np.empty([int(self.total_time) + pre_delay + post_delay], dtype = return_type)
chan_number = None
for i in range(len(self.channels)):
if self.channels[i] == channel:
chan_number = i
waveform_raw = getattr(self, channel).get_segment(self.total_time, sequence_time, pre_delay, post_delay)
# chan_number = None
# for i in range(len(self.channels)):
# if self.channels[i] == channel:
# chan_number = i
# do not devide by 0 (means channels is not used..)
if Vpp_data[channel]['v_pp'] == 0:
Vpp_data[channel]['v_pp'] = 1
# normalise according to the channel, put as
upload_data = ((self.waveform_cache[chan_number,:] - Vpp_data[channel]['v_off'])/Vpp_data[channel]['v_pp']).astype(return_type)
upload_data = ((waveform_raw - Vpp_data[channel]['v_off'])/Vpp_data[channel]['v_pp']).astype(return_type)
return upload_data
def clear_chache():
......@@ -177,12 +192,18 @@ class segment_single():
def get_total_time(self):
return self.my_pulse_data[-1,0]
def get_sequence(self, points = None):
def get_segment(self, points= None, t_start = 0, pre_delay = 0, post_delay = 0):
'''
Returns a numpy array that contains the points for each ns
points is the expected lenght.
input:
Number of points of the raw sequence (None, if you just want to plot it. (without the delays))
t_start: effective start time in the sequence, needed for unique segments (phase coherent microwaves between segments)
pre_delay : number of points to push before the sequence
post delay: number of points to push after the sequence.
Returns:
A numpy array that contains the points for each ns
points is the expected lenght.
'''
t, wvf = self._generate_sequence(points)
t, wvf = self._generate_segment(points, t_start, pre_delay, post_delay)
return wvf
@property
......@@ -194,20 +215,22 @@ class segment_single():
return np.min(self.my_pulse_data[:,1])
def _generate_sequence(self, t_tot= None):
def _generate_segment(self, t_tot= None, t_start = 0, pre_delay = 0, post_delay = 0):
# TODO implement t_start feature.
# 1 make base sequence:
if t_tot is None:
t_tot = self.total_time
times = np.linspace(0, int(t_tot-1), int(t_tot))
my_sequence = np.zeros([int(t_tot)])
times = np.linspace(-pre_delay, int(t_tot-1 + post_delay), int(t_tot + pre_delay + post_delay))
my_sequence = np.zeros([int(t_tot + pre_delay + post_delay)])
for i in range(0,len(self.my_pulse_data)-1):
my_loc = np.where(times < self.my_pulse_data[i+1,0])[0]
my_loc = np.where(times[my_loc] >= self.my_pulse_data[i,0])[0]
if my_loc.size==0:
if my_loc.size == 0:
continue;
end_voltage = self.my_pulse_data[i,1] + \
......@@ -220,8 +243,8 @@ class segment_single():
return times, my_sequence
def plot_sequence(self):
x,y = self._generate_sequence()
def plot_segment(self):
x,y = self._generate_segment()
plt.plot(x,y)
plt.show()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment