Skip to content

Commit

Permalink
Merge pull request #245 from lsst-ts/tickets/DM-47381
Browse files Browse the repository at this point in the history
  • Loading branch information
edennihy authored Nov 20, 2024
2 parents a44918d + 818039e commit 5828121
Show file tree
Hide file tree
Showing 18 changed files with 171 additions and 29 deletions.
1 change: 1 addition & 0 deletions doc/news/DM-47381.bugfix.2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/take_aos_sequence_comcam.py``, wait for all images to be ingested before starting OCPS process.
1 change: 1 addition & 0 deletions doc/news/DM-47381.bugfix.3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/base_close_loop.py``, flush evt_degreeOfFreedom.
1 change: 1 addition & 0 deletions doc/news/DM-47381.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/take_aos_sequence_comcam.py``, fix call to ready_to_take_data.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``track_target_and_take_image_comcam.py``, add StateTransition usages to MTCS and ComCam usages.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``base_track_target_and_take_image.py``, add instrument name to metadata and propagate to instrument scripts.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``base_track_target.py``, add log to debug check feature.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.4.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``base_track-target_and_take_image.py``, add support for single filters or array of filters in metadata.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.5.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``track_target_and_take_iamge_comcam.py``, simplify the _handle_slew_and_change_filter method.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.6.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``base_close_loop.py``, add gain_sequence.
1 change: 1 addition & 0 deletions doc/news/DM-47381.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In ``maintel/take_aos_sequence_comcam.py``, use all topics from the camera.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def __init__(self, index, add_remotes: bool = True):
self.atcs = ATCS(self.domain, intended_usage=atcs_usage, log=self.log)
self.latiss = LATISS(self.domain, intended_usage=latiss_usage, log=self.log)

self.instrument_name = "LATISS"

@property
def tcs(self):
return self.atcs
Expand Down Expand Up @@ -105,6 +107,9 @@ def get_schema(cls):

return schema_dict

def get_instrument_name(self):
return self.instrument_name

async def configure(self, config):
"""Configure the script.
Expand Down
2 changes: 2 additions & 0 deletions python/lsst/ts/standardscripts/base_track_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ async def configure(self, config):
else:
self.log.debug(f"Ignoring component {comp}.")
setattr(self.tcs.check, comp, False)
else:
self.log.info(f"Not ignoring TCS components: {self.tcs.components_attr}.")

await super().configure(config=config)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,16 @@ def set_metadata(self, metadata):
metadata.position = [radec_icrs.ra.deg, radec_icrs.dec.deg]
metadata.rotationSystem = MetadataRotSys.SKY
metadata.cameraAngle = self.config.rot_sky
metadata.filters = ",".join(self.config.band_filter)
metadata.filters = (
self.config.band_filter
if isinstance(self.config.band_filter, str)
else ",".join(self.config.band_filter)
)
metadata.dome = MetadataDome.OPEN
metadata.nimages = self.config.num_exp
metadata.survey = self.config.program
metadata.totalCheckpoints = 3 if self.config.camera_playlist is None else 4
metadata.instrument = self.get_instrument_name()

def get_estimated_time_on_target(self):
"""Get the estimated time on target.
Expand Down Expand Up @@ -287,6 +292,16 @@ async def stop_tracking(self):
"""Implement method to stop tracking."""
raise RuntimeError()

@abc.abstractmethod
def get_instrument_name(self):
"""Get instrument name.
Returns
-------
instrument_name: `string`
"""
raise NotImplementedError()

async def cleanup(self):
if self.state.state != ScriptState.ENDING:
# abnormal termination
Expand Down
62 changes: 58 additions & 4 deletions python/lsst/ts/standardscripts/maintel/base_close_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def __init__(self, index=1, descr="") -> None:

# exposure time for the intra/extra images (in seconds)
self.exposure_time = None
self.n_images = 9

