Skip to content

Commit

Permalink
First draft
Browse files Browse the repository at this point in the history
  • Loading branch information
isotuela committed Jun 7, 2023
1 parent 13282f4 commit 8164152
Show file tree
Hide file tree
Showing 4 changed files with 358 additions and 0 deletions.
21 changes: 21 additions & 0 deletions python/lsst/ts/standardscripts/auxtel/calibrations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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

from .power_on_atcalsys import *
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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

__all__ = ["PowerOnATCalSys"]

import asyncio
import collections

import numpy as np
import yaml

from lsst.ts.idl.enums.Script import ScriptState
from lsst.ts.idl.enums import ATMonochromator, ATWhiteLight

from lsst.ts import salobj


class PowerOnATCalSys(salobj.BaseScript):
"""Powers on the ATCalSys dome flat illuminator (ATWhiteLight and ATMonochromator)
required to perform imaging white light calibrations.
Parameters
----------
index : `int`
Index of Script SAL component.
Notes
-----
"""
def __init__(self, index):
super().__init__(
index=index,
descr="Power On AT Calibration System ",
)
self.whitelightsource = salobj.Remote(
domain=self.domain, name="ATWhiteLight", log=self.log
)

self.monochromator = salobj.Remote(
domain=self.domain, name="ATMonochromator", log=self.log
)

# self.cmd_timeout = 10
self.change_grating_time = 60
self.open_shutter_time = 20
self.chiller_temp_tolerance_relative = 0.1
self.chiller_temp_time = 60 * 2
self.track_lamp_warmup = 60
self.lamp_warmup_time = 15 * 60

@classmethod
def get_schema(cls):
schema_yaml = """
$schema: http://json-schema.org/draft-07/schema#
$id: https://github.com/lsst-ts/ts_standardscripts/auxtel/SlewTelescopeIcrs.yaml
title: PowerOnATCalSys v1
description: Configuration for PowerOnATCalSys.
Each attribute can be specified as a scalar or array.
All arrays must have the same length (one item per image).
type: object
properties:
chiller_temperature:
description:
default: 20
- type: number
minimum: 0
whitelight_power:
description: White light power.
default: 910
- type: number
minimum: 0
wavelength:
description: Wavelength (nm). 0 nm is for white light.
default: 0
- type: number
minimum: 0
grating_type:
description: Grating type for each image. The choices are 0=blue, 1=red, 2=mirror.
default: 2
anyOf:
- type: integer
enum: [0, 1, 2]
entrance_slit_width:
description: Width of the monochrometer entrance slit (mm)
default: 5
- type: number
minimum: 0
exit_slit_width:
description: Width of the monochromator entrance slit (mm)
default: 5
- type: number
minimum: 0
additionalProperties: false
"""
return yaml.safe_load(schema_yaml)

async def configure(self, config):
"""Configure the script.
Parameters
----------
config : ``self.cmd_configure.DataType``
Raises
------
salobj.ExpectedError :
If the data does not match the schema, or the lengths
of all values that are arrays do not match.
"""
self.log.info("Configure started")

# self.chiller_temperature = config.chiller_temperature

# self.whitelight_power = config.whitelight_power

# self.wavelength = config.wavelength

# self.grating_type = config.grating_type

# self.entrance_slit_width = config.entrance_slit_width

# self.exit_slit_width = config.exit_slit_width

self.config = config

self.log.info("Configure completed")

def set_metadata(self, metadata):
"""Compute estimated duration.
Parameters
----------
metadata : SAPY_Script.Script_logevent_metadataC
"""
metadata.duration = 60 * 15

async def run(self):
"""Run script."""

await self.checkpoint("Start the chiller")

await whitelightsource.cmd_setChillerTemperature.set_start(temperature=self.chiller_temperature)
await whitelightsource.cmd_startChiller.set_start()

# confirm the chiller is running at chiller_temperature within tolerance
try:
await asyncio.wait_for(
is_chiller_temp_within_tolerance(), self.chiller_temp_time
)
except asyncio.TimeoutError:
self.log.info(f"Gave up waiting after {self.chiller_temp_time} for the chiller to chill to {self.chiller_temperature}")

await self.checkpoint("Open the shutter")

await whitelightsource.cmd_openShutter.set_start()

# Confirm shutter is open or sent an error message? Should we include an error message if it fails rather than the RunTimeError
## shutter_state = await whitelightsource.evt_shutterState.aget()

await self.checkpoint("Turning on lamp")
await whitelightsource.cmd_turnLampOn.set_start(power = self.whitelight_power)

