def __init__(self, trv = None, elements = None, epoch_time = 0.0, conic_mu = 3.986004415e14, use_logs = {'inertial': 'inertial.log', 'elements': 'elements.log'}): """ Constructor. Must provide either trv or elements/epoch_time. Generally expects everything to be in meters. Args: trv: initial time, position, velocity vector elements: orbital elements, e.g. from make_elements() epoch_time: epoch time of elements (default: 0.0) conic_mu: gravitational constant for central body (e.g. earth) for conic approximation (default is earth); should be in m^3/s^2""" self.conic_mu = conic_mu if elements is not None: # Note that SPICE uses km, km/s, and we use m, m/s trv = np.hstack((0.0, spice.conics(elements, epoch_time) * 1000.0)) self.trv = trv self.logs = {} if use_logs['inertial']: self.logs['inertial'] = Log(use_logs['inertial'], mode = 'w') self.log_inertial() if use_logs['elements']: self.logs['elements'] = Log(use_logs['elements'], mode = 'w') self.log_elements()
def coes2state( coes, mu = pd.earth[ 'mu' ], deg = True ): a, e, i, ta, aop, raan = coes if deg: i *= nt.d2r ta *= nt.d2r aop *= nt.d2r raan *= nt.d2r rp = a * ( 1 - e ) return spice.conics( [ rp, e, i, raan, aop, ta, 0, mu], 0 )
def dist(x, ele_ast, Gm): #x[0] is time and x[1] is true anomaly M_A = toMean(x[1], ele_ast[1]) pos_earth = sp.spkpos('EARTH', x[0], 'J2000', 'LT+S', 'SUN') ele = np.zeros(8) ele[0:4] = ele_ast[0:4] ele[5] = M_A ele[6] = 0 ele[7] = Gm xyz_asteroid = sp.conics(ele, 0)[0:3] d = np.linalg.norm(pos_earth[1] - xyz_asteroid) return d
def dist1(x, ele_ast, Gm): #x[0] node, x[1] argp, x[2] time(earth), x[3] tA of asteroid pos_earth = sp.spkpos('Earth', x[2], 'J2000', 'LT+S', 'SUN') ele = np.zeros(8) ele[0:2] = ele_ast[0:2] ele[3] = x[0] ele[4] = x[1] ele[5] = toMean(x[3], ele_ast[1]) ele[6] = 0 ele[7] = Gm xyz_ast = sp.conics(ele, 0)[0:3] d = np.linalg.norm(pos_earth[1] - xyz_ast) return d
def cometary2cartesian(epoch, com, frame='ecliptic', mu=cnst.GM): """Uses spiceypy to convert cometary orbital elements to HELIOCENTRIC (!) Cartesian states Parameters: ----------- epoch ... epoch of orbital elements [JD] com ... cometary element array [q,e,inc(deg),node(deg),peri(deg),tp(JD)]] frame ... reference frame of output Cartesian state ('ecliptic', 'icrf') mu ... Gravitational parameter Returns: -------- cart ... Cartesian state (x,y,z,vx,vy,vz) External dependencies: ---------------------- spiceypy (imported as sp) numpy (imported as np) cometary2keplerian """ kep = cometary2keplerian(epoch, com, mu)[0] # Input for spiceypy.conics: # q = pericenter distance # e = eccentricity # i = inclination (rad) # node = longitude of the ascending node (rad) # w = argument of pericenter (rad) # M = mean anomaly at epoch (rad) # T0 = epoch # mu = gravitational parameter cart = sp.conics( np.array([ com[0], com[1], np.deg2rad(com[2]), np.deg2rad(com[3]), np.deg2rad(com[4]), np.deg2rad(kep[5]), 0, mu ]), 0) inframe = 'ecliptic' res = frameChange(cart, inframe, frame) return res
def keplerian2cartesian(epoch, kep, frame='ecliptic', mu=cnst.GM): """Uses spiceypy to convert Keplerian orbital elements to heliocentric Cartesian states. Parameters: ----------- epoch ... epoch of orbital elements [JD] kep ... orbital element array [a,e,inc(deg),peri/w(deg),node(deg),M(deg)] frame ... coordinate frame of Cartesian state output: 'ecliptic', 'icrf' mu ... gravitational parameter Returns: -------- cart ... heliocentric Cartesian state (x,y,z,vx,vy,vz). External dependencies: ---------------------- spiceypy (imported as sp) numpy (imported as np) """ q = kep[0] * (1 - kep[1]) # Input for spiceypy.conics: # q = pericenter distance # e = eccentricity # i = inclination (deg) # node = longitude of the ascending node (deg) # w = argument of pericenter (deg) # M = mean anomaly at epoch (deg) # T0 = epoch # mu = gravitational parameter cart = sp.conics( np.array([ q, kep[1], np.deg2rad(kep[2]), np.deg2rad(kep[4]), np.deg2rad(kep[3]), np.deg2rad(kep[5]), 0, mu ]), 0) inframe = 'ecliptic' res = frameChange(cart, inframe, frame) return res
def comet_position_error(et,p,elts): RP,ECC,ARGP,LNODE,INC,TP = elts qRP = RP*au qECC = ECC qINC = deg2rad(INC) qLNODE = deg2rad(LNODE) qARGP = deg2rad(ARGP) T0 = et qTP = spice.str2et('%.14f JD'%TP) qTP = qTP - 69.184 #fixme qa = np.abs(qRP/(1-qECC)) qn = np.sqrt(mu/(qa*qa*qa)) qM0 = qn*(T0-qTP) qelts = qRP,qECC,qINC,qLNODE,qARGP,qM0,T0,mu state = spice.conics(qelts,et) p_c = state[0:3] e = p - p_c return np.sqrt(np.sum(e*e))
def cometary2cartesian(epoch, com, mu=0.01720209895**2): """Uses spiceypy to convert cometary orbital elements to HELIOCENTRIC (!) Cartesian states Parameters: ----------- epoch ... epoch of orbital elements [JD] com ... cometary element array [e,q,tp(JD),node(deg),peri(deg),inc(deg)]] mu ... Gravitational parameter Returns: -------- cart ... Cartesian state (x,y,z,vx,vy,vz). External dependencies: ---------------------- spiceypy (imported as sp) numpy (imported as np) cometary2keplerian """ kep = cometary2keplerian(epoch, com, mu) # Input for spiceypy.conics: # q = pericenter distance # e = eccentricity # i = inclination (deg) # node = longitude of the ascending node (deg) # w = argument of pericenter (deg) # M = mean anomaly at epoch (deg) # T0 = epoch # mu = gravitational parameter cart = sp.conics( np.array([ com[1], com[0], np.deg2rad(com[5]), np.deg2rad(com[3]), np.deg2rad(com[4]), np.deg2rad(kep[5]), 0, mu ]), 0) return cart
def keplerian2cartesian(epoch, kep, mu=0.01720209895**2): """Uses spiceypy to convert Keplerian orbital elements to HELIOCENTRIC (!) Cartesian states Parameters: ----------- epoch ... epoch of orbital elements [JD] kep ... orbital element array [a,e,inc(deg),peri/w(deg),node(deg),M(deg)] mu ... Gravitational parameter Returns: -------- cart ... Cartesian state (x,y,z,vx,vy,vz). External dependencies: ---------------------- spiceypy (imported as sp) numpy (imported as np) """ q = kep[0] * (1 - kep[1]) # Input for spiceypy.conics: # q = pericenter distance # e = eccentricity # i = inclination (deg) # node = longitude of the ascending node (deg) # w = argument of pericenter (deg) # M = mean anomaly at epoch (deg) # T0 = epoch # mu = gravitational parameter cart = sp.conics( np.array([ q, kep[1], np.deg2rad(kep[2]), np.deg2rad(kep[4]), np.deg2rad(kep[3]), np.deg2rad(kep[5]), 0, mu ]), 0) return cart
def convert_tle(line1, line2): i_deg = float(line2[8:16]) asc_node_deg = float(line2[17:25]) e = float("." + line2[26:33]) a_of_p_deg = float(line2[34:42]) mean_a_deg = float(line2[43:51]) mean_motion_rev_day = float(line2[53:63]) epoch_day = float(line1[20:32]) tle_year = int(line1[18:20]) if (0 <= tle_year <= 56): epoch_year = 2000 + tle_year else: epoch_year = 1900 + tle_year year_start_to_j2000 = (date(epoch_year, 1, 1) - date(2000, 1, 1)).days - .5 days_since_j2000 = year_start_to_j2000 + epoch_day - 1 print(days_since_j2000 + 2451545) seconds_since_j2000 = days_since_j2000 * 86400 mean_motion_rad_s = mean_motion_rev_day * (2*m.pi) / 86400 a = (gm_earth / (mean_motion_rad_s ** 2))**(1/3) r_p = a*(1-e) r_p_km = r_p / 1000 i_r = m.radians(i_deg) asc_node_r = m.radians(asc_node_deg) a_of_p_r = m.radians(a_of_p_deg) mean_a_r = m.radians(mean_a_deg) gm_earth_km = gm_earth / 1e9 keplerian_elements = np.array([r_p_km, e, i_r, asc_node_r, a_of_p_r, mean_a_r, seconds_since_j2000, gm_earth_km]) sv_geo_km = spice.conics(keplerian_elements, seconds_since_j2000) sv_geo = sv_geo_km * 1000 earth_sv = spice.spkssb(3, seconds_since_j2000, 'J2000') * 1000 sv = np.add(sv_geo, earth_sv) return sv
def get_rv_spice(self, epoch): '''returns state-vector using SPICE function at input epoch [MJD]''' # convert epoch from MJD to JD for handling in SPICE epoch_JD = epoch + 2400000.5 # compute periapsis rp = self.a * (1 - self.e_mag) # compute mean anomaly at input epoch M0_current = self.get_meanAnom(epoch_JD) # insert orbital elements into array elements_array = [rp, self.e_mag, self.i, self.LAN, self.omega, M0_current, epoch_JD, self.mu] # compute orbital elements state_vec = spice.conics(elements_array, epoch_JD) # prepare outputs r_vec_spice = state_vec[:len(state_vec)//2] v_vec_spice = state_vec[len(state_vec)//2:] return r_vec_spice, v_vec_spice
def __init__(self, body, tEntrance, state, tExit=None): self.body = body self.entranceState = state self.tExit = tExit self.tEntrance = tEntrance self.GM = body.Gmass[0] #output vars explanation #https://ssd.jpl.nasa.gov/?sb_elem #function documentation #https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/oscltx_c.html self.ele = sp.oscltx(state, convert(tEntrance), self.GM) #orbital elements in array form self.elements = { #orbital elements in dict form, for clarity "RP": self.ele[0], #Perifocal distance. "ECC": self.ele[1], #Eccentricity. "INC": self.ele[2], #Inclination. "LNODE": self.ele[3], #Longitude of the ascending node. "ARGP": self.ele[4], #Argument of periapsis. "M0": self.ele[5], #Mean anomaly at epoch. "T0": self.ele[6], #Epoch. "GM": self.ele[7], #Gravitational parameter. "NU": self.ele[8], #True anomaly at epoch. "A": self.ele[ 9] #Semi-major axis. A is set to zero if it is not computable. } self.ele = self.ele[:8] #trim for further use #print(self.elements) r = np.array(state[:3]) v = np.array(state[3:6]) #https://space.stackexchange.com/questions/22000/simulate-celestial-body-motion-on-hyperbolic-trajectory-2d h = np.cross(r, v) e = np.cross(v, h) / self.GM - r / mag(r) #eccentricity direction as i i = e / np.linalg.norm(e) self.eccVec = i #rotational momentum direction as k k = h / np.linalg.norm(h) self.up = k #figure out j from the first 2 vectors j = np.cross(k, i) self.rMtrx = np.array([i, j, k]) #print('rMtrx:',self.rMtrx) #print("ijk:", i, j, k) #transform 3d coordinate to 2d coordinate rR = squish(r, self.rMtrx) vR = squish(v, self.rMtrx) self.angleIn = None self.angleOut = None # dA/dt = r * v / 2 arealVelocity = mag(rR) * mag(vR) / 2 self.av = arealVelocity if not tExit == None: #print(tExit - tEntrance).total_seconds() self.angleIn = angleFromR(r, self) tState = sp.conics(self.ele, convert(tExit)) self.angleOut = angleFromR(tState[0:3], self) if self.angleOut < self.angleIn: self.angleOut += np.pi * 2 return #print("results:", rR, vR) # #find total area sweeped by the object inside the sphere of influence # #focal distance = rp + semi major axis, take negative value foc = -1 * (abs(self.elements["RP"]) + abs(self.elements["A"])) A = self.elements['A'] RP = self.elements['RP'] E = self.elements['ECC'] #x coordinate, when setting the actual origin instead #of the focus as the origin X = lambda x: foc + x # scipy integration -- quad (formula, lower bound, upper bound) Area = lambda r: 2 * abs( quad(lambda x: math.sqrt(x**2 - A**2), X(r[0]), X(RP))[0] * math. sqrt(E**2 - 1) + .5 * np.sign(r[0]) * abs(r[0] * r[1])) #print(Area(rR)) self.deltaT = Area(rR) / arealVelocity """ #exit state using symmetry vfR = np.array([vR[0]*-1, vR[1]]) rfR = np.array([rR[0], rR[1]*-1]) #convert back to 3d J2000 rather than 2d orbital-plane coords self.vf = unSquish(vfR, self.rMtrx) self.rf = unSquish(rfR, self.rMtrx) """ #exit state using deltaT #self.exitState = sp.prop2b(self.GM, state, self.deltaT) self.exitState = sp.conics(self.ele, self.deltaT + self.elements['T0']) #print("deltaT: ", self.deltaT, "\nvf:", self.vf, "\nrf: ", self.rf) if not self.angleIn == None: self.angleIn = angleFromR(r, self) if not self.angleOut == None: self.angleOut = angleFromR(self.exitState[0:3], self) if self.angleOut < self.angleIn: self.angleOut += np.pi * 2
# function. Note: the mean anomaly is 0 degrees and will be set as a default # value in the SQLite database HALE_BOPP_ORB_ELEM = [spiceypy.convrt(HALE_BOPP_DF['Perihelion_dist'] \ .iloc[0], 'AU', 'km'), \ HALE_BOPP_DF['e'].iloc[0], \ np.radians(HALE_BOPP_DF['i'].iloc[0]), \ np.radians(HALE_BOPP_DF['Node'].iloc[0]), \ np.radians(HALE_BOPP_DF['Peri'].iloc[0]), \ 0.0, \ HALE_BOPP_DF['EPOCH_ET'].iloc[0], \ GM_SUN] #%% # Compute the state vector for midnight 2020-05-10 HALE_BOPP_ST_VEC = spiceypy.conics(HALE_BOPP_ORB_ELEM, \ spiceypy.utc2et('2020-05-10')) # Compare with results from https://ssd.jpl.nasa.gov/horizons.cgi print('Comparison of the computed state \n' \ 'vector with the NASA HORIZONS results') print('==========================================') print(f'X in km (Comp): {HALE_BOPP_ST_VEC[0]:e}') print('X in km (NASA): 5.348377806424425E+08') print('==========================================') print(f'Y in km (Comp): {HALE_BOPP_ST_VEC[1]:e}') print('Y in km (NASA): -2.702225247057124E+09') print('==========================================') print(f'Z in km (Comp): {HALE_BOPP_ST_VEC[2]:e}') print('Z in km (NASA): -5.904425343521862E+09') print('==========================================') print(f'VX in km/s (Comp): {HALE_BOPP_ST_VEC[3]:e}')
def propagate_spice(etr_mjd, eldf, MU=1.32712440018 * 10**11, step=1000, sv_option=True, dr_option=True): """ SPICE-based orbit propagator developed for GTOC4 Args: et_MJD (lst): list including start and end time of propagation (in MJD) step (float): steps of propagation eldf (pandas df): pandas dataframe of orbital elements to be propagated (expect spacecraft to be first row) sv_option (bool): if set to True, compute state-vector dr_option (bool): if set to True, compute relative position vector between object of first row and object of other rows Returns: (tuple): time-array [JD], state-vector (if True), relative position vector (if True), and relative position scalar state-vector is3 d numpy array, where: 1st index: number of arrays = number of bodies to propagate 2nd index: rows = timesteps 3rd index: columns = x, y, z, vx, vy, vz, name of object relative position vector is also 3d numpy array, where: 1st index: number of arrays = number of bodies relative to spacecraft 2nd index: rows = timesteps 3rd index: columns = dx, dy, dz, name of object and relative position salar is 3d numpy array, where: 1st index: number of arrays = number of bodies relative to spacecraft 2nd index: rows = timesteps 3rd index: name of object Examples: et, sv, dr, drnorm = propagate_spice(etr_MJD, el_pd1, MU=1.32712440018*10**11, step=steps, sv_option=True, dr_option=True) """ # measure time at start of program tstart = time.time() # convert time range from MJD to JD etr_jd = mjd2jd((etr_mjd)) # create time array etrsteps = [ x * (etr_jd[1] - etr_jd[0]) / step + etr_jd[0] for x in range(step) ] # store number of bodies to propagate [bdy, tmp] = eldf.shape # initialize 3d numpy array to store state-vectors and object name if sv_option == True: sv = np.zeros((bdy, step, 7)) # initialise 3d numpy array to store relative position vector and object name if dr_option == True: dr = np.zeros((bdy - 1, step, 4)) drnorm = np.zeros((bdy - 1, step, 2)) # propagate over time array for i in range(step): # prepare orbital elements for spice.conics() function for j in range(bdy): # convert orbital elements to spice-format rp = eldf.at[j, 'a'] * au2km * (1 - eldf.at[j, 'e']) elts = np.array([ rp, eldf.at[j, 'e'], np.rad2deg(eldf.at[j, 'i']), np.rad2deg(eldf.at[j, 'LAN']), np.rad2deg(eldf.at[j, 'omega']), np.rad2deg(eldf.at[j, 'M0']), mjd2jd(eldf.at[j, 'Epoch']), MU ]) tmp = spice.conics(elts, etrsteps[i]) # FIXME - store state-vector of one object if sv_option == True: for k in range(6): sv[(j, i, k)] = tmp[k] sv[j, i, 6] = eldf.at[j, 'Name'] # store relative state-vector of current object (except if object is the spacecraft ifself) if dr_option == True: if j == 0: sc_currentpos = np.zeros(3) # store current spacecraft location sc_currentpos[0] = tmp[0] # state-vector[0] sc_currentpos[1] = tmp[1] # state-vector[1] sc_currentpos[2] = tmp[2] # state-vector[2] else: # compute relative vector for l in range(3): dr[(j - 1, i, l)] = tmp[l] - sc_currentpos[l] dr[(j - 1, i, 3)] = eldf.at[j, 'Name'] drnorm[(j - 1, i, 0)] = np.sqrt(dr[(j - 1, i, 0)]**2 + dr[(j - 1, i, 1)]**2 + dr[(j - 1, i, 2)]**2) drnorm[(j - 1, i, 1)] = eldf.at[j, 'Name'] # measure time tend = time.time() # computation time dt = tend - tstart # print computational time print(f'Propagation time: {round(dt,2)} [sec]') if sv_option == True and dr_option == True: return etrsteps, sv, dr, drnorm elif sv_option == True and dr_option == False: return etrsteps, sv elif sv_option == False and dr_option == True: return etrsteps, dr, drnorm
def relPosition(self, deltaT): return sp.conics(self.ele, deltaT + self.elements['T0'])
# Our computation will be performed within a while condition (to check whether # 67P entered the SOI or not); thus we need to set an initial value for the # while condition. Here: a very large distance between 67P and Jupiter comet_jup_dist = 10.0**10 # While condition: Compute the following coding part as long as 67P did not # enter Jupiter's SOI while comet_jup_dist > SOI_JUPITER_R: # Add one hour to the date-time stamp and convert it ot ET datetime_stamp = datetime_stamp + datetime.timedelta(hours=1) et_stamp = spiceypy.datetime2et(datetime_stamp) # Compute the state vector of 67P based on the initial orbital elements # (Sun-centric in ECLIPJ2000) COMET_67P_STATE_ORB = spiceypy.conics(COMET_67P_ORB_ELEM, et_stamp) # Compute Jupiter's state vector in as seen from the Sun JUPITER_STATE, _ = spiceypy.spkgeo(targ=5, \ et=et_stamp, \ ref='ECLIPJ2000', \ obs=10) # Compute the distance between Jupiter and 67P comet_jup_dist = spiceypy.vnorm(JUPITER_STATE[:3] - COMET_67P_STATE_ORB[:3]) #%% # If the while condition is not fulfilled, 67P crosses Jupiter's SOI! Let's # take a look when this happened and also let's verify the distance to
def get_coord(self, dates, target, system, observatory=None, corr='NONE', precess=False, return_ltime=False, no_velocity=False): """ Function to calculate the coordinates of a spacecraft or solar system body :param dates: Astropy time object of dates(s). :param target: String name of target body to calculate the position of. :param system: String name of coordinate system to calculate position in. :param observatory: String name of observatory for origin of coordinate system. :param corr: Boolean. If True performs correction for planetary abberation. :param precess: Boolean. If True accounts for precession in .. :param return_ltime: Boolean. If True also returns light travel time between observatory and target. :param no_velocity: Boolean. If True, the velocity components are not returned in the state vector. :return state: Array, giving state at each dates. Either len(dates)x3 or len(dates)x6, depending on no_velocity :return ltime (optional): The light travel time between observatory and target """ # TODO: Add in error checks to make sure target is specified. # The NAIF codes for stereo a and b are: sta_naif_code = '-234' stb_naif_code = '-235' target = self.get_naif_body_code(target) if observatory is not None: observatory = self.get_naif_body_code(observatory) frame, observatory = self.get_system_frame_names( system, observatory, precess) # Pull out the max valid ephemeris time for each craft if target == sta_naif_code: max_dates = self.__PredMaxdates__['sta'] else: max_dates = self.__PredMaxdates__['stb'] # Get ephemeris times for the input dates. If larger than max dates, set to max dates and set correct flag. correct_late_times = False if dates.isscalar: if dates <= max_dates: et = [spice.str2et(dates.isot)] else: et = [spice.str2et(max_dates.isot)] correct_late_times = True else: if all(dates <= max_dates): et = spice.str2et(dates.isot.tolist()) else: et = spice.str2et(dates.isot.tolist()) id_gt_max = dates > max_dates et[id_gt_max] = spice.str2et(max_dates.isot) correct_late_times = True # Now add in the calls to spice functions to get the state variables # If no velocity, use spkpos. if no_velocity: state, ltime = spice.spkpos(target, et, frame, corr, observatory) # Convert state from list of arrays to numpy array state = np.array(state) else: # Velocity requested. This needs spkezr, which only accepts floats. So loop through et and call spkezr for state = np.zeros((dates.size, 6), dtype=float) ltime = np.zeros(dates.size, dtype=float) for i in range(dates.size): state[i, :], ltime[i] = spice.spkezr(target, et[i], frame, corr, observatory) # Add in correction to get states for times after max ephemeris time using # Conics. Loop over entries after maximum time if correct_late_times: if observatory == sta_naif_code: elts = self.__PredConic__['sta'] elif observatory == stb_naif_code: elts = self.__PredConic__['stb'] for idt in np.argwhere(id_gt_mxt is True): et = spice.str2et(dates.isot[idt]) temp = spice.conics(elts, et) # Updates state with conics estimate. if no_velocity: state[idt, :] = temp[0:3] else: state[idt, :] = temp ltime[idt] = -1 # Now convert updatesd coords from HAE to specified system temp = state[:, id_gt_mxt] temp = convert_stereo_coord(dates.isot[id_gt_mxt], temp, 'HAE', system) state[:, id_gt_mxt] = temp # If only one date, loose first dimension if dates.isscalar: state = np.squeeze(state) if return_ltime: return state, ltime else: return state
# 3rd party libraries import spiceypy as spice # AWP library 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',
# Set the date-time of the Asteroid Day (yeah) SAMPLE_ET = spiceypy.utc2et('2020-06-30T00:00:00') # Set the Asteroid Day ET and mean anomaly (in radians) ast_2020_jx1_df.loc[:, 'COMP_ET'] = SAMPLE_ET ast_2020_jx1_df.loc[:, 'M0_RAD'] = np.radians(AST_2020JX1_M0_DEG) # Compute the state vectors for all re-sampled orbital elements ast_2020_jx1_df.loc[:, 'STATE_VEC'] = \ ast_2020_jx1_df.apply(lambda x: \ spiceypy.conics(elts=x[['PERIH_KM', \ 'ECC', \ 'I_RAD', \ 'LNODE_RAD', \ 'ARGP_RAD', \ 'M0_RAD', \ 'EPOCH_ET', \ 'GM_SUN']].values, \ et=x['COMP_ET']), axis=1) # And extract the positional information from the state vector ast_2020_jx1_df.loc[:, 'POS_VEC_KM'] = \ ast_2020_jx1_df['STATE_VEC'].apply(lambda x: x[:3]) #%% # Let's compute the largest deviation / distance of the re-sampled position # vector by using the scipy function cdist from scipy.spatial.distance import cdist
# Initial & Final Conditions sma = 8000 ecc = 0 rp = sma * (1 - ecc) P = 2 * np.pi * np.sqrt(sma**3 / cn.mu) tspan = np.array([0, 0.8 * P]) elm0 = np.array([ rp, ecc, np.deg2rad(10), np.deg2rad(0), np.deg2rad(0), np.deg2rad(0), 0, cn.mu ]) states0 = sp.conics(elm0, 0) sma = sma + 100 rp = sma * (1 - ecc) elm1 = np.array([ rp, ecc, np.deg2rad(10), np.deg2rad(0), np.deg2rad(0), np.deg2rad(0), 0, cn.mu ]) states1 = sp.conics(elm1, 0) statesf = sp.prop2b(cn.mu, states1, tspan[-1]) # Method of Particular Solutions Shooting Method [v0, mps_err] = mps_twobody(tspan, states0, statesf) print("mps err:", mps_err)
# Set an empty array that will store the distances between the Sun # and ATLAS atlas_vecs = [] # Set an empty array that will store the distances between the Sun # and the Solar Orbiter solar_orb_vecs = [] # Iterate through the time array (comet ATLAS) for atlas_time_step in TIME_ARRAY: # Compute the ET 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
print(f'Long. of. asc. node in degrees: {round(CERES_LNODE_DEG, 1)} ' \ '(MPC: 80.3)') print(f'Argument of perih. in degrees: {round(CERES_ARGP_DEG, 1)} ' \ '(MPC: 73.6)') print(f'Orbit period in years: {round(CERES_ORB_TIME_YEARS, 2)} ' \ '(MPC: 4.61)') print('\n') #%% # Convert the orbital elements back to the state vector CERES_STATE_RE = spiceypy.conics([CERES_ORBITAL_ELEMENTS[0], \ CERES_ORBITAL_ELEMENTS[1], \ CERES_ORBITAL_ELEMENTS[2], \ CERES_ORBITAL_ELEMENTS[3], \ CERES_ORBITAL_ELEMENTS[4], \ CERES_ORBITAL_ELEMENTS[5], \ CERES_ORBITAL_ELEMENTS[6], \ GM_SUN], DATETIME_ET) print('State vector of Ceres from the kernel:\n' \ f'{CERES_STATE_VECTOR}') print('State vector of Ceres based on the determined orbital elements:\n' \ f'{CERES_STATE_RE}') print('\n') #%% # On spaceweather.com we can see that an asteroid has a close Earth fly-by: # 136795(1997BQ) on 2020-May-21 at a distance of 16.1 Lunar Distance #
node = rad(gfd(i, 'node[deg]', df)) omega = rad(gfd(i, 'w[deg]', df)) M_A = toMean(rad(gfd(i, 'trueAnomaly2[deg]', df)), e) elts_ast = np.array([a, e, inc, node, omega, M_A, et, Gm]) opt1 = sc.optimize.minimize(dist, [T0, rad(gfd(i, 'trueAnomaly2[deg]', df))], args=(elts_ast, Gm), method='CG') opt2 = sc.optimize.minimize(dist1, [node, omega, opt1.x[0], opt1.x[1]], args=(elts_ast, Gm), method='CG') print(opt2.x) new_mean = toMean(opt2.x[3], e) new_elts_ast = np.array( [a, e, inc, opt2.x[0], opt2.x[1], new_mean, et, Gm]) new_pos_ast = convertAst(sp.conics(new_elts_ast, 0)) new_earth_pos = convertEarth( sp.spkezr('EARTH', opt2.x[2], 'J2000', 'LT+S', 'SUN')) distance = np.linalg.norm(new_earth_pos[0:3] - new_pos_ast[0:3]) #print(distance) if distance > r_earth: new_elts_ast = sp.oscelt(correction(new_earth_pos - new_pos_ast), T0, Gm) final_data = [ C_Time_earth, new_elts_ast[0] * 6.6846e-9, new_elts_ast[1], new_elts_ast[2], new_elts_ast[3], new_elts_ast[4], new_elts_ast[5] ] with open('new_earth_impactors.csv', 'a', newline='') as file: writer = csv.writer(file) writer.writerow(final_data)
#Convert into correct form for spiceypy method r_p_km = r_p * au / 1000 i_r = m.radians(i) asc_node_r = m.radians(asc_node) a_of_p_r = m.radians(a_of_p) mean_anomaly_r = m.radians(mean_anomaly) epoch_JD2000_s = (epoch - 2451545) * 86400 #spice.str2et('jd 2453979.5') keplerian_elements = [ r_p_km, e, i_r, asc_node_r, a_of_p_r, mean_anomaly_r, epoch_JD2000_s, GM_sun_km ] '''First, convert Keplerian elemets to a Cartesian state vector. Spiceypy gives it in km and km/s.''' sv_helio_ecliptic_km = spice.conics(keplerian_elements, epoch_JD2000_s) sv_helio_ecliptic = sv_helio_ecliptic_km * 1000 print("Heliocentric ecliptic cartesian:", np.ndarray.tolist(sv_helio_ecliptic)) '''Then, convert from ecliptic to equatorial frame''' eps = m.radians(23.43927944444444) #obliquity to the ecliptic sv_helio_equatorial = [] sv_helio_equatorial[0:3] = ecl_to_eq(sv_helio_ecliptic[0:3], eps) sv_helio_equatorial[3:6] = ecl_to_eq(sv_helio_ecliptic[3:6], eps) print("Heliocentric equatorial:", sv_helio_equatorial) '''Then, convert from heliocentric to barycentric coordinates''' sun_posvel = spice.spkssb(10, epoch_JD2000_s, 'J2000') * 1000