Coverage for python/lsst/obs/lsst/translators/comCamSim.py: 28%

66 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-01 17:14 -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 Simulated LSST Commissioning Camera""" 

12 

13__all__ = ("LsstComCamSimTranslator", ) 

14 

15import logging 

16import warnings 

17 

18import astropy.utils.exceptions 

19from astropy.coordinates import AltAz 

20from astro_metadata_translator import cache_translation 

21 

22from .lsstCam import LsstCamTranslator 

23from .lsst import SIMONYI_TELESCOPE 

24 

25log = logging.getLogger(__name__) 

26 

27 

28class LsstComCamSimTranslator(LsstCamTranslator): 

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

30 

31 name = "LSSTComCamSim" 

32 """Name of this translation class""" 

33 

34 _const_map = { 

35 "instrument": "LSSTComCamSim", 

36 } 

37 

38 cameraPolicyFile = 'policy/comCamSim.yaml' 

39 

40 @classmethod 

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

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

43 supplied header. 

44 

45 Looks for "COMCAMSIM" instrument in case-insensitive manner but 

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

47 telescopes using commissioning cameras. 

48 

49 Parameters 

50 ---------- 

51 header : `dict`-like 

52 Header to convert to standardized form. 

53 filename : `str`, optional 

54 Name of file being translated. 

55 

56 Returns 

57 ------- 

58 can : `bool` 

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

60 otherwise. 

61 """ 

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

63 telescope = header["TELESCOP"] 

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

65 if instrument == "comcamsim" and telescope in (SIMONYI_TELESCOPE, "LSST"): 

66 return True 

67 

68 return False 

69 

70 @classmethod 

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

72 """Fix Simulated ComCam headers. 

73 

74 Notes 

75 ----- 

76 Content will be added as needed. 

77 

78 Corrections are reported as debug level log messages. 

79 

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

81 process. 

82 """ 

83 modified = False 

84 

85 # Calculate the standard label to use for log messages 

86 log_label = cls._construct_log_prefix(obsid, filename) 

87 

88 # Some simulated files lack RASTART/DECSTART etc headers. Since these 

89 # are simulated they can be populated directly from the RA/DEC headers. 

90 synced_radec = False 

91 for key in ("RA", "DEC"): 

92 for time in ("START", "END"): 

93 time_key = f"{key}{time}" 

94 if not header.get(time_key): 

95 if (value := header.get(key)): 

96 header[time_key] = value 

97 synced_radec = True 

98 if synced_radec: 

99 modified = True 

100 log.debug("%s: Synced RASTART/RAEND/DECSTART/DECEND headers with RA/DEC headers", log_label) 

101 

102 if not header.get("RADESYS") and header.get("RA") and header.get("DEC"): 

103 header["RADESYS"] = "ICRS" 

104 log.debug("%s: Forcing undefined RADESYS to '%s'", log_label, header["RADESYS"]) 

105 modified = True 

106 

107 if not header.get("TELCODE"): 

108 if camcode := header.get("CAMCODE"): 

109 header["TELCODE"] = camcode 

110 modified = True 

111 log.debug("%s: Setting TELCODE header from CAMCODE header", log_label) 

112 else: 

113 # Get the code from the OBSID. 

114 code, _ = obsid.split("_", 1) 

115 header["TELCODE"] = code 

116 modified = True 

117 log.debug("%s: Determining telescope code of %s from OBSID", log_label, code) 

118 

119 return modified 

120 

121 def _is_on_mountain(self): 

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

123 installed on the mountain. 

124 Returns 

125 ------- 

126 is : `bool` 

127 `True` if instrument is on the mountain. 

128 

129 Notes 

130 ----- 

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

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

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

134 """ 

135 return True 

136 

137 @cache_translation 

138 def to_altaz_begin(self): 

139 # Tries to calculate the value. Simulated files for ops-rehearsal 3 

140 # did not have the AZ/EL headers defined. 

141 if self.are_keys_ok(["ELSTART", "AZSTART"]): 

142 return super().to_altaz_begin() 

143 

144 # Calculate it from the RA/Dec and time. 

145 # The time is not consistent with the HASTART/AMSTART values. 

146 # This means that the elevation may well come out negative. 

147 if self.are_keys_ok(["RA", "DEC"]): 

148 # Derive from RADec in absence of any other information 

149 radec = self.to_tracking_radec() 

150 if radec is not None: 

151 # This can trigger warnings because of the future dates 

152 with warnings.catch_warnings(): 

153 warnings.simplefilter("ignore", category=astropy.utils.exceptions.AstropyWarning) 

154 altaz = radec.transform_to(AltAz()) 

155 return altaz 

156 

157 return None