Coverage for python/lsst/obs/lsst/translators/comCam.py: 29%
61 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-03 03:57 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-03 03:57 -0700
1# This file is currently part of obs_lsst but is written to allow it
2# to be migrated to the astro_metadata_translator package at a later date.
3#
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the LICENSE file in this directory for details of code ownership.
7#
8# Use of this source code is governed by a 3-clause BSD-style
9# license that can be found in the LICENSE file.
11"""Metadata translation code for the LSST Commissioning Camera"""
13__all__ = ("LsstComCamTranslator", )
15import logging
16from numbers import Number
18from astropy.time import Time
19from .lsstCam import LsstCamTranslator
20from .lsst import SIMONYI_TELESCOPE
22log = logging.getLogger(__name__)
24DETECTOR_SERIALS = {
25 "S00": "ITL-3800C-229",
26 "S01": "ITL-3800C-251",
27 "S02": "ITL-3800C-215",
28 "S10": "ITL-3800C-326",
29 "S11": "ITL-3800C-283",
30 "S12": "ITL-3800C-243",
31 "S20": "ITL-3800C-319",
32 "S21": "ITL-3800C-209",
33 "S22": "ITL-3800C-206",
34}
36# Date ComCam left Tucson bound for Chile
37COMCAM_TO_CHILE_DATE = Time("2020-03-13T00:00", format="isot", scale="utc")
40class LsstComCamTranslator(LsstCamTranslator):
41 """Metadata translation for the LSST Commissioning Camera."""
43 name = "LSSTComCam"
44 """Name of this translation class"""
46 _const_map = {
47 "instrument": "LSSTComCam",
48 }
50 # Use the comCam raft definition
51 cameraPolicyFile = "policy/comCam.yaml"
53 # Date (YYYYMM) the camera changes from using lab day_offset (Pacific time)
54 # to summit day_offset (12 hours).
55 _CAMERA_SHIP_DATE = 202003
57 @classmethod
58 def can_translate(cls, header, filename=None):
59 """Indicate whether this translation class can translate the
60 supplied header.
62 Looks for "COMCAM" instrument in case-insensitive manner but
63 must be on LSST telescope. This avoids confusion with other
64 telescopes using commissioning cameras.
66 Parameters
67 ----------
68 header : `dict`-like
69 Header to convert to standardized form.
70 filename : `str`, optional
71 Name of file being translated.
73 Returns
74 -------
75 can : `bool`
76 `True` if the header is recognized by this class. `False`
77 otherwise.
78 """
79 if "INSTRUME" in header and "TELESCOP" in header:
80 telescope = header["TELESCOP"]
81 instrument = header["INSTRUME"].lower()
82 if instrument == "comcam" and telescope in (SIMONYI_TELESCOPE, "LSST"):
83 return True
84 telcode = header.get("TELCODE", None)
85 # Some lab data from 2019 reports that it is LSST_CAMERA.
86 if telcode == "CC" and instrument == "lsst_camera":
87 return True
89 return False
91 @classmethod
92 def fix_header(cls, header, instrument, obsid, filename=None):
93 """Fix ComCam headers.
95 Notes
96 -----
97 Fixes the following issues:
99 * If ComCam was in Chile, the FILTER is always empty (or unknown).
100 * If LSST_NUM is missing it is filled in by looking at the CCDSLOT
101 value and assuming that the ComCam detectors are fixed.
102 * If ROTPA is missing or non-numeric, it is set to 0.0.
104 Corrections are reported as debug level log messages.
106 See `~astro_metadata_translator.fix_header` for details of the general
107 process.
108 """
109 modified = False
111 # Calculate the standard label to use for log messages
112 log_label = cls._construct_log_prefix(obsid, filename)
114 physical_filter = header.get("FILTER")
115 if physical_filter in (None, "r", ""):
116 # Create a translator since we need the date
117 translator = cls(header)
118 if physical_filter is None:
119 header["FILTER"] = "unknown"
120 physical_filter_str = "None"
121 else:
122 date = translator.to_datetime_begin()
123 if date > COMCAM_TO_CHILE_DATE:
124 header["FILTER"] = "empty"
125 else:
126 header["FILTER"] = "r_03" # it's currently 'r', which is a band not a physical_filter
128 physical_filter_str = f'"{physical_filter}"'
130 log.warning("%s: replaced FILTER %s with \"%s\"",
131 log_label, physical_filter_str, header["FILTER"])
132 modified = True
134 if header.get("INSTRUME") == "LSST_CAMERA":
135 header["INSTRUME"] = "ComCam" # Must match the can_translate check above
136 modified = True
137 log.debug("%s: Correct instrument header for ComCam", log_label)
139 if "LSST_NUM" not in header:
140 slot = header.get("CCDSLOT", None)
141 if slot in DETECTOR_SERIALS:
142 header["LSST_NUM"] = DETECTOR_SERIALS[slot]
143 modified = True
144 log.debug("%s: Set LSST_NUM to %s", log_label, header["LSST_NUM"])
146 if "ROTPA" not in header or not isinstance(header["ROTPA"], Number):
147 header["ROTPA"] = 0.0
148 log.warning("Missing ROTPA in header - replacing with 0.0")
149 modified = True
151 return modified
153 def _is_on_mountain(self):
154 """Indicate whether these data are coming from the instrument
155 installed on the mountain.
156 Returns
157 -------
158 is : `bool`
159 `True` if instrument is on the mountain.
161 Notes
162 -----
163 TODO: DM-33387 This is currently a terrible hack and MUST be removed
164 once CAP-807 and CAP-808 are done.
165 Until then, ALL non-calib ComCam data will look like it is on sky.
166 """
167 return True