Skip to content
Open
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
39 changes: 36 additions & 3 deletions src/odemis/acq/align/goffset.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,29 @@ def _checkCancelled(future: "model.ProgressiveFuture") -> None:
raise CancelledError()


def _ensure_horizontal_bin_1(detector: model.Detector) -> Dict[model.VigilantAttribute, Any]:
"""
Ensure that the detector is set to horizontal binning of 1, while keeping the same intensity
level per pixel (by increasing the exposure time proportionally).

:param detector: CCD to adjust. If binning is not supported, nothing is done.
:return: dict of attributes to restore to the previous value
"""
restore_attrs = {}
if not model.hasVA(detector, "binning") or detector.binning.value[0] == 1:
return restore_attrs

restore_attrs[detector.binning] = detector.binning.value
bin_x = detector.binning.value[0]
detector.binning.value = (1, detector.binning.value[1])

# increase exposure time to maintain same intensity per pixel
exp_t = detector.exposureTime.value
restore_attrs[detector.exposureTime] = exp_t
detector.exposureTime.value = detector.exposureTime.clip(exp_t * bin_x)

return restore_attrs

def _total_alignment_time(n_gratings: int,
n_detectors: int) -> float:
"""
Expand Down Expand Up @@ -623,10 +646,10 @@ def _do_auto_align_grating_detector_offsets(future: model.ProgressiveFuture,
:return: dict mapping (grating, detector) to alignment success boolean
:raises CancelledError: if the operation is cancelled
"""

results: Dict[tuple, bool] = {}
original_pos = {k: v for k, v in spectrograph.position.value.items()
if k in ("wavelength", "grating")}
restore_attrs : Dict[model.VigilantAttribute, Any] = {} # VAs -> value to restore

gratings = sorted(list(spectrograph.axes["grating"].choices.keys()))
logging.info(f"Available gratings: {list(spectrograph.axes['grating'].choices.keys())}")
Expand Down Expand Up @@ -671,6 +694,10 @@ def is_current_detector(d):

logging.info("Setting optical path to alignment mode: %s",align_mode)
future._subfuture = opm.setPath(align_mode, detector=first_detector)
# in the meantime, adjust the horizontal binning to the minimum, to get the best results
for d in detectors:
restore_attrs.update(_ensure_horizontal_bin_1(d))

future._subfuture.result()

_checkCancelled(future)
Expand Down Expand Up @@ -702,10 +729,10 @@ def is_current_detector(d):

logging.info("Finished alignment | Detector: %s | Grating: %s", d.name, g0)

# align remaining gratings using the first detector
if selector:
selector.moveAbsSync({selector_axes: detector_to_selector[first_detector]})

# align remaining gratings using the first detector
for g in gratings[1:]:
_checkCancelled(future)
logging.info("Switching to grating: %s", g)
Expand Down Expand Up @@ -734,12 +761,18 @@ def is_current_detector(d):
raise

finally:
logging.info("Turning off brightlight")
logging.info("Restoring previous state")
try:
bl.power.value = bl.power.range[0]
except Exception:
logging.exception("Failed to turn off the light during alignment cleanup")

try:
for va, value in restore_attrs.items():
va.value = value
except Exception:
logging.exception("Failed to restore previous detector settings")

try:
spectrograph.moveAbsSync(original_pos)
except Exception:
Expand Down
4 changes: 3 additions & 1 deletion src/odemis/acq/align/test/goffset_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ def setUpClass(cls):
cls.microscope = model.getMicroscope()
cls.optmngr = path.OpticalPathManager(cls.microscope)

# The simulator only simulates the 0th order when wavelength is set to 0nm, so force it.
cls.spgr.moveAbsSync({"wavelength": 0.0})
cls._original_position = cls.spgr.position.value.copy()

Expand Down Expand Up @@ -200,7 +201,7 @@ def test_scale_not_misaligned(self):

# If peak is already centered, the algorithm exits immediately
# so goffset should not change.
self.assertAlmostEqual(start_goffset, end_goffset, places=6,
self.assertAlmostEqual(start_goffset, end_goffset, delta=1,
msg="goffset changed even though peak was already centered (scale estimation likely ran)")

def test_scale_estimation_misaligned(self):
Expand Down Expand Up @@ -265,6 +266,7 @@ def test_single_detector_updates_grating(self):
except Exception:
logging.debug("Selector move to secondary failed or not present; continuing")

self.spgr.moveRelSync({"goffset": 321}) # intentionally misalign
start_goffset = self.spgr.position.value["goffset"]

f = sparc_auto_grating_offset(self.spgr, spccd, max_it=50)
Expand Down
3 changes: 2 additions & 1 deletion src/odemis/driver/simcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
GOFFSET_TO_PIXEL = 0.25 # Conversion factor for grating offset to image pixels.
PEAK_WIDTH = 2.5 # Width of the simulated spectrograph peak in pixels (before binning).


class Camera(model.DigitalCamera):
'''
This represent a fake digital camera, which generates as data the image
Expand Down Expand Up @@ -429,7 +430,7 @@ def _simulate(self) -> model.DataArray:
if self._spectrograph and self._spectrograph.position.value["wavelength"] < 10e-9:
current_offset = self._spectrograph.position.value["goffset"]

ccd_center_x = self._img_res[0] / 2 # find the x-coordinate of the center of the ccd
ccd_center_x = (self._img_res[0] - 1) / 2 # find the x-coordinate of the center of the ccd
x0_px = ccd_center_x + current_offset * GOFFSET_TO_PIXEL
roi_left = center[0] + trans[0] + stage_shift[0] - (res[0] / 2) * binning[0]

Expand Down
Loading
Loading