Skip to content

Save clock instructions and add runviewer parser for DummyPseudoclock #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 94 additions & 9 deletions labscript_devices/DummyPseudoclock/labscript_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,109 @@
# and labscript. The device is a PseudoclockDevice, and can be the sole device
# in a connection table or experiment.

import labscript_utils.h5_lock
import h5py
from labscript import PseudoclockDevice, Pseudoclock, ClockLine
from labscript import PseudoclockDevice, Pseudoclock, ClockLine, config, LabscriptError
import numpy as np

class _DummyPseudoclock(Pseudoclock):
def add_device(self, device):
if isinstance(device, ClockLine):
# only allow one child
if self.child_devices:
raise LabscriptError('The pseudoclock of the DummyPseudoclock %s only supports 1 clockline, which is automatically created. Please use the clockline located at %s.clockline'%(self.parent_device.name, self.parent_device.name))
Pseudoclock.add_device(self, device)
else:
raise LabscriptError('You have connected %s to %s (the Pseudoclock of %s), but %s only supports children that are ClockLines. Please connect your device to %s.clockline instead.'%(device.name, self.name, self.parent_device.name, self.name, self.parent_device.name))


class DummyPseudoclock(PseudoclockDevice):

description = 'Dummy pseudoclock'
clock_limit = 1e6
clock_resolution = 1e-6
clock_limit = 10e6
clock_resolution = 25e-9
trigger_delay = 350e-9
wait_delay = 2.5e-6
allowed_children = [_DummyPseudoclock]
max_instructions = 1e5

def __init__(self, name='dummy_pseudoclock', BLACS_connection='dummy_connection', **kwargs):
def __init__(
self, name='dummy_pseudoclock', BLACS_connection='dummy_connection', **kwargs
):
self.BLACS_connection = BLACS_connection
PseudoclockDevice.__init__(self, name, None, None, **kwargs)
self.pseudoclock = Pseudoclock(self.name + '_pseudoclock', self, 'pseudoclock')
self.clockline = ClockLine(name='clockline', pseudoclock=self.pseudoclock, connection='dummy')
self._pseudoclock = _DummyPseudoclock(
name=f'{name}_pseudoclock',
pseudoclock_device=self,
connection='pseudoclock',
)
self._clock_line = ClockLine(
name=f'{name}_clock_line',
pseudoclock=self.pseudoclock,
connection='internal',
)

@property
def pseudoclock(self):
return self._pseudoclock

@property
def clockline(self):
return self._clock_line

def add_device(self, device):
if not self.child_devices and isinstance(device, Pseudoclock):
PseudoclockDevice.add_device(self, device)
elif isinstance(device, Pseudoclock):
raise LabscriptError('The %s %s automatically creates a Pseudoclock because it only supports one. '%(self.description, self.name) +
'Instead of instantiating your own Pseudoclock object, please use the internal' +
' one stored in %s.pseudoclock'%self.name)
else:
raise LabscriptError('You have connected %s (class %s) to %s, but %s does not support children with that class.'%(device.name, device.__class__, self.name, self.name))

def generate_code(self, hdf5_file):
PseudoclockDevice.generate_code(self, hdf5_file)
group = self.init_device_group(hdf5_file)
self.set_property('stop_time', self.stop_time, location='device_properties')

