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 __init__(self, Ls, idx): self.recordLines = Ls self.index = idx midpoint, radius = map(l2d, self.recordLines[:2]) self.window = stypes.SPICEDOUBLE_CELL(2) spice.scard(0, self.window) spice.wninsd(midpoint - radius, midpoint + radius, self.window)
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 occultations(body1, body2, start, end): cnfine = spiceypy.utils.support_types.SPICEDOUBLE_CELL(MAXWIN) result = spiceypy.utils.support_types.SPICEDOUBLE_CELL(MAXWIN) # Obtain the TDB time bounds of the confinement # window, which is a single interval in this case. et0 = spiceypy.str2et(start) et1 = spiceypy.str2et(end) # Insert the time bounds into the confinement window spiceypy.wninsd(et0, et1, cnfine) # 15-minute step. Ignore any occultations lasting less than 15 minutes. # Units are TDB seconds. step = 900.0 obsrvr = "Earth" # Loop over the occultation types. for occtype in types: # For each type, do a search for both transits of # Titan across Saturn and occultations of Titan by Saturn. for j in range(2): if not j: front = body1 fframe = "IAU_" + body1 back = body2 bframe = "IAU_" + body2 else: front = body2 fframe = "IAU_" + body2 back = body1 bframe = "IAU_" + body1 spiceypy.gfoclt(occtype, front, "ellipsoid", fframe, back, "ellipsoid", bframe, "lt", obsrvr, step, cnfine, result) # Display the results print() title = spiceypy.repmc("Condition: # occultation of # by #", "#", occtype) title = spiceypy.repmc(title, "#", back) title = spiceypy.repmc(title, "#", front) print(title) for r in result: print(spiceypy.timout(r, "YYYY Mon DD HR:MN:SC"))
def is_target_in_fov(inst_name, target, et, abcorr, obsrvr): cnfine = spice.cell_double(4) spice.wninsd(et, et + 1, cnfine) step = 1 tframe = f"IAU_{target}" try: results = spice.gftfov( inst_name, target, "ELLIPSOID", tframe, abcorr, obsrvr, step, cnfine, ) except spice.utils.support_types.SpiceyError: return False return True if spice.card(results) > 0 else False
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
none, LT, LTS, ra_dec, declination, 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]), ))
iPass += 1 print('SP-Kernel passed {} half-hour tests'.format(iPass)) ### End of test ###################################################################### ### 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)):
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
# velocityangle = spice.vsep( positionalvector, velocityvector) #rads # relativevelocity = np.linalg.norm(velocityvector) * np.cos(velocityangle) # geometricdopplershift[time] = -(relativevelocity/constants.c) * 437.1e9 #conversion from km to m # pd.DataFrame(geometricdopplershift).to_csv("geometricdopplershift.csv") # 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