def hcrs_to_hgs(hcrscoord, hgsframe): """ Convert from HCRS to Heliographic Stonyhurst (HGS). HGS shares the same origin (the Sun) as HCRS, but has its Z axis aligned with the Sun's rotation axis and its X axis aligned with the projection of the Sun-Earth vector onto the Sun's equatorial plane (i.e., the component of the Sun-Earth vector perpendicular to the Z axis). Thus, the transformation matrix is the product of the matrix to align the Z axis (by de-tilting the Sun's rotation axis) and the matrix to align the X axis. The first matrix is independent of time and is pre-computed, while the second matrix depends on the time-varying Sun-Earth vector. """ if hgsframe.obstime is None: raise ValueError("To perform this transformation the coordinate" " Frame needs an obstime Attribute") # Determine the Sun-Earth vector in ICRS # Since HCRS is ICRS with an origin shift, this is also the Sun-Earth vector in HCRS sun_pos_icrs = get_body_barycentric('sun', hgsframe.obstime) earth_pos_icrs = get_body_barycentric('earth', hgsframe.obstime) sun_earth = earth_pos_icrs - sun_pos_icrs # De-tilt the Sun-Earth vector to the frame with the Sun's rotation axis parallel to the Z axis sun_earth_detilt = sun_earth.transform(_SUN_DETILT_MATRIX) # Remove the component of the Sun-Earth vector that is parallel to the Sun's north pole hgs_x_axis_detilt = CartesianRepresentation(sun_earth_detilt.xyz * [1, 1, 0]) # The above vector, which is in the Sun's equatorial plane, is also the X axis of HGS x_axis = CartesianRepresentation(1, 0, 0) rot_matrix = _make_rotation_matrix_from_reprs(hgs_x_axis_detilt, x_axis) return matrix_product(rot_matrix, _SUN_DETILT_MATRIX)
def _sun_earth_icrf(time): """ Return the Sun-Earth vector for ICRF-based frames. """ sun_pos_icrs = get_body_barycentric('sun', time) earth_pos_icrs = get_body_barycentric('earth', time) return earth_pos_icrs - sun_pos_icrs
def get_body_heliographic_stonyhurst(body, time='now', observer=None): """ Return a `~sunpy.coordinates.frames.HeliographicStonyhurst` frame for the location of a solar-system body at a specified time. The location can be corrected for light travel time to an observer. Parameters ---------- body : `str` The solar-system body for which to calculate positions time : {parse_time_types} Time to use in a parse_time-compatible format observer : `~astropy.coordinates.SkyCoord` If None, the returned coordinate is the instantaneous or "true" location. If not None, the returned coordinate is the astrometric location (i.e., accounts for light travel time to the specified observer) Returns ------- out : `~sunpy.coordinates.frames.HeliographicStonyhurst` Location of the solar-system body in the `~sunpy.coordinates.HeliographicStonyhurst` frame Notes ----- There is no correction for aberration due to observer motion. For a body close to the Sun in angular direction relative to the observer, the correction can be negligible because the apparent location of the body will shift in tandem with the Sun. """ obstime = parse_time(time) if observer is None: body_icrs = get_body_barycentric(body, obstime) else: observer_icrs = SkyCoord(observer).icrs.cartesian # This implementation is modeled after Astropy's `_get_apparent_body_position` light_travel_time = 0. * u.s emitted_time = obstime delta_light_travel_time = 1. * u.s # placeholder value while np.any(np.fabs(delta_light_travel_time) > 1.0e-8 * u.s): body_icrs = get_body_barycentric(body, emitted_time) distance = (body_icrs - observer_icrs).norm() delta_light_travel_time = light_travel_time - distance / speed_of_light light_travel_time = distance / speed_of_light emitted_time = obstime - light_travel_time if light_travel_time.isscalar: ltt_string = f"{light_travel_time.to_value('s'):.2f}" else: ltt_string = f"{light_travel_time.to_value('s')}" log.info( f"Apparent body location accounts for {ltt_string} seconds of light travel time" ) body_hgs = ICRS(body_icrs).transform_to( HeliographicStonyhurst(obstime=obstime)) return body_hgs
def hcrs_to_hgs(hcrscoord, hgsframe): """ Convert from HCRS to Heliographic Stonyhurst (HGS). HGS shares the same origin (the Sun) as HCRS, but has its Z axis aligned with the Sun's rotation axis and its X axis aligned with the projection of the Sun-Earth vector onto the Sun's equatorial plane (i.e., the component of the Sun-Earth vector perpendicular to the Z axis). Thus, the transformation matrix is the product of the matrix to align the Z axis (by de-tilting the Sun's rotation axis) and the matrix to align the X axis. The first matrix is independent of time and is pre-computed, while the second matrix depends on the time-varying Sun-Earth vector. """ if hgsframe.obstime is None: raise ValueError("To perform this transformation the coordinate" " Frame needs an obstime Attribute") # Check whether differentials are involved on either end has_differentials = ( (hcrscoord._data is not None and hcrscoord.data.differentials) or (hgsframe._data is not None and hgsframe.data.differentials)) # Determine the Sun-Earth vector in ICRS # Since HCRS is ICRS with an origin shift, this is also the Sun-Earth vector in HCRS # If differentials exist, also obtain Sun and Earth velocities if has_differentials: sun_pos_icrs, sun_vel = get_body_barycentric_posvel( 'sun', hgsframe.obstime) earth_pos_icrs, earth_vel = get_body_barycentric_posvel( 'earth', hgsframe.obstime) else: sun_pos_icrs = get_body_barycentric('sun', hgsframe.obstime) earth_pos_icrs = get_body_barycentric('earth', hgsframe.obstime) sun_earth = earth_pos_icrs - sun_pos_icrs # De-tilt the Sun-Earth vector to the frame with the Sun's rotation axis parallel to the Z axis sun_earth_detilt = sun_earth.transform(_SUN_DETILT_MATRIX) # Rotate the Sun-Earth vector about the Z axis so that it lies in the XZ plane rot_matrix = _rotation_matrix_reprs_to_xz_about_z(sun_earth_detilt) total_matrix = rot_matrix @ _SUN_DETILT_MATRIX # All of the above is calculated for the HGS observation time # If the HCRS observation time is different, calculate the translation in origin if not _ignore_sun_motion and np.any( hcrscoord.obstime != hgsframe.obstime): sun_pos_old_icrs = get_body_barycentric('sun', hcrscoord.obstime) offset_icrf = sun_pos_icrs - sun_pos_old_icrs else: offset_icrf = sun_pos_icrs * 0 # preserves obstime shape # Add velocity if needed (at the HGS observation time) if has_differentials: vel_icrf = (sun_vel - earth_vel).represent_as(CartesianDifferential) offset_icrf = offset_icrf.with_differentials(vel_icrf) offset = offset_icrf.transform(total_matrix) return total_matrix, offset
def calculate_trip(depart, arrive, date): t = Time(date) solar_system_ephemeris.set('builtin') start = get_body_barycentric(depart, t) end = get_body_barycentric(arrive, t) distance = ((start.x - end.x)**2 + (start.y - end.y)**2 + (start.z - end.z)**2)**(1/2) return distance.to(u.km)
def get_body_heliographic_stonyhurst(body, time='now', observer=None): """ Return a `~sunpy.coordinates.frames.HeliographicStonyhurst` frame for the location of a solar-system body at a specified time. The location can be corrected for light travel time to an observer. Parameters ---------- body : `str` The solar-system body for which to calculate positions time : various Time to use as `~astropy.time.Time` or in a parse_time-compatible format observer : `~astropy.coordinates.SkyCoord` If None, the returned coordinate is the instantaneous or "true" location. If not None, the returned coordinate is the astrometric location (i.e., accounts for light travel time to the specified observer) Returns ------- out : `~sunpy.coordinates.frames.HeliographicStonyhurst` Location of the solar-system body in the `~sunpy.coordinates.HeliographicStonyhurst` frame Notes ----- There is no correction for aberration due to observer motion. For a body close to the Sun in angular direction relative to the observer, the correction can be negligible because the apparent location of the body will shift in tandem with the Sun. """ obstime = parse_time(time) if observer is None: body_icrs = get_body_barycentric(body, obstime) else: observer_icrs = SkyCoord(observer).icrs.cartesian # This implementation is modeled after Astropy's `_get_apparent_body_position` light_travel_time = 0.*u.s emitted_time = obstime delta_light_travel_time = 1.*u.s # placeholder value while np.any(np.fabs(delta_light_travel_time) > 1.0e-8*u.s): body_icrs = get_body_barycentric(body, emitted_time) distance = (body_icrs - observer_icrs).norm() delta_light_travel_time = light_travel_time - distance / speed_of_light light_travel_time = distance / speed_of_light emitted_time = obstime - light_travel_time log.info(f"Apparent body location accounts for {light_travel_time.to('s').value:.2f}" " seconds of light travel time") body_hgs = ICRS(body_icrs).transform_to(HGS(obstime=obstime)) return body_hgs
def computeItteration(t): print(t) earth = get_body_barycentric('earth', t) ex.append(earth.x / u.km) ey.append(earth.y / u.km) moon = get_body_barycentric('moon', t) mx.append(moon.x / u.km) my.append(moon.y / u.km) sun = get_body_barycentric('sun', t) sx.append(sun.x / u.km) sy.append(sun.y / u.km)
def speed(planet, t1): dt = 1 * u.s t2 = t1 + dt c1 = SkyCoord(get_body_barycentric(planet, t1)).cartesian c2 = SkyCoord(get_body_barycentric(planet, t2)).cartesian vx = (c2.x.to('m') - c1.x.to('m')) / dt vy = (c2.y.to('m') - c1.y.to('m')) / dt vz = (c2.z.to('m') - c1.z.to('m')) / dt speed = np.sqrt(vx**2 + vy**2 + vz**2) return speed
def icrs_to_cb_crs(icrs_coord, cb_crs_frame): """Conversion from ICRS to Celestial Reference System of a Central Body.""" if not u.m.is_equivalent(icrs_coord.cartesian.x.unit): raise u.UnitsError( _NEED_ORIGIN_HINT.format(icrs_coord.__class__.__name__)) if icrs_coord.data.differentials: # Calculate the barycentric position and velocity (of a solar system body). # Uses default ephemeris r_icrs, v_icrs = get_body_barycentric_posvel( cb_crs_frame.body_name, cb_crs_frame.obstime, ephemeris=cb_crs_frame.ephemeris_type, ) v_icrs = CartesianDifferential.from_cartesian(v_icrs) # Prepare final coord vector with velocity cb_crs_coord = (-r_icrs).with_differentials(-v_icrs) else: # Calculate the barycentric position ONLY (of a solar system body). # Uses default ephemeris. This is faster than the one above with velocities for # JPL ephemerides. cb_crs_coord = -get_body_barycentric( cb_crs_frame.body_name, cb_crs_frame.obstime, ephemeris=cb_crs_frame.ephemeris_type, ) # Return transformation matrix (None) and translation vector (with velocities) return None, cb_crs_coord
def test_coord_get(): # Test default (instance=None) obs = Helioprojective.observer assert obs is "earth" # Test get obstime = "2013-04-01" obs = Helioprojective(observer="earth", obstime=obstime).observer earth = get_earth(obstime) assert isinstance(obs, HeliographicStonyhurst) assert_quantity_allclose(obs.lon, earth.lon) assert_quantity_allclose(obs.lat, earth.lat) assert_quantity_allclose(obs.radius, earth.radius) # Test get obstime = "2013-04-01" obs = Helioprojective(obstime=obstime).observer earth = get_earth(obstime) assert isinstance(obs, HeliographicStonyhurst) assert_quantity_allclose(obs.lon, earth.lon) assert_quantity_allclose(obs.lat, earth.lat) assert_quantity_allclose(obs.radius, earth.radius) # Test get mars obstime = Time(parse_time("2013-04-01")) obs = Helioprojective(observer="mars", obstime=obstime).observer out_icrs = ICRS(get_body_barycentric("mars", obstime)) mars = out_icrs.transform_to(HeliographicStonyhurst(obstime=obstime)) assert isinstance(obs, HeliographicStonyhurst) assert_quantity_allclose(obs.lon, mars.lon) assert_quantity_allclose(obs.lat, mars.lat) assert_quantity_allclose(obs.radius, mars.radius)
def earth_ephemeris(t): """ Calculate the ephemeris for the earth in the BCRS using astropy tools. NOTE: There are several versions of the solar system ephemeris available in astropy and others can be provided through a URL (see the documentation of astropy.coordinates.solar_system_ephemeris). Depending on the accuracy needed, employing an ephemeris different from the default may be better. Parameters ---------- t : array Barycentric Julian Year times at which to calculate the ephemeris. Returns ------- Array of shape (3,t.size) representing the xyz components of the ephemeris at times t. Note: the units of angle should be radians and the units of time Julian years ('jyear'), while the distance unit is the AU. """ times = Time(t, format='jyear', scale='tcb') # 'tcb': Barycentric Coordinate Time (TCB) ephemeris = get_body_barycentric('earth', times) # unit A.U. return np.vstack((ephemeris.x.value, ephemeris.y.value, ephemeris.z.value))
def AstrometryFunc0(x, Delta1, Delta2, PMra, PMdec, pi): ras, decs, mjds = x years = (mjds - mjds[0]) * d2y bary0 = coords.get_body_barycentric('earth', Time(mjds, format='mjd')) if JPL: bary = bary0 / 1.496e8 else: bary = bary0 # Parallax factors Fac1 = (bary.x * np.sin(ras / d2a * np.pi / 180.) - bary.y * np.cos(ras / d2a * np.pi / 180.)) Fac2 = bary.x * np.cos(ras/d2a *np.pi/180.) * np.sin(decs/d2a *np.pi/180.) + \ bary.y * np.sin(ras/d2a *np.pi/180.) * np.sin(decs/d2a *np.pi/180.) - \ bary.z * np.cos(decs/d2a *np.pi/180.) RAsend = Delta1 + PMra * years + pi * Fac1.value DECsend = Delta2 + PMdec * years + pi * Fac2.value if RA == True and DEC == False: return RAsend elif RA == False and DEC == True: return DECsend else: return np.concatenate([RAsend, DECsend]).flatten()
def roemer_delay(epoch, ecl_latitude, ecl_longitude, ephemeris='de432s'): """ Computes the Roemer timing delay about the Solar System Barycentre at the position the Earth, given a set of ecliptic coordinates for a given pulsar. Parameters ---------- epoch : array_like, float observation timestamp(s) where delay is evaluated, in MJD format. ecl_longitudeatitude : float ecliptic latitude, in units of degrees. ecl_longitude = : float ecliptic longitude, in units of degrees. Returns ------- delay: array_like, float time delay due to orbital motion of the Earth, in seconds. """ # before computing anythin, set the ephemeris context to be value # supplied at the function call. solar_system_ephemeris.set(ephemeris) time = Time(epoch, format='mjd') # now comoute the required position vectors. r_earth = get_body_barycentric('earth', time) s_pulsar = pulsar_position_ecliptic(ecl_latitude, ecl_longitude) # finally, compute and return the delay. delay = r_earth.dot(s_pulsar).to(u.m) / c return delay
def astrometryfunc(x, Delta1, Delta2, PMra, PMdec, pi): """ Compute proper motion and parallax model for a set of ra/dec/mjd values.""" # x: input list of central RA and DEC positions and array of MJDs # Delta1: initial dRA position # Delta2: initial dDEC position # PMra: proper motion in RA (arcsec/yr) # PMdec: proper motion in DEC (arcsec/yr) # pi: parallax (arcsec) ra0, dec0, mjds = x n = len(mjds) years = (mjds - mjds[0]) * d2y ras = np.zeros(n, np.float64) + ra0 decs = np.zeros(n, np.float64) + dec0 bary = coords.get_body_barycentric('earth', Time(mjds, format='mjd')) # Parallax factors Fac1 = (bary.x * np.sin(ras * np.pi / 180.) - bary.y * np.cos(ras * np.pi / 180.)) Fac2 = bary.x * np.cos(ras*np.pi/180.) * np.sin(decs*np.pi/180.) + \ bary.y * np.sin(ras*np.pi/180.) * np.sin(decs*np.pi/180.) - \ bary.z * np.cos(decs*np.pi/180.) RAsend = Delta1 + PMra * years + pi * Fac1.value DECsend = Delta2 + PMdec * years + pi * Fac2.value return np.concatenate([RAsend, DECsend]).flatten()
def plot_solar_system(time, num_of_bodies=9, r_body=None): '''Plots the solar system in the x-y ecliptic frame, at a specified time. Inputs: time: astropy.Time object to get planetary positions num-of-planets: ''' plt.style.use('dark_background') for i in range(num_of_bodies): position_eq = get_body_barycentric(bodies[i], time).get_xyz().value [x, y, z] = eq_to_ecl(position_eq) plt.plot(x, y, color=body_colors[bodies[i]], marker='o') plt.annotate(bodies[i][0].upper(), (x + .05, y)) max_distance = (x**2 + y**2)**.5 if not (r_body is None): r_au = r_body.get_xyz().to(u.au).value [x, y, z] = eq_to_ecl(r_au) print(x, y, z) plt.plot(x, y, color='r', marker='x') max_distance = max(max_distance, (x**2 + y**2)) axis_distance = max_distance * 1.1 plt.axis((-axis_distance, axis_distance, -axis_distance, axis_distance)) plt.xlabel("x (AU)") plt.ylabel("y (AU)") plt.show()
def get_2d_positions(time): #time_now = Time(datetime.datetime.utcnow(), scale='utc') positions=[] body_ignore=['earth-moon-barycenter','moon','pluto'] for body in solar_system_ephemeris.bodies: if body in body_ignore: continue pos = get_body_barycentric(body, time).xyz.to(u.au) positions.append(pos.value) positions=np.array(positions) points = np.transpose(positions-centroid(positions)) normal = svd_plane_normal(points) a = normal b = np.array([1,0,plane((1,0),normal)]) c = b/np.linalg.norm(b) b = np.cross(a,b) coord_transform = np.vstack([c,b,a]) new_points = [] for row in np.transpose(points): new_points.append(np.dot(coord_transform,row+centroid(positions))) new_points = np.array(new_points) return new_points
def test_hcrs_hgs_array_obstime(): # Get the Earth location in HCRS at two times times = Time(['2017-01-01', '2017-06-01']) earth_hcrs = SkyCoord(get_body_barycentric('earth', times), frame='icrs', obstime=times).hcrs # Transform each time in separate calls (uses scalar obstime) earth_hgs_0 = earth_hcrs[0].transform_to(HeliographicStonyhurst) earth_hgs_1 = earth_hcrs[1].transform_to(HeliographicStonyhurst) # Transform both times in one call (uses array obstime) earth_hgs = earth_hcrs.transform_to(HeliographicStonyhurst) # Confirm that the two approaches produce the same results assert quantity_allclose(earth_hgs_0.lon, earth_hgs[0].lon, atol=1e-12 * u.deg) assert quantity_allclose(earth_hgs_0.lat, earth_hgs[0].lat, rtol=1e-10) assert quantity_allclose(earth_hgs_0.radius, earth_hgs[0].radius, rtol=1e-10) assert quantity_allclose(earth_hgs_1.lon, earth_hgs[1].lon, atol=1e-12 * u.deg) assert quantity_allclose(earth_hgs_1.lat, earth_hgs[1].lat, rtol=1e-10) assert quantity_allclose(earth_hgs_1.radius, earth_hgs[1].radius, rtol=1e-10)
def _get_delta_annual(self): """ calculates projected Earth positions required by annual parallax """ index = (self.parameters.t_0_par, self.coords.ra.value, self.coords.dec.value, tuple(self.times.tolist())) if index == Trajectory._get_delta_annual_last_index: return Trajectory._get_delta_annual_last if index in Trajectory._get_delta_annual_results: return Trajectory._get_delta_annual_results[index] time_ref = self.parameters.t_0_par velocity = utils.Utils.velocity_of_Earth(time_ref) / 1731.45683 # We change units from km/s to AU/d. if not np.all(np.isfinite(self.times)): msg = "Some times have incorrect values: {:}".format( self.times[~np.isfinite(self.times)]) raise ValueError(msg) position = get_body_barycentric(body='earth', time=Time(self.times, format='jd', scale='tdb')) position_ref = get_body_barycentric(body='earth', time=Time(time_ref, format='jd', scale='tdb')) # Seems that get_body_barycentric depends on time system, but there is # no way to set BJD part of BJD_TDB in astropy.Time(). The option # *format* above indicates if the first argument of Time() is # a float indicating JD or e.g., a string in the form # '1999-01-01T00:00:00.123' - this would be value 'fits'. # Hence, the user has to provide BJD times (or at least HJD). # Main calculation is in 2 lines below: delta_s = (position_ref.xyz.T - position.xyz.T).to(u.au).value delta_s += np.outer(self.times - time_ref, velocity) # and the results require projecting on the plane of the sky: out_n = np.dot(delta_s, self.coords.north_projected) out_e = np.dot(delta_s, self.coords.east_projected) out = {'N': out_n, 'E': out_e} Trajectory._get_delta_annual_results[index] = out Trajectory._get_delta_annual_last_index = index + tuple() Trajectory._get_delta_annual_last = out return out
def test_icrs_body_position_to_planetary_frame_yields_zeros(body, frame): with solar_system_ephemeris.set("builtin"): epoch = J2000 vector = get_body_barycentric(body.name, epoch) vector_result = ICRS(vector).transform_to(frame(obstime=epoch)).represent_as(CartesianRepresentation) assert_quantity_allclose(vector_result.xyz, [0, 0, 0] * u.km, atol=1e-7 * u.km)
def tick(): count = 0 while True: t = Time.now() + TimeDelta(86400 * count, format='sec') mercury = get_body_barycentric('Mercury', t) venus = get_body_barycentric('Venus', t) earth = get_body_barycentric('Earth', t) mars = get_body_barycentric('Mars', t) jupiter = get_body_barycentric('Jupiter', t) socketio.emit( 'tick', { 'mercury': { 'x': round(mercury.x.value / SCALE, 2), 'y': round(mercury.y.value / SCALE, 2), 'z': round(mercury.z.value / SCALE, 2), }, 'venus': { 'x': round(venus.x.value / SCALE, 2), 'y': round(venus.y.value / SCALE, 2), 'z': round(venus.z.value / SCALE, 2), }, 'earth': { 'x': round(earth.x.value / SCALE, 2), 'y': round(earth.y.value / SCALE, 2), 'z': round(earth.z.value / SCALE, 2), }, 'mars': { 'x': round(mars.x.value / SCALE, 2), 'y': round(mars.y.value / SCALE, 2), 'z': round(mars.z.value / SCALE, 2), }, 'jupiter': { 'x': round(jupiter.x.value / SCALE, 2), 'y': round(jupiter.y.value / SCALE, 2), 'z': round(jupiter.z.value / SCALE, 2), }, 't': str(t.to_datetime().replace(microsecond=0).isoformat()) + 'Z', }) socketio.sleep(0.01) count += 1
def distance(position, destination, date=None): """Calculate the distance between two planets. Args: position: Planet the journey starts at. destination: Destination of the journey. date: Date the journey is started (defaults to current time). Returns: Distance in m. """ if not date: date = dt.datetime.now() time = at.Time(date) return (ac.get_body_barycentric(position, time) - ac.get_body_barycentric(destination, time)).norm().to(au.m).value
def earth_distance(time='now'): """ Return the distance between the Sun and the Earth at a specified time. Parameters ---------- time : {parse_time_types} Time to use in a parse_time-compatible format Returns ------- out : `~astropy.coordinates.Distance` The Sun-Earth distance """ obstime = parse_time(time) vector = get_body_barycentric('earth', obstime) - get_body_barycentric('sun', obstime) return Distance(vector.norm())
def calculate_sun_earth_angles_rad(time_vector): """Compute the angle between the x axis and the Earth This function computes the angle on the plane of the Ecliptic (assuming to be the xy plane) between the Sun-Earth direction and the x axis. Depending on the type of the parameter `time_vector`, the result is computed differently: - If `time_vector` is a ``astropy.time.Time`` object, the angle is computed using the Barycentric Mean Ecliptic reference frame and the Ephemerides tables provided by AstroPy (slow but accurate) - Otherwise, `time_vector` is assumed to be a NumPy array of floats, and a simple circular motion with constant angular velocity is assumed. The angular velocity is ``YEARLY_OMEGA_SPIN_HZ``, which is equal to :math:`2π/T`, with T being the average duration of one year in seconds, and it is assumed that at time `t = 0` the angle is zero. """ # This is the geometry of the problem: the Ecliptic plane is # supposed to be on the xy plane, with the Sun in the origin, and # we're looking for the angle θ in the figure: # # ┌──────────────────────────────┐ # 1 │⠀⠀⠀⠀⠀⠀⠀⢀⡠⠤⠒⠊⠉⠉⠉⡏⠉⠉⠉⠒⠤⢄⡀⠀⠀⠀⠀⠀⠀⠀│ # │⠀⠀⠀⠀⠀⡠⠊⠁⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠈⠓⢄⠀⠀⠀⠀⠀│ # │⠀⠀⠀⡰⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⢆⠀⠀⠀│ # │⠀⢀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢲⡀⠀│ # │⢀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊⠁⢱⠀│ # │⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢀⡠⠒⠉⠀⠀⠀⠀⠀⢇│ # │⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⣀⠔⠊⠁⠀⠀θ⠀⠀⠀⠀⠀⢸│ # y [AU] │⡧⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⡷⠭⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⢼│ # │⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇Sun⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│ # │⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜│ # │⠈⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁│ # │⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠁⠀│ # │⠀⠀⠀⠱⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀│ # │⠀⠀⠀⠀⠀⠑⢄⡀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⢀⡤⠒⠁⠀⠀⠀⠀│ # -1 │⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⠤⣀⣀⣀⣀⣇⣀⣀⡠⠤⠔⠊⠁⠀⠀⠀⠀⠀⠀⠀│ # └──────────────────────────────┘ # -1 1 # x [AU] # # where of course θ points towards the Earth. The value of the # angle depends whether we're using proper MJD dates (in this # case, we need ephemeridis tables) or not: in the latter case we # just assume that at time 0 the angle θ is zero, and that the # Earth follows a uniform circular motion around the Sun with # frequency ω = 2πν = YEARLY_OMEGA_SPIN_HZ. if isinstance(time_vector, astropy.time.Time): pos = get_body_barycentric("earth", time_vector) coord = ICRS(pos).transform_to(BarycentricMeanEcliptic).cartesian return np.arctan2(coord.x.value, coord.y.value) else: return YEARLY_OMEGA_SPIN_HZ * time_vector
def test_planetary_icrs_frame_is_just_translation(body, frame): with solar_system_ephemeris.set("builtin"): epoch = J2000 vector = CartesianRepresentation(x=100 * u.km, y=100 * u.km, z=100 * u.km) vector_result = frame(vector, obstime=epoch).transform_to(ICRS).represent_as(CartesianRepresentation) expected_result = get_body_barycentric(body.name, epoch) + vector assert_quantity_allclose(vector_result.xyz, expected_result.xyz)
def get_barycentric(self, t, t0=None, tformat=None): if tformat is None: tformat = self.unitc.tname if tformat == 'jd' and t0 is None: t0 = 2451545 # jan 1 2000 in jd t_ = Time(t + t0, format=tformat) pos = get_body_barycentric(self.name, t_).xyz.to( self.unitc.dname).to_value() # cartesian representation return pos
def change_attractor(self, new_attractor, force=False): """Changes orbit attractor. Only changes from attractor to parent or the other way around are allowed. Parameters ---------- new_attractor : poliastro.bodies.Body Desired new attractor. force : bool If `True`, changes attractor even if physically has no-sense. Returns ------- ss: poliastro.twobody.orbit.Orbit Orbit with new attractor """ if self.attractor == new_attractor: return self elif self.attractor == new_attractor.parent: # "Sun -> Earth" r_soi = laplace_radius(new_attractor) barycentric_position = get_body_barycentric(new_attractor.name, self.epoch) # Transforming new_attractor's frame into frame of attractor new_attractor_r = ( ICRS(barycentric_position) .transform_to(self.get_frame()) .represent_as(CartesianRepresentation) .xyz ) distance = norm(self.r - new_attractor_r) elif self.attractor.parent == new_attractor: # "Earth -> Sun" r_soi = laplace_radius(self.attractor) distance = norm(self.r) else: raise ValueError("Cannot change to unrelated attractor") if distance > r_soi and not force: raise ValueError( "Orbit is out of new attractor's SOI. If required, use 'force=True'." ) elif self.ecc < 1.0 and not force: raise ValueError("Orbit will never leave the SOI of its current attractor") else: warn( "Leaving the SOI of the current attractor", PatchedConicsWarning, stacklevel=2, ) new_frame = get_frame(new_attractor, self.plane, obstime=self.epoch) coords = self.get_frame().realize_frame( self.represent_as(CartesianRepresentation, CartesianDifferential) ) ss = Orbit.from_coords(new_attractor, coords.transform_to(new_frame)) return ss
def _get_delta_annual(self): """ calculates projected Earth positions required by annual parallax """ index = (self.parameters.t_0_par, hash(self.coords), tuple(self.times.tolist())) if index == Trajectory._get_delta_annual_last_index: return Trajectory._get_delta_annual_last if index in Trajectory._get_delta_annual_results: return Trajectory._get_delta_annual_results[index] time_ref = self.parameters.t_0_par velocity = utils.Utils.velocity_of_Earth(time_ref) / 1731.45683 # We change units from km/s to AU/d. if not np.all(np.isfinite(self.times)): msg = "Some times have incorrect values: {:}".format( self.times[~np.isfinite(self.times)]) raise ValueError(msg) position = get_body_barycentric(body='earth', time=Time(self.times, format='jd', scale='tdb')) position_ref = get_body_barycentric(body='earth', time=Time(time_ref, format='jd', scale='tdb')) # Seems that get_body_barycentric depends on time system, but there is # no way to set BJD_TDB in astropy.Time(). # Likewise, get_jd12 depends on time system. # Main calculation is in 2 lines below: delta_s = (position_ref.xyz.T - position.xyz.T).to(u.au).value delta_s += np.outer(self.times - time_ref, velocity) # and the results require projecting on the plane of the sky: out_n = np.dot(delta_s, self.coords.north_projected) out_e = np.dot(delta_s, self.coords.east_projected) out = {'N': out_n, 'E': out_e} Trajectory._get_delta_annual_results[index] = out Trajectory._get_delta_annual_last_index = index + tuple() Trajectory._get_delta_annual_last = out return out
def get_body_heliographic_stonyhurst(body, time='now', observer=None): """ Return a `~sunpy.coordinates.frames.HeliographicStonyhurst` frame for the location of a solar-system body at a specified time. Parameters ---------- body : `str` The solar-system body for which to calculate positions time : various Time to use as `~astropy.time.Time` or in a parse_time-compatible format observer : `~astropy.coordinates.SkyCoord` If not None, the returned coordinate is the apparent location (i.e., accounts for light travel time) Returns ------- out : `~sunpy.coordinates.frames.HeliographicStonyhurst` Location of the solar-system body in the `~sunpy.coordinates.HeliographicStonyhurst` frame """ obstime = parse_time(time) if observer is None: body_icrs = get_body_barycentric(body, obstime) else: observer_icrs = SkyCoord(observer).icrs.cartesian # This implementation is modeled after Astropy's `_get_apparent_body_position` light_travel_time = 0.*u.s emitted_time = obstime delta_light_travel_time = 1.*u.s # placeholder value while np.any(np.fabs(delta_light_travel_time) > 1.0e-8*u.s): body_icrs = get_body_barycentric(body, emitted_time) distance = (body_icrs - observer_icrs).norm() delta_light_travel_time = light_travel_time - distance / speed_of_light light_travel_time = distance / speed_of_light emitted_time = obstime - light_travel_time log.info(f"Apparent body location accounts for {light_travel_time.to('s').value:.2f}" " seconds of light travel time") body_hgs = ICRS(body_icrs).transform_to(HGS(obstime=obstime)) return body_hgs
def get_R(mjd): # R is the position vector of Earth # requires an mjd (or array of) # returns R, a three component array describing the position vector of Earth # in au R = get_body_barycentric('earth', Time(mjd, format='mjd', scale='tcb')) return np.array( [R.x.to(u.AU) / u.AU, R.y.to(u.AU) / u.AU, R.z.to(u.AU) / u.AU]).T
def test_icrs_body_position_to_planetary_frame_yields_zeros(body, frame): with solar_system_ephemeris.set("builtin"): epoch = J2000 vector = get_body_barycentric(body.name, epoch) vector_result = (ICRS(vector).transform_to( frame(obstime=epoch)).represent_as(CartesianRepresentation)) assert_quantity_allclose(vector_result.xyz, [0, 0, 0] * u.km, atol=1e-7 * u.km)
def _ecliptic_to_icrs(from_coo, to_frame): # first un-precess from ecliptic to ICRS orientation rmat = _ecliptic_rotation_matrix() intermed_repr = from_coo.cartesian.transform(matrix_transpose(rmat)) # now offset back to barycentric, which is the correct center for ICRS # get barycentric sun coordinate bary_sun_pos = get_body_barycentric("sun", from_coo.obstime) newrepr = intermed_repr + bary_sun_pos return to_frame.realize_frame(newrepr)
def _icrs_to_ecliptic(from_coo, to_frame): # get barycentric sun coordinate bary_sun_pos = get_body_barycentric("sun", to_frame.obstime) # offset to heliocentric heliocart = from_coo.cartesian - bary_sun_pos # now compute the matrix to precess to the right orientation rmat = _ecliptic_rotation_matrix() newrepr = heliocart.transform(rmat) return to_frame.realize_frame(newrepr)
def plot_moon(self, t, satellite): clock = satellite.clock time_utc = clock.met_to_utc(t) earth_r = get_body_barycentric('earth', time_utc) moon_r = get_body_barycentric('moon', time_utc) r_e_m = moon_r - earth_r e = satellite.get_pos(t) r = -e * satellite.pos_unit - np.array( [r_e_m.x.value, r_e_m.y.value, r_e_m.z.value]) * u.km moon_point_d = cartesian_to_spherical(-r[0], -r[1], -r[2]) self.plot(moon_point_d[2].deg, moon_point_d[1].deg, 'o', color='#72777b', markersize=1.5 * self.size) self.text(moon_point_d[2].deg, moon_point_d[1].deg, 'moon', size=self.size, va='center', ha='center')
def objPosVel2SSB(objname, t, ephem): """This function computes a solar system object position and velocity respect to solar system barycenter using astropy coordinates get_body_barycentric() method. Parameters ---------- objname: str Solar system object name. Current support solar system bodies are listed in astropy.coordinates.olar_system_ephemeris.bodies attribution. t: Astropy.time.Time object Observation time in Astropy.time.Time object format. ephem: str The ephem to for computing solar system object position and velocity """ ephem = ephem.lower() objname = objname.lower() # Use astropy to compute postion. try: pos = coor.get_body_barycentric(objname, t, ephemeris=ephem) except ValueError: pos = coor.get_body_barycentric(objname, t, ephemeris= kernel_link_base + "%s.bsp" % ephem) # Use jplephem to compute velocity. # TODO: Astropy 1.3 will have velocity calculation availble. kernel = SPK.open(datapath("%s.bsp" % ephem)) # Compute vel from planet barycenter to solar system barycenter lcod = len(str(jpl_obj_code[objname])) tjd1 = t.tdb.jd1 tjd2 = t.tdb.jd2 # Planets with barycenter computing if lcod == 3: _, vel_pbary_ssb = kernel[0,jpl_obj_code[objname]/100].compute_and_differentiate(tjd1, tjd2) _, vel_p_pbary = kernel[jpl_obj_code[objname]/100, jpl_obj_code[objname]].compute_and_differentiate(tjd1, tjd2) vel = vel_pbary_ssb + vel_p_pbary # Planets without barycenter computing else: _, vel = kernel[0,jpl_obj_code[objname]].compute_and_differentiate(tjd1, tjd2) return PosVel(pos.xyz, vel / SECS_PER_DAY * u.km/u.second, origin='ssb', obj=objname)
def get_jwst_position2(times, jwstpos, use_jpl_ephemeris=False): ''' This returns the pair of relative positions from the barycenter and heliocenter to JWST in that order as a tuple of two arrays, each of shape (len(times), 3). ''' t = Time(times, format="mjd", scale="tt") if use_jpl_ephemeris: from astropy.coordinates import solar_system_ephemeris solar_system_ephemeris.set('jpl') # Vectors from the solar-system barycenter to the center of the Earth. bary_earth = acoord.get_body_barycentric("earth", t) # Vectors from the solar-system barycenter to the center of the Sun. bary_sun = acoord.get_body_barycentric("sun", t) # Vectors from the center of the Sun to the center of the Earth. sun_earth = bary_earth - bary_sun # Convert to ordinary numpy arrays of 3-element vectors, in km. barysun_centerearth_pos = np.empty((len(t), 3), dtype=np.float64) barysun_centerearth_pos[:, 0] = bary_earth.x.si.value / 1000. barysun_centerearth_pos[:, 1] = bary_earth.y.si.value / 1000. barysun_centerearth_pos[:, 2] = bary_earth.z.si.value / 1000. centersun_centerearth_pos = np.empty((len(t), 3), dtype=np.float64) centersun_centerearth_pos[:, 0] = sun_earth.x.si.value / 1000. centersun_centerearth_pos[:, 1] = sun_earth.y.si.value / 1000. centersun_centerearth_pos[:, 2] = sun_earth.z.si.value / 1000. centerearth_jwst = jwstpos return (barysun_centerearth_pos + centerearth_jwst), \ (centersun_centerearth_pos + centerearth_jwst)
def test_hcrs_hgs(): # Get the current Earth location in HCRS now = Time(parse_time('now')) earth_hcrs = SkyCoord(get_body_barycentric('earth', now), frame='icrs', obstime=now).hcrs # Convert from HCRS to HGS earth_hgs = earth_hcrs.transform_to(HeliographicStonyhurst) # The HGS longitude of the Earth should be zero within numerical error assert quantity_allclose(earth_hgs.lon, 0*u.deg, atol=1e-12*u.deg) # The HGS latitude and radius should be within valid ranges assert quantity_allclose(earth_hgs.lat, 0*u.deg, atol=7.3*u.deg) assert quantity_allclose(earth_hgs.radius, 1*u.AU, atol=0.017*u.AU)
def test_hcrs_hgs(): # Get the current Earth location in HCRS adate = parse_time('2015/05/01 01:13:00') earth_hcrs = SkyCoord(get_body_barycentric('earth', adate), frame='icrs', obstime=adate).hcrs # Convert from HCRS to HGS earth_hgs = earth_hcrs.transform_to(HeliographicStonyhurst) # The HGS longitude of the Earth should be zero within numerical error # Due to an issue with wrapping at +-360, we shift it to pass the test. assert quantity_allclose((earth_hgs.lon+1*u.deg) % (360*u.deg), 1*u.deg, atol=1e-12*u.deg) # The HGS latitude and radius should be within valid ranges assert quantity_allclose(earth_hgs.lat, 0*u.deg, atol=7.3*u.deg) assert quantity_allclose(earth_hgs.radius, 1*u.AU, atol=0.017*u.AU)
def test_hcrs_hgs_array_obstime(): # Get the Earth location in HCRS at two times times = Time(['2017-01-01', '2017-06-01']) earth_hcrs = SkyCoord(get_body_barycentric('earth', times), frame='icrs', obstime=times).hcrs # Transform each time in separate calls (uses scalar obstime) earth_hgs_0 = earth_hcrs[0].transform_to(HeliographicStonyhurst) earth_hgs_1 = earth_hcrs[1].transform_to(HeliographicStonyhurst) # Transform both times in one call (uses array obstime) earth_hgs = earth_hcrs.transform_to(HeliographicStonyhurst) # Confirm that the two approaches produce the same results assert quantity_allclose(earth_hgs_0.lon, earth_hgs[0].lon, atol=1e-12*u.deg) assert quantity_allclose(earth_hgs_0.lat, earth_hgs[0].lat, rtol=1e-10) assert quantity_allclose(earth_hgs_0.radius, earth_hgs[0].radius, rtol=1e-10) assert quantity_allclose(earth_hgs_1.lon, earth_hgs[1].lon, atol=1e-12*u.deg) assert quantity_allclose(earth_hgs_1.lat, earth_hgs[1].lat, rtol=1e-10) assert quantity_allclose(earth_hgs_1.radius, earth_hgs[1].radius, rtol=1e-10)
def build_ephem_interpolant(body, period, t_span, rtol=1e-5): h = (period * rtol).to(u.day).value t_span = ((t_span[0].to(u.day).value, t_span[1].to(u.day).value + 0.01)) t_values = np.linspace(*t_span, int((t_span[1] - t_span[0]) / h)) r_values = np.zeros((t_values.shape[0], 3)) for i, t in enumerate(t_values): epoch = Time(t, format='jd', scale='tdb') r = get_body_barycentric(body.name, epoch) r = (ICRS(x=r.x, y=r.y, z=r.z, representation_type=CartesianRepresentation) .transform_to(GCRS(obstime=epoch)) .represent_as(CartesianRepresentation) ) r_values[i] = r.xyz.to(u.km) t_values = ((t_values - t_span[0]) * u.day).to(u.s).value return interp1d(t_values, r_values, kind='cubic', axis=0, assume_sorted=True)
def to_icrs(planet_coo, _): # this is just an origin translation so without a distance it cannot go ahead if isinstance(planet_coo.data, UnitSphericalRepresentation): raise u.UnitsError(_NEED_ORIGIN_HINT.format(planet_coo.__class__.__name__)) if planet_coo.data.differentials: bary_sun_pos, bary_sun_vel = get_body_barycentric_posvel( planet_coo.body.name, planet_coo.obstime ) bary_sun_pos = bary_sun_pos.with_differentials( bary_sun_vel.represent_as(CartesianDifferential) ) else: bary_sun_pos = get_body_barycentric( planet_coo.body.name, planet_coo.obstime ) bary_sun_vel = None return None, bary_sun_pos
def from_icrs(icrs_coo, planet_frame): # this is just an origin translation so without a distance it cannot go ahead if isinstance(icrs_coo.data, UnitSphericalRepresentation): raise u.UnitsError(_NEED_ORIGIN_HINT.format(icrs_coo.__class__.__name__)) if icrs_coo.data.differentials: bary_sun_pos, bary_sun_vel = get_body_barycentric_posvel( planet_frame.body.name, planet_frame.obstime ) # Beware! Negation operation is not supported for differentials bary_sun_pos = (-bary_sun_pos).with_differentials( -bary_sun_vel.represent_as(CartesianDifferential) ) else: bary_sun_pos = -get_body_barycentric( planet_frame.body.name, planet_frame.obstime ) bary_sun_vel = None return None, bary_sun_pos
def build_ephem_interpolant(body, period, t_span, rtol=1e-5): """Interpolates ephemerides data Parameters ---------- body : Body Source body. period : ~astropy.units.Quantity Orbital period. t_span : list(~astropy.units.Quantity) Initial and final epochs. rtol : float, optional Relative tolerance. Controls the number of sampled data points, defaults to 1e-5. Returns ------- intrp : ~scipy.interpolate.interpolate.interp1d Interpolated function. """ h = (period * rtol).to(u.day).value t_span = (t_span[0].to(u.day).value, t_span[1].to(u.day).value + 0.01) t_values = np.linspace(*t_span, int((t_span[1] - t_span[0]) / h)) r_values = np.zeros((t_values.shape[0], 3)) for i, t in enumerate(t_values): epoch = Time(t, format="jd", scale="tdb") r = get_body_barycentric(body.name, epoch) r = ( ICRS(x=r.x, y=r.y, z=r.z, representation_type=CartesianRepresentation) .transform_to(GCRS(obstime=epoch)) .represent_as(CartesianRepresentation) ) r_values[i] = r.xyz.to(u.km) t_values = ((t_values - t_span[0]) * u.day).to(u.s).value return interp1d(t_values, r_values, kind="cubic", axis=0, assume_sorted=True)
def get_body_heliographic_stonyhurst(body, time='now'): """ Return a `~sunpy.coordinates.frames.HeliographicStonyhurst` frame for the location of a solar-system body at a specified time. Parameters ---------- body : `str` The solar-system body for which to calculate positions time : various Time to use as `~astropy.time.Time` or in a parse_time-compatible format Returns ------- out : `~sunpy.coordinates.frames.HeliographicStonyhurst` Location of the solar-system body in the `~sunpy.coordinates.HeliographicStonyhurst` frame """ obstime = parse_time(time) body_icrs = ICRS(get_body_barycentric(body, obstime)) body_hgs = body_icrs.transform_to(HGS(obstime=obstime)) return body_hgs