Coverage for tests/test_makeRawVisitInfo.py: 15%
183 statements
« prev ^ index » next coverage.py v6.4, created at 2022-06-02 03:54 -0700
« prev ^ index » next coverage.py v6.4, created at 2022-06-02 03:54 -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/>.
22import math
23import unittest
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
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)
41def getMetadata(keyValDict):
42 md = PropertySet()
43 for key, val in keyValDict.items():
44 md.set(key, val)
45 return md
48class VisitInfoTestCase(lsst.utils.tests.TestCase):
49 """Test lsst.afw.image.VisitInfo, a simple struct-like class"""
51 def setUp(self):
52 self.makeRawVisitInfo = SimpleMakeRawVisitInfo()
54 def testMakeRawVisitInfo(self):
55 """Test base class functor MakeRawVisitInfo
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)
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)
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 {
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)
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)
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())
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)
141 def testPopItem(self):
142 md = getMetadata(
143 {
144 "TIMESYS": "UTC",
145 "OTHER": 5,
146 }
147 )
148 names = set(md.names())
150 timesys = self.makeRawVisitInfo.popItem(md, "TIMESYS")
151 self.assertEqual(timesys, "UTC")
152 self.assertEqual(set(md.names()), names)
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)
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"]))
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)
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)
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)
189 badFloat = self.makeRawVisitInfo.popFloat(md, "STR")
190 self.assertTrue(math.isnan(badFloat))
192 missingValue = self.makeRawVisitInfo.popFloat(md, "BADKEY")
193 self.assertTrue(math.isnan(missingValue))
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 }
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
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)
219 badAngle = self.makeRawVisitInfo.popAngle(md, "STR")
220 self.assertTrue(math.isnan(badAngle.asDegrees()))
222 missingAngle = self.makeRawVisitInfo.popAngle(md, "BADKEY")
223 self.assertTrue(math.isnan(missingAngle.asDegrees()))
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)
237 for key, dateStr in dataDict.items():
238 if not key.startswith("DATE"):
239 continue
241 lsstSys = dict(
242 UTC=DateTime.UTC,
243 TAI=DateTime.TAI,
244 ).get(timesys, DateTime.UTC)
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)
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)
261 badDate = self.makeRawVisitInfo.popIsoDate(md, "BADISODATE")
262 self.assertEqual(badDate, DateTime())
264 missingDate = self.makeRawVisitInfo.popIsoDate(md, "BADKEY")
265 self.assertEqual(missingDate, DateTime())
267 def testPopMjdDate(self):
268 for timesys in (None, "UTC", "TAI"):
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)
279 for key, mjdDate in dataDict.items():
280 if not key.startswith("DATE"):
281 continue
283 lsstSys = dict(
284 UTC=DateTime.UTC,
285 TAI=DateTime.TAI,
286 ).get(timesys, DateTime.UTC)
288 desDate = DateTime(mjdDate, DateTime.MJD, lsstSys)
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())
295 badDate = self.makeRawVisitInfo.popMjdDate(md, "BADMJDDATE")
296 self.assertEqual(badDate, DateTime())
298 missingDate = self.makeRawVisitInfo.popMjdDate(md, "BADKEY")
299 self.assertEqual(missingDate, DateTime())
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)
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)
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 )
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))
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)
338def setup_module(module):
339 lsst.utils.tests.init()
342class MemoryTester(lsst.utils.tests.MemoryTestCase):
343 pass
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()