Hide keyboard shortcuts

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 

8 

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"] 

20 

21 

22def _solarRaDec(mjd, epoch=2000.0): 

23 """ 

24 Return the RA and Dec of the Sun in radians 

25 

26 @param [in] mjd is the date represented as a 

27 ModifiedJulianDate object. 

28 

29 @param [in] epoch is the mean epoch of the coordinate system 

30 (default is 2000.0) 

31 

32 @param [out] RA of Sun in radians 

33 

34 @param [out] Dec of Sun in radians 

35 """ 

36 

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) 

40 

41 return palpy.dcc2s(-1.0 * params[4:7]) 

42 

43 

44def solarRaDec(mjd, epoch=2000.0): 

45 """ 

46 Return the RA and Dec of the Sun in degrees 

47 

48 @param [in] mjd is the date represented as a 

49 ModifiedJulianDate object. 

50 

51 @param [in] epoch is the mean epoch of the coordinate system 

52 (default is 2000.0) 

53 

54 @param [out] RA of Sun in degrees 

55 

56 @param [out] Dec of Sun in degress 

57 """ 

58 

59 solarRA, solarDec = _solarRaDec(mjd, epoch=epoch) 

60 return np.degrees(solarRA), np.degrees(solarDec) 

61 

62 

63def _distanceToSun(ra, dec, mjd, epoch=2000.0): 

64 """ 

65 Calculate the distance from an (ra, dec) point to the Sun (in radians). 

66 

67 @param [in] ra in radians 

68 

69 @param [in] dec in radians 

70 

71 @param [in] mjd is the date represented as a 

72 ModifiedJulianDate object. 

73 

74 @param [in] epoch is the epoch of the coordinate system 

75 (default is 2000.0) 

76 

77 @param [out] distance on the sky to the Sun in radians 

78 """ 

79 

80 sunRa, sunDec = _solarRaDec(mjd, epoch=epoch) 

81 

82 return haversine(ra, dec, sunRa, sunDec) 

83 

84 

85def distanceToSun(ra, dec, mjd, epoch=2000.0): 

86 """ 

87 Calculate the distance from an (ra, dec) point to the Sun (in degrees). 

88 

89 @param [in] ra in degrees 

90 

91 @param [in] dec in degrees 

92 

93 @param [in] mjd is the date represented as a 

94 ModifiedJulianDate object. 

95 

96 @param [in] epoch is the epoch of the coordinate system 

97 (default is 2000.0) 

98 

99 @param [out] distance on the sky to the Sun in degrees 

100 """ 

101 

102 return np.degrees(_distanceToSun(np.radians(ra), np.radians(dec), mjd, epoch=epoch)) 

103 

104 

105def refractionCoefficients(wavelength=0.5, site=None): 

106 """ Calculate the refraction using PAL's refco routine 

107 

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 

110 

111 @param [in] wavelength is effective wavelength in microns (default 0.5) 

112 

113 @param [in] site is an instantiation of the Site class defined in 

114 sims_utils/../Site.py 

115 

116 One should call PAL refz to apply the coefficients calculated here 

117 

118 """ 

119 precision = 1.e-10 

120 

121 if site is None: 

122 raise RuntimeError("Cannot call refractionCoefficients; no site information") 

123 

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) 

134 

135 return _refcoOutput[0], _refcoOutput[1] 

136 

137 

138def applyRefraction(zenithDistance, tanzCoeff, tan3zCoeff): 

139 """ Calculted refracted Zenith Distance 

140 

141 uses the quick PAL refco routine which approximates the refractin calculation 

142 

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). 

145 

146 @param [in] tanzCoeff is the first output from refractionCoefficients (above) 

147 

148 @param [in] tan3zCoeff is the second output from refractionCoefficients (above) 

149 

150 @param [out] refractedZenith is the refracted zenith distance in radians 

151 

152 """ 

153 

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.") 

158 

159 if isinstance(zenithDistance, np.ndarray): 

160 refractedZenith = palpy.refzVector( 

161 zenithDistance, tanzCoeff, tan3zCoeff) 

