Alert Observations API

1. What is an alert observation?

The TJO control system architecture supports programmatic submission of observations through an API, which are referred to as alert observations.
Alert observations are granted the highest priority within the TJO scheduling system. They are scheduled as soon as possible, subject to visibility windows, observing constraints, and weather conditions, with a reaction time limited by telescope pointing, typically ranging from 1 to 2 minutes.
Alert observations have been ideally designed for observations of short-lived, transient events that require urgent observations. At this moment, only alert observations are accepted through this API, although in the future we may  also accept regular non-urgent observations.
For observations of targets of other nature, please use the usual channel for observation upload and definition through Phase 2 of MUR.

For those users registered in MUR the API specification document can be found at MUR API DOCS accessed by the same username and password used to access MUR.

2. How to request access to alert observations?

For those planning to submit a new proposal for the upcoming semester and intend to submit alert observations via the API, the first step is to mark the "alert observations" checkbox in your new proposal submission form. The use of this feature should be properly  justified in your scientific justification.
If you plan to submit alert observations from an already active proposal, please contact the TJO staff at obsmontsec@ieec.cat.
To use this feature, users should be subscribed to an alert distribution system for astronomical events. Ideally, this includes a full-stream alert broker (e.g., Alerce, Fink, ANTARES, AMPEL), email lists (e.g., TNS), apps (e.g., Astro-COLIBRI), or any service meeting the following criteria:
  • Capable of running continuously in the background on your computer/server.
  • Capable of generating alerts that are detectable and parseable by a programming language (preferably Python).
  • Provides coordinates and (estimated) magnitudes of the targets in the alerts.
Users are responsible for setting up connections with their desired alert distribution system and processing their data. TJO staff will only assist with the script to send data to the API.

3. How should alert observations be formatted?

We have created an API to facilitate the programmatic submission of new observations to MUR. Observations must be sent in JSON-formatted data, where keys and accepted values align with those in MUR. The JSON should contain five distinct objects: 'settings', 'targets', 'observing_constraints', 'instrument_configurations', and 'sequence'. Multiple objects can be defined for ‘targets’, ‘observing_constraints’, and 'instrument_configurations' labeled as ‘tN’, ‘oN’, and ‘iN’, respectively, where N = 1, 2, .. j. These objects can be later combined through the key 'equation' inside 'sequence'. The general structure of the JSON is exemplified below:
 
	data = {
		"settings": SETTINGS,
			"targets": {
			"t1": TARGET_1,
			"t2": TARGET_2,
			"tN": TARGET_N
			},
		"observing_constraints": {
			"o1": OBSERVING_CONSTRAINT_1,
			"o2": OBSERVING_CONSTRAINT_2,
			"oN": OBSERVING_CONSTRAINT_N
			},
		"instrument_configurations": {
			"i1": INSTRUMENT_CONFIGURATION_1,
			"i2": INSTRUMENT_CONFIGURATION_2,
			"iN": INSTRUMENT_CONFIGURATION_N
			},
		"sequence": SEQUENCE
		}
 
Here is a breakdown of the keys and acceptable formats for each of these objects:
  • settings: general options to configure your observation.
	SETTINGS = {
		"proposal": YOUR_PROPOSAL_ID
		}
 
proposal: float  
ID number of your proposal.
 
  • targets: each new target should be stored as a new object ‘t1’, ‘t2’, ‘t3’, etc. 
	TARGET_N = {
		"source_name": SOURCE_NAME,
		"coordinate_format": COORDINATE_FORMAT,
		"coordinates": COORDINATES
		}
 
