|
|
|
@@ -71,10 +71,13 @@ from . import dac
|
|
|
|
|
class Experiment:
|
|
|
|
|
"""
|
|
|
|
|
Class holding the complete state tree for a single experiment. This state tree represents one
|
|
|
|
|
program on the pulse card. It is written in a single file and picked up by the backend.
|
|
|
|
|
"""
|
|
|
|
|
## Experiment class holding the state tree
|
|
|
|
|
program on the pulse card. It is written in a single file and picked up by the backend.
|
|
|
|
|
|
|
|
|
|
:param Gating gating: gate length in s
|
|
|
|
|
:param list rf_sources: list of rf sources ttl channels
|
|
|
|
|
:param list rf_gates: list of rf gates ttl channels len(rf_gates) == len(rf_sources)
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
job_id = 0
|
|
|
|
|
|
|
|
|
|
def __init__(self, gating=None, rf_sources=[], rf_gates=[]):
|
|
|
|
@@ -102,14 +105,6 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
# Commands -------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
## Creates a state with ttl signals of duration *length*.
|
|
|
|
|
#
|
|
|
|
|
# **Example:**
|
|
|
|
|
# ttl_pulse(length=1e-6,value=3)
|
|
|
|
|
# will create a ttl pulse on channels 0 and 1 (2**0 + 2**1) of duration 1us
|
|
|
|
|
# @param length time length if this state
|
|
|
|
|
# @param channel select a single channel (1...24)
|
|
|
|
|
# @param value select the channels via decimal representation (2**0 + 2**1 ...)
|
|
|
|
|
def ttl_pulse(self, length, channel = None, value = None):
|
|
|
|
|
"""
|
|
|
|
|
Creates a state with length **length** and switches
|
|
|
|
@@ -117,8 +112,7 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
:param float length: pulse length in seconds
|
|
|
|
|
:param int channel: selects a single channel (No. 1 - 24)
|
|
|
|
|
:param int value: lines to set (integer) for example value=3 selects channels 1 and 2 (2**1 + 2**2)
|
|
|
|
|
|
|
|
|
|
:param int value: lines to set (integer) for example value=3 selects channels 0 and 1 (2**0 + 2**1)
|
|
|
|
|
"""
|
|
|
|
|
the_value=0
|
|
|
|
|
if value is not None:
|
|
|
|
@@ -150,12 +144,11 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
def rf_pulse(self, length=None, phase=0, source=0):
|
|
|
|
|
"""
|
|
|
|
|
make an rf pulse, including gating and phase switching
|
|
|
|
|
Make an rf pulse, including gating and phase switching. Only possible if Experiment is configured.
|
|
|
|
|
|
|
|
|
|
:param float length: pulse length
|
|
|
|
|
:param float phase: pulse phase
|
|
|
|
|
:param int source: source id
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
|
|
|
|
if not self.gating:
|
|
|
|
|
raise SyntaxError("Can not use rf_pulse without configuration: Experiment(gating=None, rf_sources=[], rf_gates=[]")
|
|
|
|
@@ -167,7 +160,9 @@ class Experiment:
|
|
|
|
|
def state_start(self, time):
|
|
|
|
|
"""
|
|
|
|
|
Starts a state in the pulse programs with duration *time*.
|
|
|
|
|
This must be closed with state_end()
|
|
|
|
|
This must be closed with :func:`state_end`.
|
|
|
|
|
|
|
|
|
|
Typically only used in very special cases.
|
|
|
|
|
"""
|
|
|
|
|
self.state_list.append('<state time="%s">\n' % repr(time))
|
|
|
|
|
self.total_time[-1] += time
|
|
|
|
@@ -175,13 +170,10 @@ class Experiment:
|
|
|
|
|
## End of *state_start*
|
|
|
|
|
def state_end(self):
|
|
|
|
|
"""
|
|
|
|
|
Closes a previous start_state()
|
|
|
|
|
Closes a previous :func:`state_start`
|
|
|
|
|
"""
|
|
|
|
|
self.state_list.append('</state>\n')
|
|
|
|
|
|
|
|
|
|
## An empty state doing nothing
|
|
|
|
|
# @param time Duration of this state
|
|
|
|
|
# @param ttls Additinional ttl channels
|
|
|
|
|
def wait(self, time, ttls=None, gating=False):
|
|
|
|
|
"""
|
|
|
|
|
Wait specified **time** doing nothing.
|
|
|
|
@@ -201,24 +193,19 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
self.total_time[-1] += time
|
|
|
|
|
|
|
|
|
|
## Records data with given number of samples, sampling-frequency frequency and sensitivity
|
|
|
|
|
# @param samples Number of samples to record
|
|
|
|
|
# @param frequency Sampling frequency
|
|
|
|
|
# @param timelength Length of this state, per default calculated automatically
|
|
|
|
|
# @param sensitivity Sensitivity in Umax/V
|
|
|
|
|
# @param ttls Additional ttl channels
|
|
|
|
|
|
|
|
|
|
def record(self, samples, frequency, timelength=None, sensitivity = None, ttls=None, channels = 3, offset = None, impedance = None):
|
|
|
|
|
"""
|
|
|
|
|
Records data with given number of samples, sampling frequency and sensitivity.
|
|
|
|
|
Optionally, the time length of this state can be specified. If not specified **timelength** is
|
|
|
|
|
Records data with a given number of samples, sampling frequency and sensitivity.
|
|
|
|
|
Optionally, the time length of this state can be specified. If not specified, **timelength** is
|
|
|
|
|
deduced from **samples**/**frequency**:
|
|
|
|
|
|
|
|
|
|
:param int samples: Number of samples to record
|
|
|
|
|
:param float frequency: Sampling frequency
|
|
|
|
|
:param float frequency: Sampling frequency / Hz
|
|
|
|
|
:param float timelength: Length of this state, per default calculated automatically
|
|
|
|
|
:param float sensitivity: Sensitivity in Umax/V
|
|
|
|
|
:param int ttls: lines to set (integer)
|
|
|
|
|
:param int channels: default=3, Number of channels counting from 0
|
|
|
|
|
:param float sensitivity: Sensitivity in U_MAX/V
|
|
|
|
|
:param int ttls: additional ttl lines to set (integer)
|
|
|
|
|
:param int channels: channels to activate, default=3 (0+1)
|
|
|
|
|
"""
|
|
|
|
|
attributes='s="%d" f="%d"'%(samples,frequency)#%g
|
|
|
|
|
if channels != 1 and channels != 3 and channels != 5 and channels != 15:
|
|
|
|
@@ -270,11 +257,11 @@ class Experiment:
|
|
|
|
|
def loop_start(self, iterations):
|
|
|
|
|
"""
|
|
|
|
|
creates a loop of given number of iterations and has to be closed by loop_end().
|
|
|
|
|
Commands inside the loop can not change, i.e. the parameters are the same for each loop run.
|
|
|
|
|
Commands inside the loop can not change, i.e., the parameters are the same for each loop run.
|
|
|
|
|
This loop is created on the pulse programmer, thus saving commands.
|
|
|
|
|
One must close the loop with loop_end (see below)!
|
|
|
|
|
Close the loop with :func:`loop_end`.
|
|
|
|
|
|
|
|
|
|
:param int iterations: iterations to loop
|
|
|
|
|
:param int iterations: number of iterations to loop
|
|
|
|
|
"""
|
|
|
|
|
l = StateLoop(iterations)
|
|
|
|
|
self.state_list.append(l)
|
|
|
|
@@ -288,7 +275,7 @@ class Experiment:
|
|
|
|
|
## End loop state
|
|
|
|
|
def loop_end(self):
|
|
|
|
|
"""
|
|
|
|
|
Closes a loop_start() statement.
|
|
|
|
|
Closes a l:func:`loop_start` statement.
|
|
|
|
|
"""
|
|
|
|
|
# (This line could probably be guarded by a mutex)
|
|
|
|
|
self.state_list = self.list_stack.pop(-1)
|
|
|
|
@@ -297,11 +284,7 @@ class Experiment:
|
|
|
|
|
loopiterations=self.loop_iterations.pop(-1)
|
|
|
|
|
self.total_time[-1] += looptime*loopiterations
|
|
|
|
|
|
|
|
|
|
## Set the frequency and phase of the frequency source.
|
|
|
|
|
## This state needs 2us.
|
|
|
|
|
# @param frequency New frequency in Hz
|
|
|
|
|
# @param phase New phase in degrees
|
|
|
|
|
# @param ttls Additional ttl channels
|
|
|
|
|
|
|
|
|
|
def set_frequency(self, frequency, phase, ttls=0):
|
|
|
|
|
"""
|
|
|
|
|
Sets the frequency and phase of the frequency source and optionally further channels.
|
|
|
|
@@ -309,7 +292,7 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
:param float frequency: frequency to set in Hz
|
|
|
|
|
:param float phase: phase to set
|
|
|
|
|
:param int ttls: default=0, lines to set (integer)
|
|
|
|
|
:param int ttls: default=0, additional ttl lines to set (integer)
|
|
|
|
|
"""
|
|
|
|
|
s_content = '<analogout id="0" f="%f" phase="%f"/>' % (frequency, phase)
|
|
|
|
|
if ttls != 0:
|
|
|
|
@@ -318,16 +301,10 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
self.total_time[-1] += 2e-6
|
|
|
|
|
|
|
|
|
|
## Creates a, possibly shaped, pulsed gradient.
|
|
|
|
|
# @param dac_value DAC value to set
|
|
|
|
|
# @param length Duration of the state, minimum length is 42*90ns=3.78us (default)
|
|
|
|
|
# @param shape Tuple of (shape, resolution/seconds), shape can be one of: rec (default), sin2, sin
|
|
|
|
|
# @param is_seq If set to *True*, do NOT set DAC to zero after this state
|
|
|
|
|
# @param trigger Additional ttl channels
|
|
|
|
|
def set_pfg(self, dac_value=None, length=None, shape=('rec', 0), trigger=4, is_seq=False):
|
|
|
|
|
"""
|
|
|
|
|
This sets the value for the PFG, it also sets it back automatically.
|
|
|
|
|
If you don't whish to do so (i.e. line shapes) set is_seq=1
|
|
|
|
|
This sets the value for the PFG, it also sets it back to dac_value=0 automatically.
|
|
|
|
|
If you don't wish to do so (i.e. line shapes) set is_seq=1
|
|
|
|
|
If you want to set a trigger, set trigger (default=4, i.e. channel 2)
|
|
|
|
|
If you want shaped gradients: shape=(ashape, resolution), ashape can be rec, sin2, sin
|
|
|
|
|
|
|
|
|
@@ -392,22 +369,15 @@ class Experiment:
|
|
|
|
|
else: # don't know what to do
|
|
|
|
|
raise SyntaxError("form is unknown: %s"%form)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## sets the value of a DAC
|
|
|
|
|
# @param dac_value DAC value to set
|
|
|
|
|
# @param dac_id ID of the dac in case of multiple DAC(default=1)
|
|
|
|
|
# @param length Duration of the state
|
|
|
|
|
# @param is_seq If set to *True*, do NOT set DAC to zero after this state
|
|
|
|
|
# @param ttls Additional ttl channels
|
|
|
|
|
def set_dac(self, dac_value, dac_id=1, length=None, is_seq=False, ttls=0):
|
|
|
|
|
"""
|
|
|
|
|
This sets the value for the DAC and if given the TTLs.
|
|
|
|
|
It also sets it back to zero automatically.
|
|
|
|
|
This sets the value for the DAC and if given additional ttl lines.
|
|
|
|
|
It also sets it back to zero automatically when is_seq=False.
|
|
|
|
|
If you don't wish to set the value back to zero (i.e. line shapes) set is_seq=True
|
|
|
|
|
|
|
|
|
|
The state length is at least 3.78 µs (is_seq=1) or 7.28µs (is_seq=0).
|
|
|
|
|
|
|
|
|
|
:param int dac_value: dac value, between -2**19-1 and +2**19
|
|
|
|
|
:param int dac_value: DAC value, between -2**19-1 and +2**19
|
|
|
|
|
:param int dac_id: default=1, which DAC to control
|
|
|
|
|
:param float length: default=None, length of this state in seconds. If *None* length=42*90ns=3.78µs
|
|
|
|
|
:param bool is_seq: default=False, do not reset DAC to 0 (zero) if True
|
|
|
|
@@ -429,10 +399,6 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
self.total_time[-1] += 42*9e-8
|
|
|
|
|
|
|
|
|
|
## sets the phase of the frequency source.
|
|
|
|
|
## This state needs 0.5us, though the phase switching time is dependent on the frequency source
|
|
|
|
|
# @param phase New phase to set
|
|
|
|
|
# @param ttls Additional ttl channels
|
|
|
|
|
def set_phase(self, phase, ttls=0):
|
|
|
|
|
"""
|
|
|
|
|
Sets the phase of the RF source to this value.
|
|
|
|
@@ -450,11 +416,7 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
self.total_time[-1] += 0.5e-6
|
|
|
|
|
|
|
|
|
|
## sets a description which is carried via the back end result
|
|
|
|
|
## file to the result script in the front end. In the result script
|
|
|
|
|
## you can extract the description with get_description(key)
|
|
|
|
|
# @param key Name of description
|
|
|
|
|
# @param value Value of description
|
|
|
|
|
|
|
|
|
|
def set_description(self, key, value):
|
|
|
|
|
"""Sets a description which is carried via the back end result
|
|
|
|
|
file to the result script in the front end. In the result script
|
|
|
|
@@ -467,7 +429,6 @@ class Experiment:
|
|
|
|
|
print('Warning: Overwriting existing description "%s" = "%s" with "%s"' % (key, self.description[key], value))
|
|
|
|
|
self.description[key] = value
|
|
|
|
|
|
|
|
|
|
## set the PTS310/PTS500 frequency source to local mode
|
|
|
|
|
def set_pts_local(self):
|
|
|
|
|
"""
|
|
|
|
|
Sets the PTS310/PTS500 frequency source to local mode.
|
|
|
|
@@ -483,7 +444,7 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
# Public Methods -------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
def get_job_id(self):
|
|
|
|
|
def get_job_id(self) -> int:
|
|
|
|
|
"""
|
|
|
|
|
Returns the job-id of the current experiment
|
|
|
|
|
|
|
|
|
@@ -554,13 +515,11 @@ class Experiment:
|
|
|
|
|
|
|
|
|
|
def get_length(self):
|
|
|
|
|
timelength = 0.0
|
|
|
|
|
|
|
|
|
|
#calculate the correct timelength also for unclosed loops, if there is no unclosed loop
|
|
|
|
|
#the timelength of this experiment is self.total_time[-1].
|
|
|
|
|
for i in range(len(self.total_time)):
|
|
|
|
|
timelength += self.total_time[i]
|
|
|
|
|
timelength *= self.loop_iterations[i]
|
|
|
|
|
|
|
|
|
|
return timelength
|
|
|
|
|
|
|
|
|
|
class Quit(Experiment):
|
|
|
|
@@ -570,8 +529,6 @@ class Quit(Experiment):
|
|
|
|
|
|
|
|
|
|
# /Public Methods ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def self_test():
|
|
|
|
|
e = Experiment()
|
|
|
|
|
e.set_description("key", "value")
|
|
|
|
|