Example #1
0
def transform_lonlat(t, lons, lats, frame_from, frame_to):
    """Transform longitude/latitude direction from one reference frame to another.

    Parameters
    ----------
    t : datetime.datetime
        Evaluation datetimes.
    lons : np.ndarray
        Longitudes array.
    lats : np.ndarray
        Latitudes array.

    Returns
    -------
    (np.ndarray, np.ndarray)
        Transformed longitude/latitude's.

    Raises
    ------
    ValueError
        If source and target frame are equal.
    """
    if frame_from == frame_to:
        raise ValueError("source frame and target frame are equal")

    lons_rad = 2 * np.pi * lons / 360
    lats_rad = 2 * np.pi * lats / 360

    vecs = np.array([
        [np.cos(lon), np.sin(lon), np.sin(lat)]
        for (lon, lat) in list(zip(lons_rad, lats_rad))
    ])

    if isinstance(t, datetime.datetime):
        for i in range(0, len(vecs)):
            vecs[i] = spiceypy.mxv(spiceypy.pxform(frame_from, frame_to,
                                   spiceypy.datetime2et(t)),
                                   vecs[i])
    elif isinstance(t, np.ndarray) or isinstance(t, list):
        for i in range(0, len(t)):
            vecs[i] = spiceypy.mxv(spiceypy.pxform(frame_from, frame_to,
                                   spiceypy.datetime2et(t[i])),
                                   vecs[i])
    else:
        raise TypeError("variable t is not a datetime or array/list")

    res = np.array([
        [360 * np.arccos(v[0]) / 2 / np.pi, 360 * np.arcsin(v[2]) / 2 / np.pi]
        for v in vecs
    ])

    return res
Example #2
0
def velocityUlysses(date, RF="HCI"):
    """
    Get Ulysses' eigen-velocity from SPICE and transform it to RTN-coordinates

    :param date: datetime object
    :param RF: etspice.ReferenceFrame
    :return: eigen-velocity in RTN-coordinates
    """
    from spiceypy import spkezr, datetime2et
    [x, y, z, vx, vy, vz] = spkezr('ULYSSES', datetime2et(date), RF, 'None',
                                   'SUN')[0]  # cart in km(/s)
    r_sph = cart2spher(np.array([x, y, z]))
    v_sph = cart2spher(np.array(
        [vx, vy,
         vz]))  #does not really make sense but is needed for the rtn-function
    if RF in ["HCI", "HCI_B", "HCI_J"]:  # solar equatorial coord. sys.
        pass
    elif RF in ["ECLIPJ2000", "ECLIPB1950"]:  # ecliptic coord. sys.
        t_epoch = datetime.datetime(
            2000, 1, 1, 12) if RF == "ECLIPJ2000" else datetime.datetime(
                2000, 1, 1, 12) + datetime.timedelta(days=(2433282.42345905 -
                                                           2451545.0))
        r_sph = hc_to_hg(r_sph, ang_ascnode=calc_delta(t_epoch))
        v_sph = hc_to_hg(v_sph, ang_ascnode=calc_delta(t_epoch))
    vR, vT, vN = hg_to_rtn(v_sph, r_sph)
    return (np.array([vR, vT, vN]))
