Coverage for python/lsst/obs/base/camera_tests.py: 28%

45 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-16 02:16 -0700

1# This file is part of obs_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import abc 

23import collections 

24import math 

25 

26import lsst.geom 

27from lsst.afw.cameraGeom import FIELD_ANGLE, FOCAL_PLANE 

28from lsst.daf.butler import Butler 

29 

30__all__ = ["CameraTests"] 

31 

32 

33class CameraTests(metaclass=abc.ABCMeta): 

34 """Tests that the butler returns a useable Camera. 

35 

36 In the subclasses's setUp(): 

37 * Call setUp_camera() to fill in required parameters. 

38 """ 

39 

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. 

48 

49 Parameters 

50 ---------- 

51 

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 ) 

74 

75 def _butler_args(self): 

76 """Arguments to pass to butler get in addition to camera dataset type. 

77 

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 

87 

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) 

96 

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) 

103 

104 def test_plate_scale(self): 

105 """Check the plate scale at center of focal plane 

106 

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) 

124 

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)