Coverage for tests/test_makeRawVisitInfo.py: 13%

Shortcuts 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

183 statements  

1# This file is part of obs_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

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

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

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import math 

23import unittest 

24 

25import astropy.coordinates 

26import astropy.units 

27 

28import lsst.utils.tests 

29import lsst.pex.exceptions 

30from lsst.daf.base import DateTime, PropertySet 

31from lsst.obs.base import MakeRawVisitInfo 

32from lsst.geom import degrees 

33 

34 

35class SimpleMakeRawVisitInfo(MakeRawVisitInfo): 

36 

37 def getDateAvg(self, md, exposureTime): 

38 """Return date at the middle of the exposure""" 

39 dateObs = self.popIsoDate(md, "DATE-OBS") 

40 return self.offsetDate(dateObs, 0.5*exposureTime) 

41 

42 

43def getMetadata(keyValDict): 

44 md = PropertySet() 

45 for key, val in keyValDict.items(): 

46 md.set(key, val) 

47 return md 

48 

49 

50class VisitInfoTestCase(lsst.utils.tests.TestCase): 

51 """Test lsst.afw.image.VisitInfo, a simple struct-like class""" 

52 

53 def setUp(self): 

54 self.makeRawVisitInfo = SimpleMakeRawVisitInfo() 

55 

56 def testMakeRawVisitInfo(self): 

57 """Test base class functor MakeRawVisitInfo 

58 

59 The base class only sets date and exposureTime fields, 

60 reads DATE-OBS, TIMESYS and EXPTIME, 

61 and deletes DATE-OBS and EXPTIME, but not TIMESYS. 

62 """ 

63 exposureTime = 6.2 # arbitrary value in seconds 

64 date = DateTime("2001-01-02T03:04:05.123456789", DateTime.TAI) 

65 dateNsec = date.nsecs(DateTime.TAI) 

66 startDate = DateTime(dateNsec - int(exposureTime*1e9/2), DateTime.TAI) 

67 

68 exposureId = 54321 # an arbitrary value 

69 md = getMetadata({ 

70 "DATE-OBS": startDate.toString(DateTime.UTC), 

71 "TIMESYS": "UTC", 

72 "EXPTIME": exposureTime, 

73 "EXTRA1": "an abitrary key and value", 

74 "EXTRA2": 5, 

75 }) 

76 length = len(md) 

77 visitInfo = self.makeRawVisitInfo(md=md, exposureId=exposureId) 

78 with self.assertWarns(FutureWarning): 

79 # TODO: tested for backward-compatibility; remove on DM-32138 

80 self.assertEqual(visitInfo.getExposureId(), exposureId) 

81 self.assertEqual(md.nameCount(), length) # No stripping 

82 self.assertEqual(visitInfo.getExposureTime(), exposureTime) 

83 self.assertEqual(visitInfo.getDate(), date) 

84 

85 # Test stripping keywords 

86 visitInfo = SimpleMakeRawVisitInfo(doStripHeader=True)(md=md, exposureId=exposureId) 

87 self.assertEqual(md.nameCount(), 3) # TIMESYS and two EXTRAn keywords 

88 

89 # try TIMESYS=TAI 

90 md = getMetadata({ 

91 "DATE-OBS": startDate.toString(DateTime.TAI), 

92 "TIMESYS": "TAI", 

93 "EXPTIME": exposureTime, 

94 }) 

95 length = len(md) 

96 visitInfo = self.makeRawVisitInfo(md=md, exposureId=exposureId) 

97 self.assertEqual(md.nameCount(), length) 

98 self.assertEqual(visitInfo.getExposureTime(), exposureTime) 

99 self.assertEqual(visitInfo.getDate(), date) 

100 

101 # try omitting TIMESYS, which defaults to UTC 

102 md = getMetadata({ 

103 "DATE-OBS": startDate.toString(DateTime.UTC), 

104 "EXPTIME": exposureTime, 

105 }) 

106 length = len(md) 

107 visitInfo = self.makeRawVisitInfo(md=md, exposureId=exposureId) 

108 self.assertEqual(md.nameCount(), length) 

109 self.assertEqual(visitInfo.getExposureTime(), exposureTime) 

110 self.assertEqual(visitInfo.getDate(), date) 

111 

112 # omit DATE-OBS; date should be default-constructed 

113 md = getMetadata({ 

114 "EXPTIME": exposureTime, 

115 }) 

116 length = len(md) 

117 visitInfo = self.makeRawVisitInfo(md=md, exposureId=exposureId) 

118 self.assertEqual(md.nameCount(), length) 

119 self.assertEqual(visitInfo.getExposureTime(), exposureTime) 

120 self.assertEqual(visitInfo.getDate(), DateTime()) 

121 

122 # omit EXPTIME; date should be start date, not avg date, and 

