Skip to content

Commit b5071bd

Browse files
committed
Add NI-DAQmx documentation.
This employs apidoc to auto-generate a custom templated summary of all the auto-generated sub-class models. It modifies conf.py to auto-run apidoc to create the template. Therefore new sub-classes that are added will automatically have the appropriate documentation generated. Note: apidoc creates a large number of cruft docfiles for all modules in labscript_devices using this specific custom template. This is because I haven't figured out how to get the autodoc directives to work without full specification of the module from the top level. This could be worked around by the apidoc exclude option, but it only uses very basic fnmatch syntax that can't seem to be fudged to only select a single submodule.
1 parent 68d6837 commit b5071bd

File tree

5 files changed

+207
-3
lines changed

5 files changed

+207
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,4 @@ conda_packages/
159159
docs/html/
160160
docs/source/_build/
161161
docs/source/components.rst
162+
docs/source/devices/_apidoc
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{%- macro automodule(modname, options) -%}
2+
.. automodule:: {{ modname }}
3+
{%- for option in options %}
4+
:{{ option }}:
5+
{%- endfor %}
6+
{%- endmacro %}
7+
8+
{%- macro toctree(docnames) -%}
9+
.. toctree::
10+
:maxdepth: {{ maxdepth }}
11+
{% for docname in docnames %}
12+
{{ docname }}
13+
{%- endfor %}
14+
{%- endmacro %}
15+
16+
{%- macro autosummary(submodules) -%}
17+
.. autosummary::
18+
{% for submodule in submodules %}
19+
{{submodule}}
20+
{%- endfor %}
21+
{%- endmacro %}
22+
23+
{%- macro autosum(pkgname) -%}
24+
.. autosummary::
25+
26+
{{pkgname}}
27+
{%- endmacro %}
28+
29+
{%- if is_namespace %}
30+
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
31+
{% else %}
32+
{# {{- [pkgname, ""] | join(" ") | e | heading }} #}
33+
{% endif %}
34+
35+
{%- if submodules %}
36+
{{ autosummary(submodules) }}
37+
{% if separatemodules %}
38+
{{ toctree(submodules) }}
39+
{% else %}
40+
{%- for submodule in submodules %}
41+
{% if show_headings %}
42+
{{- [submodule, ""] | join(" ") | e | heading(2) }}
43+
{% endif %}
44+
{{ automodule(submodule, automodule_options) }}
45+
{% endfor %}
46+
{%- endif %}
47+
{%- endif %}

docs/source/conf.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
]
5151

5252
autodoc_typehints = 'description'
53-
autoclass_content = 'init'
53+
autoclass_content = 'both' # options: 'both', 'class', 'init'
5454

5555
# Prefix each autosectionlabel with the name of the document it is in and a colon
5656
autosectionlabel_prefix_document = True
@@ -225,3 +225,18 @@ def setup(app):
225225
img_path=img_path
226226
)
227227
)
228+
229+
# hook to run apidoc before building
230+
app.connect('builder-inited',run_apidoc)
231+
232+
def run_apidoc(_):
233+
"""Runs apidoc with our desired parameters to generate the NI_DAQmx models docs.
234+
235+
Also manually strips out some irrelevant stuff."""
236+
from sphinx.ext.apidoc import main
237+
daq_models_path = os.path.join(os.path.abspath('..'),'labscript_devices')
238+
out_path = os.path.join(os.path.dirname(Path(__file__)),'devices','_apidoc')
239+
templates_path = os.path.join(os.path.dirname(Path(__file__)),'_templates')
240+
main(['-TMf','-s','inc',
241+
'-t', templates_path,
242+
'-o',out_path,daq_models_path])
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Sub-Classed NI DAQ Models
2+
=========================
3+
4+
.. include:: _apidoc/labscript_devices.NI_DAQmx.models.inc