162 else: 

163 refractedZenith = palpy.refz(zenithDistance, tanzCoeff, tan3zCoeff) 

164 

165 return refractedZenith 

166 

167 

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). 

172 

173 Assumes FK5 as the coordinate system 

174 units: ra_in (degrees), dec_in (degrees) 

175 

176 The precession-nutation matrix is calculated by the palpy.prenut method 

177 which uses the IAU 2006/2000A model 

178 

179 @param [in] ra in degrees 

180 

181 @param [in] dec in degrees 

182 

183 @param [in] epoch is the epoch of the mean equinox (in years; default 2000) 

184 

185 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

186 representing the date of the observation 

187 

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) 

191 

192 """ 

193 

194 output = _applyPrecession(np.radians(ra), np.radians(dec), 

195 epoch=epoch, mjd=mjd) 

196 

197 return np.degrees(output) 

198 

199 

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). 

204 

205 Assumes FK5 as the coordinate system 

206 units: ra_in (radians), dec_in (radians) 

207 

208 The precession-nutation matrix is calculated by the palpy.prenut method 

209 which uses the IAU 2006/2000A model 

210 

211 @param [in] ra in radians 

212 

213 @param [in] dec in radians 

214 

215 @param [in] epoch is the epoch of the mean equinox (in years; default 2000) 

216 

217 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

218 representing the date of the observation 

219 

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 """ 

224 

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))) 

229 

230 if mjd is None: 

231 raise RuntimeError("You need to supply applyPrecession with an mjd") 

232 

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) 

240 

241 # Apply rotation matrix 

242 xyz = cartesianFromSpherical(ra, dec) 

243 xyz = np.dot(rmat, xyz.transpose()).transpose() 

244 

245 raOut, decOut = sphericalFromCartesian(xyz) 

246 return np.array([raOut, decOut]) 

247 

248 

249def applyProperMotion(ra, dec, pm_ra, pm_dec, parallax, v_rad, 

250 epoch=2000.0, mjd=None): 

251 """Applies proper motion between two epochs. 

252 

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) 

256 

257 Returns corrected ra and dec (in radians) 

258 

259 The function palpy.pm does not work properly if the parallax is below 

260 0.00045 arcseconds 

261 

262 @param [in] ra in degrees. Can be a number or a numpy array (not a list). 

263 

264 @param [in] dec in degrees. Can be a number or a numpy array (not a list). 

265 

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). 

268 

269 @param [in] pm_dec is dec proper motion in arcsec/year. 

270 Can be a number or a numpy array (not a list). 

271 

272 @param [in] parallax in arcsec. Can be a number or a numpy array (not a list). 

273 

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). 

276 

277 @param [in] epoch is epoch in Julian years (default: 2000.0) 

278 

279 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

280 representing the date of the observation 

281 

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 """ 

286 

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) 

292 

293 return np.degrees(output) 

294 

295 

296def _applyProperMotion(ra, dec, pm_ra, pm_dec, parallax, v_rad, 

297 epoch=2000.0, mjd=None): 

298 """Applies proper motion between two epochs. 

299 

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) 

303 

304 Returns corrected ra and dec (in radians) 

305 

306 The function palpy.pm does not work properly if the parallax is below 

307 0.00045 arcseconds 

308 

309 @param [in] ra in radians. Can be a number or a numpy array (not a list). 

310 

311 @param [in] dec in radians. Can be a number or a numpy array (not a list). 

312 

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). 

315 

316 @param [in] pm_dec is dec proper motion in radians/year. 

317 Can be a number or a numpy array (not a list). 

318 

319 @param [in] parallax in radians. Can be a number or a numpy array (not a list). 

320 

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). 

323 

324 @param [in] epoch is epoch in Julian years (default: 2000.0) 

325 

326 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

327 representing the date of the observation 

328 

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) 

332 

333 """ 

334 

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)): 

338 

339 raise RuntimeError("You tried to pass lists to applyPm. " + 

340 "The method does not know how to handle lists. " + 

341 "Use numpy arrays.") 

342 

343 if mjd is None: 