source_name: str, optional. Default: DefaultTargetN
The name of the target. Any name (either cataloged or not) is valid. This name will be inserted in the FITS header and, therefore, should be shorter than 70 characters.
coordinate_format: str. Accepted: ‘equatorial’, ‘minorplanet’, and ‘comet’
The type of coordinates to be introduced. See the coordinate formats help in MUR. Use ‘equatorial’ to introduce equatorial coordinates with proper motion, use ‘minorplanet’ to introduce targets having an heliocentric orbit using mean orbital elements, and use ‘comet’ to introduce targets having an heliocentric orbit using orbital elements at perihelion.
coordinates: str
Depending on the coordinate type chosen, a specific format must be introduced. Please, visit the Coordinate formats page in MUR: https://mur.ieec.cat/en/intranet-content/67
  • observing_constraints: each new observing constraint should be stored as a new object ‘o1’, ‘o2’, ‘o3’, etc. 
	OBSERVING_CONSTRAINT_N = {
		"sky_brightness": SKY_BRIGHTNESS,
		"seeing": SEEING,
		"cloud_cover": CLOUD_COVER,
		"max_solar_elevation": SOLAR_ELEVATION,
		"moon_distance": MOON_DISTANCE,
		"airmass_min": MIN _AIRMASS,
		"airmass_max": MAX_AIRMASS,
		"hourangle_min": MIN_HOURANGLE,
		"hourangle_max": MAX_HOURANGLE,
		"delay_before": DELAY_BEFORE,
		"delay_after": DELAY_AFTER,
		"windows": [
			{"start": WINDOW_START_1,
			"end": WINDOW_END_1,
			"period": period_1},
			{"start": WINDOW_START_N,
			"end": WINDOW_END_N,
			"period": WINDOW_PERIOD_N}
			]
		}
 
sky_brightness: str, optional. Accepted: ‘bright’ (default) and ‘dark’
Specifies the maximum sky illumination for an observation to be executed. Use ‘bright’ for observations with any Moon elevation and illumination, use ‘dark’ for observations when the Moon is below the horizon or its fractional illumination is lower than 30%.
seeing: str, optional. Accepted: ‘good’ and ‘medium’ (default)
Specifies the worst seeing value for an observation to be executed and considered as valid. Use ‘good’ for observations with a seeing below 1.5 arcsec, use ‘medium’ for observations with a seeing below 2.5 arcsec.
cloud_cover: str, optional. Accepted: ‘photometric’ and ‘spectroscopic’ (default)
Specifies the sky conditions during the observation. Use ‘photometric’ for observations with completely clear sky, use ‘spectroscopic’ for observations that can also be observed when thin veils or clouds are present. 
max_solar_elevation: str, optional. Accepted: ‘night’ (default), ‘nautical’, ‘civil’, ‘sunset’
Maximum elevation of the Sun during observations. Use ‘night’ for observations with the Sun more than 18 degrees below the horizon. Use ‘nautical’ for observations with the Sun more than 12 degrees below the horizon. Use ‘civil’ for observations with the Sun more than 6 degrees below the horizon. Use ‘sunset’ for observations with the Sun below the horizon.
moon_distance: float, optional. Default: 0.0
The minimum angular distance from the target to the Moon in degrees.
airmass_min: float, optional. Default: 1.0
The airmass minimum value for observations to be taken. By definition, values must be larger than one.
airmass_max: float, optional. Default: 2.0
The airmass maximum value for observations to be taken. By definition, values must be larger than one.
hourangle_min: float, optional. Default: 0.0
Some observations may require targets to be close to the meridian or at a certain hour angle. The values specified here should be in hours.
hourangle_max: float, optional. Default: 24.0
Some observations may require targets to be close to the meridian or at a certain hour angle. The values specified here should be in hours.
delay_before: float, optional. Default: 0.0
The Delay can be used to introduce relative time delays (in hours) between consecutive observations.
delay_after: float, optional. Default: 0.0
The Delay can be used to introduce relative time delays (in hours) between consecutive observations.
windows: list, optional. Default: None
In order to specify when a particular observation has to be executed using an absolute time definition, one or several Windows can be defined. Each window should be added as a new dictionary inside the windows list, and each one should contain the following keys: 
start: str, float. Optional, default: None
The time when the window starts in Julian Days or ISO format. 
end: str, float. Optional, default: None
The time when the window ends in Julian Days or ISO format. 
period_N: float. Optional, default: 0.0
In case that a period (in days) is also specified, the specified Window will be repeated every Period.
 
  • instrument_configurations: each new instrument configuration should be stored as a new object ‘i1’, ‘i2’, ‘i3’, etc. 
	INSTRUMENT_CONFIGURATION_N = {
		"instrument": INSTRUMENT,
		"follow_type": FOLLOW_TYPE,
		"dither": DITHER_PATTERN,
		"dither_RA": DITHER_RA,
		"dither_DEC": DITHER_DEC,
		"exposures": EXPOSURES,
		"exp_time": EXPOSURE_TIME,
		"binning": BINNING,
		"subframe": SUBFRAME,
		"filter": FILTER_NAME
		}
 
