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

7 

8__all__ = ["_pupilCoordsFromObserved", 

9 "_pupilCoordsFromRaDec", "pupilCoordsFromRaDec", 

10 "_raDecFromPupilCoords", "raDecFromPupilCoords", 

11 "_observedFromPupilCoords", "observedFromPupilCoords"] 

12 

13 

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. 

21 

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. 

25 

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. 

33 

34 @param [in] ra_in is in degrees (ICRS). Can be either a numpy array or a number. 

35 

36 @param [in] dec_in is in degrees (ICRS). Can be either a numpy array or a number. 

37 

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

40 

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

43 

44 @param [in] parallax is parallax in arcsec 

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

46 

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

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

49 

50 @param [in] includeRefraction is a boolean controlling the application of refraction. 

51 

52 @param [in] obs_metadata is an ObservationMetaData instantiation characterizing the 

53 telescope location and pointing. 

54 

55 @param [in] epoch is the epoch of mean ra and dec in julian years (default=2000.0) 

56 

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

60 

61 if pm_ra is not None: 

62 pm_ra_in = radiansFromArcsec(pm_ra) 

63 else: 

64 pm_ra_in = None 

65 

66 if pm_dec is not None: 

67 pm_dec_in = radiansFromArcsec(pm_dec) 

68 else: 

69 pm_dec_in = None 

70 

71 if parallax is not None: 

72 parallax_in = radiansFromArcsec(parallax) 

73 else: 

74 parallax_in = None 

75 

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) 

81 

82 

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. 

91 

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. 

95 

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. 

103 

104 @param [in] ra_in is in radians (ICRS). Can be either a numpy array or a number. 

105 

106 @param [in] dec_in is in radians (ICRS). Can be either a numpy array or a number. 

107 

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

110 

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

113 

114 @param [in] parallax is parallax in radians 

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

116 

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

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

119 

120 @param [in] includeRefraction is a boolean controlling the application of refraction. 

121 

122 @param [in] obs_metadata is an ObservationMetaData instantiation characterizing the 

123 telescope location and pointing. 

124 

125 @param [in] epoch is the epoch of mean ra and dec in julian years (default=2000.0) 

126 

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

130 

131 are_arrays = _validate_inputs([ra_in, dec_in], ['ra_in', 'dec_in'], 

132 "pupilCoordsFromRaDec") 

133 

134 if obs_metadata is None: 

135 raise RuntimeError("Cannot call pupilCoordsFromRaDec without obs_metadata") 

136 

137 if obs_metadata.mjd is None: 

138 raise RuntimeError("Cannot call pupilCoordsFromRaDec; obs_metadata.mjd is None") 

139 

140 if epoch is None: 

141 raise RuntimeError("Cannot call pupilCoordsFromRaDec; epoch is None") 

142 

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

146 

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

149 

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) 

156 

157 return _pupilCoordsFromObserved(ra_obs, dec_obs, obs_metadata, 

158 epoch=epoch, includeRefraction=includeRefraction) 

159 

160 

161def _pupilCoordsFromObserved(ra_obs, dec_obs, obs_metadata, epoch=2000.0, includeRefraction=True): 

162 """ 

163 Convert Observed RA, Dec into pupil coordinates 

164 

165 Parameters 

166 ---------- 

167 ra_obs is the observed RA in radians 

168 

169 dec_obs is the observed Dec in radians 

170 

171 obs_metadata is an ObservationMetaData characterizing the telescope location and pointing 

172 

173 epoch is the epoch of the mean RA and Dec in julian years (default=2000.0) 

174 

175 includeRefraction is a boolean controlling the application of refraction. 

176 

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

182 

183 are_arrays = _validate_inputs([ra_obs, dec_obs], ['ra_obs', 'dec_obs'], 

184 "pupilCoordsFromObserved") 

185 

186 if obs_metadata.rotSkyPos is None: 

187 raise RuntimeError("Cannot call pupilCoordsFromObserved; " 

188 "rotSkyPos is None") 

189 

190 theta = -1.0*obs_metadata._rotSkyPos 

191 

192 ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA, 

193 obs_metadata._pointingDec, 

194 obs_metadata=obs_metadata, 

195 epoch=epoch, 

196 includeRefraction=includeRefraction) 

197 

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) 

225 

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 

229 

230 x_out = x*np.cos(theta) - y*np.sin(theta) 

231 y_out = x*np.sin(theta) + y*np.cos(theta) 

232 

233 return np.array([x_out, y_out]) 

234 

235 

236def _observedFromPupilCoords(xPupil, yPupil, obs_metadata=None, 

237 includeRefraction=True, 

238 epoch=2000.0): 

239 """ 