344 raise RuntimeError("cannot call applyProperMotion; mjd is None") 

345 

346 parallaxArcsec = arcsecFromRadians(parallax) 

347 # convert to Arcsec because that is what PALPY expects 

348 

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) 

357 

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) 

361 

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)): 

368 

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.") 

377 

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) 

382 

383 return np.array([raOut, decOut]) 

384 

385 

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 

391 

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) 

395 

396 @param [in] ra in degrees (ICRS). Can be a numpy array or a number. 

397 

398 @param [in] dec in degrees (ICRS). Can be a numpy array or a number. 

399 

400 @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in arcsec/year 

401 

402 @param [in] pm_dec is dec proper motion in arcsec/year 

403 

404 @param [in] parallax in arcsec 

405 

406 @param [in] v_rad is radial velocity in km/sec (positive if the object is receding) 

407 

408 @param [in] epoch is the julian epoch (in years) of the equinox against which to 

409 measure RA (default: 2000.0) 

410 

411 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

412 representing the date of the observation 

413 

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 """ 

417 

418 if pm_ra is not None: 

419 pm_ra_in = radiansFromArcsec(pm_ra) 

420 else: 

421 pm_ra_in = None 

422 

423 if pm_dec is not None: 

424 pm_dec_in = radiansFromArcsec(pm_dec) 

425 else: 

426 pm_dec_in = None 

427 

428 if parallax is not None: 

429 px_in = radiansFromArcsec(parallax) 

430 else: 

431 px_in = None 

432 

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) 

436 

437 return np.degrees(output) 

438 

439 

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 

445 

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) 

449 

450 @param [in] ra in radians (ICRS). Can be a numpy array or a number. 

451 

452 @param [in] dec in radians (ICRS). Can be a numpy array or a number. 

453 

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. 

456 

457 @param [in] pm_dec is dec proper motion in radians/year. 

458 Can be a numpy array or a number or None. 

459 

460 @param [in] parallax in radians. Can be a numpy array or a number or None. 

461 

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. 

464 

465 @param [in] epoch is the julian epoch (in years) of the equinox against which to 

466 measure RA (default: 2000.0) 

467 

468 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

469 representing the date of the observation 

470 

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 """ 

474 

475 if mjd is None: 

476 raise RuntimeError("cannot call appGeoFromICRS; mjd is None") 

477 

478 include_px = False 

479 

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): 

482 

483 include_px = True 

484 

485 if isinstance(ra, np.ndarray): 

486 fill_value = np.zeros(len(ra), dtype=float) 

487 else: 

488 fill_value = 0.0 

489 

490 if pm_ra is None: 

491 pm_ra = fill_value 

492 

493 if pm_dec is None: 

494 pm_dec = fill_value 

495 

496 if v_rad is None: 

497 v_rad = fill_value 

498 

499 if parallax is None: 

500 parallax = fill_value 

501 

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") 

508 

509 

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) 

521 

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). 

528 

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) 

533 

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) 

546 

547 return np.array([raOut, decOut]) 

548 

549 

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) 

555 

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. 

561 

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. 

566 

567 This method works in radians. 

568 

569 @param [in] ra in radians (apparent geocentric). Can be a numpy array or a number. 

570 

571 @param [in] dec in radians (apparent geocentric). Can be a numpy array or a number. 

572 

573 @param [in] epoch is the julian epoch (in years) of the equinox against which to 

574 measure RA (default: 2000.0) 

575 

576 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

577 representing the date of the observation 

578 

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 """ 

582 

583 are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromAppGeo") 

584 

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) 

596 

597 if are_arrays: 

598 raOut, decOut = palpy.ampqkVector(ra, dec, params) 

599 else: 

600 raOut, decOut = palpy.ampqk(ra, dec, params) 

601 

602 return np.array([raOut, decOut]) 

603 

604 

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) 

610 

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. 

616 

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. 

621 

622 This method works in degrees. 

623 

624 @param [in] ra in degrees (apparent geocentric). Can be a numpy array or a number. 

625 

626 @param [in] dec in degrees (apparent geocentric). Can be a numpy array or a number. 

627 

628 @param [in] epoch is the julian epoch (in years) of the equinox against which to 

629 measure RA (default: 2000.0) 

630 

631 @param [in] mjd is an instantiation of the ModifiedJulianDate class 

632 representing the date of the observation 

633 

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 """ 

