def __init__(self, naifid, name, orbiting): # planets are constructed with their NASA NAIF ID's (int), name (str) # and the body they are orbiting (planetary_system instance) self.name = name self.naifid = naifid # not all planets' states are available in spiceypy.spkgeo, so we # use their barrycenters' to get state vectors self.barrycenter_id = int(str(naifid)[0]) self.state, self.r_sun = spiceypy.spkgeo(targ=self.barrycenter_id, \ et=start_date_et, \ ref="ECLIPJ2000", obs=10) _, radii = spiceypy.bodvcd(naifid, "RADII", 3) self.radii = np.average(radii) * 1000 # km -> m conv. self.vis_area = 2 * self.radii**2 * np.pi self.size = int( np.ceil(self.vis_area / orbiting.scaling_factor * 50000)) self.state = self.state * 1000 # km -> m conv. self.parent = orbiting self.gm = orbiting.G * orbiting.sun_mass self.y = [] self.line = [] self.anim_data = [] self.scat = [] self.color = [] orbiting.add_planet(self)
def calc_ephemeris(target, ets, frame, observer): ''' Convenience wrapper for spkezr and spkgeo ''' if type(target) == str: return array(spice.spkezr(target, ets, frame, 'NONE', observer)[0]) else: n_states = len(ets) states = zeros((n_states, 6)) for n in range(n_states): states[n] = spice.spkgeo(target, ets[n], frame, observer)[0] return states
def vinfinity_match( planet0, planet1, v0_sc, et0, tof0, args = {} ): ''' Given an incoming v-infinity vector to planet0, calculate the outgoing v-infinity vector that will arrive at planet1 after time of flight (tof) where the incoming and outgoing v-infinity vectors at planet0 have equal magnitude ''' _args = { 'et0' : et0, 'planet1_ID': planet1, 'frame' : 'ECLIPJ2000', 'center_ID' : 0, 'mu' : pd.sun[ 'mu' ], 'tm' : 1, 'diff_step' : 1e-3, 'tol' : 1e-4 } for key in args.keys(): _args[ key ] = args[ key ] _args[ 'state0_planet0' ] = spice.spkgeo( planet0, et0, _args[ 'frame' ], _args[ 'center_ID' ] )[ 0 ] _args[ 'vinf' ] = nt.norm( v0_sc - _args[ 'state0_planet0' ][ 3: ] ) tof, steps = nt.newton_root_single_fd( calc_vinfinity, tof0, _args ) r1_planet1 = spice.spkgps( planet1, et0 + tof, _args[ 'frame' ], _args[ 'center_ID' ] )[ 0 ] v0_sc_depart, v1_sc_arrive = lt.lamberts_universal_variables( _args[ 'state0_planet0' ][ :3 ], r1_planet1, tof, { 'mu': _args[ 'mu' ], 'tm': _args[ 'tm' ] } ) return tof, v0_sc_depart, v1_sc_arrive
MAT = spiceypy.sxform(instring='ECLIPJ2000_DE405', \ tostring='ECLIPJ2000', \ et=DATETIME_ET) # Let's print the transformation matrix row-wise (spoiler alert: it is the # identity matrix) print('Transformation matrix between ECLIPJ2000_DE405 and ECLIPJ2000') for mat_row in MAT: print(f'{np.round(mat_row, 2)}') print('\n') #%% # Compute the state vector of Ceres in ECLIPJ2000 as seen from the Sun CERES_STATE_VECTOR, _ = spiceypy.spkgeo(targ=2000001, \ et=DATETIME_ET, \ ref='ECLIPJ2000', obs=10) #%% # Get the G*M value for the Sun _, GM_SUN_PRE = spiceypy.bodvcd(bodyid=10, item='GM', maxn=1) GM_SUN = GM_SUN_PRE[0] #%% # Compute the orbital elements of Ceres using the computed state vector CERES_ORBITAL_ELEMENTS = spiceypy.oscltx(state=CERES_STATE_VECTOR, \ et=DATETIME_ET, \ mu=GM_SUN)
# propagate until exit Earth SOI sc0 = SC({ 'orbit_state': state0, 'et0': et0, 'frame': FRAME, 'tspan': 100000, 'dt': 10000, 'stop_conditions': { 'exit_SOI': True } }) ''' Calculate spacecraft state w.r.t solar system barycenter at ephemeris time when spacecraft left Earth SOI ''' state_earth = spice.spkgeo(399, sc0.ets[-1], FRAME, 0)[0] state1 = sc0.states[-1, :6] + state_earth ''' Now model the spacecraft as a heliocentric elliptical orbit and propagate until enter Jupiter SOI ''' sc1 = SC({ 'orbit_state': state1, 'et0': sc0.ets[-1], 'frame': FRAME, 'tspan': 5 * 365 * 24 * 3600.0, 'dt': 30000, 'stop_conditions': { 'enter_SOI': pd.jupiter }, 'cb': pd.sun
kernels = os.path.expanduser('~/git_repos/SpaceScienceTutorial/_kernels') import spiceypy as sp sp.furnsh(kernels + '/lsk/naif0012.tls') sp.furnsh(kernels + '/spk/de432s.bsp') sp.furnsh(kernels + '/pck/gm_de431.tpc') DATE_TODAY = datetime.datetime.today() DATE_TODAY = DATE_TODAY.strftime('%Y-%m-%dT00:00:00') ET_TODAY_MIDNIGHT = sp.utc2et(DATE_TODAY) print('ET today midnight') # what is et exactly?? print(ET_TODAY_MIDNIGHT) EARTH_STATE_WRT_SUN, EARTH_SUN_LT = sp.spkgeo(targ=399 \ ,et=ET_TODAY_MIDNIGHT \ ,ref='ECLIPJ2000' \ ,obs=10 ) print("state vector of the earth wrt the sun for \"today\" (midnight): {0}". format(EARTH_STATE_WRT_SUN)) # check AU EARTH_SUN_DISTANCE = math.sqrt(EARTH_STATE_WRT_SUN[0] ** 2 \ + EARTH_STATE_WRT_SUN[1] ** 2 \ + EARTH_STATE_WRT_SUN[2] ** 2 ) EARTH_SUN_DISTANCE_AU = sp.convrt(EARTH_SUN_DISTANCE, 'km', 'AU') print('CURRENT distance between Earth and Sun in AU: {0}'.format(
print(ET_TODAY_MIDNIGHT) #%% # Can we compute now the position and velocity (so called state) of the Earth # with respect to the Sun? We use the following function to determine the # state vector and the so called light time (travel time of the light between # the Sun and our home planet). Positions are always given in km, velocities # in km/s and times in seconds # targ : Object that shall be pointed at # et : The ET of the computation # ref : The reference frame. Here, it is ECLIPJ2000 (so Medium article) # obs : The observer respectively the center of our state vector computation EARTH_STATE_WRT_SUN, EARTH_SUN_LT = spiceypy.spkgeo(targ=399, \ et=ET_TODAY_MIDNIGHT, \ ref='ECLIPJ2000', \ obs=10) #%% # An error occured. Again a kernel error. Well, we need to load a so called # spk to load positional information: spiceypy.furnsh('../_kernels/spk/de432s.bsp') #%% # Let's re-try the computation again EARTH_STATE_WRT_SUN, EARTH_SUN_LT = spiceypy.spkgeo(targ=399, \ et=ET_TODAY_MIDNIGHT, \ ref='ECLIPJ2000', obs=10)
_, GM_SUN_PRE = spiceypy.bodvcd(bodyid=10, item='GM', maxn=1) GM_SUN = GM_SUN_PRE[0] # Set the G*M value of Jupiter _, GM_JUPITER_PRE = spiceypy.bodvcd(bodyid=5, item='GM', maxn=1) GM_JUPITER = GM_JUPITER_PRE[0] #%% # Set a sample Ephemeris Time to compute a sample Jupiter state vector and the # corresponding orbital elements sample_et = spiceypy.utc2et('2000-001T12:00:00') # Compute the state vector of Jupiter as seen from the Sun in ECLIPJ2000 JUPITER_STATE, _ = spiceypy.spkgeo(targ=5, \ et=sample_et, \ ref='ECLIPJ2000', \ obs=10) # Determine the corresponding orbital elements of Jupiter JUPITER_ORB_ELEM = spiceypy.oscltx(state=JUPITER_STATE, \ et=sample_et, \ mu=GM_SUN) # Extract the semi-major axis of Jupiter ... JUPITER_A = JUPITER_ORB_ELEM[-2] # ... and print the results in AU print('Semi-major axis of Jupiter in AU: ' \ f'{spiceypy.convrt(JUPITER_A, inunit="km", outunit="AU")}') print('\n')
# Import the installed modules import numpy as np # Import matplotlib for plotting from matplotlib import pyplot as plt #%% # Create sample Ephemeris Time (ET) SAMPLE_ET = spiceypy.utc2et('2000-001T00:00:00') # Compute the state vector of Jupiter's barycentre at the defined ET as seen # from the Sun STATE_VEC_JUPITER, _ = spiceypy.spkgeo(targ=5, \ et=SAMPLE_ET, \ ref='ECLIPJ2000', \ obs=10) # Get the G*M value of the Sun _, GM_SUN = spiceypy.bodvcd(bodyid=10, item='GM', maxn=1) GM_SUN = GM_SUN[0] # Compute the orbital elements of Jupiter ... ORB_ELEM_JUPITER = spiceypy.oscltx(STATE_VEC_JUPITER, SAMPLE_ET, GM_SUN) # ... extract the semi-major axis and convert it from km to AU A_JUPITER_KM = ORB_ELEM_JUPITER[-2] A_JUPITER_AU = spiceypy.convrt(A_JUPITER_KM, 'km', 'AU') #%%
# AWP library import plotting_tools as pt if __name__ == '__main__': spice.furnsh('../../data/spice/lsk/naif0012.tls') spice.furnsh('../../data/spice/spk/de432s.bsp') spice.furnsh('voyager2_jupiter_flyby.bsp') et = spice.str2et('1979-07-09 TDB') dt = 10 * 24 * 3600.0 et0 = et - dt et1 = et + dt v_arrive = spice.spkgeo(-32, et0, 'ECLIPJ2000', 0)[0][3:] v_depart = spice.spkgeo(-32, et1, 'ECLIPJ2000', 0)[0][3:] v_jup0 = spice.spkgeo(5, et0, 'ECLIPJ2000', 0)[0][3:] v_jup1 = spice.spkgeo(5, et1, 'ECLIPJ2000', 0)[0][3:] vinf0 = v_arrive - v_jup0 vinf1 = v_depart - v_jup1 vdelta = v_depart - v_arrive v0 = {'r': v_arrive, 'label': r'$\vec{v}_{arrive}$', 'color': 'm'} v1 = {'r': v_depart, 'label': r'$\vec{v}_{depart}$', 'color': 'm'} v2 = {'r': v_jup0, 'label': r'$\vec{v}_{Jupiter}$', 'color': 'C3'} v3 = {'r': vinf0, 'label': r'$\vec{v}_{incoming}$', 'color': 'm'} v4 = {'r': vinf1, 'label': r'$\vec{v}_{outgoing}$', 'color': 'm'} v5 = {'r': vdelta, 'label': r'$\vec{\Delta v}$', 'color': 'lime'} pt.plot_orbits(
def test_Spacecraft_enter_SOI_voyager2_jupiter(plot=False): spice.furnsh(sd.leapseconds_kernel) spice.furnsh(sd.de432) frame = 'ECLIPJ2000' # beginning of coverage for Voyager 2 SPK kernel et0 = spice.str2et('1977 AUG 20 15:32:32.182') ''' Voyager 2 initial state w.r.t Earth at et0 ''' state0 = [ 7.44304239e+03, -5.12480267e+02, 2.37748995e+03, # km 5.99287925e+00, 1.19056063e+01, 5.17252832e+00 ] # km / s ''' Propagate Voyager 2 trajectory until it exits Earth's sphere of influence, where maximum altitude stop condition will be triggered ''' stop_conditions = {'max_alt': pd.earth['SOI'] - pd.earth['radius']} sc0 = SC({ 'orbit_state': state0, 'et0': et0, 'frame': frame, 'tspan': 100000, 'stop_conditions': stop_conditions }) assert sc0.cb == pd.earth assert sc0.ode_sol.success assert sc0.ode_sol.message == 'A termination event occurred.' assert sc0.ode_sol.status == 1 ''' Calculate spacecraft state w.r.t solar system barycenter at ephemeris time when spacecraft left Earth SOI ''' state_earth = spice.spkgeo(399, sc0.ets[-1], frame, 0)[0] state1 = sc0.states[-1, :6] + state_earth ''' Now model the spacecraft as a heliocentric elliptical orbit and propagate until enter Jupiter SOI ''' sc1 = SC({ 'orbit_state': state1, 'et0': sc0.ets[-1], 'frame': frame, 'tspan': 5 * 365 * 24 * 3600.0, 'dt': 30000, 'stop_conditions': { 'enter_SOI': pd.jupiter }, 'cb': pd.sun }) assert sc1.cb == pd.sun assert sc1.ode_sol.success assert sc1.ode_sol.message == 'A termination event occurred.' assert sc1.ode_sol.status == 1 if plot: ets = np.concatenate((sc0.ets, sc1.ets)) rs_earth = st.calc_ephemeris(3, ets, frame, 10)[:, :3] rs_jupiter = st.calc_ephemeris(5, ets, frame, 10)[:, :3] rs0 = sc0.states[ :, :3 ] +\ st.calc_ephemeris( 3, sc0.ets, frame, 10 )[ :, :3 ] labels = [ '$Voyager2_{EarthSOI}$', '$Voyager2_{SunSOI}$', 'Earth', 'Jupiter' ] pt.plot_orbits([rs0, sc1.states[:, :3], rs_earth, rs_jupiter], { 'labels': labels, 'colors': ['m', 'c', 'b', 'C3'], 'show': True })
def main(): # ** Convert today's time at midnight from UTC to ephemeric time ET ** #Get today's date date_today_utc = dt.datetime.today() #Convert datetime to string, replacing time with midnight date_today_utc = date_today_utc.strftime('%Y-%m-%dT00:00:00') #Get time conversion information from a lsk kernel 'naif0012.tls' spiceypy.furnsh( 'E:\Data Science Projects\Space Science\SpaceScience-P1-EarthStateVectors\data\external\_kernels\lsk/naif0012.tls' ) #Convert UTC to ET using SPICE function 'utc2et' et_today = spiceypy.utc2et(date_today_utc) # ** Compute the position and velocity of the Earth with respect to the Sun ** #First load a spk kernel 'de432s.bsp' for positional information of planets. # How to find the relevant kernel? # At https://naif.jpl.nasa.gov/pub/naif/ we navigate to generic_kernels/spk/ # planets, since we are trying to find the trajectory of our planet. We # browse the aa_summaries.txt file. The last line of each summary shows the # time range covered in each kernel. The time period of de432s is suitable. spiceypy.furnsh( 'E:\Data Science Projects\Space Science\SpaceScience-P1-EarthStateVectors\data\external/_kernels/spk/de432s.bsp' ) #Use spkgeo function to compute Earth state vectors earth_state_wrt_sun, earth_sun_lt = spiceypy.spkgeo(targ=399, \ et=et_today, \ ref='ECLIPJ2000', \ obs=10) #Report position vector of Earth print(f"Position of Earth wrt Sun on {date_today_utc} is: \n \ {earth_state_wrt_sun}", file=outfile) #Check whether computation is correct # # We check the veracity of the computed position vectors by using the result # to compute the distance between Earth and Sun. If it is around 1 AU, then # the computation is satisfactory. ("around" 1AU because Earth's orbit is # elliptical, ot perfectly circular) # #Compute distance between Earth and Sun in km earth_sun_distance = math.sqrt(earth_state_wrt_sun[0]**2.0 \ + earth_state_wrt_sun[1]**2.0 \ + earth_state_wrt_sun[2]**2.0) #Convert km to AU earth_sun_distance_AU = spiceypy.convrt(earth_sun_distance, 'km', 'AU') #Report value print(f"Current distance between the Earth and the Sun in AU: \ {earth_sun_distance_AU}", file=outfile) # ** Compute Orbital Speed of Earth ** #First we compute actual current orbital speed of Earth around Sun earth_orb_speed_wrt_sun = math.sqrt(earth_state_wrt_sun[3]**2.0 \ + earth_state_wrt_sun[4]**2.0 \ + earth_state_wrt_sun[5]**2.0) #Report current orbital speed of Earth print(f"Current orbital speed of the Earth around the Sun in km/s: \ {earth_orb_speed_wrt_sun}", file=outfile) #Now we compute theoretical orbital speed of Earth around Sun # # For this, we need the equation to determine the orbital speed. We assume # that the Sun's mass is greater than the mass of the Earth and we assume # that our planet is moving on an almost circular orbit. The orbit velocity # $v_{\text{orb}}$ can be approximated with, where $G$ is the gravitational # constant, $M$ is the mass of the Sun and $r$ is the distance between the # Earth and the Sun: # \begin{align} # v_{\text{orb}}\approx\sqrt{\frac{GM}{r}} # \end{align} # # The G*M values for different objects are found in a pck file 'gm_de431.tpc' # #Load pck kernel for G*M value spiceypy.furnsh( 'E:\Data Science Projects\Space Science\SpaceScience-P1-EarthStateVectors\data\external/_kernels/pck/gm_de431.tpc' ) _, GM_SUN = spiceypy.bodvcd(bodyid=10, item='GM', maxn=1) #Compute theoretical orbital speed v_orb_func = lambda gm, r: math.sqrt(gm / r) earth_orb_speed_wrt_sun_theory = v_orb_func(GM_SUN[0], earth_sun_distance) #Report theoretical value print(f"Theoretical orbital speed of the Earth around the Sun in km/s: \ {earth_orb_speed_wrt_sun_theory}", file=outfile)
#%% # Set an initial dataframe for the 67P computations comet_67p_df = pd.DataFrame([]) # Set the UTC date-times comet_67p_df.loc[:, 'UTC'] = datetime_range # Convert the UTC date-time strings to ET comet_67p_df.loc[:, 'ET'] = comet_67p_df['UTC'].apply(lambda x: \ spiceypy.utc2et(x.strftime('%Y-%m-%dT%H:%M:%S'))) # Compute the ET corresponding state vectors comet_67p_df.loc[:, 'STATE_VEC'] = \ comet_67p_df['ET'].apply(lambda x: spiceypy.spkgeo(targ=1000012, \ et=x, \ ref='ECLIPJ2000', \ obs=10)[0]) # Compute the state vectors corresponding orbital elements comet_67p_df.loc[:, 'STATE_VEC_ORB_ELEM'] = \ comet_67p_df.apply(lambda x: spiceypy.oscltx(state=x['STATE_VEC'], \ et=x['ET'], \ mu=GM_SUN), \ axis=1) #%% # Assign miscellaneous orbital elements as individual columns # Set the perihelion. Convert km to AU comet_67p_df.loc[:, 'PERIHELION_AU'] = \ comet_67p_df['STATE_VEC_ORB_ELEM'].apply(lambda x: \
atlas_et = spiceypy.datetime2et(atlas_time_step) # Compute the ET corresponding state vector of the comet ATLAS atlas_state_vec = spiceypy.conics(ATLAS_SPICE_ORB_EL, atlas_et) # Store the position vector atlas_vecs.append(atlas_state_vec[:3]) # Iterate through the time array (Solar Orbiter) for so_time_step in TIME_ARRAY: # Compute the ET so_et = spiceypy.datetime2et(so_time_step) # Compute the state vector of the Solar Orbiter (NAIF ID: -144) solar_orb_state_vec, _ = spiceypy.spkgeo(targ=-144, et=so_et, \ ref='ECLIPJ2000', obs=10) # Store the position vector solar_orb_vecs.append(solar_orb_state_vec[:3]) # Convert the lists that contain the vectors to numpy lists atlas_vecs = np.array(atlas_vecs) solar_orb_vecs = np.array(solar_orb_vecs) #%% # Minimum distance ATLAS - Sun MIN_DIST_ATLAS_SUN = np.min(np.linalg.norm(atlas_vecs, axis=1)) print('Minimum distance ATLAS - Sun in AU: ' \ f'{spiceypy.convrt(MIN_DIST_ATLAS_SUN, "km", "AU")}')
import numpy as np import matplotlib.pyplot as plt plt.style.use('dark_background') plt.rcParams.update({'font.size': 13}) # AWP library import orbit_calculations as oc import numerical_tools as nt import planetary_data as pd from EVME_1963 import calc_EVME_1963 if __name__ == '__main__': itvim = calc_EVME_1963() seq1 = itvim.seq[1] v_arrive = seq1['state_sc_arrive'][3:] state_venus = spice.spkgeo(2, seq1['et'], 'ECLIPJ2000', 0)[0] vinf = nt.norm(v_arrive - state_venus[3:]) span = 60 * 24 * 3600.0 dt = 1 * 24 * 3600.0 tofs = np.arange(seq1['tof'] - span, seq1['tof'] + span, dt) n_tofs = len(tofs) vinfs = np.zeros(n_tofs) args = { 'planet1_ID': 4, 'center_ID': 0, 'et0': seq1['et'], 'frame': 'ECLIPJ2000', 'state0_planet0': state_venus, 'mu': pd.sun['mu'], 'tm': 1, 'vinf': vinf
# In[11]: #We need to load it first spiceypy.furnsh('de432s.bsp') # In[12]: # Now compute the position and velocity # The first 3 values are the x, y, z components in km. The last 3 values are the corresponding velocity components in km/s. Earth_State_Sun, Earth_Sun_Light_Time = spiceypy.spkgeo(targ=399, et=ET, ref='ECLIPJ2000', obs=10) # In[13]: print(Earth_State_Sun) # In[14]: print(Earth_Sun_Light_Time) # In[15]:
Voyager 2 SPICE kernel is correct. Or you can change the path to fit your needs ''' # 3rd party libraries import spiceypy as spice import numpy as np # AWP library import spice_tools as st import plotting_tools as pt import spice_data as sd from numerical_tools import norm if __name__ == '__main__': spice.furnsh(sd.leapseconds_kernel) spice.furnsh('voyager2_jupiter_flyby.bsp') et = spice.str2et('1979-07-09 TDB') dt = 20 * 24 * 3600.0 ets = np.arange(et - dt, et + dt, 5000.0) states = st.calc_ephemeris(-32, ets, 'ECLIPJ2000', 10) state_jup = spice.spkgeo(5, et, 'ECLIPJ2000', 10)[0] hline = {'val': norm(state_jup[3:]), 'color': 'C3'} pt.plot_velocities(ets, states[:, 3:], { 'time_unit': 'days', 'hlines': [hline], 'show': True, })