*** Wartungsfenster jeden ersten Mittwoch vormittag im Monat ***

Skip to content
Snippets Groups Projects
Commit 4a7a9ff5 authored by Schabbauer, Johannes's avatar Schabbauer, Johannes
Browse files

Initial files for labscript and ADwinProII

parent 9ab043a0
No related branches found
No related tags found
No related merge requests found
Showing with 570 additions and 0 deletions
**example**
**/__pycache__/
user_devices/ADwin_old_implementation/
\ No newline at end of file
from labscript import *
# Import classes needed for the devices which will be used
from labscript_devices.PrawnBlaster.labscript_devices import PrawnBlaster
from labscript_devices.NI_DAQmx.models.NI_USB_6363 import NI_USB_6363
from user_devices.ADwinProII.labscript_devices import *
# Create ADwin-Pro II as Pseudoclock
ADwinProII(name="adwin")
#ADwin_DO_Card("DOI32_1", adwin.clockline, 99)
# Create a PrawnBlaster, saved to the variable 'prawn',
# It will be used as the single pseudoclock that triggers other devices
#PrawnBlaster(name='prawn', com_port='COM6', num_pseudoclocks=1)
# Create a NI USB-6363 multifunction I/O device, clocked by the PrawnBlaster
#NI_USB_6363(name='daq', MAX_name='Dev1',
# parent_device=prawn.clocklines[0], clock_terminal='/Dev1/PFI0',
# acquisition_rate=100e3)
# Add analog output channels to the USB-6363
#AnalogOut('ao0', daq, 'ao0')
#AnalogOut('ao1', daq, 'ao1')
# The following is standard boilerplate necessary for the file to compile
if __name__ == '__main__':
start()
stop(1)
\ No newline at end of file
Put your own Python modules and packages here, they will be made available for import system-wide.
\ No newline at end of file
#####################################################################
# #
# ADwinProII/ADwin_utils.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
#####################################################################
############################################################################
# #
# ADwinProII/__init__.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
# Some parts of the code were adapted from an older implementation #
# in the labscript-suite: #
# https://github.com/labscript-suite/labscript/blob/2.1.0/devices/adwin.py #
# #
############################################################################
\ No newline at end of file
#####################################################################
# #
# ADwinProII/blacs_tabs.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
#####################################################################
from blacs.device_base_class import DeviceTab, define_state, MODE_BUFFERED
class ADwinProIITab(DeviceTab):
def initialise_workers(self):
worker_initialisation_kwargs = {}
self.create_worker(
"main_worker",
"user_devices.ADwinProII.blacs_workers.ADwinProIIWorker",
worker_initialisation_kwargs,
)
self.primary_worker = "main_worker"
@define_state(MODE_BUFFERED, True)
def start_run(self, notify_queue):
self.wait_until_done(notify_queue)
@define_state(MODE_BUFFERED, True)
def wait_until_done(self, notify_queue):
"""Call check_if_done repeatedly in the worker until the shot is complete"""
done = yield (self.queue_work(self.primary_worker, 'check_if_done'))
# Experiment is over. Tell the queue manager about it:
if done:
notify_queue.put('done')
else:
# Not actual recursion since this just queues up another call
# after we return:
self.wait_until_done(notify_queue)
#####################################################################
# #
# ADwinProII/blacs_workers.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
#####################################################################
import time
import labscript_utils.h5_lock
import h5py
from blacs.tab_base_classes import Worker
import labscript_utils.properties as properties
class ADwinProIIWorker(Worker):
def program_manual(self, values):
return {}
def transition_to_buffered(self, device_name, h5file, initial_values, fresh):
# get stop time:
with h5py.File(h5file, 'r') as f:
props = properties.get(f, self.device_name, 'device_properties')
self.stop_time = props.get('stop_time', None) # stop_time may be absent if we are not the master pseudoclock
return {}
def check_if_done(self):
# Wait up to 1 second for the shot to be done, returning True if it is
# or False if not.
if getattr(self, 'start_time', None) is None:
self.start_time = time.time()
timeout = min(self.start_time + self.stop_time - time.time(), 1)
if timeout < 0:
return True
time.sleep(timeout)
return self.start_time + self.stop_time < time.time()
def transition_to_manual(self):
self.start_time = None
self.stop_time = None
return True
def shutdown(self):
return
def abort_buffered(self):
return self.transition_to_manual()
#####################################################################
# #
# ADwinProII/labscript_devices.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
#####################################################################
from labscript import Device, Pseudoclock, PseudoclockDevice, IntermediateDevice, ClockLine, AnalogOut, DigitalOut, bitfield, config, LabscriptError
from labscript.functions import ramp, sine, sine_ramp, sine4_ramp, sine4_reverse_ramp, exp_ramp, exp_ramp_t
import numpy as np
default_cycle_time = 2500/1e9 # T12 Processor has 1GHz clock rate, TODO Cycle time in ADbasic
# Notes:
# The ADWin (T12) runs at 1 GHz. The cycle time should be specified in hardware programming in units of this clock speed.
# Subsequently, instruction timing must be specified in units of cycles.
# Voltages are specified with a 16 bit unsigned integer, mapping the range [-10,10) volts.
# There are 32 digital outputs on a card (DIO-32-TiCo)
# There are 8 analog outputs on a card (AOut-8/16)
# There are 8 analog inputs on a card (AIn-F-8/16)
class _ADwinProII(Pseudoclock):
def add_device(self, device):
if isinstance(device, ClockLine):
# only allow one child
if self.child_devices:
raise LabscriptError('The pseudoclock of the ADwin-Pro II %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 ADwinAnalogOut(AnalogOut):
def linear_ramp(self, t, duration, initial, final):
#instruction = LinearRamp(t, duration, initial, final, self.parent_device.clock_limit)
#self.add_instruction(t, instruction)
#return instruction.duration
pass
def sin_ramp(self, t, duration, amplitude, offset, angular_period):
#instruction = SinRamp(t, duration, amplitude, offset, angular_period, self.parent_device.clock_limit)
#self.add_instruction(t, instruction)
#return instruction.duration
pass
def cos_ramp(self, t, duration, amplitude, offset, angular_period):
#instruction = CosRamp(t, duration, amplitude, offset, angular_period, self.parent_device.clock_limit)
#self.add_instruction(t, instruction)
#return instruction.duration
pass
def exp_ramp(self, t, duration, amplitude, offset, time_constant):
#instruction = ExpRamp(t, duration, amplitude, offset, time_constant, self.parent_device.clock_limit)
#self.add_instruction(t, instruction)
#return instruction.duration
pass
class ADwinDigitalOut(DigitalOut):
pass
class ADwinCard(Pseudoclock):
clock_type = 'fast clock'
def __init__(self, name, parent_device, card_number):
self.clock_limit = parent_device.clock_limit
self.clock_resolution = parent_device.clock_resolution
# Device must be accessed via the parent ADWin, so we must store
# the parent's device_no as well as the card number:
self.card_number = card_number
self.BLACS_connection = parent_device.BLACS_connection, card_number
# We won't call IntermediateDevice.__init__(), as we don't care
# about the checks it does for clocking, we don't actually have
# a clock:
Device.__init__(self, name, parent_device, card_number)
self.trigger_times = []
self.wait_times = []
self.initial_trigger_time = 0
def trigger(self, t, *args):
if t == 'initial':
t = self.initial_trigger_time
self.trigger_times.append(t)
else:
raise NotImplementedError("AdWins do not have waits implemented in labscript or the current firmware.")
# Adwin cards are coordinated internally without the need for
# triggering devices. We split them up into pseudoclocks in
# labscript because they are modular in nature, and it helps us
# be compatible with AdWins that have different card setups. But
# half of this pseudoclock stuff isn't relevant to this, so we
# override some methods to do nothing.
# def generate_code(self, hdf5_file):
# # We don't actually need to expand out ramps and construct a pseudoclock or anything
# # but we will anyway so that we have something to plot in runviewer
# expanded_change_times
# for output in self.get_all_outputs():
class ADwin_AO_Card(ADwinCard):
description = 'ADWin analog output card'
allowed_children = [AnalogOut]
def generate_code(self, hdf5_file):
Device.generate_code(self, hdf5_file)
# This group must exist in order for BLACS to know that this
# device is part of the experiment:
group = hdf5_file.create_group('/devices/%s'%self.name)
# OK, let's collect up all the analog instructions!
self.formatted_instructions = []
for output in self.get_all_outputs():
for t, instruction in output.instructions.items():
card_number = self.card_number
channel_number = output.connection
#if isinstance(instruction, RampInstruction):
# duration = instruction.duration
# if isinstance(instruction, LinearRamp):
# ramp_type = 0
# elif isinstance(instruction, SinRamp):
# ramp_type = 1
# elif isinstance(instruction, CosRamp):
# ramp_type = 2
# elif isinstance(instruction, ExpRamp):
# ramp_type = 3
# else:
# raise ValueError(instruction)
# A = instruction.A
# B = instruction.B
# C = instruction.C
# else:
# # Let's construct a ramp out of the single value instruction:
# duration = self.clock_resolution
# ramp_type = 0
# A = instruction
# B = 0
# C = instruction
formatted_instruction = {'t':t,
'duration': duration,
'card': card_number,
'channel': channel_number,
'ramp_type': ramp_type,
'A': A, 'B': B, 'C': C}
self.formatted_instructions.append(formatted_instruction)
class ADwin_DO_Card(ADwinCard):
description = 'ADWin digital output card'
allowed_children = [DigitalOut]
digital_dtype = np.uint32
n_digitals = 32
def generate_code(self, hdf5_file):
Device.generate_code(self, hdf5_file)
# This group must exist in order for BLACS to know that this
# device is part of the experiment:
group = hdf5_file.create_group('/devices/%s'%self.name)
outputs = self.get_all_outputs()
change_times = self.collect_change_times(outputs)
for output in outputs:
output.make_timeseries(change_times)
for time in change_times:
outputarray = [0]*self.n_digitals
for output in outputs:
channel = output.connection
# We have to subtract one from the channel number to get
# the correct index, as ADWin is one-indexed, curse it.
outputarray[channel - 1] = np.array(output.timeseries)
bits = bitfield(outputarray, dtype=self.digital_dtype)
self.formatted_instructions = []
for t, value in zip(change_times, bits):
formatted_instruction = {'t': t, 'card': self.card_number,'bitfield': value}
self.formatted_instructions.append(formatted_instruction)
class ADwinProII(PseudoclockDevice):
description = 'ADWin-Pro II'
clock_limit = 10e6
clock_resolution = 25e-9
trigger_delay = 350e-9
wait_delay = 2.5e-6
allowed_children = [_ADwinProII]
max_instructions = 1e5
#allowed_children = [ADwin_AO_Card, ADwin_DO_Card] # TODO where should this go?? To clockline class?
def __init__(self, name="adwin", device_no=1, cycle_time = default_cycle_time, **kwargs):
PseudoclockDevice.__init__(self, name, None, None, **kwargs)
self.BLACS_connection = name + "_" + str(device_no)
self._pseudoclock = _ADwinProII(
name=f'{name}_pseudoclock',
pseudoclock_device=self,
connection='pseudoclock',
)
self._clock_line = ClockLine(
name=f'{name}_clock_line',
pseudoclock=self.pseudoclock,
connection='internal',
)
# round cycle time to the nearest multiple of 3.3333ns TODO WHY??
quantised_cycle_time = round(cycle_time/3.333333333333e-9)
cycle_time = quantised_cycle_time*3.333333333333e-9
self.clock_limit = 1./cycle_time
self.clock_resolution = cycle_time
self.trigger_times = []
self.wait_times = []
self.initial_trigger_time = 0
@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 automatically creates a Pseudoclock because it only supports one. '%(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 do_checks(self, outputs):
if self.trigger_times != [0]:
raise LabscriptError('ADWin does not support retriggering or waiting.')
for output in outputs:
output.do_checks(self.trigger_times)
def collect_card_instructions(self, hdf5_file):
group = hdf5_file.create_group('/devices/%s'%self.name)
all_analog_instructions = []
all_digital_instructions = []
for device in self.child_devices:
if isinstance(device, ADwin_AO_Card):
all_analog_instructions.extend(device.formatted_instructions)
elif isinstance(device, ADwin_DO_Card):
all_digital_instructions.extend(device.formatted_instructions)
else:
raise AssertionError("Invalid child device, shouldn't be possible")
# Make the analog output table:
analog_dtypes = [('t',np.uint), ('duration',int), ('card',int), ('channel',int),
('ramp_type',int), ('A',int), ('B',int), ('C',int)]
# sort by time:
all_analog_instructions.sort(key=lambda instruction: instruction['t'])
analog_data = np.zeros(len(all_analog_instructions)+1, dtype=analog_dtypes)
for i, instruction in enumerate(all_analog_instructions):
analog_data[i]['t'] = round(instruction['t']/self.clock_resolution)
analog_data[i]['duration'] = round(instruction['duration']/self.clock_resolution)
analog_data[i]['card'] = instruction['card']
analog_data[i]['channel'] = instruction['channel']
analog_data[i]['ramp_type'] = instruction['ramp_type']
if instruction['ramp_type'] in [0]:
# If it's a linear ramp, map the voltages for parameter A from the range [-10,10] to a uint16:
analog_data[i]['A'] = int((instruction['A']+10)/20.*(2**16-1))
elif instruction['ramp_type'] in [1,2,3]:
# For an exp, sine or cos ramp, map A from [-10,10] to a signed int16:
analog_data[i]['A'] = int(instruction['A']/10.*(2**15-1))
else:
raise RuntimeError('Sanity check failed: Invalid ramp type! Something has gone wrong.')
analog_data[i]['B'] = round(instruction['B']/self.clock_resolution) # B has units of time
analog_data[i]['C'] = int((instruction['C']+10)/20.*(2**16-1))
# Add the 'end of data' instruction to the end:
analog_data[-1]['t'] = 2**32-1
# Save to the HDF5 file:
group.create_dataset('ANALOG_OUTS', data=analog_data)
# Make the digital output table:
digital_dtypes = [('t',np.uint), ('card',int), ('bitfield',int)]
# sort by time:
all_digital_instructions.sort(key=lambda instruction: instruction['t'])
digital_data = np.zeros(len(all_digital_instructions)+1, dtype=digital_dtypes)
for i, instruction in enumerate(all_digital_instructions):
digital_data[i]['t'] = round(instruction['t']/self.clock_resolution)
digital_data[i]['card'] = instruction['card']
digital_data[i]['bitfield'] = instruction['bitfield']
# Add the 'end of data' instruction to the end:
digital_data[-1]['t'] = 2**32-1
# Save to the HDF5 file:
group.create_dataset('DIGITAL_OUTS', data=digital_data)
#group.attrs['stop_time'] = self.stop_time/self.clock_resolution
group.attrs['cycle_time'] = self.clock_resolution
def generate_code(self, hdf5_file):
outputs = self.get_all_outputs()
#outputs, outputs_by_clockline = self.get_outputs_by_clockline()
# We call the following to do the error checking it includes,
# but we're not actually interested in the set of change times.
# Each card will handle its own timebase issues.
#ignore = self.collect_change_times(outputs, outputs_by_clockline)
#self.do_checks(outputs)
# This causes the cards to have their generate_code() methods
# called. They collect up the instructions of their outputs,
# and then we will collate them together into one big instruction
# table.
#Device.generate_code(self, hdf5_file)
#self.collect_card_instructions(hdf5_file)
# We don't actually care about these other things that pseudoclock
# classes normally do, but they still do some error checking
# that we want:
#change_times = self.collect_change_times(outputs, outputs_by_clockline)
#for output in outputs:
# output.make_timeseries(change_times)
#all_times, clock = self.expand_change_times(change_times, outputs)
#####################################################################
# #
# ADwinProII/register_classes.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
#####################################################################
import labscript_devices
labscript_device_name = 'ADwinProII'
blacs_tab = 'user_devices.ADwinProII.blacs_tabs.ADwinProIITab'
parser = 'user_devices.ADwinProII.runviewer_parsers.ADwinProIIParser'
labscript_devices.register_classes(
labscript_device_name=labscript_device_name,
BLACS_tab=blacs_tab,
runviewer_parser=parser,
)
#####################################################################
# #
# ADwin/runviever_parsers.py #
# #
# Copyright 2022, TU Vienna #
# #
# Implementation of the ADwin-Pro II for the labscript-suite, #
# used in the Léonard lab for Experimental Quantum Information. #
# #
#####################################################################
import labscript_utils.h5_lock # noqa: F401
import h5py
import numpy as np
class ADwinProIIParser(object):
clock_resolution = 1e-9
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
This is a location that you can put custom labscript device drivers, following
the same layout as device drivers in the labscript_devices module. To import them,
import from `user_devices` instead of `labscript_devices`.
In this way, you can work on device support separately from the main labscript devices
package. However, if you write support for a generally available device, please do
contribute it back and we will include it in the main labscript_devices module.
If you prefer to store custom device code elsewhere, you may modify the `user_devices`
configuration setting in labconfig to be an import path (ie 'module.submodule' format,
not a filepath) to another Python package.
\ No newline at end of file
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