Esempio n. 1
0
def animate_scenes(sat_mlab_source, gsat_mlab_source, sat_pos_eci, lla_pos,
                   eci_scene, groundtrack_scene):
    increment = 1
    frame_play = 0
    while frame_play <= sat_pos_eci.shape[0]:
        x, y, z = sat_pos_eci[frame_play, :]

        lat = attitude.normalize(np.rad2deg(lla_pos[frame_play, 0]), -90, 90)
        lon = attitude.normalize(np.rad2deg(lla_pos[frame_play, 1]), -180, 180)
        sat_mlab_source.set(x=x, y=y, z=z)
        gsat_mlab_source.set(x=lon, y=lat, z=0)
        frame_play += increment
        yield
Esempio n. 2
0
def nu_solve(r, v, fpa, mu):
    """Single impulse manuever true anomaly 
        nu = nu_solve(r,v,fpa)

     Inputs: 
        - r - magnitude of position vector in km
        - v - magnitude of velocity vector in km
        - fpa - flight path angle in rad -pi/2 < fpa < pi/2
        - mu - gravitational parameter km^3/sec^2

     Outputs: 
        - nu - true anomaly of body in rad 0 < nu < 2*pi

     Dependencies: 
        - none

     Author: 
        - Shankar Kulumani 10 Oct 2012
        - Shankar Kulumani 18 November 2017 
            - in Python

    References
        - AAE532 notes LSN 18
        - AAE532_PS7.pdf
    """
    num = (r * v**2 / mu) * np.cos(fpa) * np.sin(fpa)
    den = (r * v**2 / mu) * np.cos(fpa)**2 - 1

    nu = np.arctan2(num, den)

    nu = attitude.normalize(nu, 0, 2 * np.pi)
    return nu
Esempio n. 3
0
def nu_solve(r, v, fpa, mu):
    """Single impulse manuever true anomaly 
        nu = nu_solve(r,v,fpa)

     Inputs: 
        - r - magnitude of position vector in km
        - v - magnitude of velocity vector in km
        - fpa - flight path angle in rad -pi/2 < fpa < pi/2
        - mu - gravitational parameter km^3/sec^2

     Outputs: 
        - nu - true anomaly of body in rad 0 < nu < 2*pi

     Dependencies: 
        - none

     Author: 
        - Shankar Kulumani 10 Oct 2012
        - Shankar Kulumani 18 November 2017 
            - in Python

    References
        - AAE532 notes LSN 18
        - AAE532_PS7.pdf
    """
    num = (r * v**2 / mu) * np.cos(fpa) * np.sin(fpa)
    den = (r * v**2 / mu) * np.cos(fpa)**2 - 1

    nu = np.arctan2(num, den)

    nu = attitude.normalize(nu, 0, 2*np.pi)
    return nu
Esempio n. 4
0
def asteroid_epoch(ast_flag):
    """
    This holds the orbital elements for the asteroids as taken from JPL

    Return the state at the JD epoch for use in a propogate function
    """

    if ast_flag == 0:
        # 2008 EV5
        a = 0.9582899238313918
        ecc = 0.08348599378460778
        p = a * (1 - ecc**2)
        inc = np.deg2rad(7.436787362690259)
        raan = np.deg2rad(93.39122898787916)
        argp = np.deg2rad(234.8245876826614)
        M = np.deg2rad(3.409187469072454)
        JD_epoch = 2457800.5
    elif ast_flag == 1:
        # Itokawa
        a = 1.324163617639197
        ecc = 0.28011765678781
        p = a * (1 - ecc**2)
        inc = np.deg2rad(1.62145641293925)
        raan = np.deg2rad(69.07992986350325)
        argp = np.deg2rad(162.8034822691509)
        M = np.deg2rad(131.4340297670125)
        JD_epoch = 2457800.5
    elif ast_flag == 2:
        # Bennu
        a = 1.126391026007489
        ecc = 0.2037451112033579
        p = a * (1 - ecc**2)
        inc = np.deg2rad(6.034939195483961)
        raan = np.deg2rad(2.060867837066797)
        argp = np.deg2rad(66.22306857848962)
        M = np.deg2rad(101.7039476994243)
        JD_epoch = 2455562.5
    else:
        print("No such asteroid defined yet. Using Itokawa so nothing breaks instead")

        # Itokawa
        a = 1.324163617639197
        ecc = 0.28011765678781
        p = a * (1 - ecc**2)
        inc = np.deg2rad(1.62145641293925)
        raan = np.deg2rad(69.07992986350325)
        argp = np.deg2rad(162.8034822691509)
        M = np.deg2rad(131.4340297670125)
        JD_epoch = 2457800.5

    # convert M to nu for later and output the coe
    E, nu, count = kepler_eq_E(M, ecc)

    epoch = (p, ecc, inc, raan, argp, attitude.normalize(nu, 0, 2 * np.pi), JD_epoch)

    return epoch
Esempio n. 5
0
def draw_groundtrack_sat(sat_lla, scene):
    lat = np.rad2deg(sat_lla[0])
    lon = attitude.normalize(np.rad2deg(sat_lla[1]), -180, 180)
    alt = sat_lla[2]
    sat = mlab.points3d(lon,
                        lat,
                        0,
                        1,
                        figure=scene.mayavi_scene,
                        scale_factor=10,
                        color=(1, 0, 0))

    return sat
Esempio n. 6
0
def gstlst(jd, site_lon):
    """This program calculates GST and LST given the Julian Day and site
    longitude.

    Author:  Shankar Kulumani GWU 18 Jun 2017

    Inputs:
        jd : float
            Julian Day
        sitlon : site longitude (radians)
    Outputs:
        gst - Greenwich Sidereal Time
        lst - Local Sidereal Time

    Constants: None

    Coupling: 

    Modifications:
        18 Jun 2017 - use algorithm 15 from Vallado

    References:
        Astro 321 PREDICT
        Vallado Algorithm 15 
    """
    deg2rad = np.pi / 180
    hour2sec = 3600
    sec2deg = 15 / 3600

    Tut1 = (jd - 2451545.0) / 36525

    gst = (- 6.2e-6 * Tut1 * Tut1 * Tut1 + 0.093104 * Tut1 * Tut1
           + (876600.0 * 3600.0 + 8640184.812866) * Tut1 + 67310.54841)
    gst = (gst % 86400) * sec2deg

    gst = attitude.normalize(gst * deg2rad, 0, 2 * np.pi)
    lst = attitude.normalize(gst[0] + site_lon, 0, 2 * np.pi)

    return gst[0], lst[0]