Example #3
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
Example #4
0
def caps_all_anodes(tempdatetime):
    et = spice.datetime2et(tempdatetime)
    sclkdp = spice.sce2c(
        -82, et)  # converts an et to a continuous encoded sc clock (ticks)

    caps_els_anode_vecs = []
    for anodenumber, x in enumerate(np.arange(70, -90, -20)):
        # print(anodenumber, x)
        rotationmatrix_anode = spice.spiceypy.axisar(np.array(
            [1, 0, 0]), x * spice.rpd())  # Get angles for different anodes
        # print("rotationmatrix_anode", rotationmatrix_anode)
        postanode_rotation = spice.vhat(
            spice.mxv(rotationmatrix_anode, -spice.spiceypy.getfov(
                -82821, 20)[2]))  # Apply rotation for anodes
        # print("postanode_rotation", postanode_rotation)

        # print("caps_els_boresight", caps_els_boresight)
        cassini_caps_mat = spice.ckgp(
            -82821, sclkdp, 0, 'CASSINI_CAPS_BASE')[0]  # Get actuation angle
        # print("cassini_caps_mat", cassini_caps_mat)
        cassini_caps_act_vec = spice.mxv(
            cassini_caps_mat, postanode_rotation)  # Rotate with actuator
        # print("Actuating frame", cassini_caps_act_vec)

        CAPS_act_2_titan_cmat = spice.ckgp(
            -82000, sclkdp, 0,
            'IAU_TITAN')[0]  # Find matrix to transform to IAU_TITAN frame
        CAPS_act_2_titan_cmat_transpose = spice.xpose(
            CAPS_act_2_titan_cmat)  # Tranpose matrix
        rotated_vec = spice.mxv(CAPS_act_2_titan_cmat_transpose,
                                cassini_caps_act_vec)  # Apply Matrix
        # print("rotated_vec ", rotated_vec)
        caps_els_anode_vecs.append(rotated_vec)

    return caps_els_anode_vecs
Example #5
0
def soldir_from_titan():  #Only use for one flyby
    for counter, flyby in enumerate(flybys):
        tempdf = windsdf[windsdf['Flyby'] == flyby]
        i = pd.to_datetime(tempdf['Bulk Time']).iloc[0]
        et = spice.datetime2et(i)
        sundir, ltime = spice.spkpos('SUN', et, 'IAU_TITAN', "LT+S", 'TITAN')
    return sundir
Example #6
0
def worker_transform_frame(args):
    """Worker function for transforming 3D coordinates.

    Parameters
    ----------
    args: (np.ndarray, np.ndarray, str, str, np.ndarray, np.ndarray, list)
        Function arguments as tuple.

    Returns
    -------
    np.ndarray
        Transformed 3D coordinates.
    """
    (t, data, frame_from, frame_to, frames, frame_indices, kernels) = args

    if len(heliosat._spice["kernels_loaded"]) == 0:
        heliosat.spice.spice_init()
        heliosat.spice.spice_reload(kernels)

    if frames:
        for i in range(0, len(t)):
            data[i] = spiceypy.mxv(frames[int(frame_indices[i])], data[i])
    else:
        for i in range(0, len(t)):
            data[i] = spiceypy.mxv(spiceypy.pxform(frame_from, frame_to,
                                   spiceypy.datetime2et(t[i])),
                                   data[i])
    return data
def cassini_titan_altlatlon(tempdatetime):
    et = spice.datetime2et(tempdatetime)

    state, ltime = spice.spkezr('CASSINI', et, 'IAU_TITAN', 'NONE', 'TITAN')
    lon, lat, alt = spice.recpgr('TITAN', state[:3], spice.bodvrd('TITAN', 'RADII', 3)[1][0], 2.64e-4)

    return alt, lat * spice.dpr(), lon * spice.dpr()
Example #8
0
    def convert_to_inst(self, coords):
        """
        Convert the given coordinates to the instrument frame

        Parameters
        ----------
        coords : `astropy.coordinate.SkyCoord`
            The coordinates to transform to the instrument frame
        frame : `str`, optional
            The instrument coordinate frame (ILS or OPT)

        Returns
        -------
        tuple
            x and y coordinates
        """
        icrs_coords = coords.transform_to('icrs')
        cart_coords = icrs_coords.represent_as('cartesian')

        et = spiceypy.datetime2et(coords.obstime.to_datetime())
        sc = spiceypy.sce2c(SOLAR_ORBITER_ID, et)
        cmat, sc = spiceypy.ckgp(SOLAR_ORBITER_STIX_ILS_FRAME_ID, sc, 0, 'J2000')
        vec = cmat @ cart_coords.xyz.value
        # Rotate about z so +x towards the Sun
        # vec = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, 1]]) @ vec
        distance, latitude, longitude = spiceypy.reclat(vec.reshape(3))

        y = (latitude + np.pi) * u.rad
        x = (np.pi/2 - longitude) * u.rad

        return x, y
