Coverage for python/lsst/obs/hsc/hscPupil.py: 11%
63 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-12 03:30 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-12 03:30 -0800
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#
23from lsst.afw.cameraGeom import PupilFactory
24from lsst.geom import Angle, degrees
25import numpy as np
28class HscPupilFactory(PupilFactory):
29 """!Pupil obscuration function factory for HSC.
30 """
31 def __init__(self, visitInfo, pupilSize, npix, doCenter=True):
32 """!Construct a PupilFactory.
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)
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)]])
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
68 telescopeDiameter = 8.2 # meters
70 def _horizonRotAngle(self):
71 """!Compute rotation angle of camera with respect to horizontal
72 coordinates from self.visitInfo.
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()
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))
94 bra = self.visitInfo.getBoresightRotAngle()
95 return (bra - parAng).wrap()
97 def getPupil(self, point):
98 """!Calculate a Pupil at a given point in the focal plane.
100 @param point Point2D indicating focal plane coordinates.
101 @returns Pupil
102 """
103 subaruRadius = self.telescopeDiameter/2
105 hscFrac = 0.231 # linear fraction
106 # radius of HSC camera shadow in meters
107 hscRadius = hscFrac * subaruRadius
109 subaruStrutThick = 0.22 # meters
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
119 # Focal plane location in degrees
120 hscPlateScale = 0.168 # arcsec/pixel
121 thetaX = point.getX() * hscPlateScale / 3600
122 thetaY = point.getY() * hscPlateScale / 3600
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