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

89 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-26 09:34 +0000

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 

245 

246 

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

248 """Ensure that the translators are loaded. 

249 

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

251 translators which will automatically register themselves. 

252 

253 Returns 

254 ------- 

255 translators : `list` [ `str` ] 

256 The names of the translators provided by this package. 

257 """ 

258 return [ 

259 StarTrackerNarrowTranslator.name, 

260 StarTrackerWideTranslator.name, 

261 StarTrackerFastTranslator.name, 

262 ]