opticalib 2.1.3__tar.gz → 2.2.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 (148) hide show
  1. opticalib-2.2.0/CITATION.bib +10 -0
  2. {opticalib-2.1.3 → opticalib-2.2.0}/MANIFEST.in +5 -4
  3. {opticalib-2.1.3/opticalib.egg-info → opticalib-2.2.0}/PKG-INFO +41 -27
  4. {opticalib-2.1.3 → opticalib-2.2.0}/README.md +11 -0
  5. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/__init__.py +11 -15
  6. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/__version__.py +1 -1
  7. opticalib-2.2.0/opticalib/analyzer/__init__.py +13 -0
  8. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/analyzer/image_processing.py +37 -3
  9. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/_types.py +16 -1
  10. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/config.py +2 -2
  11. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/root.py +4 -4
  12. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/alpaoAPI.py +22 -14
  13. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/base_devices.py +23 -10
  14. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/piAPI.py +5 -5
  15. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/deformable_mirrors.py +64 -98
  16. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/interferometer.py +52 -27
  17. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/flattening.py +32 -1
  18. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/iff_processing.py +1 -3
  19. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/_stitching_algorithm.py +13 -13
  20. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/reconstructor.py +5 -5
  21. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/__init__.py +9 -2
  22. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/activity.py +47 -14
  23. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/app.py +307 -39
  24. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/kernel.py +131 -14
  25. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/plugins.py +3 -1
  26. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/alignment.py +30 -8
  27. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/base.py +51 -14
  28. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/dm_calibration.py +145 -31
  29. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/params.py +64 -18
  30. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/phasing.py +76 -17
  31. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/stitching.py +31 -5
  32. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/timeseries.py +24 -5
  33. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/common.py +9 -2
  34. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/config_editor.py +225 -5
  35. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/connect_dialog.py +23 -7
  36. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/data_browser.py +22 -8
  37. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/device_panel.py +49 -19
  38. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/device_registry.py +132 -39
  39. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/plot_viewer.py +60 -13
  40. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/workspace.py +6 -2
  41. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/procedures/iff.py +9 -11
  42. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/_API/__init__.py +2 -0
  43. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/_API/base_fake_adopticadm.py +19 -75
  44. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/_API/base_fake_alpao.py +28 -85
  45. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/_API/base_petalmirror.py +11 -52
  46. opticalib-2.2.0/opticalib/simulator/_API/base_simulated_dm.py +378 -0
  47. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/__init__.py +2 -0
  48. opticalib-2.2.0/opticalib/simulator/fake_dms.py +278 -0
  49. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/visualization.py +88 -0
  50. {opticalib-2.1.3 → opticalib-2.2.0/opticalib.egg-info}/PKG-INFO +41 -27
  51. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib.egg-info/SOURCES.txt +5 -4
  52. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib.egg-info/requires.txt +22 -3
  53. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib.egg-info/top_level.txt +0 -1
  54. opticalib-2.2.0/pyproject.toml +103 -0
  55. opticalib-2.2.0/setup.cfg +4 -0
  56. {opticalib-2.1.3 → opticalib-2.2.0}/setup_calpy.py +2 -13
  57. opticalib-2.2.0/tests/README.md +110 -0
  58. {opticalib-2.1.3 → opticalib-2.2.0}/tests/conftest.py +3 -3
  59. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_analyzer_images_processing.py +24 -0
  60. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_core_read_config.py +1 -1
  61. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_core_root.py +2 -2
  62. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_devices_deformable_mirrors.py +1 -1
  63. opticalib-2.2.0/tests/test_devices_interferometer.py +93 -0
  64. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_config_editor.py +84 -0
  65. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_kernel_bridge.py +31 -0
  66. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_kernel_side.py +23 -0
  67. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_widgets.py +47 -0
  68. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_setup_calpy.py +13 -11
  69. opticalib-2.1.3/opticalib/analyzer/__init__.py +0 -3
  70. opticalib-2.1.3/opticalib/simulator/_API/_rbf_gpu.py +0 -367
  71. opticalib-2.1.3/opticalib/simulator/fake_dms.py +0 -846
  72. opticalib-2.1.3/requirements.txt +0 -24
  73. opticalib-2.1.3/setup.cfg +0 -28
  74. opticalib-2.1.3/setup.py +0 -56
  75. {opticalib-2.1.3 → opticalib-2.2.0}/LICENSE +0 -0
  76. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/__init_script__/initCalpy.py +0 -0
  77. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/analyzer/signals.py +0 -0
  78. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/analyzer/timeseries.py +0 -0
  79. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/__init__.py +0 -0
  80. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/_configurations/configuration.yaml +0 -0
  81. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/data_classes.py +0 -0
  82. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/decorators.py +0 -0
  83. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/exceptions.py +0 -0
  84. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/core/fitsarray.py +0 -0
  85. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/__init__.py +0 -0
  86. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/i4dAPI.py +0 -0
  87. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/micAPI.py +0 -0
  88. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/_API/splattAPI.py +0 -0
  89. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/__init__.py +0 -0
  90. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/cameras.py +0 -0
  91. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/devices/wfs.py +0 -0
  92. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/__actuator_identification_wip.py +0 -0
  93. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/__init__.py +0 -0
  94. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/__pupil_calibration_wip.py +0 -0
  95. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/_misc.py +0 -0
  96. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/iff_preparation.py +0 -0
  97. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/dmutils/slaving.py +0 -0
  98. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/__init__.py +0 -0
  99. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/geometry.py +0 -0
  100. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/logger.py +0 -0
  101. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/modal_decomposer.py +0 -0
  102. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/osutils.py +0 -0
  103. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/ground/roi.py +0 -0
  104. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/kernel_backend.py +0 -0
  105. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/kernel_side.py +0 -0
  106. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/procedures/__init__.py +0 -0
  107. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/resources/opticalib.ico +0 -0
  108. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/resources/opticalib.png +0 -0
  109. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/resources/opticalib.svg +0 -0
  110. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/theme.py +0 -0
  111. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/gui/widgets/__init__.py +0 -0
  112. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/procedures/__init__.py +0 -0
  113. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/procedures/alignment.py +0 -0
  114. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/procedures/measurements.py +0 -0
  115. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/procedures/phasing.py +0 -0
  116. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/procedures/stitching.py +0 -0
  117. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/py.typed +0 -0
  118. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/_API/alpao_conf.yaml +0 -0
  119. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/_API/simdata.py +0 -0
  120. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/factory.py +0 -0
  121. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib/simulator/fake_interf.py +0 -0
  122. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib.egg-info/dependency_links.txt +0 -0
  123. {opticalib-2.1.3 → opticalib-2.2.0}/opticalib.egg-info/entry_points.txt +0 -0
  124. {opticalib-2.1.3 → opticalib-2.2.0}/tests/__init__.py +0 -0
  125. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_analyzer_signals.py +0 -0
  126. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_analyzer_timeseries.py +0 -0
  127. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_core_dataclass.py +0 -0
  128. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_core_exceptions.py +0 -0
  129. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_core_fits.py +0 -0
  130. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_dmutils_flattening.py +0 -0
  131. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_dmutils_iff_acquisition_preparation.py +0 -0
  132. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_dmutils_iff_module.py +0 -0
  133. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_dmutils_iff_processing.py +0 -0
  134. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_dmutils_slaving.py +0 -0
  135. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_ground_computerec.py +0 -0
  136. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_ground_geometry.py +0 -0
  137. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_ground_logger.py +0 -0
  138. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_ground_modal_decomposer.py +0 -0
  139. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_ground_osutils.py +0 -0
  140. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_ground_roi.py +0 -0
  141. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_activity.py +0 -0
  142. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_app.py +0 -0
  143. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_devices.py +0 -0
  144. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_gui_procedures.py +0 -0
  145. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_measurements.py +0 -0
  146. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_simulator_factory_functions.py +0 -0
  147. {opticalib-2.1.3 → opticalib-2.2.0}/tests/test_simulator_simdata.py +0 -0
  148. {opticalib-2.1.3 → opticalib-2.2.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 requirements.txt
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
- recursive-include labott *.py
8
+ graft tests
9
+ global-exclude __pycache__ *.py[cod]
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opticalib
3
- Version: 2.1.3
3
+ Version: 2.2.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
- Home-page: https://github.com/ArcetriAdaptiveOptics/opticalib
6
- Author: Adaptive Optics Group - INAF - Osservatorio Astrofisico di Arcetri
7
- Author-email: pietro.ferraiuolo@inaf.it
8
- Maintainer: Pietro Ferraiuolo
9
- License: MIT License
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
@@ -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: qtconsole
35
- Requires-Dist: PyYAML
36
- Requires-Dist: PySide6-Essentials
37
- Requires-Dist: QtPy
38
- Requires-Dist: ipykernel
39
- Requires-Dist: jupyter_client
40
- Requires-Dist: pyqtgraph
41
- Requires-Dist: qtawesome
42
- Dynamic: author
43
- Dynamic: author-email
44
- Dynamic: classifier
45
- Dynamic: description
46
- Dynamic: description-content-type
47
- Dynamic: home-page
48
- Dynamic: license
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
  ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ArcetriAdaptiveOptics/opticalib/tests.yml)
