示例#1
0
 def set_coe(self, coe, units = None, rv = True):
     if units != None:
         from unit_conversions import converter
         coe[2] = converter(coe[2], units, 'Radians')
         coe[3] = converter(coe[3], units, 'Radians')
         coe[4] = converter(coe[4], units, 'Radians')
         coe[5] = converter(coe[5], units, 'Radians')
     #  reset coe
     self.sma  = coe[0]
     self.e    = coe[1]
     self.i    = coe[2]
     self.RAAN = coe[3]
     self.AOP  = coe[4]
     self.f    = coe[5]
     self.coe  = [self.sma, self.e, self.i, self.RAAN, \
                  self.AOP, self.f]
     #  reset r, v
     if rv:
         from auxiliary import coe2rv
         self.r, self.v = coe2rv(self.coe, self.Mu)
     #  recalculate additional useful orbital parameters
     #+ E: eccentricy anomaly
     #+ M: mean anomaly
     #+ T: orbital period
     #+ n: mean motion
     from auxiliary import f2E, E2M, orbitalperiod
     from math import pi
     self.E = f2E(self.e, self.f)
     self.M = E2M(self.e, self.E)
     self.T = orbitalperiod(self.Mu, self.sma)
     self.n = 2.0*pi / self.T
示例#2
0
 def set_sma(self, sma):
     self.sma = sma
     #  reset sma in coe list
     self.coe[0] = sma
     #  reset the r and v vectors
     from auxiliary import coe2rv
     self.r, self.v = coe2rv(self.coe, self.Mu)
     #  reset the orbital period
     from auxiliary import orbitalperiod
     self.T = orbitalperiod(self.Mu, self.sma)
     #  reset the mean motion
     from math import pi
     self.n = 2.0*pi / self.T
