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
def set_v(self, v): self.v = v from auxiliary import rv2coe self.coe = rv2coe(self.r, self.v, self.Mu) # use self.set_coe() to reset all other attributes self.set_coe(self.coe, rv = False)
def set_rv(self, r, v): self.r = r; print self.r self.v = v; print self.v from auxiliary import rv2coe self.coe = rv2coe(self.r, self.v, self.Mu) self.set_coe(self.coe, rv = False)
def set_r(self, r): self.r = r from auxiliary import rv2coe self.coe = rv2coe(self.r, self.v, self.Mu) # use self.set_coe() to reset all other attributes self.set_coe(self.coe)