Example #1
0
    def computeSEPandSPEangles(self, current_epoch, spacecraft_SPICE_ID):

        # get position of object w.r.t. Earth
        spacecraft_state_wrt_Earth, light_times = spice.spkez(
            spacecraft_SPICE_ID, current_epoch, "J2000", 'NONE', 399)

        # get position of Sun w.r.t. Earth
        sun_state_wrt_Earth, light_times = spice.spkez(10, current_epoch,
                                                       "J2000", 'NONE', 399)

        Earth_state_wrt_spacecraft = [-x for x in spacecraft_state_wrt_Earth]
        sun_state_wrt_spacecraft = sun_state_wrt_Earth - spacecraft_state_wrt_Earth

        cosAngle_SEP = self.np.dot(
            sun_state_wrt_Earth[0:3],
            spacecraft_state_wrt_Earth[0:3]) / self.np.linalg.norm(
                sun_state_wrt_Earth[0:3]) / self.np.linalg.norm(
                    spacecraft_state_wrt_Earth[0:3])
        cosAngle_SPE = self.np.dot(
            sun_state_wrt_spacecraft[0:3],
            Earth_state_wrt_spacecraft[0:3]) / self.np.linalg.norm(
                sun_state_wrt_spacecraft[0:3]) / self.np.linalg.norm(
                    Earth_state_wrt_spacecraft[0:3])

        SEP_angle = self.np.arccos(cosAngle_SEP) * 180.0 / self.np.pi
        SPE_angle = self.np.arccos(cosAngle_SPE) * 180.0 / self.np.pi

        return SEP_angle, SPE_angle
    def generateDistanceReport(self,
                               output_file,
                               ephemeris_file,
                               body_SPICE_IDs=[]):
        header_row = "Gregorian_date_TDB, Julian_date_TDB, "
        for bodyID in body_SPICE_IDs:
            pass

        ephem_file_rows = self.ephemeris_file_reader.parseEMTGephemerisFile(
            ephemeris_file)
        for ephem_row in ephem_file_rows:
            # compute distance from the Sun
            spacecraft_distance_from_sun = np.sqrt(
                ephem_row.spacecraft_position_x**2 +
                ephem_row.spacecraft_position_y**2 +
                ephem_row.spacecraft_position_z**2)

            for bodyID in body_SPICE_IDs:
                # get the distance from the Sun to the body
                current_seconds_past_J2000 = spice.str2et(
                    ephem_row.gregorian_date)
                body_state_ICRF, sun_body_light_times = spice.spkez(
                    10, current_seconds_past_J2000, 'J2000', 'NONE', bodyID)
                spacecraft_distance_from_body = np.sqrt(
                    (ephem_row.spacecraft_position_x - body_state_ICRF[0])**2 +
                    (ephem_row.spacecraft_position_y - body_state_ICRF[1])**2 +
                    (ephem_row.spacecraft_position_z - body_state_ICRF[2])**2)
Example #3
0
    def rv(self, date, frame="ECLIPJ2000", corr="NONE", observer=10):
        """Position and velocity vectors.

        Parameters
        ----------
        date : string, float, astropy Time, datetime
          Processed via `util.date2time`.
        frame : string
          The name of a SPICE reference frame.
        corr : string
          The SPICE abberation correction.
        observer : integer
          The NAIF ID of the observer when computing vectors.

        Returns
        -------
        r : ndarray
          Position vector. [km]
        v : ndarray
          Velocity vector. [km/s]
       
        """

        from .. import util

        N = util.date_len(date)
        if N > 0:
            rv = tuple(zip(*(self.rv(d) for d in date)))
            return np.array(rv[0]), np.array(rv[1])

        et = core.date2et(date)
        # no light corrections, sun = 10
        state, lt = spice.spkez(self.naifid, et, frame, corr, observer)
        return np.array(state[:3]), np.array(state[3:])
