Coverage for python/lsst/obs/lsst/translators/comCam.py: 29%

61 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-10 03:26 -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. 

10 

11"""Metadata translation code for the LSST Commissioning Camera""" 

12 

13__all__ = ("LsstComCamTranslator", ) 

14 

15import logging 

16from numbers import Number 

17 

18from astropy.time import Time 

19from .lsstCam import LsstCamTranslator 

20from .lsst import SIMONYI_TELESCOPE 

21 

22log = logging.getLogger(__name__) 

23 

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} 

35 

36# Date ComCam left Tucson bound for Chile 

37COMCAM_TO_CHILE_DATE = Time("2020-03-13T00:00", format="isot", scale="utc") 

38 

39 

40class LsstComCamTranslator(LsstCamTranslator): 

41 """Metadata translation for the LSST Commissioning Camera.""" 

42 

43 name = "LSSTComCam" 

44 """Name of this translation class""" 

45 

46 _const_map = { 

47 "instrument": "LSSTComCam", 

48 } 

49 

50 # Use the comCam raft definition 

51 cameraPolicyFile = "policy/comCam.yaml" 

52 

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 

56 

57 @classmethod 

58 def can_translate(cls, header, filename=None): 

59 """Indicate whether this translation class can translate the 

60 supplied header. 

61 

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. 

65 

66 Parameters 

67 ---------- 

68 header : `dict`-like 

69 Header to convert to standardized form. 

70 filename : `str`, optional 

71 Name of file being translated. 

72 

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 

88 

89 return False 

90 

91 @classmethod 

92 def fix_header(cls, header, instrument, obsid, filename=None): 

93 """Fix ComCam headers. 

94 

95 Notes 

96 ----- 

97 Fixes the following issues: 

98 

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. 

103 

104 Corrections are reported as debug level log messages. 

105 

106 See `~astro_metadata_translator.fix_header` for details of the general 

107 process. 

108 """ 

109 modified = False 

110 

111 # Calculate the standard label to use for log messages 

112 log_label = cls._construct_log_prefix(obsid, filename) 

113 

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 

127 

128 physical_filter_str = f'"{physical_filter}"' 

129 

130 log.warning("%s: replaced FILTER %s with \"%s\"", 

131 log_label, physical_filter_str, header["FILTER"]) 

132 modified = True 

133 

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) 

138 

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"]) 

145 

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 

150 

151 return modified 

152 

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. 

160 

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