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 

2from builtins import zip 

3from builtins import range 

4import unittest 

5import numpy as np 

6import lsst.utils.tests 

7import lsst.sims.utils as utils 

8 

9 

10def setup_module(module): 

11 lsst.utils.tests.init() 

12 

13 

14def controlAltAzFromRaDec(raRad_in, decRad_in, longRad, latRad, mjd): 

15 """ 

16 Converts RA and Dec to altitude and azimuth 

17 

18 @param [in] raRad is the RA in radians 

19 (observed geocentric) 

20 

21 @param [in] decRad is the Dec in radians 

22 (observed geocentric) 

23 

24 @param [in] longRad is the longitude of the observer in radians 

25 (positive east of the prime meridian) 

26 

27 @param [in[ latRad is the latitude of the observer in radians 

28 (positive north of the equator) 

29 

30 @param [in] mjd is the universal time expressed as an MJD 

31 

32 @param [out] altitude in radians 

33 

34 @param [out[ azimuth in radians 

35 

36 see: http://www.stargazing.net/kepler/altaz.html#twig04 

37 """ 

38 obs = utils.ObservationMetaData(mjd=utils.ModifiedJulianDate(UTC=mjd), 

39 site=utils.Site(longitude=np.degrees(longRad), 

40 latitude=np.degrees(latRad), 

41 name='LSST')) 

42 

43 if hasattr(raRad_in, '__len__'): 

44 raRad, decRad = utils._observedFromICRS(raRad_in, decRad_in, obs_metadata=obs, 

45 epoch=2000.0, includeRefraction=True) 

46 else: 

47 raRad, decRad = utils._observedFromICRS(raRad_in, decRad_in, 

48 obs_metadata=obs, epoch=2000.0, includeRefraction=True) 

49 

50 lst = utils.calcLmstLast(obs.mjd.UT1, longRad) 

51 last = lst[1] 

52 haRad = np.radians(last * 15.) - raRad 

53 

54 sinDec = np.sin(decRad) 

55 cosLat = np.cos(latRad) 

56 sinLat = np.sin(latRad) 

57 sinAlt = sinDec*sinLat + np.cos(decRad)*cosLat*np.cos(haRad) 

58 altRad = np.arcsin(sinAlt) 

59 azRad = np.arccos((sinDec - sinAlt*sinLat) / (np.cos(altRad)*cosLat)) 

60 azRadOut = np.where(np.sin(haRad) >= 0.0, 2.0 * np.pi - azRad, azRad) 

61 if isinstance(altRad, float): 

62 return altRad, float(azRadOut) 

63 return altRad, azRadOut 

64 

65 

66class CompoundCoordinateTransformationsTests(unittest.TestCase): 

67 

68 def setUp(self): 

69 self.rng = np.random.RandomState(32) 

70 self.mjd = 57087.0 

71 self.tolerance = 1.0e-5 

72 

73 def testExceptions(self): 

74 """ 

75 Test to make sure that methods complain when incorrect data types are passed. 

76 """ 

77 obs = utils.ObservationMetaData(pointingRA=55.0, pointingDec=-72.0, mjd=53467.8) 

78 

79 raFloat = 1.1 

80 raList = np.array([0.2, 0.3]) 

81 

82 decFloat = 1.1 

83 decList = np.array([0.2, 0.3]) 

84 

85 self.assertRaises(RuntimeError, utils._altAzPaFromRaDec, raList, decFloat, obs) 

86 self.assertRaises(RuntimeError, utils._altAzPaFromRaDec, raFloat, decList, obs) 

87 utils._altAzPaFromRaDec(raFloat, decFloat, obs) 

88 utils._altAzPaFromRaDec(raList, decList, obs) 

89 

90 self.assertRaises(RuntimeError, utils._raDecFromAltAz, raList, decFloat, obs) 

91 self.assertRaises(RuntimeError, utils._raDecFromAltAz, raFloat, decList, obs) 

92 utils._raDecFromAltAz(raFloat, decFloat, obs) 

93 utils._raDecFromAltAz(raList, decList, obs) 

94 

95 self.assertRaises(RuntimeError, utils.altAzPaFromRaDec, raList, decFloat, obs) 