Example #4
0
def print_state(epoch):
    et = sp.utc2et(epoch)
    print()
    print(epoch, et)
    sc_from_moon = sp.spkez(-10003001, et, 'j2000', 'none', 301)[0]
    print("Moon J2000", sc_from_moon)
    sc_from_earth = sp.spkez(-10003001, et, 'j2000', 'none', 399)[0]
    print("EME2000", sc_from_earth)
    moon_from_earth = sp.spkez(301, et, 'j2000', 'none', 399)[0]
    print("Moon from Earth:", moon_from_earth)
    moon_from_emb = sp.spkez(301, et, 'j2000', 'none', 3)[0]
    print("Moon from EMB:", moon_from_emb)
    earth_from_emb = sp.spkez(399, et, 'j2000', 'none', 3)[0]
    print("Earth from EMB:", earth_from_emb)
    sum = -earth_from_emb + moon_from_emb + sc_from_moon
    print("Sum: -earth_from_emb + moon_from_emb + sc_from_moon\n\t", sum)
    print("Delta:", sum - sc_from_earth)
Example #5
0
    def computeDataFrameGeometries(self, data_frames, greg_format_string,
                                   julian_format_string):

        for data_frame in data_frames:
            self.greg_format_string = greg_format_string
            self.julian_format_string = julian_format_string
            greg_split_string = self.greg_format_string.split(' ')
            data_frame.greg_time_system_string = [
                i for i in greg_split_string if '::' in i
            ][0][2:]
            julian_split_string = self.julian_format_string.split(' ')
            data_frame.julian_time_system_string = [
                i for i in julian_split_string if '::' in i
            ][0][2:]
            current_epoch = data_frame.start_epoch
            current_epoch = spice.str2et(current_epoch)
            stop_epoch = spice.str2et(data_frame.stop_epoch)
            time_remaining = stop_epoch - current_epoch

            while time_remaining >= 0.0:
                greg_date_string = spice.timout(current_epoch,
                                                self.greg_format_string)
                JD_date = spice.timout(current_epoch,
                                       self.julian_format_string)
                for body_pair in data_frame.body_pairs:
                    # get target body state
                    body_state, light_times = spice.spkez(
                        spice.bodn2c(body_pair[1]), current_epoch,
                        data_frame.frame, 'NONE', spice.bodn2c(body_pair[0]))
                    distance_km = self.np.linalg.norm(body_state[0:3])
                    distance_AU = distance_km / self.AU
                    data_frame.data[body_pair[0]][body_pair[1]].append([
                        greg_date_string, JD_date, body_state, distance_km,
                        distance_AU
                    ])

                # compute solar geometries
                SEP_angle, SPE_angle = self.computeSEPandSPEangles(
                    current_epoch, data_frame.spacecraft_SPICE_ID)
                data_frame.data['SEP'].append(
                    [greg_date_string, JD_date, SEP_angle])
                data_frame.data['SPE'].append(
                    [greg_date_string, JD_date, SPE_angle])

                # compute any eclipses
                self.computeCircleCircleOccultations(current_epoch,
                                                     greg_date_string, JD_date,
                                                     data_frame)

                if time_remaining == 0.0:
                    break
                else:
                    if time_remaining >= data_frame.time_step:
                        current_epoch += data_frame.time_step
                        time_remaining -= data_frame.time_step
                    else:
                        time_remaining -= stop_epoch - current_epoch
                        current_epoch += stop_epoch - current_epoch
Example #6
0
def state_spice(xfunc, tais, **kwargs):
    res = {}
    try:
        for tai in tais:
            et = sp.unitim(tai, 'tai', 'et')
            sta = sp.spkez(kwargs['target'], et, kwargs['ref'], kwargs['abcorr'], kwargs['observer'])[0]
            utc = sp.et2utc(et, 'isoc', 3)

            res[utc] = [xfunc(sta[0:3]), sta[3:6]]
    except sp.stypes.SpiceyError as ex:
        return ex.value
    else:
        return res