Esempio n. 7
0
class TestECEF2ECI():
    """Example pg.106 in BMW
    """
    lon = np.deg2rad(-57.296)
    lat = 0
    alt = 6.378  # kilometer above equator
    date = (1970, 1, 2, 6, 0, 0)
    jd, _ = time.date2jd(date[0], date[1], date[2], date[3], date[4], date[5])
    gst0_exp = 1.749333
    gst_exp = attitude.normalize(9.6245, 0, 2 * np.pi)
    lst_exp = attitude.normalize(8.6245, 0, 2 * np.pi)

    eci_exp = np.array([-0.697 * 6378.137, 0.718 * 6378.137, 0])

    gst0 = time.gsttime0(date[0])
    gst, lst = time.gstlst(jd, lon)

    eci = geodetic.site2eci(lat, alt, lst)
    ecef = geodetic.lla2ecef(lat, lon, alt)
    Reci2ecef = transform.dcm_eci2ecef(jd)
    eci_from_ecef = Reci2ecef.T.dot(ecef)

    def test_gst0(self):
        np.testing.assert_allclose(self.gst0, self.gst0_exp, rtol=1e-4)

    def test_gst(self):
        np.testing.assert_allclose(self.gst, self.gst_exp, rtol=1e-4)

    def test_lst(self):
        np.testing.assert_allclose(self.lst, self.lst_exp, rtol=1e-3)

    def test_eci(self):
        np.testing.assert_allclose(self.eci, self.eci_exp, rtol=1e-2)

    def test_eci_from_ecef(self):
        np.testing.assert_allclose(self.eci_from_ecef, self.eci_exp, rtol=1e-2)
Esempio n. 8
0
def gsttime0(yr):
    """This function finds the Greenwich Sidereal time at the beginning of a
    year.  This formula is derived from the Astronomical Almanac and is good
    only for 0 hr UT, 1 Jan of a year.

    Algorithm     : Find the Julian Date Ref 4713 BC
                    Perform expansion calculation to obtain the answer
                    Check the answer for the correct quadrant and size

    Author        : Capt Dave Vallado  USAFA/DFAS  719-472-4109   12 Feb 1989
    In Ada        : Dr Ron Lisowski    USAFA/DFAS  719-472-4110   17 May 1995
    In MatLab     : LtCol Thomas Yoder USAFA/DFAS  719-333-4110   Spring 2000
    Remove Global : Shankar Kulumani 7 Dec 2014
    In Python     : Shankar Kulumani   GWU         630-336-6257   10 Jun 2017

    Inputs        :
        Yr          - Year                                 1988, 1989, etc.

    OutPuts       :
        GST0        - Returned Greenwich Sidereal Time     0 to 2Pi rad

    Locals        :
        JD          - Julian Date                          days from 4713 B.C.
        Tu          - Julian Centuries from 1 Jan 2000

    Constants     :
        TwoPI         Two times Pi (DFASmath.m constant)

    Coupling      :
        mod			  MatLab modulus function

    References    :
        1989 Astronomical Almanac pg. B6
        Escobal       pg. 18 - 21
        Explanatory Supplement pg. 73-75
        Kaplan        pg. 330-332
        BMW           pg. 103-104
    """

    TwoPI = 2 * np.pi

    JD = (367.0 * yr - np.fix(7.0 * (yr + np.fix(10.0 / 12.0)) * 0.25) +
          np.fix(275.0 / 9.0) + 1721014.5)
    Tu = (np.fix(JD) + 0.5 - 2451545.0) / 36525.0
    GST0 = 1.753368559 + 628.3319705 * Tu + 6.770708127E-06 * Tu * Tu
    GST0 = attitude.normalize(GST0, 0, 2 * np.pi)
    return GST0
Esempio n. 9
0
def asteroid_coe(JD_curr, ast_flag):
    """
        Output the current COE for the chosen asteroid
    """

    # load the asteroid COE at the epoch
    (p, ecc, inc, raan, argp, nu_0, JD_epoch) = asteroid_epoch(ast_flag)

    # compute the delta t
    delta_t = (JD_curr - JD_epoch) * 86400
    mu = 1.32712440018e20  # m^3 / s^2
    mu = 1 / 149597870700**3 * mu  # au^3 / sec^2

    # propogate to the current JD_curr
    (E_f, M_f, nu_f) = tof_delta_t(p, ecc, mu, nu_0, delta_t)

    # output the current COE
    coe = (p, ecc, inc, raan, argp, attitude.normalize(nu_f, 0, 2 * np.pi))

    return coe
Esempio n. 10
0
def rvfpa2orbit_el(mag_r, mag_v, fpa, mu):
    """Converts R,V, and FPA to orbital elements

    a, p, ecc, nu = rvfpa2orbit_el(mag_r, mag_v, fpa, mu)

    Inputs: 
        - List all inputs into function

    Outputs: 
        - List/describe outputs of function

    Dependencies: 
        - list dependent m files required for function

    Author: 
        - Shankar Kulumani 7 Oct 2012
        - Shankar Kulumani 5 Nov 2012
            - added ascending/descending logic to true anomaly
        - Shankar Kulumani 16 November 2017
            - move to python
    References
        - AAE532_PS6.pdf
        """
    # calculate semi major axis using energy relationship
    a = -mu / (mag_v**2 / 2 - mu / mag_r) / 2
    sme = -mu / (2 * a)

    # find semi latus rectum and eccentricity
    mag_h = mag_r * mag_v * np.cos(fpa)
    p = mag_h**2 / mu

    ecc = np.sqrt(-p / a + 1)

    # find true anomaly using conic equation
    nu = np.arccos((p / mag_r - 1) / ecc)
    if fpa < 0:
        nu = - nu
    nu = attitude.normalize(nu, 0, 2 * np.pi)[0]
    # orbit_el(p,ecc,0,0,0,nu,mu,'true')

    return a, p, ecc, nu
Esempio n. 11
0
def ecef2lla(ecef, r=6378.137, ee=8.1819190842622e-2):
    """Converts a ECEF vector to the equivalent Lat, longitude and Altitude
    above the reference ellipsoid

    """
    twopi = 2 * np.pi
    tol = 1e-6

    norm_vec = np.linalg.norm(ecef)

    temp = np.sqrt(ecef[0]**2 + ecef[1]**2)
    if temp < tol:
        rtasc = np.sign(ecef[2]) * np.pi * 0.5
    else:
        rtasc = np.arctan2(ecef[1], ecef[0])

    lon = rtasc
    lon = attitude.normalize(lon, 0, 2 * np.pi)

    decl = np.arcsin(ecef[2] / norm_vec)
    latgd = decl

    # iterate to find geodetic latitude
    i = 1
    olddelta = latgd + 10.0
    while np.absolute(olddelta - latgd) >= tol and i < 10:
        oldelta = latgd
        sintemp = np.sin(latgd)
        c = r / np.sqrt(1.0 - ee**2 * sintemp**2)
        latgd = np.arctan2(ecef[2] + c * ee**2 * sintemp, temp)
        i = i + 1

    # calculate the height
    if (np.pi / 2 - np.absolute(latgd)) > np.pi / 180:
        hellp = temp / np.cos(latgd) - c
    else:
        s = c * (1 - ee**2)
        hellp = ecef[2] / np.sin(latgd) - s

    latgc = gd2gc(latgd, ee**2)
    return latgc, latgd, lon, hellp
