Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of astro_metadata_translator. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the LICENSE file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12import os.path 

13import unittest 

14from astropy.time import Time 

15 

16from astro_metadata_translator import FitsTranslator, StubTranslator, ObservationInfo 

17 

18TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

19 

20 

21class InstrumentTestTranslator(FitsTranslator, StubTranslator): 

22 """Simple FITS-like translator to test the infrastructure""" 

23 

24 # Needs a name to be registered 

25 name = "TestTranslator" 

26 

27 # Indicate the instrument this class understands 

28 supported_instrument = "SCUBA_test" 

29 

30 # Some new mappings, including an override 

31 _trivial_map = {"telescope": "TELCODE", 

32 "exposure_id": "EXPID", 

33 "relative_humidity": "HUMIDITY", 

34 "detector_name": "DETNAME", 

35 "observation_id": "OBSID"} 

36 

37 # Add translator method to test joining 

38 def to_physical_filter(self): 

39 return self._join_keyword_values(["DETNAME", "HUMIDITY"], delim="_") 

40 

41 

42class MissingMethodsTranslator(FitsTranslator): 

43 """Translator class that does not implement all the methods.""" 

44 pass 

45 

46 

47class TranslatorTestCase(unittest.TestCase): 

48 

49 def setUp(self): 

50 # Known simple header 

51 self.header = {"TELESCOP": "JCMT", 

52 "TELCODE": "LSST", 

53 "INSTRUME": "SCUBA_test", 

54 "DATE-OBS": "2000-01-01T01:00:01.500", 

55 "DATE-END": "2000-01-01T02:00:01.500", 

56 "OBSGEO-X": "-5464588.84421314", 

57 "OBSGEO-Y": "-2493000.19137644", 

58 "OBSGEO-Z": "2150653.35350771", 

59 "OBSID": "20000101_00002", 

60 "EXPID": "22", # Should cast to a number 

61 "DETNAME": 76, # Should cast to a string 

62 "HUMIDITY": "55", # Should cast to a float 

63 "BAZ": "bar"} 

64 

65 def test_manual_translation(self): 

66 

67 header = self.header 

68 translator = FitsTranslator(header) 

69 

70 # Treat the header as standard FITS 

71 self.assertFalse(FitsTranslator.can_translate(header)) 

72 self.assertEqual(translator.to_telescope(), "JCMT") 

73 self.assertEqual(translator.to_instrument(), "SCUBA_test") 

74 self.assertEqual(translator.to_datetime_begin(), 

75 Time(header["DATE-OBS"], format="isot")) 

76 

77 # This class will issue warnings 

78 with self.assertLogs("astro_metadata_translator") as cm: 

79 class InstrumentTestTranslatorExtras(InstrumentTestTranslator): 

80 """Version of InstrumentTestTranslator with unexpected 

81 fields.""" 

82 name = "InstrumentTestTranslatorExtras" 

83 _trivial_map = {"foobar": "BAZ"} 

84 _const_map = {"format": "HDF5"} 

85 

86 self.assertIn("Unexpected trivial", cm.output[0]) 

87 self.assertIn("Unexpected constant", cm.output[1]) 

88 

89 # Use the special test translator instead 

90 translator = InstrumentTestTranslatorExtras(header) 

91 self.assertTrue(InstrumentTestTranslator.can_translate(header)) 

92 self.assertEqual(translator.to_telescope(), "LSST") 

93 self.assertEqual(translator.to_instrument(), "SCUBA_test") 

94 self.assertEqual(translator.to_format(), "HDF5") 

95 self.assertEqual(translator.to_foobar(), "bar") 

96 

97 def test_translator(self): 

98 header = self.header 

99 

100 # Specify a translation class 

101 with self.assertWarns(UserWarning): 

102 # Since the translator is incomplete it should issue warnings 

103 v1 = ObservationInfo(header, translator_class=InstrumentTestTranslator) 

104 self.assertEqual(v1.instrument, "SCUBA_test") 

105 self.assertEqual(v1.telescope, "LSST") 

106 self.assertEqual(v1.exposure_id, 22) 

107 self.assertIsInstance(v1.exposure_id, int) 

108 self.assertEqual(v1.detector_name, "76") 

109 self.assertEqual(v1.relative_humidity, 55.0) 

110 self.assertIsInstance(v1.relative_humidity, float) 