# Compress clock instructions with the same period
# This will halve the number of instructions roughly,
# since the DummyPseudoclock does not have a 'slow clock'
reduced_instructions = []
for instruction in self.pseudoclock.clock:
if instruction == 'WAIT':
# The following period and reps indicates a wait instruction
reduced_instructions.append({'period': 0, 'reps': 1})
continue
reps = instruction['reps']
# period is in quantised units:
period = int(round(instruction['step'] / self.clock_resolution))
if reduced_instructions and reduced_instructions[-1]['period'] == period:
reduced_instructions[-1]['reps'] += reps
else:
reduced_instructions.append({'period': period, 'reps': reps})
# The following period and reps indicates a stop instruction:
reduced_instructions.append({'period': 0, 'reps': 0})
if len(reduced_instructions) > self.max_instructions:
raise LabscriptError(
"%s %s has too many instructions. It has %d and can only support %d"
% (
self.description,
self.name,
len(reduced_instructions),
self.max_instructions,
)
)
# Store these instructions to the h5 file:
dtypes = [('period', int), ('reps', int)]
pulse_program = np.zeros(len(reduced_instructions), dtype=dtypes)
for i, instruction in enumerate(reduced_instructions):
pulse_program[i]['period'] = instruction['period']
pulse_program[i]['reps'] = instruction['reps']
group.create_dataset(
'PULSE_PROGRAM', compression=config.compression, data=pulse_program
)
# TODO: is this needed, the PulseBlasters don't save it...
self.set_property(
'is_master_pseudoclock',
self.is_master_pseudoclock,
location='device_properties',
)
self.set_property('stop_time', self.stop_time, location='device_properties')
10 changes: 7 additions & 3 deletions labscript_devices/DummyPseudoclock/register_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
#####################################################################
import labscript_devices

labscript_device_name = 'DummyPseudoclock'
blacs_tab = 'labscript_devices.DummyPseudoclock.blacs_tabs.DummyPseudoclockTab'
parser = 'labscript_devices.DummyPseudoclock.runviewer_parsers.DummyPseudoclockParser'

labscript_devices.register_classes(
'DummyPseudoclock',
BLACS_tab='labscript_devices.DummyPseudoclock.blacs_tabs.DummyPseudoclockTab',
runviewer_parser=None, #TODO make a runviwer parser for Dummy pseudoclock!
labscript_device_name=labscript_device_name,
BLACS_tab=blacs_tab,
runviewer_parser=parser,
)
64 changes: 64 additions & 0 deletions labscript_devices/DummyPseudoclock/runviewer_parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import labscript_utils.h5_lock # noqa: F401
import h5py
import numpy as np


class DummyPseudoclockParser(object):
clock_resolution = 25e-9
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mimics the structure of labscript_devices.PineBlaster but could/should probably be extracted from connection table properties.

trigger_delay = 350e-9
wait_delay = 2.5e-6

def __init__(self, path, device):
self.path = path
self.name = device.name
self.device = device

def get_traces(self, add_trace, clock=None):
if clock is not None:
times, clock_value = clock[0], clock[1]
clock_indices = np.where((clock_value[1:] - clock_value[:-1]) == 1)[0] + 1
# If initial clock value is 1, then this counts as a rising edge
# (clock should be 0 before experiment) but this is not picked up
# by the above code. So we insert it!
if clock_value[0] == 1:
clock_indices = np.insert(clock_indices, 0, 0)
clock_ticks = times[clock_indices]

# get the pulse program
with h5py.File(self.path, 'r') as f:
pulse_program = f[f'devices/{self.name}/PULSE_PROGRAM'][:]

time = []
states = []
trigger_index = 0
t = 0 if clock is None else clock_ticks[trigger_index] + self.trigger_delay
trigger_index += 1

clock_factor = self.clock_resolution / 2.0

for row in pulse_program:
if row['period'] == 0:
# special case
if row['reps'] == 1: # WAIT
if clock is not None:
t = clock_ticks[trigger_index] + self.trigger_delay
trigger_index += 1
else:
t += self.wait_delay
else:
for i in range(row['reps']):
for j in range(1, -1, -1):
time.append(t)
states.append(j)
t += row['period'] * clock_factor

clock = (np.array(time), np.array(states))

clocklines_and_triggers = {}
for pseudoclock_name, pseudoclock in self.device.child_list.items():
for clock_line_name, clock_line in pseudoclock.child_list.items():
if clock_line.parent_port == 'internal':
clocklines_and_triggers[clock_line_name] = clock
add_trace(clock_line_name, clock, self.name, clock_line.parent_port)

return clocklines_and_triggers