def mean_to_apparent(target, tdb): '''Given a target and TDB, return an apparent (RA, Dec) tuple. Thin wrapper for SLA_MAP. ''' # Complain if the minimum target fields aren't present if not target.get('ra'): raise IncompleteTargetError("Missing RA in target definition") if not target.get('dec'): raise IncompleteTargetError("Missing Declination in target definition") target = make_ra_dec_target( target['ra'], target['dec'], ra_proper_motion=target.get('ra_proper_motion'), dec_proper_motion=target.get('dec_proper_motion'), parallax=target.get('parallax'), rad_vel=target.get('rad_vel'), epoch=target.get('epoch')) (ra_app_rads, dec_app_rads) = sla.sla_map( target['ra'].in_radians(), target['dec'].in_radians(), target['ra_proper_motion'].in_radians_per_year(), target['dec_proper_motion'].in_radians_per_year(), target['parallax'], target['rad_vel'], target['epoch'], tdb) ra_apparent = Angle(radians=ra_app_rads) dec_apparent = Angle(radians=dec_app_rads) return (ra_apparent, dec_apparent)
def setup(self): # Target # Note: Aladin units are mas/yr... self.canopus = { 'ra': RightAscension('06 23 57.11'), 'dec': Declination('-52 40 03.5'), #'ra_proper_motion' : ProperMotion(RightAscension('00 00 00.01999')), #'dec_proper_motion' : ProperMotion(Declination('00 00 00.02367')), 'ra_proper_motion': ProperMotion(RightAscension('00 00 00.0')), 'dec_proper_motion': ProperMotion(Declination('00 00 00.0')), #'parallax' : 0.01043, # Units: arcsec 'parallax': 0.0, # Units: arcsec #'rad_vel' : 20.5, # Units: km/s (-ve approaches) 'rad_vel': 0.0, # Units: km/s (-ve approaches) 'epoch': 2000, } # Site (East +ve longitude) self.siding_spring = { 'latitude': Angle(degrees=-31.273), 'longitude': Angle(degrees=149.070593) } # Date self.date = datetime(year=2010, month=3, day=12)
def test_read_neocp_orbit4(self): # Element dictionary produced by TAL's code from NEOCP orbit file, migrated to # rise_set format. # This test tests very short orbits from find_orb with an arc length in # minutes expected = { 'name' : 'LSCTLF3', 'epoch' : 57109.0, 'semi_axis' : 2.2601939, 'mean_anomaly' : Angle(degrees= 23.58746), 'arg_perihelion' : Angle(degrees= 75.79622), 'long_node' : Angle(degrees= 69.92181), 'inclination' : Angle(degrees= 9.43941), 'eccentricity' : 0.3369232, 'MDM' : Angle(degrees=0.29005846), 'H' : 18.90, 'G' : 0.15, 'n_obs' : 6, 'n_oppos' : 1, 'n_nights' : old_div(18.8,1440.0), # 18.8 mins->days 'reference' : 'FO 150328', 'residual' : 0.08, 'uncertainty' : 'U', } recieved = read_neocp_orbit('test/LSCTLF3.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def test_get_rise_set_against_lco_rise_set(self): facility = FakeFacility() sites = facility.get_observing_sites() start = datetime(2018, 10, 10) end = datetime(2018, 10, 11) # Get sunrise/set from rise-set library coj = { 'latitude': Angle(degrees=sites.get('Siding Spring')['latitude']), 'longitude': Angle(degrees=sites.get('Siding Spring')['longitude']) } coj_observer = observer_for_site(sites.get('Siding Spring')) (transit, control_rise, control_set) = calc_sunrise_set(coj, start, 'sunrise') # Get rise/set from observations module rise_set = get_rise_set(coj_observer, self.sun, start, end) rise_delta = timedelta(hours=rise_set[0][0].hour, minutes=rise_set[0][0].minute, seconds=rise_set[0][0].second) set_delta = timedelta(hours=rise_set[0][1].hour, minutes=rise_set[0][1].minute, seconds=rise_set[0][1].second) self.assertLessEqual(rise_delta - control_rise, abs(timedelta(minutes=5))) self.assertLessEqual(set_delta - control_set, abs(timedelta(minutes=5)))
def test_read_neocp_comet_orbit2(self): # Hand-crafted element dictionary from MPC MPES output on 2014-09-26. # This test tests periodic comet parsing expected = { 'name' : '0299P', 'epoch' : 57000.0, 'long_node' : Angle(degrees=271.6800), 'eccentricity' : 0.282122, 'semi_axis' : None, 'mean_anomaly' : None, 'arg_perihelion' : Angle(degrees=323.5091), 'inclination' : Angle(degrees=10.4798), 'MDM' : None, 'H' : 11.5, 'G' : 4.00, 'n_obs' : None, 'n_nights' : None, 'type' : 'MPC_COMET' } recieved = read_neocp_orbit('test/Comet_299P.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def test_ephemeris_chunk_within_ha_limits(self): ha1 = Angle(degrees=3*self.HOURS_TO_DEGREES) ha2 = Angle(degrees=3.5*self.HOURS_TO_DEGREES) neg_limit = Angle(degrees=-4.6*self.HOURS_TO_DEGREES) pos_limit = Angle(degrees=4.6*self.HOURS_TO_DEGREES) assert_equal(ephemeris_chunk_within_ha_limits(ha1, ha2, neg_limit, pos_limit), True)
def test_ephemeris_chunk_fully_outside_negative_ha_limit(self): ha1 = Angle(degrees=-4.7*self.HOURS_TO_DEGREES) ha2 = Angle(degrees=-4.8*self.HOURS_TO_DEGREES) neg_limit = Angle(degrees=-4.6*self.HOURS_TO_DEGREES) pos_limit = Angle(degrees=4.6*self.HOURS_TO_DEGREES) assert_equal(ephemeris_chunk_within_ha_limits(ha1, ha2, neg_limit, pos_limit), False)
def make_minor_planet_target(target_type, epoch, inclination, long_node, arg_perihelion, semi_axis, eccentricity, mean_anomaly): """ Creates a rise-set target for a MPC minor planet Creates a dictionary rise-set formatted target that must have all the necessary coordinates of a MPC formatted minor planet. Args: target_type (str): The string representation of this target type ('MPC_MINOR_PLANET') epoch (float): The epoch of the target ephemerites inclination (float): The inclination of the target in degrees long_node (float): The longitude of the ascending node in degrees arg_perihelion (float): The argument of perihelion in degrees semi_axis (float): The semi-axis of the target eccentricity (float): The eccentricty of the target mean_anomaly (float): The mean anomaly of the target in degrees Returns: dict: A dictionary of target details to use as input to other rise-set functions """ target = { 'type': target_type, 'epoch': epoch, 'inclination': Angle(degrees=inclination), 'long_node': Angle(degrees=long_node), 'arg_perihelion': Angle(degrees=arg_perihelion), 'semi_axis': semi_axis, 'eccentricity': eccentricity, 'mean_anomaly': Angle(degrees=mean_anomaly), } return target
def test_find_moving_object_up_intervals1(self): window = { 'start' : datetime(2013, 12, 10), 'end' : datetime(2013, 12, 11) } chunksize = timedelta(hours=3) expected_intervals = [ ( datetime(2013, 12, 10, 9), datetime(2013, 12, 10, 12) ), ( datetime(2013, 12, 10, 12), datetime(2013, 12, 10, 15) ) ] expected_altitudes = [ Angle(degrees=42.7537), Angle(degrees=74.9902) ] received_ints, received_alts = find_moving_object_up_intervals(window, self.elements, self.cpt, chunksize) assert_equal(len(expected_intervals), len(received_ints)) assert_equal(len(expected_altitudes), len(received_alts)) for e, r in zip(expected_intervals, received_ints): assert_equal(e, r) for e, r in zip(expected_altitudes, received_alts): assert_almost_equal(e.in_degrees(), r.in_degrees(), places=3)
def test_read_neocp_comet_orbit1(self): # Hand-crafted element dictionary from MPC MPES output on 2014-09-26. # This test tests long-period comet parsing expected = { 'name' : 'CK13A010', 'epoch' : 57000.0, 'long_node' : Angle(degrees=300.9764), 'eccentricity' : 1.000434, 'semi_axis' : None, 'mean_anomaly' : None, 'arg_perihelion' : Angle(degrees=2.4226), 'inclination' : Angle(degrees=129.0428), 'MDM' : None, 'H' : 8.2, 'G' : 2.4, 'n_obs' : None, 'n_nights' : None, 'type' : 'MPC_COMET' } recieved = read_neocp_orbit('test/Comet_SidingSpring.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def apparent_planet_pos(planet_name, tdb, site): '''Return the topocentric apparent position (ra, dec) tuple of a planet at a particular time, from a particular site. Thin wrapper for SLA_RDPLAN. ''' latitude = site['latitude'] longitude = site['longitude'] # TODO: Raise an error if an invalid body is provided. planet = dict(mercury=1, venus=2, moon=3, mars=4, jupiter=5, saturn=6, uranus=7, neptune=8, pluto=9, sun=0) (app_ra_rads, app_dec_rads, diameter_rads) = sla.sla_rdplan(tdb, planet[planet_name], longitude.in_radians(), latitude.in_radians()) app_ra = Angle(radians=app_ra_rads) app_dec = Angle(radians=app_dec_rads) diameter = Angle(radians=diameter_rads) return (app_ra, app_dec, diameter)
def test_read_neocp_orbit5(self): # Element dictionary produced by TAL's code from NEOCP orbit file, migrated to # rise_set format. # This test tests very short orbits from find_orb with an arc length in # hours expected = { 'name' : 'LSCTLF3', 'epoch' : 57109.0, 'mean_anomaly' : Angle(degrees= 21.73763), 'arg_perihelion' : Angle(degrees= 80.02788), 'long_node' : Angle(degrees= 64.35739), 'inclination' : Angle(degrees= 12.17788), 'eccentricity' : 0.3895454, 'MDM' : Angle(degrees=0.21752314), 'semi_axis' : 2.7382037, 'H' : 18.23, 'G' : 0.15, 'n_obs' : 10, 'n_oppos' : 1, 'n_nights' : old_div(13.0,24.0), # 13.0 hrs->days 'reference' : 'FO 150328', 'residual' : 0.09, 'uncertainty' : 'U', } recieved = read_neocp_orbit('test/LSCTLF3_V2.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def test_read_neocp_orbit7(self): # Element dictionary produced by TAL's code from NEOCP orbit file, migrated to # rise_set format. # This test tests NEOCP orbits without an uncertainty. expected = { 'name' : 'N007rdx', 'epoch' : 57120.0, 'mean_anomaly' : Angle(degrees=119.11683), 'arg_perihelion' : Angle(degrees= 75.91136), 'long_node' : Angle(degrees=351.90874), 'inclination' : Angle(degrees= 3.64505), 'eccentricity' : 0.1054741, 'MDM' : Angle(degrees=1.07358714), 'semi_axis' : 0.9445925, 'H' : 21.8, 'G' : 0.15, 'n_obs' : 5, 'n_oppos' : 1, 'n_nights' : 0, 'uncertainty' : 'U', # No value, assume default 'reference' : '', } recieved = read_neocp_orbit('test/N007rdx.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def test_read_neocp_orbit3(self): # Element dictionary produced by TAL's code from NEOCP orbit file, migrated to # rise_set format. # This test tests multi-opposition orbits expected = { 'name' : '00001', 'epoch' : 57200.0, 'long_node' : Angle(degrees=80.3272), 'eccentricity' : 0.0757825, 'semi_axis' : 2.7679724, 'mean_anomaly' : Angle(degrees=138.66222), 'arg_perihelion' : Angle(degrees= 72.65410), 'inclination' : Angle(degrees= 10.59230), 'MDM' : Angle(degrees=0.21402349), 'H' : 3.34, 'G' : 0.12, 'n_obs' : 6541, 'n_oppos' : 107, 'n_nights' : '1802-2014', 'reference' : 'MPO328644', 'uncertainty' : '0', } recieved = read_neocp_orbit('test/00001_Ceres.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def __init__(self, site, start_date, end_date, horizon=0, twilight='sunrise', ha_limit_neg=-4.9, ha_limit_pos=4.9, zenith_blind_spot=0): self.site = site self.start_date = start_date self.end_date = end_date self.horizon = Angle(degrees=horizon) self.twilight = twilight self.zenith_blind_spot = Angle(degrees=zenith_blind_spot) if ha_limit_pos > 12.0 or ha_limit_pos < 0.0: msg = "Positive hour angle limit must fall between 0 and 12 hours" raise InvalidHourAngleLimit(msg) if ha_limit_neg < -12.0 or ha_limit_neg > 0.0: msg = "Negative hour angle limit must fall between -12 and 0 hours" raise InvalidHourAngleLimit(msg) self.ha_limit_neg = ha_limit_neg self.ha_limit_pos = ha_limit_pos self.dark_intervals = [] self.moon_dark_intervals = []
def test_read_neocp_orbit2(self): # Element dictionary produced by TAL's code from NEOCP orbit file, migrated to # rise_set format. expected = { 'name' : 'K13TB7L', 'epoch' : 56600.0, 'long_node' : Angle(degrees=3.36296), 'eccentricity' : 0.6898696, 'semi_axis' : 3.6038493, 'mean_anomaly' : Angle(degrees=344.69702), 'arg_perihelion' : Angle(degrees=112.20127), 'inclination' : Angle(degrees=9.36592), 'MDM' : Angle(degrees=0.14406356), 'H' : 17.7, 'G' : 0.15, 'n_obs' : 146, 'n_oppos' : 1, 'n_nights' : 67, 'reference' : 'E2013-X58', 'uncertainty' : '3', } recieved = read_neocp_orbit('test/2013TL117.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def test_read_neocp_orbit1(self): # Element dictionary produced by TAL's code from NEOCP orbit file, migrated to # rise_set format. expected = { 'name': 'P109rXK', 'epoch': 56620.0, 'long_node': Angle(degrees=224.42273), 'eccentricity': 0.5131088, 'semi_axis': 2.0773493, 'mean_anomaly': Angle(degrees=322.22271), 'arg_perihelion': Angle(degrees=307.96804), 'inclination': Angle(degrees=13.72592), 'MDM': Angle(degrees=0.3291848), 'H': 17.5, 'G': 0.15, 'n_obs': 4, 'n_oppos' : 1, 'n_nights': 0, } recieved = read_neocp_orbit('test/P109rXK.neocp') for e in list(expected.keys()): assert_equal(expected[e], recieved[e])
def make_comet_target(target_type, epoch, epochofperih, inclination, long_node, arg_perihelion, perihdist, eccentricity): """ Creates a rise-set target for a MPC comet Creates a dictionary rise-set formatted target that must have all the necessary coordinates of a MPC formatted comet. Args: target_type (str): The string representation of this target type ('MPC_COMET') epoch (float): The epoch of the target ephemerites epochofperih (float): The epoch of perihelion inclination (float): The inclination of the target in degrees long_node (float): The longitude of the ascending node in degrees arg_perihelion (float): The argument of perihelion in degrees perihdist (float): The perihelion distance of the target eccentricity (float): The eccentricty of the target Returns: dict: A dictionary of target details to use as input to other rise-set functions """ target = { 'type': target_type, 'epoch': epoch, 'epochofperih': epochofperih, 'inclination': Angle(degrees=inclination), 'long_node': Angle(degrees=long_node), 'arg_perihelion': Angle(degrees=arg_perihelion), 'perihdist': perihdist, 'eccentricity': eccentricity, } return target
def test_calc_local_hour_angle_normalises_positive_overrun(self): ra_app = Angle(degrees=108.75) coj_longitude = Angle(degrees=149.070593) date = datetime(2014, 1, 12, 14) hour_angle = calc_local_hour_angle(ra_app, coj_longitude, date) assert_almost_equal(hour_angle.in_degrees(), 2.3088, places=4)
def test_calc_local_hour_angle_normalises_negative_overrun(self): ra_app = Angle(degrees=288.75) elp_longitude = Angle(degrees=-104.015194444) date = datetime(2014, 7, 18, 7) hour_angle = calc_local_hour_angle(ra_app, elp_longitude, date) assert_almost_equal(hour_angle.in_degrees(), 8.2509, places=4)
def get_rise_set_site(site_detail): return { 'latitude': Angle(degrees=site_detail['latitude']), 'longitude': Angle(degrees=site_detail['longitude']), 'horizon': Angle(degrees=site_detail['horizon']), 'ha_limit_neg': Angle(degrees=site_detail['ha_limit_neg'] * HOURS_PER_DEGREES), 'ha_limit_pos': Angle(degrees=site_detail['ha_limit_pos'] * HOURS_PER_DEGREES) }
def test_calc_local_hour_angle(self): ra_app = Angle(degrees=30) elp_longitude = Angle(degrees=-104.015194444) date = datetime(2013, 12, 10) hour_angle = calc_local_hour_angle(ra_app, elp_longitude, date) assert_almost_equal(hour_angle.in_degrees(), -55.128564469690645, places=13)
def telescope_to_rise_set_telescope(telescope): """Convert scheduler Telescope to rise_set telescope dict.""" # TODO: Move scheduler Telescope code to rise_set. HOURS_TO_DEGREES = 15 return { 'latitude': Angle(degrees=telescope['latitude']), 'longitude': Angle(degrees=telescope['longitude']), 'ha_limit_neg': Angle(degrees=telescope['ha_limit_neg'] * HOURS_TO_DEGREES), 'ha_limit_pos': Angle(degrees=telescope['ha_limit_pos'] * HOURS_TO_DEGREES), 'zenith_blind_spot': Angle(degrees=telescope['zenith_blind_spot'] * HOURS_TO_DEGREES) }
def calc_sunrise_set(site, date, twilight): '''Return a tuple (transit, rise, set) of timedelta objects, describing the time offset for each event from the start of the provided date. ''' sun_std_alt = { 'sunrise': Angle(degrees=-5 / 6), 'sunset': Angle(degrees=-5 / 6), 'civil': Angle(degrees=-6), 'nautical': Angle(degrees=-12), 'astronomical': Angle(degrees=-18) } return calc_planet_rise_set(site, date, sun_std_alt[twilight], 'sun')
def setup(self): self.lsc = { 'name': '1m0a.domb.lsc', 'latitude': Angle(degrees=-30.1673472222), 'longitude': Angle(degrees=-70.8046722222), } self.elp = { 'name': '1m0a.doma.elp', 'latitude': Angle(degrees=30.6801), 'longitude': Angle(degrees=-104.015194444), }
def apparent_to_altzd(ra, dec, aop_params): ''' Perform apparent->observed place transformation on a targets apparent ra and dec, given aop_params which are generated from slalibs sla_aoppa call. :param ra: apparent ra :param dec: apparent dec :param aop_params: slalibs aop params structure :return: azimuth and zenith angles ''' (obs_az, obs_zd, obs_ha, obs_dec, obs_ra) = sla.sla_aopqk(ra.in_radians(), dec.in_radians(), aop_params) return Angle(radians=obs_az), Angle(radians=obs_zd)
def add_site(self, site_dict): lat_long = (site_dict['latitude'], site_dict['longitude']) HOURS_TO_DEGREES = 15.0 if lat_long not in self.site_lat_longs: self.site_lat_longs.append(lat_long) site_dict['latitude'] = Angle(degrees=site_dict['latitude']) site_dict['longitude'] = Angle(degrees=site_dict['longitude']) site_dict['horizon'] = Angle(degrees=site_dict['horizon']) site_dict['ha_limit_neg'] = Angle(degrees=site_dict['ha_limit_neg'] * HOURS_TO_DEGREES) site_dict['ha_limit_pos'] = Angle(degrees=site_dict['ha_limit_pos'] * HOURS_TO_DEGREES) self.sites[site_dict['name']] = site_dict return
def __init__(self, time=None, degrees=None, radians=None, units='time'): angle_degrees = time angle_radians = radians angle_units = units if degrees != None: angle_degrees = degrees angle_units = 'arc' Angle.__init__(self, angle_degrees, angle_radians, angle_units) # Check input self.validate_ra()
def setup(self): self.site = { 'name': 'test', 'latitude': Angle(degrees=-30.0), 'longitude': Angle(degrees=0.0) } self.h_0 = MOON_REFRACTION # 5 arcsecond tolerance self.tolerance = 5.0 / 3600.0 # rise/set/transit time tolerance in seconds self.time_tolerance = 3.0 * 60.0
def test_calc_ephemerides(self): window = { 'start' : datetime(2013, 12, 10), 'end' : datetime(2013, 12, 11) } chunksize = timedelta(hours=6) expected = [ { 'ra_app' : Angle(degrees=285.22681), 'dec_app' : Angle(degrees=-18.16350), 'start' : window['start'], 'end' : window['start'] + (1 * chunksize), }, { 'ra_app' : Angle(degrees=285.35677), 'dec_app' : Angle(degrees=-18.15597), 'start' : window['start'] + (1 * chunksize), 'end' : window['start'] + (2 * chunksize), }, { 'ra_app' : Angle(degrees=285.48538), 'dec_app' : Angle(degrees=-18.14841), 'start' : window['start'] + (2 * chunksize), 'end' : window['start'] + (3 * chunksize), }, { 'ra_app' : Angle(degrees=285.61369), 'dec_app' : Angle(degrees=-18.14033), 'start' : window['start'] + (3 * chunksize), 'end' : window['start'] + (4 * chunksize), }, { 'ra_app' : Angle(degrees=285.74337), 'dec_app' : Angle(degrees=-18.13206), 'start' : window['start'] + (4 * chunksize), 'end' : window['start'] + (4 * chunksize), }, ] received = calc_ephemerides(window, self.elements, self.cpt, chunksize) for e, r in zip(expected, received): assert_almost_equal(e['ra_app'].in_degrees(), r['ra_app'].in_degrees(), places=3) assert_almost_equal(e['dec_app'].in_degrees(), r['dec_app'].in_degrees(), places=4) assert_equal(e['start'], r['start']) assert_equal(e['end'], r['end'])