Example #7
0
    def locate_body_spice(self,JD, mu, AU):

        try:
            import spiceypy as spice
        except:
            print("PySpice not available")
            
        try:
            state,time = spice.spkez(self.SPICEID,(JD-2400000.5-51544.5)*86400.0,"eclipJ2000","NONE",10)
            r = state[0:3]
            v = state[3:6]
        except:
            print(sys.exc_info()[0])
            #call Ryne's Kepler solver
            r, v = kepler(self.SMA * AU, self.ECC, self.INC, self.RAAN, self.AOP, self.MA, self.ReferenceEpoch, JD, mu)

        return r, v
Example #8
0
    def oscelt(self, date, frame="ECLIPJ2000", mu=None):
        """Concic osculating orbital elements.

        Returns the orbit from oscelt in the SPICE toolkit.  The
        results are unreliable for eccentricities very close to 1.0,
        specific angular momentum near zero, and inclinations near 0
        or 180 degrees.  See the SPICE toolkit for notes.

        Parameters
        ----------
        date : string, float, astropy Time, datetime
          Processed via `util.date2time`.
        frame : string
          The name of a SPICE reference frame.
        mu : float, optional
          `G M` of the primary body, or `None` to use the Sun.
        
        Returns
        -------
        orbit : dict
          Orbital parameters as a dictionary.

        """

        import astropy.units as u
        from mskpy.ephem import GM_sun
        from mskpy.util import jd2time

        et = core.date2et(date)
        state, lt = spice.spkez(self.naifid, et, frame, "NONE", 10)

        if mu is None:
            mu = GM_sun.to('km3 / s2').value

        o = spice.oscelt(state, et, mu)
        orbit = {}
        orbit['q'] = (o[0] * u.km).to(u.au)
        orbit['e'] = o[1]
        orbit['i'] = (o[2] * u.rad).to(u.deg)
        orbit['node'] = (o[3] * u.rad).to(u.deg)
        orbit['peri'] = (o[4] * u.rad).to(u.deg)
        orbit['n'] = (o[5] * u.rad).to(u.deg) / u.s
        orbit['t'] = jd2time(float(core.et2jd(o[6])))

        return orbit
    def detectCloseApproaches(self, output_file, spacecraft_SPICE_ID,
                              body_SPICE_ID, body_radius, epoch_windows):

        for window in epoch_windows:
            # create a close approach object
            self.close_approaches.append(CloseApproach())
            self.close_approaches[-1].target_body_SPICE_ID = body_SPICE_ID
            self.close_approaches[-1].target_body = spice.bodc2n(body_SPICE_ID)

            left_epoch = spice.str2et(window[0])
            right_epoch = spice.str2et(window[1])
            middle_epoch = (right_epoch - left_epoch) / 2.0 + left_epoch
            left_state, left_light_times = spice.spkez(spacecraft_SPICE_ID,
                                                       left_epoch, 'J2000',
                                                       'NONE', body_SPICE_ID)
            right_state, right_light_times = spice.spkez(
                spacecraft_SPICE_ID, right_epoch, 'J2000', 'NONE',
                body_SPICE_ID)
            middle_state, middle_light_times = spice.spkez(
                spacecraft_SPICE_ID, middle_epoch, 'J2000', 'NONE',
                body_SPICE_ID)
            left_distance = np.sqrt(left_state[0]**2 + left_state[1]**2 +
                                    left_state[2]**2)
            right_distance = np.sqrt(right_state[0]**2 + right_state[1]**2 +
                                     right_state[2]**2)
            middle_distance = np.sqrt(middle_state[0]**2 + middle_state[1]**2 +
                                      middle_state[2]**2)

            # handle the case where left epoch is post periapse and we must be on an ellipse
            if middle_distance > left_distance and middle_distance > right_distance:
                # either left or right must be the closest approach
                if left_distance > right_distance:
                    # right is closest approach
                    self.close_approaches[-1].spacecraft_state = right_state
                else:
                    # left is closest approach
                    self.close_approaches[-1].spacecraft_state = left_state
                self.close_approaches[
                    -1].spacecraft_altitude = left_distance - body_radius
                break

            # middle must be closer than left and right
            # we need to determine if our middle is before or after periapse
            # look a little bit into the future to see if we are getting closer to the body or farther away
            peek_epoch = middle_epoch + 1.0
            peek_state, middle_light_times = spice.spkez(
                spacecraft_SPICE_ID, peek_epoch, 'J2000', 'NONE',
                body_SPICE_ID)
            peek_distance = np.sqrt(peek_state[0]**2 + peek_state[1]**2 +
                                    peek_state[2]**2)
            if peek_distance > middle_distance:
                # we have passed periapse, therefore we need to bracket with left_epoch
                right_epoch = middle_epoch
            else:
                left_epoch = middle_epoch

            iterations = 0
            tol = 0.001
            while iterations < 100:
                middle_epoch = (right_epoch - left_epoch) / 2.0 + left_epoch

                left_state, left_light_times = spice.spkez(
                    spacecraft_SPICE_ID, left_epoch, 'J2000', 'NONE',
                    body_SPICE_ID)
                right_state, right_light_times = spice.spkez(
                    spacecraft_SPICE_ID, right_epoch, 'J2000', 'NONE',
                    body_SPICE_ID)
                middle_state, middle_light_times = spice.spkez(
                    spacecraft_SPICE_ID, middle_epoch, 'J2000', 'NONE',
                    body_SPICE_ID)
                left_distance = np.sqrt(left_state[0]**2 + left_state[1]**2 +
                                        left_state[2]**2)
                right_distance = np.sqrt(right_state[0]**2 +
                                         right_state[1]**2 + right_state[2]**2)
                middle_distance = np.sqrt(middle_state[0]**2 +
                                          middle_state[1]**2 +
                                          middle_state[2]**2)

                if np.abs(left_distance - right_distance) < tol:
                    closest_approach_distance = middle_distance
                    break

                # determine where the left, middle and right points are located w.r.t. periapse
                time_step = 0.001
                middle_peek_state, middle_light_times = spice.spkez(
                    spacecraft_SPICE_ID, middle_epoch + time_step, 'J2000',
                    'NONE', body_SPICE_ID)
                middle_peek_distance = np.sqrt(middle_peek_state[0]**2 +
                                               middle_peek_state[1]**2 +
                                               middle_peek_state[2]**2)

                if middle_peek_distance - middle_distance < 0.0:
                    left_epoch = middle_epoch
                elif middle_peek_distance - middle_distance > 0.0:
                    right_epoch = middle_epoch

            self.close_approaches[
                -1].Julian_date = middle_epoch / 86400.0 + 2451545.0
            self.close_approaches[-1].spacecraft_state = middle_state
            self.close_approaches[
                -1].spacecraft_altitude = closest_approach_distance - body_radius
