Coverage for python/lsst/sims/utils/FocalPlaneUtils.py : 10%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from builtins import zip
2import numpy as np
3import palpy
4from lsst.sims.utils.CodeUtilities import _validate_inputs
5from lsst.sims.utils import _observedFromICRS, _icrsFromObserved
6from lsst.sims.utils import radiansFromArcsec
8__all__ = ["_pupilCoordsFromObserved",
9 "_pupilCoordsFromRaDec", "pupilCoordsFromRaDec",
10 "_raDecFromPupilCoords", "raDecFromPupilCoords",
11 "_observedFromPupilCoords", "observedFromPupilCoords"]
14def pupilCoordsFromRaDec(ra_in, dec_in,
15 pm_ra=None, pm_dec=None, parallax=None,
16 v_rad=None, includeRefraction=True,
17 obs_metadata=None, epoch=2000.0):
18 """
19 Take an input RA and dec from the sky and convert it to coordinates
20 on the focal plane.
22 This uses PAL's gnomonic projection routine which assumes that the focal
23 plane is perfectly flat. The output is in Cartesian coordinates, assuming
24 that the Celestial Sphere is a unit sphere.
26 The RA, Dec accepted by this method are in the International Celestial
27 Reference System. Before applying the gnomonic projection, this method
28 transforms those RA, Dec into observed geocentric coordinates, applying
29 the effects of precession, nutation, aberration, parallax and refraction.
30 This is done, because the gnomonic projection ought to be applied to what
31 observers actually see, rather than the idealized, above-the-atmosphere
32 coordinates represented by the ICRS.
34 @param [in] ra_in is in degrees (ICRS). Can be either a numpy array or a number.
36 @param [in] dec_in is in degrees (ICRS). Can be either a numpy array or a number.
38 @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (arcsec/yr)
39 Can be a numpy array or a number or None (default=None).
41 @param [in] pm_dec is proper motion in dec (arcsec/yr)
42 Can be a numpy array or a number or None (default=None).
44 @param [in] parallax is parallax in arcsec
45 Can be a numpy array or a number or None (default=None).
47 @param [in] v_rad is radial velocity (km/s)
48 Can be a numpy array or a number or None (default=None).
50 @param [in] includeRefraction is a boolean controlling the application of refraction.
52 @param [in] obs_metadata is an ObservationMetaData instantiation characterizing the
53 telescope location and pointing.
55 @param [in] epoch is the epoch of mean ra and dec in julian years (default=2000.0)
57 @param [out] returns a numpy array whose first row is the x coordinate on the pupil in
58 radians and whose second row is the y coordinate in radians
59 """
61 if pm_ra is not None:
62 pm_ra_in = radiansFromArcsec(pm_ra)
63 else:
64 pm_ra_in = None
66 if pm_dec is not None:
67 pm_dec_in = radiansFromArcsec(pm_dec)
68 else:
69 pm_dec_in = None
71 if parallax is not None:
72 parallax_in = radiansFromArcsec(parallax)
73 else:
74 parallax_in = None
76 return _pupilCoordsFromRaDec(np.radians(ra_in), np.radians(dec_in),
77 pm_ra=pm_ra_in, pm_dec=pm_dec_in,
78 parallax=parallax_in, v_rad=v_rad,
79 includeRefraction=includeRefraction,
80 obs_metadata=obs_metadata, epoch=epoch)
83def _pupilCoordsFromRaDec(ra_in, dec_in,
84 pm_ra=None, pm_dec=None,
85 parallax=None, v_rad=None,
86 includeRefraction=True,
87 obs_metadata=None, epoch=2000.0):
88 """
89 Take an input RA and dec from the sky and convert it to coordinates
90 on the focal plane.
92 This uses PAL's gnomonic projection routine which assumes that the focal
93 plane is perfectly flat. The output is in Cartesian coordinates, assuming
94 that the Celestial Sphere is a unit sphere.
96 The RA, Dec accepted by this method are in the International Celestial
97 Reference System. Before applying the gnomonic projection, this method
98 transforms those RA, Dec into observed geocentric coordinates, applying
99 the effects of precession, nutation, aberration, parallax and refraction.
100 This is done, because the gnomonic projection ought to be applied to what
101 observers actually see, rather than the idealized, above-the-atmosphere
102 coordinates represented by the ICRS.
104 @param [in] ra_in is in radians (ICRS). Can be either a numpy array or a number.
106 @param [in] dec_in is in radians (ICRS). Can be either a numpy array or a number.
108 @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
109 Can be a numpy array or a number or None (default=None).
111 @param [in] pm_dec is proper motion in dec (radians/yr)
112 Can be a numpy array or a number or None (default=None).
114 @param [in] parallax is parallax in radians
115 Can be a numpy array or a number or None (default=None).
117 @param [in] v_rad is radial velocity (km/s)
118 Can be a numpy array or a number or None (default=None).
120 @param [in] includeRefraction is a boolean controlling the application of refraction.
122 @param [in] obs_metadata is an ObservationMetaData instantiation characterizing the
123 telescope location and pointing.
125 @param [in] epoch is the epoch of mean ra and dec in julian years (default=2000.0)
127 @param [out] returns a numpy array whose first row is the x coordinate on the pupil in
128 radians and whose second row is the y coordinate in radians
129 """
131 are_arrays = _validate_inputs([ra_in, dec_in], ['ra_in', 'dec_in'],
132 "pupilCoordsFromRaDec")
134 if obs_metadata is None:
135 raise RuntimeError("Cannot call pupilCoordsFromRaDec without obs_metadata")
137 if obs_metadata.mjd is None:
138 raise RuntimeError("Cannot call pupilCoordsFromRaDec; obs_metadata.mjd is None")
140 if epoch is None:
141 raise RuntimeError("Cannot call pupilCoordsFromRaDec; epoch is None")
143 if obs_metadata.rotSkyPos is None:
144 # there is no observation meta data on which to base astrometry
145 raise RuntimeError("Cannot calculate [x,y]_focal_nominal without obs_metadata.rotSkyPos")
147 if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
148 raise RuntimeError("Cannot calculate [x,y]_focal_nominal without pointingRA and Dec in obs_metadata")
150 ra_obs, dec_obs = _observedFromICRS(ra_in, dec_in,
151 pm_ra=pm_ra, pm_dec=pm_dec,
152 parallax=parallax, v_rad=v_rad,
153 obs_metadata=obs_metadata,
154 epoch=epoch,
155 includeRefraction=includeRefraction)
157 return _pupilCoordsFromObserved(ra_obs, dec_obs, obs_metadata,
158 epoch=epoch, includeRefraction=includeRefraction)
161def _pupilCoordsFromObserved(ra_obs, dec_obs, obs_metadata, epoch=2000.0, includeRefraction=True):
162 """
163 Convert Observed RA, Dec into pupil coordinates
165 Parameters
166 ----------
167 ra_obs is the observed RA in radians
169 dec_obs is the observed Dec in radians
171 obs_metadata is an ObservationMetaData characterizing the telescope location and pointing
173 epoch is the epoch of the mean RA and Dec in julian years (default=2000.0)
175 includeRefraction is a boolean controlling the application of refraction.
177 Returns
178 --------
179 A numpy array whose first row is the x coordinate on the pupil in
180 radians and whose second row is the y coordinate in radians
181 """
183 are_arrays = _validate_inputs([ra_obs, dec_obs], ['ra_obs', 'dec_obs'],
184 "pupilCoordsFromObserved")
186 if obs_metadata.rotSkyPos is None:
187 raise RuntimeError("Cannot call pupilCoordsFromObserved; "
188 "rotSkyPos is None")
190 theta = -1.0*obs_metadata._rotSkyPos
192 ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA,
193 obs_metadata._pointingDec,
194 obs_metadata=obs_metadata,
195 epoch=epoch,
196 includeRefraction=includeRefraction)
198 # palpy.ds2tp performs the gnomonic projection on ra_in and dec_in
199 # with a tangent point at (pointingRA, pointingDec)
200 #
201 if not are_arrays:
202 try:
203 x, y = palpy.ds2tp(ra_obs, dec_obs, ra_pointing, dec_pointing)
204 except:
205 x = np.NaN
206 y = np.NaN
207 else:
208 try:
209 x, y = palpy.ds2tpVector(ra_obs, dec_obs, ra_pointing, dec_pointing)
210 except:
211 # apparently, one of your ra/dec values was improper; we will have to do this
212 # element-wise, putting NaN in the place of the bad values
213 x = []
214 y = []
215 for rr, dd in zip(ra_obs, dec_obs):
216 try:
217 xx, yy = palpy.ds2tp(rr, dd, ra_pointing, dec_pointing)
218 except:
219 xx = np.NaN
220 yy = np.NaN
221 x.append(xx)
222 y.append(yy)
223 x = np.array(x)
224 y = np.array(y)
226 # rotate the result by rotskypos (rotskypos being "the angle of the sky relative to
227 # camera coordinates" according to phoSim documentation) to account for
228 # the rotation of the focal plane about the telescope pointing
230 x_out = x*np.cos(theta) - y*np.sin(theta)
231 y_out = x*np.sin(theta) + y*np.cos(theta)
233 return np.array([x_out, y_out])
236def _observedFromPupilCoords(xPupil, yPupil, obs_metadata=None,
237 includeRefraction=True,
238 epoch=2000.0):
239 """
240 Convert pupil coordinates into observed (RA, Dec)
242 @param [in] xPupil -- pupil coordinates in radians.
243 Can be a numpy array or a number.
245 @param [in] yPupil -- pupil coordinates in radians.
246 Can be a numpy array or a number.
248 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
249 the state of the telescope
251 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
252 transformations (in years; defaults to 2000)
254 @param[in] includeRefraction -- a boolean which controls the effects of refraction
255 (refraction is used when finding the observed coordinates of the boresite specified
256 by obs_metadata)
258 @param [out] a 2-D numpy array in which the first row is observed RA and the second
259 row is observed Dec (both in radians). Note: these are not ICRS coordinates.
260 These are RA and Dec-like coordinates resulting from applying precession, nutation,
261 diurnal aberration and annual aberration on top of ICRS coordinates.
263 WARNING: This method does not account for apparent motion due to parallax.
264 This method is only useful for mapping positions on a theoretical focal plane
265 to positions on the celestial sphere.
266 """
268 are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'],
269 "observedFromPupilCoords")
271 if obs_metadata is None:
272 raise RuntimeError("Cannot call observedFromPupilCoords without obs_metadata")
274 if epoch is None:
275 raise RuntimeError("Cannot call observedFromPupilCoords; epoch is None")
277 if obs_metadata.rotSkyPos is None:
278 raise RuntimeError("Cannot call observedFromPupilCoords without rotSkyPos " +
279 "in obs_metadata")
281 if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
282 raise RuntimeError("Cannot call observedFromPupilCoords " +
283 "without pointingRA, pointingDec in obs_metadata")
285 if obs_metadata.mjd is None:
286 raise RuntimeError("Cannot calculate RA, Dec without mjd " +
287 "in obs_metadata")
289 ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA,
290 obs_metadata._pointingDec,
291 obs_metadata=obs_metadata,
292 epoch=epoch,
293 includeRefraction=includeRefraction)
295 # This is the same as theta in pupilCoordsFromRaDec, except without the minus sign.
296 # This is because we will be reversing the rotation performed in that other method.
297 theta = obs_metadata._rotSkyPos
299 x_g = xPupil*np.cos(theta) - yPupil*np.sin(theta)
300 y_g = xPupil*np.sin(theta) + yPupil*np.cos(theta)
302 # x_g and y_g are now the x and y coordinates
303 # can now use the PALPY method palDtp2s to convert to RA, Dec.
305 if are_arrays:
306 raObs, decObs = palpy.dtp2sVector(x_g, y_g, ra_pointing, dec_pointing)
307 else:
308 raObs, decObs = palpy.dtp2s(x_g, y_g, ra_pointing, dec_pointing)
310 return raObs, decObs
313def observedFromPupilCoords(xPupil, yPupil, obs_metadata=None,
314 includeRefraction=True,
315 epoch=2000.0):
316 """
317 Convert pupil coordinates into observed (RA, Dec)
319 @param [in] xPupil -- pupil coordinates in radians.
320 Can be a numpy array or a number.
322 @param [in] yPupil -- pupil coordinates in radians.
323 Can be a numpy array or a number.
325 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
326 the state of the telescope
328 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
329 transformations (in years; defaults to 2000)
331 @param[in] includeRefraction -- a boolean which controls the effects of refraction
332 (refraction is used when finding the observed coordinates of the boresite specified
333 by obs_metadata)
335 @param [out] a 2-D numpy array in which the first row is observed RA and the second
336 row is observed Dec (both in degrees). Note: these are not ICRS coordinates.
337 These are RA and Dec-like coordinates resulting from applying precession, nutation,
338 diurnal aberration and annual aberration on top of ICRS coordinates.
340 WARNING: This method does not account for apparent motion due to parallax.
341 This method is only useful for mapping positions on a theoretical focal plane
342 to positions on the celestial sphere.
343 """
344 ra_rad, dec_rad = _observedFromPupilCoords(xPupil, yPupil,
345 obs_metadata=obs_metadata,
346 includeRefraction=includeRefraction,
347 epoch=2000.0)
349 return np.degrees(ra_rad), np.degrees(dec_rad)
352def raDecFromPupilCoords(xPupil, yPupil, obs_metadata=None,
353 includeRefraction=True, epoch=2000.0):
354 """
355 @param [in] xPupil -- pupil coordinates in radians.
356 Can be a numpy array or a number.
358 @param [in] yPupil -- pupil coordinates in radians.
359 Can be a numpy array or a number.
361 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
362 the state of the telescope
364 @param[in] includeRefraction -- a boolean which controls the effects of refraction
365 (refraction is used when finding the observed coordinates of the boresite specified
366 by obs_metadata)
368 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
369 transformations (in years; defaults to 2000)
371 @param [out] a 2-D numpy array in which the first row is RA and the second
372 row is Dec (both in degrees; both in the International Celestial Reference System)
374 WARNING: This method does not account for apparent motion due to parallax.
375 This method is only useful for mapping positions on a theoretical focal plane
376 to positions on the celestial sphere.
377 """
379 output = _raDecFromPupilCoords(xPupil, yPupil,
380 obs_metadata=obs_metadata,
381 epoch=epoch,
382 includeRefraction=includeRefraction)
384 return np.degrees(output)
387def _raDecFromPupilCoords(xPupil, yPupil, obs_metadata=None,
388 includeRefraction=True, epoch=2000.0):
389 """
390 @param [in] xPupil -- pupil coordinates in radians.
391 Can be a numpy array or a number.
393 @param [in] yPupil -- pupil coordinates in radians.
394 Can be a numpy array or a number.
396 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
397 the state of the telescope
399 @param[in] includeRefraction -- a boolean which controls the effects of refraction
400 (refraction is used when finding the observed coordinates of the boresite specified
401 by obs_metadata)
403 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
404 transformations (in years; defaults to 2000)
406 @param [out] a 2-D numpy array in which the first row is RA and the second
407 row is Dec (both in radians; both in the International Celestial Reference System)
409 WARNING: This method does not account for apparent motion due to parallax.
410 This method is only useful for mapping positions on a theoretical focal plane
411 to positions on the celestial sphere.
412 """
414 are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'], "raDecFromPupilCoords")
416 if obs_metadata is None:
417 raise RuntimeError("Cannot call raDecFromPupilCoords without obs_metadata")
419 if epoch is None:
420 raise RuntimeError("Cannot call raDecFromPupilCoords; epoch is None")
422 if obs_metadata.rotSkyPos is None:
423 raise RuntimeError("Cannot call raDecFromPupilCoords without rotSkyPos " +
424 "in obs_metadata")
426 if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
427 raise RuntimeError("Cannot call raDecFromPupilCoords " +
428 "without pointingRA, pointingDec in obs_metadata")
430 if obs_metadata.mjd is None:
431 raise RuntimeError("Cannot calculate RA, Dec without mjd " +
432 "in obs_metadata")
434 raObs, decObs = _observedFromPupilCoords(xPupil, yPupil,
435 obs_metadata=obs_metadata,
436 epoch=epoch,
437 includeRefraction=includeRefraction)
439 ra_icrs, dec_icrs = _icrsFromObserved(raObs, decObs,
440 obs_metadata=obs_metadata,
441 epoch=epoch,
442 includeRefraction=includeRefraction)
444 return np.array([ra_icrs, dec_icrs])