Note
This page is automatically generated by exporting this example notebook.
Example Notebook¶
The code below represents an example user-define operation that can be converted into a SAL Script, used to illustrate the process. It is assumed that the user understands and has tested the code they wish to convert to a SAL Script.
In this example we will perform a dithering pattern on the sky with the Auxiliary Telescope and take a sequence of images at each position.
We will use functionality from the two main observatory control classes for the Auxiliary Telescope; ATCS and LATISS. More information about these can be found in the ts_observatory_control user guide.
import asyncio
import logging
import numpy as np
from matplotlib import pyplot as plt
from lsst.ts import salobj
from lsst.ts.observatory.control.auxtel.atcs import ATCS
from lsst.ts.observatory.control.auxtel.latiss import LATISS
from lsst.ts.observatory.control.utils.enums import RotType
Setting up logging¶
When running on a notebook you may be interested in getting logging feedback. To enable this you may want to setup the Python logging facility.
This next cell will setup the basic log configuration in debug mode. If
you find this too verbose and want to change the level, you can replace
logging.DEBUG
with logging.INFO
, logging.WARNING
,
logging.ERROR
or skip the next cell altogether.
logging.basicConfig(format="%(name)s:%(message)s", level=logging.DEBUG)
Matplotlib can be chatty so, better decrease its log level.
logging.getLogger("matplotlib").setLevel(logging.WARNING)
Setup control classes¶
The first step in interacting with the system is to setup the SalObj library and the control classes.
This is done by creating a salobj.Domain
, an object to handle the
DDS communication and later passing it to the control classes.
domain = salobj.Domain()
atcs = ATCS(domain)
latiss = LATISS(domain)
Reducing salobj.Remote internal logging.¶
The internal salobj.Remote
classes can get very chatty due to the
incomming traffic from the CSCs. You can reduce this by using a method
provided by the control classes.
atcs.set_rem_loglevel(logging.ERROR)
latiss.set_rem_loglevel(logging.ERROR)
Wait for salobj to finish setup DDS communication.¶
This is a background task that we need to await
before we can
communicate with the components.
The control software performs numerous tasks asynchronously, with
Python’s asyncio
library. Using an await
statement ensures that
the command will not return until it’s completed. For more information
see documentation in the asyncio
library.
await asyncio.gather(atcs.start_task, latiss.start_task)
Executing Operations¶
From now on we are ready to interact with the system.
We are now going to write down the loop that performs the dithering and data taking.
I will assume you had some time to think about the problem and exercice it enough to get confortable with parameterizing it and so on.
The idea is to develop a procedure that will do the following:
- Slew to a target that is defined by a name that can be resolved by simbad, and a rotator setup.
- Given a pre-defined grid of x/y offsets from the original position;
- Offset the telescope to each,
- Take a set of pre-defined observations.
We start by defining the parameters in the cells bellow.
Target definition¶
The next cell defines the target to slew to and the rotator value/type.
target_name = "HD 164461"
rot_value = 0.
rot_type = RotType.PhysicalSky
Define offset grid¶
n_grid = 11 # how many visits in the grid
grid_x = (np.random.rand(n_grid)-0.5)*120. # offset in image coordinate x-axis (in arcsec)
grid_y = (np.random.rand(n_grid)-0.5)*120. # offset in image coordinate y-axis (in arcsec)
We are in a Jupyter notebook so we might as well plot the grid generated above.
plt.plot(grid_x, grid_y, '.:')
plt.xlabel("x-offset in arcsec")
plt.ylabel("y-offset in arcsec")
Define observations setup¶
exptime = [5., 10., 20.] # list of exposure times in seconds
obs_filter = ["RG610", "RG610", "RG610"] # list of filters
obs_grating = ["empty_1", "ronchi90lpmm", "ronchi170lpmm"] # list of gratings
Run observation sequence¶
Now we have the parameters defined we can run a loop that will execute the dithering and observing sequence.
await atcs.slew_object(name=target_name, rot=rot_value, rot_type=rot_type)
Note on the operation bellow¶
As of ts_observatory_control v0.7, there is a background race condition
between changing the instrument configuration and taking an image. Once
the instrument configuration changes, the ATAOS
component will apply
offsets to focus and telescope position to compensate for focus/image
motion due to filter/grating settings. This will end up resulting in
image motion, if the appropriate events are not waited on.
A fix for this issue is being worked out in DM-28530 and will soon be
available. In order to keep this example simple and clear, we decided
not to add the current workaround to this issue. In any case, if you
plan on executing operations that involves setting the instrument
configuration and taking an image with any of the LATISS.take_*
commands, check with observatory personnel whether this condition was
already resolved.
for xx, yy in zip(grid_x, grid_y):
# Offset telescope
# Use non-relative offset as they are easier to reset
await atcs.offset_xy(x=xx, y=yy, relative=False)
# Take data
for etime, flt, grt in zip(exptime, obs_filter, obs_grating):
await latiss.take_object(exptime=etime, filter=flt, grating=grt)
# Reset offset
await atcs.offset_xy(x=0., y=0., relative=False)