Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/src/user_guide/calibration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ Calibration Procedure
---------------------
1. **Open the Calibration Tool**: Click **Calibration** |equalizer| in the toolbar or navigate to **Shaping > Calibration > Calibration**.

2. **Select the SLM**: Ensure the **Shaper** module is loaded in PyMoDAQ’s Dashboard.
2. **Select the SLM and the camera**: Ensure an actuator named **Shaper** (the SLM) and a detector named **Camera**
(seeing the fringes) are loaded in PyMoDAQ’s Dashboard. The scan configuration (``BeamShapingCalibration``
scanner, ``Shaper`` and ``Camera`` selected) is a Scan Manager entry shipped with the package.

3. **Run Calibration**: The tool performs a **double-slit experiment**:

Expand Down
Binary file not shown.

This file was deleted.

1 change: 0 additions & 1 deletion src/pymodaq_plugins_beam_shaping/scanners/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

@author: Sebastien Weber
"""
from cProfile import label
from typing import List, Tuple, Any, TYPE_CHECKING
import re
import numpy as np
Expand Down
56 changes: 56 additions & 0 deletions src/pymodaq_plugins_beam_shaping/scripts/make_calibration_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Generate the Scan Manager entry used by the SLM calibration (resources/holography_calibration.scan)

The entry selects the detector named Camera, the actuator named Shaper and the BeamShapingCalibration scanner. Only the
names matter: mock modules are loaded in a Dashboard to create the entry with the Scan Manager itself, so it stays
valid whatever the real camera and SLM are. Run it again if the format of the entries changes with PyMoDAQ.
"""
import sys
import time
from pathlib import Path

from pymodaq.control_modules.enums import DAQTypesEnum
from pymodaq.utils.managers.modules import ModuleType
from pymodaq.utils.managers.modules.loader import PluginInfo

from pymodaq_plugins_beam_shaping.utilities.calibrating import CALIBRATION_ENTRY


def create_dashboard_with_camera_and_shaper(qapp, timeout=20.):
""" Create a Dashboard with a detector named Camera and an actuator named Shaper (mock ones)"""
from pymodaq.dashboard import create_load_dashboard

_, dashboard = create_load_dashboard()
dashboard.module_creator.load_created_modules([
[PluginInfo(id=1, name='Shaper', class_name='MockShaper', type=ModuleType.Actuator, do_init=False)],
[PluginInfo(id=2, name='Camera', class_name='MockShaperCamera', type=ModuleType.Detector, do_init=False,
daq_type=DAQTypesEnum.DAQ2D)]])
start = time.time()
while time.time() - start < timeout and len(dashboard.modules_manager.modules_all) < 2:
qapp.processEvents()
return dashboard


def main(entry_path: Path = CALIBRATION_ENTRY):
from pymodaq_gui.qt_utils import mkQApp
from pymodaq.extensions import DAQScan
from pymodaq.utils.gui_utils.loader_utils import create_extension

qapp = mkQApp('CalibrationEntry')
dashboard = create_dashboard_with_camera_and_shaper(qapp)
_, daq_scan = create_extension(dashboard, DAQScan)

scan_manager = daq_scan.scan_manager
modules = scan_manager.modules_manager
for name, selected in (('detectors', ['Camera']), ('actuators', ['Shaper'])):
modules.settings[name] = dict(all_items=modules.settings[name]['all_items'], selected=selected)
scan_manager.scanner.settings.child('scan_type').setValue('Scan1D')
scan_manager.scanner.settings.child('scan_sub_type').setValue('BeamShapingCalibration')

entry_path.unlink(missing_ok=True)
scan_manager.save_entries(entry_path)
print(f'Entry saved in {entry_path}')


if __name__ == '__main__':
main()
sys.exit(0)
18 changes: 8 additions & 10 deletions src/pymodaq_plugins_beam_shaping/utilities/calibrating.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
logger = set_logger(get_module_name(__file__))


# TODO: the xml file resources/holography_calibration.xml was a scan configuration of the pymodaq < 5.3 scanner manager
# (ScanManager.get_scanner_folder no longer exists). It has to be rewritten as an entry of the scan manager of 5.3
# (folder given by ScanManager.get_entry_folder) before BeamShapingCalibration can be used again.
# Scan Manager entry (pymodaq >= 5.3) selecting the detector named Camera, the actuator named Shaper and the
# BeamShapingCalibration scanner. It is generated by scripts/make_calibration_entry.py
CALIBRATION_ENTRY = Path(__file__).parents[1].joinpath('resources', 'holography_calibration.scan')


