Coverage for python/lsst/obs/rubinGenericCamera/translator.py: 74%

87 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 04:19 -0700

1import os 

2 

3import numpy as np 

4import astropy.units as u 

5from astropy.time import Time 

6 

7from astro_metadata_translator import cache_translation 

8from lsst.obs.lsst.translators.lsst import LsstBaseTranslator 

9 

10from lsst.utils import getPackageDir 

11 

12__all__ = ["StarTrackerNarrowTranslator", "StarTrackerWideTranslator", "StarTrackerFastTranslator",] 

13 

14 

15class RubinGenericCameraTranslator(LsstBaseTranslator): 

16 """Metadata translator for Rubin Generic Camera FITS headers""" 

17 

18 name = None # you must specialise this class 

19 """Name of this translation class""" 

20 

21 supported_instrument = None # you must specialise this class 

22 """Supports the LSST Generic Camera instrument.""" 

23 

24 default_search_path = os.path.join(getPackageDir("obs_rubinGenericCamera"), "corrections") 

25 """Default search path to use to locate header correction files.""" 

26 

27 default_resource_root = os.path.join(getPackageDir("obs_rubinGenericCamera"), "corrections") 

28 """Default resource path root to use to locate header correction files.""" 

29 

30 DETECTOR_MAX = 1 

31 

32 _const_map = {"detector_num": 0, 

33 "boresight_rotation_coord": "sky", 

34 "physical_filter": "empty", 

35 "detector_group": "None", 

36 "relative_humidity": None, 

37 "pressure": None, 

38 "temperature": None, 

39 "focus_z": None, 

40 } 

41 """Constant mappings""" 

42 

43 _trivial_map = { 

44 "boresight_rotation_angle": (["ROTPA", "ROTANGLE"], dict(default=np.NaN, unit=u.deg)), 

45 "detector_name": "CCD0", 

46 "object": ("OBJECT", dict(default="UNKNOWN")), 

47 "observation_id": "OBSID", 

48 "observation_type": "IMGTYPE", 

49 "science_program": ("PROGRAM", dict(default="unknown")), 

50 "telescope": "TELESCOP", 

51 } 

52 """One-to-one mappings""" 

53 

54 extensions = dict( 

55 ) 

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 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 

76 return False # you must specialise this class 

77 

78 @cache_translation 

79 def to_datetime_begin(self): 

80 self._used_these_cards("MJD-BEG") 

81 return Time(self._header["MJD-BEG"], scale="tai", format="mjd") 

82 

83 @cache_translation 

84 def to_instrument(self): 

85 return None # you must specialise this class 

86 

87 @cache_translation 

88 def to_exposure_time(self): 

89 # Docstring will be inherited. Property defined in properties.py 

90 # Some data is missing a value for EXPTIME. 

91 # Have to be careful we do not have circular logic when trying to 

92 # guess 

93 if self.is_key_ok("EXPTIME"): 

94 return self.quantity_from_card("EXPTIME", u.s) 

95 

96 # A missing or undefined EXPTIME is problematic. Set to -1 

97 # to indicate that none was found. 

98 self.log.warning("%s: Insufficient information to derive exposure time. Setting to -1.0s", 

99 self._log_prefix) 

100 return -1.0 * u.s 

101 

102 @cache_translation 

103 def to_dark_time(self): # N.b. defining this suppresses a warning re setting from exptime 

104 if "DARKTIME" in self._header: 

105 darkTime = self._header["DARKTIME"] 

106 self._used_these_cards("DARKTIME") 

107 return (darkTime, dict(unit=u.s)) 

108 return self.to_exposure_time() 

109 

110 

111class StarTrackerTranslator(RubinGenericCameraTranslator): 

112 name = None # must be specialised 

113 """Name of this translation class""" 

114 

115 supported_instrument = None # must be specialised 

116 """Supports the Rubin Star Tracker instrument.""" 

117 

118 @classmethod 

119 def _is_startracker(cls, header, filename=None): 

120 """Indicate whether the supplied header comes from a starTracker 

121 

122 Parameters 

123 ---------- 

124 header : `dict`-like 

125 Header to convert to standardized form. 

126 filename : `str`, optional 

127 Name of file being translated. 

128 

129 Returns 

130 ------- 

131 (isStarTracker, camId) : (`True`, `int`) or (`False`, `None`) 

132 isStarTracker is `True` iff header comes from a starTracker 

133 camId is the cameraID, e.g. 101 for the wide-field startracker, 

134 102 for the narrow-field startracker 

135 """ 

136 if "INSTRUME" not in header or header["INSTRUME"] != "StarTracker" or "OBSID" not in header: 

137 return (False, None) 

138 

139 camId = int(header["OBSID"][2:5]) 

140 

141 return (True, camId) 

142 

143 

144class StarTrackerNarrowTranslator(StarTrackerTranslator): 

145 name = "StarTrackerNarrow" 

146 """Name of this translation class""" 

147 

148 supported_instrument = "StarTrackerNarrow" 

149 """Supports the Rubin Star Tracker narrow-field instrument.""" 

150 

151 _const_map = {"instrument": "StarTrackerNarrow", 

152 "detector_serial": "00:0f:31:03:ae:60", # MAC address 

153 } 

154 

155 @classmethod 

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

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

158 supplied header. 

159 

160 Parameters 

161 ---------- 

162 header : `dict`-like 

163 Header to convert to standardized form. 

164 filename : `str`, optional 

165 Name of file being translated. 

166 

167 Returns 

168 ------- 

169 can : `bool` 

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

171 otherwise. 

172 """ 

173 

174 isStarTracker, camId = cls._is_startracker(header, filename=None) 

175 

176 return isStarTracker and camId == 102 

177 

178 

179class StarTrackerWideTranslator(StarTrackerTranslator): 

180 name = "StarTrackerWide" 

181 """Name of this translation class""" 

182 

183 supported_instrument = "StarTrackerWide" 

184 """Supports the Rubin Star Tracker wide-field instrument.""" 

185 

186 _const_map = {"instrument": "StarTrackerWide", 

187 "detector_serial": "00:0f:31:03:60:c2", # MAC address 

188 } 

189 

190 @classmethod 

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

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

193 supplied header. 

194 

195 Parameters 

196 ---------- 

197 header : `dict`-like 

198 Header to convert to standardized form. 

199 filename : `str`, optional 

200 Name of file being translated. 

201 

202 Returns 

203 ------- 

204 can : `bool` 

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

206 otherwise. 

207 """ 

208 isStarTracker, camId = cls._is_startracker(header, filename=None) 

209 

210 return isStarTracker and camId == 101 

211 

212 

213class StarTrackerFastTranslator(StarTrackerTranslator): 

214 name = "StarTrackerFast" 

215 """Name of this translation class""" 

216 

217 supported_instrument = "starTrackerFast" 

218 """Supports the STARTRACKERFAST dome-seeing instrument.""" 

219 

220 _const_map = {"instrument": "StarTrackerFast", 

221 "detector_serial": "00:0F:31:03:3F:BA", # MAC address 

222 } 

223 

224 @classmethod 

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

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

227 supplied header. 

228 

229 Parameters 

230 ---------- 

231 header : `dict`-like 

232 Header to convert to standardized form. 

233 filename : `str`, optional 

234 Name of file being translated. 

235 

236 Returns 

237 ------- 

238 can : `bool` 

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

240 otherwise. 

241 """ 

242 isStarTracker, camId = cls._is_startracker(header, filename=None) 

243 

244 return isStarTracker and camId == 103