Example #10
0
def getPerturberState(body_name,
                      times,
                      frame="ecliptic",
                      origin="heliocenter"):
    """
    Query the JPL ephemeris files loaded in SPICE for the state vectors of desired perturbers.
    
    Major bodies and dynamical centers available: 
        'solar system barycenter', 'sun',
        'mercury', 'venus', 'earth', 
        'mars', 'jupiter', 'saturn', 
        'uranus', 'neptune'
    
    Parameters
    ----------
    body_name : str
        Name of major body.
    times : `~astropy.time.core.Time` (N)
        Times at which to get state vectors.
    frame : {'equatorial', 'ecliptic'}
        Return perturber state in the equatorial or ecliptic J2000 frames. 
    origin : {'barycenter', 'heliocenter'}
        Return perturber state with heliocentric or barycentric origin.
    
    Returns
    -------
    states : `~numpy.ndarray` (N, 6)
        Heliocentric ecliptic J2000 state vector with postion in AU 
        and velocity in AU per day.
    """
    if origin == "barycenter":
        center = 0  # Solar System Barycenter
    elif origin == "heliocenter":
        center = 10  # Heliocenter
    else:
        err = ("origin should be one of 'heliocenter' or 'barycenter'")
        raise ValueError(err)

    if frame == "ecliptic":
        frame_spice = "ECLIPJ2000"
    elif frame == "equatorial":
        frame_spice = "J2000"
    else:
        err = ("frame should be one of {'equatorial', 'ecliptic'}")
        raise ValueError(err)

    # Make sure SPICE is ready to roll
    setupSPICE()

    # Check that times is an astropy time object
    _checkTime(times, "times")

    # Convert MJD epochs in TDB to ET in TDB
    epochs_tdb = times.tdb
    epochs_et = np.array(
        [sp.str2et('JD {:.16f} TDB'.format(i)) for i in epochs_tdb.jd])

    # Get position of the body in heliocentric ecliptic J2000 coordinates
    states = []
    for epoch in epochs_et:
        state, lt = sp.spkez(NAIF_MAPPING[body_name.lower()], epoch,
                             frame_spice, 'NONE', center)
        states.append(state)
    states = np.vstack(states)

    # Convert to AU and AU per day
    states = states / KM_P_AU
    states[:, 3:] = states[:, 3:] * S_P_DAY
    return states
