def get_horizons_coord(body, time='now', id_type='majorbody', *, include_velocity=False): """ Queries JPL HORIZONS and returns a `~astropy.coordinates.SkyCoord` for the location of a solar-system body at a specified time. This location is the instantaneous or "true" location, and is not corrected for light travel time or observer motion. .. note:: This function requires the Astroquery package to be installed and requires an Internet connection. Parameters ---------- body : `str` The solar-system body for which to calculate positions. One can also use the search form linked below to find valid names or ID numbers. id_type : `str` If 'majorbody', search by name for planets, satellites, or other major bodies. If 'smallbody', search by name for asteroids or comets. If 'id', search by ID number. time : {parse_time_types}, `dict` Time to use in a parse_time-compatible format. Alternatively, this can be a dictionary defining a range of times and dates; the range dictionary has to be of the form {{'start': start_time, 'stop': stop_time, 'step':’n[y|d|m|s]’}}. ``start_time`` and ``stop_time`` must be in a parse_time-compatible format, and are interpreted as UTC time. ``step`` must be a string with either a number and interval length (e.g. for every 10 seconds, ``'10s'``), or a plain number for a number of evenly spaced intervals. For more information see the docstring of `astroquery.jplhorizons.HorizonsClass`. Keyword Arguments ----------------- include_velocity : `bool` If True, include the body's velocity in the output coordinate. Defaults to False. Returns ------- `~astropy.coordinates.SkyCoord` Location of the solar-system body Notes ----- Be aware that there can be discrepancies between the coordinates returned by JPL HORIZONS, the coordinates reported in mission data files, and the coordinates returned by `~sunpy.coordinates.get_body_heliographic_stonyhurst`. References ---------- * `JPL HORIZONS <https://ssd.jpl.nasa.gov/?horizons>`_ * `JPL HORIZONS form to search bodies <https://ssd.jpl.nasa.gov/horizons.cgi?s_target=1#top>`_ * `Astroquery <https://astroquery.readthedocs.io/en/latest/>`_ Examples -------- >>> from sunpy.coordinates.ephemeris import get_horizons_coord Query the location of Venus >>> get_horizons_coord('Venus barycenter', '2001-02-03 04:05:06') # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for Venus Barycenter (2) [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=2001-02-03T04:05:06.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, AU) (-33.93155836, -1.64998443, 0.71915147)> Query the location of the SDO spacecraft >>> get_horizons_coord('SDO', '2011-11-11 11:11:11') # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for Solar Dynamics Observatory (spac [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=2011-11-11T11:11:11.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, AU) (0.01019118, 3.29640728, 0.99011042)> Query the location of the SOHO spacecraft via its ID number (-21) >>> get_horizons_coord(-21, '2004-05-06 11:22:33', 'id') # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for SOHO (spacecraft) (-21) [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=2004-05-06T11:22:33.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, AU) (0.25234902, -3.55863633, 0.99923086)> Query the location and velocity of the asteroid Juno >>> get_horizons_coord('Juno', '1995-07-18 07:17', 'smallbody', include_velocity=True) # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for 3 Juno (A804 RA) [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=1995-07-18T07:17:00.000, rsun=695700.0 km): (lon, lat, radius) in (deg, deg, AU) (-25.16107532, 14.59098438, 3.17667664) (d_lon, d_lat, d_radius) in (arcsec / s, arcsec / s, km / s) (-0.03306548, 0.00052415, -2.66709222)> Query the location of Solar Orbiter at a set of 12 regularly sampled times >>> get_horizons_coord('Solar Orbiter', ... time={{'start': '2020-12-01', ... 'stop': '2020-12-02', ... 'step': '12'}}) # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for Solar Orbiter (spacecraft) (-144 [sunpy.coordinates.ephemeris] ... """ if isinstance(time, dict): if set(time.keys()) != set(['start', 'stop', 'step']): raise ValueError('time dictionary must have the keys ["start", "stop", "step"]') epochs = time jpl_fmt = '%Y-%m-%d %H:%M:%S' epochs['start'] = parse_time(epochs['start']).tdb.strftime(jpl_fmt) epochs['stop'] = parse_time(epochs['stop']).tdb.strftime(jpl_fmt) else: obstime = parse_time(time) array_time = np.reshape(obstime, (-1,)) # Convert to an array, even if scalar epochs = array_time.tdb.jd.tolist() # Time must be provided in JD TDB # Import here so that astroquery is not a module-level dependency from astroquery.jplhorizons import Horizons query = Horizons(id=body, id_type=id_type, location='500@10', # Heliocentric (mean ecliptic) epochs=epochs) try: result = query.vectors() except Exception as e: # Catch and re-raise all exceptions, and also provide query URL if generated if query.uri is not None: log.error(f"See the raw output from the JPL HORIZONS query at {query.uri}") raise e finally: query._session.close() log.info(f"Obtained JPL HORIZONS location for {result[0]['targetname']}") log.debug(f"See the raw output from the JPL HORIZONS query at {query.uri}") if isinstance(time, dict): obstime = parse_time(result['datetime_jd'], format='jd', scale='tdb') else: # JPL HORIZONS results are sorted by observation time, so this sorting needs to be undone. # Calling argsort() on an array returns the sequence of indices of the unsorted list to put the # list in order. Calling argsort() again on the output of argsort() reverses the mapping: # the output is the sequence of indices of the sorted list to put that list back in the # original unsorted order. unsorted_indices = obstime.argsort().argsort() result = result[unsorted_indices] vector = CartesianRepresentation(result['x'], result['y'], result['z']) if include_velocity: velocity = CartesianDifferential(result['vx'], result['vy'], result['vz']) vector = vector.with_differentials(velocity) coord = SkyCoord(vector, frame=HeliocentricEclipticIAU76, obstime=obstime) return coord.transform_to(HeliographicStonyhurst).reshape(obstime.shape)
def get_horizons_coord(body, time='now', id_type='majorbody', *, include_velocity=False): """ Queries JPL HORIZONS and returns a `~astropy.coordinates.SkyCoord` for the location of a solar-system body at a specified time. This location is the instantaneous or "true" location, and is not corrected for light travel time or observer motion. .. note:: This function requires the Astroquery package to be installed and requires an Internet connection. Parameters ---------- body : `str` The solar-system body for which to calculate positions. One can also use the search form linked below to find valid names or ID numbers. id_type : `str` If 'majorbody', search by name for planets, satellites, or other major bodies. If 'smallbody', search by name for asteroids or comets. If 'id', search by ID number. time : {parse_time_types} Time to use in a parse_time-compatible format Keyword Arguments ----------------- include_velocity : `bool` If True, include the body's velocity in the output coordinate. Defaults to False. Returns ------- `~astropy.coordinates.SkyCoord` Location of the solar-system body Notes ----- Be aware that there can be discrepancies between the coordinates returned by JPL HORIZONS, the coordinates reported in mission data files, and the coordinates returned by `~sunpy.coordinates.get_body_heliographic_stonyhurst`. References ---------- * `JPL HORIZONS <https://ssd.jpl.nasa.gov/?horizons>`_ * `JPL HORIZONS form to search bodies <https://ssd.jpl.nasa.gov/horizons.cgi?s_target=1#top>`_ * `Astroquery <https://astroquery.readthedocs.io/en/latest/>`_ Examples -------- >>> from sunpy.coordinates.ephemeris import get_horizons_coord Query the location of Venus >>> get_horizons_coord('Venus barycenter', '2001-02-03 04:05:06') # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for Venus Barycenter (2) [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=2001-02-03T04:05:06.000): (lon, lat, radius) in (deg, deg, AU) (-33.93155836, -1.64998443, 0.71915147)> Query the location of the SDO spacecraft >>> get_horizons_coord('SDO', '2011-11-11 11:11:11') # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for Solar Dynamics Observatory (spac [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=2011-11-11T11:11:11.000): (lon, lat, radius) in (deg, deg, AU) (0.01019118, 3.29640728, 0.99011042)> Query the location of the SOHO spacecraft via its ID number (-21) >>> get_horizons_coord(-21, '2004-05-06 11:22:33', 'id') # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for SOHO (spacecraft) (-21) [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=2004-05-06T11:22:33.000): (lon, lat, radius) in (deg, deg, AU) (0.25234902, -3.55863633, 0.99923086)> Query the location and velocity of the asteroid Juno >>> get_horizons_coord('Juno', '1995-07-18 07:17', 'smallbody', include_velocity=True) # doctest: +REMOTE_DATA INFO: Obtained JPL HORIZONS location for 3 Juno [sunpy.coordinates.ephemeris] <SkyCoord (HeliographicStonyhurst: obstime=1995-07-18T07:17:00.000): (lon, lat, radius) in (deg, deg, AU) (-25.16107572, 14.59098456, 3.17667662) (d_lon, d_lat, d_radius) in (arcsec / s, arcsec / s, km / s) (-0.00514936, -0.00205857, 8.89781348)> """ obstime = parse_time(time) array_time = np.reshape(obstime, (-1, )) # Convert to an array, even if scalar # Import here so that astroquery is not a module-level dependency from astroquery.jplhorizons import Horizons query = Horizons( id=body, id_type=id_type, location='500@10', # Heliocentric (mean ecliptic) epochs=array_time.tdb.jd.tolist()) # Time must be provided in JD TDB try: result = query.vectors() except Exception as e: # Catch and re-raise all exceptions, and also provide query URL if generated if query.uri is not None: log.error( f"See the raw output from the JPL HORIZONS query at {query.uri}" ) raise e finally: query._session.close() log.info(f"Obtained JPL HORIZONS location for {result[0]['targetname']}") log.debug(f"See the raw output from the JPL HORIZONS query at {query.uri}") # JPL HORIZONS results are sorted by observation time, so this sorting needs to be undone. # Calling argsort() on an array returns the sequence of indices of the unsorted list to put the # list in order. Calling argsort() again on the output of argsort() reverses the mapping: # the output is the sequence of indices of the sorted list to put that list back in the # original unsorted order. unsorted_indices = obstime.argsort().argsort() result = result[unsorted_indices] vector = CartesianRepresentation(result['x'], result['y'], result['z']) if include_velocity: velocity = CartesianDifferential(result['vx'], result['vy'], result['vz']) vector = vector.with_differentials(velocity) coord = SkyCoord(vector, frame=HeliocentricEclipticIAU76, obstime=obstime) return coord.transform_to(HeliographicStonyhurst).reshape(obstime.shape)