123 # exposureTime should be nan 

124 md = getMetadata({ 

125 "DATE-OBS": startDate.toString(DateTime.UTC), 

126 }) 

127 length = len(md) 

128 visitInfo = self.makeRawVisitInfo(md=md, exposureId=exposureId) 

129 self.assertEqual(md.nameCount(), length) 

130 self.assertTrue(math.isnan(visitInfo.getExposureTime())) 

131 self.assertEqual(visitInfo.getDate(), startDate) 

132 

133 def testPopItem(self): 

134 md = getMetadata({ 

135 "TIMESYS": "UTC", 

136 "OTHER": 5, 

137 }) 

138 names = set(md.names()) 

139 

140 timesys = self.makeRawVisitInfo.popItem(md, "TIMESYS") 

141 self.assertEqual(timesys, "UTC") 

142 self.assertEqual(set(md.names()), names) 

143 

144 defVal = self.makeRawVisitInfo.popItem(md, "BADKEY", default=7) 

145 self.assertEqual(set(md.names()), names) 

146 self.assertEqual(defVal, 7) 

147 missingItem = self.makeRawVisitInfo.popItem(md, "BADKEY") 

148 self.assertIsNone(missingItem) 

149 

150 # Repeat, with stripping 

151 timesys = SimpleMakeRawVisitInfo(doStripHeader=True).popItem(md, "TIMESYS") 

152 self.assertEqual(timesys, "UTC") 

153 self.assertEqual(set(md.names()), set(["OTHER"])) 

154 

155 defVal = SimpleMakeRawVisitInfo(doStripHeader=True).popItem(md, "BADKEY", default=7) 

156 self.assertEqual(set(md.names()), set(["OTHER"])) 

157 self.assertEqual(defVal, 7) 

158 missingItem = SimpleMakeRawVisitInfo(doStripHeader=True).popItem(md, "BADKEY") 

159 self.assertIsNone(missingItem) 

160 

161 def testPopFloat(self): 

162 dataDict = { 

163 "FLOAT": 5.5, 

164 "INT": 5, 

165 "FLOATSTR": "6.1", 

166 "INTSTR": "6", 

167 "STR": "FOO", 

168 } 

169 length = len(dataDict) 

170 

171 for key, desValue in dataDict.items(): 

172 if key == "STR": 

173 continue 

174 md = getMetadata(dataDict) 

175 value = self.makeRawVisitInfo.popFloat(md, key) 

176 self.assertAlmostEqual(value, float(desValue)) 

177 self.assertEqual(len(md.names()), length) 

178 

179 badFloat = self.makeRawVisitInfo.popFloat(md, "STR") 

180 self.assertTrue(math.isnan(badFloat)) 

181 

182 missingValue = self.makeRawVisitInfo.popFloat(md, "BADKEY") 

183 self.assertTrue(math.isnan(missingValue)) 

184 

185 def testPopAngle(self): 

186 dataDict = { 

187 "DEG1": 270.5, 

188 "DEG2": "45:30", 

189 "DEG3": "-310:12:32", 

190 "HR_1": -23.5, 

191 "HR_2": "23:30", 

192 "HR_3": "-13:15:16.7", 

193 "STR": "FOO", 

194 } 

195 

196 for key, desValue in dataDict.items(): 

197 if key == "STR": 

198 continue 

199 elif key.startswith("DEG"): 

200 units = astropy.units.deg 

201 else: 

202 units = astropy.units.h 

203 

204 desAngleDeg = astropy.coordinates.Angle(desValue, unit=units).deg 

205 md = getMetadata(dataDict) 

206 angle = self.makeRawVisitInfo.popAngle(md, key, units=units) 

207 self.assertAnglesAlmostEqual(angle, desAngleDeg*degrees) 

208 

209 badAngle = self.makeRawVisitInfo.popAngle(md, "STR") 

210 self.assertTrue(math.isnan(badAngle.asDegrees())) 

211 

212 missingAngle = self.makeRawVisitInfo.popAngle(md, "BADKEY") 

213 self.assertTrue(math.isnan(missingAngle.asDegrees())) 

214 

215 def testPopIsoDate(self): 

216 for timesys in (None, "UTC", "TAI"): 

217 dataDict = { 

218 "DATE1": "2001-02-03T04:05:06.123456789", 

219 "DATE2": "2001-02-03T04:05:06.123456789Z", # Z should be ignored 

220 "DATE3": "1980-03-04T01:02:03.999999999", 

221 "BADISODATE": "51234.354", 

222 } 

223 if timesys is not None: 

224 dataDict["TIMESYS"] = timesys 

225 length = len(dataDict) 

226 

227 for key, dateStr in dataDict.items(): 

228 if not key.startswith("DATE"): 

229 continue 

230 