637 

638 raOut, decOut = _icrsFromAppGeo(np.radians(ra), np.radians(dec), 

639 epoch=epoch, mjd=mjd) 

640 

641 return np.array([np.degrees(raOut), np.degrees(decOut)]) 

642 

643 

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. 

649 

650 This method works in degrees. 

651 

652 @param [in] ra is geocentric apparent RA (degrees). Can be a numpy array or a number. 

653 

654 @param [in] dec is geocentric apparent Dec (degrees). Can be a numpy array or a number. 

655 

656 @param [in] includeRefraction is a boolean to turn refraction on and off 

657 

658 @param [in] altAzHr is a boolean indicating whether or not to return altitude 

659 and azimuth 

660 

661 @param [in] wavelength is effective wavelength in microns (default: 0.5) 

662 

663 @param [in] obs_metadata is an ObservationMetaData characterizing the 

664 observation. 

665 

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) 

668 

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 """ 

673 

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) 

679 

680 return np.degrees(raDec), np.degrees(altAz) 

681 

682 else: 

683 output = _observedFromAppGeo(np.radians(ra), np.radians(dec), 

684 includeRefraction=includeRefraction, 

685 altAzHr=altAzHr, wavelength=wavelength, 

686 obs_metadata=obs_metadata) 

687 

688 return np.degrees(output) 

689 

690 

691def _calculateObservatoryParameters(obs_metadata, wavelength, includeRefraction): 

692 """ 

693 Computer observatory-based parameters using palpy.aoppa 

694 

695 @param [in] obs_metadata is an ObservationMetaData characterizing 

696 the specific telescope site and pointing 

697 

698 @param [in] wavelength is the effective wavelength in microns 

699 

700 @param [in] includeRefraction is a boolean indicating whether or not 

701 to include the effects of refraction 

702 

703 @param [out] the numpy array of observatory paramters calculated by 

704 palpy.aoppa 

705 """ 

706 

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 

719 

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) 

750 

751 return obsPrms 

752 

753 

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. 

759 

760 This method works in radians. 

761 

762 @param [in] ra is geocentric apparent RA (radians). Can be a numpy array or a number. 

763 

764 @param [in] dec is geocentric apparent Dec (radians). Can be a numpy array or a number. 

765 

766 @param [in] includeRefraction is a boolean to turn refraction on and off 

767 

768 @param [in] altAzHr is a boolean indicating whether or not to return altitude 

769 and azimuth 

770 

771 @param [in] wavelength is effective wavelength in microns (default: 0.5) 

772 

773 @param [in] obs_metadata is an ObservationMetaData characterizing the 

774 observation. 

775 

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) 

778 

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. 

782 

783 """ 

784 

785 are_arrays = _validate_inputs( 

786 [ra, dec], ['ra', 'dec'], "observedFromAppGeo") 

787 

788 if obs_metadata is None: 

789 raise RuntimeError( 

790 "Cannot call observedFromAppGeo without an obs_metadata") 

791 

792 if obs_metadata.site is None: 

793 raise RuntimeError( 

794 "Cannot call observedFromAppGeo: obs_metadata has no site info") 

795 

796 if obs_metadata.mjd is None: 

797 raise RuntimeError( 

798 "Cannot call observedFromAppGeo: obs_metadata has no mjd") 

799 

800 obsPrms = _calculateObservatoryParameters( 

801 obs_metadata, wavelength, includeRefraction) 

802 

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 # 

811 

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) 

818 

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 

825 

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) 

836 

837 return np.array([raOut, decOut]), np.array([alt, az]) 

838 return np.array([raOut, decOut]) 

839 

840 

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. 

846 

847 Note: This method is only accurate at zenith distances less than ~75 degrees. 

848 

849 This method works in degrees. 

850 

