def find_maven_apsis(segment='periapse'): """ Calculates the ephemeris times at apoapse or periapse for all MAVEN orbits between orbital insertion and now. Requires furnishing of all SPICE kernels. Parameters ---------- segment : str The orbit point at which to calculate the ephemeris time. Choices are 'periapse' and 'apoapse'. Defaults to 'periapse'. Returns ------- orbit_numbers : array Array of MAVEN orbit numbers. et_array : array Array of ephemeris times for chosen orbit segment. """ # set starting and ending times et_start = 464623267 # MAVEN orbital insertion et_end = spice.datetime2et(datetime.utcnow()) # right now # do very complicated SPICE stuff target = 'Mars' abcorr = 'NONE' observer = 'MAVEN' relate = '' refval = 0. if segment == 'periapse': relate = 'LOCMIN' refval = 3396. + 500. elif segment == 'apoapse': relate = 'LOCMAX' refval = 3396. + 6200. adjust = 0. step = 60. # 1 minute steps, since we are only looking within periapse segment for periapsis et = [et_start, et_end] cnfine = spice.utils.support_types.SPICEDOUBLE_CELL(2) spice.wninsd(et[0], et[1], cnfine) ninterval = round((et[1] - et[0]) / step) result = spice.utils.support_types.SPICEDOUBLE_CELL(round(1.1 * (et[1] - et[0]) / 4.5)) spice.gfdist(target, abcorr, observer, relate, refval, adjust, step, ninterval, cnfine, result=result) count = spice.wncard(result) et_array = np.zeros(count) if count == 0: print('Result window is empty.') else: for i in range(count): lr = spice.wnfetd(result, i) left = lr[0] right = lr[1] if left == right: et_array[i] = left # make array of orbit numbers orbit_numbers = np.arange(1, len(et_array) + 1, 1, dtype=int) # return orbit numbers and array of ephemeris times return orbit_numbers, et_array
def computeOccultations(self, observer_in, occultation_bodies_in, target_in, target_shape_in, target_frame_in, step_size_in, aberration_correction_in, greg_format_string_in): self.greg_format_string = greg_format_string_in split_string = self.greg_format_string.split(' ') self.time_system_string = [i for i in split_string if '::' in i][0][2:] self.observer = str(observer_in) self.occultation_bodies = occultation_bodies_in self.target = target_in self.target_shape = target_shape_in self.target_frame = target_frame_in self.search_step_size = step_size_in self.aberration_correction = aberration_correction_in self.cumulative_results = {} for body in self.occultation_bodies: # add a new key to the results dictionary for this body self.cumulative_results[body.name] = [] for occultation_type in body.occultation_types: body.search_start_ET_seconds = spice.str2et(body.search_start) body.search_end_ET_seconds = spice.str2et(body.search_end) spice.wninsd(body.search_start_ET_seconds, body.search_end_ET_seconds, self.SPICE_search_window) spice.gfoclt(occultation_type, body.name, body.shape, body.frame, self.target, self.target_shape, self.target_frame, self.aberration_correction, self.observer, self.search_step_size, self.SPICE_search_window, self.results_window) winsiz = spice.wncard(self.results_window) for body_index in range(winsiz): [intbeg, intend] = spice.wnfetd(self.results_window, body_index) btmstr = spice.timout(intbeg, self.greg_format_string) etmstr = spice.timout(intend, self.greg_format_string) occultation_event = OccultationEvent() occultation_event.start = btmstr occultation_event.stop = etmstr occultation_event.start_JD = greg2Julian(btmstr) occultation_event.stop_JD = greg2Julian(etmstr) occultation_event.duration = intend - intbeg occultation_event.type = occultation_type occultation_event.observer = self.observer occultation_event.occulting_body = body.name occultation_event.occulting_body_shape = body.shape occultation_event.target = self.target occultation_event.target_body_shape = self.target_shape self.cumulative_results[body.name].append( occultation_event)
def check_coverage(self): cover = spice.utils.support_types.SPICEDOUBLE_CELL(2000) if local == 0: spice.spkcov(auxdir + 'spk/MSGR_HGM008_INTGCB.bsp', -236, cover) else: spice.spkcov( '/home/sberton2/Works/NASA/Mercury_tides/spktst/MSGR_HGM008_INTGCB.bsp', -236, cover) twind = [spice.wnfetd(cover, i) for i in range(spice.wncard(cover))] epo_in = np.sort(self.ladata_df.ET_TX.values) self.ladata_df['in_spk'] = np.array( [np.sum([t[0] <= val <= t[1] for t in twind]) for val in epo_in]) > 0 if debug: print(len(self.ladata_df.loc[self.ladata_df['in_spk'] == False])) print("lensel", len(self.ladata_df), len(self.ladata_df.loc[self.ladata_df['in_spk']])) self.ladata_df = self.ladata_df.loc[self.ladata_df['in_spk']]
def core(): class SpiceVariables: obs = '-74' # NAIF code for MEX target = 'MARS ODYSSEY' # NAIF code for TGO ['EARTH'/'SUN'/ a groundstation etc] obsfrm = 'IAU_MARS' abcorr = 'NONE' crdsys = 'LATITUDINAL' coord = 'LATITUDE' stepsz = 100.0 # Check every 300 seconds if there is an occultation MAXILV = 100000 #Max number of occultations that can be returned by gfoclt bshape = 'POINT' fshape = 'DSK/UNPRIORITIZED' front = 'MARS' fframe = 'IAU_MARS' TFMT = 'YYYY-MM-DD HR:MN:SC' # Format that Cosmographia understands sv = SpiceVariables() #-----------------------------------------------------<VALUES TO EDIT REGULARLY>---------------------------------------- # If you only wish to analysis mutual [cross-link] occultation between MEX and TGO, then this is the only section that # needs to be edited start = '2020 MAR 1' stop = '2020 MAR 3' OCCSELECTION = 17 # Which occultation do you wish to see in Cosmographia? [optional] here = path.abspath(path.dirname(__file__)) PathtoMetaKernel1 = here + '/TGO/krns/mk/em16_plan.tm' PathtoMetaKernel2 = here + '/MEX/krns/mk/MEX_OPS.tm' #----------------------------------------------------------------------------------------------------------------------- spice.furnsh(PathtoMetaKernel1) spice.furnsh(PathtoMetaKernel2) sv = SpiceVariables() # Setting Variables ingresslist = np.array([1.0], dtype=float) etbeg = spice.str2et(start) etend = spice.str2et(stop) # Form a windows that gfoclt can populate window = stypes.SPICEDOUBLE_CELL(2) spice.wninsd(etbeg, etend, window) occwindow = stypes.SPICEDOUBLE_CELL(sv.MAXILV) #find occultation windows between the dates listed above [ most comp cost in this function] spice.gfoclt('ANY', sv.front, sv.fshape, sv.fframe, sv.target, sv.bshape, 'J2000', sv.abcorr, sv.obs, sv.stepsz, window, occwindow) winsiz = spice.wncard(occwindow) # Find cardinality (number of windows) #initialize lists to form dataframe lon, lat, dist, sza, angle = (np.ones(winsiz - 1) for i in range(5)) # Enter the ingress epochs into a dataframe occlist = np.ones((winsiz, 3)) for i in range(winsiz): [ingress, egress ] = spice.wnfetd(occwindow, i) # extract the begining and ends of the windows if i == 1: ingresslist = ingress else: ingresslist = np.append(ingresslist, [ingress]) occs = pd.DataFrame(ingresslist, columns=['Time']) occ = occs.Time[OCCSELECTION] return occ # sv = main.SpiceVariables() # occ = core() #print("strange result:", occ) # #print(result) # #form the dataframe # length = 120 # occs = pd.DataFrame(ingresslist, columns=['Time']) # residualdoppler = np.zeros(length) # velocitydoppler = np.zeros(length) # RESIDUALSUM = np.zeros(length) # #Calculate the residual doppler as the sum of the neutral and ionosphere # tic = timer.perf_counter() # for time in tqdm(range(length)): #begin time at occultation epoch and go to 2 mins pior # ray, dist, totalperiods, remainingdistance = main.producegeometrylamda(occs.Time[OCCSELECTION], sv, time*8)# Produce geometry in wavelenghts to investigate electric distance # _,_,_,_, alt = main.Location(occs.Time[OCCSELECTION], sv, time*8) # ionoresidual = atmosphere.iono(ray[2,:],totalperiods) # neutralresidual = atmosphere.neutral(ray[2,:],totalperiods) # residual = 1 + (ionoresidual + neutralresidual) # # plt.plot(range(totalperiods),residual[0,:]) # # plt.title("Refractive Index through Propergation of Ray") # # plt.xlabel("MEX->TGO distance (km)") # # plt.ylabel("Refractive Index") # # plt.show() # #DO A TEST TO SEE IF THE NET REFRACTIVE INDEX CHANGES OVER TIME, THEN U CAN HONE IN ON THE ERRRO # # account for the plus 1 # [electricdistance, geometric, resdopplershift] = main.doppler(residual, totalperiods, dist, remainingdistance) # miss = electricdistance - dist + remainingdistance # a possitive number as electric distance is greater that geometric due to iono # howwrongurcodeis = geometric - dist # is this purly due to rounding of that 9945 (each 1 is ~685 m) # residualdoppler[time] = resdopplershift # #find the geometric doppler too UNTESTED MODULE # sc2scstates = spice.spkezr(sv.target, (occs.Time[OCCSELECTION] - time*8), sv.fframe, 'LT+S', sv.obs) # velocityvector = sc2scstates[0][3:6] # velocityvector = velocityvector[0:3] # positionalvector = sc2scstates[0][0:3] # positionalvector = positionalvector[0:3] # velocityangle = spice.vsep( positionalvector, velocityvector) #rads # relativevelocity = np.linalg.norm(velocityvector) * np.cos(velocityangle) # geometricdopplershift = -(relativevelocity/constants.c) * 437.1e6 # velocitydoppler[time] = geometricdopplershift *1000 # becuase spice is in km and c is in m # toc = timer.perf_counter() # passingtime = toc-tic # print('elapsed time in seconds:', passingtime) # noise = np.random.normal(0,50,velocitydoppler.shape) # RESIDUALSUM = residualdoppler + velocitydoppler + noise # fig, ax = plt.subplots(3) # ax[0].plot(range(-960,0,8),velocitydoppler[: : -1] ) # ax[0].set_title('Geometric', loc='left') # ax[0].set_xlabel('Time after Occ (s)') # ax[0].set_ylabel('Doppler Shift (Hz)') # ax[1].plot(range(-960,0,8),residualdoppler[: : -1] ) # ax[1].set_title('Residual', loc='left') # ax[1].set_xlabel('Time after Occ (s)') # ax[1].set_ylabel('Doppler Shift (Hz)') # ax[2].plot(range(-960,0,8),RESIDUALSUM[: : -1]) # ax[2].set_title('Product + Noise', loc='left') # ax[2].set_xlabel('Time to Horizon Epoch (s)') # ax[2].set_ylabel('Doppler Shift (Hz)') # plt.show() # #then add noise
equals, ) = 'earth 0 j2000 none lt lt+s ra/dec declination ='.upper().split() sp.furnsh('de421.bsp') gaia_db = 'gaia.sqlite3' ### Find ET of spring equinox wrt Solar System Barycenter (SSB) & earth cnfine = sp.utils.support_types.SPICEDOUBLE_CELL(2) result = sp.utils.support_types.SPICEDOUBLE_CELL(200) sp.wninsd(0., sp.pi() * .5e7, cnfine) r = sp.gfposc(ssb, j2000, none, earth, ra_dec, declination, equals, 0., 0., 10 * sp.spd(), 200, cnfine, result) ### Get SSB->Earth and Earth->SSB vectors at that time et = sp.wnfetd(result, 0)[0] ssb2e = sp.spkezr(earth, et, j2000, none, ssb)[0] e2ssblt = sp.spkezr(ssb, et, j2000, LT, earth)[0] if do_debug: ### N.B. Light-time correction on Earth->SSB will have no effect print( dict( etcal=sp.etcal(et, 99), ssb2earth_au=sp.vscl(aupkm, ssb2e[:3]), earth2ssb_au=sp.vscl(aupkm, e2ssblt[:3]), )) assert 0.0 == sp.vnormg(sp.vaddg(ssb2e, e2ssblt, 6), 6) ### Get a star (ra,dec,parallax) near the pole with parallax > 10mas/y cn = sl3.connect(gaia_db) cu = cn.cursor()
### Find local minima using SPICE Geometry Finder sp.gfpa(sClock, sMinute, "NONE", sHour, "LOCMIN", 1e-6, 0.0, spm, 6000, cnfine, result) ### Confirm that 22 minima were found assert 22 == sp.wncard(result) print('SP-Kernel passed [{}-alignments per day] test'.format( sp.wncard(result))) ### Optional logging if doDebug: print('Alignments between {} and {}:'.format( sp.etcal(etStart + 0.0005, 99), sp.etcal(etStop + 0.0005, 99))) for iWin in range(sp.wncard(result)): left, right = sp.wnfetd(result, iWin) print(' {}: {}'.format(iWin, sp.etcal(left + 0.0005, 99))) ### Repeat with extra time cnfine = sp.stypes.SPICEDOUBLE_CELL(2) etStop = et0 + spd + 10e-3 sp.wninsd(etStart, etStop, cnfine) ### Find local minima sp.gfpa(sClock, sMinute, "NONE", sHour, "LOCMIN", 1e-6, 0.0, spm, 6000, cnfine, result) ### Confirm that 23 minima were found assert 23 == sp.wncard(result) print('SP-Kernel passed [{}-alignments per (day+20ms)] test'.format( sp.wncard(result)))
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) ets_SPICE = spice.wnfetd(cell, 0) cal0 = spice.et2utc(ets_SPICE[0], 'C', 1) cal1 = spice.et2utc(ets_SPICE[1], 'C', 1) print('\n*** SPICE RESULTS ***') print(f'{cal0} --> {cal1}') sc.plot_3d() sc.calc_eclipses(vv=True) sc.plot_eclipse_array({'time_unit': 'seconds', 'show': True})
def find_segment_et(orbit_number, data_directory, segment='apoapse'): """Calculates the ephemeris time at the moment of apoapsis or periapsis for an orbit. Requires data files exist for the choice of orbit and segment. If not, use the full-mission "find_maven_apsis" function available in the "data" sub-module. Also requires furnishing of all SPICE kernels. Parameters ---------- orbit_number : int The MAVEN orbit number. data_directory : str Absolute system path to the location containing orbit block folders ("orbit01300", orbit01400", etc.) segment : str For which orbit segment you want to calculate the ephemeris time. Options are 'apoapse' and 'periapse." Default choice is 'apoapse'. Returns ------- et : float The ephemeris time for the chosen segment/orbit number. """ # load files files = find_files(orbit=orbit_number, segment=segment, data_directory=data_directory) if len(files) == 0: raise Exception('No %s files for orbit %i.' % (segment, orbit_number)) hdul = files[0] et_start = hdul['integration'].data['et'][0] # do very complicated SPICE stuff target = 'Mars' abcorr = 'NONE' observer = 'MAVEN' relate = '' refval = 0. if segment == 'periapse': relate = 'LOCMIN' refval = 3396. + 500. elif segment == 'apoapse': relate = 'LOCMAX' refval = 3396. + 6200. adjust = 0. step = 60. et = [et_start, et_start + 4800] cnfine = spice.utils.support_types.SPICEDOUBLE_CELL(2) spice.wninsd(et[0], et[1], cnfine) ninterval = 100 result = spice.utils.support_types.SPICEDOUBLE_CELL(100) spice.gfdist(target, abcorr, observer, relate, refval, adjust, step, ninterval, cnfine, result=result) et = spice.wnfetd(result, 0)[0] # return the ephemeris time of the orbit segment return et
def find_maven_apsis_et(self, end_time: datetime = None, apsis: str = 'apoapse') \ -> tuple[np.ndarray, np.ndarray]: """Calculate the ephemeris times at either orbital apses between a start and end time. To do this, SPICE checks the Mars-MAVEN distance in steps of 1 minute, and returns the local minima (for periapsis) or local maxima (for apoapsis). Parameters ---------- end_time Ending datetime to get apsis ephemeris times for. Default is :code:`None`, which will get times up until today. apsis The apsis to get the ephemeris times for. Can be either 'apoapse' or 'periapse'. Returns ------- orbits Relative MAVEN orbit numbers between the start and end dates. et Ephemeris times at the given apsis for all the orbits in the time range. """ # TODO: it'd be nice to be able to specify a start time. This is easy to # implement here, but I'm not sure how I'd compute orbit numbers. et_start = 464623267 et_end = spice.datetime2et(datetime.utcnow()) if end_time is None \ else spice.datetime2et(end_time) relate, refval = self.__set_apsis_flags(apsis) adjust = 0. step = 60. cnfine = spice.utils.support_types.SPICEDOUBLE_CELL(2) spice.wninsd(et_start, et_end, cnfine) ninterval = round((et_end - et_start) / step) result = spice.utils.support_types.SPICEDOUBLE_CELL( round(1.1 * (et_end - et_start) / 4.5)) spice.gfdist(self.__target, self.__abcorr, self.__observer, relate, refval, adjust, step, ninterval, cnfine, result=result) count = spice.wncard(result) et_array = np.zeros(count) if count == 0: print('Result window is empty.') else: for i in range(count): lr = spice.wnfetd(result, i) left = lr[0] right = lr[1] if left == right: et_array[i] = left # make array of orbit numbers orbit_numbers = np.arange(1, len(et_array) + 1, 1, dtype=int) # return orbit numbers and array of ephemeris times return orbit_numbers, et_array
def cov_int(object_cov, object_id, kernel, time_format='TDB', global_boundary=False, report=False): """ Generates a list of time windows out of a SPICE cell for which either the SPICE API spkcov_c or ckcov_c have been run. :param object_cov: SPICE :type object_cov: :param object_id: Object ID or Name for which we provide the coverage :type object_id: Union[str, int] :param kernel: Kernel name for which the coverage is being checked :type kernel: str :param time_format: Desired output format; 'UTC' or 'CAL' :type time_format: str :param global_boundary: Boolean to indicate whether if we want all the coverage windows or only the absolute start and finish coverage times :type global_boundary: bool :param report: If True prints the resulting coverage on the screen :type report: bool :return: Time Windows in the shape of a list :rtype: list """ boundaries = False if '/' in kernel: kernel = kernel.split('/')[-1] # # Reporting should only be activated if we are not asking for global # boundaries. # if report and not global_boundary: try: body_name = spiceypy.bodc2n(object_id) except: body_name = spiceypy.frmnam(object_id, 60) print("Coverage for {} in {} [{}]:".format(body_name, kernel, time_format)) number_of_intervals = list(range(spiceypy.wncard(object_cov))) interval_start_list = [] interval_finish_list = [] coverage = [] for element in number_of_intervals: et_boundaries = spiceypy.wnfetd(object_cov, element) if time_format == 'CAL' or time_format == 'UTC': boundaries = et2cal(et_boundaries, format=time_format) else: boundaries = et_boundaries interval_start = boundaries[0] interval_finish = boundaries[1] if report and not global_boundary: print("Interval {}: {} - {}\n".format(element, boundaries[0], boundaries[1])) coverage.append(interval_start) coverage.append(interval_finish) interval_start_list.append(interval_start) interval_finish_list.append(interval_finish) # # If the global_boundary parameter is set the only output is the global # coverage start and finish # if global_boundary: start_time = min(interval_start) finish_time = max(interval_finish) coverage = et2cal([start_time, finish_time], format=time_format) return coverage
occwindow = stypes.SPICEDOUBLE_CELL(sv.MAXILV) # find occultation windows between the dates listed above [ most comp cost in this function] spice.gfoclt('ANY', sv.front, sv.fshape, sv.fframe, sv.target, sv.bshape, '', sv.abcorr, sv.obs, sv.stepsz, window, occwindow) winsiz = spice.wncard(occwindow) # Find cardinality (number of windows) # initialize lists to form dataframe lon, lat, dist, sza, angle = (np.ones(winsiz - 1) for i in range(5)) # Enter the ingress epochs into a dataframe occlist = np.ones((winsiz, 3)) for i in range(winsiz): # extract the begining and ends of the windows [ingress, egress] = spice.wnfetd(occwindow, i) if i == 1: ingresslist = ingress egresslist = egress else: ingresslist = np.append(ingresslist, [ingress]) egresslist = np.append(egresslist, [egress]) #MEX,TGO = FindDopplerMain.ephemerides(636391881,1) # form the dataframe occs = pd.DataFrame(ingresslist, columns=['Time']) occs['Ingress'] = ingresslist occs['Egress'] = egresslist # Form the profile geometry to be ported into Cosmographia (see README)
def do_main(argv): sp.kclear() halfpi, dpr = sp.halfpi(), sp.dpr() fnck = 'out.bc' target = 'TEMPEL 1' frame = 'DIF_SPACECRAFT' for arg in argv: if arg.startswith('--ck='): fnck = arg[5:] elif arg.startswith('--target='): target = arg[9:] elif arg.startswith('--k='): sp.furnsh(arg[4:]) else: assert False, 'Unknown argument [{0}]'.format(arg) frameid = sp.gipool('FRAME_{0}'.format(frame), 0, 1)[0] scid = sp.gipool('CK_{0}_SPK'.format(frameid), 0, 1)[0] scname = sp.bodc2s(scid, 99) cover = sp.stypes.SPICEDOUBLE_CELL(200) sp.scard(0, cover) cover = sp.ckcov(fnck, frameid, False, "INTERVAL", 0.0, "TDB") sp.furnsh(fnck) vbore = sp.vpack(-1, 0, 0) vorbitnorm = sp.vpack(0, 1, 0) for ipair in range(sp.wncard(cover)): et0, etend = sp.wnfetd(cover, 0) etlast, ettca, niter = et0, (et0 + etend) * 0.5, 0 while ettca != etlast and niter < 20: etlast = ettca state6 = sp.spkezr(target, ettca, "j2000", "none", scname)[0] vpos, vvel = state6[:3], state6[3:] det = sp.vdot(vvel, vpos) / sp.vdot(vvel, vvel) ettca -= det niter += 1 print(dict( TCAtdb=sp.etcal(ettca, 99), niter=niter, )) et = et0 ets, boreerrs, orbitnormerrs = list(), list(), list() while et <= etend: state6 = sp.spkezr(target, et, frame, "none", scname)[0] vpos, vvel = state6[:3], state6[3:] ets.append(et - et0) boreerrs.append(dpr * sp.vsep(vbore, vpos)) orbitnormerrs.append(dpr * (sp.vsep(vbore, vorbitnorm) - halfpi)) et += max([0.01, 0.1 * abs(cos(sp.vsep(vpos, vvel)))]) try: plt.plot(ets, orbitnormerrs, 'o', label='Orbit normal error') plt.plot(ets, boreerrs, 'o', label='Boresight error') plt.axvline(ettca - et0, label='TCA') plt.xlabel('Time, s past {0} TDB'.format(sp.etcal(et0, 99))) plt.ylabel('Error, deg') plt.legend(loc='best') plt.show() except: if do_debug: import traceback as tb tb.print_exc() print(boreerrs[::1000]) print(orbitnormerrs[::1000])