96 self.assertRaises(RuntimeError, utils.altAzPaFromRaDec, raFloat, decList, obs) 

97 utils.altAzPaFromRaDec(raFloat, decFloat, obs) 

98 utils.altAzPaFromRaDec(raList, decList, obs) 

99 

100 self.assertRaises(RuntimeError, utils.raDecFromAltAz, raList, decFloat, obs) 

101 self.assertRaises(RuntimeError, utils.raDecFromAltAz, raFloat, decList, obs) 

102 utils.raDecFromAltAz(raFloat, decFloat, obs) 

103 utils.raDecFromAltAz(raList, decList, obs) 

104 

105 def test_raDecFromAltAz(self): 

106 """ 

107 Test conversion of Alt, Az to Ra, Dec using data on the Sun 

108 

109 This site gives the altitude and azimuth of the Sun as a function 

110 of time and position on the earth 

111 

112 http://aa.usno.navy.mil/data/docs/AltAz.php 

113 

114 This site gives the apparent geocentric RA, Dec of major celestial objects 

115 as a function of time 

116 

117 http://aa.usno.navy.mil/data/docs/geocentric.php 

118 

119 This site converts calendar dates into Julian Dates 

120 

121 http://aa.usno.navy.mil/data/docs/JulianDate.php 

122 """ 

123 

124 hours = np.radians(360.0 / 24.0) 

125 minutes = hours / 60.0 

126 seconds = minutes / 60.0 

127 

128 longitude_list = [] 

129 latitude_list = [] 

130 mjd_list = [] 

131 alt_list = [] 

132 az_list = [] 

133 ra_app_list = [] 

134 dec_app_list = [] 

135 

136 longitude_list.append(np.radians(-22.0 - 33.0 / 60.0)) 

137 latitude_list.append(np.radians(11.0 + 45.0 / 60.0)) 

138 mjd_list.append(2457364.958333 - 2400000.5) # 8 December 2015 11:00 UTC 

139 alt_list.append(np.radians(41.1)) 

140 az_list.append(np.radians(134.7)) 

141 ra_app_list.append(16.0 * hours + 59.0 * minutes + 16.665 * seconds) 

142 dec_app_list.append(np.radians(-22.0 - 42.0 / 60.0 - 2.94 / 3600.0)) 

143 

144 longitude_list.append(np.radians(-22.0 - 33.0 / 60.0)) 

145 latitude_list.append(np.radians(11.0 + 45.0 / 60.0)) 

146 mjd_list.append(2457368.958333 - 2400000.5) # 12 December 2015 11:00 UTC 

147 alt_list.append(np.radians(40.5)) 

148 az_list.append(np.radians(134.7)) 

149 ra_app_list.append(17.0 * hours + 16.0 * minutes + 51.649 * seconds) 

150 dec_app_list.append(np.radians(-23.0 - 3 / 60.0 - 50.35 / 3600.0)) 

151 

152 longitude_list.append(np.radians(145.0 + 23.0 / 60.0)) 

153 latitude_list.append(np.radians(-64.0 - 5.0 / 60.0)) 

154 mjd_list.append(2456727.583333 - 2400000.5) # 11 March 2014, 02:00 UTC 

155 alt_list.append(np.radians(29.5)) 

156 az_list.append(np.radians(8.2)) 

157 ra_app_list.append(23.0 * hours + 24.0 * minutes + 46.634 * seconds) 

158 dec_app_list.append(np.radians(-3.0 - 47.0 / 60.0 - 47.81 / 3600.0)) 

159 

160 longitude_list.append(np.radians(145.0 + 23.0 / 60.0)) 

161 latitude_list.append(np.radians(-64.0 - 5.0 / 60.0)) 

162 mjd_list.append(2456731.583333 - 2400000.5) # 15 March 2014, 02:00 UTC 

163 alt_list.append(np.radians(28.0)) 

164 az_list.append(np.radians(7.8)) 

165 ra_app_list.append(23.0 * hours + 39.0 * minutes + 27.695 * seconds) 

166 dec_app_list.append(np.radians(-2.0 - 13.0 / 60.0 - 18.32 / 3600.0)) 

167 

168 for longitude, latitude, mjd, alt, az, ra_app, dec_app in \ 

