opticalib 2.3.0__tar.gz → 2.4.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.
Files changed (159) hide show
  1. {opticalib-2.3.0 → opticalib-2.4.0}/MANIFEST.in +7 -0
  2. {opticalib-2.3.0/opticalib.egg-info → opticalib-2.4.0}/PKG-INFO +6 -1
  3. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/__init__.py +7 -1
  4. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/__version__.py +15 -1
  5. opticalib-2.4.0/opticalib/_version.py +24 -0
  6. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/analyzer/image_processing.py +11 -10
  7. opticalib-2.4.0/opticalib/core/_configurations/DOCS.md +499 -0
  8. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/_configurations/configuration.yaml +52 -29
  9. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/_types.py +17 -0
  10. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/config.py +207 -161
  11. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/decorators.py +1 -1
  12. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/root.py +28 -7
  13. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/devices/_API/__init__.py +5 -5
  14. opticalib-2.4.0/opticalib/devices/_API/cameras/GigE_live_viewer.py +236 -0
  15. opticalib-2.4.0/opticalib/devices/_API/cameras/frame_buffer.py +133 -0
  16. opticalib-2.4.0/opticalib/devices/_API/dms/__init__.py +0 -0
  17. {opticalib-2.3.0/opticalib/devices/_API → opticalib-2.4.0/opticalib/devices/_API/dms}/alpaoAPI.py +18 -14
  18. {opticalib-2.3.0/opticalib/devices/_API → opticalib-2.4.0/opticalib/devices/_API/dms}/micAPI.py +2 -1
  19. {opticalib-2.3.0/opticalib/devices/_API → opticalib-2.4.0/opticalib/devices/_API/dms}/piAPI.py +23 -13
  20. {opticalib-2.3.0/opticalib/devices/_API → opticalib-2.4.0/opticalib/devices/_API/dms}/splattAPI.py +2 -1
  21. opticalib-2.4.0/opticalib/devices/_API/wfs/__init__.py +0 -0
  22. opticalib-2.4.0/opticalib/devices/_API/wfs/pupil_detector.py +185 -0
  23. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/devices/cameras.py +257 -55
  24. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/devices/deformable_mirrors.py +33 -36
  25. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/devices/interferometer.py +138 -42
  26. opticalib-2.4.0/opticalib/devices/wfs.py +529 -0
  27. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/_misc.py +2 -2
  28. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/flattening.py +54 -16
  29. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/iff_processing.py +109 -44
  30. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/osutils.py +4 -0
  31. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/kernel_side.py +80 -4
  32. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/params.py +173 -0
  33. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/common.py +4 -4
  34. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/device_panel.py +8 -2
  35. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/device_registry.py +19 -7
  36. opticalib-2.4.0/opticalib/py.typed +0 -0
  37. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/fake_interf.py +1 -1
  38. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/visualization.py +6 -5
  39. {opticalib-2.3.0 → opticalib-2.4.0/opticalib.egg-info}/PKG-INFO +6 -1
  40. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib.egg-info/SOURCES.txt +19 -5
  41. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib.egg-info/requires.txt +1 -0
  42. opticalib-2.4.0/opticalib.egg-info/scm_file_list.json +192 -0
  43. opticalib-2.4.0/opticalib.egg-info/scm_version.json +8 -0
  44. {opticalib-2.3.0 → opticalib-2.4.0}/pyproject.toml +13 -3
  45. opticalib-2.4.0/pytest.ini +15 -0
  46. {opticalib-2.3.0 → opticalib-2.4.0}/tests/conftest.py +3 -3
  47. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_core_read_config.py +57 -219
  48. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_core_root.py +27 -0
  49. opticalib-2.4.0/tests/test_devices_cameras.py +854 -0
  50. opticalib-2.4.0/tests/test_devices_interferometer.py +846 -0
  51. opticalib-2.4.0/tests/test_devices_wfs.py +800 -0
  52. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_dmutils_flattening.py +3 -3
  53. opticalib-2.4.0/tests/test_dmutils_iff_normalization.py +308 -0
  54. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_dmutils_iff_processing.py +7 -7
  55. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_devices.py +8 -5
  56. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_kernel_side.py +74 -0
  57. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_procedures.py +184 -0
  58. opticalib-2.3.0/opticalib/devices/wfs.py +0 -374
  59. opticalib-2.3.0/tests/test_devices_interferometer.py +0 -93
  60. {opticalib-2.3.0 → opticalib-2.4.0}/CITATION.bib +0 -0
  61. {opticalib-2.3.0 → opticalib-2.4.0}/LICENSE +0 -0
  62. {opticalib-2.3.0 → opticalib-2.4.0}/README.md +0 -0
  63. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/__init_script__/initCalpy.py +0 -0
  64. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/analyzer/__init__.py +0 -0
  65. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/analyzer/signals.py +0 -0
  66. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/analyzer/timeseries.py +0 -0
  67. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/__init__.py +0 -0
  68. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/data_classes.py +0 -0
  69. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/exceptions.py +0 -0
  70. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/core/fitsarray.py +0 -0
  71. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/devices/_API/base_devices.py +0 -0
  72. opticalib-2.3.0/opticalib/py.typed → opticalib-2.4.0/opticalib/devices/_API/cameras/__init__.py +0 -0
  73. {opticalib-2.3.0/opticalib/devices/_API → opticalib-2.4.0/opticalib/devices/_API/wfs}/i4dAPI.py +0 -0
  74. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/devices/__init__.py +0 -0
  75. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/__actuator_identification_wip.py +0 -0
  76. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/__init__.py +0 -0
  77. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/__pupil_calibration_wip.py +0 -0
  78. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/iff_preparation.py +2 -2
  79. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/dmutils/slaving.py +0 -0
  80. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/__init__.py +0 -0
  81. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/_stitching_algorithm.py +0 -0
  82. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/geometry.py +0 -0
  83. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/logger.py +0 -0
  84. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/modal_decomposer.py +0 -0
  85. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/reconstructor.py +0 -0
  86. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/ground/roi.py +0 -0
  87. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/__init__.py +0 -0
  88. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/activity.py +0 -0
  89. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/app.py +0 -0
  90. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/kernel.py +0 -0
  91. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/kernel_backend.py +0 -0
  92. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/plugins.py +0 -0
  93. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/__init__.py +0 -0
  94. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/alignment.py +0 -0
  95. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/base.py +0 -0
  96. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/dm_calibration.py +0 -0
  97. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/phasing.py +0 -0
  98. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/stitching.py +0 -0
  99. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/procedures/timeseries.py +0 -0
  100. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/resources/opticalib.ico +0 -0
  101. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/resources/opticalib.png +0 -0
  102. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/resources/opticalib.svg +0 -0
  103. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/theme.py +0 -0
  104. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/__init__.py +0 -0
  105. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/config_editor.py +0 -0
  106. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/connect_dialog.py +0 -0
  107. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/data_browser.py +0 -0
  108. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/plot_viewer.py +0 -0
  109. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/gui/widgets/workspace.py +0 -0
  110. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/procedures/__init__.py +0 -0
  111. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/procedures/alignment.py +0 -0
  112. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/procedures/iff.py +0 -0
  113. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/procedures/measurements.py +0 -0
  114. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/procedures/phasing.py +0 -0
  115. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/procedures/stitching.py +0 -0
  116. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/__init__.py +0 -0
  117. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/alpao_conf.yaml +0 -0
  118. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/base_fake_adopticadm.py +0 -0
  119. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/base_fake_alpao.py +0 -0
  120. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/base_petalmirror.py +0 -0
  121. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/base_simulated_dm.py +0 -0
  122. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/_API/simdata.py +0 -0
  123. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/__init__.py +0 -0
  124. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/factory.py +0 -0
  125. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib/simulator/fake_dms.py +0 -0
  126. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib.egg-info/dependency_links.txt +0 -0
  127. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib.egg-info/entry_points.txt +0 -0
  128. {opticalib-2.3.0 → opticalib-2.4.0}/opticalib.egg-info/top_level.txt +0 -0
  129. {opticalib-2.3.0 → opticalib-2.4.0}/setup.cfg +0 -0
  130. {opticalib-2.3.0 → opticalib-2.4.0}/setup_calpy.py +0 -0
  131. {opticalib-2.3.0 → opticalib-2.4.0}/tests/README.md +0 -0
  132. {opticalib-2.3.0 → opticalib-2.4.0}/tests/__init__.py +0 -0
  133. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_analyzer_images_processing.py +0 -0
  134. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_analyzer_signals.py +0 -0
  135. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_analyzer_timeseries.py +0 -0
  136. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_core_dataclass.py +0 -0
  137. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_core_exceptions.py +0 -0
  138. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_core_fits.py +0 -0
  139. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_devices_deformable_mirrors.py +0 -0
  140. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_dmutils_iff_acquisition_preparation.py +0 -0
  141. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_dmutils_iff_module.py +0 -0
  142. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_dmutils_slaving.py +0 -0
  143. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_ground_computerec.py +0 -0
  144. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_ground_geometry.py +0 -0
  145. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_ground_logger.py +0 -0
  146. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_ground_modal_decomposer.py +0 -0
  147. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_ground_osutils.py +0 -0
  148. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_ground_roi.py +0 -0
  149. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_activity.py +0 -0
  150. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_app.py +0 -0
  151. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_config_editor.py +0 -0
  152. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_kernel_bridge.py +0 -0
  153. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_gui_widgets.py +0 -0
  154. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_measurements.py +0 -0
  155. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_setup_calpy.py +0 -0
  156. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_simulator_factory_functions.py +0 -0
  157. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_simulator_run_cmd_history.py +0 -0
  158. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_simulator_simdata.py +0 -0
  159. {opticalib-2.3.0 → opticalib-2.4.0}/tests/test_typings.py +0 -0
