Coverage for tests/test_makeRawVisitInfo.py: 16%
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
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
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/>.
22import math
23import unittest
25import astropy.coordinates
26import astropy.units
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
35class SimpleMakeRawVisitInfo(MakeRawVisitInfo):
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)
43def getMetadata(keyValDict):
44 md = PropertySet()
45 for key, val in keyValDict.items():
46 md.set(key, val)
47 return md
50class VisitInfoTestCase(lsst.utils.tests.TestCase):
51 """Test lsst.afw.image.VisitInfo, a simple struct-like class"""
53 def setUp(self):
54 self.makeRawVisitInfo = SimpleMakeRawVisitInfo()
56 def testMakeRawVisitInfo(self):
57 """Test base class functor MakeRawVisitInfo
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)
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)
85 # Test stripping keywords
86 visitInfo = SimpleMakeRawVisitInfo(doStripHeader=True)(md=md, exposureId=exposureId)
87 self.assertEqual(md.nameCount(), 3) # TIMESYS and two EXTRAn keywords
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)
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)
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())
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)
133 def testPopItem(self):
134 md = getMetadata({
135 "TIMESYS": "UTC",
136 "OTHER": 5,
137 })
138 names = set(md.names())
140 timesys = self.makeRawVisitInfo.popItem(md, "TIMESYS")
141 self.assertEqual(timesys, "UTC")
142 self.assertEqual(set(md.names()), names)
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)
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"]))
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)
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)
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)
179 badFloat = self.makeRawVisitInfo.popFloat(md, "STR")
180 self.assertTrue(math.isnan(badFloat))
182 missingValue = self.makeRawVisitInfo.popFloat(md, "BADKEY")
183 self.assertTrue(math.isnan(missingValue))
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 }
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
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)
209 badAngle = self.makeRawVisitInfo.popAngle(md, "STR")
210 self.assertTrue(math.isnan(badAngle.asDegrees()))
212 missingAngle = self.makeRawVisitInfo.popAngle(md, "BADKEY")
213 self.assertTrue(math.isnan(missingAngle.asDegrees()))
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)
227 for key, dateStr in dataDict.items():
228 if not key.startswith("DATE"):
229 continue
231 lsstSys = dict(
232 UTC=DateTime.UTC,
233 TAI=DateTime.TAI,
234 ).get(timesys, DateTime.UTC)
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)
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)
251 badDate = self.makeRawVisitInfo.popIsoDate(md, "BADISODATE")
252 self.assertEqual(badDate, DateTime())
254 missingDate = self.makeRawVisitInfo.popIsoDate(md, "BADKEY")
255 self.assertEqual(missingDate, DateTime())
257 def testPopMjdDate(self):
258 for timesys in (None, "UTC", "TAI"):
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)
269 for key, mjdDate in dataDict.items():
270 if not key.startswith("DATE"):
271 continue
273 lsstSys = dict(
274 UTC=DateTime.UTC,
275 TAI=DateTime.TAI,
276 ).get(timesys, DateTime.UTC)
278 desDate = DateTime(mjdDate, DateTime.MJD, lsstSys)
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())
285 badDate = self.makeRawVisitInfo.popMjdDate(md, "BADMJDDATE")
286 self.assertEqual(badDate, DateTime())
288 missingDate = self.makeRawVisitInfo.popMjdDate(md, "BADKEY")
289 self.assertEqual(missingDate, DateTime())
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)
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)
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 )
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))
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)
328def setup_module(module):
329 lsst.utils.tests.init()
332class MemoryTester(lsst.utils.tests.MemoryTestCase):
333 pass
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()