851 @param [in] ra is observed RA (degrees). Can be a numpy array or a number. 

852 

853 @param [in] dec is observed Dec (degrees). Can be a numpy array or a number. 

854 

855 @param [in] includeRefraction is a boolean to turn refraction on and off 

856 

857 @param [in] wavelength is effective wavelength in microns (default: 0.5) 

858 

859 @param [in] obs_metadata is an ObservationMetaData characterizing the 

860 observation. 

861 

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 """ 

866 

867 raOut, decOut = _appGeoFromObserved(np.radians(ra), np.radians(dec), 

868 includeRefraction=includeRefraction, 

869 wavelength=wavelength, 

870 obs_metadata=obs_metadata) 

871 

872 return np.array([np.degrees(raOut), np.degrees(decOut)]) 

873 

874 

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. 

880 

881 Note: This method is only accurate at zenith distances less than ~ 75 degrees. 

882 

883 This method works in radians. 

884 

885 @param [in] ra is observed RA (radians). Can be a numpy array or a number. 

886 

887 @param [in] dec is observed Dec (radians). Can be a numpy array or a number. 

888 

889 @param [in] includeRefraction is a boolean to turn refraction on and off 

890 

891 @param [in] wavelength is effective wavelength in microns (default: 0.5) 

892 

893 @param [in] obs_metadata is an ObservationMetaData characterizing the 

894 observation. 

895 

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 """ 

900 

901 are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "appGeoFromObserved") 

902 

903 if obs_metadata is None: 

904 raise RuntimeError("Cannot call appGeoFromObserved without an obs_metadata") 

905 

906 if obs_metadata.site is None: 

907 raise RuntimeError("Cannot call appGeoFromObserved: obs_metadata has no site info") 

908 

909 if obs_metadata.mjd is None: 

910 raise RuntimeError("Cannot call appGeoFromObserved: obs_metadata has no mjd") 

911 

912 obsPrms = _calculateObservatoryParameters(obs_metadata, wavelength, includeRefraction) 

913 

914 if are_arrays: 

915 raOut, decOut = palpy.oapqkVector('r', ra, dec, obsPrms) 

916 else: 

917 raOut, decOut = palpy.oapqk('r', ra, dec, obsPrms) 

918 

919 return np.array([raOut, decOut]) 

920 

921 

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). 

927 

928 included are precession-nutation, aberration, proper motion, parallax, refraction, 

929 radial velocity, diurnal aberration. 

930 

931 This method works in degrees. 

932 

933 @param [in] ra is the unrefracted RA in degrees (ICRS). Can be a numpy array or a number. 

934 

935 @param [in] dec is the unrefracted Dec in degrees (ICRS). Can be a numpy array or a number. 

936 

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). 

939 

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). 

942 

943 @param [in] parallax is parallax in arcsec 

944 Can be a numpy array or a number or None (default=None). 

945 

946 @param [in] v_rad is radial velocity (km/s) 

947 Can be a numpy array or a number or None (default=None). 

948 

949 @param [in] obs_metadata is an ObservationMetaData object describing the 

950 telescope pointing. 

951 

952 @param [in] epoch is the julian epoch (in years) against which the mean 

953 equinoxes are measured. 

954 

955 @param [in] includeRefraction toggles whether or not to correct for refraction 