Example #9
0
    def get_position(self, *, date, frame):
        """
        Get the position of SolarOrbiter at the given date in the given coordinate frame.

        Parameters
        ----------
        date : `datetime.datetime`
            Date at which to obtain position
        frame : `str`
            Name of the coordinate frame ('IAU_SUN', 'SOLO_HEE')

        Returns
        -------
        tuple
            The x, y, and z components of the spacecraft position
        """
        et = spiceypy.datetime2et(date)
        (x, y, z), lt = spiceypy.spkpos('SOLO', et, frame, 'None', 'SUN')
        return x*u.km, y*u.km, z*u.km
Example #10
0
    def datetime_to_scet(self, adatetime):
        """
        Convert datetime to SCET.

        Parameters
        ----------
        adatetime : `datetime.datetime` or `astropy.time.Time`
            Time to convert to SCET

        Returns
        -------
        `str`
            SCET of datetime encoded as spacecraft clock string
        """
        if isinstance(adatetime, ApTime):
            adatetime = adatetime.to_datetime()
        et = spiceypy.datetime2et(adatetime)
        scet = spiceypy.sce2s(SOLAR_ORBITER_ID, et)
        return scet
Example #11
0
def cassini_ramdirection_SCframe(tempdatetime, target='CASSINI', frame='J2000', observ='titan', corrtn='NONE',
                                 output=False):
    et = spice.datetime2et(tempdatetime)

    state, ltime = spice.spkezr(target, et, frame, corrtn, observ)
    ramdir = spice.vhat(state[3:6])

    # Gets Attitude
    sclkdp = spice.sce2c(-82, et)  # converts an et to a continuous encoded sc clock (ticks)
    ckgp_output = spice.ckgp(-82000, sclkdp, 0, frame)
    cmat = ckgp_output[0]

    ram_unit = spice.mxv(cmat, ramdir)

    if output:
        print('ET        = {:20.6f}'.format(et))
        print('VX        = {:20.6f}'.format(ram_unit[0]))
        print('VY        = {:20.6f}'.format(ram_unit[1]))
        print('VZ        = {:20.6f}'.format(ram_unit[2]))

    return ram_unit
Example #12
0
    def get_orientation(self, date, frame):
        """
        Get the orientation or roll, pith and yaw of STIX (ILS or OPT).

        Parameters
        ----------
        date : `datetime.datetime`
            Date at which to obtain orientation information
        frame : `str`
            Name of the coordinate frame
        Returns
        -------
        tuple
            Roll, pitch and yaw of the spacecraft
        """
        et = spiceypy.datetime2et(date)
        sc = spiceypy.sce2c(SOLAR_ORBITER_ID, et)
        cmat, sc = spiceypy.ckgp(SOLAR_ORBITER_SRF_FRAME_ID, sc, 0, frame)
        vec = cmat @ np.eye(3)
        roll, pitch, yaw = spiceypy.m2eul(vec, 1, 2, 3)
        roll, pitch, yaw = np.rad2deg([roll, pitch, yaw])*u.deg
        return roll, pitch, yaw
Example #13
0
    def trajectory(self,
                   dt: Union[datetime.datetime, Sequence[datetime.datetime]],
                   reference_frame: str = "J2000",
                   observer: str = "SUN",
                   units: str = "AU") -> np.ndarray:
        logger = logging.getLogger(__name__)

        dt = sanitize_dt(dt)

        traj = np.array(
            spiceypy.spkpos(self.name_naif, spiceypy.datetime2et(dt),
                            reference_frame, "NONE", observer)[0])

        if units == "AU":
            traj *= 6.68459e-9
        elif units == "m":
            traj *= 1e3
        elif units == "km":
            pass
        else:
            logger.exception("unit \"%s\" is not supported", units)
            raise ValueError("unit \"%s\" is not supported", units)

        return traj
