import units, math

GM = units.gravitationalConstant * units.earthMass

class EscapeVelocity(Exception):
    pass

class CrashIntoEarth(Exception):
    pass

class Orbit:
    # http://scienceworld.wolfram.com/physics/Two-BodyProblem.html

    # Supply (r,v) for either perigee or apogee
    def __init__(self, r1, v1):
        rv = r1 * v1
        # energy per kilogram
        E = 0.5 * v1**2 - GM / r1
        if E.coeff >= 0:
            raise EscapeVelocity
        # Energy per kilogram, in excess of the mass just sitting
        # on the surface of the Earth
        self.E = E + (GM / units.earthRadius)
        a, b, c = E, GM, -0.5 * rv**2
        d = (b**2 - 4 * a * c)**0.5
        x1 = (-b - d) / (2 * a)
        x2 = (-b + d) / (2 * a)
        if x2 < x1: x1, x2 = x2, x1
        self.perigee = (x1, rv / x1)
        self.apogee = (x2, rv / x2)
        safeAltitude = units.earthRadius + 150 * units.km
        if self.perigee[0] < safeAltitude:
            raise CrashIntoEarth

##################

def circularVelocityFromRadius(r):
    return (GM / r) ** .5

def circularRadiusFromVelocity(v):
    return GM / v**2

def circularRadiusFromOmega(w):
    return (GM / w**2) ** (1./3)

def circularOmegaFromRadius(r):
    return (GM / r**3) ** .5

def escapeVelocityFromRadius(r):
    return (2 * GM / r) ** .5

# I picked up a 12-inch globe, and I want to show where
# geosynchronous orbit is.
def geo():
    geosync = circularRadiusFromOmega(2 * math.pi / (24 * units.hour))
    print "geosynchronous orbit at", geosync
    print "or in miles", geosync.inTermsOf(units.mile)
    geosync -= units.earthRadius
    print "geosynchronous orbit above ground", geosync
    print "or in miles", geosync.inTermsOf(units.mile)

    print "geosync altitude in earth diameters",
    print geosync / (2 * units.earthRadius)

    globeScale = units.earthRadius / (6 * units.inch)
    print "geosync on 12-inch globe",
    print (geosync / globeScale).inTermsOf(units.inch)

def apogeeFromPerigee(rmin, vmin):
    return Orbit(rmin, vmin).apogee

def main():
    for i in range(1,7):
        dv = i * 500 * units.m / units.sec

        ###############################################################
        # We start out launching from JoSH's space ramp, going faster than
        # we need for a circular orbit, this puts us in an elliptic orbit
        # starting at the end of the space ramp. See
        # http://www.imm.org/Reports/Rep016.html.

        rmin = units.earthRadius + 100 * units.km
        vmin = circularVelocityFromRadius(rmin) + dv
        rmax, vmax = apogeeFromPerigee(rmin, vmin)

        print "dv", dv
        print "perigee", vmin, rmin
        print "apogee", vmax, rmax

        ################################################################
        # When we reach apogee, we grab the bottom end of a space elevator
        # cable. The space elevator is a rope with a weight in the middle,
        # and the weight is in a circular orbit. Because this elevator is
        # not in geosynchronous orbit, it doesn't need to be as long or
        # have the tensile strength of a geosynchronous space elevator.
        # Our velocity at apogee tells us the orbital radius of the
        # weight.

        omega = vmax / rmax
        rweight = circularRadiusFromOmega(omega)
        print "orbital period for weight", \
              (2 * math.pi / omega) / units.hour, "hours"

        ################################################################
        # We climb the cable to the counterweight. We pay the station
        # operator in rocket fuel, which he uses to correct the orbit of
        # the elevator which we've perturbed. We continue upward to the
        # top end of the cable. The upper end of the cable can be as long
        # as we want -- it's not subject to much tension.

        # Cable should be much longer above than below?
        rtop = rweight + 3 * (rweight - rmax)

        print "Lower cable length", rweight - rmax
        print "Altitude of weight", rweight
        print "Upper cable length", rtop - rweight
        print "Total cable length", rtop - rmax

        ################################################################
        # Now we let go of the upper end of the cable. Big question: do
        # we attain escape velocity for Earth? Tidal forces tend to keep
        # the cable radially oriented to the Earth, and maybe if we climb
        # the upper cable slowly, we won't mess that up. If so, then we
        # keep the same angular velocity, and linear velocity scales with
        # radius.

        print "Escape velocity?", escapeVelocityFromRadius(rtop), "<", \
              vmax * (rtop / rmax)
        print
