Coverage for python/lsst/sims/utils/AstrometryUtils.py : 11%

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 __future__ import division
2import numpy as np
3import palpy
4from lsst.sims.utils.CodeUtilities import _validate_inputs
5from lsst.sims.utils import arcsecFromRadians, cartesianFromSpherical, sphericalFromCartesian
6from lsst.sims.utils import radiansFromArcsec
7from lsst.sims.utils import haversine
9__all__ = ["_solarRaDec", "solarRaDec",
10 "_distanceToSun", "distanceToSun",
11 "applyRefraction", "refractionCoefficients",
12 "_applyPrecession", "applyPrecession",
13 "_applyProperMotion", "applyProperMotion",
14 "_appGeoFromICRS", "appGeoFromICRS",
15 "_icrsFromAppGeo", "icrsFromAppGeo",
16 "_observedFromAppGeo", "observedFromAppGeo",
17 "_appGeoFromObserved", "appGeoFromObserved",
18 "_observedFromICRS", "observedFromICRS",
19 "_icrsFromObserved", "icrsFromObserved"]
22def _solarRaDec(mjd, epoch=2000.0):
23 """
24 Return the RA and Dec of the Sun in radians
26 @param [in] mjd is the date represented as a
27 ModifiedJulianDate object.
29 @param [in] epoch is the mean epoch of the coordinate system
30 (default is 2000.0)
32 @param [out] RA of Sun in radians
34 @param [out] Dec of Sun in radians
35 """
37 params = palpy.mappa(epoch, mjd.TDB)
38 # params[4:7] is a unit vector pointing from the Sun
39 # to the Earth (see the docstring for palpy.mappa)
41 return palpy.dcc2s(-1.0 * params[4:7])
44def solarRaDec(mjd, epoch=2000.0):
45 """
46 Return the RA and Dec of the Sun in degrees
48 @param [in] mjd is the date represented as a
49 ModifiedJulianDate object.
51 @param [in] epoch is the mean epoch of the coordinate system
52 (default is 2000.0)
54 @param [out] RA of Sun in degrees
56 @param [out] Dec of Sun in degress
57 """
59 solarRA, solarDec = _solarRaDec(mjd, epoch=epoch)
60 return np.degrees(solarRA), np.degrees(solarDec)
63def _distanceToSun(ra, dec, mjd, epoch=2000.0):
64 """
65 Calculate the distance from an (ra, dec) point to the Sun (in radians).
67 @param [in] ra in radians
69 @param [in] dec in radians
71 @param [in] mjd is the date represented as a
72 ModifiedJulianDate object.
74 @param [in] epoch is the epoch of the coordinate system
75 (default is 2000.0)
77 @param [out] distance on the sky to the Sun in radians
78 """
80 sunRa, sunDec = _solarRaDec(mjd, epoch=epoch)
82 return haversine(ra, dec, sunRa, sunDec)
85def distanceToSun(ra, dec, mjd, epoch=2000.0):
86 """
87 Calculate the distance from an (ra, dec) point to the Sun (in degrees).
89 @param [in] ra in degrees
91 @param [in] dec in degrees
93 @param [in] mjd is the date represented as a
94 ModifiedJulianDate object.
96 @param [in] epoch is the epoch of the coordinate system
97 (default is 2000.0)
99 @param [out] distance on the sky to the Sun in degrees
100 """
102 return np.degrees(_distanceToSun(np.radians(ra), np.radians(dec), mjd, epoch=epoch))
105def refractionCoefficients(wavelength=0.5, site=None):
106 """ Calculate the refraction using PAL's refco routine
108 This calculates the refraction at 2 angles and derives a tanz and tan^3z
109 coefficient for subsequent quick calculations. Good for zenith distances < 76 degrees
111 @param [in] wavelength is effective wavelength in microns (default 0.5)
113 @param [in] site is an instantiation of the Site class defined in
114 sims_utils/../Site.py
116 One should call PAL refz to apply the coefficients calculated here
118 """
119 precision = 1.e-10
121 if site is None:
122 raise RuntimeError("Cannot call refractionCoefficients; no site information")
124 # TODO the latitude in refco needs to be astronomical latitude,
125 # not geodetic latitude
126 _refcoOutput = palpy.refco(site.height,
127 site.temperature_kelvin,
128 site.pressure,
129 site.humidity,
130 wavelength,
131 site.latitude_rad,
132 site.lapseRate,
133 precision)
135 return _refcoOutput[0], _refcoOutput[1]
138def applyRefraction(zenithDistance, tanzCoeff, tan3zCoeff):
139 """ Calculted refracted Zenith Distance
141 uses the quick PAL refco routine which approximates the refractin calculation
143 @param [in] zenithDistance is unrefracted zenith distance of the source in radians.
144 Can either be a number or a numpy array (not a list).
146 @param [in] tanzCoeff is the first output from refractionCoefficients (above)
148 @param [in] tan3zCoeff is the second output from refractionCoefficients (above)
150 @param [out] refractedZenith is the refracted zenith distance in radians
152 """
154 if isinstance(zenithDistance, list):
155 raise RuntimeError("You passed a list of zenithDistances to " +
156 "applyRefraction. The method won't know how to " +
157 "handle that. Pass a numpy array.")
159 if isinstance(zenithDistance, np.ndarray):
160 refractedZenith = palpy.refzVector(
161 zenithDistance, tanzCoeff, tan3zCoeff)
162 else:
163 refractedZenith = palpy.refz(zenithDistance, tanzCoeff, tan3zCoeff)
165 return refractedZenith
168def applyPrecession(ra, dec, epoch=2000.0, mjd=None):
169 """
170 applyPrecession() applies precesion and nutation to coordinates between two epochs.
171 Accepts RA and dec as inputs. Returns corrected RA and dec (in degrees).
173 Assumes FK5 as the coordinate system
174 units: ra_in (degrees), dec_in (degrees)
176 The precession-nutation matrix is calculated by the palpy.prenut method
177 which uses the IAU 2006/2000A model
179 @param [in] ra in degrees
181 @param [in] dec in degrees
183 @param [in] epoch is the epoch of the mean equinox (in years; default 2000)
185 @param [in] mjd is an instantiation of the ModifiedJulianDate class
186 representing the date of the observation
188 @param [out] a 2-D numpy array in which the first row is the RA
189 corrected for precession and nutation and the second row is the
190 Dec corrected for precession and nutation (both in degrees)
192 """
194 output = _applyPrecession(np.radians(ra), np.radians(dec),
195 epoch=epoch, mjd=mjd)
197 return np.degrees(output)
200def _applyPrecession(ra, dec, epoch=2000.0, mjd=None):
201 """
202 _applyPrecession() applies precesion and nutation to coordinates between two epochs.
203 Accepts RA and dec as inputs. Returns corrected RA and dec (in radians).
205 Assumes FK5 as the coordinate system
206 units: ra_in (radians), dec_in (radians)
208 The precession-nutation matrix is calculated by the palpy.prenut method
209 which uses the IAU 2006/2000A model
211 @param [in] ra in radians
213 @param [in] dec in radians
215 @param [in] epoch is the epoch of the mean equinox (in years; default 2000)
217 @param [in] mjd is an instantiation of the ModifiedJulianDate class
218 representing the date of the observation
220 @param [out] a 2-D numpy array in which the first row is the RA
221 corrected for precession and nutation and the second row is the
222 Dec corrected for precession and nutation (both in radians)
223 """
225 if hasattr(ra, '__len__'):
226 if len(ra) != len(dec):
227 raise RuntimeError("You supplied %d RAs but %d Decs to applyPrecession" %
228 (len(ra), len(dec)))
230 if mjd is None:
231 raise RuntimeError("You need to supply applyPrecession with an mjd")
233 # Determine the precession and nutation
234 # palpy.prenut takes the julian epoch for the mean coordinates
235 # and the MJD for the the true coordinates
236 #
237 # TODO it is not specified what this MJD should be (i.e. in which
238 # time system it should be reckoned)
239 rmat = palpy.prenut(epoch, mjd.TT)
241 # Apply rotation matrix
242 xyz = cartesianFromSpherical(ra, dec)
243 xyz = np.dot(rmat, xyz.transpose()).transpose()
245 raOut, decOut = sphericalFromCartesian(xyz)
246 return np.array([raOut, decOut])
249def applyProperMotion(ra, dec, pm_ra, pm_dec, parallax, v_rad,
250 epoch=2000.0, mjd=None):
251 """Applies proper motion between two epochs.
253 units: ra (degrees), dec (degrees), pm_ra (arcsec/year), pm_dec
254 (arcsec/year), parallax (arcsec), v_rad (km/sec, positive if receding),
255 epoch (Julian years)
257 Returns corrected ra and dec (in radians)
259 The function palpy.pm does not work properly if the parallax is below
260 0.00045 arcseconds
262 @param [in] ra in degrees. Can be a number or a numpy array (not a list).
264 @param [in] dec in degrees. Can be a number or a numpy array (not a list).
266 @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in arcsec/year.
267 Can be a number or a numpy array (not a list).
269 @param [in] pm_dec is dec proper motion in arcsec/year.
270 Can be a number or a numpy array (not a list).
272 @param [in] parallax in arcsec. Can be a number or a numpy array (not a list).
274 @param [in] v_rad is radial velocity in km/sec (positive if the object is receding).
275 Can be a number or a numpy array (not a list).
277 @param [in] epoch is epoch in Julian years (default: 2000.0)
279 @param [in] mjd is an instantiation of the ModifiedJulianDate class
280 representing the date of the observation
282 @param [out] a 2-D numpy array in which the first row is the RA corrected
283 for proper motion and the second row is the Dec corrected for proper motion
284 (both in degrees)
285 """
287 output = _applyProperMotion(np.radians(ra), np.radians(dec),
288 radiansFromArcsec(pm_ra),
289 radiansFromArcsec(pm_dec),
290 radiansFromArcsec(parallax),
291 v_rad, epoch=epoch, mjd=mjd)
293 return np.degrees(output)
296def _applyProperMotion(ra, dec, pm_ra, pm_dec, parallax, v_rad,
297 epoch=2000.0, mjd=None):
298 """Applies proper motion between two epochs.
300 units: ra (radians), dec (radians), pm_ra (radians/year), pm_dec
301 (radians/year), parallax (radians), v_rad (km/sec, positive if receding),
302 epoch (Julian years)
304 Returns corrected ra and dec (in radians)
306 The function palpy.pm does not work properly if the parallax is below
307 0.00045 arcseconds
309 @param [in] ra in radians. Can be a number or a numpy array (not a list).
311 @param [in] dec in radians. Can be a number or a numpy array (not a list).
313 @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in radians/year.
314 Can be a number or a numpy array (not a list).
316 @param [in] pm_dec is dec proper motion in radians/year.
317 Can be a number or a numpy array (not a list).
319 @param [in] parallax in radians. Can be a number or a numpy array (not a list).
321 @param [in] v_rad is radial velocity in km/sec (positive if the object is receding).
322 Can be a number or a numpy array (not a list).
324 @param [in] epoch is epoch in Julian years (default: 2000.0)
326 @param [in] mjd is an instantiation of the ModifiedJulianDate class
327 representing the date of the observation
329 @param [out] a 2-D numpy array in which the first row is the RA corrected
330 for proper motion and the second row is the Dec corrected for proper motion
331 (both in radians)
333 """
335 if (isinstance(ra, list) or isinstance(dec, list) or
336 isinstance(pm_ra, list) or isinstance(pm_dec, list) or
337 isinstance(parallax, list) or isinstance(v_rad, list)):
339 raise RuntimeError("You tried to pass lists to applyPm. " +
340 "The method does not know how to handle lists. " +
341 "Use numpy arrays.")
343 if mjd is None:
344 raise RuntimeError("cannot call applyProperMotion; mjd is None")
346 parallaxArcsec = arcsecFromRadians(parallax)
347 # convert to Arcsec because that is what PALPY expects
349 # Generate Julian epoch from MJD
350 #
351 # 19 November 2015
352 # I am assuming here that the time scale should be
353 # Terrestrial Dynamical Time (TT), since that is used
354 # as the independent variable for apparent geocentric
355 # ephemerides
356 julianEpoch = palpy.epj(mjd.TT)
358 # because PAL and ERFA expect proper motion in terms of "coordinate
359 # angle; not true angle" (as stated in erfa/starpm.c documentation)
360 pm_ra_corrected = pm_ra / np.cos(dec)
362 if isinstance(ra, np.ndarray):
363 if ((len(ra) != len(dec) or
364 len(ra) != len(pm_ra) or
365 len(ra) != len(pm_dec) or
366 len(ra) != len(parallaxArcsec)) or
367 len(ra) != len(v_rad)):
369 raise RuntimeError("You passed: " +
370 "%d RAs, " % len(ra) +
371 "%d Dec, " % len(dec) +
372 "%d pm_ras, " % len(pm_ra) +
373 "%d pm_decs, " % len(pm_dec) +
374 "%d parallaxes, " % len(parallaxArcsec) +
375 "%d v_rads " % len(v_rad) +
376 "to applyPm; those numbers need to be identical.")
378 raOut, decOut = palpy.pmVector(ra, dec, pm_ra_corrected, pm_dec,
379 parallaxArcsec, v_rad, epoch, julianEpoch)
380 else:
381 raOut, decOut = palpy.pm(ra, dec, pm_ra_corrected, pm_dec, parallaxArcsec, v_rad, epoch, julianEpoch)
383 return np.array([raOut, decOut])
386def appGeoFromICRS(ra, dec, pm_ra=None, pm_dec=None, parallax=None,
387 v_rad=None, epoch=2000.0, mjd=None):
388 """
389 Convert the mean position (RA, Dec) in the International Celestial Reference
390 System (ICRS) to the mean apparent geocentric position
392 units: ra (degrees), dec (degrees), pm_ra (arcsec/year), pm_dec
393 (arcsec/year), parallax (arcsec), v_rad (km/sec; positive if receding),
394 epoch (Julian years)
396 @param [in] ra in degrees (ICRS). Can be a numpy array or a number.
398 @param [in] dec in degrees (ICRS). Can be a numpy array or a number.
400 @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in arcsec/year
402 @param [in] pm_dec is dec proper motion in arcsec/year
404 @param [in] parallax in arcsec
406 @param [in] v_rad is radial velocity in km/sec (positive if the object is receding)
408 @param [in] epoch is the julian epoch (in years) of the equinox against which to
409 measure RA (default: 2000.0)
411 @param [in] mjd is an instantiation of the ModifiedJulianDate class
412 representing the date of the observation
414 @param [out] a 2-D numpy array in which the first row is the apparent
415 geocentric RA and the second row is the apparent geocentric Dec (both in degrees)
416 """
418 if pm_ra is not None:
419 pm_ra_in = radiansFromArcsec(pm_ra)
420 else:
421 pm_ra_in = None
423 if pm_dec is not None:
424 pm_dec_in = radiansFromArcsec(pm_dec)
425 else:
426 pm_dec_in = None
428 if parallax is not None:
429 px_in = radiansFromArcsec(parallax)
430 else:
431 px_in = None
433 output = _appGeoFromICRS(np.radians(ra), np.radians(dec),
434 pm_ra=pm_ra_in, pm_dec=pm_dec_in,
435 parallax=px_in, v_rad=v_rad, epoch=epoch, mjd=mjd)
437 return np.degrees(output)
440def _appGeoFromICRS(ra, dec, pm_ra=None, pm_dec=None, parallax=None,
441 v_rad=None, epoch=2000.0, mjd=None):
442 """
443 Convert the mean position (RA, Dec) in the International Celestial Reference
444 System (ICRS) to the mean apparent geocentric position
446 units: ra (radians), dec (radians), pm_ra (radians/year), pm_dec
447 (radians/year), parallax (radians), v_rad (km/sec; positive if receding),
448 epoch (Julian years)
450 @param [in] ra in radians (ICRS). Can be a numpy array or a number.
452 @param [in] dec in radians (ICRS). Can be a numpy array or a number.
454 @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in radians/year.
455 Can be a numpy array or a number or None.
457 @param [in] pm_dec is dec proper motion in radians/year.
458 Can be a numpy array or a number or None.
460 @param [in] parallax in radians. Can be a numpy array or a number or None.
462 @param [in] v_rad is radial velocity in km/sec (positive if the object is receding).
463 Can be a numpy array or a number or None.
465 @param [in] epoch is the julian epoch (in years) of the equinox against which to
466 measure RA (default: 2000.0)
468 @param [in] mjd is an instantiation of the ModifiedJulianDate class
469 representing the date of the observation
471 @param [out] a 2-D numpy array in which the first row is the apparent
472 geocentric RAand the second row is the apparent geocentric Dec (both in radians)
473 """
475 if mjd is None:
476 raise RuntimeError("cannot call appGeoFromICRS; mjd is None")
478 include_px = False
480 if (pm_ra is not None or pm_dec is not None or
481 v_rad is not None or parallax is not None):
483 include_px = True
485 if isinstance(ra, np.ndarray):
486 fill_value = np.zeros(len(ra), dtype=float)
487 else:
488 fill_value = 0.0
490 if pm_ra is None:
491 pm_ra = fill_value
493 if pm_dec is None:
494 pm_dec = fill_value
496 if v_rad is None:
497 v_rad = fill_value
499 if parallax is None:
500 parallax = fill_value
502 are_arrays = _validate_inputs([ra, dec, pm_ra, pm_dec, v_rad, parallax],
503 ['ra', 'dec', 'pm_ra', 'pm_dec', 'v_rad',
504 'parallax'],
505 "appGeoFromICRS")
506 else:
507 are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "appGeoFromICRS")
510 # Define star independent mean to apparent place parameters
511 # palpy.mappa calculates the star-independent parameters
512 # needed to correct RA and Dec
513 # e.g the Earth barycentric and heliocentric position and velocity,
514 # the precession-nutation matrix, etc.
515 #
516 # arguments of palpy.mappa are:
517 # epoch of mean equinox to be used (Julian)
518 #
519 # date (MJD)
520 prms = palpy.mappa(epoch, mjd.TDB)
522 # palpy.mapqk does a quick mean to apparent place calculation using
523 # the output of palpy.mappa
524 #
525 # Taken from the palpy source code (palMap.c which calls both palMappa and palMapqk):
526 # The accuracy is sub-milliarcsecond, limited by the
527 # precession-nutation model (see palPrenut for details).
529 if include_px:
530 # because PAL and ERFA expect proper motion in terms of "coordinate
531 # angle; not true angle" (as stated in erfa/starpm.c documentation)
532 pm_ra_corrected = pm_ra / np.cos(dec)
534 if are_arrays:
535 if include_px:
536 raOut, decOut = palpy.mapqkVector(ra, dec, pm_ra_corrected, pm_dec,
537 arcsecFromRadians(parallax), v_rad, prms)
538 else:
539 raOut, decOut = palpy.mapqkzVector(ra, dec, prms)
540 else:
541 if include_px:
542 raOut, decOut = palpy.mapqk(ra, dec, pm_ra_corrected, pm_dec,
543 arcsecFromRadians(parallax), v_rad, prms)
544 else:
545 raOut, decOut = palpy.mapqkz(ra, dec, prms)
547 return np.array([raOut, decOut])
550def _icrsFromAppGeo(ra, dec, epoch=2000.0, mjd=None):
551 """
552 Convert the apparent geocentric position in (RA, Dec) to
553 the mean position in the International Celestial Reference
554 System (ICRS)
556 This method undoes the effects of precession, annual aberration,
557 and nutation. It is meant for mapping pointing RA and Dec (which
558 presumably include the above effects) back to mean ICRS RA and Dec
559 so that the user knows how to query a database of mean RA and Decs
560 for objects observed at a given telescope pointing.
562 WARNING: This method does not account for apparent motion due to parallax.
563 This means it should not be used to invert the ICRS-to-apparent geocentric
564 transformation for actual celestial objects. This method is only useful
565 for mapping positions on a theoretical celestial sphere.
567 This method works in radians.
569 @param [in] ra in radians (apparent geocentric). Can be a numpy array or a number.
571 @param [in] dec in radians (apparent geocentric). Can be a numpy array or a number.
573 @param [in] epoch is the julian epoch (in years) of the equinox against which to
574 measure RA (default: 2000.0)
576 @param [in] mjd is an instantiation of the ModifiedJulianDate class
577 representing the date of the observation
579 @param [out] a 2-D numpy array in which the first row is the mean ICRS RA and
580 the second row is the mean ICRS Dec (both in radians)
581 """
583 are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromAppGeo")
585 # Define star independent mean to apparent place parameters
586 # palpy.mappa calculates the star-independent parameters
587 # needed to correct RA and Dec
588 # e.g the Earth barycentric and heliocentric position and velocity,
589 # the precession-nutation matrix, etc.
590 #
591 # arguments of palpy.mappa are:
592 # epoch of mean equinox to be used (Julian)
593 #
594 # date (MJD)
595 params = palpy.mappa(epoch, mjd.TDB)
597 if are_arrays:
598 raOut, decOut = palpy.ampqkVector(ra, dec, params)
599 else:
600 raOut, decOut = palpy.ampqk(ra, dec, params)
602 return np.array([raOut, decOut])
605def icrsFromAppGeo(ra, dec, epoch=2000.0, mjd=None):
606 """
607 Convert the apparent geocentric position in (RA, Dec) to
608 the mean position in the International Celestial Reference
609 System (ICRS)
611 This method undoes the effects of precession, annual aberration,
612 and nutation. It is meant for mapping pointing RA and Dec (which
613 presumably include the above effects) back to mean ICRS RA and Dec
614 so that the user knows how to query a database of mean RA and Decs
615 for objects observed at a given telescope pointing.
617 WARNING: This method does not account for apparent motion due to parallax.
618 This means it should not be used to invert the ICRS-to-apparent geocentric
619 transformation for actual celestial objects. This method is only useful
620 for mapping positions on a theoretical celestial sphere.
622 This method works in degrees.
624 @param [in] ra in degrees (apparent geocentric). Can be a numpy array or a number.
626 @param [in] dec in degrees (apparent geocentric). Can be a numpy array or a number.
628 @param [in] epoch is the julian epoch (in years) of the equinox against which to
629 measure RA (default: 2000.0)
631 @param [in] mjd is an instantiation of the ModifiedJulianDate class
632 representing the date of the observation
634 @param [out] a 2-D numpy array in which the first row is the mean ICRS RA and
635 the second row is the mean ICRS Dec (both in degrees)
636 """
638 raOut, decOut = _icrsFromAppGeo(np.radians(ra), np.radians(dec),
639 epoch=epoch, mjd=mjd)
641 return np.array([np.degrees(raOut), np.degrees(decOut)])
644def observedFromAppGeo(ra, dec, includeRefraction=True,
645 altAzHr=False, wavelength=0.5, obs_metadata=None):
646 """
647 Convert apparent geocentric (RA, Dec) to observed (RA, Dec). More
648 specifically: apply refraction and diurnal aberration.
650 This method works in degrees.
652 @param [in] ra is geocentric apparent RA (degrees). Can be a numpy array or a number.
654 @param [in] dec is geocentric apparent Dec (degrees). Can be a numpy array or a number.
656 @param [in] includeRefraction is a boolean to turn refraction on and off
658 @param [in] altAzHr is a boolean indicating whether or not to return altitude
659 and azimuth
661 @param [in] wavelength is effective wavelength in microns (default: 0.5)
663 @param [in] obs_metadata is an ObservationMetaData characterizing the
664 observation.
666 @param [out] a 2-D numpy array in which the first row is the observed RA
667 and the second row is the observed Dec (both in degrees)
669 @param [out] a 2-D numpy array in which the first row is the altitude
670 and the second row is the azimuth (both in degrees). Only returned
671 if altAzHr == True.
672 """
674 if altAzHr:
675 raDec, altAz = _observedFromAppGeo(np.radians(ra), np.radians(dec),
676 includeRefraction=includeRefraction,
677 altAzHr=altAzHr, wavelength=wavelength,
678 obs_metadata=obs_metadata)
680 return np.degrees(raDec), np.degrees(altAz)
682 else:
683 output = _observedFromAppGeo(np.radians(ra), np.radians(dec),
684 includeRefraction=includeRefraction,
685 altAzHr=altAzHr, wavelength=wavelength,
686 obs_metadata=obs_metadata)
688 return np.degrees(output)
691def _calculateObservatoryParameters(obs_metadata, wavelength, includeRefraction):
692 """
693 Computer observatory-based parameters using palpy.aoppa
695 @param [in] obs_metadata is an ObservationMetaData characterizing
696 the specific telescope site and pointing
698 @param [in] wavelength is the effective wavelength in microns
700 @param [in] includeRefraction is a boolean indicating whether or not
701 to include the effects of refraction
703 @param [out] the numpy array of observatory paramters calculated by
704 palpy.aoppa
705 """
707 # Correct site longitude for polar motion slaPolmo
708 #
709 # 5 January 2016
710 # palAop.c (which calls Aoppa and Aopqk, as we do here) says
711 # * - The azimuths etc produced by the present routine are with
712 # * respect to the celestial pole. Corrections to the terrestrial
713 # * pole can be computed using palPolmo.
714 #
715 # As a future issue, we should figure out how to incorporate polar motion
716 # into these calculations. For now, we will set polar motion to zero.
717 xPolar = 0.0
718 yPolar = 0.0
720 #
721 # palpy.aoppa computes star-independent parameters necessary for
722 # converting apparent place into observed place
723 # i.e. it calculates geodetic latitude, magnitude of diurnal aberration,
724 # refraction coefficients and the like based on data about the observation site
725 if includeRefraction:
726 obsPrms = palpy.aoppa(obs_metadata.mjd.UTC, obs_metadata.mjd.dut1,
727 obs_metadata.site.longitude_rad,
728 obs_metadata.site.latitude_rad,
729 obs_metadata.site.height,
730 xPolar,
731 yPolar,
732 obs_metadata.site.temperature_kelvin,
733 obs_metadata.site.pressure,
734 obs_metadata.site.humidity,
735 wavelength,
736 obs_metadata.site.lapseRate)
737 else:
738 # we can discard refraction by setting pressure and humidity to zero
739 obsPrms = palpy.aoppa(obs_metadata.mjd.UTC, obs_metadata.mjd.dut1,
740 obs_metadata.site.longitude_rad,
741 obs_metadata.site.latitude_rad,
742 obs_metadata.site.height,
743 xPolar,
744 yPolar,
745 obs_metadata.site.temperature,
746 0.0,
747 0.0,
748 wavelength,
749 obs_metadata.site.lapseRate)
751 return obsPrms
754def _observedFromAppGeo(ra, dec, includeRefraction=True,
755 altAzHr=False, wavelength=0.5, obs_metadata=None):
756 """
757 Convert apparent geocentric (RA, Dec) to observed (RA, Dec). More specifically:
758 apply refraction and diurnal aberration.
760 This method works in radians.
762 @param [in] ra is geocentric apparent RA (radians). Can be a numpy array or a number.
764 @param [in] dec is geocentric apparent Dec (radians). Can be a numpy array or a number.
766 @param [in] includeRefraction is a boolean to turn refraction on and off
768 @param [in] altAzHr is a boolean indicating whether or not to return altitude
769 and azimuth
771 @param [in] wavelength is effective wavelength in microns (default: 0.5)
773 @param [in] obs_metadata is an ObservationMetaData characterizing the
774 observation.
776 @param [out] a 2-D numpy array in which the first row is the observed RA
777 and the second row is the observed Dec (both in radians)
779 @param [out] a 2-D numpy array in which the first row is the altitude
780 and the second row is the azimuth (both in radians). Only returned
781 if altAzHr == True.
783 """
785 are_arrays = _validate_inputs(
786 [ra, dec], ['ra', 'dec'], "observedFromAppGeo")
788 if obs_metadata is None:
789 raise RuntimeError(
790 "Cannot call observedFromAppGeo without an obs_metadata")
792 if obs_metadata.site is None:
793 raise RuntimeError(
794 "Cannot call observedFromAppGeo: obs_metadata has no site info")
796 if obs_metadata.mjd is None:
797 raise RuntimeError(
798 "Cannot call observedFromAppGeo: obs_metadata has no mjd")
800 obsPrms = _calculateObservatoryParameters(
801 obs_metadata, wavelength, includeRefraction)
803 # palpy.aopqk does an apparent to observed place
804 # correction
805 #
806 # it corrects for diurnal aberration and refraction
807 # (using a fast algorithm for refraction in the case of
808 # a small zenith distance and a more rigorous algorithm
809 # for a large zenith distance)
810 #
812 if are_arrays:
813 azimuth, zenith, hourAngle, decOut, raOut = palpy.aopqkVector(
814 ra, dec, obsPrms)
815 else:
816 azimuth, zenith, hourAngle, decOut, raOut = palpy.aopqk(
817 ra, dec, obsPrms)
819 #
820 # Note: this is a choke point. Even the vectorized version of aopqk
821 # is expensive (it takes about 0.006 seconds per call)
822 #
823 # Actually, this is only a choke point if you are dealing with zenith
824 # distances of greater than about 70 degrees
826 if altAzHr:
827 #
828 # palpy.de2h converts equatorial to horizon coordinates
829 #
830 if are_arrays:
831 az, alt = palpy.de2hVector(
832 hourAngle, decOut, obs_metadata.site.latitude_rad)
833 else:
834 az, alt = palpy.de2h(
835 hourAngle, decOut, obs_metadata.site.latitude_rad)
837 return np.array([raOut, decOut]), np.array([alt, az])
838 return np.array([raOut, decOut])
841def appGeoFromObserved(ra, dec, includeRefraction=True,
842 wavelength=0.5, obs_metadata=None):
843 """
844 Convert observed (RA, Dec) to apparent geocentric (RA, Dec). More
845 specifically: undo the effects of refraction and diurnal aberration.
847 Note: This method is only accurate at zenith distances less than ~75 degrees.
849 This method works in degrees.
851 @param [in] ra is observed RA (degrees). Can be a numpy array or a number.
853 @param [in] dec is observed Dec (degrees). Can be a numpy array or a number.
855 @param [in] includeRefraction is a boolean to turn refraction on and off
857 @param [in] wavelength is effective wavelength in microns (default: 0.5)
859 @param [in] obs_metadata is an ObservationMetaData characterizing the
860 observation.
862 @param [out] a 2-D numpy array in which the first row is the apparent
863 geocentric RA and the second row is the apparentGeocentric Dec (both
864 in degrees)
865 """
867 raOut, decOut = _appGeoFromObserved(np.radians(ra), np.radians(dec),
868 includeRefraction=includeRefraction,
869 wavelength=wavelength,
870 obs_metadata=obs_metadata)
872 return np.array([np.degrees(raOut), np.degrees(decOut)])
875def _appGeoFromObserved(ra, dec, includeRefraction=True,
876 wavelength=0.5, obs_metadata=None):
877 """
878 Convert observed (RA, Dec) to apparent geocentric (RA, Dec).
879 More specifically: undo the effects of refraction and diurnal aberration.
881 Note: This method is only accurate at zenith distances less than ~ 75 degrees.
883 This method works in radians.
885 @param [in] ra is observed RA (radians). Can be a numpy array or a number.
887 @param [in] dec is observed Dec (radians). Can be a numpy array or a number.
889 @param [in] includeRefraction is a boolean to turn refraction on and off
891 @param [in] wavelength is effective wavelength in microns (default: 0.5)
893 @param [in] obs_metadata is an ObservationMetaData characterizing the
894 observation.
896 @param [out] a 2-D numpy array in which the first row is the apparent
897 geocentric RA and the second row is the apparentGeocentric Dec (both
898 in radians)
899 """
901 are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "appGeoFromObserved")
903 if obs_metadata is None:
904 raise RuntimeError("Cannot call appGeoFromObserved without an obs_metadata")
906 if obs_metadata.site is None:
907 raise RuntimeError("Cannot call appGeoFromObserved: obs_metadata has no site info")
909 if obs_metadata.mjd is None:
910 raise RuntimeError("Cannot call appGeoFromObserved: obs_metadata has no mjd")
912 obsPrms = _calculateObservatoryParameters(obs_metadata, wavelength, includeRefraction)
914 if are_arrays:
915 raOut, decOut = palpy.oapqkVector('r', ra, dec, obsPrms)
916 else:
917 raOut, decOut = palpy.oapqk('r', ra, dec, obsPrms)
919 return np.array([raOut, decOut])
922def observedFromICRS(ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None,
923 obs_metadata=None, epoch=None, includeRefraction=True):
924 """
925 Convert mean position (RA, Dec) in the International Celestial Reference Frame
926 to observed (RA, Dec).
928 included are precession-nutation, aberration, proper motion, parallax, refraction,
929 radial velocity, diurnal aberration.
931 This method works in degrees.
933 @param [in] ra is the unrefracted RA in degrees (ICRS). Can be a numpy array or a number.
935 @param [in] dec is the unrefracted Dec in degrees (ICRS). Can be a numpy array or a number.
937 @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (arcsec/yr)
938 Can be a numpy array or a number or None (default=None).
940 @param [in] pm_dec is proper motion in dec (arcsec/yr)
941 Can be a numpy array or a number or None (default=None).
943 @param [in] parallax is parallax in arcsec
944 Can be a numpy array or a number or None (default=None).
946 @param [in] v_rad is radial velocity (km/s)
947 Can be a numpy array or a number or None (default=None).
949 @param [in] obs_metadata is an ObservationMetaData object describing the
950 telescope pointing.
952 @param [in] epoch is the julian epoch (in years) against which the mean
953 equinoxes are measured.
955 @param [in] includeRefraction toggles whether or not to correct for refraction
957 @param [out] a 2-D numpy array in which the first row is the observed
958 RA and the second row is the observed Dec (both in degrees)
959 """
961 if pm_ra is not None:
962 pm_ra_in = radiansFromArcsec(pm_ra)
963 else:
964 pm_ra_in = None
966 if pm_dec is not None:
967 pm_dec_in = radiansFromArcsec(pm_dec)
968 else:
969 pm_dec_in = None
971 if parallax is not None:
972 parallax_in = radiansFromArcsec(parallax)
973 else:
974 parallax_in = None
976 output = _observedFromICRS(np.radians(ra), np.radians(dec),
977 pm_ra=pm_ra_in, pm_dec=pm_dec_in, parallax=parallax_in,
978 v_rad=v_rad, obs_metadata=obs_metadata, epoch=epoch,
979 includeRefraction=includeRefraction)
981 return np.degrees(output)
984def _observedFromICRS(ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None,
985 obs_metadata=None, epoch=None, includeRefraction=True):
986 """
987 Convert mean position (RA, Dec) in the International Celestial Reference Frame
988 to observed (RA, Dec)-like coordinates.
990 included are precession-nutation, aberration, proper motion, parallax, refraction,
991 radial velocity, diurnal aberration.
993 This method works in radians.
995 @param [in] ra is the unrefracted RA in radians (ICRS). Can be a numpy array or a number.
997 @param [in] dec is the unrefracted Dec in radians (ICRS). Can be a numpy array or a number.
999 @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
1000 Can be a numpy array or a number or None (default=None).
1002 @param [in] pm_dec is proper motion in dec (radians/yr)
1003 Can be a numpy array or a number or None (default=None).
1005 @param [in] parallax is parallax in radians
1006 Can be a numpy array or a number or None (default=None).
1008 @param [in] v_rad is radial velocity (km/s)
1009 Can be a numpy array or a number or None (default=None).
1011 @param [in] obs_metadata is an ObservationMetaData object describing the
1012 telescope pointing.
1014 @param [in] epoch is the julian epoch (in years) against which the mean
1015 equinoxes are measured.
1017 @param [in] includeRefraction toggles whether or not to correct for refraction
1019 @param [out] a 2-D numpy array in which the first row is the observed
1020 RA and the second row is the observed Dec (both in radians)
1022 """
1024 if obs_metadata is None:
1025 raise RuntimeError("Cannot call observedFromICRS; obs_metadata is none")
1027 if obs_metadata.mjd is None:
1028 raise RuntimeError("Cannot call observedFromICRS; obs_metadata.mjd is none")
1030 if epoch is None:
1031 raise RuntimeError("Cannot call observedFromICRS; you have not specified an epoch")
1033 ra_apparent, dec_apparent = _appGeoFromICRS(ra, dec, pm_ra=pm_ra,
1034 pm_dec=pm_dec, parallax=parallax,
1035 v_rad=v_rad, epoch=epoch, mjd=obs_metadata.mjd)
1037 ra_out, dec_out = _observedFromAppGeo(ra_apparent, dec_apparent, obs_metadata=obs_metadata,
1038 includeRefraction=includeRefraction)
1040 return np.array([ra_out, dec_out])
1043def icrsFromObserved(ra, dec, obs_metadata=None, epoch=None, includeRefraction=True):
1044 """
1045 Convert observed RA, Dec into mean International Celestial Reference Frame (ICRS)
1046 RA, Dec. This method undoes the effects of precession, nutation, aberration (annual
1047 and diurnal), and refraction. It is meant so that users can take pointing RA and Decs,
1048 which will be in the observed reference system, and transform them into ICRS for
1049 purposes of querying database tables (likely to contain mean ICRS RA, Dec) for objects
1050 visible from a given pointing.
1052 Note: This method is only accurate at angular distances from the sun of greater
1053 than 45 degrees and zenith distances of less than 75 degrees.
1055 WARNING: This method does not account for apparent motion due to parallax.
1056 This means it should not be used to invert the ICRS-to-observed coordinates
1057 transformation for actual celestial objects. This method is only useful
1058 for mapping positions on a theoretical celestial sphere.
1060 This method works in degrees.
1062 @param [in] ra is the observed RA in degrees. Can be a numpy array or a number.
1064 @param [in] dec is the observed Dec in degrees. Can be a numpy array or a number.
1066 @param [in] obs_metadata is an ObservationMetaData object describing the
1067 telescope pointing.
1069 @param [in] epoch is the julian epoch (in years) against which the mean
1070 equinoxes are measured.
1072 @param [in] includeRefraction toggles whether or not to correct for refraction
1074 @param [out] a 2-D numpy array in which the first row is the mean ICRS
1075 RA and the second row is the mean ICRS Dec (both in degrees)
1076 """
1078 ra_out, dec_out = _icrsFromObserved(np.radians(ra), np.radians(dec),
1079 obs_metadata=obs_metadata,
1080 epoch=epoch, includeRefraction=includeRefraction)
1082 return np.array([np.degrees(ra_out), np.degrees(dec_out)])
1085def _icrsFromObserved(ra, dec, obs_metadata=None, epoch=None, includeRefraction=True):
1086 """
1087 Convert observed RA, Dec into mean International Celestial Reference Frame (ICRS)
1088 RA, Dec. This method undoes the effects of precession, nutation, aberration (annual
1089 and diurnal), and refraction. It is meant so that users can take pointing RA and Decs,
1090 which will be in the observed reference system, and transform them into ICRS for
1091 purposes of querying database tables (likely to contain mean ICRS RA, Dec) for objects
1092 visible from a given pointing.
1094 Note: This method is only accurate at angular distances from the sun of greater
1095 than 45 degrees and zenith distances of less than 75 degrees.
1097 WARNING: This method does not account for apparent motion due to parallax.
1098 This means it should not be used to invert the ICRS-to-observed coordinates
1099 transformation for actual celestial objects. This method is only useful
1100 for mapping positions on a theoretical celestial sphere.
1102 This method works in radians.
1104 @param [in] ra is the observed RA in radians. Can be a numpy array or a number.
1106 @param [in] dec is the observed Dec in radians. Can be a numpy array or a number.
1108 @param [in] obs_metadata is an ObservationMetaData object describing the
1109 telescope pointing.
1111 @param [in] epoch is the julian epoch (in years) against which the mean
1112 equinoxes are measured.
1114 @param [in] includeRefraction toggles whether or not to correct for refraction
1116 @param [out] a 2-D numpy array in which the first row is the mean ICRS
1117 RA and the second row is the mean ICRS Dec (both in radians)
1118 """
1120 _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromObserved")
1122 if obs_metadata is None:
1123 raise RuntimeError("Cannot call icrsFromObserved; obs_metadata is None")
1125 if obs_metadata.mjd is None:
1126 raise RuntimeError("Cannot call icrsFromObserved; obs_metadata.mjd is None")
1128 if epoch is None:
1129 raise RuntimeError("Cannot call icrsFromObserved; you have not specified an epoch")
1131 ra_app, dec_app = _appGeoFromObserved(ra, dec, obs_metadata=obs_metadata,
1132 includeRefraction=includeRefraction)
1134 ra_icrs, dec_icrs = _icrsFromAppGeo(ra_app, dec_app, epoch=epoch,
1135 mjd=obs_metadata.mjd)
1137 return np.array([ra_icrs, dec_icrs])