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 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 self.assertEqual(visitInfo.getExposureId(), exposureId) 

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

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

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

82 

83 # Test stripping keywords 

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

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

86 

87 # try TIMESYS=TAI 

88 md = getMetadata({ 

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

90 "TIMESYS": "TAI", 

91 "EXPTIME": exposureTime, 

92 }) 

93 length = len(md) 

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

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

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

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

98 

99 # try omitting TIMESYS, which defaults to UTC 

100 md = getMetadata({ 

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

102 "EXPTIME": exposureTime, 

103 }) 

104 length = len(md) 

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

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

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

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

109 

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

111 md = getMetadata({ 

112 "EXPTIME": exposureTime, 

113 }) 

114 length = len(md) 

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

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

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

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

119 

120 # omit EXPTIME; date should be start date, not avg date, and exposureTime should be nan 

121 md = getMetadata({ 

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

123 }) 

124 length = len(md) 

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

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

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

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

129 

130 def testPopItem(self): 

131 md = getMetadata({ 

132 "TIMESYS": "UTC", 

133 "OTHER": 5, 

134 }) 

135 names = set(md.names()) 

136 

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

138 self.assertEqual(timesys, "UTC") 

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

140 

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

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

143 self.assertEqual(defVal, 7) 

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

145 self.assertIsNone(missingItem) 

146 

147 # Repeat, with stripping 

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

149 self.assertEqual(timesys, "UTC") 

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

151 

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

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

154 self.assertEqual(defVal, 7) 

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

156 self.assertIsNone(missingItem) 

157 

158 def testPopFloat(self): 

159 dataDict = { 

160 "FLOAT": 5.5, 

161 "INT": 5, 

162 "FLOATSTR": "6.1", 

163 "INTSTR": "6", 

164 "STR": "FOO", 

165 } 

166 length = len(dataDict) 

167 

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

169 if key == "STR": 

170 continue 

171 md = getMetadata(dataDict) 

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

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

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

175 

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

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

178 

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

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

181 

182 def testPopAngle(self): 

183 dataDict = { 

184 "DEG1": 270.5, 

185 "DEG2": "45:30", 

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

187 "HR_1": -23.5, 

188 "HR_2": "23:30", 

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

190 "STR": "FOO", 

191 } 

192 

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

194 if key == "STR": 

195 continue 

196 elif key.startswith("DEG"): 

197 units = astropy.units.deg 

198 else: 

199 units = astropy.units.h 

200 

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

202 md = getMetadata(dataDict) 

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

204 self.assertAnglesAlmostEqual(angle, desAngleDeg*degrees) 

205 

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

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

208 

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

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

211 

212 def testPopIsoDate(self): 

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

214 dataDict = { 

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

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

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

218 "BADISODATE": "51234.354", 

219 } 

220 if timesys is not None: 

221 dataDict["TIMESYS"] = timesys 

222 length = len(dataDict) 

223 

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

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

226 continue 

227 

228 lsstSys = dict( 

229 UTC=DateTime.UTC, 

230 TAI=DateTime.TAI, 

231 ).get(timesys, DateTime.UTC) 

232 

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

234 # because lsst.daf.base.DateTime is very picky 

235 lsstDateStr = dateStr 

236 if lsstSys == DateTime.UTC: 

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

238 lsstDateStr = lsstDateStr + "Z" 

239 elif dateStr.endswith("Z"): 

240 lsstDateStr = lsstDateStr[0:-1] 

241 desDate = DateTime(lsstDateStr, lsstSys) 

242 

243 md = getMetadata(dataDict) 

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

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

246 self.assertEqual(date, desDate) 

247 

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

249 self.assertEqual(badDate, DateTime()) 

250 

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

252 self.assertEqual(missingDate, DateTime()) 

253 

254 def testPopMjdDate(self): 

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

256 

257 dataDict = { 

258 "DATE1": 51943.1705801, 

259 "DATE2": 44302.0433218, 

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

261 } 

262 if timesys is not None: 

263 dataDict["TIMESYS"] = timesys 

264 length = len(dataDict) 

265 

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

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

268 continue 

269 

270 lsstSys = dict( 

271 UTC=DateTime.UTC, 

272 TAI=DateTime.TAI, 

273 ).get(timesys, DateTime.UTC) 

274 

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

276 

277 md = getMetadata(dataDict) 

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

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

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

281 

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

283 self.assertEqual(badDate, DateTime()) 

284 

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

286 self.assertEqual(missingDate, DateTime()) 

287 

288 def testEraFromLstAndLongitude(self): 

289 LST = 90*degrees 

290 Longitude = 50*degrees 

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

292 self.assertAnglesAlmostEqual(era, LST-Longitude) 

293 

294 def testEraFromLstAndLongitude_float_vs_Angle_fails(self): 

295 val1 = 90*degrees 

296 val2 = 50.0 

297 with self.assertRaises(TypeError): 

298 self.makeRawVisitInfo.eraFromLstAndLongitude(val1, val2) 

299 with self.assertRaises(TypeError): 

300 self.makeRawVisitInfo.eraFromLstAndLongitude(val2, val1) 

301 

302 def testAltitudeFromZenithDistance(self): 

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

304 desAltDeg = 90-zdDeg 

305 self.assertAnglesAlmostEqual( 

306 desAltDeg*degrees, 

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

308 ) 

309 

310 def testCentigradeFromKelvin(self): 

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

312 (0, -273.15), 

313 (301.5, 28.35), 

314 ): 

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

316 

317 def testPascalFromMmHg(self): 

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

319 (1, 133.32239), 

320 (0.062, 8.26598818), 

321 ): 

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

323 

324 

325def setup_module(module): 

326 lsst.utils.tests.init() 

327 

328 

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

330 pass 

331 

332 

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

334 lsst.utils.tests.init() 

335 unittest.main()