added sphinx documentation
Build Debian Packages / build (bookworm, debian12) (push) Failing after 7m19s
Build Debian Packages / build (bullseye, debian11) (push) Failing after 7m5s
Build Debian Packages / build (trixie, debian13) (push) Failing after 8m2s

This commit is contained in:
2026-03-23 17:07:51 +01:00
parent 27466fe2d6
commit 75f32e1502
6 changed files with 161 additions and 76 deletions
+32 -75
View File
@@ -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")