Skip to content

Commit

Permalink
modified wavelength change and setup laser
Browse files Browse the repository at this point in the history
  • Loading branch information
parfa30 committed Oct 30, 2024
1 parent 30f7d56 commit 0433cf2
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 52 deletions.
1 change: 1 addition & 0 deletions doc/news/DM-46276.feature.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``mtcalsys.yaml``, Added laser configuration information to all tests, including laser mode and optical configuration.
8 changes: 8 additions & 0 deletions doc/news/DM-46276.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Add features to allow ``MTCalSys`` to better handle the laser
- In ``mtcalsys.py`` made the following changes:
- Added ``laser_start_propagate`` and ``laser_stop_propagate()``
- Added ``get_laser_parameters()``
- Improved ``setup_laser()`` to change the wavelength and the optical configuration
- Changed ``change_laser_wavelength()`` so it can be used for the laser or whitelight system
- In ``mtcalsys.yaml`` added a laser functional setup
- In ``mtcalsys_schema.yaml`` added laser mode and optical configuration
14 changes: 14 additions & 0 deletions python/lsst/ts/observatory/control/data/mtcalsys.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ whitelight_y:

scan_r:
calib_type: Mono
laser_mode: 1
optical_configuration: F1_SCU
use_camera: true
mtcamera_filter: r
led_location: 174.91
Expand All @@ -130,3 +132,15 @@ scan_r:
electrometer_range: -1
exposure_times:
- 15.0

laser_functional:
calib_type: Mono
laser_mode: 1
use_camera: false
optical_configuration: SCU
wavelength: 500.0
use_electrometer: false
use_fiberspectrograph_red: false
use_fiberspectrograph_blue: false
exposure_times:
- 15.0
19 changes: 12 additions & 7 deletions python/lsst/ts/observatory/control/data/mtcalsys_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,23 @@ properties:
type: array
items:
type: number
laser_mode:
type: integer
optical_configuration:
type: string
enum:
- SCU
- NO_SCU
- F1_SCU
- F2_SCU
- F2_NO_SCU
- F1_NO_SCU
required:
- calib_type
- use_camera
- mtcamera_filter
- wavelength
- led_location
- led_focus
- use_camera
- use_electrometer
- use_fiberspectrograph_red
- use_fiberspectrograph_blue
- electrometer_integration_time
- electrometer_mode
- electrometer_range
- exposure_times
additionalProperties: false
220 changes: 175 additions & 45 deletions python/lsst/ts/observatory/control/maintel/mtcalsys.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

import numpy as np
from lsst.ts import salobj, utils

# TODO: (DM-46168) Revert workaround for TunableLaser XML changes
from lsst.ts.observatory.control.utils.enums import LaserOpticalConfiguration
from lsst.ts.xml.enums.TunableLaser import LaserDetailedState