instrument: str. Accepted: ‘meia3’, ‘ares’
Instrument to be used.
exposures: int
The number of exposures to be taken when using the current Instrument configuration. All the exposures will be taken consecutively. In case that a Dither pattern is defined, Exposures will be taken for each one of the different pointings defined.
exp_time: float
The exposure time for each one of the exposures defined in the current Instrument configuration.
binning: str, optional. Accepted: ‘1x1’ (default), ‘2x2’
Currently, two binnings can be specified for MEIA3 and ARES: 1x1 and 2x2. 
subframe: int, optional. Default: 4096
 In case that a faster read-out is desired, the CCD area to be read can be reduced as desired with a Subframe value (in pixels). The Subframe value is the number of pixels in each direction (rows and columns) to be read around the center of the CCD. The subframe value is applied before binning.
filter: str. Accepted for MEIA3: ‘U’, ‘B’, ‘V’, ‘R’, ‘I’, ‘SDSS g’, ‘SDSS r’, ‘SDSS i’, ‘SDSS z’, ‘H alpha’; Accepted for ARES: ‘red’, ‘green’, ‘red+ThAr’, ‘green+ThAr’, ‘red+LEDS’, ‘green+LEDS’, ‘red+tungsten’, ‘green+tungsten’, ‘none’
Ten different filters can be selected for MEIA3: Johnson-Cousins U, B, V, R and I, SDSS g, r, i, and z and H alpha (650-664 nm). In addition, exposures can also be taken without a filter. For ARES, two different VPHs can be selected, red and green,  together with the desired calibration lamp (ThAr, tungsten or LED lamp).
follow_type (MEIA3 only): str, optional. Accepted: ‘object’ (default), ‘sidereal’, ‘none’.
Telescope tracking. Use ‘none’ when no tracking is desired. Use ‘object’ to follow the object, use ‘sidereal’ for sidereal tracking, regardless of the object movement.  For Equatorial coordinate types, there is no difference between ‘object’ and ‘sidereal’.
dither (MEIA3 only): str, optional. Accepted: ‘1x1’ (default), ‘2x2’, ‘3x3’
In case that several exposures with slightly different telescope pointings (e.g., to make a mosaic) are required, they can be specified through the Dither field. The dithers are defined by selecting a Dither pattern, a dither_RA and a dither_DEC.
dither_RA (MEIA3 only): float, optional. Default: 0.0
The dither values (in degrees), correspond to the maximum size of the pattern. For further information, some examples are also provided in the Examples page.
dither_DEC (MEIA3 only): float, optional. Default: 0.0
The dither values (in degrees), correspond to the maximum size of the pattern. For further information, some examples are also provided in the Examples page.
  • sequence: Observations at the TJO are provided as sequences. Each observation sequence involves the combination of observing blocks including three elements: Targets, Observing constraints and Instrument configurations (a TOI). A complete observing sequence can be defined by combining one or several TOIs.
	SEQUENCE = {
		"equation": EQUATION,
		"priority": PRIORITY,
		"repetitions": REPETITIONS,
		"delay": DELAY
		}
 
equation: str
Equations indicate what to be observed and how. Therefore, they are the most important element of a sequence. The minimum element of an equation is the combination of one Target, one Observing constraint and one Instrument configuration (a TOI). The combination is done by typing a t and a number for a target, an o and a number for the observing constraint and an i and a number for the instrument configuration. Please, visit the Phase 2 help page in MUR for more information: https://mur.ieec.cat/en/intranet-content/65
priority: float, optional. Default: 10.0
Defined in case that two or more sequences of the same proposal can be observed at the same time.
repetitions: int, optional. Default: 1
The number of times that a sequence has to be executed. When the sequence has to be executed as long as there is time available, a value of -1 should be provided.
delay: float, optional. Default: 12.0
In case that more than one iteration is requested, the minimum time between two successive executions of the same sequence can be specified (in hours)

4. Exposure time calculator

A common parameter often provided in astronomical alerts is the magnitude or expected magnitude of an event in a specific filter. To help users to dynamically select an appropriate exposure time for their observations, we offer an Exposure Time Calculator API that can be accessed programmatically. 
For more information about how to use the Exposure Time Calculator, please refer to the specific help section in MUR.

5. How do I send alert observations to the TJO?