Example #14
0
def get_satellite_position(satname, timestamp, kernelpath=None, kernellist=None, refframe="J2000", refobject='SUN', rlonlat=False):
    """
    Returns satellite position from the reference frame of the Sun.
    Files here:
    https://sohowww.nascom.nasa.gov/solarsoft/stereo/gen/data/spice/depm/ahead/
    and
    https://stereo-ssc.nascom.nasa.gov/data/moc_sds/ahead/data_products/ephemerides/

    Parameters
    ==========
    satname : str
        Name of satellite. Recognised strings: 'stereo'
    timestamp : datetime.datetime object / list of dt objs
        Times of positions to return.
    kernelpath : str (default=None)
        Optional path to directory containing kernels, else local "kernels" is used.
    kernellist : str (default=None)
        Optional list of kernels in directory kernelpath, else local list is used.
    refframe : str (default='J2000')
        See SPICE Required Reading Frames. J2000 is standard, HEE/HEEQ are heliocentric.
    refobject : str (default='Sun')
        String for reference onject, e.g. 'Sun' or 'Earth'
    rlonlat : bool (default=False)
        If True, returns coordinates in (r, lon, lat) format, not (x,y,z).

    Returns
    =======
    position : array(x,y,z), list of arrays for multiple timestamps
        Position of satellite in x, y, z with reference frame refframe and Earth as
        observing body. Returns (r,lon,lat) if rlonlat==True.
    """

    if 'stereo' in satname.lower():
        if 'ahead' in satname.lower() or 'a' in satname.lower():
            satstr = 'STEREO AHEAD'
        if 'behind' in satname.lower() or 'b' in satname.lower():
            satstr = 'STEREO BEHIND'
    elif 'dscovr' in satname.lower():
        satstr = 'DSCOVR'
        logger.error("get_satellite_position: DSCOVR kernels not yet implemented!")
    else:
        satstr = satname.upper()
        logger.warning("get_satellite_position: No specific SPICE kernels for {} satellite!".format(satname))

    if kernellist == None:
        kernellist = required_kernels[satstr]

    if kernelpath == None:
        kernelpath = os.path.join('data/kernels')

    logger.info("get_satellite_position: Reading SPICE kernel files from {}".format(kernelpath))

    for k in kernellist:
        spiceypy.furnsh(os.path.join(kernelpath, k))
    time = spiceypy.datetime2et(timestamp)

    position, lighttimes = spiceypy.spkpos(satstr, time, refframe, 'NONE', refobject)

    if rlonlat:
        if len(position) > 3:
            return np.asarray([spiceypy.reclat(x) for x in position])
        return spiceypy.reclat(position)
    else:
        return position
