Getting started with GORT¶
What is GORT?¶
gort
(Genetically Organized Robotic Technology) is a high-level library to interact with the Local Volume Mapper (LVM) observatory.
The LVM software is based on the concept of actor. An actor is a consumer that receives commands from a client, executes an action (expose an spectrograph, open the dome), and communicates back to the user via replies. LVM uses AMQP/RabbitMQ as the message passing system for commands and replies.
gort
provides three levels of abstraction for accessing the LVM infrastructure:
The lowest level access is a programmatic API to interact with the individual actors that run in the LVM infrastructure.
On top of that,
gort
defines a series ofGortDeviceSet
andGortDevice
classes that expose specific device functionality that is of general use. These classes provide less comprehensive access to the actor devices, but encapsulate the main features that a user is likely to use, and allow to command multiple devices as one, e.g., to send all four telescopes to zenith.The highest level part of
gort
provides the tools for unassisted observing, e.g., observing a tile. Ultimately,gort
provides a robotic mode that takes care of all aspects of observing.
Minimal example¶
The following is a minimum example of how to use the Gort
client to connect to the actor system and retrieve the status of the science telescope.
This code must be run in one of the LVM mountain servers with access to the RabbitMQ exchange.
>>> from gort import Gort
>>> g = await Gort().init()
>>> g.connected()
True
>>> await g.telescopes.sci.update_status()
{'is_tracking': False,
'is_connected': True,
'is_slewing': False,
'is_enabled': False,
'ra_j2000_hours': 9.31475952237581,
'dec_j2000_degs': 26.2017449132552,
'ra_apparent_hours': 9.33749451003047,
'dec_apparent_degs': 26.1043875064315,
'altitude_degs': -61.6417256185769,
'azimuth_degs': 88.1701770599825,
'field_angle_rate_at_target_degs_per_sec': 0.0,
'field_angle_here_degs': -14.3931305251519,
'field_angle_at_target_degs': 0.0,
'axis0': {'dist_to_target_arcsec': 0.0,
'is_enabled': False,
'position_degs': 308.137461864407,
'rms_error_arcsec': 0.0,
'servo_error_arcsec': 0.0},
'axis1': {'dist_to_target_arcsec': 0.0,
'is_enabled': False,
'position_degs': 308.137461864407,
'rms_error_arcsec': 0.0,
'servo_error_arcsec': 0.0,
'position_timestamp': '2023-03-11 16:54:22.5626'},
'model': {'filename': 'DefaultModel.pxp',
'num_points_enabled': 99,
'num_points_total': 111,
'rms_error_arcsec': 18.1248458630523,
'position_degs': 22.8931398305085,
'position_timestamp': '2023-03-11 16:54:22.5626'},
'geometry': 1}
gort
is an asynchronous library which enables multiple processes to run at the same time without blocking the event loop. gort
is written using asyncio
. A certain familiarity with asynchronous programming is recommended, but at a minimum, many of gort
’s functions and methods are actually coroutines that need to be awaited when called. Awaiting a coroutine informs the event manager that other coroutines can be run at the same time, allowing concurrency. In the API documentation, coroutines are prefixed by an async label. Those methods and functions need to be awaited.
Programmatic actor API¶
AMQP actors typically receive commands as a string with CLI-like format. For example, to expose spectrograph sp1
and take a dark of 900 seconds one would do
The programmatic interface allows to convert this command to an asynchronous coroutine like
where remote_actor
is a RemoteActor
instance that represents the lvmscp.sp1
actor.
Remote actors can be added to a Gort
instance by calling the add_actor
method with the name of the actor. This requires the actor to be running CLU 2.0+ and accept the get-command-model
command
In practice, when an instance of Gort
is created, most if not relevant actors are added as remote actors and initialised, and can be accessed from the Gort.actors
dictionary
The list of available commands is accessible as a dictionary of RemoteCommand
under the commands
attribute
These RemoteCommand
can be called and awaited with the arguments the command accepts
RemoteCommand
returns an ActorReply
which includes all the replies generated by the command, which can be accessed as a list under ActorReply.replies
. It’s often convenient to flatten all the replies into a single dictionary of keyword-value
Under the hood, RemoteCommand
are implemented using unclick, a reverse parser for click. Some features and options may not be fully implemented.
Device sets¶
Gort
defines a series of GortDeviceSet
objects that allow the user to communicate with the various infrastructure devices at a relatively high level. Each GortDeviceSet
is composed of one or more GortDevice
, each associated to a physical device and with an associated actor.
For example, Gort.telescopes
provides methods to command all four telescopes. The TelescopeSet
is composed of four Telescope
devices, sci
, skye
, skyw
, spec
that provide access to a single telecope. This allows to, for example, move all telescopes to zenith as one
or command only one telescope
Devices can have their own subdevices. For example all the Telescope
instances have Focuser
devices that allow to command the focuser
More details on how to use the device sets for observing, with code examples, are provided here.
Observing a target¶
To observe a science target along with appropriate calibrators we define a Tile
object either from the scheduler
of from coordinates, allowing calibrators to be filled out by the scheduler
A Tile
can then be observed using GortObserver
or, more conviniently, with Gort.observe_tile
To learn more about observing tiles, see the corresponding section.
Using gort
in IPython¶
gort
can generally be used in IPython, but note that there’s a small caveat. As described here, IPython does not keep a running event loop while a command is not being executed. This means that Gort
cannot keep a connection open to the RabbitMQ exchange and eventually the connection closes.
Gort
will try to recreate the connection to the exchange when needed, if it finds it closed, but this can fail in some corner cases. In this case simply recreate the Gort
client with
This issue should not affect running gort
on an script or in a Jupyter notebook, which runs a persistent background event loop.