@@ -6,4 +6,11 @@ include opticalib/simulator/_API/alpao_conf.yaml
6
6
  include opticalib/__init_script__/initCalpy.py
7
7
  recursive-include opticalib/gui/resources *.svg *.png *.ico
8
8
  graft tests
9
+ # setuptools-scm adds every git-tracked file to the sdist: drop the dev-only ones.
10
+ prune docs
11
+ prune .github
12
+ exclude CLAUDE.md
13
+ exclude labott.code-workspace
14
+ exclude .readthedocs.yaml
15
+ exclude .gitignore
9
16
  global-exclude __pycache__ *.py[cod]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opticalib
3
- Version: 2.3.0
3
+ Version: 2.4.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
5
  Author-email: Adaptive Optics Group - INAF - Osservatorio Astrofisico di Arcetri <pietro.ferraiuolo@inaf.it>
6
6
  Maintainer-email: Pietro Ferraiuolo <pietro.ferraiuolo@inaf.it>
@@ -9,6 +9,10 @@ Project-URL: Homepage, https://github.com/ArcetriAdaptiveOptics/opticalib
9
9
  Project-URL: Repository, https://github.com/ArcetriAdaptiveOptics/opticalib
10
10
  Project-URL: Issues, https://github.com/ArcetriAdaptiveOptics/opticalib/issues