Example #11
0
    def computeCircleCircleOccultations(self, current_epoch, greg_date_string,
                                        JD_date, data_frame):

        for occlt in data_frame.data['occultations']:

            # source body position w.r.t. target
            spacecraft_state_wrt_source, light_times = spice.spkez(
                spice.bodn2c(occlt.target_name), current_epoch,
                data_frame.frame, 'NONE', spice.bodn2c(occlt.source_body_name))
            source_range = self.np.linalg.norm(
                spacecraft_state_wrt_source[0:3])

            # occultation body positon w.r.t. target
            spacecraft_state_wrt_occulter, light_times = spice.spkez(
                spice.bodn2c(occlt.target_name), current_epoch,
                data_frame.frame, 'NONE',
                spice.bodn2c(occlt.occulting_body_name))
            occulter_range = self.np.linalg.norm(
                spacecraft_state_wrt_occulter[0:3])

            # compute the SPO (source-probe-occulter) angle
            cosAngle_SPO = self.np.dot(spacecraft_state_wrt_source[0:3],
                                       spacecraft_state_wrt_occulter[0:3]
                                       ) / source_range / occulter_range
            SPO_angle = self.np.arccos(cosAngle_SPO)

            # compute half-angles
            source_half_angle = self.np.arcsin(
                data_frame.body_dict[occlt.source_body_name].radius /
                source_range)
            occulter_half_angle = self.np.arcsin(
                data_frame.body_dict[occlt.occulting_body_name].radius /
                occulter_range)

            # body areas
            source_area = self.np.pi * source_half_angle**2
            occulter_area = self.np.pi * occulter_half_angle**2

            # compute eclipse %
            eclipse_percentage = 0.0
            eclipse_type = "none"
            # first test if we have an eclipse condition at all
            if source_half_angle + occulter_half_angle < SPO_angle:
                eclipse_percentage = 0.0
            else:
                if SPO_angle < occulter_half_angle - source_half_angle:
                    # we are at full eclipse
                    eclipse_percentage = 100.0
                    eclipse_type = "full"
                else:
                    if SPO_angle < source_half_angle - occulter_half_angle:
                        # annular eclipse
                        eclipse_percentage = 100.0 * (occulter_area /
                                                      source_area)
                        eclipse_type = "annular"
                    else:
                        # partial eclipse
                        x1 = 1.0 / 2.0 * (SPO_angle**2 - source_half_angle**2 +
                                          occulter_half_angle**2) / SPO_angle
                        a1 = occulter_half_angle**2 * self.np.arccos(
                            x1 / occulter_half_angle) - x1 * self.np.sqrt(
                                occulter_half_angle**2 - x1**2)
                        a2 = source_half_angle**2 * self.np.arccos(
                            (SPO_angle - x1) / source_half_angle) - (
                                SPO_angle -
                                x1) * self.np.sqrt(source_half_angle**2 -
                                                   (SPO_angle - x1)**2)
                        eclipse_percentage = (a1 + a2) / source_area
                        eclipse_percentage *= 100.0
                        eclipse_type = "partial"

            occlt.data.append(
                [greg_date_string, JD_date, eclipse_percentage, eclipse_type])