示例#3
0
def prussing_conway(
    ic1,
    ic2,
    mu,
    TransferTime=0,
    FindTimeParabolic=False,
    FindMinEnergy=False,
    FindlambertArc=False,
    Retrograde=False,
    NonDimUnits=True,
    ScaleOutput=True,
    iLimit=500,
    tol=1e-2,
):
    """
        prussing_conway()
        
        Future Work
        1.) Need to eliminate v1, v2 input arguments / 2014_05_18
        2.) lambert should be able to use minimum time for
        TransferTime without having to call lambert() twice
        3.) Should separate lambert functions into a separate module,
        which should eliminate r,v being set to 3d vectors from 2d
        by rv2coe() call
        4.) scipy calls appear to be working fine, should eliminate
        all the junk code floating around and cleanup
        5.) Shouldn't be rescaling if not using NonDimUnits
        6.) Need Spatial Capability!!!
        """

    """ DEBUG_SCIPY """
    DEBUG_SCIPY = True

    # import statements
    from math import acos, asin, sin, pi, cos, sqrt
    from numpy import array
    from numpy.linalg import norm
    from auxiliary import rv2coe, hohmann, orbitalperiod

    # initialze output dictionary
    out = {
        "a": -1,
        "res": -1,
        "iter": 0,
        "alpha": -1,
        "beta": -1,
        "tp": -1,
        "tm": -1,
        "v1": 0,
        "v2": 0,
        "DU": -1,
        "TU": -1,
        "MU": -1,
        "theta": -1,
    }

    #  select lambert arc computation if neither
    #  parabolic time nor min. energy selected
    if not FindTimeParabolic and not FindMinEnergy:
        FindlambertArc = True

    #  initializations
    r1 = ic1[0:2]
    r2 = ic2[0:2]
    v1 = ic1[2:4]
    v2 = ic2[2:4]
    #  convert r, v to coe
    oe1 = rv2coe(r1, v1, mu)
    oe2 = rv2coe(r2, v2, mu)
    #  convert to array type
    r1 = array(r1[0:2])
    r2 = array(r2[0:2])
    v1 = array(v1[0:2])
    v2 = array(v2[0:2])

    #  nondim units
    G = 6.67384 * 1e-20  # (km^3/kg/s^2)
    DU = oe1[0]
    MU = mu / G
    TU = sqrt(1 / (G * MU / DU ** 3))
    if NonDimUnits:
        mu = 1
        #  scaling
        oe1[0] = oe1[0] / DU
        oe2[0] = oe2[0] / DU
        r1 = r1 / DU
        r2 = r2 / DU
        v1 = v1 * TU / DU
        v2 = v2 * TU / DU
        TransferTime = TransferTime / TU

    # chord and semiperimeter
    chord = norm(r1 - r2)
    semip = (norm(r1) + norm(r2) + chord) / 2

    # rotation of r1, 90 degrees
    temp = array([-r1[1], r1[0]])

    # find angle between r1 and r2
    theta = acos(r1.dot(r2) / (norm(r1) * norm(r2)))
    theta2 = acos(temp.dot(r2) / (norm(r1) * norm(r2)))
    if theta2 >= pi / 2:
        theta = 2 * pi - theta

    # update out entry
    out["theta"] = theta

    # sign of sin(theta)
    if sin(theta) < 0:
        sgn = -1
    if sin(theta) > 0:
        sgn = 1
    if sin(theta) == 0:
        sgn = 0

    # parabolic transfer time
    if FindTimeParabolic or FindlambertArc:
        tp = time_parabolic(semip, chord, mu, sgn)
        out["tp"] = tp
    if FindTimeParabolic:
        return out

    # calculate the minimum energy transfer time (elliptic case)
    if FindMinEnergy or FindlambertArc:
        tm = time_minenergy(semip, chord, mu, theta)
        out["tm"] = tm
    if FindMinEnergy:
        return out

    #  let the user know if they selected an infeasbile
    # + transfer time
    if TransferTime < tp:
        print(
            "lambert: The transfer time (%.2f) is too short "
            "for an Elliptic Orbit (< %.2f) --> Return" % (TransferTime, tp)
        )
        return out

    # calculate semi-major axis, alpha, and beta
    # scale tol if nondimunits == False
    if not NonDimUnits:
        tol = tol * DU

    """ DEBUGGING for scipy.optimize 2014_05_18 """
    if not DEBUG_SCIPY:
        # initialize
        res = 10 * tol
        a = semip / 2  # oe1[0]
        ainc = a * 1e-3
        dir = 1
        dx = ainc
        damp = 10
        i = 1

        # while-loop unitl tolerance is met
        while res > tol:
            # save old
            if i == 2:
                temp = res
            if i > 2:
                res0 = temp
                temp = res

            # flip the direction of ainc if necessary
            if i > 2 and res > res0:
                dir = -1 * dir
            # calculate damping needed
            if i > 2 and res < res0:
                slope = (res - res0) / ainc
                if res > 0:
                    dx = -res / slope
                if dx / ainc < damp:
                    ainc = ainc / 2
            # update a
            a = a + dir * ainc

            # calculate residual
            res = lamberteq(a, mu, TransferTime, semip, chord, tm, theta)

            # if limit reached -> break
            if i > iLimit:
                break

            # update i
            i += 1

    if DEBUG_SCIPY:
        """ DEBUGGING for scipy.optimize 2014_05_18 """
        from scipy.optimize import minimize

        a = semip / 2
        bnds = ((a, None),)
        res = minimize(lamberteq, array(a), (mu, TransferTime, semip, chord, tm, theta), bounds=bnds, method="L-BFGS-B")

        """ DEBUGGING for scipy.optimize 2014_05_18 """
        #        print res
        a = res.x
        i = 0

    # alpha and beta are the rectilinear equivalent of
    # eccentric anomaly for transfer orbit
    alpha = 2 * asin(sqrt(semip / (2.0 * a)))
    beta = 2 * asin(sqrt((semip - chord) / (2.0 * a)))

    # perform necessary corrections
    if TransferTime > tm:
        alpha = 2 * pi - alpha
    if theta >= pi and theta < 2 * pi:
        beta = -beta

    # switches for retrograde orbits.
    # will need post-shooting method to adjust
    if Retrograde == True:
        beta = -beta

    # compute delta-vs
    # use hohmann if beta ~0 or ~180 degrees
    # else
    # use default calculations for v1 and v2
    u1 = r1 / norm(r1)
    u2 = r2 / norm(r2)
    uc = (r2 - r1) / norm(r2 - r1)
    # if abs(pi - (alpha - beta)) < 1E-4 and oe1[1] < 1E-2 and oe2[1] < 1E-2:
    if abs(beta % pi) < 1e-4:
        dv1, dv2 = hohmann(mu, oe1[0], oe2[0])
        v1 = v1 + dv1 * v1 / norm(v1)
        v2 = v2 + dv2 * v2 / norm(v2)
    else:
        cota = cos(alpha / 2) / sin(alpha / 2)
        cotb = cos(beta / 2) / sin(beta / 2)
        A = sqrt(mu / (4 * a)) * cota
        B = sqrt(mu / (4 * a)) * cotb
        v1 = (B + A) * uc + (B - A) * u1
        v2 = (B + A) * uc - (B - A) * u2

    # compute semilatus rectum for output
    p = 4 * a * (semip - norm(r1)) * (semip - norm(r2)) * (sin((alpha + beta) / 2) ** 2) / (chord ** 2)
    # compute orbital elements, output ecc and true anomaly
    oef = rv2coe(r1, v1, mu)
    f = oef[5]
    # if retrograde, mod() true anomaly (i.e. flip i pi)
    if abs(oef[2] - pi) < 1e-3:
        f = 2 * pi - oef[5]
    psi = 2 * pi - f
    ev = [cos(psi) * u1[0] - sin(psi) * u1[1], sin(psi) * u1[0] + cos(psi) * u1[1]]
    # position of the vacant focus
    pf = [-2 * a * oef[1] * ev[0], -2 * a * oef[1] * ev[1]]

    # compile output
    out["a"] = a
    out["e"] = oef[1]
    out["ev"] = ev
    out["f"] = oef[5]
    out["T"] = orbitalperiod(mu, a)
    out["pf"] = pf
    """ DEBUGGING scipy.optimize 2014_05_18 """
    if DEBUG_SCIPY:
        out["res"] = res.fun
    if not DEBUG_SCIPY:
        out["res"] = res
    out["iter"] = i - 1
    out["alpha"] = alpha
    out["beta"] = beta
    out["time"] = TransferTime
    out["v1"] = v1
    out["v2"] = v2
    out["DU"] = DU
    out["TU"] = TU
    out["MU"] = MU
    # scale ouput if needed
    if ScaleOutput:
        out["a"] = out["a"] * DU
        out["T"] = out["T"] * TU
        out["tp"] = out["tp"] * TU
        out["tm"] = out["tm"] * TU
        out["v1"] = out["v1"] * DU / TU
        out["v2"] = out["v2"] * DU / TU

    return out
