22 __all__ = [
'PupilFactory',
'Pupil']
28 """Pupil obscuration function.
32 illuminated : `numpy.ndarray`, (Nx, Ny)
33 2D numpy array indicating which parts of the pupil plane are
36 Size of pupil plane array in meters. Note that this may be larger
37 than the actual diameter of the illuminated pupil to accommodate
40 Sampling interval of pupil plane array in meters.
50 """Pupil obscuration function factory for use with Fourier optics.
54 visitInfo : `lsst.afw.image.VisitInfo`
55 Visit information for a particular exposure.
57 Size in meters of constructed Pupil array.
58 Note that this may be larger than the actual diameter of the
59 illuminated pupil to accommodate zero-padding.
61 Constructed Pupils will be npix x npix.
64 def __init__(self, visitInfo, pupilSize, npix):
69 u = (np.arange(npix, dtype=np.float64) - (npix - 1)/2) * self.
pupilScale
70 self.u, self.
v = np.meshgrid(u, u)
73 """Calculate a Pupil at a given point in the focal plane.
77 point : `lsst.geom.Point2D`
78 The focal plane coordinates.
83 The Pupil at ``point``.
85 raise NotImplementedError(
86 "PupilFactory not implemented for this camera")
89 def _pointLineDistance(p0, p1, p2):
90 """Compute the right-angle distance between the points given by `p0`
91 and the line that passes through `p1` and `p2`.
95 p0 : `tuple` of `numpy.ndarray`
96 2-tuple of numpy arrays (x, y focal plane coordinates)
97 p1 : ``pair`` of `float`
98 x,y focal plane coordinates
99 p2 : ``pair`` of `float`
100 x,y focal plane coordinates
104 distances : `numpy.ndarray`
105 Numpy array of distances; shape congruent to p0[0].
112 return np.abs(dy21*x0 - dx21*y0 + x2*y1 - y2*x1)/np.hypot(dy21, dx21)
114 def _fullPupil(self):
115 """Make a fully-illuminated Pupil.
120 The illuminated pupil.
122 illuminated = np.ones(self.u.shape, dtype=np.bool)
125 def _cutCircleInterior(self, pupil, p0, r):
126 """Cut out the interior of a circular region from a Pupil.
131 Pupil to modify in place.
132 p0 : `pair`` of `float`
133 2-tuple indicating region center.
135 Circular region radius.
137 r2 = (self.u - p0[0])**2 + (self.
v - p0[1])**2
138 pupil.illuminated[r2 < r**2] =
False
140 def _cutCircleExterior(self, pupil, p0, r):
141 """Cut out the exterior of a circular region from a Pupil.
146 Pupil to modify in place
147 p0 : `pair`` of `float`
148 2-tuple indicating region center.
150 Circular region radius.
152 r2 = (self.u - p0[0])**2 + (self.
v - p0[1])**2
153 pupil.illuminated[r2 > r**2] =
False
155 def _cutRay(self, pupil, p0, angle, thickness):
156 """Cut out a ray from a Pupil.
161 Pupil to modify in place.
162 p0 : `pair`` of `float`
163 2-tuple indicating ray starting point.
164 angle : `pair` of `float`
165 Ray angle measured CCW from +x.
169 angleRad = angle.asRadians()
172 p1 = (p0[0] + 1, p0[1] + np.tan(angleRad))
173 d = PupilFactory._pointLineDistance((self.u, self.
v), p0, p1)
174 pupil.illuminated[(d < 0.5*thickness)
175 & ((self.u - p0[0])*np.cos(angleRad)
176 + (self.
v - p0[1])*np.sin(angleRad) >= 0)] =
False
178 def _centerPupil(self, pupil):
179 """Center the illuminated portion of the pupil in array.
184 Pupil to modify in place
186 def center(arr, axis):
187 smash = np.sum(arr, axis=axis)
188 w = np.where(smash)[0]
189 return int(0.5*(np.min(w)+np.max(w)))
190 ycenter = center(pupil.illuminated, 0)
191 xcenter = center(pupil.illuminated, 1)
192 ytarget = pupil.illuminated.shape[0]//2
193 xtarget = pupil.illuminated.shape[1]//2
194 pupil.illuminated = np.roll(np.roll(pupil.illuminated,