def test_read_write_bsp( plot = False ): ''' Propagate a 30 degree inclination orbit (20 periods), write BSP kernel, then read back the BSP kernel, ensuring that latitudes are within +-30 degrees before and after writing kernel ''' spice.furnsh( sd.pck00010 ) sc = SC( { 'coes' : [ 8000.0, 0.01, 30.0, 0, 0, 0 ], 'tspan': '20' } ) ''' Ensure latitudes are +-30 degrees ''' sc.calc_latlons() assert np.all( sc.latlons[ :, 2 ] <= 30.0 ) assert np.all( sc.latlons[ :, 2 ] >= -30.0 ) ''' spice.spkopn will error if a bsp with the requested filename already exists ''' filename = 'test_read_write_bsp.bsp' if os.path.isfile( filename ): os.remove( filename ) ''' Write bsp with all the default arguments except filename ''' st.write_bsp( sc.ets, sc.states[ :, :6 ], { 'bsp_fn': filename } ) ''' Now read back the bsp and ensure that latitudes are still within +-30 degrees ''' spice.furnsh( filename ) states = st.calc_ephemeris( -999, sc.ets, 'IAU_EARTH', 399 ) latlons = nt.cart2lat( states[ :, :3 ] ) assert np.all( latlons[ :, 2 ] <= 30.0 ) assert np.all( latlons[ :, 2 ] >= -30.0 ) os.remove( filename )
def test_Spacecraft_minimum_altitude_stop_condition(plot=False): sc = SC({ 'coes': [pd.earth['radius'] + 1000.0, 0.5, 0.0, 90.0, 0.0, 0.0], 'tspan': '1', 'dt': 100.0, 'rtol': 1e-9, 'stop_conditions': { 'min_alt': 100.0 } # km }) assert sc.cb == pd.earth ''' Since inclination is 0, all z-axis components of Spacecraft position and velocity should be 0 ''' assert np.all(sc.states[:, 2] == 0.0) assert np.all(sc.states[:, 5] == 0.0) ''' The orbital elements were set such that minimum altitude crossing would occur, triggering the min_alt stop condition ''' assert sc.ode_sol.success assert sc.ode_sol.message == 'A termination event occurred.' assert sc.ode_sol.status == 1 assert pytest.approx( np.linalg.norm( sc.ode_sol.y_events[ 0 ][ :3 ] ) -\ sc.cb[ 'radius' ] - 100.0, abs = 6.0e-3 ) == 0.0 if plot: sc.plot_altitudes({ 'hlines': [{ 'val': 100.0, 'color': 'c' }], 'time_unit': 'hours', 'title': 'test_Spacecraft_minimum_altitude_stop_condition', 'show': True })
def test_Spacecraft_basic_propagation(plot=False): sc = SC({ 'coes': [pd.earth['radius'] + 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0], 'tspan': '1', 'dt': 100.0, 'rtol': 1e-8 }) assert sc.cb == pd.earth ''' Since inclination is 0, all z-axis components of Spacecraft position and velocity should be 0 ''' assert np.all(sc.states[:, 2] == 0.0) assert np.all(sc.states[:, 5] == 0.0) ''' Since there are no orbital perturbations, all COEs except true anomaly should be close to constant (not exactly constant due to numerical error). However, since this is a circular orbit, periapsis is loosely defined, causing errors in true anomaly (not exactly linear), argument of periapsis (bounces back and forth between 0 and 359.9 degrees), eccentricity (random noise), and semi-major axis (drift / random noise) ''' sc.calc_coes() assert sc.coes_calculated assert pytest.approx(sc.coes[:, 0], abs=1e-3) == pd.earth['radius'] + 1000.0 # sma assert pytest.approx(sc.coes[:, 1], abs=1e-6) == 0.0 # ecc assert np.all(sc.coes[:, 2] == 0.0) # inc assert np.all(sc.coes[:, 5] == 0.0) # raan sc.calc_apoapses_periapses() apse_diffs = sc.apoapses - sc.periapses assert pytest.approx(apse_diffs, abs=1e-3) == 0.0 if plot: sc.plot_coes()
def test_Spacecraft_inclination_latitude(plot=False): spice.furnsh(sd.pck00010) sc = SC({ 'coes': [pd.earth['radius'] + 1000.0, 0.01, 50.0, 0.0, 0.0, 0.0], 'tspan': '3', 'dt': 10.0, 'rtol': 1e-8 }) assert sc.cb == pd.earth ''' Given that this spacecraft has 50 degrees inclination, the latitude coordinates should remain in between -50 and 50 degrees, since inclination is defined as the angle between the orbital plane and Earth's equatorial plane ''' sc.calc_latlons() assert np.all( sc.latlons[ :, 2 ] <= 50.0 ) and\ np.all( sc.latlons[ :, 2 ] >= -50.0 ) if plot: sc.plot_groundtracks()
def test_Spacecraft_maximum_altitude_stop_condition(plot=False): stop_conditions = {'max_alt': pd.earth['SOI'] - pd.earth['radius']} sc = SC({ 'coes': [-(pd.earth['radius'] + 1000.0), 1.8, 0.0, 10.0, 0.0, 0.0], 'tspan': 5 * 24 * 3600.0, 'rtol': 1e-9, 'stop_conditions': stop_conditions }) assert sc.cb == pd.earth ''' Since inclination is 0, all z-axis components of Spacecraft position and velocity should be 0 ''' assert np.all(sc.states[:, 2] == 0.0) assert np.all(sc.states[:, 5] == 0.0) ''' The orbital elements were set such that maximum altitude crossing would occur, triggering the max_alt stop condition ''' assert sc.ode_sol.success assert sc.ode_sol.message == 'A termination event occurred.' assert sc.ode_sol.status == 1 assert pytest.approx( np.linalg.norm( sc.ode_sol.y_events[ 1 ][ :3 ] ) -\ sc.cb[ 'radius' ], abs = 1e-3 ) ==\ sc.cb[ 'SOI' ] - sc.cb[ 'radius' ] if plot: hline = {'val': pd.earth['SOI'] - pd.earth['radius'], 'color': 'c'} sc.plot_altitudes({ 'hlines': [hline], 'time_unit': 'hours', 'title': 'test_Spacecraft_maximum_altitude_stop_condition', 'show': True })
''' state0 = [ 7.44304239e+03, -5.12480267e+02, 2.37748995e+03, # km 5.99287925e+00, 1.19056063e+01, 5.17252832e+00 ] # km / s # 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({
from Spacecraft import Spacecraft as SC import orbit_calculations as oc import plotting_tools as pt import planetary_data as pd if __name__ == '__main__': periapsis = pd.earth[ 'radius' ] + 4000.0 # km coes_circular = [ periapsis, 0, 0, 0, 0, 0 ] coes_elliptical = [ periapsis / 0.3, 0.7, 0, 0, 0, 0 ] state_parabolic = spice.conics( [ periapsis, 1.0, 0, 0, 0, -10.0, 0, pd.earth[ 'mu' ] ], 0 ) state_hyperbolic = spice.conics( [ periapsis, 2.5, 0, 0, 0, -10.0, 0, pd.earth[ 'mu' ] ], 0 ) sc_circular = SC( { 'coes': coes_circular, 'tspan': '1' } ) sc_elliptical = SC( { 'coes': coes_elliptical, 'tspan': '2' } ) sc_parabolic = SC( { 'orbit_state': state_parabolic, 'tspan': 70000.0 } ) sc_hyperbolic = SC( { 'orbit_state': state_hyperbolic, 'tspan': 30000.0 } ) rs = [ sc_circular.states [ :, :3 ], sc_elliptical.states[ :, :3 ], sc_parabolic.states[ :, :3 ], sc_hyperbolic.states[ :, :3 ] ] labels = [ 'Circular $(ecc=0.0)$', 'Elliptical $(ecc=0.7)$', 'Parabolic $(ecc=1.0)$', 'Hyperbolic $(ecc=3.0)$' ] sc_elliptical.plot_states( { 'time_unit': 'hours', 'show' : True } ) pt.plot_orbits( rs,
from Spacecraft import Spacecraft as SC import orbit_calculations as oc import numpy as np import matplotlib.pyplot as plt plt.style.use('dark_background') # Molniya orbital elements a = 6778.0 coes = [a, 0.0, 0.0, 0.0, 0.0, 0.0] sc_config = {'coes': coes, 'tspan': '2.5', 'dt': 100.0} if __name__ == '__main__': sc = SC(sc_config) accels = np.zeros((sc.states.shape[0], 3)) for n in range(sc.states.shape[0]): accels[n] = oc.two_body_ode(sc.ets[n], sc.states[n])[3:] rnorms = np.linalg.norm(sc.states[:, :3], axis=1) vnorms = np.linalg.norm(sc.states[:, 3:6], axis=1) anorms = np.linalg.norm(accels, axis=1) fig, (ax0, ax1, ax2) = plt.subplots(3, 1, figsize=(20, 10)) ets = (sc.ets - sc.ets[0]) / 3600.0 ax0.plot(ets, accels[:, 0], 'r', label=r'$a_x$') ax0.plot(ets, accels[:, 1], 'g', label=r'$a_y$')
import ode_tools as ot import plotting_tools as pt import planetary_data as pd if __name__ == '__main__': r0 = pd.earth['radius'] + 1000.0 v0_circ = (pd.earth['mu'] / r0)**0.5 state0_sc0 = [r0, 0, 0, 0, v0_circ, 0.0] state0_sc1 = [r0, 0, 0, 2.0, 9.0, 0.0] state0_sc2 = [r0, 0, 0, 1.0, 0.0, 8.0] state0_sc3 = [r0, 0, 0, 1.0, 6.0, 6.0] states0 = [state0_sc0, state0_sc1, state0_sc2, state0_sc3] rs = [] sc_config = {'tspan': '1'} for state0 in states0: sc_config['orbit_state'] = state0 rs.append(SC(sc_config).states[:, :3]) pt.plot_orbits( rs, { 'labels': range(4), 'colors': ['C3', 'm', 'c', 'lime'], 'traj_lws': 2, 'azimuth': -32, 'elevation': 70, 'show': True })
''' AWP | Astrodynamics with Python by Alfonso Gonzalez https://github.com/alfonsogonzalez/AWP https://www.youtube.com/c/AlfonsoGonzalezSpaceEngineering Hello world of Spacecraft class Two-body propagation with J2 perturbation for 100 periods ''' # Python standard libraries # AWP libraries from Spacecraft import Spacecraft as SC from planetary_data import earth if __name__ == '__main__': coes = [earth['radius'] + 1000, 0.05, 30.0, 0.0, 0.0, 0.0] sc = SC({ 'coes': coes, 'tspan': '100', 'dt': 100.0, 'orbit_perts': { 'J2': True } }) sc.plot_3d()
# AWP library from Spacecraft import Spacecraft as SC import spice_data as sd import spice_tools as st # 3rd party libraries import spiceypy as spice if __name__ == '__main__': spice.furnsh(sd.leapseconds_kernel) spice.furnsh(sd.de432) spice.furnsh(sd.pck00010) sc = SC({ 'date0': '2021-03-03 22:10:35 TDB', 'coes': [7480.0, 0.09, 5.5, 6.26, 5.95, 0.2], 'tspan': '40', }) st.write_bsp(sc.ets, sc.states[:, :6], {'bsp_fn': 'leo.bsp'}) spice.furnsh('leo.bsp') et0 = spice.str2et('2021-03-03 22:10:40 TDB') etf = spice.str2et('2021-03-04 TDB') timecell = spice.utils.support_types.SPICEDOUBLE_CELL(2) spice.appndd(et0, timecell) spice.appndd(etf, timecell) cell = spice.gfoclt('ANY', '399', 'ELLIPSOID', 'IAU_EARTH', '10', 'ELLIPSOID', 'IAU_SUN', 'LT', '-999', 120.0, timecell)
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 })
# AWP libraries from Spacecraft import Spacecraft as SC from planetary_data import earth import plotting_tools as pt # 3rd party libraries import numpy as np aops = np.arange(0, 360, 90) incs = np.arange(0, 90, 20) tas = [0, 180] coes = [earth['radius'] + 10000, 0.05, 0.0, 0.0, 0.0, 0.0] scs = [] config = {'tspan': '1', 'dt': 100.0} print(len(aops) * len(incs) * len(tas)) if __name__ == '__main__': for inc in incs: for aop in aops: for ta in tas: coes[2] = inc coes[4] = ta coes[5] = aop config['coes'] = coes sc = SC(config) scs.append(sc) rs = [sc.states[:, :3] for sc in scs] pt.plot_orbits(rs, {'traj_lws': 1, 'show': True})
''' AWP | Astrodynamics with Python by Alfonso Gonzalez https://github.com/alfonsogonzalez/AWP https://www.youtube.com/c/AlfonsoGonzalezSpaceEngineering Sun-synchronous orbit spacecraft eclipse calculations ''' # AWP library from Spacecraft import Spacecraft as SC import spice_data as sd # 3rd party libraries import spiceypy as spice if __name__ == '__main__': spice.furnsh( sd.leapseconds_kernel ) spice.furnsh( sd.de432 ) sc = SC( { 'date0': '2021-01-01', 'coes' : [ 6378 + 890.0, 0.0, 99.0, 0, 0, 80 ], 'tspan': '4', } ) sc.calc_eclipses( vv = True ) sc.plot_eclipse_array( { 'time_unit': 'days', 'show' : True } )
# 3rd party libraries import numpy as np # AWP library from Spacecraft import Spacecraft as SC import orbit_calculations as oc import numerical_tools as nt import plotting_tools as pt import planetary_data as pd if __name__ == '__main__': periapsis = pd.earth[ 'radius' ] + 4000.0 # km coes = [ periapsis / 0.3, 0.7, 0, 30.0, 0, 0 ] state = oc.coes2state( coes ) v_rot = np.dot( nt.Cz( 40.0 * nt.d2r ), state[ 3: ] ) state_rot = np.concatenate( ( state[ :3 ], v_rot ) ) sc0 = SC( { 'orbit_state': state, 'tspan': '1' } ) sc_rot = SC( { 'orbit_state': state_rot, 'tspan': '1' } ) pt.plot_orbits( [ sc0.states[ :, :3 ], sc_rot.states[ :, :3 ] ], { 'labels' : [ 'Before', 'After' ], 'colors' : [ 'c', 'm' ], 'azimuth' : -90.0, 'elevation' : 90.0, 'axes_custom': 36000.0, 'traj_lws' : 2.5, 'show' : True } )
''' AWP | Astrodynamics with Python by Alfonso Gonzalez https://github.com/alfonsogonzalez/AWP https://www.youtube.com/c/AlfonsoGonzalezSpaceEngineering Geostationary orbit spacecraft eclipse calculations ''' from Spacecraft import Spacecraft as SC import spice_data as sd import spiceypy as spice if __name__ == '__main__': spice.furnsh( sd.leapseconds_kernel ) spice.furnsh( sd.de432 ) sc = SC( { 'date0': '2021-01-01', 'coes' : [ 42164, 0, 0, 0, 0, 0 ], 'tspan': '365', } ) sc.plot_3d() sc.calc_eclipses( vv = True ) sc.plot_eclipse_array( { 'time_unit': 'days', 'show' : True, } )