11
11
  Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
12
16
  Classifier: Operating System :: Microsoft :: Windows
13
17
  Classifier: Operating System :: POSIX :: Linux
14
18
  Classifier: Operating System :: MacOS
@@ -31,6 +35,7 @@ Requires-Dist: jdcal
31
35
  Requires-Dist: tqdm
32
36
  Requires-Dist: matplotlib
33
37
  Requires-Dist: numpy
38
+ Requires-Dist: packaging
34
39
  Requires-Dist: rbfgpu
35
40
  Requires-Dist: xupy>=2.0
36
41
  Requires-Dist: ipython
@@ -38,6 +38,7 @@ from . import (
38
38
  simulator,
39
39
  visualization,
40
40
  )
41
+ vis = visualization
41
42
 
42
43
  from .devices.interferometer import _4DInterferometer
43
44
 
@@ -47,7 +48,12 @@ del _4DInterferometer
47
48
 
48
49
  from .devices import *
49
50
 
50
- vis = visualization
51
+ # ---------------------------------------------------------------------------- #
52
+ # Checking configuration schema version compatibility
53
+ # ---------------------------------------------------------------------------- #
54
+ config.check_configuration_version()
55
+ # ____________________________________________________________________________ #
56
+ # ---------------------------------------------------------------------------- #
51
57
 