# Define operation mode handler function
self.operation_model_handlers = {
Expand Down Expand Up @@ -203,6 +204,15 @@ def get_schema(cls) -> typing.Dict[str, typing.Any]:
type: string
enum: {[dof_name.name for dof_name in DOFName]}
default: [1, 2, 3, 4, 5]
gain_sequence:
description: >-
Gain sequence to apply to the offsets.
oneOf:
- type: array
items:
type: number
- type: number
default: 0
apply_corrections:
description: >-
Apply OFC corrections after each iteration.
Expand Down Expand Up @@ -274,6 +284,8 @@ async def configure(self, config: types.SimpleNamespace) -> None:
# Set apply_corrections
self.apply_corrections = config.apply_corrections

self.gain_sequence = config.gain_sequence

for comp in getattr(config, "ignore", []):
if comp not in self.mtcs.components_attr:
self.log.warning(
Expand Down Expand Up @@ -363,13 +375,26 @@ async def take_intra_extra_focal_images(
note=self.note,
)

task1 = self.oods.evt_imageInOODS.next(flush=False, timeout=self.exposure_time)
task1 = self.wait_for_images_in_oods()
# Move the hexapod back to in focus position
task2 = self.mtcs.offset_camera_hexapod(x=0, y=0, z=-self.dz, u=0, v=0)
await asyncio.gather(task1, task2)

return intra_image, extra_image

async def wait_for_images_in_oods(self):

for _ in range(self.n_images):
try:
image_in_oods = await self.oods.evt_imageInOODS.next(
flush=False, timeout=self.exposure_time
)
self.log.info(
f"Image {image_in_oods.obsid} {image_in_oods.raft} {image_in_oods.sensor} ingested."
)
except asyncio.TimeoutError:
self.log.warning("Timeout waiting for images in OODS.")

async def handle_fam_mode(self) -> None:
"""Handle Full Array Mode."""

Expand Down Expand Up @@ -422,13 +447,15 @@ async def handle_cwfs_mode(self) -> None:
visitId=visit_id, timeout=2 * CMD_TIMEOUT, config=self.wep_config
)

async def compute_ofc_offsets(self, rotation_angle: float) -> None:
async def compute_ofc_offsets(self, rotation_angle: float, gain: float) -> None:
"""Compute offsets using ts_ofc.
Parameters
----------
rotation_angle : `float`
Rotation angle of the camera in deg.
gain : `float`
Gain to apply to the offsets.
"""
# Create the config to run OFC
config = {
Expand All @@ -444,15 +471,40 @@ async def compute_ofc_offsets(self, rotation_angle: float) -> None:
config_yaml = yaml.safe_dump(config)

# Run OFC
self.mtcs.rem.mtaos.evt_degreeOfFreedom.flush()
await self.mtcs.rem.mtaos.cmd_runOFC.set_start(
config=config_yaml, timeout=CMD_TIMEOUT
config=config_yaml, timeout=CMD_TIMEOUT, userGain=gain
)

# Return offsets
return await self.mtcs.rem.mtaos.evt_degreeOfFreedom.next(
flush=False, timeout=STD_TIMEOUT
)

def get_gain(self, iteration: int) -> float:
"""Get the gain to apply to the offsets.
Parameters
----------
iteration : `int`
Iteration number.
Returns
-------
gain : `float`
Gain to apply to the offsets.
"""
if isinstance(self.gain_sequence, float) or isinstance(self.gain_sequence, int):
return float(self.gain_sequence)
else:
if iteration >= len(self.gain_sequence):
self.log.warning(
"Iteration is greater than the length of the gain sequence. "
"Using the last value of the gains sequence."
)
return self.gain_sequence[-1]
return self.gain_sequence[iteration]

async def arun(self, checkpoint: bool = False) -> None:
"""Perform wavefront error measurements and DOF adjustments until the
thresholds are reached.
Expand Down Expand Up @@ -509,7 +561,9 @@ async def arun(self, checkpoint: bool = False) -> None:
)

# Compute ts_ofc offsets
dof_offset = await self.compute_ofc_offsets(rotation_angle)
dof_offset = await self.compute_ofc_offsets(
rotation_angle, self.get_gain(i)
)

# If apply_corrections is true,
# then we apply the corrections
Expand Down
35 changes: 29 additions & 6 deletions python/lsst/ts/standardscripts/maintel/take_aos_sequence_comcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import yaml
from lsst.ts import salobj
from lsst.ts.observatory.control.maintel.comcam import ComCam, ComCamUsages
from lsst.ts.observatory.control.maintel.comcam import ComCam
from lsst.ts.observatory.control.maintel.mtcs import MTCS

from ..base_block_script import BaseBlockScript
Expand Down Expand Up @@ -66,6 +66,7 @@ def __init__(self, index, descr="Take AOS sequence with ComCam.") -> None:
self.camera = None
self.ocps = None
self.current_z_position = 0
self.n_images = 9

@classmethod
def get_schema(cls) -> dict:
Expand Down Expand Up @@ -217,8 +218,7 @@ async def configure_camera(self) -> None:
self.camera = ComCam(
self.domain,
log=self.log,
intended_usage=ComCamUsages.TakeImage + ComCamUsages.StateTransition,
tcs_ready_to_take_data=self.mtcs.tcs_ready_to_take_data,
tcs_ready_to_take_data=self.mtcs.ready_to_take_data,
)
await self.camera.start_task
else:
Expand Down Expand Up @@ -290,9 +290,32 @@ async def take_aos_sequence(self) -> None:

if self.mode == Mode.TRIPLET:
self.log.debug("Waiting for images to be ingested in OODS.")
await self.camera.rem.ccoods.evt_imageInOODS.next(
flush=False, timeout=self.exposure_time
)
extra_image_ingested = False
while not extra_image_ingested:
try:
image_in_oods = await self.camera.rem.ccoods.evt_imageInOODS.next(
flush=False, timeout=self.exposure_time
)
try:
image_name_split = image_in_oods.obsid.split("_")
image_index = int(
f"{image_name_split[-2]}{image_name_split[-1][1:]}"
)
extra_image_ingested = image_index == extra_visit_id[0]
except Exception:
self.log.exception(
"Failed to parse image name into index for {image_in_oods.obsid}."
)

self.log.info(
f"Image {image_in_oods.obsid} {image_in_oods.raft} {image_in_oods.sensor} ingested."
)

except asyncio.TimeoutError:
self.log.warning(
"Timeout waiting for images to ingest. Continuing."
)
break
self.log.info("Send processing request to RA OCPS.")
config = {
"LSSTComCam-FROM-OCS_DONUTPAIR": f"{intra_visit_id[0]},{extra_visit_id[0]}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ def __init__(self, index, add_remotes: bool = True):
)

mtcs_usage, comcam_usage = (
(MTCSUsages.Slew, ComCamUsages.TakeImageFull)
(
MTCSUsages.Slew | MTCSUsages.StateTransition,
ComCamUsages.TakeImageFull | ComCamUsages.StateTransition,
)
if add_remotes
else (MTCSUsages.DryTest, ComCamUsages.DryTest)
)
Expand All @@ -62,6 +65,8 @@ def __init__(self, index, add_remotes: bool = True):
self.mtcs = MTCS(self.domain, intended_usage=mtcs_usage, log=self.log)
self.comcam = ComCam(self.domain, intended_usage=comcam_usage, log=self.log)

self.instrument_name = "LSSTComCam"

@property
def tcs(self):
return self.mtcs
Expand All @@ -77,6 +82,9 @@ def get_schema(cls):

return schema_dict

def get_instrument_name(self):
return self.instrument_name

async def load_playlist(self):
"""Load playlist."""
await self.comcam.rem.cccamera.cmd_play.set_start(
Expand Down Expand Up @@ -128,22 +136,15 @@ async def _handle_slew_and_change_filter(self):
it there while the filter is changing.
"""

tasks_slew_with_fixed_rot = [
asyncio.create_task(
self.mtcs.slew_icrs(
ra=self.config.ra,
dec=self.config.dec,
rot=self.angle_filter_change,
rot_type=RotType.Physical,
target_name=f"{self.config.name} - filter change",
az_wrap_strategy=self.config.az_wrap_strategy,
time_on_target=self.get_estimated_time_on_target(),
)
),
asyncio.create_task(self._wait_rotator_reach_filter_change_angle()),
]

await self.mtcs.process_as_completed(tasks_slew_with_fixed_rot)
await self.mtcs.slew_icrs(
ra=self.config.ra,
dec=self.config.dec,
rot=self.angle_filter_change,
rot_type=RotType.Physical,
target_name=f"{self.config.name} - filter change",
az_wrap_strategy=self.config.az_wrap_strategy,
time_on_target=self.get_estimated_time_on_target(),
)

await self.comcam.setup_filter(filter=self.config.band_filter)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def __init__(self, index, add_remotes: bool = True):

self.gencam = None

self.instrument_name = "GenCam"

@property
def tcs(self):
return self.mtcs
Expand All @@ -126,6 +128,9 @@ async def assert_feasibility(self):
*(gencam.assert_liveliness() for gencam in self.gencam),
)

def get_instrument_name(self):
return self.instrument_name

async def configure(self, config):
await super().configure(config)

Expand Down
Loading

0 comments on commit 5828121

Please sign in to comment.