Coverage for python/lsst/obs/hsc/hscPupil.py: 11%

63 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-04 11:49 +0000

1# 

2# LSST Data Management System 

3# Copyright 2017 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

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

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

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

11# (at your option) any later version. 

12# 

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

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

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

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23from lsst.afw.cameraGeom import PupilFactory 

24from lsst.geom import Angle, degrees 

25import numpy as np 

26 

27 

28class HscPupilFactory(PupilFactory): 

29 """!Pupil obscuration function factory for HSC. 

30 """ 

31 def __init__(self, visitInfo, pupilSize, npix, doCenter=True): 

32 """!Construct a PupilFactory. 

33 

34 @param[in] visitInfo VisitInfo object for a particular exposure. 

35 @param[in] pupilSize Size in meters of constructed Pupils. 

36 @param[in] npix Constructed Pupils will be npix x npix. 

37 @param[in] doCenter Center illuminated pixels in pupil. 

38 """ 

39 PupilFactory.__init__(self, visitInfo, pupilSize, npix) 

40 

41 hra = self._horizonRotAngle() 

42 hraRad = hra.asRadians() 

43 rot = np.array([[np.cos(hraRad), np.sin(hraRad)], 

44 [-np.sin(hraRad), np.cos(hraRad)]]) 

45 

46 # Compute spider shadow parameters accounting for rotation angle. 

47 # Location where pairs of struts meet near prime focus. 

48 unrotStartPos = [np.array([0.43, 0.43]), 

49 np.array([0.43, 0.43]), 

50 np.array([-0.43, -0.43]), 

51 np.array([-0.43, -0.43])] 

52 # Half angle between pair of struts that meet at Subaru prime focus 

53 # ring. 

54 strutAngle = 51.75*degrees 

55 alpha = strutAngle - 45.0*degrees 

56 unrotAngles = [90*degrees + alpha, 

57 -alpha, 

58 180*degrees - alpha, 

59 270*degrees + alpha] 

60 # Apply rotation and save the results 

61 self._spiderStartPos = [] 

62 self._spiderAngles = [] 

63 for pos, angle in zip(unrotStartPos, unrotAngles): 

64 self._spiderStartPos.append(np.dot(rot, pos)) 

65 self._spiderAngles.append(angle - hra) 

66 self._doCenter = doCenter 

67 

68 telescopeDiameter = 8.2 # meters 

69 

70 def _horizonRotAngle(self): 

71 """!Compute rotation angle of camera with respect to horizontal 

72 coordinates from self.visitInfo. 

73 

74 @returns horizon rotation angle. 

75 """ 

76 observatory = self.visitInfo.getObservatory() 

77 lat = observatory.getLatitude() 

78 lon = observatory.getLongitude() 

79 radec = self.visitInfo.getBoresightRaDec() 

80 ra = radec.getRa() 

81 dec = radec.getDec() 

82 era = self.visitInfo.getEra() 

83 ha = (era + lon - ra).wrap() 

84 alt = self.visitInfo.getBoresightAzAlt().getLatitude() 

85 

86 # parallactic angle 

87 sinParAng = (np.cos(lat.asRadians()) * np.sin(ha.asRadians()) 

88 / np.cos(alt.asRadians())) 

89 cosParAng = np.sqrt(1 - sinParAng*sinParAng) 

90 if dec > lat: 

91 cosParAng = -cosParAng 

92 parAng = Angle(np.arctan2(sinParAng, cosParAng)) 

93 

94 bra = self.visitInfo.getBoresightRotAngle() 

95 return (bra - parAng).wrap() 

96 

97 def getPupil(self, point): 

98 """!Calculate a Pupil at a given point in the focal plane. 

99 

100 @param point Point2D indicating focal plane coordinates. 

101 @returns Pupil 

102 """ 

103 subaruRadius = self.telescopeDiameter/2 

104 

105 hscFrac = 0.231 # linear fraction 

106 # radius of HSC camera shadow in meters 

107 hscRadius = hscFrac * subaruRadius 

108 

109 subaruStrutThick = 0.22 # meters 

110 

111 # See DM-8589 for more detailed description of following parameters 

112 # d(lensCenter)/d(theta) in meters per degree 

113 lensRate = 0.0276 * 3600 / 128.9 * subaruRadius 

114 # d(cameraCenter)/d(theta) in meters per degree 

115 hscRate = 0.00558 * 3600 / 128.9 * subaruRadius 

116 # Projected radius of lens obstruction in meters 

117 lensRadius = subaruRadius * 138./128.98 

118 

119 # Focal plane location in degrees 

120 hscPlateScale = 0.168 # arcsec/pixel 

121 thetaX = point.getX() * hscPlateScale / 3600 

122 thetaY = point.getY() * hscPlateScale / 3600 

123 

124 pupil = self._fullPupil() 

125 # Cut out primary mirror exterior 

126 self._cutCircleExterior(pupil, (0.0, 0.0), subaruRadius) 

127 # Cut out camera shadow 

128 camX = thetaX * hscRate 

129 camY = thetaY * hscRate 

130 self._cutCircleInterior(pupil, (camX, camY), hscRadius) 

131 # Cut outer edge where L1 is too small 

132 lensX = thetaX * lensRate 

133 lensY = thetaY * lensRate 

134 self._cutCircleExterior(pupil, (lensX, lensY), lensRadius) 

135 # Cut out spider shadow 

136 for pos, angle in zip(self._spiderStartPos, self._spiderAngles): 

137 x = pos[0] + camX 

138 y = pos[1] + camY 

139 self._cutRay(pupil, (x, y), angle, subaruStrutThick) 

140 if self._doCenter: 

141 self._centerPupil(pupil) 

142 return pupil