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 inWindow(self, confinementWindow): """ Return True if record window intersects the confinement window argument, else return False """ return 0 < spice.wncard(spice.wnintd(self.window, confinementWindow))
pts = list(spice.gdpool('PAIR_TIMES', 0, 100)) nPETs = len(pts) # Set or clear debug flag try: doDebug = True if spice.gcpool("DEBUG", 0, 1, 999)[0] else False except: doDebug = False # Allocate cell of SpiceDoubles pws = stypes.SPICEDOUBLE_CELL(nPETs) # Add ET endpoints to cell pts.reverse() while pts: spice.appndd(pts.pop(), pws) # Convert those pairs of ET endpoints into confinement window pws = spice.wnvald(nPETs, nPETs, pws) assert (spice.wncard(pws) << 1) == nPETs pts = list(spice.gdpool('PAIR_TIMES', 0, 100)) # Get the filename of, and read, the input XFR file filename = spice.gcpool("XPC_KERNEL", 0, 1, 999)[0] assert filename # Read transfer file as XpcFile object xpcFileIn = XpcFile(fnIn=filename) if doDebug: import pprint pprint.pprint((vars(xpcFileIn), vars(xpcFileIn.segments[0]))) # Slice read XpcFile object per confinement window xpcFileOut = xpcFileIn.slice(pws) if doDebug: pprint.pprint((vars(xpcFileOut), vars(xpcFileOut.segments[0]))) # Write sliced object as SPICE transfer file with open(xpcFileOut.filename, 'wb') as fOutOut:
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
### Find classic problem: how many times in a day the hour and minute ### hands are aligned cnfine = sp.stypes.SPICEDOUBLE_CELL(2) result = sp.stypes.SPICEDOUBLE_CELL(200) ### Set confinement window to 24h at 10ms before two successive midnights etStart, etStop = et0 - 10e-3, et0 + spd - 10e-3 sp.wninsd(etStart, etStop, cnfine) ### 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)
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
# Setting Variables ingresslist = np.array([1.0], dtype=float) egresslist = 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, '', 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])
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])