61
+ [![PyPI version](https://img.shields.io/pypi/v/opticalib.svg)](https://pypi.org/project/opticalib/)
62
+ [![Python versions](https://img.shields.io/pypi/pyversions/opticalib.svg)](https://pypi.org/project/opticalib/)
63
+ [![License: MIT](https://img.shields.io/pypi/l/opticalib.svg)](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
  ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ArcetriAdaptiveOptics/opticalib/tests.yml)
4
+ [![PyPI version](https://img.shields.io/pypi/v/opticalib.svg)](https://pypi.org/project/opticalib/)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/opticalib.svg)](https://pypi.org/project/opticalib/)
6
+ [![License: MIT](https://img.shields.io/pypi/l/opticalib.svg)](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.1.3"
12
+ __version__ = "2.2.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,38 @@ 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
+ padded_img = _np.full(shape, fill_value)
49
+ y, x = offset
50
+ h, w = _np.shape(img)
51
+ padded_img[y : y + h, x : x + w] = img.data
52
+ return padded_img
53
+
54
+
23
55
  def frame(idx: int, mylist: list[_ot.ImageData] | _ot.CubeData) -> _ot.ImageData:
24
56
  """
25
57
  Returns a single frame from a list of files or from a cube.
@@ -225,7 +257,7 @@ def push_pull_reduction_algorithm(
225
257
  # Template weights computation
226
258
  w = _xp.asarray(
227
259
  template.astype(_np.result_type(template, imagelist[0].data), copy=True),
228
- dtype=_xp.float,
260
+ dtype=_ot.compute_float(),
229
261
  )
230
262
  if n_images > 2:
231
263
  w[1:-1] *= 2.0
@@ -233,9 +265,9 @@ def push_pull_reduction_algorithm(
233
265
  master_mask = _np.logical_or.reduce([ima.mask for ima in imagelist])
234
266
  # Compute weighted sum over realizations on raw data
235
267
  stack = _xp.stack(
236
- [_xp.asarray(ima.data, dtype=_xp.float) for ima in imagelist],
268
+ [_xp.asarray(ima.data, dtype=_ot.compute_float()) for ima in imagelist],
237
269
  axis=0,
238
- dtype=_xp.float,
270
+ dtype=_ot.compute_float(),
239
271
  ) # (n, H, W)
240
272
  image = _xp.asnumpy(_xp.tensordot(w, stack, axes=(0, 0))) # (H, W)
241
273
 
@@ -860,6 +892,8 @@ def rebin2_d_array(
860
892
 
861
893
 
862
894
  __all__ = [
895
+ "pad_image_to_shape",
896
+ # "into_4d_full_frame",
863
897
  "frame",
864
898
  "unwrap_piston",
865
899
  "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, *, slave: bool | str = False
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
@@ -558,8 +558,8 @@ def get_cmd_delay(bpath: str = _cfold):
558
558
  sub_section="DM",
559
559
  path=bpath,
560
560
  )
561
- if "sequentialDelay" in dm_config:
562
- cmdDelay = float(dm_config["sequentialDelay"])
561
+ if "sequential_delay" in dm_config:
562
+ cmdDelay = float(dm_config["sequential_delay"])
563
563
  elif "delay" in dm_config:
564
564
  cmdDelay = float(dm_config["delay"])
565
565
  elif "triggeredMode" in dm_config and "cmdDelay" in dm_config["triggeredMode"]:
@@ -351,7 +351,7 @@ class ConfSettingReader4D:
351
351
  defined object:
352
352
 
353
353
  >>> cr = ConfSettingReader4D(file_path)
354
- >>> cr.get_image_widht_in_pixels()
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 get_image_widht_in_pixels(self):
385
+ def get_image_width_in_pixels(self):
386
386
  """
387
- Returns the image widht in pixel scale
387
+ Returns the image width in pixel scale
388
388
 
389
389
  Returns
390
390
  -------
391
- image_wight_in_pixels : int
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 = int(
116
- self._plico_dm.get_number_of_actuators()
117
- ) if self._is_plico else int(self._sdk_dm.Get("NbOfActuator"))
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.cmdHistory = None
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("Version information is not available for PLICO devices.")
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)
@@ -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(CONFIGURATION_FOLDER, "alpao_sdk")
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(self.sdk_folder_path):
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,17 @@ 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(self, cmd: _ot.ArrayLike, slave: bool | str) -> _ot.ArrayLike:
120
+ def _apply_slaving(self, cmd: _ot.ArrayLike|_ot.MatrixLike, slave: bool | str) -> _ot.ArrayLike|_ot.MatrixLike:
101
121
  """
102
122
  Apply actuator slaving to a command when requested.
103
123
  """
104
124
  method = self._get_slaving_method(slave)
105
125
  if method is None:
106
126
  return cmd
127
+ if _ot.isinstance_(cmd, "MatrixLike"):
128
+ return compute_slaved_command_matrix(self, cmdmat=cmd, method=slave)
107
129
  return compute_slave_cmd(self, cmd, method=method)
108
130
 
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
131
 
119
132
  class BaseCamera(ABC):
120
133
 
@@ -31,9 +31,9 @@ class BasePetalMirror:
31
31
 
32
32
  self._had_error = False
33
33
  self.is_segmented = True
34
- self.nSegments = 6
35
- self.nActsPerSegment = 3
36
- self.n_acts = self.nSegments * self.nActsPerSegment
34
+ self.n_segments = 6
35
+ self.n_acts_per_segment = 3
36
+ self.n_acts = self.n_segments * self.n_acts_per_segment
37
37
  self._slaveIds = ptl_config.get("slave_ids", [])
38
38
  self._borderIds = ptl_config.get("border_ids", [])
39
39
 
@@ -213,7 +213,7 @@ class BasePetalMirror:
213
213
  CommandError
214
214
  If any command is outside the allowed limits.
215
215
  """
216
- for k in self.nSegments:
216
+ for k in range(self.n_segments):
217
217
  for ax, c in enumerate(cmd[k * 3 : k * 3 + 3]):
218
218
  if not (_CMDLIMITS[ax][0] <= c <= _CMDLIMITS[ax][1]):
219
219
  raise CommandError(f"Command for actuator {ax} out of bounds: {c}")
@@ -326,4 +326,4 @@ class BasePetalMirror:
326
326
  raise RuntimeError("Failed to check/enable axes") from err
327
327
 
328
328
  def __repr__(self):
329
- return f"PetalMirror(nSegments={self.nSegments}, nActsPerSegment={self.nActsPerSegment})"
329
+ return f"PetalMirror(n_segments={self.n_segments}, n_acts_per_segment={self.n_acts_per_segment})"