169 zip(longitude_list, latitude_list, mjd_list, alt_list, az_list, 

170 ra_app_list, dec_app_list): 

171 

172 obs = utils.ObservationMetaData(site=utils.Site(longitude=np.degrees(longitude), 

173 latitude=np.degrees(latitude), name='LSST'), 

174 mjd=utils.ModifiedJulianDate(UTC=mjd)) 

175 

176 ra_icrs, dec_icrs = utils._raDecFromAltAz(alt, az, obs) 

177 ra_test, dec_test = utils._appGeoFromICRS(ra_icrs, dec_icrs, mjd=obs.mjd) 

178 

179 distance = np.degrees(utils.haversine(ra_app, dec_app, ra_test, dec_test)) 

180 # this is all the precision we have in the alt,az data taken from the USNO 

181 self.assertLess(distance, 0.1) 

182 

183 correction = np.degrees(utils.haversine(ra_test, dec_test, ra_icrs, dec_icrs)) 

184 self.assertLess(distance, correction) 

185 

186 def testAltAzRADecRoundTrip(self): 

187 """ 

188 Test that altAzPaFromRaDec and raDecFromAltAz really invert each other 

189 """ 

190 

191 mjd = 58350.0 

192 

193 alt_in = [] 

194 az_in = [] 

195 for alt in np.arange(0.0, 90.0, 10.0): 

196 for az in np.arange(0.0, 360.0, 10.0): 

197 alt_in.append(alt) 

198 az_in.append(az) 

199 

200 alt_in = np.array(alt_in) 

201 az_in = np.array(az_in) 

202 

203 for lon in (0.0, 90.0, 135.0): 

204 for lat in (60.0, 30.0, -60.0, -30.0): 

205 

206 obs = utils.ObservationMetaData(mjd=mjd, 

207 site=utils.Site(longitude=lon, latitude=lat, name='LSST')) 

208 

209 ra_in, dec_in = utils.raDecFromAltAz(alt_in, az_in, obs) 

210 

211 self.assertIsInstance(ra_in, np.ndarray) 

212 self.assertIsInstance(dec_in, np.ndarray) 

213 

214 self.assertFalse(np.isnan(ra_in).any(), msg='there were NaNs in ra_in') 

215 self.assertFalse(np.isnan(dec_in).any(), msg='there were NaNs in dec_in') 

216 

217 # test that passing them in one at a time gives the same answer 

218 for ix in range(len(alt_in)): 

219 ra_f, dec_f = utils.raDecFromAltAz(alt_in[ix], az_in[ix], obs) 

220 self.assertIsInstance(ra_f, np.float) 

221 self.assertIsInstance(dec_f, np.float) 

222 self.assertAlmostEqual(ra_f, ra_in[ix], 12) 

223 self.assertAlmostEqual(dec_f, dec_in[ix], 12) 

224 

225 alt_out, az_out, pa_out = utils.altAzPaFromRaDec(ra_in, dec_in, obs) 

226 

227 self.assertFalse(np.isnan(pa_out).any(), msg='there were NaNs in pa_out') 

228 

229 for alt_c, az_c, alt_t, az_t in \ 

230 zip(np.radians(alt_in), np.radians(az_in), np.radians(alt_out), np.radians(az_out)): 

231 distance = utils.arcsecFromRadians(utils.haversine(az_c, alt_c, az_t, alt_t)) 

232 self.assertLess(distance, 0.2) 

233 # not sure why 0.2 arcsec is the limiting precision of this test 

234 

235 def testAltAzFromRaDec(self): 

236 """ 

237 Test conversion from RA, Dec to Alt, Az 

238 """ 

239 

240 nSamples = 100 

241 ra = self.rng.random_sample(nSamples)*2.0*np.pi 

242 dec = (self.rng.random_sample(nSamples)-0.5)*np.pi 

243 lon_rad = 1.467 

244 lat_rad = -0.234 

245 controlAlt, controlAz = controlAltAzFromRaDec(ra, dec, 

246 lon_rad, lat_rad, 

247 self.mjd) 

248 

249 obs = utils.ObservationMetaData(mjd=utils.ModifiedJulianDate(UTC=self.mjd), 

250 site=utils.Site(longitude=np.degrees(lon_rad), 

251 latitude=np.degrees(lat_rad), 

252 name='LSST')) 

