Esempio n. 1
0
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
Esempio n. 2
0
 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)
Esempio n. 3
0
    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)
Esempio n. 4
0
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"))
Esempio n. 5
0
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)):
Esempio n. 9
0
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
Esempio n. 10
0
    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