Coverage for tests/testCameraUtils.py : 7%

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
1from __future__ import with_statement
2from builtins import zip
3from builtins import range
4import os
5import numpy as np
6import unittest
7import lsst.utils.tests
8from lsst.utils import getPackageDir
10from lsst.sims.utils import ObservationMetaData, radiansFromArcsec, arcsecFromRadians
11from lsst.sims.utils import haversine
12from lsst.obs.lsstSim import LsstSimMapper
14from lsst.sims.utils import pupilCoordsFromRaDec, observedFromICRS
15from lsst.sims.coordUtils import (chipNameFromRaDec,
16 chipNameFromPupilCoords,
17 _chipNameFromRaDec)
19from lsst.sims.coordUtils import (pixelCoordsFromPupilCoords,
20 pixelCoordsFromRaDec,
21 _pixelCoordsFromRaDec)
23from lsst.sims.coordUtils import (focalPlaneCoordsFromPupilCoords,
24 focalPlaneCoordsFromRaDec,
25 _focalPlaneCoordsFromRaDec)
27from lsst.sims.coordUtils import pupilCoordsFromPixelCoords
28from lsst.sims.coordUtils import raDecFromPixelCoords, _raDecFromPixelCoords
29from lsst.sims.coordUtils import getCornerPixels, _getCornerRaDec, getCornerRaDec
31from lsst.sims.coordUtils import pupilCoordsFromFocalPlaneCoords
32from lsst.sims.coordUtils import lsst_camera
34from lsst.geom import Point2D
35from lsst.afw.cameraGeom import FIELD_ANGLE, FOCAL_PLANE
37def setup_module(module):
38 lsst.utils.tests.init()
41class ChipNameTest(unittest.TestCase):
43 @classmethod
44 def setUpClass(cls):
45 cls.camera = LsstSimMapper().camera
47 @classmethod
48 def tearDownClass(cls):
49 del cls.camera
51 def setUp(self):
52 self.rng = np.random.RandomState(45532)
54 def testRuns(self):
55 """
56 Test that chipName runs, and that the various iterations of that method
57 are all self-consistent
58 """
59 nStars = 100
60 ra0 = 45.0
61 dec0 = -112.0
62 rotSkyPos = 135.0
63 mjd = 42350.0
64 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
65 mjd=mjd, rotSkyPos=rotSkyPos)
67 raList = (self.rng.random_sample(nStars)-0.5)*1000.0/3600.0 + ra0
68 decList = (self.rng.random_sample(nStars)-0.5)*1000.0/3600.0 + dec0
70 xpList, ypList = pupilCoordsFromRaDec(raList, decList,
71 obs_metadata=obs,
72 epoch=2000.0)
74 names1 = chipNameFromRaDec(raList, decList,
75 obs_metadata=obs,
76 epoch=2000.0,
77 camera=self.camera)
79 names2 = _chipNameFromRaDec(np.radians(raList), np.radians(decList),
80 obs_metadata=obs,
81 epoch=2000.0,
82 camera=self.camera)
84 names3 = chipNameFromPupilCoords(xpList, ypList, camera=self.camera)
86 np.testing.assert_array_equal(names1, names2)
87 np.testing.assert_array_equal(names1, names3)
89 isNone = 0
90 isNotNone = 0
91 for name in names1:
92 if name is None:
93 isNone += 1
94 else:
95 isNotNone += 1
97 self.assertGreater(isNotNone, 0)
99 def testExceptions(self):
100 """
101 Test that exceptions are raised when they should be
102 """
104 nStars = 10
105 xpList = self.rng.random_sample(nStars)*0.1
106 ypList = self.rng.random_sample(nStars)*0.1
108 obs = ObservationMetaData(pointingRA=25.0, pointingDec=112.0, mjd=42351.0,
109 rotSkyPos=35.0)
111 # verify that an exception is raised if you do not pass in a camera
112 with self.assertRaises(RuntimeError) as context:
113 chipNameFromPupilCoords(xpList, ypList)
114 self.assertEqual('No camera defined. Cannot run chipName.',
115 context.exception.args[0])
117 with self.assertRaises(RuntimeError) as context:
118 chipNameFromRaDec(xpList, ypList, obs_metadata=obs, epoch=2000.0)
119 self.assertEqual('No camera defined. Cannot run chipName.',
120 context.exception.args[0])
122 with self.assertRaises(RuntimeError) as context:
123 _chipNameFromRaDec(xpList, ypList, obs_metadata=obs, epoch=2000.0)
124 self.assertEqual('No camera defined. Cannot run chipName.',
125 context.exception.args[0])
127 # verify that an exception is raised if you do not pass in a numpy array
128 with self.assertRaises(RuntimeError) as context:
129 chipNameFromPupilCoords(list(xpList), ypList)
130 self.assertIn("The arg xPupil", context.exception.args[0])
132 with self.assertRaises(RuntimeError) as context:
133 _chipNameFromRaDec(list(xpList), ypList, obs_metadata=obs, epoch=2000.0)
134 self.assertIn("The arg ra", context.exception.args[0])
136 with self.assertRaises(RuntimeError) as context:
137 chipNameFromPupilCoords(xpList, list(ypList))
138 self.assertIn("The input arguments:", context.exception.args[0])
139 self.assertIn("yPupil", context.exception.args[0])
141 with self.assertRaises(RuntimeError) as context:
142 _chipNameFromRaDec(xpList, list(ypList), obs_metadata=obs, epoch=2000.0)
143 self.assertIn("The input arguments:", context.exception.args[0])
144 self.assertIn("Dec", context.exception.args[0])
146 # do not need to run the above test on chipNameFromRaDec because
147 # the conversion from degrees to radians that happens inside that
148 # method automatically casts lists as numpy arrays
150 # verify that an exception is raised if the two coordinate arrays contain
151 # different numbers of elements
152 xpDummy = self.rng.random_sample(nStars//2)
154 with self.assertRaises(RuntimeError) as context:
155 chipNameFromPupilCoords(xpDummy, ypList, camera=self.camera)
157 self.assertEqual(context.exception.args[0],
158 "The arrays input to chipNameFromPupilCoords all need "
159 "to have the same length")
161 with self.assertRaises(RuntimeError) as context:
162 chipNameFromRaDec(xpDummy, ypList, obs_metadata=obs, epoch=2000.0,
163 camera=self.camera)
165 self.assertEqual(context.exception.args[0],
166 "The arrays input to chipNameFromRaDec all need to have the same length")
168 with self.assertRaises(RuntimeError) as context:
169 _chipNameFromRaDec(xpDummy, ypList, obs_metadata=obs, epoch=2000.0,
170 camera=self.camera)
172 self.assertEqual(context.exception.args[0],
173 "The arrays input to chipNameFromRaDec all need to have the same length")
175 # verify that an exception is raised if you call chipNameFromRaDec
176 # without an ObservationMetaData
177 with self.assertRaises(RuntimeError) as context:
178 chipNameFromRaDec(xpList, ypList, epoch=2000.0, camera=self.camera)
180 self.assertEqual(context.exception.args[0],
181 'You need to pass an ObservationMetaData into chipName')
183 with self.assertRaises(RuntimeError) as context:
184 _chipNameFromRaDec(xpList, ypList, epoch=2000.0, camera=self.camera)
186 self.assertEqual(context.exception.args[0],
187 'You need to pass an ObservationMetaData into chipName')
189 # verify that an exception is raised if you call chipNameFromRaDec
190 # with an ObservationMetaData that has no mjd
191 obsDummy = ObservationMetaData(pointingRA=25.0, pointingDec=-112.0,
192 rotSkyPos=112.0)
194 with self.assertRaises(RuntimeError) as context:
195 chipNameFromRaDec(xpList, ypList, epoch=2000.0, obs_metadata=obsDummy,
196 camera=self.camera)
198 self.assertEqual(context.exception.args[0],
199 'You need to pass an ObservationMetaData with an mjd into chipName')
201 with self.assertRaises(RuntimeError) as context:
202 _chipNameFromRaDec(xpList, ypList, epoch=2000.0, obs_metadata=obsDummy,
203 camera=self.camera)
205 self.assertEqual(context.exception.args[0],
206 'You need to pass an ObservationMetaData with an mjd into chipName')
208 # verify that an exception is raised if you all chipNameFromRaDec
209 # using an ObservationMetaData without a rotSkyPos
210 obsDummy = ObservationMetaData(pointingRA=25.0, pointingDec=-112.0,
211 mjd=52350.0)
213 with self.assertRaises(RuntimeError) as context:
214 chipNameFromRaDec(xpList, ypList, epoch=2000.0, obs_metadata=obsDummy,
215 camera=self.camera)
217 self.assertEqual(context.exception.args[0],
218 'You need to pass an ObservationMetaData with a rotSkyPos into chipName')
220 with self.assertRaises(RuntimeError) as context:
221 _chipNameFromRaDec(xpList, ypList, epoch=2000.0, obs_metadata=obsDummy,
222 camera=self.camera)
224 self.assertEqual(context.exception.args[0],
225 'You need to pass an ObservationMetaData with a rotSkyPos into chipName')
227 def testNaNbecomesNone(self):
228 """
229 Test that chipName maps NaNs and Nones in RA, Dec, and
230 pupil coordinates to None as chip name
231 """
232 nStars = 100
233 ra0 = 45.0
234 dec0 = -112.0
235 rotSkyPos = 135.0
236 mjd = 42350.0
237 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
238 mjd=mjd, rotSkyPos=rotSkyPos)
240 for badVal in [np.NaN, None]:
242 raList = (self.rng.random_sample(nStars)-0.5)*5.0/3600.0 + ra0
243 decList = (self.rng.random_sample(nStars)-0.5)*5.0/3600.0 + dec0
245 raList[5] = badVal
246 raList[10] = badVal
247 decList[10] = badVal
248 decList[25] = badVal
250 xpList, ypList = pupilCoordsFromRaDec(raList, decList,
251 obs_metadata=obs,
252 epoch=2000.0)
254 names1 = chipNameFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0,
255 camera=self.camera)
257 names2 = _chipNameFromRaDec(np.radians(raList), np.radians(decList),
258 obs_metadata=obs, epoch=2000.0, camera=self.camera)
260 names3 = chipNameFromPupilCoords(xpList, ypList, camera=self.camera)
262 np.testing.assert_array_equal(names1, names2)
263 np.testing.assert_array_equal(names1, names3)
265 for ix in range(len(names1)):
266 if ix != 5 and ix != 10 and ix != 25:
267 self.assertEqual(names1[ix], 'R:2,2 S:1,1')
268 self.assertEqual(names2[ix], 'R:2,2 S:1,1')
269 self.assertEqual(names3[ix], 'R:2,2 S:1,1')
270 else:
271 self.assertIsNone(names1[ix], None)
272 self.assertIsNone(names2[ix], None)
273 self.assertIsNone(names3[ix], None)
275 def testPassingFloats(self):
276 """
277 Test that you can pass floats of RA, Dec into chipNameFromRaDec.
279 Ditto for chipNameFromPupilCoords
280 """
282 ra0 = 45.0
283 dec0 = -112.0
284 rotSkyPos = 135.0
285 mjd = 42350.0
286 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
287 mjd=mjd, rotSkyPos=rotSkyPos)
289 nStars = 100
290 raList = (self.rng.random_sample(nStars)-0.5)*500.0/3600.0 + ra0
291 decList = (self.rng.random_sample(nStars)-0.5)*500.0/3600.0 + dec0
293 chipNameList = chipNameFromRaDec(raList, decList, camera=self.camera, obs_metadata=obs)
295 n_not_none = 0
296 # now iterate over the list of RA, Dec to make sure that the same name comes back
297 for ix, (rr, dd) in enumerate(zip(raList, decList)):
298 test_name = chipNameFromRaDec(rr, dd, camera=self.camera, obs_metadata=obs)
299 self.assertIsInstance(rr, np.float)
300 self.assertIsInstance(dd, np.float)
301 self.assertEqual(chipNameList[ix], test_name)
302 if test_name is not None:
303 try:
304 self.assertIsInstance(test_name, str)
305 except AssertionError:
306 self.assertIsInstance(test_name, unicode)
307 n_not_none += 1
309 self.assertGreater(n_not_none, 50)
311 # try it with pupil coordinates
312 n_not_none = 0
313 xpList, ypList = pupilCoordsFromRaDec(raList, decList, obs_metadata=obs)
314 chipNameList = chipNameFromPupilCoords(xpList, ypList, camera=self.camera)
315 for ix, (xp, yp) in enumerate(zip(xpList, ypList)):
316 test_name = chipNameFromPupilCoords(xp, yp, camera=self.camera)
317 self.assertIsInstance(xp, np.float)
318 self.assertIsInstance(yp, np.float)
319 self.assertEqual(chipNameList[ix], test_name)
320 if test_name is not None:
321 try:
322 self.assertIsInstance(test_name, str)
323 except AssertionError:
324 self.assertIsInstance(test_name, unicode)
325 n_not_none += 1
327 self.assertGreater(n_not_none, 50)
329 def test_one_by_one(self):
330 """
331 Test that runing chipNameFromRaDec one at a time gives
332 the same results as running in batch mode
333 """
334 ra = 145.0
335 dec = -25.0
336 obs = ObservationMetaData(pointingRA=ra, pointingDec=dec,
337 mjd=59580.0, rotSkyPos=113.0)
338 rng = np.random.RandomState(100)
339 theta = rng.random_sample(100)*2.0*np.pi
340 rr = rng.random_sample(len(theta))*2.0
341 ra_list = ra + rr*np.cos(theta)
342 dec_list = dec + rr*np.sin(theta)
343 name_control = chipNameFromRaDec(ra_list, dec_list, obs_metadata=obs,
344 camera=self.camera)
345 is_none = 0
346 for ra, dec, name in zip(ra_list, dec_list, name_control):
347 test_name = chipNameFromRaDec(ra, dec, obs_metadata=obs,
348 camera=self.camera)
349 self.assertEqual(test_name, name)
350 if test_name is None:
351 is_none += 1
352 self.assertGreater(is_none, 0)
353 self.assertLess(is_none, len(ra_list))
356class PixelCoordTest(unittest.TestCase):
358 longMessage = True
360 @classmethod
361 def setUpClass(cls):
362 cls.camera = LsstSimMapper().camera
364 @classmethod
365 def tearDownClass(cls):
366 del cls.camera
368 def setUp(self):
369 self.rng = np.random.RandomState(11324)
371 def testConsistency(self):
372 """
373 Test that all of the pixelCoord calculation methods agree with
374 each other
375 """
376 ra0 = 95.0
377 dec0 = -33.0
378 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
379 mjd=52350.0, rotSkyPos=27.0)
381 nStars = 100
382 raList = (self.rng.random_sample(nStars)-0.5)*1.5 + ra0
383 decList = (self.rng.random_sample(nStars)-0.5)*1.5 + dec0
385 xpList, ypList = pupilCoordsFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0)
387 chipNameList = chipNameFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0,
388 camera=self.camera)
390 for includeDistortion in [True, False]:
392 xx1, yy1 = pixelCoordsFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0,
393 camera=self.camera, includeDistortion=includeDistortion)
395 xx2, yy2 = _pixelCoordsFromRaDec(np.radians(raList), np.radians(decList),
396 obs_metadata=obs, epoch=2000.0,
397 camera=self.camera, includeDistortion=includeDistortion)
399 xx3, yy3 = pixelCoordsFromPupilCoords(xpList, ypList, camera=self.camera,
400 includeDistortion=includeDistortion)
402 xx4, yy4 = pixelCoordsFromPupilCoords(xpList, ypList, chipName=chipNameList,
403 camera=self.camera,
404 includeDistortion=includeDistortion)
406 xx5, yy5 = pixelCoordsFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0,
407 camera=self.camera, includeDistortion=includeDistortion,
408 chipName=chipNameList)
410 xx6, yy6 = _pixelCoordsFromRaDec(np.radians(raList), np.radians(decList),
411 obs_metadata=obs, epoch=2000.0,
412 camera=self.camera, includeDistortion=includeDistortion,
413 chipName=chipNameList)
415 np.testing.assert_array_equal(xx1, xx2)
416 np.testing.assert_array_equal(xx1, xx3)
417 np.testing.assert_array_equal(xx1, xx4)
418 np.testing.assert_array_equal(xx1, xx5)
419 np.testing.assert_array_equal(xx1, xx6)
421 np.testing.assert_array_equal(yy1, yy2)
422 np.testing.assert_array_equal(yy1, yy3)
423 np.testing.assert_array_equal(yy1, yy4)
424 np.testing.assert_array_equal(yy1, yy5)
425 np.testing.assert_array_equal(yy1, yy6)
427 # make sure that objects which do not fall on a chip
428 # get NaN pixel coords
429 ctNaN = 0
430 ctNotNaN = 0
431 for x, y, name in zip(xx1, yy1, chipNameList):
432 if name is None:
433 np.testing.assert_equal(x, np.NaN)
434 np.testing.assert_equal(y, np.NaN)
435 ctNaN += 1
436 else:
437 self.assertFalse(np.isnan(x), msg='x is Nan; should not be')
438 self.assertFalse(np.isnan(y), msg='y is Nan; should not be')
439 ctNotNaN += 1
441 self.assertGreater(ctNaN, 0)
442 self.assertGreater(ctNotNaN, 0)
444 # now test that passing in the points one at a time gives consistent results
445 for ix in range(len(raList)):
446 x_f, y_f = pixelCoordsFromRaDec(raList[ix], decList[ix], obs_metadata=obs,
447 epoch=2000.0, camera=self.camera,
448 includeDistortion=includeDistortion)
449 self.assertIsInstance(x_f, np.float)
450 self.assertIsInstance(y_f, np.float)
451 if not np.isnan(x_f):
452 self.assertEqual(x_f, xx1[ix])
453 self.assertEqual(y_f, yy1[ix])
454 else:
455 np.testing.assert_equal(xx1[ix], np.NaN)
456 np.testing.assert_equal(yy1[ix], np.NaN)
458 x_f, y_f = pixelCoordsFromRaDec(raList[ix], decList[ix], obs_metadata=obs,
459 epoch=2000.0, camera=self.camera,
460 includeDistortion=includeDistortion,
461 chipName=chipNameList[ix])
462 self.assertIsInstance(x_f, np.float)
463 self.assertIsInstance(y_f, np.float)
464 if not np.isnan(x_f):
465 self.assertEqual(x_f, xx1[ix])
466 self.assertEqual(y_f, yy1[ix])
467 else:
468 np.testing.assert_equal(xx1[ix], np.NaN)
469 np.testing.assert_equal(yy1[ix], np.NaN)
471 x_f, y_f = pixelCoordsFromRaDec(raList[ix], decList[ix], obs_metadata=obs,
472 epoch=2000.0, camera=self.camera,
473 includeDistortion=includeDistortion,
474 chipName=[chipNameList[ix]])
475 self.assertIsInstance(x_f, np.float)
476 self.assertIsInstance(y_f, np.float)
477 if not np.isnan(x_f):
478 self.assertEqual(x_f, xx1[ix])
479 self.assertEqual(y_f, yy1[ix])
480 else:
481 np.testing.assert_equal(xx1[ix], np.NaN)
482 np.testing.assert_equal(yy1[ix], np.NaN)
484 def testSingleChipName(self):
485 """
486 Test that pixelCoordsFromRaDec works when a list of RA, Dec are passed in,
487 but only one chipName
488 """
489 ra0 = 95.0
490 dec0 = -33.0
491 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
492 mjd=52350.0, rotSkyPos=27.0)
494 nStars = 100
495 raList = ra0 - (self.rng.random_sample(nStars)-0.5)*1.5
496 decList = (self.rng.random_sample(nStars)-0.5)*1.5 + dec0
498 xpList, ypList = pupilCoordsFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0)
500 chipNameList = chipNameFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0,
501 camera=self.camera)
503 chosen_chip = 'R:2,1 S:1,1'
504 valid_pts = np.where(chipNameList == chosen_chip)[0]
505 self.assertGreater(len(valid_pts), 1, msg='list_of_chips: %s' % str(chipNameList[valid_pts]))
506 xPixControl, yPixControl = pixelCoordsFromRaDec(raList[valid_pts], decList[valid_pts],
507 obs_metadata=obs,
508 includeDistortion=True,
509 camera=self.camera)
511 xPixTest, yPixTest = pixelCoordsFromRaDec(raList[valid_pts], decList[valid_pts],
512 obs_metadata=obs,
513 includeDistortion=True,
514 camera=self.camera,
515 chipName=chosen_chip)
517 np.testing.assert_array_almost_equal(xPixControl, xPixTest, 12)
518 np.testing.assert_array_almost_equal(yPixControl, yPixTest, 12)
520 xPixTest, yPixTest = pixelCoordsFromRaDec(raList[valid_pts], decList[valid_pts],
521 obs_metadata=obs,
522 includeDistortion=True,
523 camera=self.camera,
524 chipName=[chosen_chip])
526 np.testing.assert_array_almost_equal(xPixControl, xPixTest, 12)
527 np.testing.assert_array_almost_equal(yPixControl, yPixTest, 12)
529 # test raDecFromPixelCoords
530 raTest, decTest = raDecFromPixelCoords(xPixControl, yPixControl, chosen_chip,
531 camera=self.camera, obs_metadata=obs,
532 includeDistortion=True)
534 distance = arcsecFromRadians(haversine(np.radians(raList[valid_pts]),
535 np.radians(decList[valid_pts]),
536 np.radians(raTest), np.radians(decTest)))
538 self.assertLess(distance.max(), 0.004) # because of the imprecision in
539 # _icrsFromObserved, this is the best we can do
541 raTest, decTest = raDecFromPixelCoords(xPixControl, yPixControl, [chosen_chip],
542 camera=self.camera, obs_metadata=obs,
543 includeDistortion=True)
545 distance = arcsecFromRadians(haversine(np.radians(raList[valid_pts]),
546 np.radians(decList[valid_pts]),
547 np.radians(raTest), np.radians(decTest)))
549 self.assertLess(distance.max(), 0.004)
551 def testExceptions(self):
552 """
553 Test that pixelCoord calculation methods raise exceptions when
554 they should
555 """
556 nPoints = 100
557 xpList = self.rng.random_sample(nPoints)*np.radians(1.0)
558 ypList = self.rng.random_sample(nPoints)*np.radians(1.0)
559 obs = ObservationMetaData(pointingRA=25.0,
560 pointingDec=-36.0,
561 rotSkyPos=122.0,
562 mjd=41325.0)
564 raList = self.rng.random_sample(nPoints)*1.0+25.0
565 decList = self.rng.random_sample(nPoints)*1.0-36.0
567 # check that an error is raised when you forget to
568 # pass in a camera
569 with self.assertRaises(RuntimeError) as context:
570 pixelCoordsFromPupilCoords(xpList, ypList)
572 self.assertEqual(context.exception.args[0],
573 'Camera not specified. Cannot calculate pixel coordinates.')
575 with self.assertRaises(RuntimeError) as context:
576 pixelCoordsFromRaDec(raList, decList, obs_metadata=obs,
577 epoch=2000.0)
579 self.assertEqual(context.exception.args[0],
580 'Camera not specified. Cannot calculate pixel coordinates.')
582 with self.assertRaises(RuntimeError) as context:
583 _pixelCoordsFromRaDec(np.radians(raList),
584 np.radians(decList),
585 obs_metadata=obs,
586 epoch=2000.0)
588 self.assertEqual(context.exception.args[0],
589 'Camera not specified. Cannot calculate pixel coordinates.')
591 # test that an exception is raised when you pass in something
592 # that is not a numpy array
593 with self.assertRaises(RuntimeError) as context:
594 pixelCoordsFromPupilCoords(list(xpList), ypList,
595 camera=self.camera)
597 self.assertIn("The arg xPupil", context.exception.args[0])
599 with self.assertRaises(RuntimeError) as context:
600 pixelCoordsFromPupilCoords(xpList, list(ypList),
601 camera=self.camera)
603 self.assertIn("The input arguments:", context.exception.args[0])
604 self.assertIn("yPupil", context.exception.args[0])
606 with self.assertRaises(RuntimeError) as context:
607 _pixelCoordsFromRaDec(list(np.radians(raList)),
608 np.radians(decList),
609 obs_metadata=obs,
610 epoch=2000.0,
611 camera=self.camera)
613 self.assertIn("The arg ra", context.exception.args[0])
615 with self.assertRaises(RuntimeError) as context:
616 _pixelCoordsFromRaDec(np.radians(raList),
617 list(np.radians(decList)),
618 obs_metadata=obs,
619 epoch=2000.0,
620 camera=self.camera)
622 self.assertIn("The input arguments:", context.exception.args[0])
623 self.assertIn("dec", context.exception.args[0])
625 # do not need to run the above test on pixelCoordsFromRaDec,
626 # because the conversion from degrees to radians that happens
627 # inside that method automatically casts lists as numpy arrays
629 # test that an exception is raised if you pass in mis-matched
630 # input arrays
631 with self.assertRaises(RuntimeError) as context:
632 pixelCoordsFromPupilCoords(xpList, ypList[0:10],
633 camera=self.camera)
635 self.assertEqual(context.exception.args[0],
636 "The arrays input to pixelCoordsFromPupilCoords "
637 "all need to have the same length")
639 with self.assertRaises(RuntimeError) as context:
640 pixelCoordsFromRaDec(raList, decList[0:10],
641 obs_metadata=obs,
642 epoch=2000.0,
643 camera=self.camera)
645 self.assertEqual(context.exception.args[0],
646 "The arrays input to pixelCoordsFromRaDec all need "
647 "to have the same length")
649 with self.assertRaises(RuntimeError) as context:
650 _pixelCoordsFromRaDec(np.radians(raList),
651 np.radians(decList[0:10]),
652 obs_metadata=obs,
653 epoch=2000.0,
654 camera=self.camera)
656 self.assertEqual(context.exception.args[0],
657 "The arrays input to pixelCoordsFromRaDec all need "
658 "to have the same length")
660 # test that an error is raised if you pass an incorrect
661 # number of chipNames to pixelCoordsFromPupilCoords
662 with self.assertRaises(RuntimeError) as context:
663 pixelCoordsFromPupilCoords(xpList, ypList, chipName=['R:2,2 S:1,1']*10,
664 camera=self.camera)
666 self.assertIn("You passed 10 chipNames", context.exception.args[0])
668 with self.assertRaises(RuntimeError) as context:
669 pixelCoordsFromRaDec(raList, decList, chipName=['R:2,2 S:1,1']*10,
670 camera=self.camera,
671 obs_metadata=obs,
672 epoch=2000.0)
674 self.assertIn("You passed 10 chipNames", context.exception.args[0])
676 with self.assertRaises(RuntimeError) as context:
677 _pixelCoordsFromRaDec(np.radians(raList),
678 np.radians(decList),
679 chipName=['R:2,2 S:1,1']*10,
680 camera=self.camera,
681 obs_metadata=obs,
682 epoch=2000.0)
684 self.assertIn("You passed 10 chipNames", context.exception.args[0])
686 # test that an exception is raised if you call one of the
687 # pixelCoordsFromRaDec methods without an ObservationMetaData
688 with self.assertRaises(RuntimeError) as context:
689 pixelCoordsFromRaDec(raList, decList,
690 camera=self.camera, epoch=2000.0)
692 self.assertEqual(context.exception.args[0],
693 'You need to pass an ObservationMetaData into '
694 'pixelCoordsFromRaDec')
696 with self.assertRaises(RuntimeError) as context:
697 _pixelCoordsFromRaDec(raList, decList,
698 camera=self.camera, epoch=2000.0)
700 self.assertEqual(context.exception.args[0],
701 'You need to pass an ObservationMetaData into '
702 'pixelCoordsFromRaDec')
704 # test that an exception is raised if you try to use an
705 # ObservationMetaData without an mjd
706 obsDummy = ObservationMetaData(pointingRA=25.0,
707 pointingDec=-36.0,
708 rotSkyPos=112.0)
710 with self.assertRaises(RuntimeError) as context:
711 pixelCoordsFromRaDec(raList, decList,
712 camera=self.camera,
713 epoch=2000.0,
714 obs_metadata=obsDummy)
716 self.assertEqual(context.exception.args[0],
717 'You need to pass an ObservationMetaData '
718 'with an mjd into pixelCoordsFromRaDec')
720 with self.assertRaises(RuntimeError) as context:
721 _pixelCoordsFromRaDec(raList, decList,
722 camera=self.camera,
723 epoch=2000.0,
724 obs_metadata=obsDummy)
726 self.assertEqual(context.exception.args[0],
727 'You need to pass an ObservationMetaData '
728 'with an mjd into pixelCoordsFromRaDec')
730 # test that an exception is raised if you try to use an
731 # ObservationMetaData without a rotSkyPos
732 obsDummy = ObservationMetaData(pointingRA=25.0,
733 pointingDec=-36.0,
734 mjd=53000.0)
736 with self.assertRaises(RuntimeError) as context:
737 pixelCoordsFromRaDec(raList, decList,
738 camera=self.camera,
739 epoch=2000.0,
740 obs_metadata=obsDummy)
742 self.assertEqual(context.exception.args[0],
743 'You need to pass an ObservationMetaData '
744 'with a rotSkyPos into pixelCoordsFromRaDec')
746 obsDummy = ObservationMetaData(pointingRA=25.0,
747 pointingDec=-36.0,
748 mjd=53000.0)
749 with self.assertRaises(RuntimeError) as context:
750 _pixelCoordsFromRaDec(raList, decList,
751 camera=self.camera,
752 epoch=2000.0,
753 obs_metadata=obsDummy)
755 self.assertEqual(context.exception.args[0],
756 'You need to pass an ObservationMetaData '
757 'with a rotSkyPos into pixelCoordsFromRaDec')
759 @unittest.skip("The test camera has changed")
760 def testResults(self):
761 """
762 Test that the results of the pixelCoords methods make sense. Note that the test
763 camera has a platescale of 0.02 arcsec per pixel (2.0 arcsec per mm encoded in
764 CameraForUnitTests.py and 10 microns per pixel encoded in cameraData/focalplanelayout.txt).
765 We will use that to set the control values for our unit test.
767 Note: This unit test will fail if the test camera ever changes.
769 Note: Because we have already tested the self-consistency of
770 pixelCoordsFromPupilCoords and pixelCoordsFromRaDec, we will
771 only be testing pixelCoordsFromPupilCoords here, because it
772 is easier.
773 """
775 arcsecPerPixel = 0.02
776 arcsecPerMicron = 0.002
778 # list a bunch of detector centers in radians
779 x22 = 0.0
780 y22 = 0.0
782 x32 = radiansFromArcsec(40000.0 * arcsecPerMicron)
783 y32 = 0.0
785 x40 = radiansFromArcsec(80000.0 * arcsecPerMicron)
786 y40 = radiansFromArcsec(-80000.0 * arcsecPerMicron)
788 # assemble a bunch of displacements in pixels
789 dxPixList = []
790 dyPixList = []
791 for xx in np.arange(-1999.0, 1999.0, 500.0):
792 for yy in np.arange(-1999.0, 1999.0, 500.0):
793 dxPixList.append(xx)
794 dyPixList.append(yy)
796 dxPixList = np.array(dxPixList)
797 dyPixList = np.array(dyPixList)
799 # convert to raidans
800 dxPupList = radiansFromArcsec(dxPixList*arcsecPerPixel)
801 dyPupList = radiansFromArcsec(dyPixList*arcsecPerPixel)
803 # assemble a bunch of test pupil coordinate pairs
804 xPupList = x22 + dxPupList
805 yPupList = y22 + dyPupList
806 xPupList = np.append(xPupList, x32 + dxPupList)
807 yPupList = np.append(yPupList, y32 + dyPupList)
808 xPupList = np.append(xPupList, x40 + dxPupList)
809 yPupList = np.append(yPupList, y40 + dyPupList)
811 # this is what the chipNames ought to be for these points
812 chipNameControl = np.array(['Det22'] * len(dxPupList))
813 chipNameControl = np.append(chipNameControl, ['Det32'] * len(dxPupList))
814 chipNameControl = np.append(chipNameControl, ['Det40'] * len(dxPupList))
816 chipNameTest = chipNameFromPupilCoords(xPupList, yPupList, camera=self.camera)
818 # verify that the test points fall on the expected chips
819 np.testing.assert_array_equal(chipNameControl, chipNameTest)
821 # Note, the somewhat backwards way in which we go from dxPixList to
822 # xPixControl is due to the fact that pixel coordinates are actually
823 # aligned so that the x-axis is along the read-out direction, which
824 # makes positive x in pixel coordinates correspond to positive y
825 # in pupil coordinates
826 xPixControl = 1999.5 + dyPixList
827 yPixControl = 1999.5 - dxPixList
828 xPixControl = np.append(xPixControl, 1999.5 + dyPixList)
829 yPixControl = np.append(yPixControl, 1999.5 - dxPixList)
830 xPixControl = np.append(xPixControl, 1999.5 + dyPixList)
831 yPixControl = np.append(yPixControl, 1999.5 - dxPixList)
833 # verify that the pixel coordinates are as expected to within 0.01 pixel
834 xPixTest, yPixTest = pixelCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera,
835 includeDistortion=False)
837 np.testing.assert_array_almost_equal(xPixTest, xPixControl, 2)
838 np.testing.assert_array_almost_equal(yPixTest, yPixControl, 2)
840 # now test that we get the same results when we pass the pupil coords in
841 # one at a time
842 for ix in range(len(xPupList)):
843 xpx_f, ypx_f = pixelCoordsFromPupilCoords(xPupList[ix], yPupList[ix],
844 camera=self.camera,
845 includeDistortion=False)
846 self.assertIsInstance(xpx_f, np.float)
847 self.assertIsInstance(ypx_f, np.float)
848 self.assertAlmostEqual(xpx_f, xPixTest[ix], 12)
849 self.assertAlmostEqual(ypx_f, yPixTest[ix], 12)
851 def testOffChipResults(self):
852 """
853 Test that the results of the pixelCoords methods make sense in the case
854 that you specify a chip name that is not necessarily the chip on which
855 the object actually fell.
857 Note that the test camera has a platescale of 0.02 arcsec per pixel
858 (2.0 arcsec per mm encoded in CameraForUnitTests.py and 10 microns per
859 pixel encoded in cameraData/focalplanelayout.txt). We will use that to
860 set the control values for our unit test.
862 Note: This unit test will fail if the test camera ever changes.
864 Note: Because we have already tested the self-consistency of
865 pixelCoordsFromPupilCoords and pixelCoordsFromRaDec, we will
866 only be testing pixelCoordsFromPupilCoords here, because it
867 is easier.
868 """
870 arcsecPerPixel = 0.2
871 arcsecPerMicron = 0.02
873 # list a bunch of detector centers in radians
874 x22 = 0.0
875 y22 = 0.0
877 x32 = 0.008
878 y32 = 0.0
880 x40 = 0.0
881 y40 = -0.012
883 # assemble a bunch of displacements in pixels
884 dxPixList = []
885 dyPixList = []
886 for xx in np.arange(-1000.0, 1000.0, 250.0):
887 for yy in np.arange(-1000.0, 1000.0, 250.0):
888 dxPixList.append(xx)
889 dyPixList.append(yy)
891 dxPixList = np.array(dxPixList)
892 dyPixList = np.array(dyPixList)
894 # convert to radians
895 dxPupList = radiansFromArcsec(dxPixList*arcsecPerPixel)
896 dyPupList = radiansFromArcsec(dyPixList*arcsecPerPixel)
898 # assemble a bunch of test pupil coordinate pairs
899 xPupList = x22 + dxPupList
900 yPupList = y22 + dyPupList
901 xPupList = np.append(xPupList, x32 + dxPupList)
902 yPupList = np.append(yPupList, y32 + dyPupList)
903 xPupList = np.append(xPupList, x40 + dxPupList)
904 yPupList = np.append(yPupList, y40 + dyPupList)
906 # this is what the chipNames ought to be for these points
907 chipNameControl = np.array(['R:2,2 S:1,1'] * len(dxPupList))
908 chipNameControl = np.append(chipNameControl, ['R:3,2 S:0,1'] * len(dxPupList))
909 chipNameControl = np.append(chipNameControl, ['R:2,1 S:1,1'] * len(dxPupList))
911 chipNameTest = chipNameFromPupilCoords(xPupList, yPupList, camera=self.camera)
912 print(np.unique(chipNameTest))
914 # verify that the test points fall on the expected chips
915 np.testing.assert_array_equal(chipNameControl, chipNameTest)
918 """Something about the new camera doesn't match, so this doesn't quite work.
919 Commenting this section out for now - scale wrong maybe? Or bigger camera (and
920 thus the pixels don't match because pixels within ccd vs. focal plane?)?
922 # Note, the somewhat backwards way in which we go from dxPupList to
923 # xPixControl is due to the fact that pixel coordinates are actually
924 # aligned so that the x-axis is along the read-out direction, which
925 # makes positive x in pixel coordinates correspond to positive y
926 # in pupil coordinates
927 xPixControl = 1000 + arcsecFromRadians(yPupList - y40)/arcsecPerPixel
928 yPixControl = 1000 - arcsecFromRadians(xPupList - x40)/arcsecPerPixel
930 # verify that the pixel coordinates are as expected to within 0.01 pixel
931 inputChipNames = ['R:2,2 S:1,1'] * len(xPupList)
932 xPixTest, yPixTest = pixelCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera,
933 includeDistortion=False,
934 chipName=inputChipNames)
936 np.testing.assert_array_almost_equal(xPixTest, xPixControl, 2)
937 np.testing.assert_array_almost_equal(yPixTest, yPixControl, 2)
939 # now test that we get the same results when we pass the pupil coords in
940 # one at a time
941 for ix in range(len(xPupList)):
942 xpx_f, ypx_f = pixelCoordsFromPupilCoords(xPupList[ix], yPupList[ix],
943 camera=self.camera,
944 includeDistortion=False,
945 chipName=inputChipNames[ix])
946 self.assertIsInstance(xpx_f, np.float)
947 self.assertIsInstance(ypx_f, np.float)
948 self.assertAlmostEqual(xpx_f, xPixTest[ix], 12)
949 self.assertAlmostEqual(ypx_f, yPixTest[ix], 12)
951 # We will now use this opportunity to test that pupilCoordsFromPixelCoords
952 # and pixelCoordsFromPupilCoords work when we pass in a list of pixel
953 # coords, but only one chip name
954 xPix_one, yPix_one = pixelCoordsFromPupilCoords(xPupList, yPupList,
955 camera=self.camera,
956 includeDistortion=False,
957 chipName='R:2,1 S:1,1')
959 np.testing.assert_array_almost_equal(xPix_one, xPixTest, 12)
960 np.testing.assert_array_almost_equal(yPix_one, yPixTest, 12)
962 xPix_one, yPix_one = pixelCoordsFromPupilCoords(xPupList, yPupList,
963 camera=self.camera,
964 includeDistortion=False,
965 chipName='R:2,1 S:1,1')
967 np.testing.assert_array_almost_equal(xPix_one, xPixTest, 12)
968 np.testing.assert_array_almost_equal(yPix_one, yPixTest, 12)
970 xPupTest, yPupTest = pupilCoordsFromPixelCoords(xPixTest, yPixTest, 'R:2,1 S:1,1',
971 camera=self.camera,
972 includeDistortion=False)
974 np.testing.assert_array_almost_equal(xPupTest, xPupList, 12)
975 np.testing.assert_array_almost_equal(yPupTest, yPupList, 12)
977 xPupTest, yPupTest = pupilCoordsFromPixelCoords(xPixTest, yPixTest, ['Det40'],
978 camera=self.camera,
979 includeDistortion=False)
981 np.testing.assert_array_almost_equal(xPupTest, xPupList, 12)
982 np.testing.assert_array_almost_equal(yPupTest, yPupList, 12)
983 """
985 def testNaN(self):
986 """
987 Verify that NaNs and Nones input to pixelCoordinate calculation methods result
988 in NaNs coming out
989 """
990 ra0 = 25.0
991 dec0 = -35.0
992 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
993 rotSkyPos=42.0, mjd=42356.0)
995 raList = self.rng.random_sample(100)*100.0/3600.0 + ra0
996 decList = self.rng.random_sample(100)*100.0/3600.0 + dec0
997 chipNameList = chipNameFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0,
998 camera=self.camera)
1000 # make sure that all of the test points actually fall on chips
1001 for name in chipNameList:
1002 self.assertIsNotNone(name)
1004 xPupList, yPupList = pupilCoordsFromRaDec(raList, decList, obs_metadata=obs, epoch=2000.0)
1006 # make sure that none of the test points already result in NaN pixel coordinates
1007 xPixList, yPixList = pixelCoordsFromRaDec(raList, decList, obs_metadata=obs,
1008 epoch=2000.0, camera=self.camera)
1010 for xx, yy in zip(xPixList, yPixList):
1011 self.assertFalse(np.isnan(xx), msg='xx is NaN; should not be')
1012 self.assertFalse(np.isnan(yy), msg='yy is NaN; should not be')
1013 self.assertIsNotNone(xx, None)
1014 self.assertIsNotNone(yy, None)
1016 for badVal in [np.NaN, None]:
1017 raList[5] = badVal
1018 decList[7] = badVal
1019 raList[9] = badVal
1020 decList[9] = badVal
1022 xPixList, yPixList = pixelCoordsFromRaDec(raList, decList, obs_metadata=obs,
1023 epoch=2000.0, camera=self.camera)
1025 for ix, (xx, yy) in enumerate(zip(xPixList, yPixList)):
1026 if ix in [5, 7, 9]:
1027 np.testing.assert_equal(xx, np.NaN)
1028 np.testing.assert_equal(yy, np.NaN)
1029 else:
1030 self.assertFalse(np.isnan(xx), msg='xx is NaN; should not be')
1031 self.assertFalse(np.isnan(yy), msg='yy is NaN; should not be')
1032 self.assertIsNotNone(xx)
1033 self.assertIsNotNone(yy)
1035 xPixList, yPixList = _pixelCoordsFromRaDec(np.radians(raList), np.radians(decList),
1036 obs_metadata=obs, epoch=2000.0, camera=self.camera)
1038 for ix, (xx, yy) in enumerate(zip(xPixList, yPixList)):
1039 if ix in [5, 7, 9]:
1040 np.testing.assert_equal(xx, np.NaN)
1041 np.testing.assert_equal(yy, np.NaN)
1042 else:
1043 self.assertFalse(np.isnan(xx), msg='xx is NaN; should not be')
1044 self.assertFalse(np.isnan(yy), msg='yy is NaN; should not be')
1045 self.assertIsNotNone(xx)
1046 self.assertIsNotNone(yy)
1048 xPupList[5] = badVal
1049 yPupList[7] = badVal
1050 xPupList[9] = badVal
1051 yPupList[9] = badVal
1052 xPixList, yPixList = pixelCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera)
1053 for ix, (xx, yy) in enumerate(zip(xPixList, yPixList)):
1055 # verify the same result if we had passed in pupil coords one-at-a-time
1056 xpx_f, ypx_f = pixelCoordsFromPupilCoords(xPupList[ix], yPupList[ix], camera=self.camera)
1057 self.assertIsInstance(xpx_f, np.float)
1058 self.assertIsInstance(ypx_f, np.float)
1060 if ix in [5, 7, 9]:
1061 np.testing.assert_equal(xx, np.NaN)
1062 np.testing.assert_equal(yy, np.NaN)
1063 np.testing.assert_equal(xpx_f, np.NaN)
1064 np.testing.assert_equal(ypx_f, np.NaN)
1065 else:
1066 self.assertFalse(np.isnan(xx), msg='xx is NaN; should not be')
1067 self.assertFalse(np.isnan(yy), msg='yy is NaN; should not be')
1068 self.assertIsNotNone(xx)
1069 self.assertIsNotNone(yy)
1070 self.assertAlmostEqual(xx, xpx_f, 12)
1071 self.assertAlmostEqual(yy, ypx_f, 12)
1073 def testDistortion(self):
1074 """
1075 Make sure that the results from pixelCoordsFromPupilCoords are different
1076 if includeDistortion is True as compared to if includeDistortion is False
1078 Note: This test passes because the test camera has a pincushion distortion.
1079 If we take that away, the test will no longer pass.
1080 """
1081 xp = radiansFromArcsec((self.rng.random_sample(100)-0.5)*500.0)
1082 yp = radiansFromArcsec((self.rng.random_sample(100)-0.5)*500.0)
1084 xu, yu = pixelCoordsFromPupilCoords(xp, yp, camera=self.camera, includeDistortion=False)
1085 xd, yd = pixelCoordsFromPupilCoords(xp, yp, camera=self.camera, includeDistortion=True)
1087 # just verify that the distorted versus undistorted coordinates vary in the
1088 # 4th decimal place
1089 self.assertRaises(AssertionError,
1090 np.testing.assert_array_almost_equal, xu, xd, 4)
1092 self.assertRaises(AssertionError,
1093 np.testing.assert_array_almost_equal, yu, yd, 4)
1095 # make sure that distortions are also present when we pass pupil coordinates in
1096 # one-at-a-time
1097 for ix in range(len(xp)):
1098 x_f, y_f = pixelCoordsFromPupilCoords(xp[ix], yp[ix], camera=self.camera,
1099 includeDistortion=True)
1101 self.assertAlmostEqual(xd[ix], x_f, 12)
1102 self.assertAlmostEqual(yd[ix], y_f, 12)
1103 self.assertIsInstance(x_f, np.float)
1104 self.assertIsInstance(y_f, np.float)
1107class FocalPlaneCoordTest(unittest.TestCase):
1109 @classmethod
1110 def setUpClass(cls):
1111 cls.camera = LsstSimMapper().camera
1113 @classmethod
1114 def tearDownClass(cls):
1115 del cls.camera
1117 def setUp(self):
1118 self.rng = np.random.RandomState(8374522)
1120 def testConsistency(self):
1121 """
1122 Test that all of the focalPlaneCoord calculation methods
1123 return self-consistent answers.
1124 """
1126 ra0 = 34.1
1127 dec0 = -23.0
1128 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1129 mjd=43257.0, rotSkyPos = 127.0)
1131 raCenter, decCenter = observedFromICRS(np.array([ra0]),
1132 np.array([dec0]),
1133 obs_metadata=obs,
1134 epoch=2000.0)
1136 nStars = 100
1137 raList = self.rng.random_sample(nStars)*1000.0/3600.0 + raCenter[0]
1138 decList = self.rng.random_sample(nStars)*1000.0/3600.0 + decCenter[0]
1140 xPupList, yPupList = pupilCoordsFromRaDec(raList, decList,
1141 obs_metadata=obs,
1142 epoch=2000.0)
1144 xf1, yf1 = focalPlaneCoordsFromRaDec(raList, decList,
1145 obs_metadata=obs,
1146 epoch=2000.0, camera=self.camera)
1148 xf2, yf2 = _focalPlaneCoordsFromRaDec(np.radians(raList),
1149 np.radians(decList),
1150 obs_metadata=obs,
1151 epoch=2000.0, camera=self.camera)
1153 xf3, yf3 = focalPlaneCoordsFromPupilCoords(xPupList, yPupList,
1154 camera=self.camera)
1156 np.testing.assert_array_equal(xf1, xf2)
1157 np.testing.assert_array_equal(xf1, xf3)
1158 np.testing.assert_array_equal(yf1, yf2)
1159 np.testing.assert_array_equal(yf1, yf3)
1161 for x, y in zip(xf1, yf1):
1162 self.assertFalse(np.isnan(x), msg='x is NaN; should not be')
1163 self.assertIsNotNone(x)
1164 self.assertFalse(np.isnan(y), msg='y is NaN; should not be')
1165 self.assertIsNotNone(y)
1167 # now test that focalPlaneCoordsFromRaDec and
1168 # focalPlaneCoordsFromPupilCoords give the same results
1169 # when you pass the inputs in one-by-one
1170 for ix in range(len(xf1)):
1171 x_f, y_f = focalPlaneCoordsFromRaDec(raList[ix], decList[ix],
1172 camera=self.camera,
1173 obs_metadata=obs, epoch=2000.0)
1174 self.assertIsInstance(x_f, float)
1175 self.assertIsInstance(y_f, float)
1176 self.assertEqual(x_f, xf1[ix])
1177 self.assertEqual(y_f, yf1[ix])
1179 x_f, y_f = focalPlaneCoordsFromPupilCoords(xPupList[ix], yPupList[ix],
1180 camera=self.camera)
1181 self.assertIsInstance(x_f, float)
1182 self.assertIsInstance(y_f, float)
1183 self.assertEqual(x_f, xf1[ix])
1184 self.assertEqual(y_f, yf1[ix])
1186 def testExceptions(self):
1187 """
1188 Test that the focalPlaneCoord methods raise the exceptions
1189 (with the correct messages) when they should.
1190 """
1192 ra0 = 34.0
1193 dec0 = -19.0
1194 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1195 rotSkyPos=61.0, mjd=52349.0)
1197 nStars = 10
1198 raList = (self.rng.random_sample(nStars)-0.5) + ra0
1199 decList = (self.rng.random_sample(nStars)-0.5) + dec0
1200 xPupList, yPupList = pupilCoordsFromRaDec(raList, decList,
1201 obs_metadata=obs,
1202 epoch=2000.0)
1204 # verify that an error is raised when you forget to pass
1205 # in a camera
1206 with self.assertRaises(RuntimeError) as context:
1207 xf, yf = focalPlaneCoordsFromPupilCoords(xPupList, yPupList)
1208 self.assertEqual(context.exception.args[0],
1209 "You cannot calculate focal plane coordinates "
1210 "without specifying a camera")
1212 with self.assertRaises(RuntimeError) as context:
1213 xf, yf = focalPlaneCoordsFromRaDec(raList, decList,
1214 obs_metadata=obs,
1215 epoch=2000.0)
1217 self.assertEqual(context.exception.args[0],
1218 "You cannot calculate focal plane coordinates "
1219 "without specifying a camera")
1221 with self.assertRaises(RuntimeError) as context:
1222 xf, yf = _focalPlaneCoordsFromRaDec(raList, decList,
1223 obs_metadata=obs,
1224 epoch=2000.0)
1226 self.assertEqual(context.exception.args[0],
1227 "You cannot calculate focal plane coordinates "
1228 "without specifying a camera")
1230 # test that an error is raised when you pass in something that
1231 # is not a numpy array
1232 with self.assertRaises(RuntimeError) as context:
1233 xf, yf = focalPlaneCoordsFromPupilCoords(list(xPupList), yPupList,
1234 camera=self.camera)
1235 self.assertIn("The arg xPupil", context.exception.args[0])
1237 with self.assertRaises(RuntimeError) as context:
1238 xf, yf = focalPlaneCoordsFromPupilCoords(xPupList, list(yPupList),
1239 camera=self.camera)
1240 self.assertIn("The input arguments:", context.exception.args[0])
1241 self.assertIn("yPupil", context.exception.args[0])
1243 with self.assertRaises(RuntimeError) as context:
1244 xf, yf = _focalPlaneCoordsFromRaDec(list(raList), decList,
1245 obs_metadata=obs,
1246 epoch=2000.0,
1247 camera=self.camera)
1248 self.assertIn("The arg ra", context.exception.args[0])
1250 with self.assertRaises(RuntimeError) as context:
1251 xf, yf = _focalPlaneCoordsFromRaDec(raList, list(decList),
1252 obs_metadata=obs,
1253 epoch=2000.0,
1254 camera=self.camera)
1255 self.assertIn("The input arguments:", context.exception.args[0])
1256 self.assertIn("dec", context.exception.args[0])
1258 # we do not have to run the test above on focalPlaneCoordsFromRaDec
1259 # because the conversion to radians automatically casts lists into
1260 # numpy arrays
1262 # test that an error is raised if you pass in mismatched numbers
1263 # of x and y coordinates
1264 with self.assertRaises(RuntimeError) as context:
1265 xf, yf = focalPlaneCoordsFromPupilCoords(xPupList, yPupList[0:4],
1266 camera=self.camera)
1267 self.assertEqual(context.exception.args[0],
1268 "The arrays input to focalPlaneCoordsFromPupilCoords "
1269 "all need to have the same length")
1271 with self.assertRaises(RuntimeError) as context:
1272 xf, yf = focalPlaneCoordsFromRaDec(raList, decList[0:4],
1273 obs_metadata=obs,
1274 epoch=2000.0,
1275 camera=self.camera)
1276 self.assertEqual(context.exception.args[0],
1277 "The arrays input to focalPlaneCoordsFromRaDec "
1278 "all need to have the same length")
1280 with self.assertRaises(RuntimeError) as context:
1281 xf, yf = _focalPlaneCoordsFromRaDec(raList, decList[0:4],
1282 obs_metadata=obs,
1283 epoch=2000.0,
1284 camera=self.camera)
1285 self.assertEqual(context.exception.args[0],
1286 "The arrays input to focalPlaneCoordsFromRaDec "
1287 "all need to have the same length")
1289 # test that an error is raised if you call
1290 # focalPlaneCoordsFromRaDec without an ObservationMetaData
1291 with self.assertRaises(RuntimeError) as context:
1292 xf, yf = focalPlaneCoordsFromRaDec(raList, decList,
1293 epoch=2000.0,
1294 camera=self.camera)
1296 self.assertEqual(context.exception.args[0],
1297 "You have to specify an ObservationMetaData to run "
1298 "focalPlaneCoordsFromRaDec")
1300 with self.assertRaises(RuntimeError) as context:
1301 xf, yf = _focalPlaneCoordsFromRaDec(raList, decList,
1302 epoch=2000.0,
1303 camera=self.camera)
1304 self.assertEqual(context.exception.args[0],
1305 "You have to specify an ObservationMetaData to run "
1306 "focalPlaneCoordsFromRaDec")
1308 # test that an error is raised if you pass an ObservationMetaData
1309 # without an mjd into focalPlaneCoordsFromRaDec
1310 obsDummy = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1311 rotSkyPos=112.0)
1312 with self.assertRaises(RuntimeError) as context:
1313 xf, yf = focalPlaneCoordsFromRaDec(raList, decList,
1314 obs_metadata=obsDummy,
1315 epoch=2000.0,
1316 camera=self.camera)
1317 self.assertEqual(context.exception.args[0],
1318 "You need to pass an ObservationMetaData with an "
1319 "mjd into focalPlaneCoordsFromRaDec")
1321 with self.assertRaises(RuntimeError) as context:
1322 xf, yf = _focalPlaneCoordsFromRaDec(raList, decList,
1323 obs_metadata=obsDummy,
1324 epoch=2000.0,
1325 camera=self.camera)
1327 self.assertEqual(context.exception.args[0],
1328 "You need to pass an ObservationMetaData with an "
1329 "mjd into focalPlaneCoordsFromRaDec")
1331 # test that an error is raised if you pass an ObservationMetaData
1332 # without a rotSkyPos into focalPlaneCoordsFromRaDec
1333 obsDummy = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1334 mjd=42356.0)
1335 with self.assertRaises(RuntimeError) as context:
1336 xf, yf = focalPlaneCoordsFromRaDec(raList, decList,
1337 obs_metadata=obsDummy,
1338 epoch=2000.0,
1339 camera=self.camera)
1341 self.assertEqual(context.exception.args[0],
1342 "You need to pass an ObservationMetaData with a "
1343 "rotSkyPos into focalPlaneCoordsFromRaDec")
1345 with self.assertRaises(RuntimeError) as context:
1346 xf, yf = _focalPlaneCoordsFromRaDec(raList, decList,
1347 obs_metadata=obsDummy,
1348 epoch=2000.0,
1349 camera=self.camera)
1350 self.assertEqual(context.exception.args[0],
1351 "You need to pass an ObservationMetaData with a "
1352 "rotSkyPos into focalPlaneCoordsFromRaDec")
1354 @unittest.skip("The test camera has changed")
1355 def testResults(self):
1356 """
1357 Test that the focalPlaneCoords methods give sensible results.
1359 Note: since we have already tested the self-consistency of
1360 focalPlaneCoordsFromPupilCoords and focalPlaneCoordsFromRaDec,
1361 we will only test focalPlaneCoordsFromPupilCoords here, since it
1362 is easier.
1364 Note that the test camera has a platescale of 0.02 arcsec per pixel
1365 (2.0 arcsec per mm encoded in CameraForUnitTests.py and 10 microns
1366 per pixel encoded in cameraData/focalplanelayout.txt). We will use
1367 that to set the control values for our unit test.
1369 Note: This unit test will fail if the test camera ever changes.
1370 """
1372 arcsecPerPixel = 0.02
1373 arcsecPerMicron = 0.002
1374 mmPerArcsec = 0.5
1376 # list a bunch of detector centers in radians
1377 x22 = 0.0
1378 y22 = 0.0
1380 x32 = radiansFromArcsec(40000.0 * arcsecPerMicron)
1381 y32 = 0.0
1383 x40 = radiansFromArcsec(80000.0 * arcsecPerMicron)
1384 y40 = radiansFromArcsec(-80000.0 * arcsecPerMicron)
1386 # assemble a bunch of displacements in pixels
1387 dxPixList = []
1388 dyPixList = []
1389 for xx in np.arange(-1999.0, 1999.0, 500.0):
1390 for yy in np.arange(-1999.0, 1999.0, 500.0):
1391 dxPixList.append(xx)
1392 dyPixList.append(yy)
1394 dxPixList = np.array(dxPixList)
1395 dyPixList = np.array(dyPixList)
1397 # convert to raidans
1398 dxPupList = radiansFromArcsec(dxPixList*arcsecPerPixel)
1399 dyPupList = radiansFromArcsec(dyPixList*arcsecPerPixel)
1401 # assemble a bunch of test pupil coordinate pairs
1402 xPupList = x22 + dxPupList
1403 yPupList = y22 + dyPupList
1404 xPupList = np.append(xPupList, x32 + dxPupList)
1405 yPupList = np.append(yPupList, y32 + dyPupList)
1406 xPupList = np.append(xPupList, x40 + dxPupList)
1407 yPupList = np.append(yPupList, y40 + dyPupList)
1409 # this is what the chipNames ought to be for these points
1410 chipNameControl = np.array(['Det22'] * len(dxPupList))
1411 chipNameControl = np.append(chipNameControl, ['Det32'] * len(dxPupList))
1412 chipNameControl = np.append(chipNameControl, ['Det40'] * len(dxPupList))
1414 chipNameTest = chipNameFromPupilCoords(xPupList, yPupList, camera=self.camera)
1416 # verify that the test points fall on the expected chips
1417 np.testing.assert_array_equal(chipNameControl, chipNameTest)
1419 # convert into millimeters on the focal plane
1420 xFocalControl = arcsecFromRadians(xPupList)*mmPerArcsec
1421 yFocalControl = arcsecFromRadians(yPupList)*mmPerArcsec
1423 xFocalTest, yFocalTest = focalPlaneCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera)
1425 np.testing.assert_array_almost_equal(xFocalTest, xFocalControl, 3)
1426 np.testing.assert_array_almost_equal(yFocalTest, yFocalControl, 3)
1428 def test_pupilCoordsFromFocalPlaneCoords(self):
1429 """
1430 Test that pupilCoordsFromFocalPlaneCoords inverts
1431 focalPlaneCoordsFromPupilCoords
1432 """
1433 rng = np.random.RandomState(88123)
1434 n_pts = 20
1435 x_pup = rng.random_sample(n_pts)*0.05-0.025
1436 y_pup = rng.random_sample(n_pts)*0.05-0.025
1438 x_f, y_f = focalPlaneCoordsFromPupilCoords(x_pup, y_pup,
1439 camera=lsst_camera())
1441 x_p_test, y_p_test = pupilCoordsFromFocalPlaneCoords(x_f, y_f,
1442 camera=lsst_camera())
1444 np.testing.assert_array_almost_equal(arcsecFromRadians(x_pup),
1445 arcsecFromRadians(x_p_test),
1446 decimal=6)
1448 np.testing.assert_array_almost_equal(arcsecFromRadians(y_pup),
1449 arcsecFromRadians(y_p_test),
1450 decimal=6)
1452 # test that it works on scalars, too
1453 for ii in range(len(x_pup)):
1454 (x_p_test,
1455 y_p_test) = pupilCoordsFromFocalPlaneCoords(x_f[ii], y_f[ii],
1456 camera=lsst_camera())
1458 self.assertAlmostEqual(arcsecFromRadians(x_pup[ii]),
1459 arcsecFromRadians(x_p_test),
1460 6)
1462 self.assertAlmostEqual(arcsecFromRadians(y_pup[ii]),
1463 arcsecFromRadians(y_p_test),
1464 6)
1466 del lsst_camera._lsst_camera
1468 def test_pupilCoordsFromFocalPlaneCoordsNaNs(self):
1469 """
1470 Test that pupilCoordsFromFocalPlaneCoords handles NaNs correctly
1471 """
1472 xp, yp = pupilCoordsFromFocalPlaneCoords(1.0, 2.0, camera=lsst_camera())
1473 self.assertFalse(np.isnan(xp))
1474 self.assertFalse(np.isnan(yp))
1475 xp, yp = pupilCoordsFromFocalPlaneCoords(np.NaN, 2.0, camera=lsst_camera())
1476 self.assertTrue(np.isnan(xp))
1477 self.assertTrue(np.isnan(yp))
1478 xp, yp = pupilCoordsFromFocalPlaneCoords(1.0, np.NaN, camera=lsst_camera())
1479 self.assertTrue(np.isnan(xp))
1480 self.assertTrue(np.isnan(yp))
1482 x = np.array([1,2,3,4])
1483 y = np.array([5,6,7,8])
1484 xp, yp = pupilCoordsFromFocalPlaneCoords(x, y, camera=lsst_camera())
1485 for xx, yy in zip(xp, yp):
1486 self.assertFalse(np.isnan(xx))
1487 self.assertFalse(np.isnan(yy))
1489 x = np.array([np.NaN,2,3,4])
1490 y = np.array([5,np.NaN,7,8])
1491 xp, yp = pupilCoordsFromFocalPlaneCoords(x, y, camera=lsst_camera())
1492 self.assertTrue(np.isnan(xp[0]))
1493 self.assertTrue(np.isnan(yp[0]))
1494 self.assertTrue(np.isnan(xp[1]))
1495 self.assertTrue(np.isnan(yp[1]))
1496 for ii in range(2,4):
1497 self.assertFalse(np.isnan(xp[ii]))
1498 self.assertFalse(np.isnan(yp[ii]))
1500 del lsst_camera._lsst_camera
1503class ConversionFromPixelTest(unittest.TestCase):
1505 @classmethod
1506 def setUpClass(cls):
1507 cls.camera = LsstSimMapper().camera
1509 @classmethod
1510 def tearDownClass(cls):
1511 del cls.camera
1513 def setUp(self):
1514 self.rng = np.random.RandomState(543)
1516 def testPupCoordsException(self):
1517 """
1518 Test that pupilCoordsFromPixelCoords raises an exception when you
1519 call it without a camera
1520 """
1521 nStars = 100
1522 xPupList = radiansFromArcsec((self.rng.random_sample(nStars)-0.5)*320.0)
1523 yPupList = radiansFromArcsec((self.rng.random_sample(nStars)-0.5)*320.0)
1524 chipNameList = chipNameFromPupilCoords(xPupList, yPupList, camera=self.camera)
1525 xPix, yPix = pixelCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera)
1526 with self.assertRaises(RuntimeError) as context:
1527 xPupTest, yPupTest = pupilCoordsFromPixelCoords(xPix, yPix, chipNameList)
1528 self.assertEqual(context.exception.args[0],
1529 "You cannot call pupilCoordsFromPixelCoords without specifying "
1530 "a camera")
1532 def testPupCoordsResults(self):
1533 """
1534 Test that the results from pupilCoordsFromPixelCoords are consistent
1535 with the results from pixelCoordsFromPupilCoords
1536 """
1538 nStars = 100
1539 xPupList = radiansFromArcsec((self.rng.random_sample(nStars)-0.5)*320.0)
1540 yPupList = radiansFromArcsec((self.rng.random_sample(nStars)-0.5)*320.0)
1541 chipNameList = chipNameFromPupilCoords(xPupList, yPupList, camera=self.camera)
1542 for includeDistortion in [True, False]:
1543 xPix, yPix = pixelCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera,
1544 includeDistortion=includeDistortion)
1545 xPupTest, yPupTest = pupilCoordsFromPixelCoords(xPix, yPix, chipNameList, camera=self.camera,
1546 includeDistortion=includeDistortion)
1548 dx = arcsecFromRadians(xPupTest-xPupList)
1549 np.testing.assert_array_almost_equal(dx, np.zeros(len(dx)), 9)
1550 dy = arcsecFromRadians(yPupTest-yPupList)
1551 np.testing.assert_array_almost_equal(dy, np.zeros(len(dy)), 9)
1553 ctNaN = 0
1554 for x, y in zip(xPupTest, yPupTest):
1555 if np.isnan(x) or np.isnan(y):
1556 ctNaN += 1
1557 self.assertLess(ctNaN, len(xPupTest)/10)
1559 # test passing in pixel coordinates one at a time
1560 for ix in range(len(xPupList)):
1561 xp_f, yp_f = pupilCoordsFromPixelCoords(xPix[ix], yPix[ix], chipNameList[ix],
1562 camera=self.camera,
1563 includeDistortion=includeDistortion)
1565 self.assertIsInstance(xp_f, np.float)
1566 self.assertIsInstance(yp_f, np.float)
1567 self.assertAlmostEqual(xp_f, xPupTest[ix], 12)
1568 self.assertAlmostEqual(yp_f, yPupTest[ix], 12)
1570 def testPupCoordsNaN(self):
1571 """
1572 Test that points which do not have a chip return NaN for pupilCoordsFromPixelCoords
1573 """
1574 nStars = 10
1575 xPupList = radiansFromArcsec((self.rng.random_sample(nStars)-0.5)*320.0)
1576 yPupList = radiansFromArcsec((self.rng.random_sample(nStars)-0.5)*320.0)
1577 chipNameList = chipNameFromPupilCoords(xPupList, yPupList, camera=self.camera)
1578 chipNameList[5] = None
1579 xPix, yPix = pixelCoordsFromPupilCoords(xPupList, yPupList, camera=self.camera)
1580 xPupTest, yPupTest = pupilCoordsFromPixelCoords(xPix, yPix, chipNameList, camera=self.camera)
1581 np.testing.assert_equal(xPupTest[5], np.NaN)
1582 np.testing.assert_equal(yPupTest[5], np.NaN)
1584 def testRaDecExceptions(self):
1585 """
1586 Test that raDecFromPupilCoords raises exceptions when it is supposed to
1587 """
1588 nStars = 20
1589 ra0 = 45.0
1590 dec0 = -19.0
1591 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1592 mjd=43525.0, rotSkyPos=145.0)
1594 xPixList = self.rng.random_sample(nStars)*4000.0
1595 yPixList = self.rng.random_sample(nStars)*4000.0
1597 chipDexList = self.rng.randint(0, len(self.camera), nStars)
1598 camera_detector_keys = list([det.getName() for det in self.camera])
1599 self.assertGreater(len(camera_detector_keys), 0)
1600 chipNameList = [self.camera[camera_detector_keys[ii]].getName() for ii in chipDexList]
1602 # test that an error is raised if you do not pass in a camera
1603 with self.assertRaises(RuntimeError) as context:
1604 ra, dec = raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1605 obs_metadata=obs, epoch=2000.0)
1606 self.assertEqual(context.exception.args[0],
1607 "You cannot call raDecFromPixelCoords without specifying a camera")
1609 with self.assertRaises(RuntimeError) as context:
1610 ra, dec = _raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1611 obs_metadata=obs, epoch=2000.0)
1612 self.assertEqual(context.exception.args[0],
1613 "You cannot call raDecFromPixelCoords without specifying a camera")
1615 # test that an error is raised if you do not pass in an ObservationMetaData
1616 with self.assertRaises(RuntimeError) as context:
1617 ra, dec = raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1618 epoch=2000.0, camera=self.camera)
1619 self.assertEqual(context.exception.args[0],
1620 "You cannot call raDecFromPixelCoords without an ObservationMetaData")
1622 # test that an error is raised if you do not pass in an ObservationMetaData
1623 with self.assertRaises(RuntimeError) as context:
1624 ra, dec = _raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1625 epoch=2000.0, camera=self.camera)
1626 self.assertEqual(context.exception.args[0],
1627 "You cannot call raDecFromPixelCoords without an ObservationMetaData")
1629 # test that an error is raised if you pass in an ObservationMetaData
1630 # without an mjd
1631 obsDummy = ObservationMetaData(pointingRA=ra0, pointingDec=dec0, rotSkyPos=95.0)
1632 with self.assertRaises(RuntimeError) as context:
1633 ra, dec = raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1634 obs_metadata=obsDummy,
1635 epoch=2000.0, camera=self.camera)
1636 self.assertEqual(context.exception.args[0],
1637 "The ObservationMetaData in raDecFromPixelCoords must have an mjd")
1639 with self.assertRaises(RuntimeError) as context:
1640 ra, dec = _raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1641 obs_metadata=obsDummy,
1642 epoch=2000.0, camera=self.camera)
1643 self.assertEqual(context.exception.args[0],
1644 "The ObservationMetaData in raDecFromPixelCoords must have an mjd")
1646 # test that an error is raised if you pass in an ObservationMetaData
1647 # without a rotSkyPos
1648 obsDummy = ObservationMetaData(pointingRA=ra0, pointingDec=dec0, mjd=43243.0)
1649 with self.assertRaises(RuntimeError) as context:
1650 ra, dec = raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1651 obs_metadata=obsDummy,
1652 epoch=2000.0, camera=self.camera)
1653 self.assertEqual(context.exception.args[0],
1654 "The ObservationMetaData in raDecFromPixelCoords must have a rotSkyPos")
1656 with self.assertRaises(RuntimeError) as context:
1657 ra, dec = _raDecFromPixelCoords(xPixList, yPixList, chipNameList,
1658 obs_metadata=obsDummy,
1659 epoch=2000.0, camera=self.camera)
1660 self.assertEqual(context.exception.args[0],
1661 "The ObservationMetaData in raDecFromPixelCoords must have a rotSkyPos")
1663 # test that an error is raised if you pass in lists of pixel coordinates,
1664 # rather than numpy arrays
1665 with self.assertRaises(RuntimeError) as context:
1666 ra, dec = raDecFromPixelCoords(list(xPixList), yPixList,
1667 chipNameList, obs_metadata=obs,
1668 epoch=2000.0, camera=self.camera)
1669 self.assertIn("The arg xPix", context.exception.args[0])
1671 with self.assertRaises(RuntimeError) as context:
1672 ra, dec = raDecFromPixelCoords(xPixList, list(yPixList),
1673 chipNameList, obs_metadata=obs,
1674 epoch=2000.0, camera=self.camera)
1675 self.assertIn("The input arguments:", context.exception.args[0])
1676 self.assertIn("yPix", context.exception.args[0])
1678 with self.assertRaises(RuntimeError) as context:
1679 ra, dec = _raDecFromPixelCoords(list(xPixList), yPixList,
1680 chipNameList, obs_metadata=obs,
1681 epoch=2000.0, camera=self.camera)
1682 self.assertIn("The arg xPix", context.exception.args[0])
1684 with self.assertRaises(RuntimeError) as context:
1685 ra, dec = _raDecFromPixelCoords(xPixList, list(yPixList),
1686 chipNameList, obs_metadata=obs,
1687 epoch=2000.0, camera=self.camera)
1688 self.assertIn("The input arguments:", context.exception.args[0])
1689 self.assertIn("yPix", context.exception.args[0])
1691 # test that an error is raised if you pass in mismatched lists of
1692 # xPix and yPix
1693 with self.assertRaises(RuntimeError) as context:
1694 ra, dec = raDecFromPixelCoords(xPixList, yPixList[0:13], chipNameList,
1695 obs_metadata=obs, epoch=2000.0, camera=self.camera)
1696 self.assertEqual(context.exception.args[0],
1697 "The arrays input to raDecFromPixelCoords all need to have the same length")
1699 with self.assertRaises(RuntimeError) as context:
1700 ra, dec = _raDecFromPixelCoords(xPixList, yPixList[0:13], chipNameList,
1701 obs_metadata=obs, epoch=2000.0, camera=self.camera)
1702 self.assertEqual(context.exception.args[0],
1703 "The arrays input to raDecFromPixelCoords all need to have the same length")
1705 # test that an error is raised if you do not pass in the same number of chipNames
1706 # as pixel coordinates
1707 with self.assertRaises(RuntimeError) as context:
1708 ra, dec = raDecFromPixelCoords(xPixList, yPixList, ['R:2,2 S:1,1']*22,
1709 obs_metadata=obs, epoch=2000.0, camera=self.camera)
1710 self.assertIn("22 chipNames", context.exception.args[0])
1712 with self.assertRaises(RuntimeError) as context:
1713 ra, dec = _raDecFromPixelCoords(xPixList, yPixList, ['R:2,2 S:1,1']*22,
1714 obs_metadata=obs, epoch=2000.0, camera=self.camera)
1715 self.assertIn("22 chipNames", context.exception.args[0])
1717 def testResults(self):
1718 """
1719 Test that raDecFromPixelCoords results are consistent with
1720 pixelCoordsFromRaDec
1721 """
1722 nStars = 200
1723 ra0 = 45.0
1724 dec0 = -19.0
1725 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1726 mjd=43525.0, rotSkyPos=145.0)
1728 xPixList = self.rng.random_sample(nStars)*3000.0 + 100
1729 yPixList = self.rng.random_sample(nStars)*3000.0 + 100
1731 #chipDexList = self.rng.randint(0, len(self.camera), nStars)
1732 chipDexList = self.rng.randint(0, 1, nStars)
1733 camera_detector_keys = list([det.getName() for det in self.camera])
1734 self.assertGreater(len(camera_detector_keys), 0)
1735 chipNameList = [self.camera[camera_detector_keys[ii]].getName() for ii in chipDexList]
1737 for includeDistortion in [True, False]:
1739 raDeg, decDeg = raDecFromPixelCoords(xPixList, yPixList, chipNameList, obs_metadata=obs,
1740 epoch=2000.0, camera=self.camera,
1741 includeDistortion=includeDistortion)
1743 raRad, decRad = _raDecFromPixelCoords(xPixList, yPixList, chipNameList, obs_metadata=obs,
1744 epoch=2000.0, camera=self.camera,
1745 includeDistortion=includeDistortion)
1747 # first, make sure that the radians and degrees methods agree with each other
1748 dRa = arcsecFromRadians(raRad-np.radians(raDeg))
1749 np.testing.assert_array_almost_equal(dRa, np.zeros(len(raRad)), 9)
1750 dDec = arcsecFromRadians(decRad-np.radians(decDeg))
1751 np.testing.assert_array_almost_equal(dDec, np.zeros(len(decRad)), 9)
1753 # now make sure that the results from raDecFromPixelCoords are consistent
1754 # with the results from pixelCoordsFromRaDec by taking the ra and dec
1755 # arrays found above and feeding them back into pixelCoordsFromRaDec
1756 # and seeing if we get the same results
1757 xPixTest, yPixTest = pixelCoordsFromRaDec(raDeg, decDeg, obs_metadata=obs,
1758 epoch=2000.0, camera=self.camera,
1759 includeDistortion=includeDistortion)
1761 distance = np.sqrt(np.power(xPixTest-xPixList, 2) + np.power(yPixTest-yPixList, 2))
1762 idx = np.where(np.isnan(distance))
1763 self.assertLess(distance.max(), 0.2) # because of the imprecision in _icrsFromObserved,
1764 # this is the best we can get; note that, in our test
1765 # camera, each pixel is 10 microns in size and the
1766 # plate scale is 2 arcsec per mm, so 0.2 pixels is
1767 # 0.004 arcsec
1769 # test passing the pixel coordinates in one at a time
1770 for ix in range(len(xPixList)):
1771 ra_f, dec_f = raDecFromPixelCoords(xPixList[ix], yPixList[ix],
1772 chipNameList[ix], obs_metadata=obs,
1773 epoch=2000.0, camera=self.camera,
1774 includeDistortion=includeDistortion)
1775 self.assertIsInstance(ra_f, np.float)
1776 self.assertIsInstance(dec_f, np.float)
1777 self.assertAlmostEqual(ra_f, raDeg[ix], 12)
1778 self.assertAlmostEqual(dec_f, decDeg[ix], 12)
1780 def testResultsOffChip(self):
1781 """
1782 Test that raDecFromPixelCoords results are consistent with
1783 pixelCoordsFromRaDec with chip names are specified
1785 Note: this is the same test as in testResults, except that
1786 we are going to intentionally make the pixel coordinate lists
1787 fall outside the boundaries of the chips defined in chipNameList
1788 """
1789 nStars = 200
1790 ra0 = 45.0
1791 dec0 = -19.0
1792 obs = ObservationMetaData(pointingRA=ra0, pointingDec=dec0,
1793 mjd=43525.0, rotSkyPos=145.0)
1795 xPixList = self.rng.random_sample(nStars)*4000.0 + 4000.0
1796 yPixList = self.rng.random_sample(nStars)*4000.0 + 4000.0
1798 chipDexList = self.rng.randint(0, len(self.camera), nStars)
1799 camera_detector_keys = list([det.getName() for det in self.camera])
1800 self.assertGreater(len(camera_detector_keys), 0)
1801 chipNameList = [self.camera[camera_detector_keys[ii]].getName() for ii in chipDexList]
1803 for includeDistortion in [True, False]:
1805 raDeg, decDeg = raDecFromPixelCoords(xPixList, yPixList, chipNameList, obs_metadata=obs,
1806 epoch=2000.0, camera=self.camera,
1807 includeDistortion=includeDistortion)
1809 raRad, decRad = _raDecFromPixelCoords(xPixList, yPixList, chipNameList, obs_metadata=obs,
1810 epoch=2000.0, camera=self.camera,
1811 includeDistortion=includeDistortion)
1813 # first, make sure that the radians and degrees methods agree with each other
1814 dRa = arcsecFromRadians(raRad-np.radians(raDeg))
1815 np.testing.assert_array_almost_equal(dRa, np.zeros(len(raRad)), 9)
1816 dDec = arcsecFromRadians(decRad-np.radians(decDeg))
1817 np.testing.assert_array_almost_equal(dDec, np.zeros(len(decRad)), 9)
1819 # now make sure that the results from raDecFromPixelCoords are consistent
1820 # with the results from pixelCoordsFromRaDec by taking the ra and dec
1821 # arrays found above and feeding them back into pixelCoordsFromRaDec
1822 # and seeing if we get the same results
1823 xPixTest, yPixTest = pixelCoordsFromRaDec(raDeg, decDeg, chipName=chipNameList,
1824 obs_metadata=obs,
1825 epoch=2000.0,
1826 camera=self.camera,
1827 includeDistortion=includeDistortion)
1829 distance = np.sqrt(np.power(xPixTest-xPixList, 2) + np.power(yPixTest-yPixList, 2))
1830 self.assertLess(distance.max(), 0.2) # because of the imprecision in _icrsFromObserved,
1831 # this is the best we can get; note that, in our
1832 # test camera, each pixel is 10 microns in size and
1833 # the plate scale is 2 arcsec per mm, so 0.2 pixels is
1834 # 0.004 arcsec
1836 def testDistortion(self):
1837 """
1838 Make sure that the results from pupilCoordsFromPixelCoords are different
1839 if includeDistortion is True as compared to if includeDistortion is False
1841 Note: This test passes because the test camera has a pincushion distortion.
1842 If we take that away, the test will no longer pass.
1843 """
1844 nStars = 200
1845 xPixList = self.rng.random_sample(nStars)*4000.0 + 4000.0
1846 yPixList = self.rng.random_sample(nStars)*4000.0 + 4000.0
1848 chipDexList = self.rng.randint(0, len(self.camera), nStars)
1849 camera_detector_keys = list([det.getName() for det in self.camera])
1850 self.assertGreater(len(camera_detector_keys), 0)
1851 chipNameList = [self.camera[camera_detector_keys[ii]].getName() for ii in chipDexList]
1853 xu, yu = pupilCoordsFromPixelCoords(xPixList, yPixList, chipNameList, camera=self.camera,
1854 includeDistortion=False)
1856 xd, yd = pupilCoordsFromPixelCoords(xPixList, yPixList, chipNameList, camera=self.camera,
1857 includeDistortion=True)
1859 # just verify that the distorted versus undistorted coordinates vary in the 4th decimal
1860 self.assertRaises(AssertionError,
1861 np.testing.assert_array_almost_equal,
1862 arcsecFromRadians(xu),
1863 arcsecFromRadians(xd),
1864 4)
1866 self.assertRaises(AssertionError,
1867 np.testing.assert_array_almost_equal,
1868 arcsecFromRadians(yu),
1869 arcsecFromRadians(yd),
1870 4)
1873class CornerTest(unittest.TestCase):
1875 @classmethod
1876 def setUpClass(cls):
1877 cls.camera = LsstSimMapper().camera
1879 @classmethod
1880 def tearDownClass(cls):
1881 del cls.camera
1883 def testCornerPixels(self):
1884 """
1885 Test the method to get the pixel coordinates of the corner
1886 of a detector
1887 """
1888 det_name = self.camera[12].getName()
1889 corners = getCornerPixels(det_name, self.camera)
1890 # [(0, 0), (0, 3999), (4071, 0), (4071, 3999)]
1891 self.assertEqual(corners[0][0], 0)
1892 self.assertEqual(corners[0][1], 0)
1893 self.assertEqual(corners[1][0], 0)
1894 self.assertEqual(corners[1][1], 3999)
1895 self.assertEqual(corners[2][0], 4071)
1896 self.assertEqual(corners[2][1], 0)
1897 self.assertEqual(corners[3][0], 4071)
1898 self.assertEqual(corners[3][1], 3999)
1899 self.assertEqual(len(corners), 4)
1900 for row in corners:
1901 self.assertEqual(len(row), 2)
1903 def testCornerRaDec_radians(self):
1904 """
1905 Test that the method to return the Ra, Dec values of the corner
1906 of a chip (in radians) works by validating its results against
1907 known pixel corner values and the _raDecFromPixelCoords method,
1908 which is tested separately.
1909 """
1910 obs = ObservationMetaData(pointingRA=23.0, pointingDec=-65.0,
1911 rotSkyPos=52.1, mjd=59582.3)
1913 det_name = self.camera[12].getName()
1914 cornerTest = _getCornerRaDec(det_name, self.camera, obs)
1916 ra_control, dec_control = _raDecFromPixelCoords(np.array([0, 0, 4071, 4071]),
1917 np.array([0, 3999, 0, 3999]),
1918 [det_name]*4,
1919 camera=self.camera,
1920 obs_metadata=obs,
1921 epoch=2000.0, includeDistortion=True)
1923 # Loop over the control values and the corner values, using
1924 # haversine() method to find the angular distance between the
1925 # test and control values. Assert that they are within
1926 # 0.01 milli-arcsecond of each other
1927 for rr1, dd1, cc in zip(ra_control, dec_control, cornerTest):
1928 dd = haversine(rr1, dd1, cc[0], cc[1])
1929 self.assertLess(arcsecFromRadians(dd), 0.00001)
1931 def testCornerRaDec_degrees(self):
1932 """
1933 Test that method to get corner RA, Dec in degrees is consistent
1934 with method to get corner RA, Dec in radians
1935 """
1937 obs = ObservationMetaData(pointingRA=31.0, pointingDec=-45.0,
1938 rotSkyPos=46.2, mjd=59583.4)
1940 det_name = self.camera[1101].getName()
1942 cornerRad = _getCornerRaDec(det_name, self.camera, obs)
1943 cornerDeg = getCornerRaDec(det_name, self.camera, obs)
1944 for cc1, cc2 in zip(cornerRad, cornerDeg):
1945 dd = haversine(cc1[0], cc1[1], np.radians(cc2[0]), np.radians(cc2[1]))
1946 self.assertLess(arcsecFromRadians(dd), 0.000001)
1949class MotionTestCase(unittest.TestCase):
1950 """
1951 This class will contain test methods to verify that the camera utils
1952 work when proper motion, parallax, and radial velocity are not None.
1953 """
1955 @classmethod
1956 def setUpClass(cls):
1957 cls.camera = LsstSimMapper().camera
1959 @classmethod
1960 def tearDownClass(cls):
1961 del cls.camera
1963 def set_data(self, seed):
1964 """
1965 Accept a seed integer. Return an ObservationMetaData
1966 and numpy arrays of RA, Dec (in degrees),
1967 pm_ra, pm_dec, parallax (in arcsec) and v_rad (in km/s)
1968 centered on that bore site.
1969 """
1970 rng = np.random.RandomState(seed)
1971 n_obj = 100
1972 ra = 23.1
1973 dec = -15.6
1974 rotSkyPos = 23.56
1975 mjd = 59723.2
1976 obs = ObservationMetaData(pointingRA=ra, pointingDec=dec,
1977 rotSkyPos=rotSkyPos, mjd=mjd)
1978 # Distribute objects out to rr degrees from ra/dec
1979 rr = rng.random_sample(n_obj)*2
1980 theta = rng.random_sample(n_obj)*2.0*np.pi
1981 ra_list = ra + rr*np.cos(theta)
1982 dec_list = dec + rr*np.sin(theta)
1983 pm_ra = rng.random_sample(n_obj)*20.0 - 10.0
1984 pm_dec = rng.random_sample(n_obj)*20.0 - 10.0
1985 parallax = rng.random_sample(n_obj)*1.0 - 0.5
1986 v_rad = rng.random_sample(n_obj)*600.0 - 300.0
1987 return obs, ra_list, dec_list, pm_ra, pm_dec, parallax, v_rad
1989 def test_chip_name(self):
1990 """
1991 Test that chipNameFromRaDec with non-zero proper motion etc.
1992 agrees with chipNameFromPupilCoords when pupilCoords are
1993 calculated with the same proper motion, etc.
1994 """
1995 (obs, ra_list, dec_list,
1996 pm_ra_list, pm_dec_list,
1997 parallax_list, v_rad_list) = self.set_data(8231)
1999 for is_none in ('pm_ra', 'pm_dec', 'parallax', 'v_rad'):
2000 pm_ra = pm_ra_list
2001 pm_dec = pm_dec_list
2002 parallax = parallax_list
2003 v_rad = v_rad_list
2005 if is_none == 'pm_ra':
2006 pm_ra = None
2007 elif is_none == 'pm_dec':
2008 pm_dec = None
2009 elif is_none == 'parallax':
2010 parallax = None
2011 elif is_none == 'v_rad':
2012 v_rad = None
2014 xp, yp = pupilCoordsFromRaDec(ra_list, dec_list,
2015 pm_ra=pm_ra, pm_dec=pm_dec,
2016 parallax=parallax, v_rad=v_rad,
2017 obs_metadata=obs)
2019 name_control = chipNameFromPupilCoords(xp, yp, camera=self.camera)
2021 name_test = chipNameFromRaDec(ra_list, dec_list,
2022 pm_ra=pm_ra, pm_dec=pm_dec,
2023 parallax=parallax, v_rad=v_rad,
2024 obs_metadata=obs, camera=self.camera)
2026 name_radians = _chipNameFromRaDec(np.radians(ra_list), np.radians(dec_list),
2027 pm_ra=radiansFromArcsec(pm_ra),
2028 pm_dec=radiansFromArcsec(pm_dec),
2029 parallax=radiansFromArcsec(parallax), v_rad=v_rad,
2030 obs_metadata=obs, camera=self.camera)
2032 np.testing.assert_array_equal(name_control, name_test)
2033 np.testing.assert_array_equal(name_control, name_radians)
2034 self.assertGreater(len(np.unique(name_control.astype(str))), 4)
2035 self.assertLess(len(np.where(np.equal(name_control, None))[0]), 2*len(name_control)/3)
2037 def test_pixel_coords(self):
2038 """
2039 Test that pixelCoordsFromRaDec with non-zero proper motion etc.
2040 agrees with pixelCoordsFromPupilCoords when pupilCoords are
2041 calculated with the same proper motion, etc.
2042 """
2043 (obs, ra_list, dec_list,
2044 pm_ra_list, pm_dec_list,
2045 parallax_list, v_rad_list) = self.set_data(72)
2047 for is_none in ('pm_ra', 'pm_dec', 'parallax', 'v_rad'):
2048 pm_ra = pm_ra_list
2049 pm_dec = pm_dec_list
2050 parallax = parallax_list
2051 v_rad = v_rad_list
2053 if is_none == 'pm_ra':
2054 pm_ra = None
2055 elif is_none == 'pm_dec':
2056 pm_dec = None
2057 elif is_none == 'parallax':
2058 parallax = None
2059 elif is_none == 'v_rad':
2060 v_rad = None
2062 xp, yp = pupilCoordsFromRaDec(ra_list, dec_list,
2063 pm_ra=pm_ra, pm_dec=pm_dec,
2064 parallax=parallax, v_rad=v_rad,
2065 obs_metadata=obs)
2067 xpx_control, ypx_control = pixelCoordsFromPupilCoords(xp, yp, camera=self.camera)
2069 xpx_test, ypx_test = pixelCoordsFromRaDec(ra_list, dec_list,
2070 pm_ra=pm_ra, pm_dec=pm_dec,
2071 parallax=parallax, v_rad=v_rad,
2072 obs_metadata=obs, camera=self.camera)
2074 xpx_radians, ypx_radians = _pixelCoordsFromRaDec(np.radians(ra_list), np.radians(dec_list),
2075 pm_ra=radiansFromArcsec(pm_ra),
2076 pm_dec=radiansFromArcsec(pm_dec),
2077 parallax=radiansFromArcsec(parallax),
2078 v_rad=v_rad,
2079 obs_metadata=obs, camera=self.camera)
2081 np.testing.assert_array_equal(xpx_control, xpx_test)
2082 np.testing.assert_array_equal(ypx_control, ypx_test)
2083 np.testing.assert_array_equal(xpx_control, xpx_radians)
2084 np.testing.assert_array_equal(ypx_control, ypx_radians)
2085 self.assertLess(len(np.where(np.isnan(xpx_control))[0]), 2*len(xpx_control)/3)
2087 def test_focal_plane_coords(self):
2088 """
2089 Test that focalPlaneCoordsFromRaDec with non-zero proper motion etc.
2090 agrees with focalPlaneCoordsFromPupilCoords when pupilCoords are
2091 calculated with the same proper motion, etc.
2092 """
2093 (obs, ra_list, dec_list,
2094 pm_ra_list, pm_dec_list,
2095 parallax_list, v_rad_list) = self.set_data(72)
2097 for is_none in ('pm_ra', 'pm_dec', 'parallax', 'v_rad'):
2098 pm_ra = pm_ra_list
2099 pm_dec = pm_dec_list
2100 parallax = parallax_list
2101 v_rad = v_rad_list
2103 if is_none == 'pm_ra':
2104 pm_ra = None
2105 elif is_none == 'pm_dec':
2106 pm_dec = None
2107 elif is_none == 'parallax':
2108 parallax = None
2109 elif is_none == 'v_rad':
2110 v_rad = None
2112 xp, yp = pupilCoordsFromRaDec(ra_list, dec_list,
2113 pm_ra=pm_ra, pm_dec=pm_dec,
2114 parallax=parallax, v_rad=v_rad,
2115 obs_metadata=obs)
2117 xf_control, yf_control = focalPlaneCoordsFromPupilCoords(xp, yp, camera=self.camera)
2119 xf_test, yf_test = focalPlaneCoordsFromRaDec(ra_list, dec_list,
2120 pm_ra=pm_ra, pm_dec=pm_dec,
2121 parallax=parallax, v_rad=v_rad,
2122 obs_metadata=obs, camera=self.camera)
2124 xf_radians, yf_radians = _focalPlaneCoordsFromRaDec(np.radians(ra_list), np.radians(dec_list),
2125 pm_ra=radiansFromArcsec(pm_ra),
2126 pm_dec=radiansFromArcsec(pm_dec),
2127 parallax=radiansFromArcsec(parallax),
2128 v_rad=v_rad, obs_metadata=obs,
2129 camera=self.camera)
2131 np.testing.assert_array_equal(xf_control, xf_test)
2132 np.testing.assert_array_equal(yf_control, yf_test)
2133 np.testing.assert_array_equal(xf_control, xf_radians)
2134 np.testing.assert_array_equal(yf_control, yf_radians)
2135 self.assertEqual(len(np.where(np.isnan(xf_control))[0]), 0)
2136 self.assertEqual(len(np.where(np.isnan(yf_control))[0]), 0)
2139class MemoryTestClass(lsst.utils.tests.MemoryTestCase):
2140 pass
2142if __name__ == "__main__": 2142 ↛ 2143line 2142 didn't jump to line 2143, because the condition on line 2142 was never true
2143 lsst.utils.tests.init()
2144 unittest.main()