253 

254 # verify parallactic angle against an expression from 

255 # http://www.astro.washington.edu/groups/APO/Mirror.Motions/Feb.2000.Image.Jumps/report.html#Image%20motion%20directions 

256 # 

257 ra_obs, dec_obs = utils._observedFromICRS(ra, dec, obs_metadata=obs, epoch=2000.0, 

258 includeRefraction=True) 

259 

260 lmst, last = utils.calcLmstLast(obs.mjd.UT1, lon_rad) 

261 hourAngle = np.radians(last * 15.0) - ra_obs 

262 controlSinPa = np.sin(hourAngle) * np.cos(lat_rad) / np.cos(controlAlt) 

263 

264 testAlt, testAz, testPa = utils._altAzPaFromRaDec(ra, dec, obs) 

265 

266 distance = utils.arcsecFromRadians(utils.haversine(controlAz, controlAlt, testAz, testAlt)) 

267 self.assertLess(distance.max(), 0.0001) 

268 self.assertLess(np.abs(np.sin(testPa) - controlSinPa).max(), self.tolerance) 

269 

270 # test non-vectorized version 

271 for r, d in zip(ra, dec): 

272 controlAlt, controlAz = controlAltAzFromRaDec(r, d, lon_rad, lat_rad, self.mjd) 

273 testAlt, testAz, testPa = utils._altAzPaFromRaDec(r, d, obs) 

274 lmst, last = utils.calcLmstLast(obs.mjd.UT1, lon_rad) 

275 r_obs, dec_obs = utils._observedFromICRS(r, d, obs_metadata=obs, 

276 epoch=2000.0, includeRefraction=True) 

277 hourAngle = np.radians(last * 15.0) - r_obs 

278 controlSinPa = np.sin(hourAngle) * np.cos(lat_rad) / np.cos(controlAlt) 

279 distance = utils.arcsecFromRadians(utils.haversine(controlAz, controlAlt, testAz, testAlt)) 

280 self.assertLess(distance, 0.0001) 

281 self.assertLess(np.abs(np.sin(testPa) - controlSinPa), self.tolerance) 

282 

283 def test_altAzPaFromRaDec_no_refraction(self): 

284 """ 

285 Test that altAzPaFromRaDec gives a sane answer when you turn off 

286 refraction. 

287 """ 

288 

289 rng = np.random.RandomState(44) 

290 n_samples = 10 

291 n_batches = 10 

292 for i_batch in range(n_batches): 

293 # first, generate some sane RA, Dec values by generating sane 

294 # Alt, Az values with refraction and converting them into 

295 # RA, Dec 

296 alt_sane = rng.random_sample(n_samples)*45.0 + 45.0 

297 az_sane = rng.random_sample(n_samples)*360.0 

298 mjd_input = rng.random_sample(n_samples)*10000.0 + 40000.0 

299 mjd_list = utils.ModifiedJulianDate.get_list(TAI=mjd_input) 

300 

301 ra_sane = [] 

302 dec_sane = [] 

303 obs_sane = [] 

304 for alt, az, mjd in zip(alt_sane, az_sane, mjd_list): 

305 obs = utils.ObservationMetaData(mjd=mjd) 

306 ra, dec = utils.raDecFromAltAz(alt, az, obs) 

307 ra_sane.append(ra) 

308 dec_sane.append(dec) 

309 obs_sane.append(obs) 

310 

311 # Now, loop over our refracted RA, Dec, Alt, Az values. 

312 # Convert from RA, Dec to unrefracted Alt, Az. Then, apply refraction 

313 # with our applyRefraction method. Check that the resulting refracted 

314 # zenith distance is: 

315 # 1) within 0.1 arcsec of the zenith distance of the already refracted 

316 # alt value calculated above 

317 # 

318 # 2) closer to the zenith distance calculated above than to the 

319 # unrefracted zenith distance 

320 for ra, dec, obs, alt_ref, az_ref in \ 

321 zip(ra_sane, dec_sane, obs_sane, alt_sane, az_sane): 

322 

323 alt, az, pa = utils.altAzPaFromRaDec(ra, dec, obs, 

324 includeRefraction = False) 

325 