Esempio n. 12
0
def rvfpa2orbit_el(mag_r, mag_v, fpa, mu):
    """Converts R,V, and FPA to orbital elements

    a, p, ecc, nu = rvfpa2orbit_el(mag_r, mag_v, fpa, mu)

    Inputs: 
        - List all inputs into function

    Outputs: 
        - List/describe outputs of function

    Dependencies: 
        - list dependent m files required for function

    Author: 
        - Shankar Kulumani 7 Oct 2012
        - Shankar Kulumani 5 Nov 2012
            - added ascending/descending logic to true anomaly
        - Shankar Kulumani 16 November 2017
            - move to python
    References
        - AAE532_PS6.pdf
        """
    # calculate semi major axis using energy relationship
    a = -mu / (mag_v**2 / 2 - mu / mag_r) / 2
    sme = -mu / (2 * a)

    # find semi latus rectum and eccentricity
    mag_h = mag_r * mag_v * np.cos(fpa)
    p = mag_h**2 / mu

    ecc = np.sqrt(-p / a + 1)

    # find true anomaly using conic equation
    nu = np.arccos((p / mag_r - 1) / ecc)
    if fpa < 0:
        nu = -nu
    nu = attitude.normalize(nu, 0, 2 * np.pi)[0]
    # orbit_el(p,ecc,0,0,0,nu,mu,'true')

    return a, p, ecc, nu
Esempio n. 13
0
def ecef2lla(ecef, r=6378.137, ee=8.1819190842622e-2):
    """Converts a ECEF vector to the equivalent Lat, longitude and Altitude
    above the reference ellipsoid

    """
    twopi = 2 * np.pi
    tol = 1e-6

    norm_vec = np.linalg.norm(ecef)

    temp = np.sqrt(ecef[0]**2 + ecef[1]**2)
    if temp < tol:
        rtasc = np.sign(ecef[2]) * np.pi * 0.5
    else:
        rtasc = np.arctan2(ecef[1], ecef[0])

    lon = rtasc
    lon = attitude.normalize(lon, 0, 2 * np.pi)

    decl = np.arcsin(ecef[2] / norm_vec)
    latgd = decl

    # iterate to find geodetic latitude
    i = 1
    olddelta = latgd + 10.0
    while np.absolute(olddelta - latgd) >= tol and i < 10:
        oldelta = latgd
        sintemp = np.sin(latgd)
        c = r / np.sqrt(1.0 - ee**2 * sintemp**2)
        latgd = np.arctan2(ecef[2] + c * ee**2 * sintemp, temp)
        i = i + 1

    # calculate the height
    if (np.pi / 2 - np.absolute(latgd)) > np.pi / 180:
        hellp = temp / np.cos(latgd) - c
    else:
        s = c * (1 - ee**2)
        hellp = ecef[2] / np.sin(latgd) - s

    latgc = gd2gc(latgd, ee**2)
    return latgc, latgd, lon, hellp
Esempio n. 14
0
def rhoazel(sat_eci, site_eci, site_lat, site_lst):
    """
    This function calculates the topcentric range,azimuth and elevation from
    the site vector and satellite position vector.

    Author:   C2C Shankar Kulumani   USAFA/CS-19   719-333-4741

    Inputs:
        sat_eci - satellite ECI position vector (km)
        site_eci - site ECI position vector (km)
        site_lat - site geodetic latitude (radians)
        site_lst - site local sidereal time (radians)

    Outputs:
        rho - range (km)
        az - asimuth (radians)
        el - elevation (radians)

    Globals: None

    Constants: None

    Coupling: 

    References:
        Astro 321 Predict LSN 22 
    """

    site2sat_eci = sat_eci - site_eci

    site2sat_sez = attitude.rot3(-site_lst).dot(site2sat_eci)
    site2sat_sez = attitude.rot2(-(np.pi / 2 - site_lat)).dot(site2sat_sez)

    rho = np.linalg.norm(site2sat_sez)
    el = np.arcsin(site2sat_sez[2] / rho)
    az = attitude.normalize(np.arctan2(site2sat_sez[1], -site2sat_sez[0]), 0,
                            2 * np.pi)[0]

    return rho, az, el
Esempio n. 15
0
def rhoazel(sat_eci, site_eci, site_lat, site_lst):
    """
    This function calculates the topcentric range,azimuth and elevation from
    the site vector and satellite position vector.

    Author:   C2C Shankar Kulumani   USAFA/CS-19   719-333-4741

    Inputs:
        sat_eci - satellite ECI position vector (km)
        site_eci - site ECI position vector (km)
        site_lat - site geodetic latitude (radians)
        site_lst - site local sidereal time (radians)

    Outputs:
        rho - range (km)
        az - asimuth (radians)
        el - elevation (radians)

    Globals: None

    Constants: None

    Coupling: 

    References:
        Astro 321 Predict LSN 22 
    """

    site2sat_eci = sat_eci - site_eci

    site2sat_sez = attitude.rot3(-site_lst).dot(site2sat_eci)
    site2sat_sez = attitude.rot2(-(np.pi / 2 - site_lat)).dot(site2sat_sez)

    rho = np.linalg.norm(site2sat_sez)
    el = np.arcsin(site2sat_sez[2] / rho)
    az = attitude.normalize(np.arctan2(site2sat_sez[1], -site2sat_sez[0]), 0,
                            2 * np.pi)[0]

    return rho, az, el
