def allowedPos(world_pos, date): """ This routine can be used to check whether WFIRST would be allowed to look at a particular position (`world_pos`) on a given `date`. This is determined by the angle of this position relative to the Sun. In general, WFIRST can point at angles relative to the Sun in the range 90+/-36 degrees. Obviously, pointing too close to the Sun would result in overly high sky backgrounds. It is less obvious why WFIRST cannot look at a spot directly opposite from the Sun (180 degrees on the sky). The reason is that the observatory is aligned such that if the observer is looking at some sky position, the solar panels are oriented at 90 degrees from that position. So it's always optimal for the observatory to be pointing at an angle of 90 degrees relative to the Sun. It is also permitted to look within 36 degrees of that optimal position. @param world_pos A galsim.CelestialCoord indicating the position at which the observer wishes to look. @param date A python datetime object indicating the desired date of observation. @returns True or False, indicating whether it is permitted to look at this position on this date. """ # Find the Sun's location on the sky on this date. from galsim.celestial import _ecliptic_to_equatorial, _sun_position_ecliptic sun = _ecliptic_to_equatorial(_sun_position_ecliptic(date), date.year) # Find the angle between that and the supplied position angle_deg = abs(world_pos.distanceTo(sun) / galsim.degrees) # Check if it's within tolerance. min_ang = 90. - 36. max_ang = 90. + 36. return angle_deg >= min_ang and angle_deg <= max_ang
def bestPA(world_pos, date): """ This routine determines the best position angle for the observatory for a given observation date and position on the sky. The best/optimal position angle is determined by the fact that the solar panels are at 90 degrees to the position being observed, and it is best to have those facing the Sun as directly as possible. Note that if a given `world_pos` is not actually observable on the given `date`, then this routine will return None. @param world_pos A galsim.CelestialCoord indicating the position at which the observer wishes to look. @param date A python datetime object indicating the desired date of observation. @returns the best position angle for the observatory, as a galsim.Angle, or None if the position is not observable. """ # First check for observability. if not allowedPos(world_pos, date): return None # Find the location of the sun on this date. +X_observatory points out into the sky, towards # world_pos, while +Z is in the plane of the sky pointing towards the sun as much as possible. from galsim.celestial import _ecliptic_to_equatorial, _sun_position_ecliptic sun = _ecliptic_to_equatorial(_sun_position_ecliptic(date), date.year) # Now we do a projection onto the sky centered at world_pos to find the (u, v) for the Sun. sun_tp = world_pos.project(sun, 'gnomonic') # We want to rotate around by 90 degrees to find the +Y obs direction. Specifically, we want # (+X, +Y, +Z)_obs to form a right-handed coordinate system. y_obs_tp = galsim.PositionD(-sun_tp.y, sun_tp.x) y_obs = world_pos.deproject(y_obs_tp, 'gnomonic') # Finally the observatory position angle is defined by the angle between +Y_observatory and the # celestial north pole. It is defined as position angle east of north. north = galsim.CelestialCoord(y_obs.ra, 90. * galsim.degrees) obs_pa = world_pos.angleBetween(y_obs, north) return obs_pa
def test_ecliptic(): """Test the conversion from equatorial to ecliptic coordinates.""" # Use locations of ecliptic poles from http://en.wikipedia.org/wiki/Ecliptic_pole north_pole = galsim.CelestialCoord(galsim.HMS_Angle('18:00:00.00'), galsim.DMS_Angle('66:33:38.55')) el, b = north_pole.ecliptic() # North pole should have b=90 degrees, with el being completely arbitrary. numpy.testing.assert_almost_equal(b.rad(), pi / 2, decimal=6) south_pole = galsim.CelestialCoord(galsim.HMS_Angle('06:00:00.00'), galsim.DMS_Angle('-66:33:38.55')) el, b = south_pole.ecliptic() # South pole should have b=-90 degrees, with el being completely arbitrary. numpy.testing.assert_almost_equal(b.rad(), -pi / 2, decimal=6) # Also confirm that positions that should be the same in equatorial and ecliptic coordinates are # actually the same: vernal_equinox = galsim.CelestialCoord(0. * galsim.radians, 0. * galsim.radians) el, b = vernal_equinox.ecliptic() numpy.testing.assert_almost_equal(b.rad(), 0., decimal=6) numpy.testing.assert_almost_equal(el.rad(), 0., decimal=6) autumnal_equinox = galsim.CelestialCoord(pi * galsim.radians, 0. * galsim.radians) el, b = autumnal_equinox.ecliptic() numpy.testing.assert_almost_equal(el.rad(), pi, decimal=6) numpy.testing.assert_almost_equal(b.rad(), 0., decimal=6) # Finally, test the results of using a date to get ecliptic coordinates with respect to the sun, # instead of absolute ones. For this, use dates and times of vernal and autumnal equinox # in 2014 from # http://wwp.greenwichmeantime.com/longest-day/ # and the conversion to Julian dates from # http://www.aavso.org/jd-calculator import datetime vernal_eq_date = datetime.datetime(2014, 3, 20, 16, 57, 0) el, b = vernal_equinox.ecliptic(epoch=2014) el_rel, b_rel = vernal_equinox.ecliptic(epoch=2014, date=vernal_eq_date) # Vernal equinox: should have (el, b) = (el_rel, b_rel) = 0.0 numpy.testing.assert_almost_equal(el_rel.rad(), el.rad(), decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), b.rad(), decimal=6) # Now do the autumnal equinox: should have (el, b) = (pi, 0) = (el_rel, b_rel) when we look at # the time of the vernal equinox. el, b = autumnal_equinox.ecliptic(epoch=2014) el_rel, b_rel = autumnal_equinox.ecliptic(epoch=2014, date=vernal_eq_date) numpy.testing.assert_almost_equal(el_rel.rad(), el.rad(), decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), b.rad(), decimal=6) # And check that if it's the date of the autumnal equinox (sun at (180, 0)) but we're looking at # the position of the vernal equinox (0, 0), then (el_rel, b_rel) = (-180, 0) autumnal_eq_date = datetime.datetime(2014, 9, 23, 2, 29, 0) el_rel, b_rel = vernal_equinox.ecliptic(epoch=2014, date=autumnal_eq_date) numpy.testing.assert_almost_equal(el_rel.rad(), -pi, decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), 0., decimal=6) # And check that if it's the date of the vernal equinox (sun at (0, 0)) but we're looking at # the position of the autumnal equinox (180, 0), then (el_rel, b_rel) = (180, 0) el_rel, b_rel = autumnal_equinox.ecliptic(epoch=2014, date=vernal_eq_date) numpy.testing.assert_almost_equal(el_rel.rad(), pi, decimal=3) numpy.testing.assert_almost_equal(b_rel.rad(), 0., decimal=6) # Check round-trips: go from CelestialCoord to ecliptic back to equatorial, and make sure # results are the same. This includes use of a function that isn't available to users, but we # use it for a few things so we should still make sure it's working properly. from galsim.celestial import _ecliptic_to_equatorial north_pole_2 = _ecliptic_to_equatorial(north_pole.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(north_pole.ra.rad(), north_pole_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(north_pole.dec.rad(), north_pole_2.dec.rad(), decimal=6) south_pole_2 = _ecliptic_to_equatorial(south_pole.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(south_pole.ra.rad(), south_pole_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(south_pole.dec.rad(), south_pole_2.dec.rad(), decimal=6) vernal_equinox_2 = _ecliptic_to_equatorial( vernal_equinox.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(vernal_equinox.ra.rad(), vernal_equinox_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(vernal_equinox.dec.rad(), vernal_equinox_2.dec.rad(), decimal=6) autumnal_equinox_2 = _ecliptic_to_equatorial( autumnal_equinox.ecliptic(epoch=2014), 2014) numpy.testing.assert_almost_equal(autumnal_equinox.ra.rad(), autumnal_equinox_2.ra.rad(), decimal=6) numpy.testing.assert_almost_equal(autumnal_equinox.dec.rad(), autumnal_equinox_2.dec.rad(), decimal=6)