326 tanz, tanz3 = utils.refractionCoefficients(site=obs.site) 

327 refracted_zd = utils.applyRefraction(np.radians(90.0-alt), tanz, tanz3) 

328 

329 # Check that the two independently refracted zenith distances agree 

330 # to within 0.1 arcsec 

331 self.assertLess(np.abs(utils.arcsecFromRadians(refracted_zd) - 

332 utils.arcsecFromRadians(np.radians(90.0-alt_ref))), 

333 0.1) 

334 

335 # Check that the two refracted zenith distances are closer to each other 

336 # than to the unrefracted zenith distance 

337 self.assertLess(np.abs(np.degrees(refracted_zd)-(90.0-alt_ref)), 

338 np.abs((90.0-alt_ref) - (90.0-alt))) 

339 

340 self.assertLess(np.abs(np.degrees(refracted_zd)-(90.0-alt_ref)), 

341 np.abs(np.degrees(refracted_zd) - (90.0-alt))) 

342 

343 def test_raDecFromAltAz_noref(self): 

344 """ 

345 test that raDecFromAltAz correctly inverts altAzPaFromRaDec, even when 

346 refraction is turned off 

347 """ 

348 

349 rng = np.random.RandomState(55) 

350 n_samples = 10 

351 n_batches = 10 

352 

353 for i_batch in range(n_batches): 

354 d_sun = 0.0 

355 while d_sun < 45.0: # because ICRS->Observed transformation breaks down close to the sun 

356 

357 alt_in = rng.random_sample(n_samples)*50.0 + 20.0 

358 az_in = rng.random_sample(n_samples)*360.0 

359 obs = utils.ObservationMetaData(mjd=43000.0) 

360 ra_in, dec_in = utils.raDecFromAltAz(alt_in, az_in, obs=obs, includeRefraction=False) 

361 

362 d_sun = utils.distanceToSun(ra_in, dec_in, obs.mjd).min() 

363 

364 alt_out, az_out, pa_out = utils.altAzPaFromRaDec(ra_in, dec_in, obs=obs, 

365 includeRefraction=False) 

366 

367 dd = utils.haversine(np.radians(alt_out), np.radians(az_out), 

368 np.radians(alt_in), np.radians(az_in)) 

369 self.assertLess(utils.arcsecFromRadians(dd).max(), 0.01) 

370 

371 def test_raDecAltAz_noRefraction_degVsRadians(self): 

372 """ 

373 Check that raDecFromAltAz and altAzPaFromRaDec are consistent in a degrees-versus-radians 

374 sense when refraction is turned off 

375 """ 

376 

377 rng = np.random.RandomState(34) 

378 n_samples = 10 

379 ra_in = rng.random_sample(n_samples)*360.0 

380 dec_in = rng.random_sample(n_samples)*180.0 - 90.0 

381 mjd = 43000.0 

382 obs = utils.ObservationMetaData(mjd=mjd) 

383 alt, az, pa = utils.altAzPaFromRaDec(ra_in, dec_in, obs, includeRefraction=False) 

384 alt_rad, az_rad, pa_rad = utils._altAzPaFromRaDec(np.radians(ra_in), 

385 np.radians(dec_in), 

386 obs, includeRefraction=False) 

387 

388 distance = utils.haversine(az_rad, alt_rad, 

389 np.radians(az), np.radians(alt)) 

390 self.assertLess(utils.arcsecFromRadians(distance).min(), 0.001) 

391 np.testing.assert_array_almost_equal(pa, np.degrees(pa_rad), decimal=12) 

392 

393 ra, dec = utils.raDecFromAltAz(alt, az, obs, includeRefraction=False) 

394 ra_rad, dec_rad = utils._raDecFromAltAz(alt_rad, az_rad, obs, includeRefraction=False) 

395 distance = utils.haversine(ra_rad, dec_rad, np.radians(ra), np.radians(dec)) 

396 self.assertLess(utils.arcsecFromRadians(distance).min(), 0.001) 

397 

398 

399class MemoryTestClass(lsst.utils.tests.MemoryTestCase): 

400 pass 

401 

402if __name__ == "__main__": 402 ↛ 403line 402 didn't jump to line 403, because the condition on line 402 was never true

403 lsst.utils.tests.init() 

404 unittest.main()