Coverage for python/astro_metadata_translator/properties.py: 60%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of astro_metadata_translator.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the LICENSE file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12"""Properties calculated by this package.
14Defines all properties in one place so that both `ObservationInfo` and
15`MetadataTranslator` can use them. In particular, the translator
16base class can use knowledge of these properties to predefine translation
17stubs with documentation attached, and `ObservationInfo` can automatically
18define the getter methods.
20"""
21from __future__ import annotations
23__all__ = (
24 "PropertyDefinition",
25 "PROPERTIES",
26)
28from dataclasses import dataclass
29from typing import Any, Callable, Optional, Tuple
31import astropy.coordinates
32import astropy.time
33import astropy.units
35# Helper functions to convert complex types to simple form suitable
36# for JSON serialization
37# All take the complex type and return simple python form using str, float,
38# int, dict, or list.
39# All assume the supplied parameter is not None.
42def earthlocation_to_simple(location: astropy.coordinates.EarthLocation) -> Tuple[float, ...]:
43 """Convert EarthLocation to tuple.
45 Parameters
46 ----------
47 location : `astropy.coordinates.EarthLocation`
48 The location to simplify.
50 Returns
51 -------
52 geocentric : `tuple` of (`float`, `float`, `float`)
53 The geocentric location as three floats in meters.
54 """
55 geocentric = location.to_geocentric()
56 return tuple(c.to_value(astropy.units.m) for c in geocentric)
59def simple_to_earthlocation(simple: Tuple[float, ...], **kwargs: Any) -> astropy.coordinates.EarthLocation:
60 """Convert simple form back to EarthLocation."""
61 return astropy.coordinates.EarthLocation.from_geocentric(*simple, unit=astropy.units.m)
64def datetime_to_simple(datetime: astropy.time.Time) -> Tuple[float, float]:
65 """Convert Time to tuple.
67 Parameters
68 ----------
69 datetime : `astropy.time.Time`
70 The time to simplify.
72 Returns
73 -------
74 mjds : `tuple` of (`float`, `float`)
75 The two MJDs in TAI.
76 """
77 tai = datetime.tai
78 return (tai.jd1, tai.jd2)
81def simple_to_datetime(simple: Tuple[float, float], **kwargs: Any) -> astropy.time.Time:
82 """Convert simple form back to astropy.time.Time"""
83 return astropy.time.Time(*simple, format="jd", scale="tai")
86def exptime_to_simple(exptime: astropy.units.Quantity) -> float:
87 """Convert exposure time Quantity to seconds."""
88 return exptime.to_value(astropy.units.s)
91def simple_to_exptime(simple: float, **kwargs: Any) -> astropy.units.Quantity:
92 """Convert simple form back to Quantity."""
93 return simple * astropy.units.s
96def angle_to_simple(angle: astropy.coordinates.Angle) -> float:
97 """Convert Angle to degrees."""
98 return angle.to_value(astropy.units.deg)
101def simple_to_angle(simple: float, **kwargs: Any) -> astropy.coordinates.Angle:
102 """Convert degrees to Angle."""
103 return astropy.coordinates.Angle(simple * astropy.units.deg)
106def temperature_to_simple(temp: astropy.units.Quantity) -> float:
107 """Convert temperature to kelvin."""
108 return temp.to(astropy.units.K, equivalencies=astropy.units.temperature()).to_value()
111def simple_to_temperature(simple: float, **kwargs: Any) -> astropy.units.Quantity:
112 """Convert scalar kelvin value back to quantity."""
113 return simple * astropy.units.K
116def pressure_to_simple(press: astropy.units.Quantity) -> float:
117 """Convert pressure Quantity to hPa."""
118 return press.to_value(astropy.units.hPa)
121def simple_to_pressure(simple: float, **kwargs: Any) -> astropy.units.Quantity:
122 """Convert the pressure scalar back to Quantity."""
123 return simple * astropy.units.hPa
126def skycoord_to_simple(skycoord: astropy.coordinates.SkyCoord) -> Tuple[float, float]:
127 """Convert SkyCoord to ICRS RA/Dec tuple"""
128 icrs = skycoord.icrs
129 return (icrs.ra.to_value(astropy.units.deg), icrs.dec.to_value(astropy.units.deg))
132def simple_to_skycoord(simple: Tuple[float, float], **kwargs: Any) -> astropy.coordinates.SkyCoord:
133 """Convert ICRS tuple to SkyCoord."""
134 return astropy.coordinates.SkyCoord(*simple, unit=astropy.units.deg)
137def altaz_to_simple(altaz: astropy.coordinates.AltAz) -> Tuple[float, float]:
138 """Convert AltAz to Alt/Az tuple.
140 Do not include obstime or location in simplification. It is assumed
141 that those will be present from other properties.
142 """
143 return (altaz.az.to_value(astropy.units.deg), altaz.alt.to_value(astropy.units.deg))
146def simple_to_altaz(simple: Tuple[float, float], **kwargs: Any) -> astropy.coordinates.AltAz:
147 """Convert simple altaz tuple to AltAz.
149 Will look for location and datetime_begin in kwargs.
150 """
151 location = kwargs.get("location")
152 obstime = kwargs.get("datetime_begin")
154 return astropy.coordinates.AltAz(
155 simple[0] * astropy.units.deg, simple[1] * astropy.units.deg, obstime=obstime, location=location
156 )
159@dataclass
160class PropertyDefinition:
161 """Definition of an instrumental property."""
163 doc: str
164 """Docstring for the property."""
166 str_type: str
167 """Python type of property as a string (suitable for docstrings)."""
169 py_type: type
170 """Actual python type."""
172 to_simple: Optional[Callable[[Any], Any]] = None
173 """Function to convert value to simple form (can be ``None``)."""
175 from_simple: Optional[Callable[[Any], Any]] = None
176 """Function to convert from simple form back to required type (can be
177 ``None``)."""
180# Dict of properties to tuple where tuple is:
181# - description of property
182# - Python type of property as a string (suitable for docstrings)
183# - Actual python type as a type
184# - Simplification function (can be None)
185# - Function to convert simple form back to required type (can be None)
186PROPERTIES = {
187 "telescope": PropertyDefinition("Full name of the telescope.", "str", str),
188 "instrument": PropertyDefinition("The instrument used to observe the exposure.", "str", str),
189 "location": PropertyDefinition(
190 "Location of the observatory.",
191 "astropy.coordinates.EarthLocation",
192 astropy.coordinates.EarthLocation,
193 earthlocation_to_simple,
194 simple_to_earthlocation,
195 ),
196 "exposure_id": PropertyDefinition(
197 "Unique (with instrument) integer identifier for this observation.", "int", int
198 ),
199 "visit_id": PropertyDefinition(
200 """ID of the Visit this Exposure is associated with.
202Science observations should essentially always be
203associated with a visit, but calibration observations
204may not be.""",
205 "int",
206 int,
207 ),
208 "physical_filter": PropertyDefinition("The bandpass filter used for this observation.", "str", str),
209 "datetime_begin": PropertyDefinition(
210 "Time of the start of the observation.",
211 "astropy.time.Time",
212 astropy.time.Time,
213 datetime_to_simple,
214 simple_to_datetime,
215 ),
216 "datetime_end": PropertyDefinition(
217 "Time of the end of the observation.",
218 "astropy.time.Time",
219 astropy.time.Time,
220 datetime_to_simple,
221 simple_to_datetime,
222 ),
223 "exposure_time": PropertyDefinition(
224 "Duration of the exposure with shutter open (seconds).",
225 "astropy.units.Quantity",
226 astropy.units.Quantity,
227 exptime_to_simple,
228 simple_to_exptime,
229 ),
230 "dark_time": PropertyDefinition(
231 "Duration of the exposure with shutter closed (seconds).",
232 "astropy.units.Quantity",
233 astropy.units.Quantity,
234 exptime_to_simple,
235 simple_to_exptime,
236 ),
237 "boresight_airmass": PropertyDefinition("Airmass of the boresight of the telescope.", "float", float),
238 "boresight_rotation_angle": PropertyDefinition(
239 "Angle of the instrument in boresight_rotation_coord frame.",
240 "astropy.coordinates.Angle",
241 astropy.coordinates.Angle,
242 angle_to_simple,
243 simple_to_angle,
244 ),
245 "boresight_rotation_coord": PropertyDefinition(
246 "Coordinate frame of the instrument rotation angle (options: sky, unknown).",
247 "str",
248 str,
249 ),
250 "detector_num": PropertyDefinition(
251 "Unique (for instrument) integer identifier for the sensor.", "int", int
252 ),
253 "detector_name": PropertyDefinition(
254 "Name of the detector within the instrument (might not be unique if there are detector groups).",
255 "str",
256 str,
257 ),
258 "detector_unique_name": PropertyDefinition(
259 (
260 "Unique name of the detector within the focal plane, generally combining detector_group with "
261 "detector_name."
262 ),
263 "str",
264 str,
265 ),
266 "detector_serial": PropertyDefinition("Serial number/string associated with this detector.", "str", str),
267 "detector_group": PropertyDefinition(
268 "Collection name of which this detector is a part. Can be None if there are no detector groupings.",
269 "str",
270 str,
271 ),
272 "detector_exposure_id": PropertyDefinition(
273 "Unique integer identifier for this detector in this exposure.",
274 "int",
275 int,
276 ),
277 "object": PropertyDefinition("Object of interest or field name.", "str", str),
278 "temperature": PropertyDefinition(
279 "Temperature outside the dome.",
280 "astropy.units.Quantity",
281 astropy.units.Quantity,
282 temperature_to_simple,
283 simple_to_temperature,
284 ),
285 "pressure": PropertyDefinition(
286 "Atmospheric pressure outside the dome.",
287 "astropy.units.Quantity",
288 astropy.units.Quantity,
289 pressure_to_simple,
290 simple_to_pressure,
291 ),
292 "relative_humidity": PropertyDefinition("Relative humidity outside the dome.", "float", float),
293 "tracking_radec": PropertyDefinition(
294 "Requested RA/Dec to track.",
295 "astropy.coordinates.SkyCoord",
296 astropy.coordinates.SkyCoord,
297 skycoord_to_simple,
298 simple_to_skycoord,
299 ),
300 "altaz_begin": PropertyDefinition(
301 "Telescope boresight azimuth and elevation at start of observation.",
302 "astropy.coordinates.AltAz",
303 astropy.coordinates.AltAz,
304 altaz_to_simple,
305 simple_to_altaz,
306 ),
307 "science_program": PropertyDefinition("Observing program (survey or proposal) identifier.", "str", str),
308 "observation_type": PropertyDefinition(
309 "Type of observation (currently: science, dark, flat, bias, focus).",
310 "str",
311 str,
312 ),
313 "observation_id": PropertyDefinition(
314 "Label uniquely identifying this observation (can be related to 'exposure_id').",
315 "str",
316 str,
317 ),
318 "observation_reason": PropertyDefinition(
319 "Reason this observation was taken, or its purpose ('science' and 'calibration' are common values)",
320 "str",
321 str,
322 ),
323 "exposure_group": PropertyDefinition(
324 "Label to use to associate this exposure with others (can be related to 'exposure_id').",
325 "str",
326 str,
327 ),
328 "observing_day": PropertyDefinition(
329 "Integer in YYYYMMDD format corresponding to the day of observation.", "int", int
330 ),
331 "observation_counter": PropertyDefinition(
332 (
333 "Counter of this observation. Can be counter within observing_day or a global counter. "
334 "Likely to be observatory specific."
335 ),
336 "int",
337 int,
338 ),
339 "has_simulated_content": PropertyDefinition(
340 "Boolean indicating whether any part of this observation was simulated.", "bool", bool, None, None
341 ),
342 "group_counter_start": PropertyDefinition(
343 "Observation counter for the start of the exposure group."
344 "Depending on the instrument the relevant group may be "
345 "visit_id or exposure_group.",
346 "int",
347 int,
348 None,
349 None,
350 ),
351 "group_counter_end": PropertyDefinition(
352 "Observation counter for the end of the exposure group. "
353 "Depending on the instrument the relevant group may be "
354 "visit_id or exposure_group.",
355 "int",
356 int,
357 None,
358 None,
359 ),
360}