diff --git a/plugins/blanker_spectrum.py b/plugins/blanker_spectrum.py index 6e6a2c4530..e81f6a4d9a 100644 --- a/plugins/blanker_spectrum.py +++ b/plugins/blanker_spectrum.py @@ -228,7 +228,7 @@ def addSpectrum(self, name, detector): main_data = self.main_app.main_data stctrl = self._tab.streambar_controller - spg = stctrl._getAffectingSpectrograph(detector) + spg = stctrl._getAffectingSpectrograph(detector, default=main_data.spectrograph) axes = {"wavelength": ("wavelength", spg), "grating": ("grating", spg), diff --git a/plugins/la_spec.py b/plugins/la_spec.py index 1a9e1d5fe8..2a033b69f9 100644 --- a/plugins/la_spec.py +++ b/plugins/la_spec.py @@ -276,7 +276,7 @@ def addSpectrum(self, name, detector): main_data = self.main_app.main_data stctrl = self._tab.streambar_controller - spg = stctrl._getAffectingSpectrograph(detector) + spg = stctrl._getAffectingSpectrograph(detector, default=main_data.spectrograph) axes = {"wavelength": ("wavelength", spg), "grating": ("grating", spg), diff --git a/plugins/photo_det_live.py b/plugins/photo_det_live.py new file mode 100644 index 0000000000..8393358dd1 --- /dev/null +++ b/plugins/photo_det_live.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +""" +Created on 5 Dev 2025 + +@author: Éric Piel + +Gives ability to acquire a spectrum data, while keeping the raw CCD image (ie, without vertical binning) + +Copyright © 2025 Éric Piel, Delmic + +This file is part of Odemis. + +Odemis is free software: you can redistribute it and/or modify it under the terms of the GNU +General Public License version 2 as published by the Free Software Foundation. + +Odemis is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along with Odemis. If not, +see http://www.gnu.org/licenses/. +""" +import functools +import logging + +from odemis import model +from odemis.acq.stream import MonochromatorSettingsStream +from odemis.gui.conf.data import get_local_vas +from odemis.gui.main import OdemisGUIApp +from odemis.gui.plugin import Plugin + + +class PhotoDetectorLivePlugin(Plugin): + name = "Photo-detector live display" + __version__ = "1.0" + __author__ = "Éric Piel" + __license__ = "GPLv2" + + def __init__(self, microscope: model.Microscope, main_app: OdemisGUIApp): + super().__init__(microscope, main_app) + main_data = self.main_app.main_data + if not (microscope and main_data.photo_ds and main_data.role.startswith("sparc")): + logging.info("%s plugin cannot load as the microscope is not a SPARC with a photo detector.", + self.name) + return + + self._tab = self.main_app.main_data.getTabByName("sparc_acqui") + stctrl = self._tab.streambar_controller + for det in main_data.photo_ds: + name = f"{det.name} alignment" + act = functools.partial(self.add_photo_det_stream, name=name, detector=det) + stctrl.add_action(name, act) + + # Note: no need to explicitly add the "Temporal Intensity" viewport, because it's normally + # always created when a time-correlator is available, which is the assumption here. + + def add_photo_det_stream(self, name: str, detector: model.Detector): + """ Create a Monochromator stream, using a photo-detector and add to to all compatible viewports""" + main_data = self.main_app.main_data + stctrl = self._tab.streambar_controller + + # Axes on the "LabCube", which are always affecting the time-correlator photo-detectors + axes = {"density": ("density", main_data.tc_od_filter), + "filter": ("band", main_data.tc_filter)} + + spg = stctrl._getAffectingSpectrograph(detector, default=main_data.spectrograph) + axes.update({ + "wavelength": ("wavelength", spg), + "grating": ("grating", spg), + "iris-in": ("iris-in", spg), + "slit-in": ("slit-in", spg), + "slit-monochromator": ("slit-monochromator", spg), + }) + + # Also add light filter if it affects the detector + filter_in = main_data.light_filter + if filter_in and detector.name in filter_in.affects.value: + axes["filter-in"] = ("band", filter_in) + + axes = stctrl._filter_axes(axes) + + photodet_stream = MonochromatorSettingsStream( + name, + detector, + detector.data, + main_data.ebeam, + sstage=main_data.scan_stage, + opm=main_data.opm, + axis_map=axes, + emtvas={"dwellTime"}, + detvas=get_local_vas(detector, main_data.hw_settings_config), + ) + stctrl._set_default_spectrum_axes(photodet_stream) + + # Don't call _addRepStream(), because we only add a live stream, no acquisition stream + + stream_cont = stctrl._add_stream(photodet_stream, add_to_view=True) + stream_cont.stream_panel.show_visible_btn(False) + return stream_cont diff --git a/plugins/spectrum_arbscor.py b/plugins/spectrum_arbscor.py index 90c6202262..cbd9971567 100644 --- a/plugins/spectrum_arbscor.py +++ b/plugins/spectrum_arbscor.py @@ -245,7 +245,7 @@ def add_stream(self, name: str, detector: "Detector"): logging.debug("Adding spectrum arbitrary order stream for %s", detector.name) - spectrograph = stctrl._getAffectingSpectrograph(detector) + spectrograph = stctrl._getAffectingSpectrograph(detector, default=main_data.spectrograph) axes = {"wavelength": ("wavelength", spectrograph), "grating": ("grating", spectrograph), diff --git a/plugins/spectrum_raw.py b/plugins/spectrum_raw.py index b998550b54..78a9e81123 100644 --- a/plugins/spectrum_raw.py +++ b/plugins/spectrum_raw.py @@ -384,7 +384,7 @@ def addst(self): # Removes exposureTime from local (GUI) VAs to use a new one, which allows to integrate images detvas.remove("exposureTime") - spectrograph = stctrl._getAffectingSpectrograph(main_data.ccd) + spectrograph = stctrl._getAffectingSpectrograph(main_data.ccd, default=main_data.spectrograph) spectrometer = stctrl._find_spectrometer(main_data.ccd) axes = {"wavelength": ("wavelength", spectrograph), diff --git a/src/odemis/acq/path.py b/src/odemis/acq/path.py index cfb211070d..6e4766225a 100644 --- a/src/odemis/acq/path.py +++ b/src/odemis/acq/path.py @@ -192,6 +192,8 @@ 'pol-analyzer': {'pol': MD_POL_NONE}, 'light-aligner': {'x': "MD:" + model.MD_FAV_POS_ACTIVE, 'z': "MD:" + model.MD_FAV_POS_ACTIVE}, + # Can affect in case the time-correlator is placed as output of the spectrograph + 'slit-in-big': {'x': 'off'}, # closed }), 'mirror-align': (r"ccd.*", # Also used for lens alignment {'lens-switch': {'x': ("MD:" + model.MD_FAV_POS_DEACTIVE, 'off')}, diff --git a/src/odemis/driver/tmcm.py b/src/odemis/driver/tmcm.py index 852775617a..e2e1821875 100644 --- a/src/odemis/driver/tmcm.py +++ b/src/odemis/driver/tmcm.py @@ -38,6 +38,7 @@ import threading from collections import OrderedDict from concurrent.futures import CancelledError +from typing import Dict try: import canopen @@ -443,6 +444,7 @@ def __init__(self, name, role, port, axes, ustepsize, address=None, # Add digital output axes self._do_axes = do_axes or {} self._led_prot_do = led_prot_do or {} + self._expected_do_pos: Dict[str, float] = {} # DO positions, as requested by the user for channel, (an, hpos, lpos, dur) in self._do_axes.items(): if an in self._name_to_axis or an in self._name_to_do_axis: raise ValueError("Axis %s specified multiple times" % an) @@ -450,6 +452,7 @@ def __init__(self, name, role, port, axes, ustepsize, address=None, raise ValueError("Axis %s duration %s should be in seconds" % (an, dur)) axes_def[an] = model.Axis(choices={lpos, hpos}) self._name_to_do_axis[an] = channel + self._expected_do_pos[an] = lpos # Always set to low at init via call to _releaseRefSwitch() just after for channel, pos in self._led_prot_do.items(): if channel not in self._do_axes: @@ -457,6 +460,12 @@ def __init__(self, name, role, port, axes, ustepsize, address=None, if pos not in self._do_axes[channel][1:3]: raise ValueError("led_prot_do of channel %d has position %s, not in do_axes" % (channel, pos)) + if self._led_prot_do: + # Add a VA to allow forcing the LED protection on + # If it's False: normal operation, the protection is activated during referencing + # If it's True: the protection is always active + self.protection = model.BooleanVA(False, setter=self._set_protection) + model.Actuator.__init__(self, name, role, axes=axes_def, **kwargs) driver_name = driver.getSerialDriver(self._portpattern) @@ -479,7 +488,7 @@ def __init__(self, name, role, port, axes, ustepsize, address=None, logging.warning("Acceleration of axis %s is null, most probably due to a bad hardware configuration", n) # Check state of refswitch on startup - self._expected_do_pos = {} # do positions before referencing, will be reset after refswitch is released + # Use _refswitch_lock to access this attribute self._leds_on = any(self.GetIO(2, rs) for rs in self._refswitch.values()) if self._leds_on: logging.debug("Refswitch is on during initialization, releasing refswitch for all axes.") @@ -1450,30 +1459,65 @@ def _cancelReferencing2xFF(self, axis): gparam = 128 + axis self.SetGlobalParam(2, gparam, 3) # 3 => indicate cancelled + def _switch_led_prot(self, protected: bool) -> None: + """ + Blocks until the move duration is passed, and will update .position based on the actual + state of the DO axes. + Must be called with _refswitch_lock held. + :param protected: If True, force the shutters closed. If False, set them + to the expected position (if _leds_on is False). + """ + tsleep = 0 # max transition period for all shutters + if protected: + logging.debug("Forcing the protection active") + for channel, val in self._led_prot_do.items(): + do_an, hpos, lpos, dur = self._do_axes[channel] + # If the shutter is already in the right position, no need to wait for it to move + if self.position.value[do_an] == val: + logging.debug("Shutter on axis %s already in protected position", do_an) + else: + tsleep = max(tsleep, dur) + # Set the DO to the "protected" position even if the position reports it's already + # there to be really sure. It's very fast anyway. + self.SetIO(2, channel, val == hpos) + else: + # Set the shutter in the "right" position: + # if _leds_on is False, set to the expected position + # if _leds_on is True, leave them closed (ie, do nothing) + if not self._leds_on: + # Set digital axis outputs to latest requested value + for an, val in self._expected_do_pos.items(): + channel = self._name_to_do_axis[an] + if channel not in self._led_prot_do: + continue + _, hpos, lpos, dur = self._do_axes[channel] + if self.position.value[an] == val: + logging.debug("Shutter on axis %s already in protected position", an) + else: + tsleep = max(tsleep, dur) + self.SetIO(2, channel, val == hpos) + + time.sleep(tsleep) + self._updatePosition(axes={}) + + def _set_protection(self, protected: bool) -> bool: + if self.protection.value == protected: + return protected # no change + + with self._refswitch_lock: + self._switch_led_prot(protected) + + return protected + def _requestRefSwitch(self, axis): refswitch = self._refswitch.get(axis) if refswitch is None: return with self._refswitch_lock: - # Set _leds_on attribute before closing shutters to make sure they are not - # opened again in a concurrent thread - leds_were_on = self._leds_on - self._leds_on = True # do this before closing shutters - # Close shutters - tsleep = 0 # max transition period for all shutters - for channel, val in self._led_prot_do.items(): - do_an, hpos, lpos, dur = self._do_axes[channel] - if not leds_were_on: - self._expected_do_pos[do_an] = self.position.value[do_an] - # TODO: ideally, for each DO, we should know when was the last time it - # was set, and if it's been set to the requested value for long - # enough, we don't need to do the extra sleep - self.SetIO(2, channel, val == hpos) - tsleep = max(tsleep, dur) - - time.sleep(tsleep) - self._updatePosition() + self._leds_on = True + # Activate protection (ie, force the shutters closed) + self._switch_led_prot(protected=True) self._active_refswitchs.add(axis) logging.debug("Activating ref switch power line %d (for axis %d)", refswitch, axis) @@ -1510,15 +1554,8 @@ def _releaseRefSwitch(self, axis): logging.debug("Leaving ref switch power line %d active", refswitch) # Set digital axis outputs to latest requested value - if not self._leds_on: - tsleep = 0 # max transition period for all shutters - for an, val in self._expected_do_pos.items(): - channel = self._name_to_do_axis[an] - _, hpos, lpos, dur = self._do_axes[channel] - self.SetIO(2, channel, val == hpos) - tsleep = max(tsleep, dur) - time.sleep(tsleep) - self._updatePosition() + if not hasattr(self, "protection") or not self.protection.value: + self._switch_led_prot(protected=False) def _startReferencingStd(self, axis): """ @@ -1951,15 +1988,16 @@ def _doMoveAbs(self, future, pos): # Check if it's a digital output if an in self._name_to_do_axis: channel = self._name_to_do_axis[an] - _, hpos, lpos, dur = self._do_axes[channel] - with self._refswitch_lock: # don't start do move at the same time as referencing - if self._leds_on and channel in self._led_prot_do: - # don't move protected do axis now if leds are on, schedule for later - self._expected_do_pos[an] = v + with self._refswitch_lock: + self._expected_do_pos[an] = v # Update user-requested position + if channel in self._led_prot_do and (self.protection.value or self._leds_on): + # Don't move a protecting DO axes if leds are on, they will be moved + # once the protection is turned off. if v != self._led_prot_do[channel]: logging.info("Referencing LEDs are on, move on axis %s to %s will be delayed.", an, v) else: # otherwise allow change + _, hpos, lpos, dur = self._do_axes[channel] logging.info("Setting digital output on channel %s to %s." % (channel, v == hpos)) self.SetIO(2, channel, v == hpos) moving_do_axes.add(channel) diff --git a/src/odemis/gui/conf/data.py b/src/odemis/gui/conf/data.py index 41a911f183..ecbaefa7c9 100644 --- a/src/odemis/gui/conf/data.py +++ b/src/odemis/gui/conf/data.py @@ -1161,10 +1161,32 @@ )), stream.ScannedTemporalSettingsStream: OrderedDict(( + # From spectrograph, if the time-correlator is coupled after the spectrograph + ("wavelength", { + "tooltip": "Center wavelength of the spectrograph", + "control_type": odemis.gui.CONTROL_FLT, + "range": (0.0, 1900e-9), + "key_step_min": 1e-9, + }), + ("grating", {}), + ("slit-in", { + "label": "Input slit", + "tooltip": "Opening size of the spectrograph input slit.\nA wide opening is usually fine.", + }), + ("filter-in", { # filter.band axis + "label": "Input filter", + "tooltip": "Filter before the spectrograph", + "choices": util.format_band_choices, + }), + ("slit-monochromator", { + "label": "Output slit", + "tooltip": "Opening size of the spectrograph detector slit.\nThe wider, the larger the wavelength bandwidth.", + }), ("density", { # from tc-od-filter "tooltip": "Optical density", }), ("filter", { # from tc-filter + "label": "LAB Cube filter", "choices": util.format_band_choices, }), )), diff --git a/src/odemis/gui/cont/stream_bar.py b/src/odemis/gui/cont/stream_bar.py index ee2a66b37a..c440cecc4e 100644 --- a/src/odemis/gui/cont/stream_bar.py +++ b/src/odemis/gui/cont/stream_bar.py @@ -1531,22 +1531,26 @@ def _on_streams(self, streams): self._tab_data_model.acquisitionStreams.discard(acqs) break - def _getAffectingSpectrograph(self, comp): + def _getAffectingSpectrograph(self, comp: model.HwComponent, + default: Optional[model.Actuator] = None, + ) -> Optional[model.Actuator]: """ Find which spectrograph matters for the given component (ex, spectrometer) - comp (Component): the hardware which is affected by a spectrograph - return (None or Component): the spectrograph affecting the component + :param comp: the hardware which is affected by a spectrograph + :param default: component to return if none found + :return: the spectrograph affecting the component (or default, if none is found affecting the component) """ cname = comp.name main_data = self._main_data_model for spg in (main_data.spectrograph, main_data.spectrograph_ded): if spg is not None and cname in spg.affects.value: return spg - else: - logging.warning("No spectrograph found affecting component %s", cname) - # spg should be None, but in case it's an error in the microscope file - # and actually, there is a spectrograph, then use that one - return main_data.spectrograph + + # No spectrograph found + if default is not None: + logging.warning("No spectrograph found affecting component %s, assuming it's %s", + cname, default.name) + return default def _find_spectrometer(self, detector): """ @@ -1671,7 +1675,7 @@ def addAR(self): main_data = self._main_data_model - detvas = get_local_vas(main_data.ccd, self._main_data_model.hw_settings_config) + detvas = get_local_vas(main_data.ccd, main_data.hw_settings_config) if main_data.ccd.exposureTime.range[1] < 3600: # 1h # remove exposureTime from local (GUI) VAs to use a new one, which allows to integrate images @@ -1686,7 +1690,7 @@ def addAR(self): main_data.ebeam, analyzer=main_data.pol_analyzer, sstage=main_data.scan_stage, - opm=self._main_data_model.opm, + opm=main_data.opm, axis_map=axes, # TODO: add a focuser for the SPARCv2? detvas=detvas, @@ -1715,9 +1719,9 @@ def addEBIC(self, **kwargs): main_data.ebic.data, main_data.ebeam, main_data.sed.data, - focuser=self._main_data_model.ebeam_focus, + focuser=main_data.ebeam_focus, emtvas={"dwellTime"}, - detvas=get_local_vas(main_data.ebic, self._main_data_model.hw_settings_config), + detvas=get_local_vas(main_data.ebic, main_data.hw_settings_config), ) else: ebic_stream = acqstream.EBICSettingsStream( @@ -1726,9 +1730,9 @@ def addEBIC(self, **kwargs): main_data.ebic.data, main_data.ebeam, sstage=main_data.scan_stage, - focuser=self._main_data_model.ebeam_focus, + focuser=main_data.ebeam_focus, emtvas={"dwellTime"}, - detvas=get_local_vas(main_data.ebic, self._main_data_model.hw_settings_config), + detvas=get_local_vas(main_data.ebic, main_data.hw_settings_config), ) # Create the equivalent MDStream @@ -1763,11 +1767,11 @@ def addCLIntensity(self): main_data.cld.data, main_data.ebeam, sstage=main_data.scan_stage, - focuser=self._main_data_model.ebeam_focus, - opm=self._main_data_model.opm, + focuser=main_data.ebeam_focus, + opm=main_data.opm, axis_map=axes, emtvas={"dwellTime"}, - detvas=get_local_vas(main_data.cld, self._main_data_model.hw_settings_config), + detvas=get_local_vas(main_data.cld, main_data.hw_settings_config), ) # Special "safety" feature to avoid having a too high gain at start @@ -1806,7 +1810,7 @@ def addSpectrum(self, name=None, detector=None): detector = main_data.spectrometer logging.debug("Adding spectrum stream for %s", detector.name) - spg = self._getAffectingSpectrograph(detector) + spg = self._getAffectingSpectrograph(detector, default=main_data.spectrograph) axes = {"wavelength": ("wavelength", spg), "grating": ("grating", spg), @@ -1831,10 +1835,10 @@ def addSpectrum(self, name=None, detector=None): main_data.ebeam, main_data.light, sstage=main_data.scan_stage, - opm=self._main_data_model.opm, + opm=main_data.opm, axis_map=axes, - # emtvas=get_local_vas(main_data.ebeam, self._main_data_model.hw_settings_config), # no need - detvas=get_local_vas(detector, self._main_data_model.hw_settings_config), + # emtvas=get_local_vas(main_data.ebeam, main_data.hw_settings_config), # no need + detvas=get_local_vas(detector, main_data.hw_settings_config), ) self._set_default_spectrum_axes(spec_stream) @@ -1852,7 +1856,7 @@ def addAngularSpectrum(self): """ main_data = self._main_data_model - detvas = get_local_vas(main_data.ccd, self._main_data_model.hw_settings_config) + detvas = get_local_vas(main_data.ccd, main_data.hw_settings_config) # For ek acquisition we use a horizontal and a vertical binning # which are instantiated in the AngularSpectrumSettingsStream. # Removes binning from local (GUI) VAs to use a vertical and horizontal binning @@ -1862,7 +1866,7 @@ def addAngularSpectrum(self): # Removes exposureTime from local (GUI) VAs to use a new one, which allows to integrate images detvas.remove("exposureTime") - spectrograph = self._getAffectingSpectrograph(main_data.ccd) + spectrograph = self._getAffectingSpectrograph(main_data.ccd, default=main_data.spectrograph) spectrometer = self._find_spectrometer(main_data.ccd) axes = {"wavelength": ("wavelength", spectrograph), @@ -1882,7 +1886,7 @@ def addAngularSpectrum(self): spectrograph, analyzer=main_data.pol_analyzer, sstage=main_data.scan_stage, - opm=self._main_data_model.opm, + opm=main_data.opm, axis_map=axes, detvas=detvas, ) @@ -1901,13 +1905,13 @@ def addTemporalSpectrum(self): main_data = self._main_data_model - detvas = get_local_vas(main_data.streak_ccd, self._main_data_model.hw_settings_config) + detvas = get_local_vas(main_data.streak_ccd, main_data.hw_settings_config) if main_data.streak_ccd.exposureTime.range[1] < 86400: # 24h # remove exposureTime from local (GUI) VAs to use a new one, which allows to integrate images detvas.remove("exposureTime") - spg = self._getAffectingSpectrograph(main_data.streak_ccd) + spg = self._getAffectingSpectrograph(main_data.streak_ccd, default=main_data.spectrograph) axes = {"wavelength": ("wavelength", spg), "grating": ("grating", spg), @@ -1932,10 +1936,10 @@ def addTemporalSpectrum(self): main_data.streak_unit, main_data.streak_delay, sstage=main_data.scan_stage, - opm=self._main_data_model.opm, + opm=main_data.opm, axis_map=axes, detvas=detvas, - streak_unit_vas=get_local_vas(main_data.streak_unit, self._main_data_model.hw_settings_config)) + streak_unit_vas=get_local_vas(main_data.streak_unit, main_data.hw_settings_config)) self._set_default_spectrum_axes(ts_stream) # For safety, always start with the shutter closed. if model.hasVA(ts_stream, "detShutter"): @@ -1951,7 +1955,7 @@ def addMonochromator(self): """ Create a Monochromator stream and add to to all compatible viewports """ main_data = self._main_data_model - spg = self._getAffectingSpectrograph(main_data.spectrometer) + spg = self._getAffectingSpectrograph(main_data.monochromator, default=main_data.spectrograph) axes = {"wavelength": ("wavelength", spg), "grating": ("grating", spg), @@ -1960,8 +1964,6 @@ def addMonochromator(self): "slit-monochromator": ("slit-monochromator", spg), } - axes = self._filter_axes(axes) - # Also add light filter if it affects the detector for fw in (main_data.cl_filter, main_data.light_filter): if fw is None: @@ -1970,16 +1972,18 @@ def addMonochromator(self): axes["filter"] = ("band", fw) break + axes = self._filter_axes(axes) + monoch_stream = acqstream.MonochromatorSettingsStream( "Monochromator", main_data.monochromator, main_data.monochromator.data, main_data.ebeam, sstage=main_data.scan_stage, - opm=self._main_data_model.opm, + opm=main_data.opm, axis_map=axes, emtvas={"dwellTime"}, - detvas=get_local_vas(main_data.monochromator, self._main_data_model.hw_settings_config), + detvas=get_local_vas(main_data.monochromator, main_data.hw_settings_config), ) self._set_default_spectrum_axes(monoch_stream) @@ -1997,9 +2001,25 @@ def addTimeCorrelator(self): main_data = self._main_data_model + # Axes on the "LabCube", which are always affecting the time-correlator axes = {"density": ("density", main_data.tc_od_filter), "filter": ("band", main_data.tc_filter)} + spg = self._getAffectingSpectrograph(main_data.time_correlator) + if spg: + axes.update({ + "wavelength": ("wavelength", spg), + "grating": ("grating", spg), + "iris-in": ("iris-in", spg), + "slit-in": ("slit-in", spg), + "slit-monochromator": ("slit-monochromator", spg), + }) + + # Also add the spectrograph filter if it affects the detector + filter_in = main_data.light_filter + if filter_in and main_data.time_correlator.name in filter_in.affects.value: + axes["filter-in"] = ("band", filter_in) + axes = self._filter_axes(axes) tc_stream = acqstream.ScannedTemporalSettingsStream( @@ -2007,9 +2027,9 @@ def addTimeCorrelator(self): main_data.time_correlator, main_data.time_correlator.data, main_data.ebeam, - opm=self._main_data_model.opm, + opm=main_data.opm, axis_map=axes, - detvas=get_local_vas(main_data.time_correlator, self._main_data_model.hw_settings_config) + detvas=get_local_vas(main_data.time_correlator, main_data.hw_settings_config) ) # Create the equivalent MDStream diff --git a/src/odemis/odemisd/mdupdater.py b/src/odemis/odemisd/mdupdater.py index 81a47429f1..c2f6e023d3 100644 --- a/src/odemis/odemisd/mdupdater.py +++ b/src/odemis/odemisd/mdupdater.py @@ -392,14 +392,14 @@ def updateOutWavelength(self, comp_affected: model.HwComponent, Its metadata will be updated. :param filter: a (light) filter-(wheel) component (should have a "band" axis) :param spectrograph: a spectrograph component (should have a "wavelength" axis) - Only used if the detector has the role "monochromator". + Only used if the detector has the role "monochromator", "time-correlator" or "photo-detector". """ filter_pos = self.get_filter_pos(filter) # We only need to care about the spectrograph in the case of the monochromator, because for # the other types of components (eg, spectrometer), MD_OUT_WL is used exclusively for the # filter info, and the MD_WL_LIST is used to store the wavelength info (handled separately). - if comp_affected.role == "monochromator": + if any(comp_affected.role.startswith(r) for r in ("monochromator", "time-correlator", "photo-detector")): spec_bandwidth = self.getMonochromatorBandwidth(spectrograph) # None if wavelength == 0 else: spec_bandwidth = None @@ -424,11 +424,12 @@ def updateOutWavelength(self, comp_affected: model.HwComponent, logging.debug("Updating %s with intersection of filter %s and spectrograph %s -> %s", comp_affected.name, filter_bandwidth, spec_bandwidth, bandwidth) + logging.debug("Updating output wavelength for component %s to %s", comp_affected.name, bandwidth) comp_affected.updateMetadata({model.MD_OUT_WL: bandwidth}) def observeSpectrograph(self, spectrograph, comp_affected): - if comp_affected.role == "monochromator": + if any(comp_affected.role.startswith(r) for r in ("monochromator", "time-correlator", "photo-detector")): def updateOutWLRange(pos, sp=spectrograph, comp_affected=comp_affected): self.updateOutWavelength(comp_affected, self._det_to_filter.get(comp_affected.name),