Esempio n. 16
0
def kepler_eq_E(M_in, ecc_in):
    """Solve Kepler's Equation for all orbit types

    (E, nu, count) = kepler_eq_E(M, ecc)

    Purpose:
       - This function solves Kepler's equation for eccentric anomaly
       given a mean anomaly using a newton-rapson method.
           - Will work for elliptical/parabolic/hyperbolic orbits

    Inputs:
       - M - mean anomaly in rad -2*pi < M < 2*pi
       - ecc - eccentricity 0 < ecc < inf

    Outputs:
       - E - eccentric anomaly in rad 0 < E < 2*pi
       - nu - true anomaly in rad 0 < nu < 2*pi
       - count - number of iterations to converge

    Dependencies:
       - numpy - everything needs numpy
       - kinematics.attitude.normalize - normalize an angle

    Author:
       - Shankar Kulumani 15 Sept 2012
           - rewritten from code from USAFA
           - solve for elliptical orbits add others later
       - Shankar Kulumani 29 Sept 2012
           - added parabolic/hyperbolic functionality
       - Shankar Kulumani 7 Dec 2014
          - added loop for vector inputs
       - Shankar Kulumani 2 Dec 2016
          - converted to python and removed the vector inputs

    References
       - USAFA Astro 321 LSN 24-25
       - Vallado 3rd Ed pg 72
    """
    tol = 1e-9
    max_iter = 50

    E_out = []
    nu_out = []
    count_out = []

    if not hasattr(M_in, "__iter__"):
        M_in = [M_in]

    if not hasattr(ecc_in, "__iter__"):
        ecc_in = [ecc_in]

    for M, ecc in zip(M_in, ecc_in):
        # eccentricity check
        """
            HYPERBOLIC ORBIT
        """
        if ecc - 1.0 > tol:  # eccentricity logic
            # initial guess
            if ecc < 1.6:  # initial guess logic
                if M < 0.0 and (M > -np.pi or M > np.pi):
                    E_0 = M - ecc
                else:
                    E_0 = M + ecc

            else:
                if ecc < 3.6 and np.absolute(M) > np.pi:
                    E_0 = M - np.sign(M) * ecc
                else:
                    E_0 = M / (ecc - 1.0)

            # netwon's method iteration to find hyperbolic anomaly
            count = 1
            E_1 = E_0 + ((M - ecc * np.sinh(E_0) + E_0) /
                         (ecc * np.cosh(E_0) - 1.0))
            while ((np.absolute(E_1 - E_0) > tol) and (count <= max_iter)):
                E_0 = E_1
                E_1 = E_0 + ((M - ecc * np.sinh(E_0) + E_0) /
                             (ecc * np.cosh(E_0) - 1.0))
                count = count + 1

            E = E_0
            # find true anomaly
            sinv = -(np.sqrt(ecc * ecc - 1.0) * np.sinh(E_1)) / \
                (1.0 - ecc * np.cosh(E_1))
            cosv = (np.cosh(E_1) - ecc) / (1.0 - ecc * np.cosh(E_1))
            nu = np.arctan2(sinv, cosv)
        else:
            """
                PARABOLIC
            """
            if np.absolute(ecc - 1.0) < tol:  # parabolic logic
                count = 1

                S = 0.5 * (np.pi / 2 - np.arctan(1.5 * M))
                W = np.arctan(np.tan(S)**(1.0 / 3.0))

                E = 2.0 * 1.0 / np.tan(2.0 * W)

                nu = 2.0 * np.arctan(E)
            else:
                """
                    ELLIPTICAl
                """
                if ecc > tol:  # elliptical logic

                    # determine intial guess for iteration
                    if M > -np.pi and (M < 0 or M > np.pi):
                        E_0 = M - ecc
                    else:
                        E_0 = M + ecc

                    # newton's method iteration to find eccentric anomaly
                    count = 1
                    E_1 = E_0 + (M - E_0 + ecc * np.sin(E_0)) / \
                        (1.0 - ecc * np.cos(E_0))
                    while ((np.absolute(E_1 - E_0) > tol)
                           and (count <= max_iter)):
                        count = count + 1
                        E_0 = E_1
                        E_1 = E_0 + (M - E_0 + ecc * np.sin(E_0)) / \
                            (1.0 - ecc * np.cos(E_0))

                    E = E_0

                    # find true anomaly
                    sinv = (np.sqrt(1.0 - ecc * ecc) * np.sin(E_1)) / \
                        (1.0 - ecc * np.cos(E_1))
                    cosv = (np.cos(E_1) - ecc) / (1.0 - ecc * np.cos(E_1))
                    nu = np.arctan2(sinv, cosv)
                else:
                    """
                        CIRCULAR
                    """
                    # -------------------- circular -------------------
                    count = 0
                    nu = M
                    E = M

        E_out.append(E)
        nu_out.append(attitude.normalize(nu, 0, 2 * np.pi))
        count_out.append(count)

    return (np.squeeze(E_out), np.squeeze(nu_out), np.squeeze(count_out))
Esempio n. 17
0
def nu2anom(nu, ecc):
    """Calculates the eccentric and mean anomaly given eccentricity and true
    anomaly

    ( E, M ) = ecc_anomaly(nu,ecc)

    Inputs:
        - nu - true anomaly in rad -2*pi < nu < 2*pi
        - ecc - eccentricity of orbit 0 < ecc < inf

    Outputs:
        - E - (elliptical/parabolic/hyperbolic) eccentric anomaly in rad
            0 < E < 2*pi
        - M - mean anomaly in rad 0 < M < 2*pi

    Dependencies:
        - numpy - everything is dependent on numpy
        - kinematics.attitude.normalize - normalize angles
    
    Notes
    -----
    This function is valid for all orbit types. 

    Author:
        - Shankar Kulumani 20 Nov 2017
            - only now realized I already implemented other orbit types
        - Shankar Kulumani 5 Dec 2016
            - Convert to python
        - Shankar Kulumani 15 Sept 2012
            - modified from USAFA code and notes from AAE532
            - only elliptical case will add other later
        - Shankar Kulumani 17 Sept 2012
            - added rev check to reduce angle btwn 0 and 2*pi

    References
        - AAE532 notes
        - Vallado 3rd Ed
    """
    small = 1e-9

    if ecc <= small:  # circular
        E = nu
        M = nu
    elif small < ecc and ecc <= 1 - small:  # elliptical
        sine = (np.sqrt(1.0 - ecc * ecc) * np.sin(nu)) / \
            (1.0 + ecc * np.cos(nu))
        cose = (ecc + np.cos(nu)) / (1.0 + ecc * np.cos(nu))

        E = np.arctan2(sine, cose)
        M = E - ecc * np.sin(E)

        E = attitude.normalize(E, 0, 2 * np.pi)
        M = attitude.normalize(M, 0, 2 * np.pi)

    elif np.absolute(ecc - 1) <= small:  # parabolic
        B = np.tan(nu / 2)

        E = B
        M = B + 1.0 / 3 * B**3
        # TODO: Need to check if I need to do quadrant checks for parabolic or hyperbolic cases
        # E = revcheck(E);
        # M = revcheck(M);
    elif ecc > 1 + small:  # hyperbolic
        sine = (np.sqrt(ecc**2 - 1) * np.sin(nu)) / \
            (1.0 + ecc * np.cos(nu))
        H = np.arcsinh(sine)
        E = H
        M = ecc * np.sinh(H) - H

        # E = revcheck(E);
        # M = revcheck(M);
    else:
        print("Eccentricity is out of bounds 0 < ecc < inf")

    return (E, M)
