Coverage for python/lsst/obs/base/camera_tests.py: 25%
45 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-11 10:18 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-11 10:18 +0000
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 abc
23import collections
24import math
26import lsst.geom
27from lsst.afw.cameraGeom import FIELD_ANGLE, FOCAL_PLANE
28from lsst.daf.butler import Butler
30__all__ = ["CameraTests"]
33class CameraTests(metaclass=abc.ABCMeta):
34 """Tests that the butler returns a useable Camera.
36 In the subclasses's setUp():
37 * Call setUp_camera() to fill in required parameters.
38 """
40 def setUp_camera(
41 self,
42 camera_name=None,
43 n_detectors=None,
44 first_detector_name=None,
45 plate_scale=None,
46 ):
47 """Set up the necessary variables for camera tests.
49 Parameters
50 ----------
52 camera_name : `str`
53 name of this camera
54 n_detectors : `int`
55 number of detectors in this camera
56 first_detector_name : `str`
57 name of the first detector in this camera
58 plate_scale : `lsst.geom.Angle`
59 plate scale at center of focal plane, as angle-on-sky/mm
60 """
61 fields = [
62 "camera_name",
63 "n_detectors",
64 "first_detector_name",
65 "plate_scale",
66 ]
67 CameraData = collections.namedtuple("CameraData", fields)
68 self.camera_data = CameraData(
69 camera_name=camera_name,
70 n_detectors=n_detectors,
71 first_detector_name=first_detector_name,
72 plate_scale=plate_scale,
73 )
75 def _butler_args(self):
76 """Arguments to pass to butler get in addition to camera dataset type.
78 Returns
79 -------
80 kwargs : `dict`
81 The arguments to add.
82 """
83 kwargs = {}
84 if isinstance(self.butler, Butler):
85 kwargs = dict(instrument=self.camera_data.camera_name)
86 return kwargs
88 def test_iterable(self):
89 """Simplest camera test: can we get a Camera instance, and does
90 iterating return Detectors?"""
91 camera = self.butler.get("camera", **self._butler_args())
92 self.assertIsInstance(camera, lsst.afw.cameraGeom.Camera)
93 for detector in camera:
94 msg = "Failed for detector={}".format(detector)
95 self.assertIsInstance(detector, lsst.afw.cameraGeom.Detector, msg=msg)
97 def test_camera_butler(self):
98 """Check that the butler returns the right type of camera."""
99 camera = self.butler.get("camera", **self._butler_args())
100 self.assertEqual(camera.getName(), self.camera_data.camera_name)
101 self.assertEqual(len(camera), self.camera_data.n_detectors)
102 self.assertEqual(next(iter(camera)).getName(), self.camera_data.first_detector_name)
104 def test_plate_scale(self):
105 """Check the plate scale at center of focal plane
107 Check plate_scale using the FOCAL_PLANE to FIELD_ANGLE transform
108 from the camera.
109 """
110 plate_scale = self.camera_data.plate_scale
111 self.assertIsNotNone(plate_scale)
112 camera = self.butler.get("camera", **self._butler_args())
113 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE)
114 focalPlaneRadiusMm = 0.001 # an offset small enough to be in the linear regime
115 for offsetAngleRad in (0.0, 0.65, 1.3): # direction of offset; a few arbitrary angles
116 cosAng = math.cos(offsetAngleRad)
117 sinAng = math.sin(offsetAngleRad)
118 fieldAngleRadians = focalPlaneToFieldAngle.applyForward(
119 lsst.geom.Point2D(cosAng * focalPlaneRadiusMm, sinAng * focalPlaneRadiusMm)
120 )
121 fieldAngleRadius = math.hypot(*fieldAngleRadians) * lsst.geom.radians
122 measuredScale1 = fieldAngleRadius / focalPlaneRadiusMm
123 self.assertAnglesAlmostEqual(measuredScale1, plate_scale)
125 focalPlanePos = focalPlaneToFieldAngle.applyInverse(
126 lsst.geom.Point2D(
127 fieldAngleRadius.asRadians() * cosAng, fieldAngleRadius.asRadians() * sinAng
128 )
129 )
130 focalPlaneRadiusMm2 = math.hypot(*focalPlanePos)
131 measureScale2 = fieldAngleRadius / focalPlaneRadiusMm2
132 self.assertAnglesAlmostEqual(measureScale2, plate_scale)