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

44 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-04 10:08 +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/>. 

21 

22import collections 

23import math 

24 

25import lsst.geom 

26from lsst.afw.cameraGeom import FIELD_ANGLE, FOCAL_PLANE 

27from lsst.daf.butler import Butler 

28 

29__all__ = ["CameraTests"] 

30 

31 

32class CameraTests: 

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

34 

35 In the subclasses's setUp(): 

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

37 """ 

38 

39 def setUp_camera( 

40 self, 

41 camera_name=None, 

42 n_detectors=None, 

43 first_detector_name=None, 

44 plate_scale=None, 

45 ): 

46 """Set up the necessary variables for camera tests. 

47 

48 Parameters 

49 ---------- 

50 camera_name : `str` 

51 name of this camera 

52 n_detectors : `int` 

53 number of detectors in this camera 

54 first_detector_name : `str` 

55 name of the first detector in this camera 

56 plate_scale : `lsst.geom.Angle` 

57 plate scale at center of focal plane, as angle-on-sky/mm 

58 """ 

59 fields = [ 

60 "camera_name", 

61 "n_detectors", 

62 "first_detector_name", 

63 "plate_scale", 

64 ] 

65 CameraData = collections.namedtuple("CameraData", fields) 

66 self.camera_data = CameraData( 

67 camera_name=camera_name, 

68 n_detectors=n_detectors, 

69 first_detector_name=first_detector_name, 

70 plate_scale=plate_scale, 

71 ) 

72 

73 def _butler_args(self): 

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

75 

76 Returns 

77 ------- 

78 kwargs : `dict` 

79 The arguments to add. 

80 """ 

81 kwargs = {} 

82 if isinstance(self.butler, Butler): 

83 kwargs = {"instrument": self.camera_data.camera_name} 

84 return kwargs 

85 

86 def test_iterable(self): 

87 """Get a camera instance and check it is an iterable.""" 

88 camera = self.butler.get("camera", **self._butler_args()) 

89 self.assertIsInstance(camera, lsst.afw.cameraGeom.Camera) 

90 for detector in camera: 

91 msg = f"Failed for detector={detector}" 

92 self.assertIsInstance(detector, lsst.afw.cameraGeom.Detector, msg=msg) 

93 

94 def test_camera_butler(self): 

95 """Check that the butler returns the right type of camera.""" 

96 camera = self.butler.get("camera", **self._butler_args()) 

97 self.assertEqual(camera.getName(), self.camera_data.camera_name) 

98 self.assertEqual(len(camera), self.camera_data.n_detectors) 

99 self.assertEqual(next(iter(camera)).getName(), self.camera_data.first_detector_name) 

100 

101 def test_plate_scale(self): 

102 """Check the plate scale at center of focal plane. 

103 

104 Check plate_scale using the FOCAL_PLANE to FIELD_ANGLE transform 

105 from the camera. 

106 """ 

107 plate_scale = self.camera_data.plate_scale 

108 self.assertIsNotNone(plate_scale) 

109 camera = self.butler.get("camera", **self._butler_args()) 

110 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE) 

111 focalPlaneRadiusMm = 0.001 # an offset small enough to be in the linear regime 

112 for offsetAngleRad in (0.0, 0.65, 1.3): # direction of offset; a few arbitrary angles 

113 cosAng = math.cos(offsetAngleRad) 

114 sinAng = math.sin(offsetAngleRad) 

115 fieldAngleRadians = focalPlaneToFieldAngle.applyForward( 

116 lsst.geom.Point2D(cosAng * focalPlaneRadiusMm, sinAng * focalPlaneRadiusMm) 

117 ) 

118 fieldAngleRadius = math.hypot(*fieldAngleRadians) * lsst.geom.radians 

119 measuredScale1 = fieldAngleRadius / focalPlaneRadiusMm 

120 self.assertAnglesAlmostEqual(measuredScale1, plate_scale) 

121 

122 focalPlanePos = focalPlaneToFieldAngle.applyInverse( 

123 lsst.geom.Point2D( 

124 fieldAngleRadius.asRadians() * cosAng, fieldAngleRadius.asRadians() * sinAng 

125 ) 

126 ) 

127 focalPlaneRadiusMm2 = math.hypot(*focalPlanePos) 

128 measureScale2 = fieldAngleRadius / focalPlaneRadiusMm2 

129 self.assertAnglesAlmostEqual(measureScale2, plate_scale)