from ..base_calsys import BaseCalsys
Expand Down Expand Up @@ -157,52 +160,16 @@ def __init__(
self.led_rest_position = 120.0 # mm

self.laser_enclosure_temp = 20.0 # C
self.laser_warmup = 20.0 # sec

self.exptime_dict: dict[str, float] = dict(
camera=0.0,
electrometer=0.0,
fiberspectrograph=0.0,
)

def calculate_laser_focus_location(self, wavelength: float) -> float:
"""Calculates the location of the linear stage that provides the focus
for the laser projector. This location is dependent on the
wavelength of the laser.
Parameters
----------
wavelength : `float`
wavelength of the laser projector in nm
Returns
------
location of the linear stage for the laser projector focus in mm
"""
# TODO (DM-44772): implement the actual function
return 10.0

async def change_laser_wavelength(self, wavelength: float) -> None:
"""Change the TunableLaser wavelength setting
Parameters
----------
wavelength : `float`
wavelength of the laser in nm
"""

task_wavelength = self.rem.tunablelaser.cmd_changeWavelength.set_start(
wavelength=wavelength, timeout=self.long_long_timeout
)
task_focus = self.linearstage_laser_focus.cmd_moveAbsolute.set_start(
distance=self.calculate_laser_focus_location(wavelength),
timeout=self.long_long_timeout,
)

await asyncio.gather(task_wavelength, task_focus)

async def is_ready_for_flats(self) -> bool:
"""Designates if the calibraiton hardware is in a state
"""Designates if the calibration hardware is in a state
to take flats.
"""
# TODO (DM-44310): Implement method to check that the
Expand Down Expand Up @@ -240,12 +207,94 @@ async def setup_calsys(self, sequence_name: str) -> None:
await self.linearstage_projector_select.cmd_moveAbsolute.set_start(
distance=self.ls_select_laser_location, timeout=self.long_timeout
)
await self.setup_laser(config_data["laser_mode"])
await self.rem.tunablelaser.cmd_startPropagating.start(
timeout=self.long_long_timeout
await self.setup_laser(
config_data["laser_mode"],
config_data["wavelength"],
config_data["optical_configuration"],
)
await self.laser_start_propagate()

def calculate_laser_focus_location(self, wavelength: float = 500.0) -> float:
"""Calculates the location of the linear stage that provides the focus
for the laser projector. This location is dependent on the
wavelength of the laser.
Parameters
----------
wavelength : `float`
wavelength of the laser projector in nm
Default 500.0
Returns
------
location of the linear stage for the laser projector focus in mm
async def setup_laser(self, mode: LaserDetailedState) -> None:
"""
# TODO (DM-44772): implement the actual function
return 10.0

async def change_laser_wavelength(
self,
wavelength: float,
use_projector: bool = True,
) -> None:
"""Change the TunableLaser wavelength setting
Parameters
----------
wavelength : `float`
wavelength of the laser in nm
use_projector : `bool`
identifies if you are using the projector while
changing the wavelength.
Default True
"""
task_wavelength = self.rem.tunablelaser.cmd_changeWavelength.set_start(
wavelength=wavelength, timeout=self.long_long_timeout
)

if use_projector:
task_focus = self.linearstage_laser_focus.cmd_moveAbsolute.set_start(
distance=self.calculate_laser_focus_location(wavelength),
timeout=self.long_long_timeout,
)
await asyncio.gather(task_wavelength, task_focus)

else:
await task_wavelength

async def change_laser_optical_configuration(
self, optical_configuration: LaserOpticalConfiguration
) -> None:
"""Change the output of the laser.
Parameters
----------
optical_configuration : LaserOpticalConfiguration
"""
assert optical_configuration in list(LaserOpticalConfiguration)

current_configuration = (
await self.rem.tunablelaser.evt_opticalConfiguration.aget()
)
if current_configuration.configuration != optical_configuration:
self.log.debug(
f"Changing optical configuration from {current_configuration} to {optical_configuration}"
)
await self.rem.tunablelaser.cmd_setOpticalConfiguration.set_start(
configuration=optical_configuration, timeout=self.long_timeout
)

else:
self.log.debug("Laser Optical Configuration already in place.")

async def setup_laser(
self,
mode: LaserDetailedState,
wavelength: float,
optical_configuration: LaserOpticalConfiguration = LaserOpticalConfiguration.SCU,
use_projector: bool = True,
) -> None:
"""Perform all steps for preparing the laser for monochromatic flats.
This includes confirming that the thermal system is
turned on and set at the right temperature. It also checks
Expand All @@ -255,8 +304,16 @@ async def setup_laser(self, mode: LaserDetailedState) -> None:
----------
mode : LaserDetailedState
Mode of the TunableLaser
Options: CONTINUOUS, BURST, TRIGGER
Options: CONTINUOUS, BURST
wavelength : `float`
Wavelength fo the laser in nm
optical_configuration : LaserOpticalConfiguration
Output of laser
Default LaserOpticalConfiguration.SCU
use_projector : `bool`
identifies if you are using the projector while
changing the wavelength
Default True
"""
# TO-DO: DM-45693 implement thermal system checks

Expand All @@ -279,6 +336,77 @@ async def setup_laser(self, mode: LaserDetailedState) -> None:
f"{mode} not an acceptable LaserDetailedState [CONTINOUS, BURST, TRIGGER]"
)

await self.change_laser_optical_configuration(optical_configuration)
await self.change_laser_wavelength(wavelength, use_projector)

async def get_laser_parameters(self) -> tuple:
"""Get laser configuration
Returns
-------
list : configuration details
"""

return await asyncio.gather(
self.rem.tunablelaser.evt_opticalConfiguration.aget(
timeout=self.long_timeout
),
self.rem.tunablelaser.evt_wavelengthChanged.aget(timeout=self.long_timeout),
self.rem.tunablelaser.evt_interlockState.aget(timeout=self.long_timeout),
self.rem.tunablelaser.evt_burstModeSet.aget(timeout=self.long_timeout),
self.rem.tunablelaser.evt_continuousModeSet.aget(timeout=self.long_timeout),
)

async def laser_start_propagate(self) -> None:
"""Start the propagation of the Tunable Laser"""

laser_state = await self.rem.tunablelaser.evt_detailedState.next(
flush=True, timeout=self.long_timeout
)
self.log.debug(f"HERE: {laser_state.DetailedState}")

if laser_state.DetailedState not in {
LaserDetailedState.PROPAGATING_CONTINUOUS_MODE,
LaserDetailedState.PROPAGATING_BURST_MODE,
}:
try:
await self.rem.tunablelaser.cmd_startPropagateLaser.start(
timeout=self.laser_warmup
)
laser_state = await self.rem.tunablelaser.evt_detailedState.next(
flush=True, timeout=self.long_timeout
)
self.log.info(f"Laser state: {laser_state.DetailedState}")
except asyncio.TimeoutError:
raise RuntimeError(
"Tunable Laser did not start propagating when commanded"
)

async def laser_stop_propagate(self) -> None:
"""Stop the propagation of the Tunable Laser"""

laser_state = await self.rem.tunablelaser.evt_detailedState.next(
flush=True, timeout=self.long_timeout
)

if laser_state.DetailedState not in {
LaserDetailedState.NONPROPAGATING_CONTINUOUS_MODE,
LaserDetailedState.NONPROPAGATING_BURST_MODE,
}:
try:
await self.rem.tunablelaser.cmd_stopPropagateLaser.start(
timeout=self.laser_warmup
)
laser_state = await self.rem.tunablelaser.evt_detailedState.next(
flush=True, timeout=self.long_timeout
)
self.log.info(f"Laser state: {laser_state.DetailedState}")
except asyncio.TimeoutError:
raise RuntimeError(
"Tunable Laser did not stop propagating when commanded"
)

async def prepare_for_flat(self, sequence_name: str) -> None:
"""Configure the ATMonochromator according to the flat parameters
Expand Down Expand Up @@ -345,6 +473,7 @@ async def run_calibration_sequence(
"""Perform full calibration sequence, taking flats with the
camera and all ancillary instruments.
Parameters
----------
sequence_name : `str`
Expand Down Expand Up @@ -384,7 +513,7 @@ async def run_calibration_sequence(
self.log.debug(
f"Performing {calibration_type.name} calibration with {exposure.wavelength=}."
)
await self.change_laser_wavelength(wavelength=exposure.wavelength)

mtcamera_exposure_info: dict = dict()

for exptime in config_data["exposure_times"]:
Expand All @@ -400,6 +529,7 @@ async def run_calibration_sequence(
mtcamera_exposure_info.update(exposure_info)

if calibration_type == CalibrationType.Mono:
await self.change_laser_wavelength(wavelength=exposure.wavelength)
self.log.debug(
"Taking data sequence without filter for monochromatic set."
)
Expand Down
30 changes: 30 additions & 0 deletions tests/maintel/test_mtcalsys.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ async def setup_mocks(self) -> None:
"evt_largeFileObjectAvailable.next.side_effect": self.mock_fiberspectrograph_lfoa
}
)
self.mtcalsys.rem.tunablelaser.configure_mock()

async def setup_types(self) -> None:
pass
Expand Down Expand Up @@ -112,6 +113,35 @@ async def test_change_laser_wavelength(self) -> None:

await self.mtcalsys.change_laser_wavelength(wavelength=500.0)

async def test_setup_laser(self) -> None:
config_data = self.mtcalsys.get_calibration_configuration("laser_functional")

await self.mtcalsys.setup_laser(
mode=config_data["laser_mode"],
wavelength=config_data["wavelength"],
optical_configuration=config_data["optical_configuration"],
use_projector=False,
)

self.mtcalsys.rem.tunablelaser.cmd_setOpticalConfiguration.set_start.assert_awaited_with(
configuration=config_data["optical_configuration"],
timeout=self.mtcalsys.long_timeout,
)

async def test_laser_start_propagation(self) -> None:
await self.mtcalsys.laser_start_propagate()

self.mtcalsys.rem.tunablelaser.cmd_startPropagateLaser.start.assert_awaited_with(
timeout=self.mtcalsys.laser_warmup
)

async def test_laser_stop_propagation(self) -> None:
await self.mtcalsys.laser_stop_propagate()

self.mtcalsys.rem.tunablelaser.cmd_stopPropagateLaser.start.assert_awaited_with(
timeout=self.mtcalsys.laser_warmup
)

async def test_prepare_for_whitelight_flat(self) -> None:
mock_comcam = ComCam(
"FakeDomain", log=self.log, intended_usage=ComCamUsages.DryTest
Expand Down

0 comments on commit 0433cf2

Please sign in to comment.