Example #15
0
def transform_frame(t, data, frame_from, frame_to, frame_cadence=None):
    """Transform 3D coordinates from one reference frame to another.

    Parameters
    ----------
    t : list[datetime.datetime]
        Evaluation datetimes.
    data : np.ndarray
        3D vector array.
    frame_from : str
        Source refernce frame.
    frame_to : str
        Target reference frame.
    frame_cadence: float, optional
            Evaluate frame transformation matrix every "frame_cadence" seconds instead of at very
            time point (significant speed up), by default None.

    Returns
    -------
    np.ndarray
        Transformed vector array in target reference frame.

    Raises
    ------
    ValueError
        If data array has the wrong dimensions (must be 2d or 3d)
        or source and target frame are equal.
    """
    if frame_from == frame_to:
        raise ValueError("source frame and target frame are equal")

    # convert timestamps to python datetimes if required
    if not isinstance(t[0], datetime.datetime):
        t = [datetime.datetime.fromtimestamp(_t) for _t in t]

    if frame_cadence:
        frames = int((t[-1] - t[0]).total_seconds() // frame_cadence)
        frame_indices = [np.floor(_) for _ in np.linspace(0, frames, len(t), endpoint=False)]
        time_indices = np.linspace(0, len(t), frames, endpoint=False)

        frames = [spiceypy.pxform(frame_from, frame_to, spiceypy.datetime2et(t[int(i)]))
                  for i in time_indices]
    else:
        frames = None
        frame_indices = None

    if data.ndim == 2:
        if frame_cadence:
            for i in range(0, len(t)):
                data[i] = spiceypy.mxv(frames[int(frame_indices[i])], data[i])
        else:
            for i in range(0, len(t)):
                data[i] = spiceypy.mxv(spiceypy.pxform(frame_from, frame_to,
                                       spiceypy.datetime2et(t[i])),
                                       data[i])

        return data
    elif data.ndim == 3:
        max_workers = min(multiprocessing.cpu_count() * 2, data.shape[1])
        kernels = heliosat._spice["kernels_loaded"]

        with ProcessPoolExecutor(max_workers=max_workers) as executor:
            args = [(t, data[:, i], frame_from, frame_to, frames, frame_indices, kernels)
                    for i in range(data.shape[1])]

            futures = executor.map(worker_transform_frame, args)

        result = np.array([_ for _ in futures])

        return np.swapaxes(result, 0, 1)

    else:
        raise ValueError("data array can only be 2 or 3 dimensional")
#%%

# Set an empty array that will store the distances between the Sun
# and ATLAS
atlas_vecs = []

# Set an empty array that will store the distances between the Sun
# and the Solar Orbiter
solar_orb_vecs = []

# Iterate through the time array (comet ATLAS)
for atlas_time_step in TIME_ARRAY:

    # Compute the ET
    atlas_et = spiceypy.datetime2et(atlas_time_step)

    # Compute the ET corresponding state vector of the comet ATLAS
    atlas_state_vec = spiceypy.conics(ATLAS_SPICE_ORB_EL, atlas_et)

    # Store the position vector
    atlas_vecs.append(atlas_state_vec[:3])

# Iterate through the time array (Solar Orbiter)
for so_time_step in TIME_ARRAY:

    # Compute the ET
    so_et = spiceypy.datetime2et(so_time_step)

    # Compute the state vector of the Solar Orbiter (NAIF ID: -144)
    solar_orb_state_vec, _ = spiceypy.spkgeo(targ=-144, et=so_et, \
Example #17
0
def caps_crosstrack_spice(tempdatetime, windspeed):
    et = spice.datetime2et(tempdatetime)
    sclkdp = spice.sce2c(
        -82, et)  # converts an et to a continuous encoded sc clock (ticks)

    state, ltime = spice.spkezr("CASSINI", et, "IAU_TITAN", "NONE", "titan")
    ramdir = spice.vhat(state[3:6])
    # print("ramdir",ramdir)

    # Gets Attitude
    sclkdp = spice.sce2c(
        -82, et)  # converts an et to a continuous encoded sc clock (ticks)
    ckgp_output = spice.ckgp(-82000, sclkdp, 0, "IAU_TITAN")
    cmat = ckgp_output[0]
    print("cmat", cmat)

    ram_unit = spice.mxv(cmat, ramdir)  # Ram Unit in SC coords
    # print("ram_unit", ram_unit)
    anglediff = spice.vsepg(
        ram_unit[:2], np.array([0, 1, 0]),
        2)  # Find azimuthal angle between normal boresight and ram direction
    # print("anglediff", anglediff * spice.dpr())
    cassini_ram_mat = spice.rotate(-anglediff, 3)
    # print("cassini_ram_mat", cassini_ram_mat)
    # Rotates rotational axis with actuation
    # cassini_caps_mat = spice.ckgp(-82821, sclkdp, 0, 'CASSINI_CAPS_BASE')[0]  # Rotation matrix of actuation
    # print("cassini_caps_mat", cassini_caps_mat)
    anode_rotational_axis = spice.mxv(cassini_ram_mat,
                                      np.array([1, 0,
                                                0]))  # Rotate with actuator
    print("Rotational Axis", anode_rotational_axis)

    rotationmatrix_1 = spice.spiceypy.axisar(anode_rotational_axis,
                                             -70 * spice.rpd())
    rotationmatrix_2 = spice.spiceypy.axisar(anode_rotational_axis,
                                             70 * spice.rpd())

    ram_unit_rotated1 = spice.mxv(rotationmatrix_1, ram_unit)
    ram_unit_rotated2 = spice.mxv(rotationmatrix_2, ram_unit)
    scframe_spiceplane = spice.psv2pl([0, 0, 0], ram_unit_rotated1,
                                      ram_unit_rotated2)
    print("ram_unit", ram_unit, ram_unit_rotated1, ram_unit_rotated2)
    print("SC frame spice normal",
          spice.psv2pl([0, 0, 0], ram_unit_rotated1, ram_unit_rotated2))
    cmat_t = spice.xpose(cmat)
    ram_unit_rotated1_titan = spice.mxv(
        cmat_t, ram_unit_rotated1)  # Transform back to IAU Titan Frame
    ram_unit_rotated2_titan = spice.mxv(
        cmat_t, ram_unit_rotated2)  # Transform back to IAU Titan Frame
    spiceplanenormal = spice.mxv(cmat_t, spice.pl2nvp(scframe_spiceplane)[0])

    # Old method finding normal in titan frame
    # spiceplane = spice.psv2pl(state[:3], ram_unit_rotated1_titan, ram_unit_rotated2_titan)
    # spiceplanenormal = spice.pl2nvp(spiceplane)[0]

    print("SPICE NORMAL", spiceplanenormal)
    # print("Spice normal, sc frame", scframe_spicenormal_titan)

    if windspeed > 0:
        spiceplanenormal = -1 * spiceplanenormal
        print("spice plane fipped", windspeed, spiceplanenormal)

    print("vsep titan frame",
          spice.vsep(ramdir, spiceplanenormal) * spice.dpr())

    return spiceplanenormal, ram_unit_rotated1_titan, ram_unit_rotated2_titan
Example #18
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
Example #19
0
    def get_orientation(dt, frame1='SOLO_SRF', frame2='SOLO_EQUAT_NORM'):
        """
        Get the orientation or roll, pith and yaw of STIX (ILS or OPT).
        Taken from https://github.com/i4Ds/STIXCore/blob/9a765a33f2e924ead669b9b99afc1e41a4d2d8e8/stixcore
        /ephemeris/tests/test_position.py#L28-L40
        Parameters
        ----------
        date : `datetime.datetime`
            Date at which to obtain orientation information
        frame : `str`
            Name of the coordinate frame
        Returns
        -------
        tuple
            Roll, pitch and yaw of the spacecraft

        
   SOLO mission specific generic frames:

      SOLO_SUN_RTN                Sun Solar Orbiter Radial-Tangential-Normal
      SOLO_SOLAR_MHP              S/C-centred mirror helioprojective
      SOLO_IAU_SUN_2009           Sun Body-Fixed based on IAU 2009 report
      SOLO_IAU_SUN_2003           Sun Body-Fixed based on IAU 2003 report
      SOLO_GAE                    Geocentric Aries Ecliptic at J2000 (GAE)
      SOLO_GSE                    Geocentric Solar Ecliptic at J2000 (GSE)
      SOLO_HEE                    Heliocentric Earth Ecliptic at J2000 (HEE)
      SOLO_VSO                    Venus-centric Solar Orbital (VSO)

   Heliospheric Coordinate Frames developed for the NASA STEREO mission:

      SOLO_ECLIPDATE              Mean Ecliptic of Date Frame
      SOLO_HCI                    Heliocentric Inertial Frame
      SOLO_HEE_NASA               Heliocentric Earth Ecliptic Frame
      SOLO_HEEQ                   Heliocentric Earth Equatorial Frame
      SOLO_GEORTN                 Geocentric Radial Tangential Normal Frame

   Heliocentric Generic Frames(*):

      SUN_ARIES_ECL               Heliocentric Aries Ecliptic   (HAE)
      SUN_EARTH_CEQU              Heliocentric Earth Equatorial (HEEQ)
      SUN_EARTH_ECL               Heliocentric Earth Ecliptic   (HEE)
      SUN_INERTIAL                Heliocentric Inertial         (HCI)

   Geocentric Generic Frames:

      EARTH_SUN_ECL   (*)         Geocentric Solar Ecliptic     (GSE)
      EARTH_MECL_MEQX (*)         Earth Mean Ecliptic and Equinox of date
                                  frame (Auxiliary frame for EARTH_SUN_ECL)
      EARTH_MECL_MEQX_J2000       Earth Mean Ecliptic and Equinox at J2000
                                  frame (Auxiliary frame for SOLO_GSE and
                                  SOLO_HEE)


        """
        

        et = sp.datetime2et(dt)
        sc = sp.sce2c(SOLAR_ORBITER_ID, et) #convert to clock ticks
        #tol=sp.sctiks(int(sc), "1:000")
        tol= 1.0
        #cmat, sc= sp.ckgp(SOLAR_ORBITER_SRF_FRAME_ID, sc, tol, 'SOLO_ECLIP_NORM')
        #cmat, sc= sp.ckgp(SOLAR_ORBITER_SRF_FRAME_ID, sc, tol, 'SOLO_EQUAT_NORM')
        frame_id=SOLAR_ORBITER_SRF_FRAME_ID if frame1=='SOLO_SRF' else SOLAR_ORBITER_STIX_OPT_FRAME_D

        cmat, sc= sp.ckgp(frame_id, sc, tol, frame2)
        #get c-matrix orientiation information
        roll, pitch, yaw = sp.m2eul(cmat, 1, 2, 3)
        #matrix to Euler angles

        # Following lines taken from from get_sunspice_roll.pro
        #https://www.heliodocs.com/xdoc/xdoc_list.php?dir=$SSW/packages/sunspice/idl
        #if abs(roll) > 2 *math.pi:
        #    roll=roll-math.copysign(2*math.pi,roll)
        #if abs(pitch) > 0.5* math.pi:
        #    #taken from idl code get_sunspice_roll.pro
        #    pitch = math.copysign(2*math.pi,  pitch) - pitch
        #    yaw = yaw - math.copysign(2*math.pi, yaw)
        #    roll = roll - math.copysign(2*math.pi, roll)
        roll, pitch, yaw = np.rad2deg([roll, pitch, yaw])#convert to arcmin

        #take from Frederic idl code
        #if yaw <0:
        #    yaw+=360
        #yaw = yaw-180
        pitch = -pitch
        roll = -roll


        return (roll, pitch, yaw)
Example #20
0
    def get_flare_spice(sun_x,sun_y,flare_utc, observer='earth'):
        #sun_x, sun_y in unites of arcsec
        date_flare = stix_datetime.utc2datetime(flare_utc)
        et_stix = sp.datetime2et(date_flare)
        flare_coord= [sun_x, sun_y]
        # Obtain the coordinates of Solar Orbiter
        earth_hee_spice, ltime_earth_sun = sp.spkpos('EARTH', et_stix, 
                                         'SOLO_HEE_NASA', #  Reference frame of the output position vector of the object 
                                         'NONE', 'SUN')
        
        earth_hee_spice = earth_hee_spice * u.km
        # Convert the coordinates to HEE
        earth_hee = HeliocentricEarthEcliptic(earth_hee_spice, 
                                              obstime=Time(date_flare).isot, 
                                              representation_type='cartesian')
        solo_hee_spice,ltime_sun_solo = sp.spkpos('SOLO', et_stix, 'SOLO_HEE_NASA', 'NONE', 'SUN')
        solo_hee_spice = solo_hee_spice * u.km
        # Convert the coordinates to HEE
        solo_hee = HeliocentricEarthEcliptic(solo_hee_spice, 
                                         obstime=Time(date_flare).isot, 
                                         representation_type='cartesian')
        if observer=='earth':
            obs_coord=earth_hee#.transform_to(HeliographicStonyhurst(obstime=date_flare))
        else:
            obs_coord=solo_hee#.transform_to(HeliographicStonyhurst(obstime=date_flare) )
                                         
        flare_skycoord= SkyCoord(flare_coord[0]*u.arcsec, 
                              flare_coord[1]*u.arcsec,
                              obstime=date_flare,
                              observer=obs_coord,
                          frame='helioprojective')
        
        flare_hee = flare_skycoord.transform_to(HeliocentricEarthEcliptic(obstime=date_flare))
        v_flare_earth=earth_hee.cartesian-flare_hee.cartesian
        flare_hee_cart=flare_hee.cartesian
        flare_earth_r=np.sqrt(v_flare_earth.x**2+v_flare_earth.y**2+v_flare_earth.z**2)
        
        v_flare_solo=-flare_hee.cartesian+solo_hee.cartesian
        flare_solo_r=np.sqrt(v_flare_solo.x**2+v_flare_solo.y**2+v_flare_solo.z**2)
        ves=solo_hee.cartesian-earth_hee.cartesian
        v_earth_solo=np.sqrt(ves.x**2+ves.y**2+ves.z**2)
        
        flare_earth_t=flare_earth_r.to(u.m) / const.c
        flare_solo_t=flare_solo_r.to(u.m) / const.c
        owlt=v_earth_solo.to(u.m) / const.c

        flare_theta_stix=vsep(flare_hee_cart, v_flare_solo)
        flare_theta_earth=vsep(flare_hee_cart, v_flare_earth)

        return {'flare_earth_lt':flare_earth_t.value,
                'flare_solo_lt':flare_solo_t.value,
                'owlt':owlt.value,
                'dt': (flare_earth_t-flare_solo_t).value,
                'flare_solo_r':flare_solo_r.to(u.au).value,
                'dt_solar_center':ltime_earth_sun-ltime_sun_solo,
                'flare_utc':flare_utc,
                'theta_flare_norm_earth_deg':flare_theta_earth,
                'theta_flare_norm_solo_deg':flare_theta_stix,
                'earth_sun_ltime': ltime_earth_sun,
                'sun_solo_ltime': ltime_sun_solo,
                'observer':observer
    }
Example #21
0
 def get_earth_spice_HEE(datetimes):
     ets=[sp.datetime2et(t) for t in datetimes]
     pos_vel, light_times= sp.spkezr('Earth', ets,      'SOLO_HEE_NASA', 'LT+S',   'Sun')
     positions = np.array(pos_vel)[:, :3] * u.km
     return {'light_times':np.array(light_times),'positions':positions.to(u.au)}
Example #22
0
def get_satellite_position(satname,
                           timestamp,
                           kernelpath=None,
                           kernellist=None,
                           refframe="J2000",
                           refobject='SUN',
                           rlonlat=False):
    """
    Returns satellite position from the reference frame of the Sun.
    Files here:
    https://sohowww.nascom.nasa.gov/solarsoft/stereo/gen/data/spice/depm/ahead/
    and
    https://stereo-ssc.nascom.nasa.gov/data/moc_sds/ahead/data_products/ephemerides/

    Parameters
    ==========
    satname : str
        Name of satellite. Recognised strings: 'stereo'
    timestamp : datetime.datetime object / list of dt objs
        Times of positions to return.
    kernelpath : str (default=None)
        Optional path to directory containing kernels, else local "kernels" is used.
    kernellist : str (default=None)
        Optional list of kernels in directory kernelpath, else local list is used.
    refframe : str (default='J2000')
        See SPICE Required Reading Frames. J2000 is standard, HEE/HEEQ are heliocentric.
    refobject : str (default='Sun')
        String for reference onject, e.g. 'Sun' or 'Earth'
    rlonlat : bool (default=False)
        If True, returns coordinates in (r, lon, lat) format, not (x,y,z).

    Returns
    =======
    position : array(x,y,z), list of arrays for multiple timestamps
        Position of satellite in x, y, z with reference frame refframe and Earth as
        observing body. Returns (r,lon,lat) if rlonlat==True.
    """

    if 'stereo' in satname.lower():
        if 'ahead' in satname.lower() or 'a' in satname.lower():
            satstr = 'STEREO AHEAD'
        if 'behind' in satname.lower() or 'b' in satname.lower():
            satstr = 'STEREO BEHIND'
    elif 'dscovr' in satname.lower():
        satstr = 'DSCOVR'
        logger.error(
            "get_satellite_position: DSCOVR kernels not yet implemented!")
    else:
        satstr = satname.upper()
        logger.warning(
            "get_satellite_position: No specific SPICE kernels for {} satellite!"
            .format(satname))

    if kernellist == None:
        kernellist = required_kernels[satstr]

    if kernelpath == None:
        kernelpath = os.path.join('data/kernels')

    logger.info(
        "get_satellite_position: Reading SPICE kernel files from {}".format(
            kernelpath))

    for k in kernellist:
        spiceypy.furnsh(os.path.join(kernelpath, k))
    time = spiceypy.datetime2et(timestamp)

    position, lighttimes = spiceypy.spkpos(satstr, time, refframe, 'NONE',
                                           refobject)

    if rlonlat:
        if len(position) > 3:
            return np.asarray([spiceypy.reclat(x) for x in position])
        return spiceypy.reclat(position)
    else:
        return position