Coverage for python / astro_metadata_translator / serialize / fits.py: 13%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 08:43 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 08:43 +0000
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"""Transform ObservationInfo into "standard" FITS headers."""
14from __future__ import annotations
16__all__ = ("dates_to_fits", "group_to_fits", "info_to_fits")
18from typing import TYPE_CHECKING, Any
20if TYPE_CHECKING:
21 import astropy.time
23 from ..observationGroup import ObservationGroup
24 from ..observationInfo import ObservationInfo
27def dates_to_fits(date_begin: astropy.time.Time, date_end: astropy.time.Time) -> dict[str, Any]:
28 """Convert two dates into FITS form.
30 Parameters
31 ----------
32 date_begin : `astropy.time.Time`
33 Date representing the beginning of the observation.
34 date_end : `astropy.time.Time`
35 Date representing the end of the observation.
37 Returns
38 -------
39 cards : `dict` [`str`, [`str` | `float` ] ]
40 Header card keys and values following the FITS standard.
41 If neither date is defined this may be empty.
42 """
43 cards: dict[str, Any] = {}
44 if date_begin is None and date_end is None:
45 # no date headers can be written
46 return cards
48 cards["TIMESYS"] = "TAI"
50 date_avg = None
51 if date_begin is not None and date_end is not None:
52 date_avg = date_begin + (date_end - date_begin) / 2.0
54 for fragment, date in (("OBS", date_begin), ("BEG", date_begin), ("END", date_end), ("AVG", date_avg)):
55 if date is not None:
56 tai = date.tai
57 cards[f"DATE-{fragment}"] = tai.isot
58 cards[f"MJD-{fragment}"] = tai.mjd
60 return cards
63def info_to_fits(obs_info: ObservationInfo) -> tuple[dict[str, Any], dict[str, str]]:
64 """Convert an `ObservationInfo` to something suitable for writing
65 to a FITS file.
67 Parameters
68 ----------
69 obs_info : `ObservationInfo`
70 Standardized observation information to transform to FITS headers.
72 Returns
73 -------
74 cards : `dict` of `str` to (`int`, `float`, `str`, `bool`)
75 FITS header keys and values in form understood by FITS.
76 comments : `dict` of `str` to `str`
77 Suitable comment string. There will be at most one entry for each key
78 in ``cards``.
79 """
80 cards = {}
81 comments = {}
83 if obs_info.instrument is not None:
84 cards["INSTRUME"] = obs_info.instrument
85 comments["INSTRUME"] = "Name of instrument"
87 cards.update(dates_to_fits(obs_info.datetime_begin, obs_info.datetime_end))
89 return cards, comments
92def group_to_fits(obs_group: ObservationGroup) -> tuple[dict[str, Any], dict[str, str]]:
93 """Convert an `ObservationGroup` to something suitable for writing
94 to a FITS file.
96 Parameters
97 ----------
98 obs_group : `ObservationGroup`
99 Collection of observation information to transform to a single
100 FITS header.
102 Returns
103 -------
104 cards : `dict` [`str`, [`int` | `float` | `str` | `bool` ] ]
105 FITS header keys and values in form understood by FITS.
106 comments : `dict` [`str`, `str`]
107 Suitable comment string. There will be at most one entry for each key
108 in ``cards``.
109 """
110 cards = {}
111 comments = {}
113 oldest, newest = obs_group.extremes()
115 instruments = obs_group.property_values("instrument")
116 if len(instruments) == 1:
117 cards["INSTRUME"] = list(instruments)[0]
118 comments["INSTRUME"] = "Name of instrument"
120 cards.update(dates_to_fits(oldest.datetime_begin, newest.datetime_end))
122 return cards, comments