Coverage for tests/test_makeRawVisitInfo.py: 13%
182 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-12 01:53 -0800
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-12 01:53 -0800
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 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)
83 # Test stripping keywords
84 visitInfo = SimpleMakeRawVisitInfo(doStripHeader=True)(md=md, exposureId=exposureId)
85 self.assertEqual(md.nameCount(), 3) # TIMESYS and two EXTRAn keywords
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)
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)
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())
120 # omit EXPTIME; date should be start date, not avg date, and
121 # exposureTime should be nan
122 md = getMetadata({
123 "DATE-OBS": startDate.toString(DateTime.UTC),
124 })
125 length = len(md)
126 visitInfo = self.makeRawVisitInfo(md=md, exposureId=exposureId)
127 self.assertEqual(md.nameCount(), length)
128 self.assertTrue(math.isnan(visitInfo.getExposureTime()))
129 self.assertEqual(visitInfo.getDate(), startDate)
131 def testPopItem(self):
132 md = getMetadata({
133 "TIMESYS": "UTC",
134 "OTHER": 5,
135 })
136 names = set(md.names())
138 timesys = self.makeRawVisitInfo.popItem(md, "TIMESYS")
139 self.assertEqual(timesys, "UTC")
140 self.assertEqual(set(md.names()), names)
142 defVal = self.makeRawVisitInfo.popItem(md, "BADKEY", default=7)
143 self.assertEqual(set(md.names()), names)
144 self.assertEqual(defVal, 7)
145 missingItem = self.makeRawVisitInfo.popItem(md, "BADKEY")
146 self.assertIsNone(missingItem)
148 # Repeat, with stripping
149 timesys = SimpleMakeRawVisitInfo(doStripHeader=True).popItem(md, "TIMESYS")
150 self.assertEqual(timesys, "UTC")
151 self.assertEqual(set(md.names()), set(["OTHER"]))
153 defVal = SimpleMakeRawVisitInfo(doStripHeader=True).popItem(md, "BADKEY", default=7)
154 self.assertEqual(set(md.names()), set(["OTHER"]))
155 self.assertEqual(defVal, 7)
156 missingItem = SimpleMakeRawVisitInfo(doStripHeader=True).popItem(md, "BADKEY")
157 self.assertIsNone(missingItem)
159 def testPopFloat(self):
160 dataDict = {
161 "FLOAT": 5.5,
162 "INT": 5,
163 "FLOATSTR": "6.1",
164 "INTSTR": "6",
165 "STR": "FOO",
166 }
167 length = len(dataDict)
169 for key, desValue in dataDict.items():
170 if key == "STR":
171 continue
172 md = getMetadata(dataDict)
173 value = self.makeRawVisitInfo.popFloat(md, key)
174 self.assertAlmostEqual(value, float(desValue))
175 self.assertEqual(len(md.names()), length)
177 badFloat = self.makeRawVisitInfo.popFloat(md, "STR")
178 self.assertTrue(math.isnan(badFloat))
180 missingValue = self.makeRawVisitInfo.popFloat(md, "BADKEY")
181 self.assertTrue(math.isnan(missingValue))
183 def testPopAngle(self):
184 dataDict = {
185 "DEG1": 270.5,
186 "DEG2": "45:30",
187 "DEG3": "-310:12:32",
188 "HR_1": -23.5,
189 "HR_2": "23:30",
190 "HR_3": "-13:15:16.7",
191 "STR": "FOO",
192 }
194 for key, desValue in dataDict.items():
195 if key == "STR":
196 continue
197 elif key.startswith("DEG"):
198 units = astropy.units.deg
199 else:
200 units = astropy.units.h
202 desAngleDeg = astropy.coordinates.Angle(desValue, unit=units).deg
203 md = getMetadata(dataDict)
204 angle = self.makeRawVisitInfo.popAngle(md, key, units=units)
205 self.assertAnglesAlmostEqual(angle, desAngleDeg*degrees)
207 badAngle = self.makeRawVisitInfo.popAngle(md, "STR")
208 self.assertTrue(math.isnan(badAngle.asDegrees()))
210 missingAngle = self.makeRawVisitInfo.popAngle(md, "BADKEY")
211 self.assertTrue(math.isnan(missingAngle.asDegrees()))
213 def testPopIsoDate(self):
214 for timesys in (None, "UTC", "TAI"):
215 dataDict = {
216 "DATE1": "2001-02-03T04:05:06.123456789",
217 "DATE2": "2001-02-03T04:05:06.123456789Z", # Z should be ignored
218 "DATE3": "1980-03-04T01:02:03.999999999",
219 "BADISODATE": "51234.354",
220 }
221 if timesys is not None:
222 dataDict["TIMESYS"] = timesys
223 length = len(dataDict)
225 for key, dateStr in dataDict.items():
226 if not key.startswith("DATE"):
227 continue
229 lsstSys = dict(
230 UTC=DateTime.UTC,
231 TAI=DateTime.TAI,
232 ).get(timesys, DateTime.UTC)
234 # lsstDateStr = dateStr with trailing Z if UTC, else no
235 # trailing Z, because lsst.daf.base.DateTime is very picky
236 lsstDateStr = dateStr
237 if lsstSys == DateTime.UTC:
238 if not lsstDateStr.endswith("Z"):
239 lsstDateStr = lsstDateStr + "Z"
240 elif dateStr.endswith("Z"):
241 lsstDateStr = lsstDateStr[0:-1]
242 desDate = DateTime(lsstDateStr, lsstSys)
244 md = getMetadata(dataDict)
245 date = self.makeRawVisitInfo.popIsoDate(md, key, timesys=timesys)
246 self.assertEqual(len(md.names()), length)
247 self.assertEqual(date, desDate)
249 badDate = self.makeRawVisitInfo.popIsoDate(md, "BADISODATE")
250 self.assertEqual(badDate, DateTime())
252 missingDate = self.makeRawVisitInfo.popIsoDate(md, "BADKEY")
253 self.assertEqual(missingDate, DateTime())
255 def testPopMjdDate(self):
256 for timesys in (None, "UTC", "TAI"):
258 dataDict = {
259 "DATE1": 51943.1705801,
260 "DATE2": 44302.0433218,
261 "BADMJDDATE": "2001-02-03T04:05:06.123456789",
262 }
263 if timesys is not None:
264 dataDict["TIMESYS"] = timesys
265 length = len(dataDict)
267 for key, mjdDate in dataDict.items():
268 if not key.startswith("DATE"):
269 continue
271 lsstSys = dict(
272 UTC=DateTime.UTC,
273 TAI=DateTime.TAI,
274 ).get(timesys, DateTime.UTC)
276 desDate = DateTime(mjdDate, DateTime.MJD, lsstSys)
278 md = getMetadata(dataDict)
279 date = self.makeRawVisitInfo.popMjdDate(md, key, timesys=timesys)
280 self.assertEqual(len(md.names()), length)
281 self.assertAlmostEqual(date.get(), desDate.get())
283 badDate = self.makeRawVisitInfo.popMjdDate(md, "BADMJDDATE")
284 self.assertEqual(badDate, DateTime())
286 missingDate = self.makeRawVisitInfo.popMjdDate(md, "BADKEY")
287 self.assertEqual(missingDate, DateTime())
289 def testEraFromLstAndLongitude(self):
290 LST = 90*degrees
291 Longitude = 50*degrees
292 era = self.makeRawVisitInfo.eraFromLstAndLongitude(LST, Longitude)
293 self.assertAnglesAlmostEqual(era, LST-Longitude)
295 def testEraFromLstAndLongitude_float_vs_Angle_fails(self):
296 val1 = 90*degrees
297 val2 = 50.0
298 with self.assertRaises(TypeError):
299 self.makeRawVisitInfo.eraFromLstAndLongitude(val1, val2)
300 with self.assertRaises(TypeError):
301 self.makeRawVisitInfo.eraFromLstAndLongitude(val2, val1)
303 def testAltitudeFromZenithDistance(self):
304 for zdDeg in (0, 35.6, 89.999, 90.0):
305 desAltDeg = 90-zdDeg
306 self.assertAnglesAlmostEqual(
307 desAltDeg*degrees,
308 self.makeRawVisitInfo.altitudeFromZenithDistance(zdDeg*degrees),
309 )
311 def testCentigradeFromKelvin(self):
312 for tempK, desTempC in ( # a few values from http://www.convertunits.com/from/kelvin/to/centigrade
313 (0, -273.15),
314 (301.5, 28.35),
315 ):
316 self.assertAlmostEqual(desTempC, self.makeRawVisitInfo.centigradeFromKelvin(tempK))
318 def testPascalFromMmHg(self):
319 for mmHg, desPascal in ( # a few values from http://www.convertunits.com/from/mm+Hg/to/pascal
320 (1, 133.32239),
321 (0.062, 8.26598818),
322 ):
323 self.assertAlmostEqual(desPascal, self.makeRawVisitInfo.pascalFromMmHg(mmHg), places=5)
326def setup_module(module):
327 lsst.utils.tests.init()
330class MemoryTester(lsst.utils.tests.MemoryTestCase):
331 pass
334if __name__ == "__main__": 334 ↛ 335line 334 didn't jump to line 335, because the condition on line 334 was never true
335 lsst.utils.tests.init()
336 unittest.main()