52
58
  __all__ = [
53
59
  "analyzer",
@@ -9,7 +9,21 @@ 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.3.0"
12
+
13
+ # ``_version.py`` is generated by setuptools-scm at build/install time from the
14
+ # git tags. In editable installs it is refreshed only on reinstall
15
+ # (``pip install -e .``).
16
+ try:
17
+ from ._version import version as __version__
18
+ except ImportError: # not built by setuptools-scm: ask the installed metadata
19
+ from importlib import metadata as _metadata
20
+
21
+ try:
22
+ __version__ = _metadata.version("opticalib")
23
+ except _metadata.PackageNotFoundError:
24
+ __version__ = "0.0.0+unknown"
25
+ del _metadata
26
+
13
27
  __author__ = "Adaptive Optics Group - INAF - Osservatorio Astrofisico di Arcetri"
14
28
  __maintainer__ = "Pietro Ferraiuolo"
15
29
  __author_email__ = "pietro.ferraiuolo@inaf.it"
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '2.4.0'
22
+ __version_tuple__ = version_tuple = (2, 4, 0)
23
+
24
+ __commit_id__ = commit_id = 'ga3a0a791f'
@@ -50,18 +50,15 @@ def pad_image_to_shape(
50
50
  raise ValueError("Invalid padding dimensions")
51
51
  pad_width = ((y, Y - h - y), (x, X - w - x))
52
52
  pimg = _np.pad(
53
- img.data,
54
- pad_width=pad_width,
55
- mode='constant',
56
- constant_values=fill_value
53
+ img.data, pad_width=pad_width, mode="constant", constant_values=fill_value
57
54
  )
58
55
  mask = _np.pad(
59
56
  _np.ma.getmaskarray(img),
60
57
  pad_width=pad_width,
61
- mode='constant',
62
- constant_values=True
58
+ mode="constant",
59
+ constant_values=True,
63
60
  )
64
- if hasattr(img, 'header'):
61
+ if hasattr(img, "header"):
65
62
  header = img.header
66
63
  else:
67
64
  header = None
@@ -296,7 +293,7 @@ def push_pull_reduction_algorithm(
296
293
  return image
297
294
 
298
295
 
299
- def create_cube(fl_or_il: list[str], register: bool = False) -> _ot.CubeData:
296
+ def create_cube(fl_or_il: list[str]|list[_ot.ImageData], register: bool = False) -> _ot.CubeData:
300
297
  """
301
298
  Creates a cube of images from an images file list
302
299
 
@@ -357,9 +354,13 @@ def create_cube(fl_or_il: list[str], register: bool = False) -> _ot.CubeData:
357
354
  if hasattr(item, "header"):
358
355
  header.update(item.header)
359
356
 
360
- cube = _fa.fits_array(_np.ma.dstack(fl_or_il), header=header)
357
+ if len(fl_or_il) == 1:
358
+ return _np.expand_dims(
359
+ _fa.fits_array(fl_or_il[0], header=header),
360
+ axis=2
361
+ )
361
362
 
362
- return cube
363
+ return _fa.fits_array(_np.ma.dstack(fl_or_il), header=header)
363
364
 
364
365
 
365
366
  def remove_zernike_from_cube(
@@ -0,0 +1,499 @@
1
+ # CONFIGURATION FILE DOCUMENTATION
2
+
3
+ ### Author(s)
4
+
5
+ - Pietro Ferraiuolo : written in 2025 (pietro.ferraiuolo@inaf.it)
6
+
7
+ ### General description
8
+ The `configuration.yaml` is, as the name suggests, a configuration file for the optimal functioning of the `OptiCalib` python package, which includes the following main sections:
9
+
10
+ - [System](#system-section)
11
+ - [Devices](#devices-section)
12
+ - [Wavefront Sensors](#variables-overview-wfs)
13
+ - [Interferometers](#variables-overview-interferometers)
14
+ - [Deformable Mirrors](#variables-overview-deformablemirrors)
15
+ - [Cameras](#variables-overview-cameras)
16
+ - [Motors](#variables-overview-motors)
17
+ - [Phasing](#phasing-section)
18
+ - [Influence Functions](#iff-section)
19
+ - [Stitching](#stitching-section)
20
+ - [Alignment](#alignment-section)
21
+
22
+ <ins>NOTE</ins>: every section is optional. A section which is missing (or set to `null`) does not prevent the package from being imported, but the procedures which need it will raise an error when used.
23
+
24
+ ## System Section
25
+ ### Description
26
+ In this first section of the configuration file, under the `SYSTEM` key, there are the general definitions the software needs to properly operate. Here we find the base data path, where obtained data and configurations are stored and retrieved, and the schema version of the configuration file.
27
+
28
+ ### Variables Overview
29
+ - **data_path : str**
30
+
31
+ This is the base bath for the construction of the folder tree of the package.
32
+ If left undefined (empty string), the package will automatically asign as base data path the hidden folder in the home directory, and will use the copy of the `configuration.yaml` stored in its `SysConfig` folder (created from the template if not present), so that
33
+ for future modifications there will be non need to touch the "root" configuration file:
34
+
35
+ `~/.opticalib/`
36
+
37
+ The folder tree (`OPTData`, `Logging`, `SysConfig`) is created at import time.
38
+
39
+ - **version : str**
40
+
41
+ The *configuration schema version*, in `MAJOR.MINOR` format (e.g. `'2.5'`). It is independent of the package version and is bumped by hand (`CONFIG_SCHEMA_VERSION` in `opticalib.core.config`) only when the layout of the configuration file changes (sections or keys added, renamed or removed). At import time (and after `set_configuration_file`) only `MAJOR.MINOR` is compared with the schema version expected by the installed package, and a `UserWarning` is raised if:
42
+ - the key is missing or unparseable (legacy file, may need alignment);
43
+ - the configuration is *older* than expected: check this document, update the file and set `version` to the expected value;
44
+ - the configuration is *newer* than expected: it was written for a newer `opticalib`, upgrade the package.
45
+
46
+ Always quote the value (`version: '2.5'`): unquoted, YAML reads `2.10` as the number `2.1`.
47
+
48
+ - **created_with : str**
49
+
50
+ The version of the `opticalib` package which created the file. Provenance only, it is never compared with anything.
51
+
52
+ ## Devices Section
53
+ ### Description
54
+ The devices part of the configuration file, under the `DEVICES` key, is divided into five sub-sections: Wavefront Sensors (`WFS`), Interferometers (`INTERFEROMETERS`), Deformable Mirrors (`DEFORMABLE.MIRRORS`), Cameras (`CAMERAS`) and Motors (`MOTORS`). This section of the configuration serves to define key elements for the connection with the instruments, such as their `ip:port` adresses for ethernet connection.
55
+
56
+ Each device is identified by its name, which is the key under which its parameters are defined. The lookup of a device by name is case-insensitive (i.e. an entry named `phasecam6110` is found when asking for `PhaseCam6110`), as long as it is not ambiguous.
57
+
58
+ Devices can refer to other devices with the `TYPE:name` notation, for example `camera: CAMERAS:GigaVision` indicates the `GigaVision` device defined in the `CAMERAS` sub-section.
59
+
60
+ ### Variables Overview: `WFS`
61
+ The wavefront sensors (currently the `Ingot` three pupils sensor and the `Pyramid` four pupils sensor), which are based on a camera and a pupil detection algorithm. The parameters are the same for each device:
62
+
63
+ - **camera : str**
64
+
65
+ The camera used by the sensor, in the `CAMERAS:name` notation.
66
+
67
+ - **camera_base_exptime : float**
68
+
69
+ The base exposure time of the camera, in ms.
70
+
71
+ - **camera_fps : float**
72
+
73
+ Frames per second of the camera (if supported).
74
+
75
+ - **camera_binning : int**
76
+
77
+ Software binning factor of the camera.
78
+
79
+ - **sigma_threshold : float**
80
+
81
+ Sigma threshold used by the edge filter (Canny algorithm) for the pupil detection.
82
+
83
+ - **n_pupils : int**
84
+
85
+ The total number of pupils (3 for `Ingot`, 4 for `Pyramid`).
86
+
87
+ - **subapertures : int**
88
+
89
+ The number of pixels across the pupil.
90
+
91
+ - **delta_r : int**
92
+
93
+ The delta, in pixels, for the pupil search range, which is centered on `subapertures`.
94
+
95
+ - **pupils_centers : list of [int, int]**
96
+
97
+ An estimate of the centers of the pupils, to help the search. It has one `[x, y]` element for each pupil.
98
+
99
+ ### Variables Overview: `INTERFEROMETERS`
100
+ Each interferometer is defined by its own name (e.g. `PhaseCamX`, `AccuFizX`, `4DProcesser1`), under which are present the following variables:
101
+
102
+ - **ip : str**
103
+
104
+ This is the IP address of the interferometer instrument.
105
+
106
+ - **port : int**
107
+
108
+ This is the network PORT of the interferometer.
109
+
110
+ - **Paths**
111
+
112
+ In this sub-section are all the necessary paths to work with the interferometer.
113
+
114
+ - **settings : str**
115
+
116
+ This is the path to the `AppSettings.ini` configuration file of the interferometer, which needs to be network-mounted into the user's machine.
117
+
118
+ - **copied_settings : str**
119
+
120
+ This is the file name (e.g. `CameraSettings.ini`) given to the copy of the interferometer `AppSettings.ini` file, which is saved along with the acquired data.
121
+
122
+ - **capture_4dpc : str**
123
+
124
+ This is the `capture` folder in the proprietary `4D` computer, which needs to be network-mounted in the user machine.
125
+
126
+ - **produce_4dpc : str**
127
+
128
+ This is the `produce` folder in the proprietary `4D` computer, which needs to be network-mounted in the user machine.
129
+
130
+ - **produce : str**
131
+
132
+ This is the destination folder for the `produce`, which is in the user's machine.
133
+
134
+ - **capture : str**
135
+
136
+ This is the destination folder for the `capture`, which is in the user's machine.
137
+
138
+
139
+ ### Variables Overview: `DEFORMABLE.MIRRORS`
140
+
141
+ - **"device_name"** (Here goes the device name that has been defined, i.e. `AlpaoXXX` or `PetalDM`)
142
+
143
+ The variables depend on the kind of device. Those shared by (almost) all of them are:
144
+
145
+ - **ip : str**
146
+
147
+ The `IP` address of the DM device to be connected.
148
+
149
+ - **port : int**
150
+
151
+ Network `PORT` of the DM device to be connected
152
+
153
+ - **slave_ids : list of int**
154
+
155
+ The indices of the actuators to be slaved (see the actuator slaving of the `slave` option when commanding the DM).
156
+
157
+ - **border_ids : list of int**
158
+
159
+ The indices of the border actuators of the DM, used by the slaving algorithms.
160
+
161
+ The `Alpao` DMs (named `Alpao<nacts>`, e.g. `Alpao97`) can be controlled via two different backends, and the variables depend on the one in use:
162
+
163
+ - Native SDK backend:
164
+
165
+ - **serial_number : str**
166
+
167
+ The serial number of the DM, in the form `BAX<YYY>`.
168
+
169
+ - **sdk_folder_path : str**
170
+
171
+ The parent folder containing `Lib64/` (e.g. `.../Linux/Samples/Python3`).
172
+
173
+ - **acfg_path : str**
174
+
175
+ The folder containing the `.acfg` hardware configuration file (it sets the `ACECFG` environment variable).
176
+
177
+ - `plico_dm` backend:
178
+
179
+ - **plico_ip : str**
180
+
181
+ The `IP` address of the `plico_dm` server.
182
+
183
+ - **plico_port : int**
184
+
185
+ The `PORT` of the `plico_dm` server.
186
+
187
+ - **diameter : float**
188
+
189
+ The (optical) diameter of the DM.
190
+
191
+ The `PetalDM` is made of six segments, each one with its own address:
192
+
193
+ - **ip0, ..., ip5 : str**
194
+
195
+ The `IP` address of each one of the six petals.
196
+
197
+ <ins>NOTE</ins>: multiple devices can be defined in the configuration file, and actually it is recommended to define ahead all the possible available devices, to have a smoother experience on instancing the DM objects.
198
+
199
+ ### Variables Overview: `CAMERAS`
200
+
201
+ - **"device_name"** (Here goes the camera name that has been defined, i.e. `GigaVision`)
202
+
203
+ - **id : str**
204
+
205
+ The identifier of the camera (e.g. `DEV_000F314DE425`).
206
+
207
+ - **ip : str**
208
+
209
+ The `IP` address of the camera, if applicable.
210
+
211
+ - **buffer_size : int**
212
+
213
+ The number of frames kept in memory while streaming. If left undefined, it defaults to `100`.
214
+
215
+ ### Variables Overview: `MOTORS`
216
+ The motor devices usually use the `plico_motor` interface, as their SDK is not implemented in Linux.
217
+
218
+ - **"device_name"** (Here goes the motor name that has been defined, i.e. `TunableFilter`)
219
+
220
+ - **ip : str**
221
+
222
+ The `IP` address of the `plico_motor` server.
223
+
224
+ - **port : int**
225
+
226
+ The `PORT` of the `plico_motor` server.
227
+
228
+ ## Phasing Section
229
+ ### Description
230
+ In this section of the configuration file, under the `PHASING` key, are defined the parameters for the SPL phasing sensor, which is usually a camera plus a tunable filter (both to be defined in the `DEVICES` section), looking at a frame in which a certain number of PSFs is expected.
231
+
232
+ ### Variables Overview
233
+ - **camera : str**
234
+
235
+ The camera of the sensor, in the `CAMERAS:name` notation.
236
+
237
+ - **filter : str**
238
+
239
+ The tunable filter of the sensor, in the `MOTORS:name` notation.
240
+
241
+ - **expected_psfs : int**
242
+
243
+ The number of different PSFs to expect in the frame.
244
+
245
+ - **psfs_angles : list of float**
246
+
247
+ The angles of the PSFs, in degrees, with respect to the horizontal axis of the camera.
248
+
249
+ - **expected_psfs_pos : list of lists**
250
+
251
+ The expected coordinates of the PSFs, ordered as the angles. This is used to better identify the correct centroids.
252
+
253
+ - **initial_crop_half_size : int**
254
+
255
+ Half size, in pixels, of the crop used for the first PSF detection, which is then refined with a photometric centroid detection.
256
+
257
+ - **crop_half_size : int or list of int**
258
+
259
+ Half size, in pixels, of the final crop. If a list, it is `[y, x]`.
260
+
261
+ - **min_px_threshold : int**
262
+
263
+ The minimum number of pixels above the threshold to consider a PSF as detected.
264
+
265
+ - **sigma_threshold : float**
266
+
267
+ The number of sigma above the background to use as threshold for the detection.
268
+
269
+ ## IFF Section
270
+ ### Description
271
+ In this section of the configuration file, under the `INFLUENCE.FUNCTIONS` key, are defined all the essential parameters needed to acquire the influence function for a deformable mirror. For each acquisition, a copy of this section is saved along with the data (`iffConfig.yaml`), for provenance.
272
+
273
+ ### Variables Overview
274
+ There are two groups of parameters to define for the IFF acquisition: the general ones, which are related to the synchronization between the deformable mirror and the sensor, and three parameter classes defining the command to apply.
275
+
276
+ #### General parameters
277
+ In here we find information related to the hardware behaviour of the deformable mirror and its synchronization with the acquisition:
278
+
279
+ - **timing : int**
280
+
281
+ The timing information is needed for the synchronization between the dm and the interferometer acquisition, and is effectively done by repeating each column of the `timed command history` by the amount specified in this parameter. This is a tentative of replacement for the interferometer trigger line, which is the best way to perform synchronization between interferometer and Deformable Mirror's command actuation.
282
+
283
+ - **sequential_delay : float**
284
+
285
+ This is the delay, in seconds, artificially introduced between an actuated command and it's subsequent sensor acquisition, useful if the device at hand has a setting time for the commanded shape.
286
+
287
+ - **triggered_mode**
288
+
289
+ The parameters for the triggered acquisition, in which the DM runs the command history by itself, synchronized with the sensor. If it is not available, set it to `false` and comment the next two lines.
290
+
291
+ - **frequency : float**
292
+
293
+ The frequency, in Hz, at which the commands are applied.
294
+
295
+ - **cmd_delay : float**
296
+
297
+ The delay, in seconds, between the start of the command history and the start of the acquisition.
298
+
299
+ #### `TRIGGER - REGISTRATION - IFFUNC`
300
+ The following three parameter classes share almost the same structure, and define how is the `Timed Command Matrix History` (TCMH) is built, that is the command matrix that will be uploaded and run by the deformable mirror.
301
+
302
+ - `TRIGGER`
303
+
304
+ The trigger, the first part of the `TCMH`, consists of a mode "shot" with high amplitude to identify the start of the measures on the resulting data cube.
305
+
306
+ - `REGISTRATION`
307
+
308
+ The second part of the `TCMH` is the registration, where 3 (or more) actuators are individually actuated following a template, to allow the re-alignment of the images within the measured data, in the case of, for example, the system's position has drifted during the measurements.
309
+
310
+ - `IFFUNC`
311
+
312
+ The final, bigger, part which makes up the `TCMH` is the Influence function itself, which is a series of modes repeated on a template, later analized to obtain the calibration.
313
+
314
+ As for the variables iside each parameter class, they are:
315
+
316
+ - **trailing_zeros : int**
317
+
318
+ This is the number of zeros at the start of each section. Serves as a sort of "spacing" between sections of the `TCMH`.
319
+
320
+ - **modes_list : list or str**
321
+
322
+ This variables indicates the modes to command. It can be an hand-written list or, more conveniently in the case of modes equal to the number of actuators of the DM, a string can be used to form an array in python, like
323
+
324
+ ```yaml
325
+ modes_list : np.arange(0,88,1)
326
+ ```
327
+
328
+ In this way, `modes_list` is the list of integers from 0 to 87, which tells the system to run all the modes from 1 to 88 on the selected modal base. Only the `np.arange(start, stop[, step])` expression is accepted.
329
+
330
+ - **amplitude : float or list**
331
+
332
+ This is the amplitude each command receives.<br>
333
+ If passed as a single float number, it is then transformed into an array with lenght equal to the number of measured modes, while if passed as list, it must have same size as the `modes_list` array, as each value corresponds to the amplitude of the command at same inxed position.
334
+
335
+ - **template : list of int**
336
+
337
+ A list of integers defining how each mode is executed. Usually is `[1,-1,1]`, which means that each mode is executed in a `push-pull-push` way.
338
+
339
+ - **modal_base : str**
340
+
341
+ This string defines the modal base to use for the calibration. There are several options:
342
+
343
+ - **zonal**
344
+
345
+ This is the zonal modal base, which is represented by the identity matrix of dimentions $[N_{acts}\times N_{acts}]$ and means that each mode corresponds to a single actuator.
346
+
347
+ - **hadamard**
348
+
349
+ The [hadamard matrix](https://en.wikipedia.org/wiki/Hadamard_matrix), created as $H_{2^{10}}$ and then cut down on the number of actuators.
350
+
351
+ - **mirror**
352
+
353
+ This option uses the mirror's modes defined in the mirror class itself, which must be present as `*devicename*Mirror.fits` in the `ModalBases` folder.
354
+
355
+ - **"custom"**
356
+
357
+ A custom modal base is admitted, as long as the string put in the `modal_base` variable corresponds to the name of the actual modal base `.fits` file in the `ModalBases` folder.
358
+
359
+ Only the `IFFUNC` class has the following additional variables:
360
+
361
+ - **padding_zeros : int**
362
+
363
+ The number of zeros after the modes.
364
+
365
+ - **shuffle : bool**
366
+
367
+ Whether to shuffle the order of the commanded modes.
368
+
369
+ - **n_repetitions : int**
370
+
371
+ The number of times the application of each mode is repeated.
372
+
373
+ ## Stitching Section
374
+ ### Description
375
+ In this section of the configuration file, under the `STITCHING` key, are defined the parameters for the stitching procedure, in which a motorized stage moves the sensor (or the optic) to acquire different sub-apertures of a larger surface.
376
+
377
+ ### Variables Overview
378
+ - **motor : str**
379
+
380
+ The motor device which moves the stage, in the `MOTORS:name` notation.
381
+
382
+ - **pixel_scale : float**
383
+
384
+ The pixel scale of the image, in mm/px.
385
+
386
+ - **alpha : float**
387
+
388
+ The rotation angle, in degrees, between the motor axes and the image.
389
+
390
+ - **starting_coords : list of float**
391
+
392
+ The motors position, in mm, at which the stitching procedure starts.
393
+
394
+ - **home_coords : list of float**
395
+
396
+ The motors position, in mm, of the "home" position.
397
+
398
+ ## Alignment Section
399
+ ### Description
400
+ In this section of the configuration file, under the `SYSTEM.ALIGNMENT` key, are defined all the parameters and function calls of the various devices for the optical alignment of the system.
401
+
402
+ ### <ins>Important Notes</ins>
403
+ The element order of the lists is <ins>**essential**</ins>, as the code will use the same index
404
+ for all the lists to access the right information. The order of the devices in the
405
+ lists must be the same as the order in the command matrix supplied, eg. if `device_y`
406
+ is the second in place going through the command vector, then it's position must be
407
+ the second for every list in this configuration file.
408
+
409
+ ### Variables Overview
410
+ - **names : list of str**
411
+
412
+ This list contains the names of the devices, in the same order as all the
413
+ other lists. This is mainly used for logging purposes and print fancyness.<br>
414
+ <ins>Use the same order of the devices used here for every subsequent variable in the configuration.</ins>
415
+
416
+ - **devices_move_calls : list of str**
417
+
418
+ This list must contain the callables for moving each device, in the same
419
+ order as all the other lists.<br>
420
+
421
+ - **devices_read_calls : list of str**
422
+
423
+ This list must contain the callables used to read the devices positions, in
424
+ the same order as all the other lists.
425
+
426
+ - **ccd_acquisition : list of str**
427
+
428
+ This list contains the callables for the acquisition device (Interferometer, camera,...). The first item must be the acquisition method.
429
+
430
+ - **devices_dof : list of int | int**
431
+
432
+ This variable represents the total degrees of freedom, at the software level, each device has. This parameter, then, will be the length of the accepted command vector by the devices. It can be an `int` (if all the devices use have the same number of accepted dof) or a list of integers, where each int corresponds to the dof of a device.
433
+
434
+ - **dof : list of lists**
435
+
436
+ This variable, initialized as an empty list, will contain the degrees of
437
+ freedom each device <ins>can actually move</ins>.<br>
438
+
439
+ <ins>Example</ins>:<br>
440
+ If a device can move only 3 DoF, say `piston`, `tip` and `tilt`, then, if the
441
+ `devices_dof` for the device if 6 (i.e the device accept a vector of 6 elements), the 'dof' list should be the index at which these degrees of freedom are located in the
442
+ accepted command vector. In this case, the list could be [2, 3, 4] (as
443
+ in the case for the M4's OTT) and the total vector would then be `[0,0,p,t,t,0]`.
444
+
445
+ - **slices : list**
446
+
447
+ This variable is a list of slices that will be used to extract the right dof
448
+ positions from the command matrix full vector.<br>
449
+ Each slice is defined with the `start` and `stop` points:
450
+
451
+ - **start : int**
452
+
453
+ Starting point of the slice
454
+
455
+ - **stop : int**
456
+
457
+ Ending point of the slice (python notation, so the actual index used is `stop - 1`)
458
+
459
+ <ins>Example</ins>:<br>
460
+ If the command matrix is a column vector (the full command) of 7 elements,
461
+ in which the first 3 elements are for the `device_1` dof, the second 2
462
+ for `device_2` dof and the last 3 for `device_3` dof, then the slices parameters should be:
463
+
464
+ ```yaml
465
+ slices:
466
+ # device_1
467
+ - start : 0
468
+ stop : 3
469
+ # device_2
470
+ - start : 3
471
+ stop : 5
472
+ # device_3
473
+ - start : 5
474
+ stop : 7
475
+ ```
476
+
477
+ - **zernike_to_use : list of int**
478
+
479
+ This variable contains the zernike modes indices which will be used for the
480
+ Interaction Matrix Construction, extracted from the general zernike fit. It is a list of integers, where each value corresponds to a mode order.
481
+
482
+ - **push_pull_template : list of int**
483
+
484
+ Template for the push-pull algorithm. Being the command differential, the
485
+ push and pulls are to be written with respect to zero.<br>
486
+
487
+ <ins>Example</ins>: A classic `[1,-1,1]` template, translates into `[1,-2,1]`.
488
+
489
+ - **commandMatrix : str**
490
+
491
+ Control matrix file name, which must be a `.fits` file located under the `base_data_path/OPTData/Alignment/ControlMatrices` folder.
492
+
493
+ - **fitting_surface : str**
494
+
495
+ Path to the calibration fits file, whose mask will be used for zernike fitting
496
+ purposes. If no mask is to be provided, leave it as an empty string.
497
+
498
+ ### Builtin Example: M4's OTT
499
+ The template (root) configuration file has the alignment section filled with the configuration for the ELT@M4 Optical Test Tower (OTT). Here we we have three devices: the Parabola, the Reference Mirror and the M4 Exapode. Each device accept a vector of 6 elements, which means they "speak" in 6 DoF, however, for 'reality' constraints, they can only move certain dof. In particular: the Parabola can move in piston, tip and tilt, so the DoF are 2, 3 and 4; the Reference Mirror can move in tip and tilt, so the DoF are 3 and 4; the M4 Exapode can move in tip and tilt, so the DoF are 3 and 4. The command matrix (7x7) has a 7 element columns, which is the alignment command, and these devices appear in the same order as are listed above, so the ordering inside every list will be Parabola-ReferenceMirror-M4Exapode.