The use of the TJO API requires authentication through an API key. All users registered to MUR have been assigned with an API key, which can be found in the user’s profile page in MUR. 
In order to send alert observations to the TJO, you need to use a programming language capable of making HTTP requests to APIs, using the appropriate library or module. In this guide, we will walk you through the process using Python to send an observation to the TJO. Here we offer a guide with the basic steps to set up your connection to the TJO and send your observations.

The first step is to import the required for sending requests to the TJO APIs:
	import json
	import request

All TJO API requests share the same base URL, which is https://mur.ieec.cat/api/v1/alert. The endpoints for submitting observations and calculating exposure times are ‘/alert  and ‘/utils/exposure-time’, respectively. Define it in a variable along with your API key:
	URL = "https://mur.ieec.cat/api/v1"
	API_KEY = "d3f1n3h3r3y0urap1k3y"

Next, create a function to generate JSON-formatted data containing the new observation. For simplicity, we provide a basic example filling only the mandatory variables. Refer to the end of the document for a detailed, comprehensive example.
This simple function takes as an input the coordinates of the target and the exposure time, which could be computed using our exposure time calculator, and returns the JSON formatted data. The two input values should be easily obtained from the information contained in the alerts itself.
	def generate_json(coordinates, exp_time):
		data = {
			"settings" : {
			"proposal": 594,
			},
		"targets" : {
			"t1": {
				"source_name": "GJ 686",
				"coordinate_format": "equatorial",
				"coordinates": coordinates
				}
			},
		"observing_constraints" : {
			"o1" : {
				"max_solar_elevation": "nautical",
				"airmass_min": 1.0,
				"airmass_max": 2.5
				}
			},
		"instrument_configurations" : {
			"i1" : {
				"instrument": "MEIA3",
				"exposures": 1,
				"exp_time": exp_time,
				"filter": "R"
				}
			},
		"sequence" : {
			"equation": "t1o1i1",
			}
			
		return data

The final step would be to include this function inside your already-existing code that monitors the alerts. Let’s assume that this code is composed of an infinite loop that is continuously checking if there are new alerts that satisfy any user defined filter (e.g., type of event, magnitude, etc.), similar to the example below:
	while True:
		#CODE TO DETECT NEW ALERTS (NOT PROVIDED BY THE TJO)
		new_alert = CODE_TO_DETECT_ALERTS(NOT_PROVIDED_BY_TJO)
		if new_alert:
			print("New alert detected, do something")
			#INCLUDE HERE THE LINES TO SEND OBSERVATIONS TO THE TJO

Once the code detects a new alert, it should be able to extract at least the coordinates of the target (remember to translate them to the TJO format) and its magnitude in a specific filter. With the magnitude, the exposure time calculator can be used to compute the exposure time. Then, using this information, the code should generate the JSON-formatted data, and send them to the TJO. The final code should look similar to this toy-model snippet: 
	while True:
		#CODE TO DETECT NEW ALERTS (NOT PROVIDED BY THE TJO)
		new_alert = CODE_TO_DETECT_ALERTS(NOT_PROVIDED_BY_TJO))
		if new_alert:
			#EXTRACT MAGNITUDE AND COORDINATES (IN TJO FORMAT)
			magnitude = new_alert["magnitude"]
			coordinates = new_alert["coordinates"]
			#GENERATE INPUT DATA FOR THE EXPOSURE TIME CALCULATOR
			data = {'mag': magnitude, 'filter': 'R', 'sn': 50}
			#SEND THE INPUT TO THE EXPOSURE TIME CALCULATOR API
			result = requests.get(f"{URL}/utils/exposure-time",  
                                  params = data, verify = False)
			exp_time = result.json()['exposure_time']
			#GENERATE DATA WITH THE NEW OBSERVATION
			event = generate_json(coordinates, exp_time)
			#API KEY DICTIONARY
			header={"X-API-KEY": API_KEY}
			#SEND NEW OBSERVATION TO TJO API
			response = requests.post(f"{URL}/alert", json.dumps(event),
                                     headers = header, verify = False)
 			#CHECK IF THE REQUEST WAS SUCCESFUL
			if response.status_code == 201:
				print("You request was received successfuly:")
			else:
				print("There has been an error reading your request.")

Please note that this is a simplified example, and you may need to adapt it based on your specific requirements and the structure of your existing code.
For a more extensive example, refer to this file: send_alert_observation_TJO.py