Esempio n. 18
0
    def tle_update(self, jd_span, mu=398600.5):
        """Update the state of satellite given a JD time span

        This procedure uses the method of general perturbations to update
        classical elements from epoch to a future time for inclined elliptical
        orbits.  It includes the effects due to first order secular rates
        (second order for mean motion) caused by drag and J2.

        Author:   C2C Shankar Kulumani   USAFA/CS-19   719-333-4741

        Inputs:
            deltat - elapsed time since epoch (sec)
            n0 - mean motion at epoch (rad/sec)
            ndot2 - mean motion rate divided by 2 (rad/sec^2)
            ecc0 - eccentricity at epoch (unitless)
            eccdot - eccentricity rate (1/sec)
            raan0 - RAAN at epoch (rad)
            raandot - RAAN rate (rad/sec)
            argp0 - argument of periapsis at epoch (rad)
            argdot - argument of periapsis rate (rad/sec)
            mean0 - mean anomaly at epoch

        Outputs:
            n - mean motion at epoch + deltat (rad/sec)
            ecc -  eccentricity at epoch + deltat (unitless)
            raan - RAAN at epoch + deltat (rad)
            argp - argument of periapsis at epoch + deltat (rad)
            nu -  true anomaly at epoch + deltat (rad)

        Globals: None

        Constants: None

        Coupling:
            newton.m

        References:
            Astro 321 PREDICT LSN 24-25
        """

        deltat = (jd_span - self.epoch_jd) * day2sec

        # epoch orbit parameters
        n0 = self.n0
        ecc0 = self.ecc0
        ndot2 = self.ndot2
        nddot6 = self.nddot6
        raan0 = self.raan0
        argp0 = self.argp0
        mean0 = self.mean0
        inc0 = self.inc0
        adot = self.adot
        eccdot = self.eccdot
        raandot = self.raandot
        argpdot = self.argpdot

        _, nu0, _ = kepler.kepler_eq_E(mean0, ecc0)
        a0 = kepler.n2a(n0, mu)
        M0, E0 = kepler.nu2anom(nu0, ecc0)

        # propogated elements
        a1 = a0 + adot * deltat # either use this or compute a1 from n1 instead
        ecc1 = ecc0 + eccdot * deltat
        raan1 = raan0 + raandot * deltat
        argp1 = argp0 + argpdot * deltat
        inc1 = inc0 * np.ones_like(ecc1)
        n1 = n0 + ndot2 * 2 * deltat
        # a1 = kepler.n2a(n1, mu)
        p1 = kepler.semilatus_rectum(a1, ecc1)

        M1 = M0 + n0 * deltat+ ndot2 *deltat**2 + nddot6 *deltat**3
        M1 = attitude.normalize(M1, 0, 2 * np.pi)
        E1, nu1, _ = kepler.kepler_eq_E(M1, ecc1)

        # convert to vectors
        r_eci, v_eci, _, _ = kepler.coe2rv(p1, ecc1, inc1, raan1, argp1, nu1, mu)

        # save all of the variables to the object
        self.jd_span = jd_span
        self.coe = COE(n=n1, ecc=ecc1, raan=raan1, argp=argp1, mean=M1,
                       E=E1, nu=nu1, a=a1, p=p1, inc=inc1)

        self.r_eci = r_eci
        self.v_eci = v_eci

        return 0
Esempio n. 19
0
def minenergy(r1, r2, r_body, mu_body, direction):
    """Lambert minimum energy ellipse

   ( v1 v2 a p ecc ) = lambert_minenergy(r1,r2,r_body,mu_body)

   Purpose: 
       - Finds the minimum energy solution to lamberts problems given two
       position vectors.

   Inputs: 
       - r1 - initial position vector in km (1x3 or 3x1)
       - r2 - final position vector in km (1x3 or 3x1)
       - r_body - radius of central body in km (check for collision)
       - mu_body - gravitational parameter of central body in km^3/sec^2
       - direction - 'long' or 'short' direction of travel 
   Outputs: 
       - v1 - initial velocity vector in km/sec
       - v2 - final velocity vector in km/sec
       - a - semimajor axis of transfer ellipse in km
       - p - semiparameter of transfer ellipse in km
       - ecc - eccentricity of transfer ellipse
       - F - position vector of vacant focus in km 
       
   Dependencies: 
       - fg_nu - calculates velocity vectors using f and g functions
       - crash_check - check for crash into central body

   Author: 
       - Shankar Kulumani 5 Nov 2012
       - Shankar Kulumani 6 Nov 2012
           - added vacant focus calculation
       - Shankar Kulumani 14 Nov 2012
           - added crash check
        - Shankar Kulumani 13 Dec 2017
            - now in python

   References
       - AAE532 Notes LSN 30-33
       - Vallado 3rd edition
    """
    # find transfer angle
    mag_r1 = np.linalg.norm(r1)
    mag_r2 = np.linalg.norm(r2)
    cos_nu = np.dot(r1,r2)/(mag_r1*mag_r2)

    if direction == 'short':
        sin_nu = np.linalg.norm(np.cross(r1,r2))/(mag_r1*mag_r2)
    elif direction == 'long':
        sin_nu = -np.linalg.norm(np.cross(r1,r2))/(mag_r1*mag_r2)

    delta_nu = np.arctan2(sin_nu,cos_nu)
    # TODO Check on angle range for delta nu
    delta_nu = attitude.normalize(delta_nu, 0, 2* np.pi)

    # Intermediate parameters

    # c = sqrt(mag_r1^2+mag_r2^2-2*mag_r1*mag_r2*cos(delta_nu)); % Chord
    c_vec = r2-r1
    c = np.linalg.norm(c_vec)
    s = (mag_r1+mag_r2+c)/2 # Semiperimeter
    a = s/2
    # p = mag_r1*mag_r2/c*(1-cos(delta_nu)); % Semiparameter?

    alpha = 2*np.arcsin(np.sqrt(s/(2*a)))
    beta = 2*np.arcsin(np.sqrt((s-c)/(2*a)))

    p = 4*a*(s-mag_r1)*(s-mag_r2)/c**2*(np.sin(1/2*(alpha + beta))**2)

    # Transfer orbit physical properties

    ecc = np.sqrt(1-2*p/s)
    # ecc = c/(2*a);

    # vacant focus
    F = (2*a-mag_r1)*c_vec/c + r1
    # F = (2*a_m-norm(r1))*-c/norm(c) + r1

    # find velocity at each point
    # V0 = (sqrt(mu*p)/(r0*r*sin(v)))*(R - (1 - r/p*(1-cos(v)))*R0);
    ( v1, v2, f, g, f_dot, g_dot ) = kepler.fg_velocity(r1,r2,delta_nu,p,mu_body)

    # Transfer orbit time
    B = 2*np.arcsin(np.sqrt((s-c)/s))
    if delta_nu > np.pi:
        B = -B
    tof = np.sqrt(a**3/mu_body)*(np.pi - B + np.sin(B))

    # Check for crash
    crash_check(r1,v1,r2,v2,mu_body,r_body)

    return v1, v2, tof, a, p, ecc
