Coverage for tests/test_makeRawVisitInfo.py: 15%

183 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-07-09 06:22 -0700

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 

27import lsst.pex.exceptions 

28import lsst.utils.tests 

29from lsst.daf.base import DateTime, PropertySet 

30from lsst.geom import degrees 

31from lsst.obs.base import MakeRawVisitInfo 

32 

33 

34class SimpleMakeRawVisitInfo(MakeRawVisitInfo): 

35 def getDateAvg(self, md, exposureTime): 

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

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

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

39 

40 

41def getMetadata(keyValDict): 

42 md = PropertySet() 

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

44 md.set(key, val) 

45 return md 

46 

47 

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

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

50 

51 def setUp(self): 

52 self.makeRawVisitInfo = SimpleMakeRawVisitInfo() 

53 

54 def testMakeRawVisitInfo(self): 

55 """Test base class functor MakeRawVisitInfo 

56 

57 The base class only sets date and exposureTime fields, 

58 reads DATE-OBS, TIMESYS and EXPTIME, 

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

60 """ 

61 exposureTime = 6.2 # arbitrary value in seconds 

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

63 dateNsec = date.nsecs(DateTime.TAI) 

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

65 

66 exposureId = 54321 # an arbitrary value 

67 md = getMetadata( 

68 { 

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

70 "TIMESYS": "UTC", 

71 "EXPTIME": exposureTime, 

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

73 "EXTRA2": 5, 

74 } 

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 { 

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

93 "TIMESYS": "TAI", 

94 "EXPTIME": exposureTime, 

95 } 

96 ) 

97 length = len(md) 

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

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

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

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

102 

103 # try omitting TIMESYS, which defaults to UTC 

104 md = getMetadata( 

105 { 

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

107 "EXPTIME": exposureTime, 

108 } 

109 ) 

110 length = len(md) 

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

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

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

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

115 

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

117 md = getMetadata( 

118 { 

119 "EXPTIME": exposureTime, 

120 } 

121 ) 

122 length = len(md) 

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

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

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

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

127 

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

129 # exposureTime should be nan 

130 md = getMetadata( 

131 { 

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

133 } 

134 ) 

135 length = len(md) 

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

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

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

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

140 

141 def testPopItem(self): 

142 md = getMetadata( 

143 { 

144 "TIMESYS": "UTC", 

145 "OTHER": 5, 

146 } 

147 ) 

148 names = set(md.names()) 

149 

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

151 self.assertEqual(timesys, "UTC") 

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

153 

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

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

156 self.assertEqual(defVal, 7) 

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

158 self.assertIsNone(missingItem) 

159 

160 # Repeat, with stripping 

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

162 self.assertEqual(timesys, "UTC") 

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

164 

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

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

167 self.assertEqual(defVal, 7) 

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

169 self.assertIsNone(missingItem) 

170 

171 def testPopFloat(self): 

172 dataDict = { 

173 "FLOAT": 5.5, 

174 "INT": 5, 

175 "FLOATSTR": "6.1", 

176 "INTSTR": "6", 

177 "STR": "FOO", 

178 } 

179 length = len(dataDict) 

180 

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

182 if key == "STR": 

183 continue 

184 md = getMetadata(dataDict) 

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

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

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

188 

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

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

191 

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

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

194 

195 def testPopAngle(self): 

196 dataDict = { 

197 "DEG1": 270.5, 

198 "DEG2": "45:30", 

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

200 "HR_1": -23.5, 

201 "HR_2": "23:30", 

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

203 "STR": "FOO", 

204 } 

205 

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

207 if key == "STR": 

208 continue 

209 elif key.startswith("DEG"): 

210 units = astropy.units.deg 

211 else: 

212 units = astropy.units.h 

213 

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

215 md = getMetadata(dataDict) 

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

217 self.assertAnglesAlmostEqual(angle, desAngleDeg * degrees) 

218 

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

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

221 

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

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

224 

225 def testPopIsoDate(self): 

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

227 dataDict = { 

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

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

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

231 "BADISODATE": "51234.354", 

232 } 

233 if timesys is not None: 

234 dataDict["TIMESYS"] = timesys 

235 length = len(dataDict) 

236 

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

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

239 continue 

240 

241 lsstSys = dict( 

242 UTC=DateTime.UTC, 

243 TAI=DateTime.TAI, 

244 ).get(timesys, DateTime.UTC) 

245 

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

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

248 lsstDateStr = dateStr 

249 if lsstSys == DateTime.UTC: 

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

251 lsstDateStr = lsstDateStr + "Z" 

252 elif dateStr.endswith("Z"): 

253 lsstDateStr = lsstDateStr[0:-1] 

254 desDate = DateTime(lsstDateStr, lsstSys) 

255 

256 md = getMetadata(dataDict) 

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

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

259 self.assertEqual(date, desDate) 

260 

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

262 self.assertEqual(badDate, DateTime()) 

263 

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

265 self.assertEqual(missingDate, DateTime()) 

266 

267 def testPopMjdDate(self): 

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

269 

270 dataDict = { 

271 "DATE1": 51943.1705801, 

272 "DATE2": 44302.0433218, 

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

274 } 

275 if timesys is not None: 

276 dataDict["TIMESYS"] = timesys 

277 length = len(dataDict) 

278 

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

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

281 continue 

282 

283 lsstSys = dict( 

284 UTC=DateTime.UTC, 

285 TAI=DateTime.TAI, 

286 ).get(timesys, DateTime.UTC) 

287 

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

289 

290 md = getMetadata(dataDict) 

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

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

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

294 

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

296 self.assertEqual(badDate, DateTime()) 

297 

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

299 self.assertEqual(missingDate, DateTime()) 

300 

301 def testEraFromLstAndLongitude(self): 

302 LST = 90 * degrees 

303 Longitude = 50 * degrees 

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

305 self.assertAnglesAlmostEqual(era, LST - Longitude) 

306 

307 def testEraFromLstAndLongitude_float_vs_Angle_fails(self): 

308 val1 = 90 * degrees 

309 val2 = 50.0 

310 with self.assertRaises(TypeError): 

311 self.makeRawVisitInfo.eraFromLstAndLongitude(val1, val2) 

312 with self.assertRaises(TypeError): 

313 self.makeRawVisitInfo.eraFromLstAndLongitude(val2, val1) 

314 

315 def testAltitudeFromZenithDistance(self): 

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

317 desAltDeg = 90 - zdDeg 

318 self.assertAnglesAlmostEqual( 

319 desAltDeg * degrees, 

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

321 ) 

322 

323 def testCentigradeFromKelvin(self): 

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

325 (0, -273.15), 

326 (301.5, 28.35), 

327 ): 

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

329 

330 def testPascalFromMmHg(self): 

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

332 (1, 133.32239), 

333 (0.062, 8.26598818), 

334 ): 

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

336 

337 

338def setup_module(module): 

339 lsst.utils.tests.init() 

340 

341 

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

343 pass 

344 

345 

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

347 lsst.utils.tests.init() 

348 unittest.main()