240 Convert pupil coordinates into observed (RA, Dec) 

241 

242 @param [in] xPupil -- pupil coordinates in radians. 

243 Can be a numpy array or a number. 

244 

245 @param [in] yPupil -- pupil coordinates in radians. 

246 Can be a numpy array or a number. 

247 

248 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing 

249 the state of the telescope 

250 

251 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate 

252 transformations (in years; defaults to 2000) 

253 

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) 

257 

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. 

262 

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

267 

268 are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'], 

269 "observedFromPupilCoords") 

270 

271 if obs_metadata is None: 

272 raise RuntimeError("Cannot call observedFromPupilCoords without obs_metadata") 

273 

274 if epoch is None: 

275 raise RuntimeError("Cannot call observedFromPupilCoords; epoch is None") 

276 

277 if obs_metadata.rotSkyPos is None: 

278 raise RuntimeError("Cannot call observedFromPupilCoords without rotSkyPos " + 

279 "in obs_metadata") 

280 

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

284 

285 if obs_metadata.mjd is None: 

286 raise RuntimeError("Cannot calculate RA, Dec without mjd " + 

287 "in obs_metadata") 

288 

289 ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA, 

290 obs_metadata._pointingDec, 

291 obs_metadata=obs_metadata, 

292 epoch=epoch, 

293 includeRefraction=includeRefraction) 

294 

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 

298 

299 x_g = xPupil*np.cos(theta) - yPupil*np.sin(theta) 

300 y_g = xPupil*np.sin(theta) + yPupil*np.cos(theta) 

301 

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. 

304 

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) 

309 

310 return raObs, decObs 

311 

312 

313def observedFromPupilCoords(xPupil, yPupil, obs_metadata=None, 

314 includeRefraction=True, 

315 epoch=2000.0): 

316 """ 

317 Convert pupil coordinates into observed (RA, Dec) 

318 

319 @param [in] xPupil -- pupil coordinates in radians. 

320 Can be a numpy array or a number. 

321 

322 @param [in] yPupil -- pupil coordinates in radians. 

323 Can be a numpy array or a number. 

324 

325 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing 

326 the state of the telescope 

327 

328 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate 

329 transformations (in years; defaults to 2000) 

330 

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) 

334 

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. 

339 

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) 

348 

349 return np.degrees(ra_rad), np.degrees(dec_rad) 

350 

351 

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. 

357 

358 @param [in] yPupil -- pupil coordinates in radians. 

359 Can be a numpy array or a number. 

360 

361 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing 

362 the state of the telescope 

363 

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) 

367 

368 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate 

369 transformations (in years; defaults to 2000) 

370 

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) 

373 

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

378 

379 output = _raDecFromPupilCoords(xPupil, yPupil, 

380 obs_metadata=obs_metadata, 

381 epoch=epoch, 

382 includeRefraction=includeRefraction) 

383 

384 return np.degrees(output) 

385 

386 

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. 

392 

393 @param [in] yPupil -- pupil coordinates in radians. 

394 Can be a numpy array or a number. 

395 

396 @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing 

397 the state of the telescope 

398 

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) 

402 

403 @param [in] epoch -- julian epoch of the mean equinox used for the coordinate 

404 transformations (in years; defaults to 2000) 

405 

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) 

408 

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

413 

414 are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'], "raDecFromPupilCoords") 

415 

416 if obs_metadata is None: 

417 raise RuntimeError("Cannot call raDecFromPupilCoords without obs_metadata") 

418 

419 if epoch is None: 

420 raise RuntimeError("Cannot call raDecFromPupilCoords; epoch is None") 

421 

422 if obs_metadata.rotSkyPos is None: 

423 raise RuntimeError("Cannot call raDecFromPupilCoords without rotSkyPos " + 

424 "in obs_metadata") 

425 

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

429 

430 if obs_metadata.mjd is None: 

431 raise RuntimeError("Cannot calculate RA, Dec without mjd " + 

432 "in obs_metadata") 

433 

434 raObs, decObs = _observedFromPupilCoords(xPupil, yPupil, 

435 obs_metadata=obs_metadata, 

436 epoch=epoch, 

437 includeRefraction=includeRefraction) 

438 

439 ra_icrs, dec_icrs = _icrsFromObserved(raObs, decObs, 

440 obs_metadata=obs_metadata, 

441 epoch=epoch, 

442 includeRefraction=includeRefraction) 

443 

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