Esempio n. 20
0
def rv2rhoazel(r_sat_eci, v_sat_eci, lat, lon, alt, jd):
    """
    This function calculates the topcentric range,azimuth and elevation from
    the site vector and satellite position vector.

    Author:   C2C Shankar Kulumani   USAFA/CS-19   719-333-4741

    Inputs:
        sat_eci - satellite ECI position vector (km)
        site_eci - site ECI position vector (km)
        site_lat - site geodetic latitude (radians)
        site_lst - site local sidereal time (radians)

    Outputs:
        rho - range (km)
        az - asimuth (radians)
        el - elevation (radians)

    Globals: None

    Constants: None

    Coupling:

    References:
        Astro 321 Predict LSN 22
        Vallado Algorithm 27
    """
    small = constants.small
    halfpi = constants.halfpi

    # get site in ECEF
    r_site_ecef = lla2ecef(lat, lon, alt)

    # convert sat and site to ecef
    dcm_eci2ecef = transform.dcm_eci2ecef(jd)
    omega_earth = np.array([0, 0, constants.earth.omega])

    r_sat_ecef = dcm_eci2ecef.dot(r_sat_eci)
    v_sat_ecef = dcm_eci2ecef.dot(v_sat_eci) - np.cross(
        omega_earth, r_sat_ecef)

    # find relative vector in ecef frame
    rho_ecef = r_sat_ecef - r_site_ecef
    drho_ecef = v_sat_ecef - np.zeros_like(v_sat_ecef)  # site isn't moving
    rho = np.linalg.norm(rho_ecef)

    # convert to SEZ coordinate frame
    dcm_ecef2sez = attitude.rot2(np.pi / 2 - lat,
                                 'r').dot(attitude.rot3(lon, 'r'))
    rho_sez = dcm_ecef2sez.dot(rho_ecef)
    drho_sez = dcm_ecef2sez.dot(drho_ecef)

    # find azimuth and eleveation
    temp = np.sqrt(rho_sez[0]**2 + rho_sez[1]**2)

    if temp < small:  # directly over the north pole
        el = np.sign(rho_sez[2]) * halfpi  # \pm 90 deg
    else:
        mag_rho_sez = np.linalg.norm(rho_sez)
        el = np.arcsin(rho_sez[2] / mag_rho_sez)

    if temp < small:
        az = np.arctan2(drho_sez[1], -drho_sez[0])
    else:
        az = np.arctan2(rho_sez[1] / temp, -rho_sez[0] / temp)

    # range, azimuth and elevation rates
    drho = np.dot(rho_sez, drho_sez) / rho

    if np.absolute(temp**2) > small:
        daz = (drho_sez[0] * rho_sez[1] - drho_sez[1] * rho_sez[0]) / temp**2
    else:
        daz = 0

    if np.absolute(temp) > small:
        dele = (drho_sez[2] - drho * np.sin(el)) / temp
    else:
        dele = 0

    az = attitude.normalize(az, 0, constants.twopi)
    return rho, az, el, drho, daz, dele
Esempio n. 21
0
def planet_coe(JD_curr, planet_flag):
    r"""Orbital elements for any of the major solar system bodies

    Given the Julian date this function will output the approximate orbital
    elements for any of the solar system bodies. It uses an analytic
    approximation for their positions and propogates the orbital elements to
    the desired date.

    Parameters
    ----------
    jd : float
        Julian date for the epoch
    planet_flag : int
        Number 0 to 8 for each of the planets (Pluto forever)

    Returns
    -------
    coe : tuple
        p : float
            Semiparameter of orbit in AU
        ecc: float
            Eccentricity of orbit
        inc : float
            Inclination in radiands wrt to ecliptic
        raan : float
            Longitude/Right ascension of teh ascending node in radians
        argp : float
            Argument of perigee in radians
        nu : float
            True anomaly in radians
    r_ecliptic : (3,) ndarray
        Position wrt J2000 mean ecliptic (Earth's orbit around the sun) in
        kilometers defined relative to the solar system barycenter
    v_ecliptic : (3,) ndarray
        Velocity wrt J2000 mean ecliptic in kilometers per second defined
        relative to the solar system barycenter
    r_icrf : (3,) ndarray
        Position wrt J2000 equatorial/ICRF in kilometers and defined relative
        to teh solar system barycenter
    v_icrf : (3,)
        Velocity wrt J2000 equatorial/ICRF in kilometers per second defined
        relative to the solar system barycenter

    See Also
    --------
    planet_approx : Actually has the data for the approximations 

    Notes
    -----
    The orbital elements are defined with respect to the mean ecliptic and
    equinox of J2000. The fundamental plane is the ecliptic, or the orbit of
    the Earth about the sun. The fundamental direction is aligned with the
    first point of Ares or the vernal equinox.

    The vectors are defined in both the ecliptic and equatorial system with
    respect to the solar system barycenter

    Author
    ------
    Shankar Kulumani		GWU		[email protected]   23 June 2017

    References
    ----------

    .. [1] https://ssd.jpl.nasa.gov/?planet_pos
    .. [2] MEEUS, Jean H.. Astronomical Algorithms. Willmann-Bell,
    Incorporated, 1991.

    Examples
    --------
    An example of how to use the function

    >>> planet_flag = 4
    >>> coe = planet_coe(jd, planet_flag)

    """

    # Find the JD centuries past J2000 epoch
    JD_J2000 = 2451545.0
    T = (JD_curr - JD_J2000) / 36525

    # load the elements and element rates for the planets
    (a0, adot, e0, edot, inc0, incdot, meanL0, meanLdot, lonperi0, lonperidot,
     raan0, raandot, b, c, f, s) = planet_approx(JD_curr, planet_flag)

    # compute the current elements at this JD
    a = a0 + adot * T
    ecc = e0 + edot * T
    inc = inc0 + incdot * T
    L = meanL0 + meanLdot * T
    lonperi = lonperi0 + lonperidot * T
    raan = raan0 + raandot * T

    # compute argp and M/v to complete the element set
    argp = lonperi - raan
    M = L - lonperi + b * T**2 + c * np.cos(f * T) + s * np.sin(f * T)

    # M = attitude.normalize(M, -180, 180)
    # solve kepler's equation to compute E and v
    E, nu, count = kepler.kepler_eq_E(np.deg2rad(M), ecc)
    argp = attitude.normalize(np.deg2rad(argp), 0, 2 * np.pi)[0]
    # package into a vector and output
    p = a * (1 - ecc**2)
    
    au2km = constants.au2km
    coe = COE(p=p*au2km, ecc=ecc, inc=np.deg2rad(inc), raan=np.deg2rad(raan),
              argp=argp, nu=nu)

    # convert to position and velocity vectors in J2000 ecliptic and J2000
    # Earth equatorial (ECI) reference frame
    # need to convert distances to working units of kilometers
    
    r_ecliptic, v_ecliptic, r_pqw, v_pqw = kepler.coe2rv(coe.p, coe.ecc, coe.inc,
                                                         coe.raan, coe.argp, coe.nu.item(),
                                                         constants.sun.mu)

    # convert to the Earth J2000 frame (ECI) need to rotate by the obliquity of
    # the ecliptic
    Eclip2ECI = attitude.rot1(constants.obliquity)

    r_eci = Eclip2ECI.dot(r_ecliptic)
    v_eci = Eclip2ECI.dot(v_ecliptic)

    return coe, r_ecliptic, v_ecliptic, r_eci, v_eci
Esempio n. 22
0
 def test_vectorized_interior(self):
     angles = np.linspace(10, 50, 10)
     expected = angles
     actual = attitude.normalize(angles, 0, 180)
     np.testing.assert_allclose(actual, expected)