示例#4
0
 def set_parameters(self, body, altitude = 400.0):
     import solarsystem_objects_parameters as ss
     #  setup parameters for the requested body if available
     #  first see if the user is requesting the 'Earth' or
     #+ if the body is not on the given list, then default
     #+ to the 'Earth'
     if body == 'earth' or body not in ss.list:
         self.name = 'earth'
         #  default classical orbital elements is
         #+ earth about sun
         self.sma    = ss.earth['SemiMajor']
         self.e      = ss.earth['Eccentricity']
         self.i      = ss.earth['Inclination']
         self.RAAN   = 0.0
         self.AOP    = 0.0
         self.f      = 0.0
         #  epoch (Julian Date), default January 3rd 2014
         #+ which is roughly when the Earth is at periapsis
         self.epoch = 2456660.500000 * 86400.0
         #  mass and mu of the body
         self.mass = ss.earth['Mass']
         self.mu   = ss.earth['Mu']
         #  radius of body
         self.radius = ss.earth['Radius']
         #  set the gravitational constant of 'self'
         #+ central body
         self.Mu = ss.sun['Mu']
     if body == 'sun':
         self.name = 'sun'
         #  default classical orbital elements is
         #+ earth about sun
         self.sma    = 0.0
         self.e      = 0.0
         self.i      = 0.0
         self.RAAN   = 0.0
         self.AOP    = 0.0
         self.f      = 0.0
         #  epoch (Julian Date), default January 3rd 2014
         #+ which is roughly when the Earth is at periapsis
         self.epoch = 2456660.500000 * 86400.0
         #  mass and mu of the body
         self.mass = ss.sun['Mass']
         self.mu   = ss.sun['Mu']
         #  radius of body
         self.radius = ss.sun['Radius']
         #  set the gravitational constant of 'self'
         #+ central body
         self.Mu = ss.sun['Mu']
     if body == 'moon':
         self.name = 'moon'
         #  default classical orbital elements is
         #+ earth about sun
         self.sma    = ss.moon['SemiMajor']
         self.e      = ss.moon['Eccentricity']
         self.i      = ss.moon['Inclination']
         self.RAAN   = 0.0
         self.AOP    = 0.0
         self.f      = 0.0
         #  epoch (Julian Date), default January 3rd 2014
         #+ which is roughly when the Earth is at periapsis
         self.epoch = 2456660.500000 * 86400.0
         #  mass and mu of the body
         self.mass = ss.moon['Mass']
         self.mu   = ss.moon['Mu']
         #  radius of body
         self.radius = ss.moon['Radius']
         #  set the gravitational constant of 'self'
         #+ central body
         self.Mu = ss.earth['Mu']
     if body == 'mars':
         self.name = 'mars'
         #  default classical orbital elements is
         #+ earth about sun
         self.sma    = ss.mars['SemiMajor']
         self.e      = ss.mars['Eccentricity']
         self.i      = ss.mars['Inclination']
         self.RAAN   = 0.0
         self.AOP    = 0.0
         self.f      = 0.0
         #  epoch (Julian Date), default January 3rd 2014
         #+ which is roughly when the Earth is at periapsis
         self.epoch = 2456660.500000 * 86400.0
         #  mass and mu of the body
         self.mass = ss.mars['Mass']
         self.mu   = ss.mars['Mu']
         #  radius of body
         self.radius = ss.mars['Radius']
         #  set the gravitational constant of 'self'
         #+ central body
         self.Mu = ss.sun['Mu']
     if body == 'LEO':
         self.name = 'leo_spacecraft'
         #  default classical orbital elements is
         #+ spacecraft about earth
         from math import pi
         self.sma    = ss.earth['Radius'] + altitude
         self.e      = 0.0
         self.i      = 28.5 * pi / 180.0
         self.RAAN   = 0.0
         self.AOP    = 0.0
         self.f      = 0.0
         #  epoch (Julian Date), default January 3rd 2014
         #+ which is roughly when the Earth is at periapsis
         self.epoch = 2456660.500000 * 86400.0
         #  mass and mu of the body
         self.mass = 1000.0
         self.mu   = 0.0
         #  set the gravitational constant of 'self'
         #+ central body
         self.Mu = ss.earth['Mu']
     #  place the classical orbital elements into an array
     self.coe = [self.sma, self.e, self.i, self.RAAN, \
                 self.AOP, self.f]
     #  calculate the r, v vectors for 'self'
     #+ based on the classical orbital elements 'coe'
     from auxiliary import coe2rv
     self.r, self.v = coe2rv(self.coe, self.Mu)
     #  calculate additional useful orbital parameters
     #+ E: eccentricy anomaly
     #+ M: mean anomaly
     #+ T: orbital period
     #+ n: mean motion
     from auxiliary import f2E, E2M, orbitalperiod
     from math import pi
     self.E = f2E(self.e, self.f)
     self.M = E2M(self.e, self.E)
     self.T = orbitalperiod(self.Mu, self.sma)
     self.n = 2.0*pi / self.T