Example #12
0
    def printReport(self,
                    SPICEbody=399,
                    reportfilename='default_body_distance_report.csv',
                    units='AU'):
        #we need to load all of the SPICEness
        import spiceypy
        import os

        spiceypy.furnsh(self.myOptions.universe_folder + '/ephemeris_files/' +
                        self.myOptions.SPICE_leap_seconds_kernel)
        spiceypy.furnsh(self.myOptions.universe_folder + '/ephemeris_files/' +
                        self.myOptions.SPICE_reference_frame_kernel)

        for dirpath, dirnames, filenames in os.walk(
                self.myOptions.universe_folder + '/ephemeris_files/'):
            for file in filenames:
                sourcefile = os.path.join(dirpath, file)

                if sourcefile.endswith('.bsp'):
                    spiceypy.furnsh(sourcefile)

        with open(reportfilename, 'w') as file:
            #all operations
            file.write(
                'Gregorian date (ET/TDB), Julian date (ET/TDB), Spacecraft distance from '
                + spiceypy.bodc2n(SPICEbody) + ' (' + str(SPICEbody) + ') ' +
                ' (' + units + ')\n')

            for recordIndex in range(0, len(self.ephemeris_file_data)):
                myRecord = self.ephemeris_file_data[recordIndex]

                epoch = myRecord.julian_date

                seconds_since_J2000 = spiceypy.str2et(str(epoch) + " JD TDB")
                x_spacecraft = myRecord.spacecraft_position_x
                y_spacecraft = myRecord.spacecraft_position_y
                z_spacecraft = myRecord.spacecraft_position_z

                try:
                    [body_state,
                     light_time] = spiceypy.spkez(SPICEbody,
                                                  seconds_since_J2000, 'J2000',
                                                  'NONE', 10)
                except:
                    print(
                        'I don\'t have enough information to tell you the position of '
                        + str(SPICEbody) + ' with respect to the Sun on ' +
                        epoch + '\n')

                x_body = body_state[0]
                y_body = body_state[1]
                z_body = body_state[2]

                x_relative = x_spacecraft - x_body
                y_relative = y_spacecraft - y_body
                z_relative = z_spacecraft - z_body

                r_relative = (x_relative**2 + y_relative**2 +
                              z_relative**2)**0.5

                if units == 'AU':
                    r_relative /= 149597870.691
                elif units != 'km':
                    raise ('I don\'t know what a ' + units + ' is!')

                file.write(myRecord.gregorian_date + ',' +
                           str(myRecord.julian_date) + ',' + str(r_relative) +
                           '\n')

        #now we can unload all the SPICE kernels
        spiceypy.unload(self.myOptions.universe_folder + '/ephemeris_files/' +
                        self.myOptions.SPICE_leap_seconds_kernel)
        spiceypy.unload(self.myOptions.universe_folder + '/ephemeris_files/' +
                        self.myOptions.SPICE_reference_frame_kernel)

        for dirpath, dirnames, filenames in os.walk(
                self.myOptions.universe_folder + '/ephemeris_files/'):
            for file in filenames:
                sourcefile = os.path.join(dirpath, file)

                if sourcefile.endswith('.bsp'):
                    spiceypy.unload(sourcefile)
Example #13
0
def getDEstate(target,t,frame='ECLIPJ2000'):
    return sp.spkez(target,t,frame,'NONE',0)[0]