# Confirm lamp state turns on (It will go into a warm up period before it will turn on)
try:
await asyncio.wait_for(
has_lamp_finish_warm_up(), self.lamp_warm_up_time
),
except asyncio.TimeoutError:
self.log.info(f"Lamp didn't turn on after {self.lamp_warm_up_time} s")

await self.checkpoint("Configuring ATMonochromator")

await self.checkpoint("Set the grating")

await monochromator.cmd_selectGrating.set_start(gratingType=self.grating, timeout = 180)

await self.checkpoint("Set wavelength")

await monochromator.cmd_changeWavelength.set_start(wavelength=self.wavelength)

await self.checkpoint("Set slits wide open")

await monochromator.cmd_changeSlitWidth.set_start(slit=1, slitWidth= self.entrance_slit_width)
await monochromator.cmd_changeSlitWidth.set_start(slit=2, slitWidth= self.exit_slit_width)

params = await get_monochromator_parameters()

script.log.info(f"ATMonochromator grating is {params[0]}, wavelength is {params[1]} nm with entry slit width {param[2]} and exit slit width {param[3]}")

async def get_monochromator_parameters():
tmp1 = await monochromator.evt_selectedGrating.aget()
tmp2 = await monochromator.evt_wavelength.aget()
tmp3 = await monochromator.evt_entrySlitWidth.aget()
tmp4 = await monochromator.evt_exitSlitWidth.aget()
return (tmp1.gratingType, tmp2.wavelength, tmp3.width, tmp4.width )


async def has_lamp_finish_warm_up():
lamp_done_warm_up = False
start_time_lamp_warm_up = time.time()
while not lamp_done_warm_up:
lamp_state = await whitelightsource.evt_lampState.aget()
if lamp_state.basicState == ATWhiteLight.LampBasicState.WARMUP:
warmup_time_left = lamp_state.warmupEndTime - lamp_state.private_rcvStamp
script.log.info("Time Left for lamp warmup: {} min.".format(warmup_time_left/60.))
await asyncio.sleep(self.track_lamp_warmup)
lamp_done_warm_up = False
elif lamp_state.basicState == ATWhiteLight.LampBasicState.ON:
lamp_done_warm_up = True
warm_up_elapsed_time = time.time() - start_time_lamp_warm_up
script.log.info(f"White Light Lamp is on after {warm_up_elapsed_time} s")
else:
continue

async def is_chiller_temp_within_tolerance():
chiller_temp_within_tolerance = False
start_time_chill_time = time.time()
while not chiller_temp_within_tolerance:
chiller_temps = await whitelightsource.tel_chillerTemperatures.aget()
if (
chiller_temps.setTemperature * (1.0 - self.chiller_temp_tolerance_relative)
< chiller_temps.supplyTemperature
< chiller_temps.setTemperature * (1.0 + self.chiller_temp_tolerance_relative)
):
chiller_temp_within_tolerance = True
chill_time = time.time() - start_time_chill_time
script.log.info(f"Chiller reached target temperature within tolerance in {chill_time} s")
else:
continue

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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

import asyncio

from lsst.ts.standardscripts.auxtel.calibrations import PowerOnATCalSys

asyncio.run(PowerOnATCalSys.amain())
56 changes: 56 additions & 0 deletions tests/test_auxtel_power_on_atcalsys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# This file is part of ts_standardscripts
#
# Developed for the LSST Telescope and Site Systems.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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

import asyncio
import logging
import random
import unittest

import pytest
from lsst.ts.idl.enums import ATMonochromator, Script

from lsst.ts import salobj, standardscripts
from lsst.ts.standardscripts.auxtel import PowerOnATCalSys

random.seed(47) # for set_random_lsst_dds_partition_prefix

logging.basicConfig()

class TestPowerOnATCalSys(standardscripts.BaseScriptTestCase, unittest.IsolatedAsyncioTestCase):
async def basic_make_script(self, index):
self.script = PowerOnATCalSys(index=index)
#self.atcs_mock = ATCSMock()

return (self.script, self.atcs_mock)

async def test_run(self):
async with self.make_script():
await self.configure_script()

await self.run_script()

async def test_executable(self):
scripts_dir = standardscripts.get_scripts_dir()
script_path = scripts_dir / "auxtel" / "calibrations" / "power_on_atcalsys.py"
await self.check_executable(script_path)


if __name__ == "__main__":
unittest.main()

0 comments on commit 8164152

Please sign in to comment.