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

60 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-26 04:02 -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 @classmethod 

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

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

56 supplied header. 

57 

58 Looks for "COMCAM" instrument in case-insensitive manner but 

59 must be on LSST telescope. This avoids confusion with other 

60 telescopes using commissioning cameras. 

61 

62 Parameters 

63 ---------- 

64 header : `dict`-like 

65 Header to convert to standardized form. 

66 filename : `str`, optional 

67 Name of file being translated. 

68 

69 Returns 

70 ------- 

71 can : `bool` 

72 `True` if the header is recognized by this class. `False` 

73 otherwise. 

74 """ 

75 if "INSTRUME" in header and "TELESCOP" in header: 

76 telescope = header["TELESCOP"] 

77 instrument = header["INSTRUME"].lower() 

78 if instrument == "comcam" and telescope in (SIMONYI_TELESCOPE, "LSST"): 

79 return True 

80 telcode = header.get("TELCODE", None) 

81 # Some lab data from 2019 reports that it is LSST_CAMERA. 

82 if telcode == "CC" and instrument == "lsst_camera": 

83 return True 

84 

85 return False 

86 

87 @classmethod 

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

89 """Fix ComCam headers. 

90 

91 Notes 

92 ----- 

93 Fixes the following issues: 

94 

95 * If ComCam was in Chile, the FILTER is always empty (or unknown). 

96 * If LSST_NUM is missing it is filled in by looking at the CCDSLOT 

97 value and assuming that the ComCam detectors are fixed. 

98 * If ROTPA is missing or non-numeric, it is set to 0.0. 

99 

100 Corrections are reported as debug level log messages. 

101 

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

103 process. 

104 """ 

105 modified = False 

106 

107 # Calculate the standard label to use for log messages 

108 log_label = cls._construct_log_prefix(obsid, filename) 

109 

110 physical_filter = header.get("FILTER") 

111 if physical_filter in (None, "r", ""): 

112 # Create a translator since we need the date 

113 translator = cls(header) 

114 if physical_filter is None: 

115 header["FILTER"] = "unknown" 

116 physical_filter_str = "None" 

117 else: 

118 date = translator.to_datetime_begin() 

119 if date > COMCAM_TO_CHILE_DATE: 

120 header["FILTER"] = "empty" 

121 else: 

122 header["FILTER"] = "r_03" # it's currently 'r', which is a band not a physical_filter 

123 

124 physical_filter_str = f'"{physical_filter}"' 

125 

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

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

128 modified = True 

129 

130 if header.get("INSTRUME") == "LSST_CAMERA": 

131 header["INSTRUME"] = "ComCam" # Must match the can_translate check above 

132 modified = True 

133 log.debug("%s: Correct instrument header for ComCam", log_label) 

134 

135 if "LSST_NUM" not in header: 

136 slot = header.get("CCDSLOT", None) 

137 if slot in DETECTOR_SERIALS: 

138 header["LSST_NUM"] = DETECTOR_SERIALS[slot] 

139 modified = True 

140 log.debug("%s: Set LSST_NUM to %s", log_label, header["LSST_NUM"]) 

141 

142 if "ROTPA" not in header or not isinstance(header["ROTPA"], Number): 

143 header["ROTPA"] = 0.0 

144 log.warning("Missing ROTPA in header - replacing with 0.0") 

145 modified = True 

146 

147 return modified 

148 

149 def _is_on_mountain(self): 

150 """Indicate whether these data are coming from the instrument 

151 installed on the mountain. 

152 Returns 

153 ------- 

154 is : `bool` 

155 `True` if instrument is on the mountain. 

156 

157 Notes 

158 ----- 

159 TODO: DM-33387 This is currently a terrible hack and MUST be removed 

160 once CAP-807 and CAP-808 are done. 

161 Until then, ALL non-calib ComCam data will look like it is on sky. 

162 """ 

163 return True