Esempio n. 23
0
def minenergy(r1, r2, r_body, mu_body, direction):
    """Lambert minimum energy ellipse

   ( v1 v2 a p ecc ) = lambert_minenergy(r1,r2,r_body,mu_body)

   Purpose: 
       - Finds the minimum energy solution to lamberts problems given two
       position vectors.

   Inputs: 
       - r1 - initial position vector in km (1x3 or 3x1)
       - r2 - final position vector in km (1x3 or 3x1)
       - r_body - radius of central body in km (check for collision)
       - mu_body - gravitational parameter of central body in km^3/sec^2
       - direction - 'long' or 'short' direction of travel 
   Outputs: 
       - v1 - initial velocity vector in km/sec
       - v2 - final velocity vector in km/sec
       - a - semimajor axis of transfer ellipse in km
       - p - semiparameter of transfer ellipse in km
       - ecc - eccentricity of transfer ellipse
       - F - position vector of vacant focus in km 
       
   Dependencies: 
       - fg_nu - calculates velocity vectors using f and g functions
       - crash_check - check for crash into central body

   Author: 
       - Shankar Kulumani 5 Nov 2012
       - Shankar Kulumani 6 Nov 2012
           - added vacant focus calculation
       - Shankar Kulumani 14 Nov 2012
           - added crash check
        - Shankar Kulumani 13 Dec 2017
            - now in python

   References
       - AAE532 Notes LSN 30-33
       - Vallado 3rd edition
    """
    # find transfer angle
    mag_r1 = np.linalg.norm(r1)
    mag_r2 = np.linalg.norm(r2)
    cos_nu = np.dot(r1, r2) / (mag_r1 * mag_r2)

    if direction == 'short':
        sin_nu = np.linalg.norm(np.cross(r1, r2)) / (mag_r1 * mag_r2)
    elif direction == 'long':
        sin_nu = -np.linalg.norm(np.cross(r1, r2)) / (mag_r1 * mag_r2)

    delta_nu = np.arctan2(sin_nu, cos_nu)
    # TODO Check on angle range for delta nu
    delta_nu = attitude.normalize(delta_nu, 0, 2 * np.pi)

    # Intermediate parameters

    # c = sqrt(mag_r1^2+mag_r2^2-2*mag_r1*mag_r2*cos(delta_nu)); % Chord
    c_vec = r2 - r1
    c = np.linalg.norm(c_vec)
    s = (mag_r1 + mag_r2 + c) / 2  # Semiperimeter
    a = s / 2
    # p = mag_r1*mag_r2/c*(1-cos(delta_nu)); % Semiparameter?

    alpha = 2 * np.arcsin(np.sqrt(s / (2 * a)))
    beta = 2 * np.arcsin(np.sqrt((s - c) / (2 * a)))

    p = 4 * a * (s - mag_r1) * (s - mag_r2) / c**2 * (np.sin(
        1 / 2 * (alpha + beta))**2)

    # Transfer orbit physical properties

    ecc = np.sqrt(1 - 2 * p / s)
    # ecc = c/(2*a);

    # vacant focus
    F = (2 * a - mag_r1) * c_vec / c + r1
    # F = (2*a_m-norm(r1))*-c/norm(c) + r1

    # find velocity at each point
    # V0 = (sqrt(mu*p)/(r0*r*sin(v)))*(R - (1 - r/p*(1-cos(v)))*R0);
    (v1, v2, f, g, f_dot, g_dot) = kepler.fg_velocity(r1, r2, delta_nu, p,
                                                      mu_body)

    # Transfer orbit time
    B = 2 * np.arcsin(np.sqrt((s - c) / s))
    if delta_nu > np.pi:
        B = -B
    tof = np.sqrt(a**3 / mu_body) * (np.pi - B + np.sin(B))

    # Check for crash
    crash_check(r1, v1, r2, v2, mu_body, r_body)

    return v1, v2, tof, a, p, ecc
Esempio n. 24
0
 def test_upper_limit_circle(self):
     expected = -180
     actual = attitude.normalize(180, -180, 180)
     np.testing.assert_almost_equal(actual, expected)
Esempio n. 25
0
 def test_past_lower_limit_circle(self):
     expected = 179
     actual = attitude.normalize(-181, -180, 180)
     np.testing.assert_almost_equal(actual, expected)
Esempio n. 26
0
def rv2rhoazel(r_sat_eci, v_sat_eci, lat, lon, alt, jd):
    """
    This function calculates the topcentric range,azimuth and elevation from
    the site vector and satellite position vector.

    Author:   C2C Shankar Kulumani   USAFA/CS-19   719-333-4741

    Inputs:
        sat_eci - satellite ECI position vector (km)
        site_eci - site ECI position vector (km)
        site_lat - site geodetic latitude (radians)
        site_lst - site local sidereal time (radians)

    Outputs:
        rho - range (km)
        az - asimuth (radians)
        el - elevation (radians)

    Globals: None

    Constants: None

    Coupling:

    References:
        Astro 321 Predict LSN 22
        Vallado Algorithm 27
    """
    small = constants.small
    halfpi = constants.halfpi

    # get site in ECEF
    r_site_ecef = lla2ecef(lat, lon, alt)

    # convert sat and site to ecef
    dcm_eci2ecef = transform.dcm_eci2ecef(jd)
    omega_earth = np.array([0, 0, constants.earth.omega])

    r_sat_ecef = dcm_eci2ecef.dot(r_sat_eci)
    v_sat_ecef = dcm_eci2ecef.dot(
        v_sat_eci) - np.cross(omega_earth, r_sat_ecef)

    # find relative vector in ecef frame
    rho_ecef = r_sat_ecef - r_site_ecef
    drho_ecef = v_sat_ecef - np.zeros_like(v_sat_ecef)  # site isn't moving
    rho = np.linalg.norm(rho_ecef)

    # convert to SEZ coordinate frame
    dcm_ecef2sez = attitude.rot2(
        np.pi / 2 - lat, 'r').dot(attitude.rot3(lon, 'r'))
    rho_sez = dcm_ecef2sez.dot(rho_ecef)
    drho_sez = dcm_ecef2sez.dot(drho_ecef)

    # find azimuth and eleveation
    temp = np.sqrt(rho_sez[0]**2 + rho_sez[1]**2)

    if temp < small:  # directly over the north pole
        el = np.sign(rho_sez[2]) * halfpi  # \pm 90 deg
    else:
        mag_rho_sez = np.linalg.norm(rho_sez)
        el = np.arcsin(rho_sez[2] / mag_rho_sez)

    if temp < small:
        az = np.arctan2(drho_sez[1], - drho_sez[0])
    else:
        az = np.arctan2(rho_sez[1] / temp, -rho_sez[0] / temp)

    # range, azimuth and elevation rates
    drho = np.dot(rho_sez, drho_sez) / rho

    if np.absolute(temp**2) > small:
        daz = (drho_sez[0] * rho_sez[1] - drho_sez[1] * rho_sez[0]) / temp**2
    else:
        daz = 0

    if np.absolute(temp) > small:
        dele = (drho_sez[2] - drho * np.sin(el)) / temp
    else:
        dele = 0

    az = attitude.normalize(az, 0, constants.twopi)
    return rho, az, el, drho, daz, dele