docs/source/devices/ni_daqs.rst

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,139 @@
1-
NI DAQS
2-
=======
1+
NI DAQs
2+
=======
3+
4+
Overview
5+
~~~~~~~~
6+
7+
This labscript device is a master device that can control a wide range of NI Multi-function data acquistion devices.
8+
9+
Installation
10+
~~~~~~~~~~~~
11+
12+
This labscript device requires an installation of the NI-DAQmx module, available for free from `NI <https://www.ni.com/en-us/support/downloads/drivers/download.ni-daqmx.html>`_.
13+
14+
The python bindings are provided by the PyDAQmx package, available through pip.
15+
16+
17+
Adding a Device
18+
~~~~~~~~~~~~~~~
19+
20+
While the `NI_DAQmx` device can be used directly by manually specifying the many necessary parameters, it is preferable to add the device via an appropriate subclass. This process is greatly simplified by using the `get_capabilities.py` script.
21+
22+
To add support for a DAQmx device that is not yet supported, run `get_capabilities.py` on
23+
a computer with the device in question connected (or with a simulated device of the
24+
correct model configured in NI-MAX). This will introspect the capabilities of the device
25+
and add those details to capabilities.json. To generate labscript device classes for all
26+
devices whose capabilities are known, run `generate_classes.py`. Subclasses of NI_DAQmx
27+
will be made in the `models` subfolder, and they can then be imported into labscript code with:
28+
29+
..code-block:: python
30+
31+
from labscript_devices.NI_DAQmx.labscript_devices import NI_PCIe_6363
32+
33+
or similar. The class naming is based on the model name by prepending "NI\_" and
34+
replacing the hyphen with an underscore, i.e. 'PCIe-6363' -> NI_PCIe_6363.
35+
36+
Generating device classes requires the Python code-formatting library 'black', which can
37+
be installed via pip (Python 3.6+ only). If you don't want to install this library, the
38+
generation code will still work, it just won't be formatted well.
39+
40+
The current list of pre-subclassed devices is:
41+
42+
.. toctree::
43+
:maxdepth: 2
44+
45+
ni_daq_models
46+
47+
48+
Usage
49+
~~~~~
50+
51+
NI Multifunction DAQs generally provide hardware channels for the :ref:`StaticAnalogOut <labscript/StaticAnalogOut>`, :ref:`StaticDigitalOut <labscript/StaticDigitalOut>`, :ref:`AnalogOut <labscript/AnalogOut>`, :ref:`DigitalOut <labscript/DigitalOut>`, and :ref:`AnalogIn <labscript/AnalogIn>` labscript quantities for use in experiments. Exact numbers of channels, performance, and configuration depend on the model of DAQ used.
52+
53+
.. code-block:: python
54+
55+
from labscript import *
56+
57+
from labscript_devices.DummyPseudoclock.labscript_devices import DummyPseudoclock
58+
from labscript_devices.NI_DAQmx.models.NI_USB_6343 import NI_USB_6343
59+
60+
DummyPseudoclock('dummy_clock',BLACS_connection='dummy')
61+
62+
NI_USB_6343(name='daq',parent_device=dummy_clock.clockline,
63+
MAX_name='ni_usb_6343',
64+
clock_terminal='/ni_usb_6343/PFI0',
65+
acquisition_rate=100e3)
66+
67+
AnalogIn('daq_ai0',daq,'ai0')
68+
AnalogIn('daq_ai1',daq,'ai1')
69+
70+
AnalogOut('daq_ao0',daq,'ao0')
71+
AnalogIn('daq_ai1',daq,'ai1')
72+
73+
NI DAQs are also used within labscript to provide a :ref:`WaitMonitor <labscript/waitmonitor>`. When configured, the `WaitMonitor` allows for arbitrary-length pauses in experiment execution, waiting for some trigger to restart. The monitor provides a measurement of the duration of the wait for use in interpreting the resulting data from the experiment.
74+
75+
Configuration uses three digital I/O connections on the DAQ:
76+
77+
* The parent_connection which sends pulses at the beginning of the experiment, the start of the wait, and the end of the wait.
78+
* The acquisition_connection which must be wired to a counter and measures the time between the pulses of the parent connection.
79+
* The timeout_connection which can send a restart pulse if the wait times out.
80+
81+
An example configuration of a `WaitMonitor` using a NI DAQ is shown here
82+
83+
.. code-block:: python
84+
85+
# A wait monitor for AC-line triggering
86+
# This requires custom hardware
87+
WaitMonitor(name='wait_monitor',parent_device=daq,connection='port0/line0',
88+
acquisition_device=daq, acquisition_connection='ctr0',
89+
timeout_device=daq, timeout_connection='PFI1')
90+
# Necessary to ensure even number of digital out lines in shot
91+
DigitalOut('daq_do1',daq,'port0/line1')
92+
93+
Note that the counter connection is specified using the logical label `'ctr0'`. On many NI DAQs, the physical connection to this counter is PFI9. The physical wiring for this configuration would have port0/line0 wired directly to PFI9, which PFI1 being sent to the master pseudoclock retriggering system in case of timeout. If timeouts are not expect/represent experiment failure, this physical connection can be omitted.
94+
95+
96+
Detailed Documentation
97+
~~~~~~~~~~~~~~~~~~~~~~
98+
99+
.. automodule:: labscript_devices.NI_DAQmx
100+
:members:
101+
:undoc-members:
102+
:show-inheritance:
103+
:private-members:
104+
105+
.. automodule:: labscript_devices.NI_DAQmx.labscript_devices
106+
:members:
107+
:undoc-members:
108+
:show-inheritance:
109+
:private-members:
110+
111+
.. automodule:: labscript_devices.NI_DAQmx.blacs_tabs
112+
:members:
113+
:undoc-members:
114+
:show-inheritance:
115+
:private-members:
116+
117+
.. automodule:: labscript_devices.NI_DAQmx.blacs_workers
118+
:members:
119+
:undoc-members:
120+
:show-inheritance:
121+
:private-members:
122+
123+
.. automodule:: labscript_devices.NI_DAQmx.runviewer_parsers
124+
:members:
125+
:undoc-members:
126+
:show-inheritance:
127+
:private-members:
128+
129+
.. automodule:: labscript_devices.NI_DAQmx.daqmx_utils
130+
:members:
131+
:undoc-members:
132+
:show-inheritance:
133+
:private-members:
134+
135+
.. automodule:: labscript_devices.NI_DAQmx.utils
136+
:members:
137+
:undoc-members:
138+
:show-inheritance:
139+
:private-members:

0 commit comments

Comments
 (0)