Coverage for python/lsst/obs/rubinGenericCamera/translator.py: 87%
87 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 09:27 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 09:27 +0000
1import numpy as np
2import astropy.units as u
3from astropy.time import Time
5from astro_metadata_translator import cache_translation
6from lsst.obs.lsst.translators.lsst import LsstBaseTranslator
8__all__ = ["StarTrackerNarrowTranslator", "StarTrackerWideTranslator", "StarTrackerFastTranslator",]
11class RubinGenericCameraTranslator(LsstBaseTranslator):
12 """Metadata translator for Rubin Generic Camera FITS headers"""
14 name = None # you must specialise this class
15 """Name of this translation class"""
17 supported_instrument = None # you must specialise this class
18 """Supports the LSST Generic Camera instrument."""
20 default_resource_package = "lsst.obs.rubinGenericCamera"
21 """Package to use to locate the header correction resources."""
23 default_resource_root = "resources/corrections/"
24 """Default resource path root (relative to ``default_resource_package``)
25 to use to locate header correction files."""
27 DETECTOR_MAX = 1
29 _const_map = {"detector_num": 0,
30 "boresight_rotation_coord": "sky",
31 "physical_filter": "empty",
32 "detector_group": "None",
33 "relative_humidity": None,
34 "pressure": None,
35 "temperature": None,
36 "focus_z": None,
37 }
38 """Constant mappings"""
40 _trivial_map = {
41 "boresight_rotation_angle": (["ROTPA", "ROTANGLE"], dict(default=np.nan, unit=u.deg)),
42 "detector_name": "CCD0",
43 "object": ("OBJECT", dict(default="UNKNOWN")),
44 "observation_id": "OBSID",
45 "observation_type": "IMGTYPE",
46 "science_program": ("PROGRAM", dict(default="unknown")),
47 "telescope": "TELESCOP",
48 }
49 """One-to-one mappings"""
51 extensions = dict(
52 )
54 @classmethod
55 def can_translate(cls, header, filename=None):
56 """Indicate whether this translation class can translate the
57 supplied header.
59 Parameters
60 ----------
61 header : `dict`-like
62 Header to convert to standardized form.
63 filename : `str`, optional
64 Name of file being translated.
66 Returns
67 -------
68 can : `bool`
69 `True` if the header is recognized by this class. `False`
70 otherwise.
71 """
73 return False # you must specialise this class
75 @cache_translation
76 def to_datetime_begin(self):
77 self._used_these_cards("MJD-BEG")
78 return Time(self._header["MJD-BEG"], scale="tai", format="mjd")
80 @cache_translation
81 def to_instrument(self):
82 return None # you must specialise this class
84 @cache_translation
85 def to_exposure_time(self):
86 # Docstring will be inherited. Property defined in properties.py
87 # Some data is missing a value for EXPTIME.
88 # Have to be careful we do not have circular logic when trying to
89 # guess
90 if self.is_key_ok("EXPTIME"): 90 ↛ 95line 90 didn't jump to line 95 because the condition on line 90 was always true
91 return self.quantity_from_card("EXPTIME", u.s)
93 # A missing or undefined EXPTIME is problematic. Set to -1
94 # to indicate that none was found.
95 self.log.warning("%s: Insufficient information to derive exposure time. Setting to -1.0s",
96 self._log_prefix)
97 return -1.0 * u.s
99 @cache_translation
100 def to_dark_time(self): # N.b. defining this suppresses a warning re setting from exptime
101 if "DARKTIME" in self._header: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true
102 darkTime = self._header["DARKTIME"]
103 self._used_these_cards("DARKTIME")
104 return (darkTime, dict(unit=u.s))
105 return self.to_exposure_time()
108class StarTrackerTranslator(RubinGenericCameraTranslator):
109 name = None # must be specialised
110 """Name of this translation class"""
112 supported_instrument = None # must be specialised
113 """Supports the Rubin Star Tracker instrument."""
115 @classmethod
116 def _is_startracker(cls, header, filename=None):
117 """Indicate whether the supplied header comes from a starTracker
119 Parameters
120 ----------
121 header : `dict`-like
122 Header to convert to standardized form.
123 filename : `str`, optional
124 Name of file being translated.
126 Returns
127 -------
128 (isStarTracker, camId) : (`True`, `int`) or (`False`, `None`)
129 isStarTracker is `True` iff header comes from a starTracker
130 camId is the cameraID, e.g. 101 for the wide-field startracker,
131 102 for the narrow-field startracker
132 """
133 if "INSTRUME" not in header or header["INSTRUME"] != "StarTracker" or "OBSID" not in header: 133 ↛ 134line 133 didn't jump to line 134 because the condition on line 133 was never true
134 return (False, None)
136 camId = int(header["OBSID"][2:5])
138 return (True, camId)
141class StarTrackerNarrowTranslator(StarTrackerTranslator):
142 name = "StarTrackerNarrow"
143 """Name of this translation class"""
145 supported_instrument = "StarTrackerNarrow"
146 """Supports the Rubin Star Tracker narrow-field instrument."""
148 _const_map = {"instrument": "StarTrackerNarrow",
149 "detector_serial": "00:0f:31:03:ae:60", # MAC address
150 }
152 @classmethod
153 def can_translate(cls, header, filename=None):
154 """Indicate whether this translation class can translate the
155 supplied header.
157 Parameters
158 ----------
159 header : `dict`-like
160 Header to convert to standardized form.
161 filename : `str`, optional
162 Name of file being translated.
164 Returns
165 -------
166 can : `bool`
167 `True` if the header is recognized by this class. `False`
168 otherwise.
169 """
171 isStarTracker, camId = cls._is_startracker(header, filename=None)
173 return isStarTracker and camId == 102
176class StarTrackerWideTranslator(StarTrackerTranslator):
177 name = "StarTrackerWide"
178 """Name of this translation class"""
180 supported_instrument = "StarTrackerWide"
181 """Supports the Rubin Star Tracker wide-field instrument."""
183 _const_map = {"instrument": "StarTrackerWide",
184 "detector_serial": "00:0f:31:03:60:c2", # MAC address
185 }
187 @classmethod
188 def can_translate(cls, header, filename=None):
189 """Indicate whether this translation class can translate the
190 supplied header.
192 Parameters
193 ----------
194 header : `dict`-like
195 Header to convert to standardized form.
196 filename : `str`, optional
197 Name of file being translated.
199 Returns
200 -------
201 can : `bool`
202 `True` if the header is recognized by this class. `False`
203 otherwise.
204 """
205 isStarTracker, camId = cls._is_startracker(header, filename=None)
207 return isStarTracker and camId == 101
210class StarTrackerFastTranslator(StarTrackerTranslator):
211 name = "StarTrackerFast"
212 """Name of this translation class"""
214 supported_instrument = "starTrackerFast"
215 """Supports the STARTRACKERFAST dome-seeing instrument."""
217 _const_map = {"instrument": "StarTrackerFast",
218 "detector_serial": "00:0F:31:03:3F:BA", # MAC address
219 }
221 @classmethod
222 def can_translate(cls, header, filename=None):
223 """Indicate whether this translation class can translate the
224 supplied header.
226 Parameters
227 ----------
228 header : `dict`-like
229 Header to convert to standardized form.
230 filename : `str`, optional
231 Name of file being translated.
233 Returns
234 -------
235 can : `bool`
236 `True` if the header is recognized by this class. `False`
237 otherwise.
238 """
239 isStarTracker, camId = cls._is_startracker(header, filename=None)
241 return isStarTracker and camId == 103
244def _register_translators() -> list[str]:
245 """Ensure that the translators are loaded.
247 When this function is imported we are guaranteed to also import the
248 translators which will automatically register themselves.
250 Returns
251 -------
252 translators : `list` [ `str` ]
253 The names of the translators provided by this package.
254 """
255 return [
256 StarTrackerNarrowTranslator.name,
257 StarTrackerWideTranslator.name,
258 StarTrackerFastTranslator.name,
259 ]