231 lsstSys = dict( 

232 UTC=DateTime.UTC, 

233 TAI=DateTime.TAI, 

234 ).get(timesys, DateTime.UTC) 

235 

236 # lsstDateStr = dateStr with trailing Z if UTC, else no 

237 # trailing Z, because lsst.daf.base.DateTime is very picky 

238 lsstDateStr = dateStr 

239 if lsstSys == DateTime.UTC: 

240 if not lsstDateStr.endswith("Z"): 

241 lsstDateStr = lsstDateStr + "Z" 

242 elif dateStr.endswith("Z"): 

243 lsstDateStr = lsstDateStr[0:-1] 

244 desDate = DateTime(lsstDateStr, lsstSys) 

245 

246 md = getMetadata(dataDict) 

247 date = self.makeRawVisitInfo.popIsoDate(md, key, timesys=timesys) 

248 self.assertEqual(len(md.names()), length) 

249 self.assertEqual(date, desDate) 

250 

251 badDate = self.makeRawVisitInfo.popIsoDate(md, "BADISODATE") 

252 self.assertEqual(badDate, DateTime()) 

253 

254 missingDate = self.makeRawVisitInfo.popIsoDate(md, "BADKEY") 

255 self.assertEqual(missingDate, DateTime()) 

256 

257 def testPopMjdDate(self): 

258 for timesys in (None, "UTC", "TAI"): 

259 

260 dataDict = { 

261 "DATE1": 51943.1705801, 

262 "DATE2": 44302.0433218, 

263 "BADMJDDATE": "2001-02-03T04:05:06.123456789", 

264 } 

265 if timesys is not None: 

266 dataDict["TIMESYS"] = timesys 

267 length = len(dataDict) 

268 

269 for key, mjdDate in dataDict.items(): 

270 if not key.startswith("DATE"): 

271 continue 

272 

273 lsstSys = dict( 

274 UTC=DateTime.UTC, 

275 TAI=DateTime.TAI, 

276 ).get(timesys, DateTime.UTC) 

277 

278 desDate = DateTime(mjdDate, DateTime.MJD, lsstSys) 

279 

280 md = getMetadata(dataDict) 

281 date = self.makeRawVisitInfo.popMjdDate(md, key, timesys=timesys) 

282 self.assertEqual(len(md.names()), length) 

283 self.assertAlmostEqual(date.get(), desDate.get()) 

284 

285 badDate = self.makeRawVisitInfo.popMjdDate(md, "BADMJDDATE") 

286 self.assertEqual(badDate, DateTime()) 

287 

288 missingDate = self.makeRawVisitInfo.popMjdDate(md, "BADKEY") 

289 self.assertEqual(missingDate, DateTime()) 

290 

291 def testEraFromLstAndLongitude(self): 

292 LST = 90*degrees 

293 Longitude = 50*degrees 

294 era = self.makeRawVisitInfo.eraFromLstAndLongitude(LST, Longitude) 

295 self.assertAnglesAlmostEqual(era, LST-Longitude) 

296 

297 def testEraFromLstAndLongitude_float_vs_Angle_fails(self): 

298 val1 = 90*degrees 

299 val2 = 50.0 

300 with self.assertRaises(TypeError): 

301 self.makeRawVisitInfo.eraFromLstAndLongitude(val1, val2) 

302 with self.assertRaises(TypeError): 

303 self.makeRawVisitInfo.eraFromLstAndLongitude(val2, val1) 

304 

305 def testAltitudeFromZenithDistance(self): 

306 for zdDeg in (0, 35.6, 89.999, 90.0): 

307 desAltDeg = 90-zdDeg 

308 self.assertAnglesAlmostEqual( 

309 desAltDeg*degrees, 

310 self.makeRawVisitInfo.altitudeFromZenithDistance(zdDeg*degrees), 

311 ) 

312 

313 def testCentigradeFromKelvin(self): 

314 for tempK, desTempC in ( # a few values from http://www.convertunits.com/from/kelvin/to/centigrade 

315 (0, -273.15), 

316 (301.5, 28.35), 

317 ): 

318 self.assertAlmostEqual(desTempC, self.makeRawVisitInfo.centigradeFromKelvin(tempK)) 

319 

320 def testPascalFromMmHg(self): 

321 for mmHg, desPascal in ( # a few values from http://www.convertunits.com/from/mm+Hg/to/pascal 

322 (1, 133.32239), 

323 (0.062, 8.26598818), 

324 ): 

325 self.assertAlmostEqual(desPascal, self.makeRawVisitInfo.pascalFromMmHg(mmHg), places=5) 

326 

327 

328def setup_module(module): 

329 lsst.utils.tests.init() 

330 

331 

332class MemoryTester(lsst.utils.tests.MemoryTestCase): 

333 pass 

334 

335 

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

337 lsst.utils.tests.init() 

338 unittest.main()