Esempio n. 27
0
def sun_earth_eci(jd):
    """This function calculates the Geocentric Equatorial position vector for
    the Sun given the Julian Date.  This is the low precision formula and is
    valid for years from 1950 to 2050.  Accuaracy of apparent coordinates is
    0.01 degrees.  Notice many of the calculations are performed in degrees,
    and are not changed until later.  This is due to the fact that the Almanac
    uses degrees exclusively in their formulations.

    Algorithm     : Calculate the several values needed to find the vector
                    Be careful of quadrant checks

    Author        : Capt Dave Vallado  USAFA/DFAS  719-472-4109  25 Aug 1988
    In Ada        : Dr Ron Lisowski    USAFA/DFAS  719-472-4110  17 May 1995
    In MatLab     : Dr Ron Lisowski    USAFA/DFAS  719-333-4109  10 Oct 2001
    In Python     : Shankar Kulumani   GWU         630-336-6257  19 Jun 2017

    Inputs        :
    JD          - Julian Date                            days from 4713 B.C.

    Outputs       :
    RSun        - IJK Position vector of the Sun         km
    RtAsc       - Right Ascension                        rad
    Decl        - Declination                            rad

    Locals        :
    MeanLong    - Mean Longitude
    MeanAnomaly - Mean anomaly
    N           - Number of days from 1 Jan 2000
    EclpLong    - Ecliptic longitude
    Obliquity   - Mean Obliquity of the Ecliptic

    Constants     :
    Pi          -
    TwoPI       -
    InvRad      - Radians per degree

    Coupling      :

    References             :
    1996 Astronomical Almanac Pg. C24
    http://aa.usno.navy.mil/faq/docs/SunApprox.php
    """
    N = jd - 2451545.0

    meanlong = 280.461 + 0.9856474 * N
    meanlong = attitude.normalize(meanlong, 0, 360)[0]

    meananomaly = 357.528 + 0.9856003 * N
    meananomaly = attitude.normalize(meananomaly * deg2rad, 0, twopi)[0]
    if meananomaly < 0:
        meananomaly = twopi + meananomaly

    eclplong = meanlong + 1.915 * \
        np.sin(meananomaly) + 0.020 * np.sin(2 * meananomaly)
    obliquity = 23.439 - 0.0000004 * N

    meanlong = meanlong * deg2rad
    if meanlong < 0:
        meanlong = twopi + meanlong

    eclplong = eclplong * deg2rad
    obliquity = obliquity * deg2rad

    ra = np.arctan2(np.cos(obliquity) * np.sin(eclplong), np.cos(eclplong))
    dec = np.arcsin(np.sin(obliquity) * np.sin(eclplong))

    # equation of time
    eqtime = meanlong * rad2deg / 15 - ra * rad2deg / 15

    # sun vector
    sun_dist = 1.00014 - 0.01671 * \
        np.cos(meananomaly) - 0.00014 * np.cos(2 * meananomaly)

    semidiameter = 0.2666 / sun_dist  # angular semidiamter in deg
    sun_eci = [
        np.cos(eclplong) * sun_dist * au2km,
        np.cos(obliquity) * np.sin(eclplong) * sun_dist * au2km,
        np.sin(obliquity) * np.sin(eclplong) * sun_dist * au2km
    ]

    return np.squeeze(sun_eci), ra, dec
Esempio n. 28
0
    def tle_update(self, jd_span, mu=398600.5):
        """Update the state of satellite given a JD time span

        This procedure uses the method of general perturbations to update
        classical elements from epoch to a future time for inclined elliptical
        orbits.  It includes the effects due to first order secular rates
        (second order for mean motion) caused by drag and J2.

        Author:   C2C Shankar Kulumani   USAFA/CS-19   719-333-4741

        Inputs:
            deltat - elapsed time since epoch (sec)
            n0 - mean motion at epoch (rad/sec)
            ndot2 - mean motion rate divided by 2 (rad/sec^2)
            ecc0 - eccentricity at epoch (unitless)
            eccdot - eccentricity rate (1/sec)
            raan0 - RAAN at epoch (rad)
            raandot - RAAN rate (rad/sec)
            argp0 - argument of periapsis at epoch (rad)
            argdot - argument of periapsis rate (rad/sec)
            mean0 - mean anomaly at epoch

        Outputs:
            n - mean motion at epoch + deltat (rad/sec)
            ecc -  eccentricity at epoch + deltat (unitless)
            raan - RAAN at epoch + deltat (rad)
            argp - argument of periapsis at epoch + deltat (rad)
            nu -  true anomaly at epoch + deltat (rad)

        Globals: None

        Constants: None

        Coupling:
            newton.m

        References:
            Astro 321 PREDICT LSN 24-25
        """

        deltat = (jd_span - self.epoch_jd) * day2sec

        # epoch orbit parameters
        n0 = self.n0
        ecc0 = self.ecc0
        ndot2 = self.ndot2
        nddot6 = self.nddot6
        raan0 = self.raan0
        argp0 = self.argp0
        mean0 = self.mean0
        inc0 = self.inc0
        adot = self.adot
        eccdot = self.eccdot
        raandot = self.raandot
        argpdot = self.argpdot

        _, nu0, _ = kepler.kepler_eq_E(mean0, ecc0)
        a0 = kepler.n2a(n0, mu)
        M0, E0 = kepler.nu2anom(nu0, ecc0)

        # propogated elements
        a1 = a0 + adot * deltat  # either use this or compute a1 from n1 instead
        ecc1 = ecc0 + eccdot * deltat
        raan1 = raan0 + raandot * deltat
        argp1 = argp0 + argpdot * deltat
        inc1 = inc0 * np.ones_like(ecc1)
        n1 = n0 + ndot2 * 2 * deltat
        # a1 = kepler.n2a(n1, mu)
        p1 = kepler.semilatus_rectum(a1, ecc1)

        M1 = M0 + n0 * deltat + ndot2 * deltat**2 + nddot6 * deltat**3
        M1 = attitude.normalize(M1, 0, 2 * np.pi)
        E1, nu1, _ = kepler.kepler_eq_E(M1, ecc1)

        # convert to vectors
        r_eci, v_eci, _, _ = kepler.coe2rv(p1, ecc1, inc1, raan1, argp1, nu1,
                                           mu)

        # save all of the variables to the object
        self.jd_span = jd_span
        self.coe = COE(n=n1,
                       ecc=ecc1,
                       raan=raan1,
                       argp=argp1,
                       mean=M1,
                       E=E1,
                       nu=nu1,
                       a=a1,
                       p=p1,
                       inc=inc1)

        self.r_eci = r_eci
        self.v_eci = v_eci

        return 0