956 

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 """ 

960 

961 if pm_ra is not None: 

962 pm_ra_in = radiansFromArcsec(pm_ra) 

963 else: 

964 pm_ra_in = None 

965 

966 if pm_dec is not None: 

967 pm_dec_in = radiansFromArcsec(pm_dec) 

968 else: 

969 pm_dec_in = None 

970 

971 if parallax is not None: 

972 parallax_in = radiansFromArcsec(parallax) 

973 else: 

974 parallax_in = None 

975 

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) 

980 

981 return np.degrees(output) 

982 

983 

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. 

989 

990 included are precession-nutation, aberration, proper motion, parallax, refraction, 

991 radial velocity, diurnal aberration. 

992 

993 This method works in radians. 

994 

995 @param [in] ra is the unrefracted RA in radians (ICRS). Can be a numpy array or a number. 

996 

997 @param [in] dec is the unrefracted Dec in radians (ICRS). Can be a numpy array or a number. 

998 

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). 

1001 

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). 

1004 

1005 @param [in] parallax is parallax in radians 

1006 Can be a numpy array or a number or None (default=None). 

1007 

1008 @param [in] v_rad is radial velocity (km/s) 

1009 Can be a numpy array or a number or None (default=None). 

1010 

1011 @param [in] obs_metadata is an ObservationMetaData object describing the 

1012 telescope pointing. 

1013 

1014 @param [in] epoch is the julian epoch (in years) against which the mean 

1015 equinoxes are measured. 

1016 

1017 @param [in] includeRefraction toggles whether or not to correct for refraction 

1018 

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) 

1021 

1022 """ 

1023 

1024 if obs_metadata is None: 

1025 raise RuntimeError("Cannot call observedFromICRS; obs_metadata is none") 

1026 

1027 if obs_metadata.mjd is None: 

1028 raise RuntimeError("Cannot call observedFromICRS; obs_metadata.mjd is none") 

1029 

1030 if epoch is None: 

1031 raise RuntimeError("Cannot call observedFromICRS; you have not specified an epoch") 

1032 

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) 

1036 

1037 ra_out, dec_out = _observedFromAppGeo(ra_apparent, dec_apparent, obs_metadata=obs_metadata, 

1038 includeRefraction=includeRefraction) 

1039 

1040 return np.array([ra_out, dec_out]) 

1041 

1042 

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. 

1051 

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. 

1054 

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. 

1059 

1060 This method works in degrees. 

1061 

1062 @param [in] ra is the observed RA in degrees. Can be a numpy array or a number. 

1063 

1064 @param [in] dec is the observed Dec in degrees. Can be a numpy array or a number. 

1065 

1066 @param [in] obs_metadata is an ObservationMetaData object describing the 

1067 telescope pointing. 

1068 

1069 @param [in] epoch is the julian epoch (in years) against which the mean 

1070 equinoxes are measured. 

1071 

1072 @param [in] includeRefraction toggles whether or not to correct for refraction 

1073 

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 """ 

1077 

1078 ra_out, dec_out = _icrsFromObserved(np.radians(ra), np.radians(dec), 

1079 obs_metadata=obs_metadata, 

1080 epoch=epoch, includeRefraction=includeRefraction) 

1081 

1082 return np.array([np.degrees(ra_out), np.degrees(dec_out)]) 

1083 

1084 

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. 

1093 

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. 

1096 

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. 

1101 

1102 This method works in radians. 

1103 

1104 @param [in] ra is the observed RA in radians. Can be a numpy array or a number. 

1105 

1106 @param [in] dec is the observed Dec in radians. Can be a numpy array or a number. 

1107 

1108 @param [in] obs_metadata is an ObservationMetaData object describing the 

1109 telescope pointing. 

1110 

1111 @param [in] epoch is the julian epoch (in years) against which the mean 

1112 equinoxes are measured. 

1113 

1114 @param [in] includeRefraction toggles whether or not to correct for refraction 

1115 

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 """ 

1119 

1120 _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromObserved") 

1121 

1122 if obs_metadata is None: 

1123 raise RuntimeError("Cannot call icrsFromObserved; obs_metadata is None") 

1124 

1125 if obs_metadata.mjd is None: 

1126 raise RuntimeError("Cannot call icrsFromObserved; obs_metadata.mjd is None") 

1127 

1128 if epoch is None: 

1129 raise RuntimeError("Cannot call icrsFromObserved; you have not specified an epoch") 

1130 

1131 ra_app, dec_app = _appGeoFromObserved(ra, dec, obs_metadata=obs_metadata, 

1132 includeRefraction=includeRefraction) 

1133 

1134 ra_icrs, dec_icrs = _icrsFromAppGeo(ra_app, dec_app, epoch=epoch, 

1135 mjd=obs_metadata.mjd) 

1136 

1137 return np.array([ra_icrs, dec_icrs])