opticalib 2.1.3__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opticalib-2.3.0/CITATION.bib +10 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/MANIFEST.in +5 -4
- {opticalib-2.1.3/opticalib.egg-info → opticalib-2.3.0}/PKG-INFO +42 -28
- {opticalib-2.1.3 → opticalib-2.3.0}/README.md +11 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/__init__.py +11 -15
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/__version__.py +1 -1
- opticalib-2.3.0/opticalib/analyzer/__init__.py +13 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/analyzer/image_processing.py +59 -4
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/_types.py +16 -1
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/config.py +53 -2
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/root.py +4 -4
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/alpaoAPI.py +24 -16
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/base_devices.py +25 -10
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/piAPI.py +17 -8
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/deformable_mirrors.py +63 -91
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/interferometer.py +52 -27
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/_misc.py +14 -6
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/flattening.py +32 -1
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/iff_processing.py +1 -3
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/_stitching_algorithm.py +13 -13
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/osutils.py +11 -4
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/reconstructor.py +5 -5
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/__init__.py +19 -3
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/activity.py +47 -14
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/app.py +498 -62
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/kernel.py +146 -17
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/kernel_side.py +88 -0
- opticalib-2.3.0/opticalib/gui/plugins.py +304 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/alignment.py +30 -8
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/base.py +51 -14
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/dm_calibration.py +145 -31
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/params.py +64 -18
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/phasing.py +76 -17
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/stitching.py +31 -5
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/timeseries.py +24 -5
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/theme.py +10 -0
- opticalib-2.3.0/opticalib/gui/widgets/common.py +298 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/widgets/config_editor.py +225 -5
- opticalib-2.3.0/opticalib/gui/widgets/connect_dialog.py +503 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/widgets/data_browser.py +22 -8
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/widgets/device_panel.py +177 -46
- opticalib-2.3.0/opticalib/gui/widgets/device_registry.py +1272 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/widgets/plot_viewer.py +60 -13
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/widgets/workspace.py +6 -2
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/procedures/alignment.py +28 -16
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/procedures/iff.py +9 -11
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/procedures/phasing.py +4 -2
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/_API/__init__.py +2 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/_API/base_fake_adopticadm.py +19 -75
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/_API/base_fake_alpao.py +23 -85
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/_API/base_petalmirror.py +13 -52
- opticalib-2.3.0/opticalib/simulator/_API/base_simulated_dm.py +395 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/_API/simdata.py +11 -8
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/__init__.py +2 -0
- opticalib-2.3.0/opticalib/simulator/fake_dms.py +281 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/visualization.py +99 -2
- {opticalib-2.1.3 → opticalib-2.3.0/opticalib.egg-info}/PKG-INFO +42 -28
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib.egg-info/SOURCES.txt +6 -4
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib.egg-info/requires.txt +22 -3
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib.egg-info/top_level.txt +0 -1
- opticalib-2.3.0/pyproject.toml +103 -0
- opticalib-2.3.0/setup.cfg +4 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/setup_calpy.py +2 -13
- opticalib-2.3.0/tests/README.md +110 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/conftest.py +3 -3
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_analyzer_images_processing.py +146 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_core_read_config.py +63 -1
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_core_root.py +2 -2
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_devices_deformable_mirrors.py +1 -1
- opticalib-2.3.0/tests/test_devices_interferometer.py +93 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_config_editor.py +84 -0
- opticalib-2.3.0/tests/test_gui_devices.py +923 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_kernel_bridge.py +46 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_kernel_side.py +86 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_procedures.py +78 -1
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_widgets.py +173 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_setup_calpy.py +13 -11
- opticalib-2.3.0/tests/test_simulator_run_cmd_history.py +48 -0
- opticalib-2.1.3/opticalib/analyzer/__init__.py +0 -3
- opticalib-2.1.3/opticalib/gui/plugins.py +0 -128
- opticalib-2.1.3/opticalib/gui/widgets/common.py +0 -137
- opticalib-2.1.3/opticalib/gui/widgets/connect_dialog.py +0 -246
- opticalib-2.1.3/opticalib/gui/widgets/device_registry.py +0 -642
- opticalib-2.1.3/opticalib/simulator/_API/_rbf_gpu.py +0 -367
- opticalib-2.1.3/opticalib/simulator/fake_dms.py +0 -846
- opticalib-2.1.3/requirements.txt +0 -24
- opticalib-2.1.3/setup.cfg +0 -28
- opticalib-2.1.3/setup.py +0 -56
- opticalib-2.1.3/tests/test_gui_devices.py +0 -448
- {opticalib-2.1.3 → opticalib-2.3.0}/LICENSE +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/__init_script__/initCalpy.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/analyzer/signals.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/analyzer/timeseries.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/_configurations/configuration.yaml +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/data_classes.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/decorators.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/exceptions.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/core/fitsarray.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/i4dAPI.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/micAPI.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/_API/splattAPI.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/cameras.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/devices/wfs.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/__actuator_identification_wip.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/__pupil_calibration_wip.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/iff_preparation.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/dmutils/slaving.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/geometry.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/logger.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/modal_decomposer.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/ground/roi.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/kernel_backend.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/procedures/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/resources/opticalib.ico +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/resources/opticalib.png +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/resources/opticalib.svg +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/gui/widgets/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/procedures/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/procedures/measurements.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/procedures/stitching.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/py.typed +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/_API/alpao_conf.yaml +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/factory.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib/simulator/fake_interf.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib.egg-info/dependency_links.txt +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/opticalib.egg-info/entry_points.txt +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/__init__.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_analyzer_signals.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_analyzer_timeseries.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_core_dataclass.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_core_exceptions.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_core_fits.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_dmutils_flattening.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_dmutils_iff_acquisition_preparation.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_dmutils_iff_module.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_dmutils_iff_processing.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_dmutils_slaving.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_ground_computerec.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_ground_geometry.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_ground_logger.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_ground_modal_decomposer.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_ground_osutils.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_ground_roi.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_activity.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_gui_app.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_measurements.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_simulator_factory_functions.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_simulator_simdata.py +0 -0
- {opticalib-2.1.3 → opticalib-2.3.0}/tests/test_typings.py +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
@misc{opticalib,
|
|
2
|
+
doi = {10.20371/INAF/SW/2026_00013},
|
|
3
|
+
url = {https://openscience.inaf.it/?page_id=696},
|
|
4
|
+
author = {{Pietro Ferraiuolo} and {Marco Xompero} and {Runa Briguglio} and {Matteo Menessini}},
|
|
5
|
+
language = {en},
|
|
6
|
+
title = {OPTICALIB: A Python Framework for Adaptive Optics Experimentation},
|
|
7
|
+
publisher = {O.A. Arcetri},
|
|
8
|
+
year = {2025},
|
|
9
|
+
copyright = {MIT License}
|
|
10
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
include README.md
|
|
2
|
-
include
|
|
2
|
+
include LICENSE
|
|
3
|
+
include CITATION.bib
|
|
3
4
|
include opticalib/core/_configurations/configuration.yaml
|
|
4
|
-
include setup_calpy.py
|
|
5
|
-
include opticalib/__init_script__/initCalpy.py
|
|
6
5
|
include opticalib/simulator/_API/alpao_conf.yaml
|
|
6
|
+
include opticalib/__init_script__/initCalpy.py
|
|
7
7
|
recursive-include opticalib/gui/resources *.svg *.png *.ico
|
|
8
|
-
|
|
8
|
+
graft tests
|
|
9
|
+
global-exclude __pycache__ *.py[cod]
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opticalib
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: This package is a python interface for laboratory adaptive optics systems, with a focus on the calibration of deformable mirrors (DMs) and the acquisition of wavefront data using wavefront sensors.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
Author-email: Adaptive Optics Group - INAF - Osservatorio Astrofisico di Arcetri <pietro.ferraiuolo@inaf.it>
|
|
6
|
+
Maintainer-email: Pietro Ferraiuolo <pietro.ferraiuolo@inaf.it>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/ArcetriAdaptiveOptics/opticalib
|
|
9
|
+
Project-URL: Repository, https://github.com/ArcetriAdaptiveOptics/opticalib
|
|
10
|
+
Project-URL: Issues, https://github.com/ArcetriAdaptiveOptics/opticalib/issues
|
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: Microsoft :: Windows
|
|
13
13
|
Classifier: Operating System :: POSIX :: Linux
|
|
14
14
|
Classifier: Operating System :: MacOS
|
|
15
|
-
Requires-Python: >=3.
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: arte
|
|
@@ -24,37 +24,43 @@ Requires-Dist: pipython
|
|
|
24
24
|
Requires-Dist: photutils
|
|
25
25
|
Requires-Dist: scipy
|
|
26
26
|
Requires-Dist: ruamel.yaml
|
|
27
|
+
Requires-Dist: PyYAML
|
|
27
28
|
Requires-Dist: Pyro4
|
|
28
29
|
Requires-Dist: h5py
|
|
29
30
|
Requires-Dist: jdcal
|
|
30
31
|
Requires-Dist: tqdm
|
|
31
32
|
Requires-Dist: matplotlib
|
|
32
|
-
Requires-Dist: xupy
|
|
33
33
|
Requires-Dist: numpy
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist:
|
|
40
|
-
Requires-Dist:
|
|
41
|
-
Requires-Dist:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
Requires-Dist: rbfgpu
|
|
35
|
+
Requires-Dist: xupy>=2.0
|
|
36
|
+
Requires-Dist: ipython
|
|
37
|
+
Provides-Extra: gui
|
|
38
|
+
Requires-Dist: PySide6-Essentials; extra == "gui"
|
|
39
|
+
Requires-Dist: QtPy; extra == "gui"
|
|
40
|
+
Requires-Dist: qtconsole; extra == "gui"
|
|
41
|
+
Requires-Dist: ipykernel; extra == "gui"
|
|
42
|
+
Requires-Dist: jupyter_client; extra == "gui"
|
|
43
|
+
Requires-Dist: pyqtgraph; extra == "gui"
|
|
44
|
+
Requires-Dist: qtawesome; extra == "gui"
|
|
45
|
+
Requires-Dist: psutil; extra == "gui"
|
|
46
|
+
Provides-Extra: cuda12
|
|
47
|
+
Requires-Dist: xupy[cuda12]>=2.0; extra == "cuda12"
|
|
48
|
+
Provides-Extra: cuda13
|
|
49
|
+
Requires-Dist: xupy[cuda13]>=2.0; extra == "cuda13"
|
|
50
|
+
Provides-Extra: test
|
|
51
|
+
Requires-Dist: pytest; extra == "test"
|
|
52
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Requires-Dist: opticalib[gui,test]; extra == "dev"
|
|
55
|
+
Requires-Dist: flake8; extra == "dev"
|
|
49
56
|
Dynamic: license-file
|
|
50
|
-
Dynamic: maintainer
|
|
51
|
-
Dynamic: requires-dist
|
|
52
|
-
Dynamic: requires-python
|
|
53
|
-
Dynamic: summary
|
|
54
57
|
|
|
55
58
|
# OptiCalib : python framework for OPTical CALibration of deformable mirrors and AO experimentation
|
|
56
59
|
|
|
57
60
|

|
|
61
|
+
[](https://pypi.org/project/opticalib/)
|
|
62
|
+
[](https://pypi.org/project/opticalib/)
|
|
63
|
+
[](LICENSE)
|
|
58
64
|
|
|
59
65
|
`Adaptive Optics Group, INAF - Osservatorio Astrofisico di Arcetri`
|
|
60
66
|
|
|
@@ -86,6 +92,14 @@ The latests, stable, version can be installed from pypi:
|
|
|
86
92
|
pip install opticalib
|
|
87
93
|
```
|
|
88
94
|
|
|
95
|
+
The base install contains everything needed to talk to the hardware, run the simulators and the calibration routines, and use the `calpy` shell. Two optional extras add the rest:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install "opticalib[gui]" # the graphical interface (calpy --gui / OptiCalib)
|
|
99
|
+
pip install "opticalib[cuda13]" # GPU backend (CuPy, via xupy); use cuda12 for CUDA 12.x
|
|
100
|
+
pip install "opticalib[gui,cuda13]" # extras can be combined
|
|
101
|
+
```
|
|
102
|
+
|
|
89
103
|
The in-development version can be installed directly from this repository:
|
|
90
104
|
|
|
91
105
|
```bash
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# OptiCalib : python framework for OPTical CALibration of deformable mirrors and AO experimentation
|
|
2
2
|
|
|
3
3
|

|
|
4
|
+
[](https://pypi.org/project/opticalib/)
|
|
5
|
+
[](https://pypi.org/project/opticalib/)
|
|
6
|
+
[](LICENSE)
|
|
4
7
|
|
|
5
8
|
`Adaptive Optics Group, INAF - Osservatorio Astrofisico di Arcetri`
|
|
6
9
|
|
|
@@ -32,6 +35,14 @@ The latests, stable, version can be installed from pypi:
|
|
|
32
35
|
pip install opticalib
|
|
33
36
|
```
|
|
34
37
|
|
|
38
|
+
The base install contains everything needed to talk to the hardware, run the simulators and the calibration routines, and use the `calpy` shell. Two optional extras add the rest:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install "opticalib[gui]" # the graphical interface (calpy --gui / OptiCalib)
|
|
42
|
+
pip install "opticalib[cuda13]" # GPU backend (CuPy, via xupy); use cuda12 for CUDA 12.x
|
|
43
|
+
pip install "opticalib[gui,cuda13]" # extras can be combined
|
|
44
|
+
```
|
|
45
|
+
|
|
35
46
|
The in-development version can be installed directly from this repository:
|
|
36
47
|
|
|
37
48
|
```bash
|
|
@@ -5,20 +5,15 @@ OPTICALIB: adaptive OPTIcs package for dm CALIBration
|
|
|
5
5
|
Author(s)
|
|
6
6
|
---------
|
|
7
7
|
- Pietro Ferraiuolo : pietro.ferraiuolo@inaf.it
|
|
8
|
+
- Runa Briguglio: runa.briguglio@inaf.it
|
|
9
|
+
- Marco Xompero: marco.xompero@inaf.it
|
|
10
|
+
- Matteo Menessini: matteo.menessini@inaf.it
|
|
8
11
|
|
|
9
12
|
Description
|
|
10
13
|
-----------
|
|
11
14
|
OptiCalib is a package for the control of laboratory instrumentations, like
|
|
12
15
|
Interferometers and Deformable Mirrors. It also provides tools for the
|
|
13
16
|
analysis of wavefronts and images.
|
|
14
|
-
|
|
15
|
-
How to Use:
|
|
16
|
-
-----------
|
|
17
|
-
```python
|
|
18
|
-
> import opticalib
|
|
19
|
-
> interf = opticalib.PhaseCam('193.206.155.218', 8011)
|
|
20
|
-
> img = interf.acquire_map()
|
|
21
|
-
```
|
|
22
17
|
"""
|
|
23
18
|
|
|
24
19
|
from __future__ import annotations
|
|
@@ -34,13 +29,6 @@ from .core.root import (
|
|
|
34
29
|
from .core import config
|
|
35
30
|
from .core import _types as typings
|
|
36
31
|
from .core.fitsarray import fits_array
|
|
37
|
-
from .devices import *
|
|
38
|
-
from .devices.interferometer import _4DInterferometer
|
|
39
|
-
|
|
40
|
-
get_camera_settings = _4DInterferometer.get_camera_settings
|
|
41
|
-
get_frame_rate = _4DInterferometer.get_frame_rate
|
|
42
|
-
|
|
43
|
-
del _4DInterferometer
|
|
44
32
|
|
|
45
33
|
from . import (
|
|
46
34
|
analyzer,
|
|
@@ -51,6 +39,14 @@ from . import (
|
|
|
51
39
|
visualization,
|
|
52
40
|
)
|
|
53
41
|
|
|
42
|
+
from .devices.interferometer import _4DInterferometer
|
|
43
|
+
|
|
44
|
+
get_camera_settings = _4DInterferometer.get_camera_settings
|
|
45
|
+
get_frame_rate = _4DInterferometer.get_frame_rate
|
|
46
|
+
del _4DInterferometer
|
|
47
|
+
|
|
48
|
+
from .devices import *
|
|
49
|
+
|
|
54
50
|
vis = visualization
|
|
55
51
|
|
|
56
52
|
__all__ = [
|
|
@@ -9,7 +9,7 @@ Pietro Ferraiuolo
|
|
|
9
9
|
__title__ = "opticalib"
|
|
10
10
|
__long_title__ = "OPTICALIB : python framework for OPTIcal CALIBration of deformable mirrors and adaptive optics experimentation"
|
|
11
11
|
__description__ = "This package is a python interface for laboratory adaptive optics systems, with a focus on the calibration of deformable mirrors (DMs) and the acquisition of wavefront data using wavefront sensors."
|
|
12
|
-
__version__ = "2.
|
|
12
|
+
__version__ = "2.3.0"
|
|
13
13
|
__author__ = "Adaptive Optics Group - INAF - Osservatorio Astrofisico di Arcetri"
|
|
14
14
|
__maintainer__ = "Pietro Ferraiuolo"
|
|
15
15
|
__author_email__ = "pietro.ferraiuolo@inaf.it"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .image_processing import *
|
|
2
|
+
from .signals import *
|
|
3
|
+
from .timeseries import *
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _get_safe_interf_fullframe():
|
|
7
|
+
from ..devices.interferometer import _4DInterferometer
|
|
8
|
+
|
|
9
|
+
func = _4DInterferometer.into_full_frame
|
|
10
|
+
return func
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
into_4d_full_frame = _get_safe_interf_fullframe()
|
|
@@ -20,6 +20,54 @@ from ..ground import osutils as osu, modal_decomposer as _md
|
|
|
20
20
|
from scipy import stats as _stats, fft as _fft, ndimage as _ndimage
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def pad_image_to_shape(
|
|
24
|
+
img: _ot.ImageData,
|
|
25
|
+
shape: tuple[int, int],
|
|
26
|
+
offset: tuple[int, int],
|
|
27
|
+
fill_value: float = 0.0,
|
|
28
|
+
) -> _ot.ImageData:
|
|
29
|
+
"""
|
|
30
|
+
Pads an image to a specified shape with a given offset and fill value.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
img : _ot.ImageData
|
|
35
|
+
The image to be padded.
|
|
36
|
+
shape : tuple[int, int]
|
|
37
|
+
The target shape (height, width) for the padded image.
|
|
38
|
+
offset : tuple[int, int]
|
|
39
|
+
The (y, x) offset at which to place the original image in the padded image.
|
|
40
|
+
fill_value : float, optional
|
|
41
|
+
The value to fill the padded areas with. Default is 0.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
padded_img : _ot.ImageData
|
|
46
|
+
The padded image.
|
|
47
|
+
"""
|
|
48
|
+
y, x, Y, X, h, w = [*offset, *shape, *img.shape]
|
|
49
|
+
if any(p < 0 for p in [y, x, Y - h - y, X - w - x]):
|
|
50
|
+
raise ValueError("Invalid padding dimensions")
|
|
51
|
+
pad_width = ((y, Y - h - y), (x, X - w - x))
|
|
52
|
+
pimg = _np.pad(
|
|
53
|
+
img.data,
|
|
54
|
+
pad_width=pad_width,
|
|
55
|
+
mode='constant',
|
|
56
|
+
constant_values=fill_value
|
|
57
|
+
)
|
|
58
|
+
mask = _np.pad(
|
|
59
|
+
_np.ma.getmaskarray(img),
|
|
60
|
+
pad_width=pad_width,
|
|
61
|
+
mode='constant',
|
|
62
|
+
constant_values=True
|
|
63
|
+
)
|
|
64
|
+
if hasattr(img, 'header'):
|
|
65
|
+
header = img.header
|
|
66
|
+
else:
|
|
67
|
+
header = None
|
|
68
|
+
return _fa.fits_array(pimg, mask=mask, header=header)
|
|
69
|
+
|
|
70
|
+
|
|
23
71
|
def frame(idx: int, mylist: list[_ot.ImageData] | _ot.CubeData) -> _ot.ImageData:
|
|
24
72
|
"""
|
|
25
73
|
Returns a single frame from a list of files or from a cube.
|
|
@@ -225,7 +273,7 @@ def push_pull_reduction_algorithm(
|
|
|
225
273
|
# Template weights computation
|
|
226
274
|
w = _xp.asarray(
|
|
227
275
|
template.astype(_np.result_type(template, imagelist[0].data), copy=True),
|
|
228
|
-
dtype=
|
|
276
|
+
dtype=_ot.compute_float(),
|
|
229
277
|
)
|
|
230
278
|
if n_images > 2:
|
|
231
279
|
w[1:-1] *= 2.0
|
|
@@ -233,9 +281,9 @@ def push_pull_reduction_algorithm(
|
|
|
233
281
|
master_mask = _np.logical_or.reduce([ima.mask for ima in imagelist])
|
|
234
282
|
# Compute weighted sum over realizations on raw data
|
|
235
283
|
stack = _xp.stack(
|
|
236
|
-
[_xp.asarray(ima.data, dtype=
|
|
284
|
+
[_xp.asarray(ima.data, dtype=_ot.compute_float()) for ima in imagelist],
|
|
237
285
|
axis=0,
|
|
238
|
-
dtype=
|
|
286
|
+
dtype=_ot.compute_float(),
|
|
239
287
|
) # (n, H, W)
|
|
240
288
|
image = _xp.asnumpy(_xp.tensordot(w, stack, axes=(0, 0))) # (H, W)
|
|
241
289
|
|
|
@@ -269,13 +317,18 @@ def create_cube(fl_or_il: list[str], register: bool = False) -> _ot.CubeData:
|
|
|
269
317
|
cube : ndarray
|
|
270
318
|
Data cube containing the images/frames stacked, with shape (npx, npy, nframes).
|
|
271
319
|
"""
|
|
320
|
+
memmap = True
|
|
272
321
|
# check it is a list
|
|
273
322
|
if not isinstance(fl_or_il, list):
|
|
274
323
|
raise TypeError("filelist must be a list of strings or images")
|
|
275
324
|
|
|
276
325
|
# check if it is composed of file paths to load
|
|
277
326
|
if all([isinstance(item, str) for item in fl_or_il]):
|
|
278
|
-
|
|
327
|
+
N = len(fl_or_il) # Number of files
|
|
328
|
+
if N > 1000:
|
|
329
|
+
memmap = False
|
|
330
|
+
print("Disabling memory mapping for large file list: N =", N)
|
|
331
|
+
fl_or_il = [osu.read_phasemap(f, memmap=memmap) for f in fl_or_il]
|
|
279
332
|
|
|
280
333
|
# Is the list now full of images?
|
|
281
334
|
if not any(
|
|
@@ -860,6 +913,8 @@ def rebin2_d_array(
|
|
|
860
913
|
|
|
861
914
|
|
|
862
915
|
__all__ = [
|
|
916
|
+
"pad_image_to_shape",
|
|
917
|
+
# "into_4d_full_frame",
|
|
863
918
|
"frame",
|
|
864
919
|
"unwrap_piston",
|
|
865
920
|
"unwrap_image",
|
|
@@ -162,7 +162,7 @@ class _DMProtocol(Protocol):
|
|
|
162
162
|
def set_shape(self, cmd: MatrixLike, differential: bool) -> None: ...
|
|
163
163
|
def get_shape(self) -> ArrayLike: ...
|
|
164
164
|
def upload_cmd_history(
|
|
165
|
-
self, cmdhist: MatrixLike, *,
|
|
165
|
+
self, cmdhist: MatrixLike, *, slaving: bool | str = False
|
|
166
166
|
) -> None: ...
|
|
167
167
|
def run_cmd_history(
|
|
168
168
|
self,
|
|
@@ -453,3 +453,18 @@ def get_device_type(device: object) -> str:
|
|
|
453
453
|
elif isinstance_(device, "CameraDevice"):
|
|
454
454
|
return "camera"
|
|
455
455
|
return device._name
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def compute_float():
|
|
459
|
+
"""
|
|
460
|
+
Floating dtype for numerical work on the active xupy backend.
|
|
461
|
+
|
|
462
|
+
``float32`` on GPU (CuPy) for speed, ``float64`` on CPU (NumPy). This is
|
|
463
|
+
what the removed ``xupy.float`` alias used to resolve to; it is evaluated
|
|
464
|
+
at call time, so it follows ``xupy.use_cpu()`` / ``xupy.use_gpu()``.
|
|
465
|
+
"""
|
|
466
|
+
import xupy as xp
|
|
467
|
+
|
|
468
|
+
dtype = xp.float32 if xp.on_gpu else xp.float64
|
|
469
|
+
del xp
|
|
470
|
+
return dtype
|
|
@@ -15,6 +15,7 @@ Most accessors also accept an explicit ``bpath`` to point at a specific
|
|
|
15
15
|
|
|
16
16
|
import os as _os
|
|
17
17
|
import re as _re
|
|
18
|
+
from collections.abc import Mapping as _Mapping
|
|
18
19
|
from ast import literal_eval as _literal_eval
|
|
19
20
|
|
|
20
21
|
import numpy as _np
|
|
@@ -276,6 +277,52 @@ def get_section_config(section: str, sub_section: str | None = None) -> dict[str
|
|
|
276
277
|
return _get_section_config(section=section, sub_section=sub_section, path=_cfile)
|
|
277
278
|
|
|
278
279
|
|
|
280
|
+
class LazySection(_Mapping):
|
|
281
|
+
"""
|
|
282
|
+
A section of ``configuration.yaml``, read when it is used.
|
|
283
|
+
|
|
284
|
+
Modules that need a section at import time use it instead of reading the
|
|
285
|
+
file immediately, so that an experiment that does not define the
|
|
286
|
+
section (or sets it to ``null``) can still import opticalib. A missing
|
|
287
|
+
section reads as empty; ``section[key]`` then raises a ``KeyError``
|
|
288
|
+
naming the section. The file is read on every access, so the values
|
|
289
|
+
follow :func:`opticalib.set_configuration_file`.
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
section : str
|
|
294
|
+
Top-level section name.
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
def __init__(self, section: str) -> None:
|
|
298
|
+
"""Remember the section; nothing is read yet."""
|
|
299
|
+
self._section = section
|
|
300
|
+
|
|
301
|
+
def _load(self) -> dict[str, _Any]:
|
|
302
|
+
try:
|
|
303
|
+
data = get_section_config(self._section)
|
|
304
|
+
except KeyError:
|
|
305
|
+
data = None
|
|
306
|
+
return dict(data) if isinstance(data, dict) else {}
|
|
307
|
+
|
|
308
|
+
def __getitem__(self, key: str) -> _Any:
|
|
309
|
+
data = self._load()
|
|
310
|
+
if key not in data:
|
|
311
|
+
raise KeyError(
|
|
312
|
+
f"`{key}` not found in the `{self._section}` section of the configuration file"
|
|
313
|
+
)
|
|
314
|
+
return data[key]
|
|
315
|
+
|
|
316
|
+
def __iter__(self):
|
|
317
|
+
return iter(self._load())
|
|
318
|
+
|
|
319
|
+
def __len__(self) -> int:
|
|
320
|
+
return len(self._load())
|
|
321
|
+
|
|
322
|
+
def __repr__(self) -> str:
|
|
323
|
+
return f"LazySection({self._section!r})"
|
|
324
|
+
|
|
325
|
+
|
|
279
326
|
def get_device_config(device_type: str, device_name: str | None = None):
|
|
280
327
|
"""
|
|
281
328
|
Retrieves the device configuration from the YAML configuration file.
|
|
@@ -558,8 +605,8 @@ def get_cmd_delay(bpath: str = _cfold):
|
|
|
558
605
|
sub_section="DM",
|
|
559
606
|
path=bpath,
|
|
560
607
|
)
|
|
561
|
-
if "
|
|
562
|
-
cmdDelay = float(dm_config["
|
|
608
|
+
if "sequential_delay" in dm_config:
|
|
609
|
+
cmdDelay = float(dm_config["sequential_delay"])
|
|
563
610
|
elif "delay" in dm_config:
|
|
564
611
|
cmdDelay = float(dm_config["delay"])
|
|
565
612
|
elif "triggeredMode" in dm_config and "cmdDelay" in dm_config["triggeredMode"]:
|
|
@@ -696,6 +743,10 @@ def get_alignment_config():
|
|
|
696
743
|
The alignment configuration as a class, for backwards compatibility.
|
|
697
744
|
"""
|
|
698
745
|
config = get_section_config("SYSTEM.ALIGNMENT")
|
|
746
|
+
if not isinstance(config, dict):
|
|
747
|
+
raise KeyError(
|
|
748
|
+
"The `SYSTEM.ALIGNMENT` section of the configuration file is empty."
|
|
749
|
+
)
|
|
699
750
|
config["slices"] = [slice(item["start"], item["stop"]) for item in config["slices"]]
|
|
700
751
|
|
|
701
752
|
class AlignmentConfig:
|
|
@@ -351,7 +351,7 @@ class ConfSettingReader4D:
|
|
|
351
351
|
defined object:
|
|
352
352
|
|
|
353
353
|
>>> cr = ConfSettingReader4D(file_path)
|
|
354
|
-
>>> cr.
|
|
354
|
+
>>> cr.get_image_width_in_pixels()
|
|
355
355
|
2000
|
|
356
356
|
>>> cr.get_image_height_in_pixels()
|
|
357
357
|
2000
|
|
@@ -382,13 +382,13 @@ class ConfSettingReader4D:
|
|
|
382
382
|
frame_rate = self.config.get(self.camera_section, "FrameRate")
|
|
383
383
|
return float(frame_rate)
|
|
384
384
|
|
|
385
|
-
def
|
|
385
|
+
def get_image_width_in_pixels(self):
|
|
386
386
|
"""
|
|
387
|
-
Returns the image
|
|
387
|
+
Returns the image width in pixel scale
|
|
388
388
|
|
|
389
389
|
Returns
|
|
390
390
|
-------
|
|
391
|
-
|
|
391
|
+
image_width_in_pixels : int
|
|
392
392
|
Image pixel width.
|
|
393
393
|
"""
|
|
394
394
|
image_width_in_pixels = self.config.get(
|
|
@@ -5,6 +5,7 @@ from opticalib.core import _types as _t
|
|
|
5
5
|
|
|
6
6
|
import weakref
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
def _shutdown_dm(dm, reset: bool, logger, name: str) -> None:
|
|
9
10
|
"""Stop (and optionally Reset) a raw SDK handle. Module-level so it
|
|
10
11
|
holds no reference to the wrapper object."""
|
|
@@ -112,12 +113,15 @@ class BaseAlpaoMirror:
|
|
|
112
113
|
self._resolve_init(sdk_params, plico_params)
|
|
113
114
|
self._is_plico = bool(plico_params[0])
|
|
114
115
|
|
|
115
|
-
self.n_acts =
|
|
116
|
-
self._plico_dm.get_number_of_actuators()
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
self.n_acts = (
|
|
117
|
+
int(self._plico_dm.get_number_of_actuators())
|
|
118
|
+
if self._is_plico
|
|
119
|
+
else int(self._sdk_dm.Get("NbOfActuator"))
|
|
120
|
+
)
|
|
121
|
+
|
|
119
122
|
if self.n_acts != int(nacts):
|
|
120
123
|
import warnings
|
|
124
|
+
|
|
121
125
|
warnings.warn(
|
|
122
126
|
f"Number of actuators reported by the SDK ({self.n_acts}) "
|
|
123
127
|
f"does not match the called number ({nacts}). Verify your BAX files",
|
|
@@ -131,7 +135,7 @@ class BaseAlpaoMirror:
|
|
|
131
135
|
self._name
|
|
132
136
|
].get("diameter", None)
|
|
133
137
|
self.mirrorModes = None
|
|
134
|
-
self.
|
|
138
|
+
self._cmd_history = None
|
|
135
139
|
self.refAct = None
|
|
136
140
|
self.ff = None
|
|
137
141
|
|
|
@@ -194,12 +198,14 @@ class BaseAlpaoMirror:
|
|
|
194
198
|
if not self._is_plico:
|
|
195
199
|
return int(self._sdk_dm.Get("VersionInfo"))
|
|
196
200
|
else:
|
|
197
|
-
raise AttributeError(
|
|
201
|
+
raise AttributeError(
|
|
202
|
+
"Version information is not available for PLICO devices."
|
|
203
|
+
)
|
|
198
204
|
|
|
199
205
|
def close(self) -> None:
|
|
200
206
|
"""
|
|
201
207
|
Stop the DM (and Reset it if ``reset_on_close``) and release the hardware
|
|
202
|
-
resources.
|
|
208
|
+
resources.
|
|
203
209
|
|
|
204
210
|
Should be called when the DM object is no longer needed to
|
|
205
211
|
ensure a clean shutdown of the Alpao SDK connection.
|
|
@@ -311,15 +317,13 @@ class BaseAlpaoMirror:
|
|
|
311
317
|
|
|
312
318
|
def _resolve_init(
|
|
313
319
|
self,
|
|
314
|
-
sdk_params: tuple[str|None, str|None, str|None],
|
|
315
|
-
plico_params: tuple[str|None, str|None, int|None]
|
|
320
|
+
sdk_params: tuple[str | None, str | None, str | None],
|
|
321
|
+
plico_params: tuple[str | None, str | None, int | None],
|
|
316
322
|
):
|
|
317
323
|
sn, sdk_fold, acfg_p = sdk_params
|
|
318
324
|
plico, plico_ip, plico_port = plico_params
|
|
319
325
|
|
|
320
|
-
config = get_section_config(
|
|
321
|
-
"DEVICES", "DEFORMABLE.MIRRORS"
|
|
322
|
-
).get(self._name, {})
|
|
326
|
+
config = get_section_config("DEVICES", "DEFORMABLE.MIRRORS").get(self._name, {})
|
|
323
327
|
|
|
324
328
|
if plico:
|
|
325
329
|
self._plico_ip = config.get("plico_ip", plico_ip)
|
|
@@ -351,12 +355,12 @@ class BaseAlpaoMirror:
|
|
|
351
355
|
The serial number is resolved in the following priority order:
|
|
352
356
|
|
|
353
357
|
1. The *serial_number* argument, if not ``None``.
|
|
354
|
-
2. The ``
|
|
358
|
+
2. The ``serial_number`` key in the device configuration file
|
|
355
359
|
(looked up by *nacts*).
|
|
356
360
|
|
|
357
361
|
When *nacts* is provided the device configuration block
|
|
358
362
|
``Alpao{nacts}`` is read from the configuration file, allowing
|
|
359
|
-
``
|
|
363
|
+
``serial_number``, ``sdk_folder_path``, and ``acfg_path`` to be
|
|
360
364
|
set there instead of being passed as arguments. When only
|
|
361
365
|
*serial_number* is given the configuration lookup is skipped and
|
|
362
366
|
the caller is responsible for having ``ACECFG`` set in the
|
|
@@ -396,9 +400,13 @@ class BaseAlpaoMirror:
|
|
|
396
400
|
os.environ["ACECFG"] = self.acfg_path
|
|
397
401
|
|
|
398
402
|
try:
|
|
399
|
-
sdk_path = self.sdk_folder_path or os.path.join(
|
|
403
|
+
sdk_path = self.sdk_folder_path or os.path.join(
|
|
404
|
+
CONFIGURATION_FOLDER, "alpao_sdk"
|
|
405
|
+
)
|
|
400
406
|
|
|
401
|
-
if self.sdk_folder_path is not None and not os.path.exists(
|
|
407
|
+
if self.sdk_folder_path is not None and not os.path.exists(
|
|
408
|
+
self.sdk_folder_path
|
|
409
|
+
):
|
|
402
410
|
sdk_path = os.path.join(CONFIGURATION_FOLDER, self.sdk_folder_path)
|
|
403
411
|
|
|
404
412
|
if not os.path.exists(sdk_path):
|
|
@@ -36,6 +36,26 @@ class BaseDeformableMirror(ABC):
|
|
|
36
36
|
Base class for all deformable mirror devices.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
+
@property
|
|
40
|
+
def slave_ids(self):
|
|
41
|
+
return self._slaveIds
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def border_ids(self):
|
|
45
|
+
return self._borderIds
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def cmd_history(self) -> _ot.Optional[_ot.MatrixLike]:
|
|
49
|
+
"""
|
|
50
|
+
Get the command history of the simulated deformable mirror (DM).
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
cmd_history : _ot.Optional[_ot.MatrixLike]
|
|
55
|
+
The command history of the DM, or None if no history has been uploaded.
|
|
56
|
+
"""
|
|
57
|
+
return self._cmd_history
|
|
58
|
+
|
|
39
59
|
@abstractmethod
|
|
40
60
|
def set_shape(self, cmd):
|
|
41
61
|
"""
|
|
@@ -97,24 +117,19 @@ class BaseDeformableMirror(ABC):
|
|
|
97
117
|
return None
|
|
98
118
|
return "minimum-rms" if (s and b) else "zero-force"
|
|
99
119
|
|
|
100
|
-
def _apply_slaving(
|
|
120
|
+
def _apply_slaving(
|
|
121
|
+
self, cmd: _ot.ArrayLike | _ot.MatrixLike, slave: bool | str
|
|
122
|
+
) -> _ot.ArrayLike | _ot.MatrixLike:
|
|
101
123
|
"""
|
|
102
124
|
Apply actuator slaving to a command when requested.
|
|
103
125
|
"""
|
|
104
126
|
method = self._get_slaving_method(slave)
|
|
105
127
|
if method is None:
|
|
106
128
|
return cmd
|
|
129
|
+
if _ot.isinstance_(cmd, "MatrixLike"):
|
|
130
|
+
return compute_slaved_command_matrix(self, cmdmat=cmd, method=slave)
|
|
107
131
|
return compute_slave_cmd(self, cmd, method=method)
|
|
108
132
|
|
|
109
|
-
def _slave_cmdmat(self, cmdmat: _ot.ArrayLike, slave: bool | str) -> _ot.MatrixLike:
|
|
110
|
-
"""
|
|
111
|
-
Apply actuator slaving to a command matrix when requested.
|
|
112
|
-
"""
|
|
113
|
-
method = self._get_slaving_method(slave)
|
|
114
|
-
if method is None:
|
|
115
|
-
return cmdmat
|
|
116
|
-
return compute_slaved_command_matrix(self, cmdmat, method=method)
|
|
117
|
-
|
|
118
133
|
|
|
119
134
|
class BaseCamera(ABC):
|
|
120
135
|
|