def generate_clock(self)
# These lines are pretty self-explanatory. You shouldn't need to modify them
outputs = self.get_all_outputs()
self.do_checks(outputs)
self.offset_instructions_from_trigger(outputs)
#This returns an array of time points at which at least one of the channels/devices connected to the pseudoclock
# has requested a state change
change_times = self.collect_change_times(outputs)
# This now loops over all outputs (including all channels on devices, like NI cards, that are clocked by this pseudoclock)
# and gets them to effectively generate dummy instructions for all of the change_times that haven't already explicitly had an
# instruction set
for output in outputs:
output.make_timeseries(change_times)
# This method creates two things:
# all_times: a list of times....sortof. Each element is either a single time point,
# or a numpy array of time points. This is usually used by each Output instance
# to generate output values at the times the clock will tick.
# clock: a high level description of the clock, stored as a list of dictionaries.
# The dictionaries have information like the start time of this clock instruction
# the temporal step size, and the number of ticks at this step size
# So each dictionary can represent multiple clock ticks at a given rate.
all_times, clock = self.expand_change_times(change_times, outputs)
# This tells the Output instances to generate the output values for the times the clock will tick at
for output in outputs:
output.expand_timeseries(all_times)
# This saves some useful information!
self.clock = clock
self.change_times = fastflatten(change_times, float)
# This one might be of particular interest to you as it contains the time of every clock tick.
self.times = fastflatten(all_times,float)
So, what you need to do is put a bunch of this code inside a loop, where the loop is looping over each channel (for devices where each channel is completely separate from other) or groups of channels (for those devices where channels share a "clock")
It should look something like this (note this is pseudocode, not guaranteed to work!)
outputs = self.get_all_outputs()
self.do_checks(outputs)
self.offset_instructions_from_trigger(outputs)
# get groups of channels that share a clock somehow
groups = {'my group 1':[list of instances of Outputs, like instances of the AnalogOut class, that share a clock]}
change_times = {}
self.clock = {}
self.change_times = {}
self.times = {}
# You need to put the original code in a loop something like:
for group_name, list_of_channels_in_group in groups.items():
change_times[group_name] = self.collect_change_times(list_of_channels_in_group)
for output in list_of_channels_in_group:
output.make_timeseries(change_times[group_name])
all_times, clock = self.expand_change_times(change_times, outputs)
for output in list_of_channels_in_group:
output.expand_timeseries(all_times)
self.clock[group_name] = clock
self.change_times[group_name] = fastflatten(change_times, float)
self.times[group_name] = fastflatten(all_times,float)
def generate_code(self)
# calling this will result in your overridden function generate_clock being called
PseudoClock.generate_code(self, hdf5_file)
# you now have a list of time points in self.times and self.clock, for each grouping of channels
# you can access the values for each output in output.raw_output
# so something like:
groups = .... <fill in the blanks>
for group_name, list_of_channels_in_group in groups:
for output in list_of_channels_in_group:
output.raw_output # this is your list of output values for the channel
# write this information to the HDF5 file in the appropriate format.
# store it in '/devices/<device_name>/<whatever you want>'
Other things you should be aware of:
- You should specify the type of children (eg devices, or channels) that can be connected to your pseudoclock by listing the allowed classes in self.allowed_children.
- The slave boards will need to be connected to the master board in labscript via the trigger_device and trigger_connection parameters passed to the __init__ method of the slave pseudoclocks. you may need to "invent" physical connections depending on how the master board triggers the slave boards.
Hopefully that has given you enough to get started! Apologies if it isn't completely coherent, I didn't have much time to read over it!
As always, happy to answer any questions you have and to point you in the right direction (especially if something I wrote isn't clear). I know it can be time consuming trying to understand someone elses code! :)
Cheers,