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
+20
View File
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= python -m sphinx
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+36
View File
@@ -0,0 +1,36 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import sys,os
#sys.path.insert(0, os.path.abspath('../src'))
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'DAMARIS'
copyright = '2026, Markus Rosenstihl'
author = 'Markus Rosenstihl'
version = '0.19'
release = '0.19'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
]
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
language = 'en'
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
#html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
+38
View File
@@ -0,0 +1,38 @@
.. DAMARIS documentation master file, created by
sphinx-quickstart on Mon Mar 23 15:55:16 2026.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
DAMARIS documentation
=====================
Add your content using ``reStructuredText`` syntax. See the
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
documentation for details.
.. toctree::
:maxdepth: 2
:caption: Contents:
API Reference
-------------
Experiments
~~~~~~~~~~~
.. automodule:: damaris.experiments.Experiment
:members:
:undoc-members:
Data Objects
~~~~~~~~~~~~
.. automodule:: damaris.data.Accumulation
:members:
:undoc-members:
.. automodule:: damaris.data.ADC_Result
:members:
:undoc-members:
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
-1
View File
@@ -35,7 +35,6 @@ maintainers = [
[project.scripts] [project.scripts]
DAMARIS3 = "damaris.__main__:main" DAMARIS3 = "damaris.__main__:main"
[tool.setuptools] [tool.setuptools]
packages = [ packages = [
"damaris", "damaris",
+32 -75
View File
@@ -71,10 +71,13 @@ from . import dac
class Experiment: class Experiment:
""" """
Class holding the complete state tree for a single experiment. This state tree represents one 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. program on the pulse card. It is written in a single file and picked up by the backend.
"""
## Experiment class holding the state tree
: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 job_id = 0
def __init__(self, gating=None, rf_sources=[], rf_gates=[]): def __init__(self, gating=None, rf_sources=[], rf_gates=[]):
@@ -102,14 +105,6 @@ class Experiment:
# Commands ------------------------------------------------------------------------------------- # 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): def ttl_pulse(self, length, channel = None, value = None):
""" """
Creates a state with length **length** and switches Creates a state with length **length** and switches
@@ -117,8 +112,7 @@ class Experiment:
:param float length: pulse length in seconds :param float length: pulse length in seconds
:param int channel: selects a single channel (No. 1 - 24) :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 the_value=0
if value is not None: if value is not None:
@@ -150,12 +144,11 @@ class Experiment:
def rf_pulse(self, length=None, phase=0, source=0): 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 length: pulse length
:param float phase: pulse phase :param float phase: pulse phase
:param int source: source id :param int source: source id
:return:
""" """
if not self.gating: if not self.gating:
raise SyntaxError("Can not use rf_pulse without configuration: Experiment(gating=None, rf_sources=[], rf_gates=[]") 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): def state_start(self, time):
""" """
Starts a state in the pulse programs with duration *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.state_list.append('<state time="%s">\n' % repr(time))
self.total_time[-1] += time self.total_time[-1] += time
@@ -175,13 +170,10 @@ class Experiment:
## End of *state_start* ## End of *state_start*
def state_end(self): def state_end(self):
""" """
Closes a previous start_state() Closes a previous :func:`state_start`
""" """
self.state_list.append('</state>\n') 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): def wait(self, time, ttls=None, gating=False):
""" """
Wait specified **time** doing nothing. Wait specified **time** doing nothing.
@@ -201,24 +193,19 @@ class Experiment:
self.total_time[-1] += time 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): 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. 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 Optionally, the time length of this state can be specified. If not specified, **timelength** is
deduced from **samples**/**frequency**: deduced from **samples**/**frequency**:
:param int samples: Number of samples to record :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 timelength: Length of this state, per default calculated automatically
:param float sensitivity: Sensitivity in Umax/V :param float sensitivity: Sensitivity in U_MAX/V
:param int ttls: lines to set (integer) :param int ttls: additional ttl lines to set (integer)
:param int channels: default=3, Number of channels counting from 0 :param int channels: channels to activate, default=3 (0+1)
""" """
attributes='s="%d" f="%d"'%(samples,frequency)#%g attributes='s="%d" f="%d"'%(samples,frequency)#%g
if channels != 1 and channels != 3 and channels != 5 and channels != 15: if channels != 1 and channels != 3 and channels != 5 and channels != 15:
@@ -270,11 +257,11 @@ class Experiment:
def loop_start(self, iterations): def loop_start(self, iterations):
""" """
creates a loop of given number of iterations and has to be closed by loop_end(). 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. 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) l = StateLoop(iterations)
self.state_list.append(l) self.state_list.append(l)
@@ -288,7 +275,7 @@ class Experiment:
## End loop state ## End loop state
def loop_end(self): 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) # (This line could probably be guarded by a mutex)
self.state_list = self.list_stack.pop(-1) self.state_list = self.list_stack.pop(-1)
@@ -297,11 +284,7 @@ class Experiment:
loopiterations=self.loop_iterations.pop(-1) loopiterations=self.loop_iterations.pop(-1)
self.total_time[-1] += looptime*loopiterations 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): def set_frequency(self, frequency, phase, ttls=0):
""" """
Sets the frequency and phase of the frequency source and optionally further channels. 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 frequency: frequency to set in Hz
:param float phase: phase to set :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) s_content = '<analogout id="0" f="%f" phase="%f"/>' % (frequency, phase)
if ttls != 0: if ttls != 0:
@@ -318,16 +301,10 @@ class Experiment:
self.total_time[-1] += 2e-6 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): 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. This sets the value for the PFG, it also sets it back to dac_value=0 automatically.
If you don't whish to do so (i.e. line shapes) set is_seq=1 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 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 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 else: # don't know what to do
raise SyntaxError("form is unknown: %s"%form) 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): 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. This sets the value for the DAC and if given additional ttl lines.
It also sets it back to zero automatically. 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 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). 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 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 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 :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 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): def set_phase(self, phase, ttls=0):
""" """
Sets the phase of the RF source to this value. Sets the phase of the RF source to this value.
@@ -450,11 +416,7 @@ class Experiment:
self.total_time[-1] += 0.5e-6 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): def set_description(self, key, value):
"""Sets a description which is carried via the back end result """Sets a description which is carried via the back end result
file to the result script in the front end. In the result script 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)) print('Warning: Overwriting existing description "%s" = "%s" with "%s"' % (key, self.description[key], value))
self.description[key] = value self.description[key] = value
## set the PTS310/PTS500 frequency source to local mode
def set_pts_local(self): def set_pts_local(self):
""" """
Sets the PTS310/PTS500 frequency source to local mode. Sets the PTS310/PTS500 frequency source to local mode.
@@ -483,7 +444,7 @@ class Experiment:
# Public Methods ------------------------------------------------------------------------------- # Public Methods -------------------------------------------------------------------------------
def get_job_id(self): def get_job_id(self) -> int:
""" """
Returns the job-id of the current experiment Returns the job-id of the current experiment
@@ -554,13 +515,11 @@ class Experiment:
def get_length(self): def get_length(self):
timelength = 0.0 timelength = 0.0
#calculate the correct timelength also for unclosed loops, if there is no unclosed loop #calculate the correct timelength also for unclosed loops, if there is no unclosed loop
#the timelength of this experiment is self.total_time[-1]. #the timelength of this experiment is self.total_time[-1].
for i in range(len(self.total_time)): for i in range(len(self.total_time)):
timelength += self.total_time[i] timelength += self.total_time[i]
timelength *= self.loop_iterations[i] timelength *= self.loop_iterations[i]
return timelength return timelength
class Quit(Experiment): class Quit(Experiment):
@@ -570,8 +529,6 @@ class Quit(Experiment):
# /Public Methods ------------------------------------------------------------------------------ # /Public Methods ------------------------------------------------------------------------------
def self_test(): def self_test():
e = Experiment() e = Experiment()
e.set_description("key", "value") e.set_description("key", "value")