def check_monotonic(phases: np.ndarray) -> bool:
Expand Down Expand Up @@ -160,7 +160,7 @@ def do_things_after_ui_setup(self):
self.show_widgets(False)

def show_widgets(self, show=True):
for widget in (self.ui.dock_command, self.ui.get_toolbar('scan_manager')):
for widget in (self.dock_command, self.get_toolbar('scan_manager')):
widget.setVisible(show)

def do_things_after_experiment_set(self, experiment_name: str):
Expand All @@ -175,11 +175,7 @@ def do_things_after_experiment_set(self, experiment_name: str):

if hasattr(self, 'scan_manager'): #could happen because base class calls do_things_after_experiment_set before
# scan_manager is set
self.scan_manager.entry = 'holography_calibration'
self.scan_manager.execute_entry()

QtWidgets.QApplication.processEvents()

self.scan_manager.execute_entry(CALIBRATION_ENTRY)
self.connect_start_stop_step()

def connect_start_stop_step(self):
Expand Down Expand Up @@ -227,6 +223,7 @@ def connect_things(self):
self.scan_done_signal.connect(self.save_calibration)

def save_calibration(self):
dwa_calibration = None
with DataLoader(self.h5saver) as loader:
try:
dwa_calibration = loader.load_data_from_name_origin(
Expand Down Expand Up @@ -265,7 +262,8 @@ def save_calibration(self):
saver.add_data(where=saver.raw_group, data=dwa_calibration)
except NameError:
pass
self.show_calibration(show=True, calibration_dwa=dwa_calibration)
if dwa_calibration is not None:
self.show_calibration(show=True, calibration_dwa=dwa_calibration)

@classmethod
def get_calibration_dwa(cls) -> DataWithAxes:
Expand Down
50 changes: 50 additions & 0 deletions tests/test_calibration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""The SLM calibration is a DAQScan whose scan configuration comes from a Scan Manager entry"""
import numpy as np
import pytest

from pymodaq_data import DataRaw, DataToExport


def test_calibration_scanner_phase_from_fringes(qtbot):
from types import SimpleNamespace
from pymodaq.utils.scanner.scanner import Scanner
import pymodaq_plugins_beam_shaping.scanners.calibration # registers the scanner # noqa: F401

shaper = SimpleNamespace(title='Shaper', units='')
scanner = Scanner(actuators=[shaper], selected_actuators=[shaper])
scanner.settings.child('scan_type').setValue('Scan1D')
scanner.settings.child('scan_sub_type').setValue('BeamShapingCalibration')
calibration = scanner.scanner
assert calibration.n_steps == 256

data = calibration.data_actuator_at(100) # grey level on the right half of the SLM
assert data.as_grey_levels
assert set(np.unique(data[0])) == {0, 100}

x = np.arange(1024)
phases = []
for grey in range(0, 256, 32):
fringes = 100 + 50 * np.cos(2 * np.pi * x / 16 + grey / 255 * 3 * np.pi)
dwa = DataRaw('Camera', data=[np.tile(fringes, (50, 1))])
phase = calibration.process_data(DataToExport('Camera', data=[dwa])).get_data_from_name('Phase')
phases.append(phase[0][0])
assert np.unwrap(np.array(phases) - phases[0])[-1] == pytest.approx(224 / 255 * 3 * np.pi, rel=1e-2)


def test_calibration_extension_uses_scan_manager_entry(qtbot, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path) # the DAQScan creates its h5 file with a relative path on Linux
from pymodaq_gui.qt_utils import mkQApp
from pymodaq.utils.gui_utils.loader_utils import create_extension
from pymodaq_plugins_beam_shaping.scripts.make_calibration_entry import create_dashboard_with_camera_and_shaper
from pymodaq_plugins_beam_shaping.utilities.calibrating import BeamShapingCalibration, CALIBRATION_ENTRY

assert CALIBRATION_ENTRY.is_file()
dashboard = create_dashboard_with_camera_and_shaper(mkQApp('tests'))
win, calibration = create_extension(dashboard, BeamShapingCalibration)
calibration.do_things_after_experiment_set('test')

assert calibration.scan_manager.settings is not None
assert calibration.scanner.settings['scan_type'] == 'Scan1D'
assert calibration.scanner.settings['scan_sub_type'] == 'BeamShapingCalibration'
assert calibration.modules_manager.selected_actuators_name == ['Shaper']
assert calibration.modules_manager.selected_detectors_name == ['Camera']
Loading