Observatory Control User Guide¶
The Observatory Control package provides users and developers with tools to interact with the Rubin Observatory System. These tools are mainly Python classes/objects that represent major groups of components and encapsulate high-level operations involving those components. Control classes are generally separated according to which telescope system is being commanded.
In many instances, such as the telescope control system, the Auxiliary and Main telescope control system classes contain a common code base. Furthermore, there are aspects shared across all classes, such as identifying the required entities, or usages, for the given instantiation of a class. For these reasons, prior to instantiating any of the individual classes it is imperative that any sections associated to common code bases be read and understood prior to using the specific class, starting with Generic Class Functionality and Initialization and subsections therein.
If a common code documentation section exists for a certain class, it is referenced immediately at the top of the document. The following are the classes, grouped according to their common base classes and associated telescope:
TCS Related Classes
Main Telescope Related Classes
Auxiliary Telescope Related Classes
For an example of what these classes enable, the ATCS
class combines functionality for all telescope-operation related components for the Vera Rubin Auxiliary Telescope.
Actions like slewing the telescope, while making sure all involved components are in ENABLED
state and waiting until all components are in position is encapsulate in a single command;
from lsst.ts.observatory.control.auxtel import ATCS
atcs = ATCS()
await atcs.start_task
await atcs.slew_object(name="Alf Pav")
More examples for this and other CSC groups are available in the generic TCS user guide.
In the following sections we provide some general guidance and insights in using these classes as well as a detailed description of the main operations available on each one of them.
Generic Class Functionality and Initialization¶
Controlling resources¶
When writing SAL Script and notebooks to perform high-level operations using many CSCs, one must be aware of resource consumption and allocation, as it may have substantial impact on the performance of the tasks.
Both SalObj and the high-level control modules provides simple ways to control these resources, that users can employ to improve their tasks. There are mainly two features that can be used for this purpose; Sharing DDS domain and Limiting Resources.
Limiting Resources¶
When writing a SAL Script, it is very likely that only a small subset of topics (commands, events and telemetry) from a CSC group will be required.
For instance, a SAL Script that will enable all components on a group, will only require the components summaryState
and configurationsAvailable
events and the state transition commands.
In this case, it is possible to save some time and resources by limiting the group class to subscribe only to the required set, instead of the full topics set.
The package allow users to do that through the intended_usage
parameter.
By default intended_usage = None
, which causes the class to load all the resources from the remotes, which is useful when using the classes from Jupyter notebooks.
Each class defines a specific group of “usages”, for different types of operations. It is possible to combine usages, using bitwise operation, in case more than one operation is needed, e.g.;
intended_usage = Usages.StateTransition | Usages.MonitorHeartBeat
There is also a Usages.All
option that should include the topics for all the supported operations (not to be confused with the intended_usage = None
, which loads all the topics from all the components in a group).
The feature is available when instantiating the class, for example;
from lsst.ts import salobj
from lsst.ts.observatory.control.auxtel import ATCS, ATCSUsages
class MyScript(salobj.BaseScript):
def __init__(self, index):
super().__init__(index=index, descr="My script for some operation.")
self.atcs = ATCS(domain=self.domain, log=self.log, intended_usage=ATCSUsages.StateTransition)
Details of the available usages for each class is given furthermore.
Generic CSC Group behavior¶
All CSC Group classes are constructed on top of the RemoteGroup
base class, which implement generic behavior for groups of CSCs.
These generic methods and attributes will (mostly) work equally regardless of the group class.
Probably the most commonly used generic operations are the enable
and standby
methods.
The idea is that, after running enable
, all CSCs in the group will be in the ENABLED
state.
Components that where already in ENABLED
state will be left in that state.
For components in any other state the method will make sure to send the required state transition commands to enable them.
The method can run without any input argument.
Following are a couple of examples of how to use the enable
method.
This will inspect the configurationsAvailable
event from all CSCs in the ATCS
group to determine the override
for each one of them.
from lsst.ts.observatory.control.auxtel import ATCS
atcs = ATCS()
await atcs.start_task
await atcs.enable()
Override the default configuration for ATAOS.
await atcs.enable(overrides={"ataos": "constant_hex"})
And finally, override overrides for all the CSCs in the group. Note how some of them receive an empty string, which is a way of enabling the CSC with default overrides (and also work for when the CSC is not configurable).
await atcs.enable(
overrides={
"ataos": "current",
"atmcs": "",
"atptg": "",
"atpneumatics": "",
"athexapod": "current",
"atdome": "test.yaml",
"atdometrajectory": "",
}
)
To send all
components to STANDBY
state;
await atcs.standby()
It is also possible to perform state transition in individual CSCs or in subgroups using the set_state
method.
To send the ATAOS
into STANDBY
state;
from lsst.ts import salobj
from lsst.ts.observatory.control.auxtel import ATCS
atcs = ATCS()
await atcs.start_task
await atcs.set_state(salobj.STANDBY, components=["ataos"])
To check what is the current state of a particular CSC in the group, one can use get_state
method;
ataos_state = await atcs.get_state("ataos")
print(ataos_state)
Note that the method returns a salobj.State object, which is easier to understand (State.STANDBY
is more informative than 5
, which is what we get when using remotes to get the state from a CSC).
Another useful “generic” feature of the classes that users may rely on when working on Jupyter notebooks or when writing SAL Script, is that they include a Remote for all its CSCs.
The remotes are included in a class attribute called rem
.
To access them simply do;
await atcs.rem.ataos.evt_heartbeat.next(flush=True, timeout=5)
The class also contains a list of the remote names, which is the name of the CSCs in lowercase.
If the component is indexed, the name will be appended with an underscore followed by the index, e.g.; the component Test
with index 1 becomes test_1
.
A good way of knowing what CSCs are part of the group you are working with is to print this list;
print(atcs.components)
It is also possible to use this list to access the remotes programmatically, e.g.;
# Get one heartbeat and print the state of each component in ATCS
for comp in atcs.components:
await getattr(atcs.rem, comp).evt_heartbeat.next(flush=True, timeout=5)
comp_state = await atcs.get_state(comp)
print(f"{comp}: {comp_state!r}")
Generic Camera Operations¶
TBD
Generic Calsys Operations¶
Each telescope will have a calibration system (Calsys) that includes all components necessary to perform flats.
This is accomplished by projecting light onto the calibration screen.
Each system has the ability to perform two types of calibrations (calib_type
): “white light” flats, which have a broadband illumination, and “monochromatic, which have a much narrower bandwidth of illumination.
In the case of the the maintel
, there is a separate broadband illumination source per filter band.
For both telescopes, the Calsys must be setup for calibration.
This includes a warmpup period for the illumination source(s) and the configuration of optical elements depending on which calib_type
you will be performing.
This can be done at any time before the calibrations are performed, and often take some time to complete.
When ready to start taking flats, the system must be configured and the illumination sources turned on.
For successful flats, the telescope and dome must be configured in such a way that the telescope is aligned with the white spot.
When performing a flat, not only does the camera take an image, but exposures will be taken with a photodiode, using an Electrometer, and Fiber Spectrographs.
SalScripts written to perform calibration flats will resemble this flow:
at_calsys = AuxTelCalSys(BaseCalsys)
at_calsys.setup_calsys(calib_type='mono')
at_calsys.configure_flat(filter='u', wavelength=350)
at_calsys.perform_flat()
Note: The maintel will also include a Collimated Beam Projector (CBP). Description of this system will come at a later time.