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

87 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-09 11:25 +0000

1import numpy as np 

2import astropy.units as u 

3from astropy.time import Time 

4 

5from astro_metadata_translator import cache_translation 

6from lsst.obs.lsst.translators.lsst import LsstBaseTranslator 

7 

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

9 

10 

11class RubinGenericCameraTranslator(LsstBaseTranslator): 

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

13 

14 name = None # you must specialise this class 

15 """Name of this translation class""" 

16 

17 supported_instrument = None # you must specialise this class 

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

19 

20 default_resource_package = "lsst.obs.rubinGenericCamera" 

21 """Package to use to locate the header correction resources.""" 

22 

23 default_resource_root = "resources/corrections/" 

24 """Default resource path root (relative to ``default_resource_package``) 

25 to use to locate header correction files.""" 

26 

27 DETECTOR_MAX = 1 

28 

29 _const_map = {"detector_num": 0, 

30 "boresight_rotation_coord": "sky", 

31 "physical_filter": "empty", 

32 "detector_group": "None", 

33 "relative_humidity": None, 

34 "pressure": None, 

35 "temperature": None, 

36 "focus_z": None, 

37 } 

38 """Constant mappings""" 

39 

40 _trivial_map = { 

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

42 "detector_name": "CCD0", 

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

44 "observation_id": "OBSID", 

45 "observation_type": "IMGTYPE", 

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

47 "telescope": "TELESCOP", 

48 } 

49 """One-to-one mappings""" 

50 

51 extensions = dict( 

52 ) 

53 

54 @classmethod 

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

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

57 supplied header. 

58 

59 Parameters 

60 ---------- 

61 header : `dict`-like 

62 Header to convert to standardized form. 

63 filename : `str`, optional 

64 Name of file being translated. 

65 

66 Returns 

67 ------- 

68 can : `bool` 

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

70 otherwise. 

71 """ 

72 

73 return False # you must specialise this class 

74 

75 @cache_translation 

76 def to_datetime_begin(self): 

77 self._used_these_cards("MJD-BEG") 

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

79 

80 @cache_translation 

81 def to_instrument(self): 

82 return None # you must specialise this class 

83 

84 @cache_translation 

85 def to_exposure_time(self): 

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

87 # Some data is missing a value for EXPTIME. 

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

89 # guess 

90 if self.is_key_ok("EXPTIME"): 90 ↛ 95line 90 didn't jump to line 95 because the condition on line 90 was always true

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

92 

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

94 # to indicate that none was found. 

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

96 self._log_prefix) 

97 return -1.0 * u.s 

98 

99 @cache_translation 

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

101 if "DARKTIME" in self._header: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true

102 darkTime = self._header["DARKTIME"] 

103 self._used_these_cards("DARKTIME") 

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

105 return self.to_exposure_time() 

106 

107 

108class StarTrackerTranslator(RubinGenericCameraTranslator): 

109 name = None # must be specialised 

110 """Name of this translation class""" 

111 

112 supported_instrument = None # must be specialised 

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

114 

115 @classmethod 

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

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

118 

119 Parameters 

120 ---------- 

121 header : `dict`-like 

122 Header to convert to standardized form. 

123 filename : `str`, optional 

124 Name of file being translated. 

125 

126 Returns 

127 ------- 

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

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

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

131 102 for the narrow-field startracker 

132 """ 

133 if "INSTRUME" not in header or header["INSTRUME"] != "StarTracker" or "OBSID" not in header: 133 ↛ 134line 133 didn't jump to line 134 because the condition on line 133 was never true

134 return (False, None) 

135 

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

137 

138 return (True, camId) 

139 

140 

141class StarTrackerNarrowTranslator(StarTrackerTranslator): 

142 name = "StarTrackerNarrow" 

143 """Name of this translation class""" 

144 

145 supported_instrument = "StarTrackerNarrow" 

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

147 

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

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

150 } 

151 

152 @classmethod 

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

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

155 supplied header. 

156 

157 Parameters 

158 ---------- 

159 header : `dict`-like 

160 Header to convert to standardized form. 

161 filename : `str`, optional 

162 Name of file being translated. 

163 

164 Returns 

165 ------- 

166 can : `bool` 

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

168 otherwise. 

169 """ 

170 

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

172 

173 return isStarTracker and camId == 102 

174 

175 

176class StarTrackerWideTranslator(StarTrackerTranslator): 

177 name = "StarTrackerWide" 

178 """Name of this translation class""" 

179 

180 supported_instrument = "StarTrackerWide" 

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

182 

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

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

185 } 

186 

187 @classmethod 

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

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

190 supplied header. 

191 

192 Parameters 

193 ---------- 

194 header : `dict`-like 

195 Header to convert to standardized form. 

196 filename : `str`, optional 

197 Name of file being translated. 

198 

199 Returns 

200 ------- 

201 can : `bool` 

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

203 otherwise. 

204 """ 

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

206 

207 return isStarTracker and camId == 101 

208 

209 

210class StarTrackerFastTranslator(StarTrackerTranslator): 

211 name = "StarTrackerFast" 

212 """Name of this translation class""" 

213 

214 supported_instrument = "starTrackerFast" 

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

216 

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

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

219 } 

220 

221 @classmethod 

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

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

224 supplied header. 

225 

226 Parameters 

227 ---------- 

228 header : `dict`-like 

229 Header to convert to standardized form. 

230 filename : `str`, optional 

231 Name of file being translated. 

232 

233 Returns 

234 ------- 

235 can : `bool` 

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

237 otherwise. 

238 """ 

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

240 

241 return isStarTracker and camId == 103 

242 

243 

244def _register_translators() -> list[str]: 

245 """Ensure that the translators are loaded. 

246 

247 When this function is imported we are guaranteed to also import the 

248 translators which will automatically register themselves. 

249 

250 Returns 

251 ------- 

252 translators : `list` [ `str` ] 

253 The names of the translators provided by this package. 

254 """ 

255 return [ 

256 StarTrackerNarrowTranslator.name, 

257 StarTrackerWideTranslator.name, 

258 StarTrackerFastTranslator.name, 

259 ]