From d2e5679174498e4cb5f6049c32962bc97864461f Mon Sep 17 00:00:00 2001 From: Russell Anderson <5637107+rpanderson@users.noreply.github.com> Date: Fri, 12 Jun 2020 18:47:25 +1000 Subject: [PATCH] Save clock instructions (mock PineBlaster) and add runviewer_parser --- .../DummyPseudoclock/labscript_devices.py | 84 +++++++++++++++++-- .../DummyPseudoclock/register_classes.py | 10 ++- .../DummyPseudoclock/runviewer_parsers.py | 65 ++++++++++++++ 3 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 labscript_devices/DummyPseudoclock/runviewer_parsers.py diff --git a/labscript_devices/DummyPseudoclock/labscript_devices.py b/labscript_devices/DummyPseudoclock/labscript_devices.py index 3f67d2c9..ad314416 100644 --- a/labscript_devices/DummyPseudoclock/labscript_devices.py +++ b/labscript_devices/DummyPseudoclock/labscript_devices.py @@ -15,24 +15,90 @@ # 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(PseudoclockDevice): description = 'Dummy pseudoclock' - clock_limit = 1e6 - clock_resolution = 1e-6 + clock_limit = 10e6 + clock_resolution = 25e-9 + clock_type = 'fast clock' + trigger_delay = 350e-9 + wait_delay = 2.5e-6 + allowed_children = None + 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 = Pseudoclock( + 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 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') \ No newline at end of file + + # 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') diff --git a/labscript_devices/DummyPseudoclock/register_classes.py b/labscript_devices/DummyPseudoclock/register_classes.py index 32948292..4b8d4127 100644 --- a/labscript_devices/DummyPseudoclock/register_classes.py +++ b/labscript_devices/DummyPseudoclock/register_classes.py @@ -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, ) diff --git a/labscript_devices/DummyPseudoclock/runviewer_parsers.py b/labscript_devices/DummyPseudoclock/runviewer_parsers.py new file mode 100644 index 00000000..4e014f67 --- /dev/null +++ b/labscript_devices/DummyPseudoclock/runviewer_parsers.py @@ -0,0 +1,65 @@ +import labscript_utils.h5_lock # noqa: F401 +import h5py +import numpy as np + + +class DummyPseudoclockParser(object): + clock_resolution = 25e-9 + clock_type = 'fast clock' + 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