111 self.assertEqual(v1.physical_filter, "76_55") 

112 

113 # Now automated class 

114 with self.assertWarns(UserWarning): 

115 # Since the translator is incomplete it should issue warnings 

116 v1 = ObservationInfo(header) 

117 self.assertEqual(v1.instrument, "SCUBA_test") 

118 self.assertEqual(v1.telescope, "LSST") 

119 

120 location = v1.location.to_geodetic() 

121 self.assertAlmostEqual(location.height.to("m").to_value(), 4123.0, places=1) 

122 

123 # Check that headers have been removed 

124 new_hdr = v1.stripped_header() 

125 self.assertNotIn("INSTRUME", new_hdr) 

126 self.assertNotIn("OBSGEO-X", new_hdr) 

127 self.assertIn("TELESCOP", new_hdr) 

128 

129 # Check the list of cards that were used 

130 used = v1.cards_used 

131 self.assertIn("INSTRUME", used) 

132 self.assertIn("OBSGEO-Y", used) 

133 self.assertNotIn("TELESCOP", used) 

134 

135 # Stringification 

136 summary = str(v1) 

137 self.assertIn("datetime_begin", summary) 

138 

139 # Create with a subset of properties 

140 v2 = ObservationInfo(header, translator_class=InstrumentTestTranslator, 

141 subset={"telescope", "datetime_begin", "exposure_group"}) 

142 

143 self.assertEqual(v2.telescope, v1.telescope) 

144 self.assertEqual(v2.datetime_begin, v2.datetime_begin) 

145 self.assertIsNone(v2.datetime_end) 

146 self.assertIsNone(v2.location) 

147 self.assertIsNone(v2.observation_id) 

148 

149 def test_corrections(self): 

150 """Apply corrections before translation.""" 

151 header = self.header 

152 

153 # Specify a translation class 

154 with self.assertWarns(UserWarning): 

155 # Since the translator is incomplete it should issue warnings 

156 v1 = ObservationInfo(header, translator_class=InstrumentTestTranslator, 

157 search_path=[os.path.join(TESTDIR, "data", "corrections")]) 

158 

159 # These values should match the expected translation 

160 self.assertEqual(v1.instrument, "SCUBA_test") 

161 self.assertEqual(v1.detector_name, "76") 

162 self.assertEqual(v1.relative_humidity, 55.0) 

163 self.assertIsInstance(v1.relative_humidity, float) 

164 self.assertEqual(v1.physical_filter, "76_55") 

165 

166 # These two should be the "corrected" values 

167 self.assertEqual(v1.telescope, "AuxTel") 

168 self.assertEqual(v1.exposure_id, 42) 

169 

170 def test_failures(self): 

171 header = {} 

172 

173 with self.assertRaises(TypeError): 

174 ObservationInfo(header, translator_class=ObservationInfo) 

175 

176 with self.assertRaises(ValueError): 

177 ObservationInfo(header, translator_class=InstrumentTestTranslator, 

178 subset={"definitely_not_known"}) 

179 

180 with self.assertRaises(ValueError): 

181 ObservationInfo(header, translator_class=InstrumentTestTranslator, 

182 required={"definitely_not_known"}) 

183 

184 with self.assertLogs("astro_metadata_translator"): 

185 with self.assertWarns(UserWarning): 

186 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=False) 

187 

188 with self.assertRaises(KeyError): 

189 with self.assertWarns(UserWarning): 

190 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=True) 

191 

192 with self.assertLogs("astro_metadata_translator"): 

193 with self.assertWarns(UserWarning): 

194 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=False, 

195 filename="testfile1") 

196 

197 with self.assertRaises(KeyError): 

198 with self.assertWarns(UserWarning): 

199 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=True, 

200 filename="testfile2") 

201 

202 with self.assertRaises(NotImplementedError): 

203 with self.assertLogs("astro_metadata_translator", level="WARN"): 

204 ObservationInfo(header, translator_class=MissingMethodsTranslator) 

205 

206 with self.assertRaises(KeyError): 

207 with self.assertWarns(UserWarning): 

208 with self.assertLogs("astro_metadata_translator", level="WARN"): 

209 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=False, 

210 required={"boresight_airmass"}) 

211 

212 

213if __name__ == "__main__": 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true

214 unittest.main()