9/6/2018 capture--sling

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 1/173 9/6/2018 capture-orbit-sling

Preliminary analysis of an asymmetric resonant ecliptic capture orbit sling transport system

Bryan Killett, 2018

Puig-Suari et al. 1995 (http://web.archive.org/web/20171006015505/https://engineering.purdue.edu/people/james.m.longuski.1/JournalAr and Jokic and Longuski 2004 (http://web.archive.org/web/20170923011520/https://eprints.usq.edu.au/8092/1/Jokic_Longuski_JSR_v41n6_PV.pd designed tether slings on Phobos and Deimos for transporting payloads to Earth. However, their slings' tether masses scale quickly with the required payload hyperbolic excess speed, partly because the small at Phobos and Deimos necessitates high sling tip speeds. If a hyperbolic excess speed higher than ~5 km/s is required, a lower total system mass can be obtained by using multiple slings. A 'moon sling' on Phobos or Deimos throws payloads and counterbalance masses into a capture orbit with a periapsis just above the atmosphere of Mars. The capture orbit is resonant with the chosen moon to allow for repeated throws, and it's ecliptic because trans-Earth injection velocities are close to lying in the ecliptic plane. A coupled pair of asymmetric 'capture slings' in the capture orbit rendezvous with those payloads and counterbalances, then use solar power to spin up both slings by torquing against each other. Both payloads and both counterbalances are then thrown at periapsis to maximize the Oberth effect. Asymmetric capture slings allow the counterbalance to be specified approximately independently of the payload velocities, but asymmetric slings force a certain ratio between the payload masses and require ballast mass to keep the slings' centers of mass at the desired rotational axis. The capture slings can use tether reeling to avoid collisions with the moons and to rotate their periapsis velocity vector to throw payloads in different directions in the ecliptic plane.

Steps:

1. Specify the maximum payload tip speed and acceleration. 2. Choose the payloads' hyperbolic excess speeds, tether material, safety factor, etc. 3. Choose an orbital resonance for the capture slings. 4. Estimate the required tip speed to compare with the tip speed specified in step 1. 5. Initialize lists and define functions. 6. Solve for each capture sling's radius, rotation rate, counterbalance ratio, etc. 7. Print the capture slings' payload trajectories and counterbalance orbits. 8. Calculate the delta-v from the chosen moon's orbit to the inclined capture sling orbit. 9. Print the moon sling's payload and counterbalance orbits. 10. Solve for the ballast mass needed to keep an asymmetric sling's center of mass at the rotational axis. 11. Define the slings' moments of inertia and rotational kinetic energy. 12. Solve for the third capture sling which zeroes the total rotational angular momentum of the capture sling system whether it's full or empty. 13. Solve for the moon sling payload based on mt4ct and the total mass to be thrown. 14. Check the centers of mass of all slings while empty and full. 15. Calculate required power and solar panel mass for spinning the slings. 16. Animate the capture sling throw. 17. Make a 3D plot of the payloads and counterbalances to assess the risk of collisions. 18. Calculate mass of equivalent rocket. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 2/173 9/6/2018 capture-orbit-sling 19. Print summary. 20. Appendix A: Counterbalance mass ratios above 4.6033 cause collisions. 21. Appendix B: Indirect paths from the moon to the capture orbit.

This notebook was initially written to reproduce values in Jokic and Longuski 2004 (http://web.archive.org/web/20170923011520/https://eprints.usq.edu.au/8092/1/Jokic_Longuski_JSR_v41n6_PV.pd table 3. To do that, set grapple_fraction = 0, moon_name = 'Phobos', safety = 1.0, material = 'Zylon', tip_accel_max = 3*g, payload_mass = 11.2*1000 or 70*1000, cbrs[0] >= 1, cbrs[1] = 0, best_case_offset = 0.0, Isp = 379, s2p = 0.15. The moon sling payload tether mass, length, diameters, single-stage rocket propellant mass and mu_single reported in the summary at the bottom will then ~reproduce values in Jokic and Longuski 2004 table 3.

Look through phone for other things.

Radiator sun shield doesn't show up!

Add longitudinal supports to the hawsepipe. Add a light right outside the hub, for clarity.

NOTE THAT THE CAPTURE TO MOON ROTATION MATRIX DOESN'T REALLY MAKE SENSE ANYMORE AFTER THE CELL CHANGED FROM MOON FRAME TO CAPTURE_FRAME!

IN SEVERAL PLACES THE PERIAPSIS VS INFINITY HYPERBOLIC DEFLECTION ANGLE IS CALCULATED. MAKE SURE THAT IN ALL SUCH PLACES, THE CALCULATIONS ARE EITHER FOR THE MOON SLING OR THEY'RE FOR THE CAPTURE SLING BUT BOTH V_INFS ARE USED!!!

DAMNIT! THE RETROGRADE ANCHOR NEEDS TO BE REALLY DIFFERENT MASSES BEFORE AND AFTER THE THROW!

Changed all RR( to RDF( for speed https://ask.sagemath.org/question/9950/what- are-the-different-real-numbers-in-sage/ (https://ask.sagemath.org/question/9950/what-are-the-different-real-numbers-in- sage/) AT THE SAME TIME, CHANGE capture_frame_string to ecliptic_frame_string, inc_moon to inc_m2c (OR SOMETHING?!?!), H_cs_unit to H_cs_n to match others, etc??

Because tether reeling becomes less effective with more circular orbits, might consider making the destination counterbalance orbit elliptical. However, that would complicate gravity gradient stabilization of habitats and getting the counterbalances to rendezvous with those habitats.

Finish writing string for payload longitude (and rename it!), specifically start and finish the part dealing with the capture sling longitudes which don't require any tether reeling in between picking up payloads from one of the moon sling's nodes (either going out or going in).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 3/173 9/6/2018 capture-orbit-sling Eclipses aren't considered in calculations of power requirements. LINK here (http://www.csc.caltech.edu/references/Hopkins-Phobos-Deimos-Paper.pdf) FOR MOON ECLIPSES, FOR CAPTURE SLING ECLIPSES, LINK TO THESIS AND/OR THAT RECENT TETHER ANALYSIS I DOWNLOADED (CAN'T FIND IT ON TABLET, MIGHT HAVE TO LOOK IN TETHER EMAIL AND/OR THAT KHAYMAN FOLDER WITH ALL THE PAPERS THAT NEED TO BE NAMED)?

capture_frame_string should probably be called ecliptic_frame_string, and it shouldn't mention the capture slings if cbrs[1]==0.

Consider adding equatorial capture sling option. This means inc_moon needs to become inc_m2c (inclination of moon wrt capture orbit). Also have inc_q2l (equator wrt ecliptic BUT THIS IS ALREADY IN CODE AS axial_tilt_planet!) and inc_c2q or inc_c2l???

Finish J2 precession calcs.

Still not dealing with nodal precession due to the Sun, which precesses the orbit around the normal to Mars's orbital plane. ". For each satellite the net precession is retrograde about the normal to its Laplace plane, a plane lying between the Mars equator and Mars orbit;" http://www.planetary.brown.edu/planetary/geo287/PhobosDeimos/papers/Jacobson 2014.pdf (http://www.planetary.brown.edu/planetary/geo287/PhobosDeimos/papers/Jacobso 2014.pdf)

GREAT website overall, and this function should replace GAIA's distance function! http://web.archive.org/web/20110825045635/https://www.projectpluto.com/dist.cpp (http://web.archive.org/web/20110825045635/https://www.projectpluto.com/dist.cpp

Limitations:

Simple rigid tether model- no elasticity, oscillations, tumbling, chaotic motion, etc. There's no coupling between orbital and rotational angular momenta. The capture slings rotate as though they're in free space and their center of mass isn't accelerating. Completely separately, the capture sling center of mass moves in a Keplerian orbit as if it were a point mass. Only centrifugal forces are included, so forces due to gravitational gradients aren't considered. Gravitational forces due to Deimos, Phobos, and the oblateness of Mars (and all other gravitational perturbations) are ignored. Deimos and Phobos are treated as being in circular orbits that are inclined relative to the ecliptic plane. Hyperbolic payload trajectories lie in the ecliptic plane, so plane-change corrections are needed because planetary orbits aren't all in the ecliptic. The specified maximum acceleration and tip speed only apply to the payload, so if a sling uses a counterbalance ratio < 1.0, its counterbalance acceleration and tip speed will be above the specified maximums. In that case, the counterbalance arm could also reach below the target altitude of the counterbalances, which is a safety hazard. This notebook was written mostly from scratch. While this was fun and educational, the code has only been tested by one person. That's a serious limitation compared to code from Project Pluto http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 4/173 9/6/2018 capture-orbit-sling (https://www.projectpluto.com) which has been tested by many people. In particular, functions like xyz2kepler() and kepler2xyz() should probably be replaced with code from Project Pluto. On a related note, this notebook is sort of designed to allow the origin planet to be changed (e.g. so the capture slings throw payloads from Jupiter rather than Mars), but that hasn't really been tested. If you want to throw payloads from a planet that isn't Mars then beware of software bugs. You can manually run each cell by hitting Shift-Enter, but it's safer to run the entire notebook at once by clicking on Kernel -> Restart & Run All . (Unintended output can sometimes occur when running cells repeatedly or out of order.) Haven't estimated the masses of the radiators, electric motors and the central hub that transmits torque from one capture sling to the other. The summary prints values for these masses as reminders to produce rigorous estimates, but right now those values are either all zero (if estimate_misc is set to 0) or they're just copies of the solar panel mass estimate (based on Juno's solar panels). Launch windows are ignored, as is the longitude of the payload's trajectory in the ecliptic plane. If the capture slings' periapsis velocity vector isn't pointing in the right direction, they can compensate in two ways. First, they can release the payloads and counterbalances before or after periapsis, although this will require higher tip speeds and make it harder to get the counterbalances into the same desired low orbit. This hasn't been addressed. Secondly, they can use tether reeling to rotate the capture slings' until it's pointing in the right direction. The time necessary to rotate the capture orbit like this isn't addressed, nor are any of the technical requirements like electrical power or the required speed that the masses need to be reeled so the capture orbit rotates at the required rate, etc. Even if the two payloads have the same hyperbolic excess speeds, they're released at different heights so they're deflected by different hyperbolic deflection angles. As a result, they converge on two different asymptotes. This can probably be dealt with by throwing one of the payloads at a slightly different angle and a slightly different time than the other payload. Sorensen 2003 (http://web.archive.org/web/20161011042641/http://www.tethers.com/papers/AIAA_2003-5221.pdf) might help. Since both payloads are currently thrown at the optimal time and angle (i.e. at periapsis with their rotational velocity vectors parallel with the capture slings' orbital velocity vector), this will increase the tether masses. Also, it might not be enough to converge on the same asymptote; rendezvous with a cycler spacecraft will require that both payloads rendezvous with each other and the cycler in finite time, ideally with low relative velocities. None of this has been addressed, partly because it seems like these next problems need to be addressed simultaneously: This notebook allows each payload to be thrown at its own hyperbolic excess speed, but doesn't address the fact that this probably means each payload needs to be thrown at a different hyperbolic asymptote. Consider quantifying the added tether mass of changing the angle between the two payloads' asymptotes (the optimal angle with the lowest total tether mass was explained above). One could vary one or both of the slings' throw times away from the time of periapsis. But instead of trying to match the asymptote of the first sling, just choose a throw angle so the rotational velocity vector is parallel with the capture slings' orbital velocity vector. Then calculate the sling radius and rotation rate necessary to acheive the same hyperbolic excess speed- this will increase because the sling's orbital velocity isn't as fast as it is at periapsis. This increase in the tip speed increases the tether mass. The new payload's trajectory will have a different the argument of periapsis and hyperbolic deflection angle, both of which are needed to calculate the hyperbolic asymptote. Calculate the angle between this new asymptote and that of the first sling, then vary the sling's throw time again and repeat the procedure. After varying the throw time over a "large enough" timespan, one could make a plot of the required total tether mass versus throw time, and a plot of the corresponding angle between the payload asymptotes versus throw time. This could help quantify the added tether mass of changing the angle between the two payloads' asymptotes. It would help to load an ephemeris (https://en.wikipedia.org/wiki/Jet_Propulsion_Laboratory_Development_Ephemeris) and perform a rigorous search of possible ~simultaneous throws (to different planets?) in order to figure out how long http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 5/173 9/6/2018 capture-orbit-sling a "large enough" timespan is, in order to separate the two asymptotes by an angle that corresponds to an actual ~simultaneous launch window, given the actual positions of the origin planet and destination planet(s). How "simultaneous" do the throws have to be? For instance, could one payload be thrown multiple capture orbits before the other payload is thrown? That would allow for more mission flexibility but would expose the tethers to space for a longer time and increase the risk of chaotic tether motion. The capture slings are treated as though they're both rotating in the ecliptic plane, which isn't possible. In fact, safe operation will depend on the separation between the centers of mass for capture slings A and B being larger than the maximum size (in the z direction, along the slings' axis of rotation) of oscillations and/or chaotic motion. So it's necessary to account for this separation distance. A rigorous treatment during the spinup procedure is beyond the scope of this preliminary analysis. Perhaps a reasonable compromise at this stage would be to solve for the throw times (plus throw angles, radii, omegas and counterbalance ratios) while assuming they're coplanar but then estimate the targeting error by moving those payload/balance positions and velocities a 'safe' operating distance in the z- direction while keeping velocity unchanged, then recomputing the Keplerian elements of those positions/velocities using xyz2kepler(). Then estimate the delta-v needed for the two payloads/balances to rendezvous. This might be a good use for maneuvering thrusters, and a reason why the capture orbit sling system could benefit from using at least some propellant (e.g. CO2 from the Martian atmosphere) and/or fuel (e.g. LOX/CH4 made from hydrogen and the Martian atmosphere).

1. Specify the maximum payload tip speed and acceleration. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 6/173 9/6/2018 capture-orbit-sling

In [1]: import copy #Copy lists using deepcopy unless you want to copy by ref erence so changes to the copied list also change the original list. from IPython.display import Image var('mu r a v ecc cos_ta v_tip') # mu = G*M, a = semi major axis, cos _ta = cosine of set_verbose(2) #Set to 0 to disable print statements in xyz2kepler() and kepler2xyz(). textonly = 0 #Set to anything but "0" to limit output to text only. g = 9.81 #m/s^2 Earth gravity

#Try 1105.0 for the M-E Hohmann mission from Deimos using the 11:5 re sonance. #Try 3465.0 for the MEEM cycler mission from Deimos using the 11:6 re sonance. v_tip_max = 1105.0 #Highest sling payload tip speed in m/s under consideration. Determines longest sling radius at a given tip accel. For best results, run this notebook with a guess at v_tip_max, then change v_tip_max so it matches the highest calculated sling tip spee d. Then iterate this process a few times, because the calculated slin g tip speed will change after v_tip_max is changed. tip_accel_max = 1*g #Along with v_tip_max, this controls r_max: r_max = v_tip_max^2/tip_accel_max

#To simulate Hohmann transfers to/from other planets, try this Hohman n calculator by Hop David: http://web.archive.org/web/20170202021347/ http://clowder.net/hop/railroad/Hohmann.xls v_infs = [] ; mnames = [] v_infs += [ 0.0]; mnames += ['Parabola'] v_infs += [2640.0]; mnames += ['M-E Hohmann'] v_infs += [3000.0]; mnames += ['M-E in < 6 months'] v_infs += [4000.0]; mnames += ['M-E-M'] v_infs += [5470.0]; mnames += ['MEEEM cycler'] v_infs += [8000.0]; mnames += ['8 km/s cycler'] v_infs += [11900.0]; mnames += ['Aldrin cycler'] #Unless sending coun terbalance to surface or retrograde, this mission needs a counterbala nce > 4.6, which would cause a collision. for j in range(len(v_infs)): print 'Mission',str(j+1).rjust(2)+': v_i nf =',str(v_infs[j].n(digits=5)).rjust(7),'m/s -',mnames[j]

Mission 1: v_inf = 0.00000 m/s - Parabola Mission 2: v_inf = 2640.0 m/s - M-E Hohmann Mission 3: v_inf = 3000.0 m/s - M-E in < 6 months Mission 4: v_inf = 4000.0 m/s - M-E-M Mission 5: v_inf = 5470.0 m/s - MEEEM cycler Mission 6: v_inf = 8000.0 m/s - 8 km/s cycler Mission 7: v_inf = 11900. m/s - Aldrin cycler

2. Choose the payloads' hyperbolic excess speeds, tether material, safety factor, etc. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 7/173 9/6/2018 capture-orbit-sling

In [2]: mission_choice1 = 2 #Mission for sling A, or the moon sling if the ca pture sling is disabled. mission_choice2 = mission_choice1 #Mission for sling B.

mis = [mission_choice1-1,mission_choice1-1,mission_choice2-1,-1] #Pop ulate list of mission indices based on the above choices, using the s ame indices as elsewhere. E.g. the first entry is the mission for the moon sling, then sling A, sling B, sling C. The moon sling mission o nly applies if the capture sling is disabled. The mission index for c apture sling C will be copied from whichever sling it ends up being a ttached to.

#Control the payload trajectories by choosing a "trajopt" option. ##################################################################### Perpendicular capture sling options: #Values of "trajopt" < 100 setup the capture sling with its plane of rotation perpendicular to its orbital plane. The capture sling's axi s of rotation points radially away from the planet when it releases t he payload at periapsis. This orientation isn't tidally stabilized an d tether reeling for maneuvering won't have steep gravitational gradi ents. But it's easy to calculate because both payloads are released a t the same radial distance from the planet. #trajopt = 1; traj_string = 'PERPENDICULAR SETUP. Give both payloads identical v_inf and identical release radii. Counterbalance goes dir ectly into a at periapsis_capture (though it should re ally either use aerobraking or go directly into an orbit with a peria psis above atmosphere and the same semi-latus rectum as the specified counterbalance orbit).' ##################################################################### Coplanar capture sling options: #Values of "trajopt" > 100 setup the capture sling with its plane of rotation coplanar with its orbital plane, so there are two different tip speeds. This orientation is tidally stabilized and tether reelin g for maneuvering will have steep gravitational gradients. trajopt = 101; traj_string = 'COPLANAR SETUP. Capture slings release payloads A and B at r = periapsis_capture +/- sling A/B radius.'#Thi s used to continue: 'Counterbalance A either uses aerobraking or goes directly into an orbit with a periapsis above atmosphere and the sam e semi-latus rectum as the specified counterbalance orbit. Counterbal ance B either uses a tether to help lift a payload from a suborbital trajectory, aerobrakes until it can enter the specified counterbalan ce orbit, or aerobrakes until it hits Mars.'

#failure = '!!!!! FAILURE !!!!!' #This line is normally commented, so that serious errors crash the program to make the error more obviou s. The crashes happen by trying to 'print failure' so uncomment this line if those crashes aren't desired. warnstring = '!!!WARNING!!! ' if trajopt < 100 and mission_choice1 != mission_choice2: print warnstring,'Perpendicular slings require identical missions f or both slings.' print failure #Stop the program right here, because the variable 'f ailure' isn't defined.

grapple_fraction = 0.25 #Grapple mass is this fraction of payload mas s if it's accelerating at 1g. Grapple mass increases linearly with ac http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 8/173 9/6/2018 capture-orbit-sling celeration of the sling tip it's on, so it scales with the force plac ed on the grapple. material='Zylon'; sigma = 5.8E9; rho = 1560 #sigma = tensile s trength (Pa), rho = density (kg/m^3). #material='Spectra 2000'; sigma = 3.5E9; rho = 970 #sigma = tensile strength (Pa), rho = density (kg/m^3). safety = 1.0 #Engineering safety factor on sling tension. Jokic and L onguski 2004 table 3 gives a Phobos MEEM sling mass of 281 tons for a n 11.2 ton payload, effectively using safety = 1.0. Jokic and Longusk i 2004 table 4 simulates manufacturing errors to arrive at a Phobos M EEEM sling mass of 342 tons. For comparison, using a safety factor of 1.07 here yields a mass of 343 tons. throw_in = 1 #Up to two places on the capture orbit are at the moon's ("circular") radial distance. Set to "0" to throw the payload OUT aw ay from the planet or "1" to throw IN. throw_at_LAN = 1 #If 1/0, moon sling throws to the capture sling at t he longitude of the ascending/descending node. ballast_choice = 1 #If a sling's cbr != 1, the forces on the hub are unequal by default, which means the center of mass isn't at the hub. If ballast_choice == 0, ignore this problem for comparison with stud ies that also ignore it. If ballast_choice == 1, the heavier mass on each sling has an extra ballast_mass1/2 added to it. This mass is ze ro for the lighter mass (whether that's the payload or the counterbal ance). If ballast_choice == 2, the shorter sling is overbuilt as thou gh its v_c (characteristic velocity) were reduced by a factor of cbr. This is the same as multiplying the safety factor on that sling tens ion by cbr^2. Using this option, all ballast_mass values are set to z ero because the heavier counterbalance tether does all the necessary balancing. payload_mass = 1.0*1000.0 #Calculate sling mass for a payload with th is mass in kg. E.g. taxi (11.2*1000.0 kg), full vehicle (70*1000.0 k g), etc. best_case_offset = 0.0 #Only applies if cbrs[1]==0. Set best_case_off set=0.0 to match Jokic and Longuski 2004, where the moon's orbit is i n just the right orientation. Set best_case_offset=90 for a 90 degree offset, which is the worst case. mt4ct = 1 #Number of moon throws 4 each capture throw. Moon sling thr ows "mt4ct" payloads for each capture sling throw. Set mt4ct = 1 for the shortest possible time between capture sling throws. Set mt4ct = -1 to automatically size the moon payload to match the larger of the capture payload or capture counterbalance. This way neither the capt ure payload or counterbalance has to be subdivided. Set mt4ct = -2 to automatically size the moon payload to match the capture payload, so if the capture counterbalance is larger it will have to be subdivide d. If cbrs[1]==0, this is automatically disabled below by setting mt4 ct = 1. ms_spinup_time = 10*24*3600 #Spin up the moon sling over this amount of time. Set to -1 for a spinup time that's a perfect match for the shortest possible cycle time (based on the capture orbit revisit tim e and ms_attach_time below). #Set to -2 for a spinup time that's a p erfect match for the next shortest possible cycle time. Etc. If ms_sp inup_time is < 0, cs_spinup_time also have to be < 0, and vice versa. cs_spinup_time = 10*24*3600 #Spin up the capture slings over this amo unt of time. Set to -1 for a spinup time that's a perfect match for t he shortest possible cycle time (based on the capture orbit period, t he time from the moon to periapsis, and cs_attach_time below). #Set to -2 for a spinup time that's a perfect match for the next shortest http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 9/173 9/6/2018 capture-orbit-sling possible cycle time. Etc. If ms_spinup_time is < 0, cs_spinup_time a lso have to be < 0, and vice versa. ms_attach_time = 1.0*24*3600 #Time needed for the moon sling to attac h a new payload. cs_attach_time = 3.0*24*3600 #Time needed for the capture slings to r endezvous with and attach new payloads. estimate_misc = 1 #Set to 0 to zero all estimates of miscellaneous eq uipment: radiators, electric motors, the hub connecting the capture s lings. Set to 1 to copy the solar panel mass estimate as an extremely crude estimate. num_slings = 4 #Current system allows for a maximum of four slings- o ne on the moon and three in a system in an inclined resonant capture orbit (two with payload_mass and one extra payload that serves as mo ment of inertia ballast). cbrs = [0.0 for i in range(num_slings)] #Initialize list of counterba lance ratios. cbrs[0] = 4.0 #[dmr] #The first entry of cbrs is the ratio of moon sl ing counterbalance mass divided by moon sling payload mass. cbrs[1] = -1.0 #Set cbrs[1]=0 to disable the capture orbit sling, whi ch means the moon sling throws its payload directly into the specifie d hyperbolic escape trajectory. Any other value means the moon sling throws its payload into the inclined capture orbit so it can rendezv ous with the capture orbit sling. If cbrs[1] > 0, the first capture s ling's counterbalance ratio will be exactly cbrs[1], regardless of wh ere the counterbalance goes. If cbrs[1] < 0, the first capture slin g's counterbalance ratio will be calculated based on the desired coun terbalance orbits, and the value of cbrs[1] here is ignored. cbrs[2] = -1.0 #If cbrs[2] > 0, the second capture sling's counterbal ance ratio will be exactly cbrs[2], regardless of where the counterba lance goes. If cbrs[2] < 0, the second capture sling's counterbalance ratio will be calculated based on the desired counterbalance orbits, and the value of cbrs[2] here is ignored. tolerance = 1e-6 #Used to compare floating point values (e.g. any val ue less than tolerance is treated as "zero" in some calculations). To lerance can't currently go below 1e-6, otherwise find_root fails to f ind the eccentric anomaly in kepler2xyz. Probably need to replace fin d_root with code from Project Pluto: https://www.projectpluto.com/ long_time = 1/tolerance^2 #Used to calculate the payload velocity aft er a "long time" has passed. if abs(cbrs[1]) < tolerance: mt4ct = 1 #If capture orbit sling is dis abled, override number of moon throws for each capture throw. dmr = 4.60333884875169 #The "dangerous mass ratio" from near the bott om of this notebook. Any higher counterbalance mass ratio causes the longer sling to hit the heavier mass as it leaves.

Isp = 379 #Rocket comparison - specific impulse in seconds. This val ue is for a LOX/CH4 chemical rocket, as in Jokic and Longuski 2004. s2p = 0.15 #Rocket comparison - structural to propellant mass ratio. Use 0.15 to match Jokic and Longuski 2004.

descs = ['moon sling'] #Verbose descriptions of the first index in many lists. 1st index is the sling (0=M,1=A,2=B,3=C). descs += ['capture sling A'] ; descs += ['capture sling B'] ; descs + = ['capture sling C'] sdescs = ['M'] #Short descriptions. sdescs += ['A'] ; sdescs += ['B'] ; sdescs += ['C']

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 10/173 9/6/2018 capture-orbit-sling def cap1st(string1): """ Input : String of characters.\n Output: Same string, with the first letter capitalized but no other changes (as would happen if just capitalize() were used). """ return string1[0].capitalize()+string1[1:]

for j in range(num_slings-1): if cbrs[j] > tolerance: if cbrs[j] > dmr: print '!!! DANGER !!!',cap1st(descs[j]),'mass ratio',cbrs[j],'is higher than the safe limit of',dmr,'!!!' if cbrs[j] < 1/dmr: print '!!! DANGER !!!',cap1st(descs[j]),'mass ratio',cbrs[j],'is lower than the safe limit of',1/dmr,'!!!'

#Choose the units used to display time, then uncomment that line: #units=3600; uname='hours' units=3600*24; uname='days' #units=88775.244147; uname='sols' #https://en.wikipedia.org/wiki/Ti mekeeping_on_Mars #units=3600*24*7; uname='weeks' #units=3600*24*365.25; uname='years' year = 3600*24*365.25

summary_digits = 5 #Many print statements in this notebook will print "summary_digits" significant figures.

mu_mars = 4.282837E13 #G*M for Mars. https://en.m.wikipedi a.org/wiki/Standard_gravitational_parameter mu_sun = 1.32712440018E20 #G*M for the Sun. https://en.m. wikipedia.org/wiki/Standard_gravitational_parameter mu_earth = 3.986004418E14 #G*M for the Earth. https://en.m. wikipedia.org/wiki/Standard_gravitational_parameter a_mars = 227.9392E9 # https://en.m.wikipedia.org/wiki/Mar s a_earth = 149.598023E9 # https://en.m.wikipedia.org/wiki/E arth a_jupiter = 778570000000 #In meters. https://en.wikipedia.or g/wiki/Jupiter ecc_mars = 0.0934 # https://en.m.wikipedia.org/wiki/Mars ecc_earth = 0.0167086 # https://en.m.wikipedia.org/wiki/Eart h ecc_jupiter = 0.0489 #In meters. https://en.wikipedia.org/wik i/Jupiter inc_mars = 1.850*pi/180 # Relative to ecliptic plane. http s://en.m.wikipedia.org/wiki/Mars inc_earth = 0 #By definition. inc_jupiter = 1.303*pi/180 #Relative to ecliptic plane http s://en.wikipedia.org/wiki/Jupiter solar_constant = 1361 #Watts/square meter at 1 AU. https://en.wik ipedia.org/wiki/Solar_constant radius_sun = 695700000.0 #The Sun's equatorial radius in mete rs. https://en.wikipedia.org/wiki/Sun radius_mars = 3396000.0 #Mars's equatorial radius. https://e n.wikipedia.org/wiki/Mars polar_radius_mars = 3376200.0 #Mars's polar radius. https://en.wik ipedia.org/wiki/Mars http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 11/173 9/6/2018 capture-orbit-sling axial_tilt_mars = 25.19*pi/180 #Axial tilt in radians, relative to its orbital plane. J2_mars = 1960.45E-6 #Oblateness of Mars, given as "J2" he re: https://nssdc.gsfc.nasa.gov/planetary/factsheet/marsfact.html ht tp://archive.li/4Bv1P soi_mars = a_mars*(mu_mars/mu_sun)^0.4 #5.76E8m from http s://en.m.wikipedia.org/wiki/Sphere_of_influence_(astrodynamics) radius_earth = 6378100.0 #Earth's equatorial radius. https://e n.m.wikipedia.org/wiki/Earth polar_radius_earth = 6356800.0 #Earth's polar radius. https://en.m.wi kipedia.org/wiki/Earth axial_tilt_earth = 23.439*pi/180 #Axial tilt in radians, relative t o its orbital plane. J2_earth = 1082.63E-6 #Oblateness of Earth, given as "J2" h ere: https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html http://archive.li/Jj9PO soi_earth = a_earth*(mu_earth/mu_sun)^0.4 #9.24E8m from http s://en.m.wikipedia.org/wiki/Sphere_of_influence_(astrodynamics) periapsis_phobos = 9234420.0 # https://en.wikipedia.org/wiki/Phobo s_(moon) apoapsis_phobos = 9517580.0 periapsis_deimos = 23455500.0 # https://en.wikipedia.org/wiki/Deimo s_(moon) apoapsis_deimos = 23470900.0 a_phobos = (apoapsis_phobos + periapsis_phobos)/2 ; b_phobo s = sqrt(apoapsis_phobos*periapsis_phobos) a_deimos = (apoapsis_deimos + periapsis_deimos)/2 ; b_deimo s = sqrt(apoapsis_deimos*periapsis_deimos) period_phobos = (2*pi*sqrt(a_phobos^3/mu_mars)).n() #mu_mars ins tead of mu because Phobos and Deimos orbit Mars. period_deimos = (2*pi*sqrt(a_deimos^3/mu_mars)).n() inc_phobos = 26.04*pi/180 #Inclination in radians, relative t o ecliptic plane. inc_deimos = 27.58*pi/180

#These values for the Martian atmosphere are currently copied over to other planets like Earth, but if you plan to throw from Earth or aer obrake in its atmosphere you should change those values. mars_aerobrake_alt = 110000.0 #The lowest altitude used in the Mars G lobal Surveyor aerobraking process: http://web.archive.org/web/201704 16044559/https://mars.jpl.nasa.gov/mgs/sci/aerobrake/SFMech.html mars_atmo_alt = 253000.0 #MERITT's Marswhip tip never goes below 253 km altitude, so treat that as the effective height of Mars's atm osphere. lmo_alt = 400000.0 #The counterbalance masses can be sent into the 400km low Mars orbit (LMO) described in table 1 of http://w ww.csc.caltech.edu/references/Hopkins-Phobos-Deimos-Paper.pdf

#Choose a planet of origin, then uncomment that line. pname = 'Mars'; mu = mu_mars; a_pl = a_mars; r_pl = radius_mars; polar_r_pl = polar_radius_mars; axial_tilt_pl = axial_tilt_mars; s oi_pl = soi_mars; ecc_pl = ecc_mars; inc_pl = inc_mars; aerobrake_a lt = mars_aerobrake_alt; atmo_alt = mars_atmo_alt; target_alt = lmo_a lt; J2 = J2_mars #pname = 'Earth'; mu = mu_earth; a_pl = a_earth; r_pl = radius_earth; polar_r_pl = polar_radius_earth; axial_tilt_pl = axial_tilt_earth; s oi_pl = soi_earth; ecc_pl = ecc_earth; inc_pl = inc_earth; aerobrake_ http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 12/173 9/6/2018 capture-orbit-sling alt = mars_aerobrake_alt; atmo_alt = mars_atmo_alt; target_alt = lmo_ alt; J2 = J2_earth #pname = 'GM=1'; mu = 1.0; a_pl = a_earth; r_pl = radius_earth; polar_r_pl = polar_radius_earth; soi_pl = 1; ecc_pl = 0; inc_pl = 0; aerobrake_alt = mars_aerobrake_alt; atmo_alt = mars_atmo_alt; target _alt = lmo_alt #Sets G*M = 1.0 for comparison with this: https://jan us.astro.umd.edu/orbits/elements/convertframe.html

#Choose a moon of origin (Phobos or Deimos), then uncomment that lin e: #moon_name = 'Phobos'; periapsis_moon = periapsis_phobos; apoapsis_mo on = apoapsis_phobos; inc_moon = inc_phobos moon_name = 'Deimos'; periapsis_moon = periapsis_deimos; apoapsis_moo n = apoapsis_deimos; inc_moon = inc_deimos

#The planet's oblateness ("J2") affects the capture sling orbit in th ree ways, described here: http://farside.ph.utexas.edu/teaching/celes tial/Celestialhtml/node93.html http://archive.li/amqki #AP_rate = 3*J2/4*mean_motion_cs*r_pl^2/a_cs^2*(5*cos(inc_c2q)-1)/ (1-ecc_cs^2)^2 #Argument of periapsis rotation rate due to J2. Equati on 10.127 http://archive.li/amqki #LAN_rate = -3*J2/2*mean_motion_cs*r_pl^2/a_cs^2*cos(inc_c2q) #Node p recession rate due to J2. Equation 10.128 http://archive.li/amqki #mean_motion_cs_factor = 1.0 + 3*J2/2*(r_pl/a_cs)^2*(1-ecc_cs^2)^(-3/ 2)*(1-3/2*sin(inc_c2q)^2) #Mean motion factor due to J2. Equation 10. 129 (just the part in brackets because this is just the factor multip lying the standard Keplerian mean motion) http://archive.li/amqki

a_moon = (apoapsis_moon + periapsis_moon)/2 ; b_moon = sqrt(ap oapsis_moon*periapsis_moon) speed_moon = sqrt(mu/a_moon) period_moon = (2*pi*sqrt(a_moon^3/mu)).n() # https://en.wikipedia.or g/wiki/Elliptic_orbit#Orbital_period

#Specify one of the apsides of the capture orbit. Doesn't matter if i t's the apoapsis or periapsis. The resonance search will look for ano ther which satisfies the desired , then the captu re orbit periapsis/apoapsis will be set equal to the smaller/larger a psis. lowest_apsis = r_pl + target_alt + r_max #Any lower and the tips of the capture slings might hit objects in the target orbit. apsis1 = lowest_apsis #apsis1 = a_moon #This could either be a periapsis or an apoap sis.

min_s= 1; min_p= 1; #Sling completes "s" orbits in the same time it t akes the moon to complete "p" orbits. max_s=20; max_p=30; #Given periapsis_capture (specified above), displ ay resonances in these s,p ranges satisfying these conditions: #lower_bound = 12*units #Choose upper and lower bounds of period_reso nance to display in find_resonance. #upper_bound = 15*units #These bounds are useful for high v_inf missi ons. lower_bound = 0.0#*period_moon #Choose upper and lower bounds of peri od_resonance to display in find_resonance. upper_bound = 1.1*period_moon #These bounds are useful for low v_inf missions. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 13/173 9/6/2018 capture-orbit-sling assume(r,'real') list_resn_s = [] ; list_resn_p = [] ; list_periapsis = [] ; list_apoa psis = [] print 'Resonance s:p is defined as: s*period_capture = p*period_moo n.' for s in range(min_s,max_s+1): #range(n1,n2) goes up to n2-1. sys.stdout.flush() for p in range(min_p,max_p+1): if (s != p or s==1) and (s%2 + p%2 != 0) and (s%3 + p%3 != 0) and (s%4 + p%4 != 0) and (s%5 + p%5 != 0) and (s%6 + p%6 != 0) and (s%7 + p%7 != 0) and (s%8 + p%8 != 0) and (s%9 + p%9 != 0) and (s%10 + p% 10 != 0): #eliminate some redundant resonances like 2:2 or 9:3 by bru te force period_resonance = (RDF(p)/RDF(s))*period_moon revisit_time = s*period_resonance #equals p*target_period #if revisit_time < upper_bound and revisit_time > lower_bound: if period_resonance < upper_bound and period_resonance > lower_ bound: #Orbital period = 2*pi*sqrt(a^3/mu) = 2*pi*sqrt(((r_p + r_a)/ 2)^3/mu) apsis2 = (2*(mu*(period_resonance/(2*pi))^2)^(1/3) - apsis1). n() if apsis2 > lowest_apsis and max([apsis1,apsis2]) >= a_moon: #Don't display any resonances so low that the tips of the capture sli ngs might hit objects in low Mars orbit. Also don't bother displaying resonances with an apoapsis below the chosen moon's "circular" orbi t. list_resn_s += [s] ; list_resn_p += [p] list_periapsis += [min([apsis1,apsis2])] ; list_apoapsis += [max([apsis1,apsis2])] ecc = (list_apoapsis[-1] - list_periapsis[-1]) / (list_apoa psis[-1] + list_periapsis[-1]) print 'Choice',str(len(list_resn_s)).rjust(3)+' =',str(list _resn_s[-1]).rjust(2)+':',str(list_resn_p[-1]).ljust(2),'resn w',moon _name+', period=',str((period_resonance/units).n(digits=5)).rjust(7)+ ', revisit time=',(revisit_time/units).n(digits=5),uname+', periapsis =',(list_periapsis[-1]/1000).n(digits=5),', apoapsis=',(list_apoapsis [-1]/1000).n(digits=5),'km, ecc=',ecc.n(digits=5) sys.stdout.flush() print 'Finished displaying resonances.'

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 14/173 9/6/2018 capture-orbit-sling

Resonance s:p is defined as: s*period_capture = p*period_moon. Choice 1 = 1: 1 resn w Deimos, period= 1.2629, revisit time= 1.2 629 days, periapsis= 3920.5 , apoapsis= 43006. km, ecc= 0.83291 Choice 2 = 2: 1 resn w Deimos, period= 0.63147, revisit time= 1.2 629 days, periapsis= 3920.5 , apoapsis= 25641. km, ecc= 0.73476 Choice 3 = 3: 2 resn w Deimos, period= 0.84196, revisit time= 2.5 259 days, periapsis= 3920.5 , apoapsis= 31891. km, ecc= 0.78105 Choice 4 = 4: 3 resn w Deimos, period= 0.94720, revisit time= 3.7 888 days, periapsis= 3920.5 , apoapsis= 34816. km, ecc= 0.79758 Choice 5 = 5: 3 resn w Deimos, period= 0.75776, revisit time= 3.7 888 days, periapsis= 3920.5 , apoapsis= 29462. km, ecc= 0.76512 Choice 6 = 5: 4 resn w Deimos, period= 1.0103, revisit time= 5.0 517 days, periapsis= 3920.5 , apoapsis= 36520. km, ecc= 0.80611 Choice 7 = 6: 5 resn w Deimos, period= 1.0524, revisit time= 6.3 147 days, periapsis= 3920.5 , apoapsis= 37635. km, ecc= 0.81131 Choice 8 = 7: 4 resn w Deimos, period= 0.72168, revisit time= 5.0 517 days, periapsis= 3920.5 , apoapsis= 28394. km, ecc= 0.75735 Choice 9 = 7: 5 resn w Deimos, period= 0.90210, revisit time= 6.3 147 days, periapsis= 3920.5 , apoapsis= 33577. km, ecc= 0.79089 Choice 10 = 7: 6 resn w Deimos, period= 1.0825, revisit time= 7.5 776 days, periapsis= 3920.5 , apoapsis= 38423. km, ecc= 0.81483 Choice 11 = 8: 5 resn w Deimos, period= 0.78933, revisit time= 6.3 147 days, periapsis= 3920.5 , apoapsis= 30383. km, ecc= 0.77142 Choice 12 = 8: 7 resn w Deimos, period= 1.1051, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 39009. km, ecc= 0.81735 Choice 13 = 9: 5 resn w Deimos, period= 0.70163, revisit time= 6.3 147 days, periapsis= 3920.5 , apoapsis= 27792. km, ecc= 0.75275 Choice 14 = 9: 7 resn w Deimos, period= 0.98228, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 35767. km, ecc= 0.80243 Choice 15 = 9: 8 resn w Deimos, period= 1.1226, revisit time= 10. 103 days, periapsis= 3920.5 , apoapsis= 39462. km, ecc= 0.81926 Choice 16 = 10: 7 resn w Deimos, period= 0.88406, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 33075. km, ecc= 0.78806 Choice 17 = 10: 9 resn w Deimos, period= 1.1366, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 39823. km, ecc= 0.82075 Choice 18 = 11: 5 resn w Deimos, period= 0.57406, revisit time= 6.3 147 days, periapsis= 3920.5 , apoapsis= 23821. km, ecc= 0.71736 Choice 19 = 11: 6 resn w Deimos, period= 0.68887, revisit time= 7.5 776 days, periapsis= 3920.5 , apoapsis= 27407. km, ecc= 0.74971 Choice 20 = 11: 7 resn w Deimos, period= 0.80369, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 30798. km, ecc= 0.77415 Choice 21 = 11: 8 resn w Deimos, period= 0.91850, revisit time= 10. 103 days, periapsis= 3920.5 , apoapsis= 34030. km, ecc= 0.79339 Choice 22 = 11: 9 resn w Deimos, period= 1.0333, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 37130. km, ecc= 0.80899 Choice 23 = 11: 10 resn w Deimos, period= 1.1481, revisit time= 12. 629 days, periapsis= 3920.5 , apoapsis= 40117. km, ecc= 0.82195 Choice 24 = 11: 12 resn w Deimos, period= 1.3777, revisit time= 15. 155 days, periapsis= 3920.5 , apoapsis= 45808. km, ecc= 0.84233 Choice 25 = 12: 7 resn w Deimos, period= 0.73671, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 28841. km, ecc= 0.76066 Choice 26 = 12: 11 resn w Deimos, period= 1.1577, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 40361. km, ecc= 0.82293 Choice 27 = 12: 13 resn w Deimos, period= 1.3682, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 45578. km, ecc= 0.84159 Choice 28 = 13: 6 resn w Deimos, period= 0.58289, revisit time= 7.5 776 days, periapsis= 3920.5 , apoapsis= 24105. km, ecc= 0.72022 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 15/173 9/6/2018 capture-orbit-sling Choice 29 = 13: 7 resn w Deimos, period= 0.68004, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 27138. km, ecc= 0.74755 Choice 30 = 13: 8 resn w Deimos, period= 0.77719, revisit time= 10. 103 days, periapsis= 3920.5 , apoapsis= 30030. km, ecc= 0.76905 Choice 31 = 13: 9 resn w Deimos, period= 0.87434, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 32804. km, ecc= 0.78649 Choice 32 = 13: 10 resn w Deimos, period= 0.97149, revisit time= 12. 629 days, periapsis= 3920.5 , apoapsis= 35476. km, ecc= 0.80097 Choice 33 = 13: 11 resn w Deimos, period= 1.0686, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 38060. km, ecc= 0.81323 Choice 34 = 13: 12 resn w Deimos, period= 1.1658, revisit time= 15. 155 days, periapsis= 3920.5 , apoapsis= 40568. km, ecc= 0.82375 Choice 35 = 13: 14 resn w Deimos, period= 1.3601, revisit time= 17. 681 days, periapsis= 3920.5 , apoapsis= 45383. km, ecc= 0.84096 Choice 36 = 14: 9 resn w Deimos, period= 0.81189, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 31033. km, ecc= 0.77568 Choice 37 = 14: 11 resn w Deimos, period= 0.99231, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 36037. km, ecc= 0.80377 Choice 38 = 14: 13 resn w Deimos, period= 1.1727, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 40744. km, ecc= 0.82445 Choice 39 = 14: 15 resn w Deimos, period= 1.3531, revisit time= 18. 944 days, periapsis= 3920.5 , apoapsis= 45215. km, ecc= 0.84042 Choice 40 = 15: 7 resn w Deimos, period= 0.58937, revisit time= 8.8 405 days, periapsis= 3920.5 , apoapsis= 24312. km, ecc= 0.72228 Choice 41 = 15: 8 resn w Deimos, period= 0.67357, revisit time= 10. 103 days, periapsis= 3920.5 , apoapsis= 26941. km, ecc= 0.74593 Choice 42 = 15: 11 resn w Deimos, period= 0.92615, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 34240. km, ecc= 0.79453 Choice 43 = 15: 13 resn w Deimos, period= 1.0945, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 38736. km, ecc= 0.81618 Choice 44 = 15: 14 resn w Deimos, period= 1.1787, revisit time= 17. 681 days, periapsis= 3920.5 , apoapsis= 40896. km, ecc= 0.82505 Choice 45 = 15: 16 resn w Deimos, period= 1.3471, revisit time= 20. 207 days, periapsis= 3920.5 , apoapsis= 45069. km, ecc= 0.83995 Choice 46 = 16: 9 resn w Deimos, period= 0.71040, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 28056. km, ecc= 0.75479 Choice 47 = 16: 11 resn w Deimos, period= 0.86827, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 32633. km, ecc= 0.78550 Choice 48 = 16: 13 resn w Deimos, period= 1.0261, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 36940. km, ecc= 0.80810 Choice 49 = 16: 15 resn w Deimos, period= 1.1840, revisit time= 18. 944 days, periapsis= 3920.5 , apoapsis= 41030. km, ecc= 0.82556 Choice 50 = 16: 17 resn w Deimos, period= 1.3419, revisit time= 21. 470 days, periapsis= 3920.5 , apoapsis= 44941. km, ecc= 0.83953 Choice 51 = 17: 8 resn w Deimos, period= 0.59432, revisit time= 10. 103 days, periapsis= 3920.5 , apoapsis= 24470. km, ecc= 0.72382 Choice 52 = 17: 9 resn w Deimos, period= 0.66861, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 26790. km, ecc= 0.74468 Choice 53 = 17: 10 resn w Deimos, period= 0.74290, revisit time= 12. 629 days, periapsis= 3920.5 , apoapsis= 29024. km, ecc= 0.76200 Choice 54 = 17: 11 resn w Deimos, period= 0.81719, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 31185. km, ecc= 0.77665 Choice 55 = 17: 12 resn w Deimos, period= 0.89148, revisit time= 15. 155 days, periapsis= 3920.5 , apoapsis= 33282. km, ecc= 0.78924 Choice 56 = 17: 13 resn w Deimos, period= 0.96577, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 35321. km, ecc= 0.80019 Choice 57 = 17: 14 resn w Deimos, period= 1.0401, revisit time= 17. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 16/173 9/6/2018 capture-orbit-sling 681 days, periapsis= 3920.5 , apoapsis= 37309. km, ecc= 0.80982 Choice 58 = 17: 15 resn w Deimos, period= 1.1144, revisit time= 18. 944 days, periapsis= 3920.5 , apoapsis= 39249. km, ecc= 0.81837 Choice 59 = 17: 16 resn w Deimos, period= 1.1886, revisit time= 20. 207 days, periapsis= 3920.5 , apoapsis= 41147. km, ecc= 0.82602 Choice 60 = 17: 18 resn w Deimos, period= 1.3372, revisit time= 22. 733 days, periapsis= 3920.5 , apoapsis= 44829. km, ecc= 0.83916 Choice 61 = 18: 11 resn w Deimos, period= 0.77179, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 29873. km, ecc= 0.76797 Choice 62 = 18: 13 resn w Deimos, period= 0.91212, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 33854. km, ecc= 0.79243 Choice 63 = 18: 17 resn w Deimos, period= 1.1928, revisit time= 21. 470 days, periapsis= 3920.5 , apoapsis= 41251. km, ecc= 0.82642 Choice 64 = 18: 19 resn w Deimos, period= 1.3331, revisit time= 23. 996 days, periapsis= 3920.5 , apoapsis= 44728. km, ecc= 0.83883 Choice 65 = 19: 9 resn w Deimos, period= 0.59823, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 24595. km, ecc= 0.72503 Choice 66 = 19: 10 resn w Deimos, period= 0.66470, revisit time= 12. 629 days, periapsis= 3920.5 , apoapsis= 26670. km, ecc= 0.74368 Choice 67 = 19: 11 resn w Deimos, period= 0.73117, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 28676. km, ecc= 0.75946 Choice 68 = 19: 12 resn w Deimos, period= 0.79764, revisit time= 15. 155 days, periapsis= 3920.5 , apoapsis= 30623. km, ecc= 0.77301 Choice 69 = 19: 13 resn w Deimos, period= 0.86411, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 32517. km, ecc= 0.78481 Choice 70 = 19: 14 resn w Deimos, period= 0.93058, revisit time= 17. 681 days, periapsis= 3920.5 , apoapsis= 34362. km, ecc= 0.79518 Choice 71 = 19: 15 resn w Deimos, period= 0.99705, revisit time= 18. 944 days, periapsis= 3920.5 , apoapsis= 36164. km, ecc= 0.80439 Choice 72 = 19: 16 resn w Deimos, period= 1.0635, revisit time= 20. 207 days, periapsis= 3920.5 , apoapsis= 37926. km, ecc= 0.81263 Choice 73 = 19: 17 resn w Deimos, period= 1.1300, revisit time= 21. 470 days, periapsis= 3920.5 , apoapsis= 39652. km, ecc= 0.82005 Choice 74 = 19: 18 resn w Deimos, period= 1.1965, revisit time= 22. 733 days, periapsis= 3920.5 , apoapsis= 41345. km, ecc= 0.82678 Choice 75 = 19: 20 resn w Deimos, period= 1.3294, revisit time= 25. 259 days, periapsis= 3920.5 , apoapsis= 44638. km, ecc= 0.83853 Choice 76 = 20: 9 resn w Deimos, period= 0.56832, revisit time= 11. 366 days, periapsis= 3920.5 , apoapsis= 23636. km, ecc= 0.71546 Choice 77 = 20: 11 resn w Deimos, period= 0.69461, revisit time= 13. 892 days, periapsis= 3920.5 , apoapsis= 27581. km, ecc= 0.75109 Choice 78 = 20: 13 resn w Deimos, period= 0.82091, revisit time= 16. 418 days, periapsis= 3920.5 , apoapsis= 31292. km, ecc= 0.77732 Choice 79 = 20: 17 resn w Deimos, period= 1.0735, revisit time= 21. 470 days, periapsis= 3920.5 , apoapsis= 38187. km, ecc= 0.81379 Choice 80 = 20: 19 resn w Deimos, period= 1.1998, revisit time= 23. 996 days, periapsis= 3920.5 , apoapsis= 41428. km, ecc= 0.82710 Choice 81 = 20: 21 resn w Deimos, period= 1.3261, revisit time= 26. 522 days, periapsis= 3920.5 , apoapsis= 44557. km, ecc= 0.83826 Finished displaying resonances.

3. Choose an orbital resonance for the capture slings. (Go back to the top). http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 17/173 9/6/2018 capture-orbit-sling

In [3]: resonance_choice = 18 resn_s = list_resn_s[resonance_choice-1] resn_p = list_resn_p[resonance_choice-1] periapsis_capture = list_periapsis[resonance_choice-1] apoapsis_capture = list_apoapsis[resonance_choice-1] a_capture = (apoapsis_capture + periapsis_capture)/2 ; b_capt ure = sqrt(apoapsis_capture*periapsis_capture) period_capture = (2*pi*sqrt(a_capture^3/mu_mars)).n()

for j in range(1,3): print cap1st(descs[j]),'throws payloads with v_i nf =',str(v_infs[mis[j]].n(digits=5))+', which is a',mnames[mis[j]], 'mission.'

print '\nResonance number',resonance_choice,'was chosen.\n',str(resn_ s)+':'+str(resn_p),'resonance with',moon_name+', period=',str((period _capture/units).n(digits=5)).rjust(7)+', revisit time=',((resn_p*peri od_moon)/units).n(digits=5),uname+'.'

lj=37 print '\nCapture orbit apoapsis (km)'.ljust(lj+1),'=',apoapsis_captur e/1000.0 print 'Capture orbit periapsis (km)'.ljust(lj),'=',periapsis_capture/ 1000.0 print 'Capture orbit periapsis altitude (km)'.ljust(lj),'=',(periapsi s_capture-r_pl)/1000.0 ecc = (apoapsis_capture - periapsis_capture) / (apoapsis_capture + pe riapsis_capture) print 'Capture orbit eccentricity'.ljust(lj),'=',ecc Capture sling A throws payloads with v_inf = 2640.0, which is a M-E H ohmann mission. Capture sling B throws payloads with v_inf = 2640.0, which is a M-E H ohmann mission.

Resonance number 18 was chosen. 11:5 resonance with Deimos, period= 0.57406, revisit time= 6.3147 day s.

Capture orbit apoapsis (km) = 23821.3846522879 Capture orbit periapsis (km) = 3920.46738022426 Capture orbit periapsis altitude (km) = 524.467380224261 Capture orbit eccentricity = 0.717360803768281

4. Estimate the required tip speed to compare with the tip speed specified in step 1. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 18/173 9/6/2018 capture-orbit-sling

In [4]: #It's easiest to orient the initial capture sling orbit so its periap sis position lies along the positive x axis because that matches the definition of Euler angles: #https://en.wikipedia.org/wiki/Orbital_elements#Euler_angle_transform ations #"x, y are in the orbital plane and with x in the direction to the pe ricenter (periapsis). z is perpendicular to the plane of the orbit. y is mutually perpendicular to x and z." com_cs_pos0 = vector([periapsis_capture,0,0]) #Capture sl ing's center of mass position vector when it reaches periapsis. capture_speed_periapsis = sqrt(mu_mars*(2/periapsis_capture-1/a_captu re)) capture_speed_apoapsis = sqrt(mu_mars*(2/apoapsis_capture-1/a_captur e)) com_cs_vel0 = vector([0,capture_speed_periapsis,0]) #Capt ure sling's center of mass velocity vector when it reaches periapsis. H_cs = com_cs_pos0.cross_product(com_cs_vel0) H_cs_unit = H_cs/H_cs.norm() if H_cs[2].n() > 0: orbit_sign = 1 #Capture orbit sling c.o.m. orbit s counter-clockwise, so its anomaly increases over time. else: orbit_sign = -1 #Capture orbit sling c.o.m. orbit s clockwise, so its anomaly DEcreases over time.

#Position and velocity of moon when it throws its payload to the capt ure sling at the moment it passes through its ascending node in the e cliptic plane. #The "0" suffix indicades that these are the initial vectors in the m oon frame (described below) before they're rotated into the capture f rame. p_moon0 = com_cs_pos0*a_moon/com_cs_pos0.norm() v_moon0 = com_cs_vel0*speed_moon/com_cs_vel0.norm()

#Rotate the moon's position and velocity vectors so the x-y plane is the ecliptic. I.e. rotate into the capture frame. #This only works if the moon's position vector lies along the x axis. if abs(p_moon0[0] - p_moon0.norm()) > tolerance: print warnstring,'p_moon0 needs to lie along the x-axis but instead it equals:',p_moon0 print failure

def rotateX(rad): """ Input : Angle in radians.\n Output: 3D rotation matrix about Z axis by 'rad' radians.\n Reference: https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rota tions """ return matrix([[1,0,0],[0,cos(rad),-sin(rad)],[0,sin(rad),cos(rad )]])

p_moon1 = rotateX(inc_moon)*p_moon0 #Since p_moon0 currently lies alo ng the x-axis, this means p_moon1 = p_moon0. v_moon1 = rotateX(inc_moon)*v_moon0

def restrict_rad(r): """ http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 19/173 9/6/2018 capture-orbit-sling Input : Angle in radians.\n Output: Restricts angle to lie in [0,2*pi). """ while r >= 2*pi - tolerance: r = r - 2*pi #Offset [0,2*pi) by toler ance, otherwise deg = 359.999999 doesn't get changed to -0.000001. while r < -tolerance: r = r + 2*pi #Offset [0,2*pi) by toler ance, otherwise r = -1e-15 gets changed to r = 2*pi. return r

LAN_moon = restrict_rad(atan2(p_moon1[1],p_moon1[0])) #Moon's longitu de of the ascending node through the ecliptic plane. Sagemath expects arctan2(y,x). moon_frame_string = 'Final vectors in this cell are calculated in a frame where '+moon_name+' stays in the x-y plane and the capture slings are inclined by '+str((inc_moon*180 /pi).n(digits=4))+' degrees with a longitude of ascending node arbitr arily set to '+str((LAN_moon*180/pi).n(digits=4))+' degrees.\n' #Newe r code doesn't use this frame. if abs(cbrs[1]) > tolerance: capture_frame_string = 'Final vectors in this cell are calculated in a frame where the capture slings stay in the x-y (ecliptic) plane' else: capture_frame_string = 'Final vectors in this cell are calculated in a frame where the x-y plane is the eclip tic' capture_frame_string += ' and '+moon_name+' is inclined by '+str((inc _moon*180/pi).n(digits=4))+' degrees with a longitude of ascending no de arbitrarily set to '+str((LAN_moon*180/pi).n(digits=4))+' degrees. \n' nullvec = vector([0,0,0])

# = v^2/2 - mu/r = -mu/(2*a) #v = sqrt(mu*(2/r-1/a))

#Calculate the required tip speed of the capture sling to achieve the desired v_inf: assume(v_tip>0,r>0) eq_v_tip = v_infs[mis[1]]^2 == 2*((v+v_tip)^2/2 - mu/r) #Definition: C3 characteristic energy = v_inf^2 = 2*specific orbital energy http s://en.wikipedia.org/wiki/Characteristic_energy

#For simplicity, first consider a capture sling with its plane of rot ation perpendicular to its orbital plane. If the capture sling's axis of rotation points radially away from Mars when it releases the payl oad at periapsis while rotating perpendicular to Mars, r ~= periapsis _capture. (If the sling were perfectly straight and perpendicular, r would be sqrt(periapsis_capture^2+r_max^2). However, the sling will bend towards Mars somewhat so just use r = periapsis_capture to simp lify the calculations.) soln_v_tip = solve(eq_v_tip.subs(v=capture_speed_periapsis,r=periapsi s_capture),v_tip) v_tip_capture_payload1 = soln_v_tip[0].rhs().n() #This is called capt ure_payload1 because the capture sling actually throws two payloads.

#Alternatively, consider a capture sling with its plane of rotation c oplanar with its orbital plane, so there are two different tip speed s. If the capture sling releases the payload at the farthest point fr om Mars, r = periapsis_capture+r_max . http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 20/173 9/6/2018 capture-orbit-sling soln_v_tip = solve(eq_v_tip.subs(v=capture_speed_periapsis,r=periapsi s_capture+r_max),v_tip) v_tip_capture_farside = soln_v_tip[0].rhs().n() #If the capture sling releases the payload at the nearest point from Mars, r = periapsis_capture-r_max . soln_v_tip = solve(eq_v_tip.subs(v=capture_speed_periapsis,r=periapsi s_capture-r_max),v_tip) v_tip_capture_nearside = soln_v_tip[0].rhs().n()

#Calculate tip speed of the best-case direct moon sling needed to ach ieve the desired v_inf (for Phobos, this ~reproduces values in Jokic and Longuski 2004 table 3) soln3 = solve(eq_v_tip.subs(v=speed_moon,r=a_moon),v_tip) v_tip_direct_moon_best_case = soln3[0].rhs().n()

print '\n',traj_string,'\n' lj=45 desc = pname+' sphere of influence (km)' ; print desc.ljust(lj),'=',s oi_pl/1000.0 if abs(cbrs[1]) > tolerance: print 'Capture orbit apoapsis (km)'.ljust(lj),'=',apoapsis_capture/ 1000.0 print 'Capture orbit periapsis (km)'.ljust(lj),'=',periapsis_captur e/1000.0 print 'Capture orbit periapsis altitude (km)'.ljust(lj),'=',(periap sis_capture-r_pl)/1000.0 desc = 'Capture orbit period ('+uname+')' ; print desc.ljust(lj), '=',period_capture/units desc = 'Phobos period ('+uname+')' ; print desc.ljust(lj),'=',period_ phobos/units desc = 'Deimos period ('+uname+')' ; print desc.ljust(lj),'=',period_ deimos/units print 'Maximum sling radius (km)'.ljust(lj),'=',r_max/1000.0 if abs(cbrs[1]) > tolerance: print 'Capture orbit speed at periapsis (m/s)'.ljust(lj),'=',capture_speed_periapsis print 'Required tip speed of best-case direct moon sling (m/s)'.ljust (lj),'=',v_tip_direct_moon_best_case

if abs(cbrs[1]) > tolerance: print '\nConsider a capture sling with its plane of rotation perpen dicular to its orbital plane:' print 'Required tip speed of capture sling (m/s) =',v_tip_capture_payload1 if trajopt < 100 and v_tip_capture_payload1 > v_tip_max: print warnstring,'This tip speed is greater than the specified ma ximum tip speed of',v_tip_max,'m/s!' print failure print '\nConsider a capture sling with its plane of rotation coplan ar with its orbital plane, so there are two different tip speeds:' print 'Required tip speed of capture sling (coplanar setup- far sid e) =',v_tip_capture_farside print 'Required tip speed of capture sling (coplanar setup- near si de) =',v_tip_capture_nearside if trajopt >= 100 and v_tip_capture_nearside > v_tip_max: print warnstring,'The near side tip speed is greater than the spe cified maximum tip speed of',v_tip_max,'m/s!' print failure http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 21/173 9/6/2018 capture-orbit-sling

COPLANAR SETUP. Capture slings release payloads A and B at r = periap sis_capture +/- sling A/B radius.

Mars sphere of influence (km) = 577227.461555802 Capture orbit apoapsis (km) = 23821.3846522879 Capture orbit periapsis (km) = 3920.46738022426 Capture orbit periapsis altitude (km) = 524.467380224261 Capture orbit period (days) = 0.574061651272195 Phobos period (days) = 0.319026493831701 Deimos period (days) = 1.26293563279884 Maximum sling radius (km) = 124.467380224261 Capture orbit speed at periapsis (m/s) = 4331.39333469434 Required tip speed of best-case direct moon sling (m/s) = 1907.825523 32217

Consider a capture sling with its plane of rotation perpendicular to its orbital plane: Required tip speed of capture sling (m/s) = 103 6.86561791447

Consider a capture sling with its plane of rotation coplanar with its orbital plane, so there are two different tip speeds: Required tip speed of capture sling (coplanar setup- far side) = 97 3.877350324047 Required tip speed of capture sling (coplanar setup- near side) = 110 3.18116566905

5. Initialize lists and define functions. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 22/173 9/6/2018 capture-orbit-sling

In [5]: #Initialize lists that describe slings.

#cbrs were initialized above, but make a copy because negative values will be changed. orig_cbrs = copy.deepcopy(cbrs) #Use deepcopy unless you want to copy by reference so changes to the copied list also change the original list. orig_traj_string = traj_string

#These lists have a single entry per sling. 1st index is the sling (0 =M,1=A,2=B,3=C). radii = [-1.0 for i in range(num_slings)] omegas = [-1.0 for i in range(num_slings)] throw_times = [ 0.0 for i in range(num_slings)] #Time in seconds at which each sling throws its masses, relative to capture sling per iapsis. Set to 0 so trajopt < 100 don't need to explicitly zero thes e. #theta0s[j] is sling j's rotational angle "theta" at time 0 when the capture sling reaches periapsis. In general NOT equal to "theta" at throw_times[j] unless throw_times[j]==0. #If theta0s[j]==0, when the capture sling reaches periapsis, that sli ng's payload arm has a tip velocity vector parallel to the c.o.m. vel ocity. theta0s = [ 0.0 for i in range(num_slings)] #Set to 0 so tra jopt < 100 don't need to explicitly zero these. coms_empty = [-1.0 for i in range(num_slings)] coms_full = [-1.0 for i in range(num_slings)] junopanel_masses = [-1.0 for i in range(num_slings)] spinup_times = [-1.0 for i in range(num_slings)] spindown_times = [-1.0 for i in range(num_slings)] wait_times = [-1.0 for i in range(num_slings)] avg_powers = [-1.0 for i in range(num_slings)] avg_powers_alt = [-1.0 for i in range(num_slings)] avg_torques = [-1.0 for i in range(num_slings)] radiator_masses = [-1.0 for i in range(num_slings)] motor_masses = [-1.0 for i in range(num_slings)] hub_masses = [-1.0 for i in range(num_slings)]

#These lists have two entries per sling. 1st index is the sling (0=M, 1=A,2=B,3=C). 2nd index is the payload arm [0] or the counterbalance arm [1]. keplers = [[-1.0 for i in range(2)] for j in range(num_sling s)] #Keplerian elements for the payloads and balances as they're thro wn. throw_tpers = [[ 0.0 for i in range(2)] for j in range(num_sling s)] #"Time since periapsis" for the payloads and balances as they're thrown. Set to 0 so trajopt < 100 don't need to explicitly zero thes e. throw_pos = [[nullvec for i in range(2)] for j in range(num_sl ings)] #Position vectors for the payloads and balances as they're thr own. throw_vel = [[nullvec for i in range(2)] for j in range(num_sl ings)] #Velocity vectors for the payloads and balances as they're thr own. payload_masses = [[-1.0 for i in range(2)] for j in range(num_sling s)] http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 23/173 9/6/2018 capture-orbit-sling v_cs = [[-1.0 for i in range(2)] for j in range(num_sling s)] tether_masses = [[-1.0 for i in range(2)] for j in range(num_sling s)] grapple_masses = [[-1.0 for i in range(2)] for j in range(num_sling s)] ballast_masses = [[-1.0 for i in range(2)] for j in range(num_sling s)] tip_masses_empty = [[-1.0 for i in range(2)] for j in range(num_sling s)] tip_masses_full = [[-1.0 for i in range(2)] for j in range(num_sling s)] tip_diams = [[-1.0 for i in range(2)] for j in range(num_sling s)] hub_diams = [[-1.0 for i in range(2)] for j in range(num_sling s)] moments_empty = [[-1.0 for i in range(2)] for j in range(num_sling s)] moments_full = [[-1.0 for i in range(2)] for j in range(num_sling s)]

print 'Lists initialized.' Lists initialized.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 24/173 9/6/2018 capture-orbit-sling

In [6]: #Define some custom functions. print capture_frame_string

def is_numeric(expr): """ Input : An expression that's either numeric or symbolic.\n Output: A "1" if the expression is numeric (meaning ".n()" works), or a "0" if it's symbolic.\n Reference:\n https://ask.sagemath.org/question/10495/how-to-check-if-a-symbolic- expression-is-numerically-evaluable/ """ is_it = 1 if not hasattr(expr, "__n__"): is_it = 0 else: try: n = expr.n() except TypeError: is_it = 0 return is_it

def debug_sling(sind): """ Input : Sling index "sind". Prints many global variables.\n Output: Displays sling variables using sling index "sind" for debug ging. """ desc = '\n' ndigits = 3 desc += 'cbrs['+str(sind)+']= '+str(cbrs[sind].n(digits=ndigits))+ ', ' #This list was initialized separately at the beginning. desc += 'radii['+str(sind)+']= '+str(radii[sind].n(digits=ndigits)) +', ' desc += 'omegas['+str(sind)+']= '+str(omegas[sind].n(digits=ndigits ))+', ' desc += 'throw_times['+str(sind)+']= '+str(throw_times[sind].n(digi ts=ndigits))+', ' desc += 'theta0s['+str(sind)+']= '+str(theta0s[sind].n(digits=ndigi ts))+', ' desc += 'coms_empty['+str(sind)+']= '+str(coms_empty[sind].n(digits =ndigits))+', ' desc += 'coms_full['+str(sind)+']= '+str(coms_full[sind].n(digits=n digits))+', ' desc += 'junopanel_masses['+str(sind)+']= '+str(junopanel_masses[si nd].n(digits=ndigits))+', ' desc += 'spinup_times['+str(sind)+']= '+str(spinup_times[sind].n(di gits=ndigits))+', ' desc += 'spindown_times['+str(sind)+']= '+str(spindown_times[sind]. n(digits=ndigits))+', ' desc += 'wait_times['+str(sind)+']= '+str(wait_times[sind].n(digits =ndigits))+', ' desc += 'avg_powers['+str(sind)+']= '+str(avg_powers[sind].n(digits =ndigits))+', ' desc += 'avg_powers_alt['+str(sind)+']= '+str(avg_powers_alt[sind]. n(digits=ndigits))+', ' desc += 'avg_torques['+str(sind)+']= '+str(avg_torques[sind].n(digi ts=ndigits))+', ' #1st index is the sling (0=M,1=A,2=B,3=C). 2nd index is the payload http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 25/173 9/6/2018 capture-orbit-sling arm [0] or the counterbalance arm [1]. desc += 'keplers['+str(sind)+']= '+str([temp.n(digits=ndigits) for temp in keplers[sind]])+', ' desc += 'throw_tpers['+str(sind)+']= '+str([temp.n(digits=ndigits) for temp in throw_tpers[sind]])+', ' desc += 'throw_pos['+str(sind)+']= '+str([temp.n(digits=ndigits) fo r temp in throw_pos[sind]])+', ' desc += 'throw_vel['+str(sind)+']= '+str([temp.n(digits=ndigits) fo r temp in throw_vel[sind]])+', ' desc += 'payload_masses['+str(sind)+']= '+str([temp.n(digits=ndigit s) for temp in payload_masses[sind]])+', ' desc += 'v_cs['+str(sind)+']= '+str([temp.n(digits=ndigits) for tem p in v_cs[sind]])+', ' desc += 'tether_masses['+str(sind)+']= '+str([temp.n(digits=ndigits ) for temp in tether_masses[sind]])+', ' desc += 'grapple_masses['+str(sind)+']= '+str([temp.n(digits=ndigit s) for temp in grapple_masses[sind]])+', ' desc += 'ballast_masses['+str(sind)+']= '+str([temp.n(digits=ndigit s) for temp in ballast_masses[sind]])+', ' desc += 'tip_masses_empty['+str(sind)+']= '+str([temp.n(digits=ndig its) for temp in tip_masses_empty[sind]])+', ' desc += 'tip_masses_full['+str(sind)+']= '+str([temp.n(digits=ndigi ts) for temp in tip_masses_full[sind]])+', ' desc += 'tip_diams['+str(sind)+']= '+str([temp.n(digits=ndigits) fo r temp in tip_diams[sind]])+', ' desc += 'hub_diams['+str(sind)+']= '+str([temp.n(digits=ndigits) fo r temp in hub_diams[sind]])+', ' desc += 'moments_empty['+str(sind)+']= '+str([temp.n(digits=ndigits ) for temp in moments_empty[sind]])+', ' desc += 'moments_full['+str(sind)+']= '+str([temp.n(digits=ndigits) for temp in moments_full[sind]])+'.\n' print desc return #debug_sling(2)

def my_arccos(x,s): """ Input : Argument to arccos "x" and descriptive string "s".\n Output: arccos(x) if abs(x) < 1. If abs(x) is slightly >1, sets x= +1 or -1 silently. If not just "slightly" > 1, it's not silent. """ x = x.n() #Sometimes gives errors if not converted to numerical for m. if x-1 > 0: theta = arccos(1.0) if x - 1.0 > tolerance: print warnstring,'Input to',s,'arccos is > 1 by',x-1,', so',s,'arccos is being set to',theta elif -x-1 > 0: theta = arccos(-1.0) if -x - 1.0 > tolerance: print warnstring,'Input to',s,'arccos is < -1 by',-x-1,', so',s,'arccos is being set to',theta else: theta = arccos(x) return theta

################################################################ ################################################################ ################################################################ http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 26/173 9/6/2018 capture-orbit-sling def xyz2kepler(position,velocity): """ Input : Position and velocity 3D vectors.\n Output: Vector of Keplerian .\n Reference: (also see references in kepler2xyz)\n http://web.archive.org/web/20060914123625/http://ccar.colorado.edu/ asen5070/handouts/cart2kep2002.pdf """ if get_verbose() > 0: print '------' position = position.n() #Sometimes gives errors if not converted to numerical form. velocity = velocity.n() #These two numerical assignments were ad ded after many ".n()" assignment statements below failed to eliminate problems with too many terms in symbolic calculations. So those ".n ()" assignment statements below in this function (the ones commented as "sometimes gives errors if it isn't converted to numerical form", not the print statements using ".n()") might not be necessary any mo re. r = position.norm() #radial distance v = velocity.norm() #speed energy = v^2/2 - mu/r #Specific orbital energy H = position.cross_product(velocity) #specific relative orbi tal angular momentum vector h = H.norm() #magnitude of the specific relative orbi tal angular momentum vector.

if abs(energy) > tolerance: a = -mu/(2*energy) #Semi-major axis if h^2/(a*mu) > 1.0: ecc = 0.0 #Sometimes h^2/(a*mu) is larger than 1.0 by ~1e-16, w hich yields an imaginary "ecc". In that case, "ecc" should just be ze ro. if h^2/(a*mu) - 1.0 > tolerance: print '!!!WARNING!!! 1-h^2/(a* mu) =',(1.0-h^2/(a*mu)).n(),', so ecc is being set to 0.' else: ecc = sqrt(1.0 - h^2/(a*mu)) #eccentricity else: a = 0 #Semi-major axis isn't defined for parabolic radial tra jectories, so set it to zero. ecc = 1.0 #Parabolic trajectories have specific energy = 0 and ec centricity = 1.0. end ecc = ecc.n() #Comparisons involving "ecc" sometimes give errors if it isn't converted to numerical form.

p = h^2/mu #Semi-latus rectum. https://en.wikipedia.org/wiki/ Specific_relative_angular_momentum p2 = a*(1-ecc^2) #Alternate derivation of semi-latus rectum, does n't apply to parabolic trajectories.

if h > tolerance: inc = my_arccos(H[2]/h,'inclination') #[2] is z-c omponent. else: inc = 0 #For radial trajectories, inclination is undefined, so set it to zero.

if inc > tolerance and inc < pi - tolerance: LAN = atan2(H[0],-H[1]) #Longitude of the ascending node. Sagem http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 27/173 9/6/2018 capture-orbit-sling ath expects arctan2(y,x). else: LAN = 0 #Defined to be zero for non-inclined orbits. LAN = restrict_rad(LAN) #Restricts angle to lie in [0,2*pi). K = vector([0,0,1]) N = K.cross_product(H) #Node line vector. n = N.norm() #Longitude of the ascending node: https://en.wikipedia.org/wiki/Lon gitude_of_the_ascending_node if inc > tolerance and inc < pi - tolerance: LAN2 = my_arccos(N[0]/n,'LAN2') #[0] is x-component. if N[1] < 0: LAN2 = 2*pi - LAN2 #[1] is y-component. else: LAN2 = 0 #Defined to be zero for non-inclined orbits. LAN2 = restrict_rad(LAN2) #Restricts angle to lie in [0,2*pi).

if inc > tolerance and inc < pi - tolerance: arg_lat = atan2(position[2]/sin(inc),position[0]*cos(LAN)+positio n[1]*sin(LAN)) #Argument of latitude. else: arg_lat = 0 #Argument of latitude is undefined for non-inclin ed orbits, so just set it to zero. arg_lat = arg_lat.n() #arg_lat sometimes gives errors if it isn't c onverted to numerical form. arg_lat = restrict_rad(arg_lat) #Restricts angle to lie in [0,2*p i). if ecc < tolerance: nu = 0 #True anomaly is undefined for circ ular orbits, so set it to zero. elif abs(1-ecc) < tolerance: nu = my_arccos(p/r-1,'nu') #https://e n.wikipedia.org/wiki/Parabolic_trajectory#Equation_of_motion else: nu = my_arccos((a*(1-ecc^2)-r)/(ecc*r), 'nu') #True anomaly- works for elliptical or hyperbolic. vr = position.dot_product(velocity)/r #radial velocity if vr<0: nu = 2*pi - nu nu = restrict_rad(nu) #Restricts angle to lie in [0,2*pi). if ecc > tolerance: nu2 = atan2(sqrt(p/mu)*position.dot_product(vel ocity),p-r) #Alternate derivation of true anomaly. else: nu2 = 0 #True anomaly is undefined for circular orbits, so set it to zero. nu2 = restrict_rad(nu2) #Restricts angle to lie in [0,2*pi).

#Eccentricity vector: https://en.wikipedia.org/wiki/Eccentricity_ve ctor ev = velocity.cross_product(H)/mu - position/r ecc2 = norm(ev)

#Argument of periapsis: https://en.wikipedia.org/wiki/Argument_of_p eriapsis if ecc < tolerance: AP = 0 #Argument of periapsis is zero by definition for circular orbits. AP2 = 0 elif inc.n() > tolerance and inc.n() < pi - tolerance: #Inclined el liptical orbits. AP = arg_lat - nu AP2 = my_arccos(N.dot_product(ev)/(n*ecc2),'AP2') if ev[2].n() < 0: AP2 = 2*pi - AP2 #[2] is z-component. else: #Non-inclined elliptical orbits have no ascending node, so us e this: AP = arctan2(ev[1],ev[0]) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 28/173 9/6/2018 capture-orbit-sling if H[2].n() < 0: AP = 2*pi - AP #Modify if the orbit is clockwis e. AP2 = AP #In this case, AP2 isn't an 'alternate' derivation. end AP = restrict_rad(AP) #Restricts angle to lie in [0,2*pi). AP2 = restrict_rad(AP2) #Restricts angle to lie in [0,2*pi). if AP.n() == NaN and AP2.n() != NaN: if get_verbose() > 0: print 'Using AP2 because AP = NaN, but AP2 =',(AP2*180/pi).n() AP = AP2 AP2 = NaN #Swap them so it's clear on output that the alternative is NaN

#True longitude for non-inclined circular orbits: https://en.wikipe dia.org/wiki/True_anomaly#Circular_orbit_with_zero_inclination true_longitude = my_arccos(position[0]/r,'true_longitude') if velocity[0] > 0: true_longitude = 2*pi - true_longitude true_longitude = restrict_rad(true_longitude) #Restricts angle to l ie in [0,2*pi).

if h.n() > tolerance: if energy < -tolerance or ecc < 1 - tolerance: #Elliptical and ci rcular orbits definitely have energy<0, but some elliptical orbits ha ve ecc=0.99999999.. semi_minor_axis = a*sqrt(1-ecc^2) periapsis = a*(1-ecc) #http://www.bogan.ca/orbits/kepler/ orbteqtn.html http://archive.is/zQPrQ if ecc > tolerance: if get_verbose() > 0: print 'Elliptical orbit: using true ano maly.'#' (degrees) =',(nu2*180/pi).n(),', Alt. calc. =',(nu*180/pi).n () correct_arg = nu2 elif inc.n() > tolerance and inc.n() < pi - tolerance: if get_verbose() > 0: print 'Circular, inclined orbit: using argument of latitude.'#' (degrees) =',(arg_lat*180/pi).n() correct_arg = arg_lat else: if get_verbose() > 0: print 'Circular, non-inclined orbit: us ing true longitude.'#' (degrees) =',(true_longitude*180/pi).n() correct_arg = true_longitude end EA = 2*arctan2(sqrt(1-ecc)*sin(correct_arg/2),sqrt(1+ecc)*cos (correct_arg/2)) EA = EA.n() #EA sometimes gives errors if it isn't converted to numerical form. EA = restrict_rad(EA) #Restricts angle to lie in [0,2*pi). MA = EA - ecc*sin(EA) MA = restrict_rad(MA) #Restricts angle to lie in [0,2*pi). tper = sqrt(a^3/mu)*MA elif ecc > 1 + tolerance: semi_minor_axis = a*sqrt(ecc^2-1) periapsis = a*(1-ecc) #http://www.bogan.ca/orbits/kepler/ orbteqtn.html http://archive.is/zQPrQ if get_verbose() > 0: print '' EA = 2*arctanh(sqrt((ecc-1)/(ecc+1))*tan(nu2/2)) MA = ecc*sinh(EA) - EA tper = sqrt((-a)^3/mu)*MA http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 29/173 9/6/2018 capture-orbit-sling else: periapsis = p/2 #http://www.bogan.ca/orbits/kepler/orbteqtn.htm l http://archive.is/zQPrQ if get_verbose() > 0: print '' EA = tan(nu2/2) MA = EA + EA^3/3 tper = sqrt(2*(p/2)^3/mu)*MA end else: #if h < tolerance: https://en.wikipedia.org/wiki/Radial_traje ctory semi_minor_axis = 0 periapsis = 0 w = 1/r - v^2/(2*mu) if energy < -tolerance: #w is often too small, so use specific en ergy to identify the type of radial trajectory. if get_verbose() > 0: print 'Elliptical radial trajectory' tper = ((arcsin(sqrt(w*r)) - sqrt(w*r*(1-w*r)))/(sqrt(2*mu)*w^( 3/2))) elif energy > tolerance: if get_verbose() > 0: print 'Hyperbolic radial trajectory' w = abs(w) tper = (sqrt((w*r)^2+w*r)-ln(sqrt(w*r)+sqrt(1+w*r)))/(sqrt(2*mu )*w^(3/2)) else: if get_verbose() > 0: print 'Parabolic radial trajectory' tper = sqrt(2*(r)^3/(9*mu)) end end

orbital_period = (2*pi*sqrt(a^3/mu)).n() #Only applies to closed (non-radial) orbits. apoapsis = a*(1+ecc) #Only useful for elliptical orbits.

if r.n() > tolerance and v.n() > tolerance: if h.n() > tolerance: fpa = my_arccos(h/(r*v),'fpa') else: #Radial trajectories have simple flight path angles. if vr.n() > 0: fpa = pi/2 else: fpa = -pi/2 end elif r.n() > tolerance: # https://en.wikipedia.org/wiki/Equations_for_a_falling_body#Exam ples fallingtime = (arccos(sqrt(x/r)) + sqrt(x/r*(1-x/r)))*r^(3/2)/sq rt(2*mu) time2surface = fallingtime.subs(x=r_pl) #Time to fall to the plan et's (equatorial) surface. time2center = fallingtime.subs(x=0) #Time to fall to the plan et's center. if get_verbose() > 0: print 'Dropped. Time to fall to surface (ho urs)'.ljust(lj),'=',(time2surface/3600).n() if get_verbose() > 0: print 'Dropped. Time to fall to center (ho urs)'.ljust(lj),'=',(time2center/3600).n() fpa = -pi/2 #If v=0, it will quickly start falling : fpa is -90 degrees. elif v.n() > tolerance: fpa = pi/2 #If r=0, any velocity will take it right out: fpa is +90 degrees. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 30/173 9/6/2018 capture-orbit-sling else: fpa = pi/2 #If r=0 and v=0, any velocity will take it right out: fpa is +90 degrees. end

#deflection = the angle between the periapsis velocity vector and t he velocity vector at infinity. https://en.wikipedia.org/wiki/Hyperbo lic_trajectory#Eccentricity_and_angle_between_approach_and_departure if ecc > 1.0: deflection = my_arccos(-1/ecc,'deflection') - pi/2

lj = 45 #Left justify descriptions by this number of characters. if get_verbose() > 0: print 'Position vector (km)'.ljust(lj),'=',(position/1000).n() #E cho input. print 'Velocity vector (km/s)'.ljust(lj),'=',(velocity/1000).n() #Echo input. print 'Radial distance (km)'.ljust(lj),'=',(r/1000).n() print 'Speed (km/s)'.ljust(lj),'=',(v/1000).n() print 'Radial velocity (km/s)'.ljust(lj),'=',(vr/1000).n() print 'Flight path angle (degrees)'.ljust(lj),'=',(fpa*180/pi).n () if energy < -tolerance and h > tolerance: print 'Orbital period (hours)'.ljust(lj),'=',(orbital_period/36 00).n() print 'Specific relative angular momentum vector'.ljust(lj),'=',( H).n() print 'Specific relative angular momentum (m^2/s)'.ljust(lj),'=', (h).n() print 'Specific orbital energy (kJ/kg)'.ljust(lj),'=',(energy/100 0).n() if energy > tolerance: print 'Hyperbolic excess velocity at infinity (km/s)'.ljust(lj ),'=',(sqrt(2*energy)/1000).n(),', C3 (km/s)^2:',(2*energy/1e6).n() if h > tolerance: print 'Periapsis vs infinity deflection (degr ees)'.ljust(lj),'=',(deflection*180/pi).n() print 'Eccentricity vector'.ljust(lj),'=',(ev).n() print 'Eccentricity'.ljust(lj),'=',(ecc).n(),', Alt. calc. =',(ec c2).n() print 'Semi-latus rectum (km)'.ljust(lj),'=',(p/1000).n(),', Alt. calc. =',(p2/1000).n() if h > tolerance and abs(energy) > tolerance and abs(ecc-1) > tol erance: #Skip these for radial or parabolic trajectories, because in those cases they're not defined. print 'Semi-minor axis (km)'.ljust(lj),'=',(semi_minor_axis/100 0).n() print 'Semi-major axis (km)'.ljust(lj),'=',(a/1000).n() if periapsis > r_pl: print 'Periapsis altitude (km)'.ljust(lj),'=',((periapsis - r_p l)/1000).n() else: print '!!DANGER!! Periapsis is below surface by (km)'.ljust(lj ),'=',(-(periapsis - r_pl)/1000).n() print 'Periapsis (km)'.ljust(lj),'=',(periapsis/1000).n() if ecc < 1 - tolerance: #Skip apoapsis for parabolic or hyperboli c trajectories, because in those cases it's not defined. print 'Apoapsis altitude (km)'.ljust(lj),'=',((apoapsis - r_pl) /1000).n() http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 31/173 9/6/2018 capture-orbit-sling print 'Apoapsis (km)'.ljust(lj),'=',(apoapsis/1000).n() if h > tolerance: #Skip these for radial trajectories, because in those cases they're not defined. print 'Inclination (degrees)'.ljust(lj),'=',(inc*180/pi).n() print 'Longitude of the ascending node (degrees)'.ljust(lj),'=' ,(LAN2*180/pi).n(),', Alt. calc. =',(LAN*180/pi).n() print 'Argument of periapsis (degrees)'.ljust(lj),'=',(AP*180/p i).n(),', Alt. calc. =',(AP2*180/pi).n() print ' (degrees)'.ljust(lj),'=',(MA*180/pi).n() print 'Eccentric anomaly (degrees)'.ljust(lj),'=',(EA*180/pi).n () if ecc > tolerance: print 'True anomaly (degrees)'.ljust(lj),'=',(nu2*180/pi).n(), ', Alt. calc. =',(nu*180/pi).n() elif inc > tolerance and inc < pi - tolerance: print 'Argument of latitude (degrees)'.ljust(lj),'=',(arg_lat*1 80/pi).n() else: print 'True longitude (degrees)'.ljust(lj),'=',(true_longitude* 180/pi).n() end print 'Time since periapsis (hours)'.ljust(lj),'=',(tper/3600).n () print 'returns [a, ecc, inc, AP, LAN2, tper, p]' kepler = vector([a, ecc, inc, AP, LAN2, tper, p]) return kepler

################################################################ ################################################################ ################################################################ def rotateY(rad): """ Input : Angle in radians.\n Output: 3D rotation matrix about Z axis by 'rad' radians.\n Reference:\n https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations """ return matrix([[cos(rad),0,sin(rad)],[0,1,0],[-sin(rad),0,cos(rad )]])

def rotateZ(rad): """ Input : Angle in radians.\n Output: 3D rotation matrix about Z axis by 'rad' radians.\n Reference:\n https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations """ return matrix([[cos(rad),-sin(rad),0],[sin(rad),cos(rad),0],[0,0,1 ]])

def rotateV(unit,rad): """ Input : 3D unit vector, angle in radians.\n Output: 3D rotation matrix about unit vector by "rad" radians, usin g Rodrigues' rotation formula.\n References:\n https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula#Matrix_ http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 32/173 9/6/2018 capture-orbit-sling notation \n http://mathworld.wolfram.com/RodriguesRotationFormula.html \n https://math.stackexchange.com/questions/142821/matrix-for-rotation -around-a-vector """ unit /= unit.norm() #Unit vector is divided by its norm just in cas e it's not already a unit vector. #unit = vector([1,2,3]) #To check that curlish is written correctl y. Can't use the actual 0,1,2 indices because then "0" would be ambig uous. curlish = matrix([[0,-unit[2],unit[1]],[unit[2],0,-unit[0]],[-unit[ 1],unit[0],0]]) #Wolfram notes that "the entries in this matrix are d efined analogously to the differential matrix representation of the c url operator." #print 'curlish =\n',curlish return matrix.identity(3) + sin(rad)*curlish + 2*sin(rad/2)^2*curli sh^2 #This version with the "2*sin(rad/2)^2" coefficient is mathemati cally equivalent (via a half-angle trig identity) to the conventional version but as "J. M. is not a mathematician" notes at math.stackexc hange.com, this version is more numerically sound because it's less p rone to subtractive cancellations than the conventional "1-cos(rad)" coefficient when the angle "rad" is small.

################################################################ ################################################################ ################################################################ def kepler2xyz(kepler): """ Input : Keplerian orbital elements.\n Output: Position and velocity 3D vectors.\n References:\n http://web.archive.org/web/20050328065332/http://ccar.colorado.edu/ asen5070/handouts/kep2cart_2002.doc \n https://downloads.rene-schwarz.com/download/M001-Keplerian_Orbit_El ements_to_Cartesian_State_Vectors.pdf \n Generalized to hyperbolas: http://web.archive.org/web/2018061317354 2/http://astro.pas.rochester.edu/~aquillen/aqnbody/kepcart.cpp \n http://web.archive.org/web/20180613174357/https://space.stackexchan ge.com/questions/24646/finding-x-y-z-vx-vy-vz-from-hyperbolic-orbital -elements \n http://web.archive.org/web/20171209014117/http://web.mit.edu/8.01t/ www/materials/modules/chapter25.pdf \n http://web.archive.org/web/20161020194753/http://astrowww.phys.uvi c.ca/~tatum/celmechs/celm9.pdf \n http://web.archive.org/web/20050527081541/http://www.bruce-shapiro. com:80/pair/ElementConversionRecipes.pdf \n http://web.archive.org/web/20180811085042/http://shodhganga.inflibn et.ac.in/bitstream/10603/41384/10/10_chapter%202.pdf \n http://web.archive.org/web/20171215024400/http://dma.ing.uniroma1.i t/users/lss_mo/MATERIALE/AvanziniColasurdoAstrodynamics.pdf \n Sort of helpful: http://web.archive.org/web/20180613174224/https:// space.stackexchange.com/questions/23128/design-of-an-elliptical-trans fer-orbit/23130 \n http://web.archive.org/web/20180613174829/https://space.stackexchan ge.com/questions/19322/converting-orbital-elements-to-cartesian-state -vectors \n http://web.archive.org/web/20150911070910/https://physics.stackexch http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 33/173 9/6/2018 capture-orbit-sling ange.com/questions/86188/calculating-orbital-vectors-in-the-future \n Related: Tokis 2014, A Solution of Kepler’s Equation: http://web.ar chive.org/web/20170923055710/https://file.scirp.org/pdf/IJAA_20141230 13365071.pdf """ if get_verbose() > 0: print '------' kepler = kepler.n() #Sometimes gives errors if not converted to num erical form. a = kepler[0] #semi-major axis ecc = kepler[1] #eccentricity inc = kepler[2] #inclination AP = kepler[3] #argument of periapsis LAN = kepler[4] #longitude of ascending node tper = kepler[5] #time from periapsis p = kepler[6] #Adding semi-latus rectum because "a" is undefin ed for parabolic trajectories. p2 = a*(1-ecc^2) #Use the other elements to calculate the semi- latus rectum. h = sqrt(mu*p)

if ecc < 1 - tolerance: #Circular or elliptical orbit. if get_verbose() > 0: if ecc < tolerance: print 'Circular orbit' else: print 'Elliptical orbit' MA = sqrt(mu/a^3)*tper MA = restrict_rad(MA) #Restricts angle to lie in [0,2*pi). var('EAvar') anomalies = EAvar - ecc*sin(EAvar) - MA #http://www.bogan.ca/orbi ts/kepler/orbteqtn.html http://archive.is/zQPrQ EA = find_root(anomalies,-tolerance,2*pi) EA = restrict_rad(EA) #Restricts angle to lie in [0,2*pi). cosEA = cos(EA) ; sinEA = sin(EA) nu = 2*arctan2(sqrt(1+ecc)*sin(EA/2),sqrt(1-ecc)*cos(EA/2)) elif ecc > 1 + tolerance: if get_verbose() > 0: print 'Hyperbolic trajectory' MA = sqrt(mu/(-a)^3)*tper var('EAvar') anomalies = ecc*sinh(EAvar) - EAvar - MA #http://www.bogan.ca/orb its/kepler/orbteqtn.html http://archive.is/zQPrQ elimit = max([1000.0,1000.0*MA]) #This control the limits for fin d_root. Those limits weren't rigorously derived, of course, but perha ps they'll work for most cases? #display(plot(anomalies,(EAvar,-elimit,elimit),axes_labels=['EAva r','anomalies'])) EA = find_root(anomalies,-elimit,elimit) cosEA = cosh(EA) ; sinEA = sinh(EA) #Use hyperbolic trig for hype rbolas. nu = 2*arctan2(sqrt(ecc+1)*sinh(EA/2),sqrt(ecc-1)*cosh(EA/2)) #ht tps://en.wikipedia.org/wiki/Hyperbolic_trajectory else: if get_verbose() > 0: print 'Parabolic trajectory' MA = sqrt(mu/(2*(p/2)^3))*tper var('EAvar') eqp = MA == EAvar + EAvar^3/3 solnp = solve(eqp,EAvar) EA = solnp[2].rhs().n() #The first two solutions are imaginary, s http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 34/173 9/6/2018 capture-orbit-sling o use the third solution. cosEA = cos(EA) ; sinEA = sin(EA) nu = 2*arctan(EA) #http://www.bogan.ca/orbits/kepler/orbteqtn.htm l http://archive.is/zQPrQ end

r1 = a*(1-ecc*cosEA) r2 = p/(1+ecc*cos(nu)) #compare various ways of calculating radius. r3 = a*(1-ecc^2)/(1+ecc*cos(nu)) r = r2 #r2 should be more general than the other equations.

#These equations for cartesian vectors were copied from http://web. archive.org/web/20050328065332/http://ccar.colorado.edu/asen5070/hand outs/kep2cart_2002.doc #They seem to work for elliptical and hyperbolic cases. Even parabo lic trajectories seem to give vectors that are very similar to those returned by a "just barely hyperbolic" trajectory. x = r*(cos(LAN)*cos(AP+nu) - sin(LAN)*sin(AP+nu)*cos(inc)) y = r*(sin(LAN)*cos(AP+nu) + cos(LAN)*sin(AP+nu)*cos(inc)) z = r*(sin(inc)*sin(AP+nu))

vx = (x*h*ecc/(r*p))*sin(nu) - (h/r)*(cos(LAN)*sin(AP+nu) + sin(LAN )*cos(AP+nu)*cos(inc)) vy = (y*h*ecc/(r*p))*sin(nu) - (h/r)*(sin(LAN)*sin(AP+nu) - cos(LAN )*cos(AP+nu)*cos(inc)) vz = (z*h*ecc/(r*p))*sin(nu) + (h/r)*(cos(AP+nu)*sin(inc))

position = vector([x,y,z]) velocity = vector([vx,vy,vz])

#Alternate rotation matrix approach from https://downloads.rene-sch warz.com/download/M001-Keplerian_Orbit_Elements_to_Cartesian_State_Ve ctors.pdf #position2 = vector([r*cos(nu),r*sin(nu),0]) #This doesn't apply to parabolic trajectories because it uses "a": #velocity2 = vector([sqrt(mu*abs(a))/r*(-sinEA),sqrt(mu*abs(a))/r* (cosEA*sqrt(abs(1-ecc^2))),0]) #Use abs() because of hyperbolas. #rotation_matrix = rotateZ(LAN)*rotateX(inc)*rotateZ(AP) #The negat ive signs in Rene's formalism give incorrect positions here. #position2 = rotation_matrix*position2 #velocity2 = rotation_matrix*velocity2 #position = position2 #velocity = velocity2

if get_verbose() > 0: r4 = position.norm() #radial distance - check that it's identical to the above calculations. v = velocity.norm() #speed vr = position.dot_product(velocity)/r #radial velocity print 'Position vector (km) =',(position/1000).n() print 'Velocity vector (km/s) =',(velocity/1000).n() #print 'Position2vector (km) =',(position2/1000).n() #print 'Velocity2vector (km/s) =',(velocity2/1000).n() print 'Radial distance (km) =',(r2/1000).n(),', Alt. calcs. =', (r1/1000).n(),', ',(r3/1000).n(),', ',(r4/1000).n() print 'Speed (km/s) =',(v/1000).n() print 'Radial velocity (km/s) =',(vr/1000).n() http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 35/173 9/6/2018 capture-orbit-sling #print 'returns position, velocity' return position, velocity

################################################################ ################################################################ ################################################################ def xyz2spherical(v): """ Input : A 3D vector: x, y, z.\n Output: A 3D vector: "r" magnitude, "theta" inclination (rad), "ph i" (rad).\n Reference:\n https://en.wikipedia.org/wiki/Spherical_coordinate_system """ r = v.norm() if r > tolerance: theta = my_arccos(v[2]/r,'xyz2spherical') phi = arctan2(v[1],v[0]) else: theta = 0 #Arbitrarily set inclination and azimuth to 0 if r < to lerance. phi = 0 end return vector([r,theta,phi])

def spherical2xyz(v): """ Input : A 3D vector: magnitude, inclination (rad), azimuth (rad).\n Output: A 3D vector: x, y, z.\n Reference:\n https://en.wikipedia.org/wiki/Spherical_coordinate_system """ x = v[0]*sin(v[1])*cos(v[2]) y = v[0]*sin(v[1])*sin(v[2]) z = v[0]*cos(v[1]) return vector([x,y,z])

def spherical2deg(v): """ Input : A 3D vector: magnitude, inclination (rad), azimuth (rad).\n Output: A 3D vector: magnitude, inclination (deg), azimuth (deg).\n """ v[1] = v[1]*180/pi ; v[2] = v[2]*180/pi return v

def compare_vectors(v1,v2,*args): """ Input : Two 3D vectors: x, y, z. Then an optional numeric flag.\n Output: Difference of those vectors, and print statements.\n If numeric flag is either 1 or absent, numeric values are p rinted.\n If numeric flag is 0, symbolic values are printed. """ print '------' if args: numeric_flag = args[0] else: numeric_flag = 1 if numeric_flag != 0: http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 36/173 9/6/2018 capture-orbit-sling v1 = v1.n() #Sometimes gives errors if not converted to numer ical form. v2 = v2.n() n1 = v1.norm() n2 = v2.norm() dv = v2 - v1 dvnorm = dv.norm() s1 = xyz2spherical(v1) s2 = xyz2spherical(v2) sdv = xyz2spherical(dv) if dvnorm > n1*tolerance or abs(s1[1] - s2[1]) > tolerance or abs(s 1[2] - s2[2]) > tolerance or numeric_flag == 0: if numeric_flag != 0: print '#1 =',v1.n() print '#2 =',v2.n() print 'delta =',dv.n() print '#1 magnitude, inclination, azimuth (deg) =',spherical 2deg(s1).n() print '#2 magnitude, inclination, azimuth (deg) =',spherical 2deg(s2).n() print 'delta magnitude, inclination, azimuth (deg) =',spherical 2deg(sdv).n() else: print '#1 =',v1 print '#2 =',v2 print 'delta =',dv print '#1 magnitude, inclination, azimuth (deg) =',spherical 2deg(s1) print '#2 magnitude, inclination, azimuth (deg) =',spherical 2deg(s2) print 'delta magnitude, inclination, azimuth (deg) =',spherical 2deg(sdv) if n1 > tolerance and n2 > tolerance: angle = my_arccos((v1.dot_product(v2))/(n1*n2),'compare_vectors angle') #From the definition of the dot product. if numeric_flag != 0: print 'angle between vectors #1 and #2 (d egrees) =',(angle*180/pi).n() else: print 'angle between vectors #1 and #2 (d egrees) =',(angle*180/pi) cross_prod = v1.cross_product(v2) cross_prod /= cross_prod.norm() if numeric_flag != 0: print 'Normalized cross product =',(cross _prod).n() else: print 'Normalized cross product =',(cross _prod).n() else: print 'Vectors are the same, within tolerance.' return dv

print 'Capture sling orbit:' set_verbose(2) kepler_com_cs = xyz2kepler(com_cs_pos0,com_cs_vel0)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 37/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

Capture sling orbit: ------Elliptical orbit: using true anomaly. Position vector (km) = (3920.46738022426, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.000000000000000, 4.33139333469434, 0.000000000000000) Radial distance (km) = 3920.46738022426 Speed (km/s) = 4.33139333469434 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 13.7774796305327 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.69810862795899e10) Specific relative angular momentum (m^2/s) = 1.69810862795899e10 Specific orbital energy (kJ/kg) = -1543.81798121506 Eccentricity vector = (0.717360803768281, 0.000000000000000, 0.000000000000000) Eccentricity = 0.717360803768281 , A lt. calc. = 0.717360803768281 Semi-latus rectum (km) = 6732.85701124926 , Al t. calc. = 6732.85701124927 Semi-minor axis (km) = 9663.89991054697 Semi-major axis (km) = 13870.9260162561 Periapsis altitude (km) = 524.467380224261 Periapsis (km) = 3920.46738022426 Apoapsis altitude (km) = 20425.3846522879 Apoapsis (km) = 23821.3846522879 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Mean anomaly (degrees) = 0.000000000000000 Eccentric anomaly (degrees) = 0.000000000000000 True anomaly (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Time since periapsis (hours) = 0.000000000000000 returns [a, ecc, inc, AP, LAN2, tper, p]

6. Solve for each capture sling's radius, rotation rate, counterbalance ratio, etc. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 38/173 9/6/2018 capture-orbit-sling

In [7]: if abs(cbrs[1]) < tolerance: print 'Skipping this section because the capture sling has been disabled.' elif trajopt < 100: print 'Skipping this section because it only applies to coplanar trajectory options.' else: lj = 50 ; summary_digits = 5 #Many print statements in this noteboo k will print "summary_digits" significant figures. traj_string = orig_traj_string #Otherwise running this cell repeate dly keeps adding the same counterbalance notes to traj_string. for j in range(1,3): #Loop through capture slings A and B (j = 1 an d 2). print '\n------' print 'Solving for '+descs[j]+'\'s radius, rotation rate, counter balance ratio, etc.' #Capture sling A/B releases the payload at the farthest/nearest p oint to Mars, so r = periapsis_capture +/- capture sling radius "c_ra d". someneg = (-1)^(j-1) #This term is sometimes negative (when j=2), which allows the same equation to be used for slings A and B. throw_times[j] = 0.0 #Sling "j" throws its masses exactly when t he capture sling center of mass reaches periapsis. theta0s [j] = 0.0 #Sling "j" throws its masses exactly along t he capture sling center of mass velocity vector. throw_tpers[j][0] = 0 #"Time since periapsis" is currently always zero for both payloads and both counterbalances. throw_tpers[j][1] = 0

var('c_rad w cbr r_p') assume(c_rad>0,w>0,cbr>0,r_p>0)

#energy = v^2/2 - mu/r #Specific orbital energy #energy = (capture_speed_periapsis+c_rad*w)^2/2 - mu/(periapsis_c apture+someneg*c_rad) #Specific orbital energy #tip_accel_max = c_rad*w^2 #max accel >= v^2/r = r*w^2 #w = sqrt(tip_accel_max/c_rad) energy_max_accel = (capture_speed_periapsis+c_rad*sqrt(tip_accel_ max/c_rad))^2/2 - mu/(periapsis_capture+someneg*c_rad) #Specific orbi tal energy

pt1 = plot( (energy_max_accel)/1000.0, c_rad, 0, r_max, rgbcolor ='red', linestyle = '-', fill=False, thickness=1, legend_label = 'specific orbital energy (kJ/kg)' ) pt2 = plot( 0.5*v_infs[mis[j]]^2/1000.0, c_rad, 0, r_max, rgb color='green', linestyle = '-.', fill=False, thickness=1, legend_labe l = 'target specific energy (kJ/kg)' ) #display(plot(0.5*v_infs[mis[j]]^2 - energy_max_accel,(c_rad,0,r_ max),axes_labels=['c_rad','energy differences'])) target_c_rad = find_root(0.5*v_infs[mis[j]]^2 - energy_max_accel, 0,r_max) #desc = descs[j]+' radius1 target_c_rad (km)' ; print desc.ljust (lj),':',target_c_rad/1000.0 pt3 = line([(target_c_rad,0),(target_c_rad,0.5*v_infs[mis[j]]^2/1 000.0)]) if textonly == 0: show( pt1 + pt2 + pt3, axes=true, frame=True, gridlines=false, figsize=6, xmin=0, xmax=r_max, ymin=0, ymax=0.52*v http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 39/173 9/6/2018 capture-orbit-sling _infs[mis[j]]^2/1000.0 ,axes_labels=[descs[j]+' sling radius at max a ccel','specific orbital energy (kJ/kg)']) else: print 'Textonly =',textonly,'so this image wasn\'t displaye d.' c_rad = target_c_rad w = sqrt(tip_accel_max/c_rad)

if c_rad > r_max: print warnstring,descs[j],'radius1',c_rad/1000. 0,'km is larger than maximum sling radius',r_max/1000.0,'km.' else: desc = descs[j]+' radius1 (km)' ; print desc.ljust(lj),':', (c_rad/1000.0).n() desc = descs[j]+' omega (rad/s)' ; print desc.ljust(lj),':',w.n()

#counterbalance orbit has apoapsis at r-someneg*c_rad/cbr, periap sis needs to be above r_pl + atmo_alt OR it needs to aerobrake. #v^2 = mu*(2/r-1/a) #v at apoapsis #v^2 = mu*(2/(a*(1+ecc))-1/a) = mu/a*(2/(1+ecc) - 1) = mu/a*((1-e cc)/(1+ecc)) = mu/a*(r_p/r_a) = 2*mu*(r_p/r_a)/(r_p+r_a) #v at apoaps is eq_cbr3 = (capture_speed_periapsis-c_rad/cbr*w)^2 == 2*mu*(r_p/(p eriapsis_capture-someneg*c_rad/cbr))/(r_p+(periapsis_capture-someneg* c_rad/cbr)) #v at apoapsis soln_cbr3 = solve(eq_cbr3,r_p) perCB1(cbr) = soln_cbr3[0].rhs() #print 'perCB1 =',perCB1 apoCB1(cbr) = periapsis_capture-someneg*c_rad/cbr if textonly == 0: plot((perCB1-r_pl)/1000.0,(cbr,1/dmr,dmr),axes_ labels=[descs[j]+' counterbalance ratio','balance periapsis altitude (km)']) else: print 'Textonly =',textonly,'so this image wasn\'t displaye d.'

pt1 = plot( (perCB1-r_pl)/1000.0, cbr, 1/dmr, dmr, rgbcolor='re d', linestyle = '-', fill=False, thickness=1, legend_label = 'peri apsis altitude' ) pt2 = plot( (apoCB1-r_pl)/1000.0, cbr, 1/dmr, dmr, rgbcolor='blu e', linestyle = '--', fill=False, thickness=1, legend_label = 'apoap sis altitude' ) pt3 = plot( (target_alt)/1000.0, cbr, 1/dmr, dmr, rgbcolo r='green', linestyle = '-.', fill=False, thickness=1, legend_label = 'target altitude' ) if textonly == 0: show( pt1 + pt2 + pt3, axes=true, frame=True, gridlines=false, figsize=6, xmin=1/dmr, xmax=dmr, ymin=0, ymax=(per iapsis_capture - r_pl)/1000.0,axes_labels=[descs[j]+' cbr','balance a psis altitudes (km)'] ) else: print 'Textonly =',textonly,'so this image wasn\'t displaye d.' slr = (0.5*(apoCB1+perCB1)*(1-((apoCB1-perCB1)/(apoCB1+perCB1))^2 )) pt1 = plot( slr/1000.0, cbr, 1/dmr, dmr, rgbcolor='red', lines tyle = '-', fill=False, thickness=1, legend_label ='semi-latus rect um' ) pt2 = plot( (target_alt+r_pl)/1000.0, cbr, 1/dmr, dmr, rgbcolor= 'green', linestyle = '-.', fill=False, thickness=1, legend_label = 'target semi-latus rectum' ) #display(plot(slr - (target_alt+r_pl),(cbr,1/dmr,dmr),axes_labels =[descs[j]+' cbr','semi-latus rectum differences'])) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 40/173 9/6/2018 capture-orbit-sling target_cbr_cs = RDF(find_root(slr - (target_alt+r_pl),1/dmr,dmr)) if orig_cbrs[j] > tolerance: target_cbr_cs = orig_cbrs[j] desc = descs[j]+' counterbalance ratio was specified' ; print d esc.ljust(lj),':',target_cbr_cs traj_string += ' Counterbalance '+sdescs[j]+' was randomly sent to a periapsis altitude of '+str(((perCB1(target_cbr_cs) - r_pl)/100 0.0).n(digits=summary_digits))+' km.' elif (perCB1(target_cbr_cs) - r_pl) < atmo_alt: print 'Can\'t send counterbalance',sdescs[j],'directly into an orbit with the same semi-latus rectum as the target orbit because th en its periapsis altitude would be',(perCB1(target_cbr_cs) - r_pl)/10 00.0,'km.\n' target_cbr_cs = RDF(find_root(perCB1(cbr) - (r_pl+aerobrake_alt ),1/dmr,dmr)) traj_string += ' Counterbalance '+sdescs[j]+' aerobrakes at a p eriapsis altitude of '+str(((perCB1(target_cbr_cs) - r_pl)/1000.0).n( digits=summary_digits))+' km.' #'Counterbalance A either uses aerobra king or goes directly into an orbit with a periapsis above atmosphere and the same semi-latus rectum as the specified counterbalance orbi t. Counterbalance B either uses a tether to help lift a payload from a suborbital trajectory, aerobrakes until it can enter the specified counterbalance orbit, or aerobrakes until it hits Mars.' desc = descs[j]+' counterbalance ratio was derived' ; print des c.ljust(lj),':',target_cbr_cs else: traj_string += ' Counterbalance '+sdescs[j]+' has a periapsis a ltitude of '+str(((perCB1(target_cbr_cs) - r_pl)/1000.0).n(digits=sum mary_digits))+' km and the same semi-latus rectum as the specified co unterbalance orbit.' pt3 = line([(target_cbr_cs,0),(target_cbr_cs,(target_alt+r_pl)*1. 1/1000.0)]) print 'periapsis altitude at target cbr (km)'.ljust(lj),':',(perC B1(target_cbr_cs) - r_pl)/1000.0 print 'apoapsis altitude at target cbr (km)'.ljust(lj),':',(apoC B1(target_cbr_cs) - r_pl)/1000.0 if apoCB1(target_cbr_cs) < perCB1(target_cbr_cs): print warnstrin g+'Periapsis is above apoapsis, so counterbalance',sdescs[j],'goes U P!' print 'orbital period of counterbalance (hours)'.ljust(lj),':',(2 *pi*sqrt((target_alt+r_pl)^3/mu)/3600).n() print 'payload arm length (km)'.ljust(lj),':',c_rad/1000.0 print 'balance arm length (km)'.ljust(lj),':',c_rad/target_cbr_cs /1000.0 print 'velocity at payload tip (m/s)'.ljust(lj),':',c_rad*w print 'velocity at balance tip (m/s)'.ljust(lj),':',c_rad/target_ cbr_cs*w print 'acceleration at payload tip (g\'s)'.ljust(lj),':',c_rad*w^ 2/g print 'acceleration at balance tip (g\'s)'.ljust(lj),':',c_rad/ta rget_cbr_cs*w^2/g print 'payload arm could reach down to',(periapsis_capture-c_rad- r_pl)/1000.0,'km above the surface of',pname if textonly == 0: show( pt1 + pt2 + pt3, axes=true, frame=True, gridlines=false, figsize=6, xmin=1/dmr, xmax=dmr, ymin=0, ymax=(tar get_alt+r_pl)*1.1/1000.0 ,axes_labels=[descs[j]+' cbr','semi-latus re ctum']) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 41/173 9/6/2018 capture-orbit-sling else: print 'Textonly =',textonly,'so this image wasn\'t displaye d.'

cbrs [j] = RDF(target_cbr_cs) radii [j] = RDF(target_c_rad) omegas[j] = RDF(sqrt(tip_accel_max/radii[j])) #Angular rotation r ate of the capture orbit sling with index j. debug_sling(j) print traj_string

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 42/173 9/6/2018 capture-orbit-sling

------Solving for capture sling A's radius, rotation rate, counterbalance r atio, etc.

capture sling A radius1 (km) : 99.1859671034564 capture sling A omega (rad/s) : 0.00994510533955 852

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 43/173 9/6/2018 capture-orbit-sling

periapsis altitude at target cbr (km) : 375.794571474538 2 apoapsis altitude at target cbr (km) : 424.518111025446 orbital period of counterbalance (hours) : 1.97242682475854 payload arm length (km) : 99.1859671034564 balance arm length (km) : 99.9492691988 velocity at payload tip (m/s) : 986.414891049860 velocity at balance tip (m/s) : 994.006010794 acceleration at payload tip (g's) : 1.00000000000000 acceleration at balance tip (g's) : 1.00769566621 payload arm could reach down to 425.281413120804 km above the surface of Mars

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 44/173 9/6/2018 capture-orbit-sling

cbrs[1]= 0.992, radii[1]= 99200., omegas[1]= 0.00994, throw_times[1]= 0.000, theta0s[1]= 0.000, coms_empty[1]= -1.00, coms_full[1]= -1.00, junopanel_masses[1]= -1.00, spinup_times[1]= -1.00, spindown_times[1] = -1.00, wait_times[1]= -1.00, avg_powers[1]= -1.00, avg_powers_alt [1]= -1.00, avg_torques[1]= -1.00, keplers[1]= [-1.00, -1.00], throw_ tpers[1]= [0.000, 0.000], throw_pos[1]= [(0.000, 0.000, 0.000), (0.00 0, 0.000, 0.000)], throw_vel[1]= [(0.000, 0.000, 0.000), (0.000, 0.00 0, 0.000)], payload_masses[1]= [-1.00, -1.00], v_cs[1]= [-1.00, -1.0 0], tether_masses[1]= [-1.00, -1.00], grapple_masses[1]= [-1.00, -1.0 0], ballast_masses[1]= [-1.00, -1.00], tip_masses_empty[1]= [-1.00, - 1.00], tip_masses_full[1]= [-1.00, -1.00], tip_diams[1]= [-1.00, -1.0 0], hub_diams[1]= [-1.00, -1.00], moments_empty[1]= [-1.00, -1.00], m oments_full[1]= [-1.00, -1.00].

------Solving for capture sling B's radius, rotation rate, counterbalance r atio, etc.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 45/173 9/6/2018 capture-orbit-sling

capture sling B radius1 (km) : 124.000561331188 capture sling B omega (rad/s) : 0.00889452332022 582

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 46/173 9/6/2018 capture-orbit-sling

Can't send counterbalance B directly into an orbit with the same semi -latus rectum as the target orbit because then its periapsis altitude would be 173.01636602082382 km.

capture sling B counterbalance ratio was derived : 0.917526189989 periapsis altitude at target cbr (km) : 109.999999999999 53 apoapsis altitude at target cbr (km) : 659.613998036490 orbital period of counterbalance (hours) : 1.97242682475854 payload arm length (km) : 124.000561331188 balance arm length (km) : 135.146617812 velocity at payload tip (m/s) : 1102.92588448134 velocity at balance tip (m/s) : 1202.06474378 acceleration at payload tip (g's) : 1.00000000000000 acceleration at balance tip (g's) : 1.08988714536 payload arm could reach down to 400.466818893073 km above the surface of Mars

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 47/173 9/6/2018 capture-orbit-sling

cbrs[2]= 0.918, radii[2]= 124000., omegas[2]= 0.00889, throw_times[2] = 0.000, theta0s[2]= 0.000, coms_empty[2]= -1.00, coms_full[2]= -1.0 0, junopanel_masses[2]= -1.00, spinup_times[2]= -1.00, spindown_times [2]= -1.00, wait_times[2]= -1.00, avg_powers[2]= -1.00, avg_powers_al t[2]= -1.00, avg_torques[2]= -1.00, keplers[2]= [-1.00, -1.00], throw _tpers[2]= [0.000, 0.000], throw_pos[2]= [(0.000, 0.000, 0.000), (0.0 00, 0.000, 0.000)], throw_vel[2]= [(0.000, 0.000, 0.000), (0.000, 0.0 00, 0.000)], payload_masses[2]= [-1.00, -1.00], v_cs[2]= [-1.00, -1.0 0], tether_masses[2]= [-1.00, -1.00], grapple_masses[2]= [-1.00, -1.0 0], ballast_masses[2]= [-1.00, -1.00], tip_masses_empty[2]= [-1.00, - 1.00], tip_masses_full[2]= [-1.00, -1.00], tip_diams[2]= [-1.00, -1.0 0], hub_diams[2]= [-1.00, -1.00], moments_empty[2]= [-1.00, -1.00], m oments_full[2]= [-1.00, -1.00].

COPLANAR SETUP. Capture slings release payloads A and B at r = periap sis_capture +/- sling A/B radius. Counterbalance A has a periapsis al titude of 375.79 km and the same semi-latus rectum as the specified c ounterbalance orbit. Counterbalance B aerobrakes at a periapsis altit ude of 110.00 km.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 48/173 9/6/2018 capture-orbit-sling

In [8]: #If cbrs[1] was initially negative, it's not any more. So there's no need to write abs(cbrs[1]) from now on: if cbrs[1] < tolerance: print 'Skipping this section because the capt ure sling has been disabled.' else:

print capture_frame_string

#debug_sling(1) #debug_sling(2) print 'Orbit sign =',orbit_sign,'(+1 for CCW, -1 for CW)' #print 'com_cs_pos0 =',com_cs_pos0 #print 'com_cs_vel0 =',com_cs_vel0

def sling_tips(t): """ Input : Time in seconds since capture sling reaches periapsis.\n Output: Lists of lists of position and velocity vectors of the ca pture sling tips, relative to its center of mass. Dummy entries for m oon sling tips included. """ tips_pos = [[nullvec for i in range(2)] for j in range(num_slings )] tips_vel = [[nullvec for i in range(2)] for j in range(num_slings )] if trajopt < 100: #Perpendicular setup - capture sling axis point s radially away from the planet at periapsis. somerotate = rotateY(pi/2) #This only works when com_cs_pos0 po ints along the x-axis. A more general solution would rotate the sling s' axis of rotation around a vector that's perpendicular to both the original axis of rotation and com_cs_pos0. else: #Coplanar setup - capture sling axis points alo ng specific orbital angular momentum vector. somerotate = 1 for j in range(1,3): #Loop through capture slings A and B. if j == 1: rotation_sign = orbit_sign #Capture sling A rotates in the same way as the orbit. else: rotation_sign = -orbit_sign #Capture sling B rotates in the opposite way as the orbit. theta_t = rotation_sign*omegas[j]*t + theta0s[j] if j == 2: theta_t += pi.n() #Capture sling B is rotated by "p i" so if its theta0s == 0, its payload is closest to the planet. tips_pos[j][0] = somerotate*rotateZ(theta_t)*radii[j]*com_cs_po s0/com_cs_pos0.norm() #Orient using unit vector in the direction of t he capture sling center of mass when it reaches periapsis. tips_pos[j][1] = -tips_pos[j][0]/cbrs[j] someneg = (-1)^(j-1) #This term is sometimes negative (when j= 2), which allows the same equation to be used for slings A and B. tips_vel[j][0] = somerotate*rotateZ(theta_t)*radii[j]*omegas[j] *someneg*com_cs_vel0/com_cs_vel0.norm() #Velocity vector needs to be reversed for capture sling B. tips_vel[j][1] = -tips_vel[j][0]/cbrs[j] return tips_pos, tips_vel

tips_pos, tips_vel = sling_tips(0) if 2 == 1: http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 49/173 9/6/2018 capture-orbit-sling print 'A tips0 pos:',tips_pos[1] print 'A tips0 vel:',tips_vel[1] print 'B tips0 pos:',tips_pos[2] print 'B tips0 vel:',tips_vel[2]

cs_axis = tips_pos[1][0].cross_product(tips_vel[1][0]) #This vector points from the capture sling system's total center of mass to the c enter of mass of sling A. cs_axis /= cs_axis.norm() #Unit vector along capture sling axis. print 'capture sling rotation axis =',cs_axis

#The capture sling also throws counterbalance mass backwards to kee p the sling's center of mass stationary and to prevent the capture #sling's orbit from lowering due to throwing the payload forward.

#First, consider throwing the counterbalance mass into a circular o rbit. v_circ_at_periapsis = sqrt(mu/periapsis_capture) delta_v_capture_to_circ = capture_speed_periapsis - v_circ_at_peria psis

if trajopt == 1: #Obtain the radius, cbr and omega from the tip velocities calcula ted above. cbrs [1] = v_tip_capture_payload1/delta_v_capture_to_circ radii [1] = v_tip_capture_payload1^2/tip_accel_max #if cbrs[1] < 1: radii[1] /= cbrs[1] #If payload moves slower, ac cel limit applies to the counterbalance arm, not the payload arm. Dis abled because this is harder to fix for the coplanar configuration, s o the accel limit now applies ONLY to the payload arm. omegas[1] = v_tip_capture_payload1/radii[1] #Angular rotation rat e of the capture orbit sling A. cbrs [2] = cbrs [1] #Here, slings A and B are identical. radii [2] = radii [1] omegas[2] = omegas[1]

for j in range(1,3): #Loop through capture orbit slings. kepler_com_cs[5] = throw_times[j] com_cs_pos,com_cs_vel = kepler2xyz(kepler_com_cs) tips_pos, tips_vel = sling_tips(throw_times[j]) for k in range(2): #Loop through capture orbit sling arms. throw_pos[j][k] = com_cs_pos + tips_pos[j][k] throw_vel[j][k] = com_cs_vel + tips_vel[j][k]

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 50/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

Orbit sign = 1 (+1 for CCW, -1 for CW) capture sling rotation axis = (0.0, 0.0, 1.0) ------Elliptical orbit Position vector (km) = (3920.46738022426, -2.23712340321356e-10, - 0.000000000000000) Velocity vector (km/s) = (1.43919013598211e-13, 4.33139333469434, 0.0 00000000000000) Radial distance (km) = 3920.46738022426 , Alt. calcs. = 3920.467380 22426 , 3920.46738022426 , 3920.46738022426 Speed (km/s) = 4.33139333469434 Radial velocity (km/s) = -1.03241859272351e-13 ------Elliptical orbit Position vector (km) = (3920.46738022426, -2.23712340321356e-10, - 0.000000000000000) Velocity vector (km/s) = (1.43919013598211e-13, 4.33139333469434, 0.0 00000000000000) Radial distance (km) = 3920.46738022426 , Alt. calcs. = 3920.467380 22426 , 3920.46738022426 , 3920.46738022426 Speed (km/s) = 4.33139333469434 Radial velocity (km/s) = -1.03241859272351e-13

7. Print the capture slings' payload trajectories and counterbalance orbits. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 51/173 9/6/2018 capture-orbit-sling

In [9]: if cbrs[1] < tolerance: print '!!!!!Need to display counterbalance tr ajectory for cbrs[1]=0!!!' else: print capture_frame_string print 'capture orbit speed at periapsis (m/s) =', capture_speed_periapsis print 'circular orbit speed at periapsis (m/s) =', v_circ_at_periapsis print 'delta-v from capture orbit to circular orbit (m/s) =', delta_v_capture_to_circ print 'required tip speed of capture payload sling A (m/s) =', radii[1]*omegas[1] print 'mass ratio (counterbalance mass divided by payload mass) =', cbrs[1]

for j in range(1,num_slings-1): #Don't loop over capture sling C be cause its counterbalance ratio hasn't yet been derived. if cbrs[j] > dmr: print '\n!!! DANGER !!!',cap1st(descs[j]),'ma ss ratio',cbrs[j],'is higher than the safe limit of',dmr,'!!!' if cbrs[j] < 1/dmr: print '\n!!! DANGER !!!',cap1st(descs[j]),'ma ss ratio',cbrs[j], 'is lower than the safe limit of',1/dmr,'!!!'

print '\nPayload A trajectory:' j = 1 ; k = 0 keplers[j][k] = xyz2kepler(throw_pos[j][k],throw_vel[j][k]) throw_tpers[j][k] = keplers[j][k][5]

print '\nCounterbalance A orbit:' j = 1 ; k = 1 keplers[j][k] = xyz2kepler(throw_pos[j][k],throw_vel[j][k]) throw_tpers[j][k] = keplers[j][k][5]

print '\nPayload B trajectory:' j = 2 ; k = 0 keplers[j][k] = xyz2kepler(throw_pos[j][k],throw_vel[j][k]) throw_tpers[j][k] = keplers[j][k][5]

print '\nCounterbalance B orbit:' j = 2 ; k = 1 keplers[j][k] = xyz2kepler(throw_pos[j][k],throw_vel[j][k]) throw_tpers[j][k] = keplers[j][k][5]

#Describe the payloads' outgoing hyperbolic asymptotes. keplers_copy = copy.deepcopy(keplers) #Use deepcopy unless you want to copy by reference so changes to the copied list also change the o riginal list. payload_lon_string = '' for j in range(1,num_slings-1): keplers_copy[j][0][5] = long_time pD_far,vD_far = kepler2xyz(keplers_copy[j][0]) payload_lon_direct = restrict_rad(atan2(vD_far[1],vD_far[0])) #Lo ngitude of the payload's outgoing asymptote, relative to the moon's a scending node. Sagemath expects arctan2(y,x). print 'Payload '+sdescs[j]+'\'s velocity vector after',(long_time /year).n(digits=summary_digits),'years:',vD_far print 'Payload '+sdescs[j]+'\'s velocity vector is aimed at',(pay http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 52/173 9/6/2018 capture-orbit-sling load_lon_direct*180/pi).n(),'degrees longitude, relative to',moon_nam e+'\'s ascending node.' payload_lon_string += 'Payload '+sdescs[j]+'\'s longitude after ' +str((long_time/year).n(digits=summary_digits))+' years is '+str((pay load_lon_direct*180/pi).n(digits=summary_digits))+' degrees, ' payload_lon_string += 'and its residual velocity out of the eclip tic plane is '+str((vD_far[2]).n(digits=summary_digits))+' m/s. ' payload_lon_string += 'No periapsis rotation is required if paylo ad '+sdescs[j]+'\'s longitude =FINISH THIS BY LOOPING. ALSO, CHECK PR EVIOUS LINES CAUSE THEY WERE HASTILY COPIED!' print '\n'+payload_lon_string+'\n'

if trajopt < 100: #This simple calculation doesn't apply to coplana r capture orbit slings. print '\nUsing these orbits and the highest safe mass ratio, the highest safe payload tip speed is (m/s):',dmr*delta_v_capture_to_cir c print 'Using that tip speed, this is the fastest possible payload trajectory:' p_i = com_cs_pos0 v_i = (capture_speed_periapsis + delta_v_capture_to_circ*dmr)*com _cs_vel0/com_cs_vel0.norm() kepler = xyz2kepler(p_i,v_i)

print '\nUsing these orbits and the lowest safe mass ratio, the s lowest safe payload tip speed is (m/s):',delta_v_capture_to_circ/dmr print 'Using that tip speed, this is the slowest possible payload trajectory:' p_i = com_cs_pos0 v_i = (capture_speed_periapsis + delta_v_capture_to_circ/dmr)*com _cs_vel0/com_cs_vel0.norm() kepler = xyz2kepler(p_i,v_i)

print '\nLow altitude circular orbit:' p_lmo = (target_alt+r_pl)*com_cs_pos0/com_cs_pos0.norm() #Position of low altitude circular orbit. v_lmo = sqrt(mu/(target_alt+r_pl))*com_cs_vel0/com_cs_vel0.norm() # Velocity of low altitude circular orbit. kepler_lmo = xyz2kepler(p_lmo,v_lmo)

#This simple payload deflection calculation only applies if both pa yload trajectories are in the same plane. deflectionA = my_arccos(-1/keplers[1][0][1],'deflection') - pi/2 #"1" at the end is the Keplers index for eccentricity. deflectionB = my_arccos(-1/keplers[2][0][1],'deflection') - pi/2 #These deflections are relative to the periapsis velocity vectors, which are determined by the argument of periapsis (keplers element w ith index "3"). print '\nSimple payload deflection subtraction (degrees):',(((defle ctionA+keplers[1][0][3])-(deflectionB+keplers[2][0][3]))*180/pi).n()

#Compare velocity vectors at a "very large" time since periapsis to obtain the angle between them. keplers_copy = copy.deepcopy(keplers) #Use deepcopy unless you want to copy by reference so changes to the copied list also change the o riginal list. keplers_copy[1][0][5] = long_time http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 53/173 9/6/2018 capture-orbit-sling keplers_copy[2][0][5] = long_time pA_far,vA_far = kepler2xyz(keplers_copy[1][0]) pB_far,vB_far = kepler2xyz(keplers_copy[2][0]) print '\nNumeric payload deflection angle (degrees), absolute valu e:' compare_vectors(vA_far,vB_far)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 54/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

capture orbit speed at periapsis (m/s) = 4331.39333 469434 circular orbit speed at periapsis (m/s) = 3305.19320 027322 delta-v from capture orbit to circular orbit (m/s) = 1026.20013 442112 required tip speed of capture payload sling A (m/s) = 986.414891 05 mass ratio (counterbalance mass divided by payload mass) = 0.99236310 4788

Payload A trajectory: ------Hyperbolic trajectory Position vector (km) = (4019.65334732772, - 2.23712340321356e-10, 0.000000000000000) Velocity vector (km/s) = (1.43919013598211e-1 3, 5.31780822574420, 0.000000000000000) Radial distance (km) = 4019.65334732772 Speed (km/s) = 5.31780822574420 Radial velocity (km/s) = -1.52041662799987e-13 Flight path angle (degrees) = 0.000000000000000 Specific relative angular momentum vector = (-0.000000000000000, 0.000000000000000, 2.13757456350595e10) Specific relative angular momentum (m^2/s) = 2.13757456350595e10 Specific orbital energy (kJ/kg) = 3484.80000000000 Hyperbolic excess velocity at infinity (km/s) = 2.64000000000000 , C3 (km/s)^2: 6.96960000000000 Periapsis vs infinity deflection (degrees) = 37.1962472232506 Eccentricity vector = (1.65413126788470, - 1.61757013386656e-14, 0.000000000000000) Eccentricity = 1.65413126788470 , Al t. calc. = 1.65413126788470 Semi-latus rectum (km) = 10668.6876351999 , Al t. calc. = 10668.6876351999 Semi-minor axis (km) = -8096.87334661346 Semi-major axis (km) = -6145.02553948577 Periapsis altitude (km) = 623.653347327717 Periapsis (km) = 4019.65334732772 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = -5.60293753805189e-13 , Alt. calc. = -5.60293753805189e-13 Mean anomaly (degrees) = -8.53573971326177e-13 Eccentric anomaly (degrees) = -1.30489706460053e-12 True anomaly (degrees) = -2.62848194844022e-12 , Alt. calc. = -1.70754729250319e-6 Time since periapsis (hours) = -9.63242853357347e-15 returns [a, ecc, inc, AP, LAN2, tper, p]

Counterbalance A orbit: http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 55/173 9/6/2018 capture-orbit-sling ------Elliptical orbit: using true anomaly. Position vector (km) = (3820.51811102545, - 2.23712340321356e-10, -0.000000000000000) Velocity vector (km/s) = (1.43919013598211e-1 3, 3.33738732390023, 0.000000000000000) Radial distance (km) = 3820.51811102545 Speed (km/s) = 3.33738732390023 Radial velocity (km/s) = -5.15033629202851e-14 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 1.97254867994179 Specific relative angular momentum vector = (0.000000000000000, - 0.000000000000000, 1.27505487144676e10) Specific relative angular momentum (m^2/s) = 1.27505487144676e10 Specific orbital energy (kJ/kg) = -5641.01767024399 Eccentricity vector = (-0.0064174832608271 4, 1.57089830136041e-14, 0.000000000000000) Eccentricity = 0.00641748326083358 , Alt. calc. = 0.00641748326082714 Semi-latus rectum (km) = 3796.00000000025 , Al t. calc. = 3796.00000000025 Semi-minor axis (km) = 3796.07816982026 Semi-major axis (km) = 3796.15634124999 Periapsis altitude (km) = 375.794571474513 Periapsis (km) = 3771.79457147451 Apoapsis altitude (km) = 424.518111025472 Apoapsis (km) = 3820.51811102547 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 179.999999999860 , Al t. calc. = 179.999999999860 Mean anomaly (degrees) = 180.000000000139 Eccentric anomaly (degrees) = 180.000000000138 True anomaly (degrees) = 180.000000000137 , Al t. calc. = 180.000081864383 Time since periapsis (hours) = 0.986274339971654 returns [a, ecc, inc, AP, LAN2, tper, p]

Payload B trajectory: ------Hyperbolic trajectory Position vector (km) = (3796.46681889307, - 2.23697154632303e-10, 0.000000000000000) Velocity vector (km/s) = (1.44054083063624e-1 3, 5.43431921917568, 0.000000000000000) Radial distance (km) = 3796.46681889307 Speed (km/s) = 5.43431921917568 Radial velocity (km/s) = -1.76149359949131e-13 Flight path angle (degrees) = 0.000000000000000 Specific relative angular momentum vector = (-0.000000000000000, 0.000000000000000, 2.06312125988734e10) Specific relative angular momentum (m^2/s) = 2.06312125988734e10 Specific orbital energy (kJ/kg) = 3484.80000000000 Hyperbolic excess velocity at infinity (km/s) = 2.64000000000000 , C3 (km/s)^2: 6.96960000000000 Periapsis vs infinity deflection (degrees) = 38.1789052710473 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 56/173 9/6/2018 capture-orbit-sling Eccentricity vector = (1.61781139793453, - 1.04710406686117e-14, 0.000000000000000) Eccentricity = 1.61781139793453 , Al t. calc. = 1.61781139793453 Semi-latus rectum (km) = 9938.43411037852 , Al t. calc. = 9938.43411037852 Semi-minor axis (km) = -7814.85325714902 Semi-major axis (km) = -6145.02553948577 Periapsis altitude (km) = 400.466818893073 Periapsis (km) = 3796.46681889307 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = -3.70838305495469e-13 , Alt. calc. = -3.70838305495469e-13 Mean anomaly (degrees) = -9.01952604088587e-13 Eccentric anomaly (degrees) = -1.45991577219845e-12 True anomaly (degrees) = -3.00516982546808e-12 , Alt. calc. = 0.000000000000000 Time since periapsis (hours) = -1.01783726910692e-14 returns [a, ecc, inc, AP, LAN2, tper, p]

Counterbalance B orbit: ------Elliptical orbit: using true anomaly. Position vector (km) = (4055.61399803649, - 2.23728891008648e-10, -0.000000000000000) Velocity vector (km/s) = (1.43771803124127e-1 3, 3.12932859091382, 0.000000000000000) Radial distance (km) = 4055.61399803649 Speed (km/s) = 3.12932859091382 Radial velocity (km/s) = -2.88583376106089e-14 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 1.96059710486674 Specific relative angular momentum vector = (0.000000000000000, - 0.000000000000000, 1.26913488377659e10) Specific relative angular momentum (m^2/s) = 1.26913488377659e10 Specific orbital energy (kJ/kg) = -5663.91910657185 Eccentricity vector = (-0.0726847466928630, 1.25612742670732e-14, 0.000000000000000) Eccentricity = 0.0726847466928633 , Alt. calc. = 0.0726847466928630 Semi-latus rectum (km) = 3760.83272190518 , Al t. calc. = 3760.83272190518 Semi-minor axis (km) = 3770.80663480852 Semi-major axis (km) = 3780.80699901825 Periapsis altitude (km) = 110.000000000000 Periapsis (km) = 3506.00000000000 Apoapsis altitude (km) = 659.613998036492 Apoapsis (km) = 4055.61399803649 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 179.999999999990 , Al t. calc. = 179.999999999990 Mean anomaly (degrees) = 180.000000000008 Eccentric anomaly (degrees) = 180.000000000007 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 57/173 9/6/2018 capture-orbit-sling True anomaly (degrees) = 180.000000000007 , Al t. calc. = 180.000006331755 Time since periapsis (hours) = 0.980298552433412 returns [a, ecc, inc, AP, LAN2, tper, p] ------Hyperbolic trajectory Position vector (km) = (-1.59600415940996e12, 2.10294384273536e12, 0.000000000000000) Velocity vector (km/s) = (-1.59600393112755, 2.10294352855019, 0.0000 00000000000) Radial distance (km) = 2.64000039442278e12 , Alt. calcs. = 2.640000 11717475e12 , 2.64000039442278e12 , 2.64000039442278e12 Speed (km/s) = 2.64000000614503 Radial velocity (km/s) = 2.64000000614503 Payload A's velocity vector after 31688. years: (-1596.00393112755, 2 102.94352855019, 0.000000000000000) Payload A's velocity vector is aimed at 127.196247223250 degrees long itude, relative to Deimos's ascending node. ------Hyperbolic trajectory Position vector (km) = (-1.63183423226953e12, 2.07526316382690e12, 0.000000000000000) Velocity vector (km/s) = (-1.63183422339311, 2.07526313989546, 0.0000 00000000000) Radial distance (km) = 2.64000003044385e12 , Alt. calcs. = 2.640000 11731145e12 , 2.64000003044385e12 , 2.64000003044385e12 Speed (km/s) = 2.64000000614503 Radial velocity (km/s) = 2.64000000614503 Payload B's velocity vector after 31688. years: (-1631.83422339311, 2 075.26313989546, 0.000000000000000) Payload B's velocity vector is aimed at 128.178905271047 degrees long itude, relative to Deimos's ascending node.

Payload A's longitude after 31688. years is 127.20 degrees, and its r esidual velocity out of the ecliptic plane is 0.00000 m/s. No periaps is rotation is required if payload A's longitude =FINISH THIS BY LOOP ING. ALSO, CHECK PREVIOUS LINES CAUSE THEY WERE HASTILY COPIED!Payloa d B's longitude after 31688. years is 128.18 degrees, and its residua l velocity out of the ecliptic plane is 0.00000 m/s. No periapsis rot ation is required if payload B's longitude =FINISH THIS BY LOOPING. A LSO, CHECK PREVIOUS LINES CAUSE THEY WERE HASTILY COPIED!

Low altitude circular orbit: ------Circular, non-inclined orbit: using true longitude. Position vector (km) = (3796.00000000000, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.000000000000000, 3.35894328621369, 0.000000000000000) Radial distance (km) = 3796.00000000000 Speed (km/s) = 3.35894328621369 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 1.97242682475854 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.27505487144672e10) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 58/173 9/6/2018 capture-orbit-sling Specific relative angular momentum (m^2/s) = 1.27505487144672e10 Specific orbital energy (kJ/kg) = -5641.25000000000 Eccentricity vector = (0.000000000000000, 0.000000000000000, 0.000000000000000) Eccentricity = 0.000000000000000 , A lt. calc. = 0.000000000000000 Semi-latus rectum (km) = 3796.00000000000 , Al t. calc. = 3796.00000000000 Semi-minor axis (km) = 3796.00000000000 Semi-major axis (km) = 3796.00000000000 Periapsis altitude (km) = 400.000000000000 Periapsis (km) = 3796.00000000000 Apoapsis altitude (km) = 400.000000000000 Apoapsis (km) = 3796.00000000000 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Mean anomaly (degrees) = 0.000000000000000 Eccentric anomaly (degrees) = 0.000000000000000 True longitude (degrees) = 0.000000000000000 Time since periapsis (hours) = 0.000000000000000 returns [a, ecc, inc, AP, LAN2, tper, p]

Simple payload deflection subtraction (degrees): -0.982658047796883 ------Hyperbolic trajectory Position vector (km) = (-1.59600415940996e12, 2.10294384273536e12, 0.000000000000000) Velocity vector (km/s) = (-1.59600393112755, 2.10294352855019, 0.0000 00000000000) Radial distance (km) = 2.64000039442278e12 , Alt. calcs. = 2.640000 11717475e12 , 2.64000039442278e12 , 2.64000039442278e12 Speed (km/s) = 2.64000000614503 Radial velocity (km/s) = 2.64000000614503 ------Hyperbolic trajectory Position vector (km) = (-1.63183423226953e12, 2.07526316382690e12, 0.000000000000000) Velocity vector (km/s) = (-1.63183422339311, 2.07526313989546, 0.0000 00000000000) Radial distance (km) = 2.64000003044385e12 , Alt. calcs. = 2.640000 11731145e12 , 2.64000003044385e12 , 2.64000003044385e12 Speed (km/s) = 2.64000000614503 Radial velocity (km/s) = 2.64000000614503

Numeric payload deflection angle (degrees), absolute value: ------#1 = (-1596.00393112755, 2102.94352855019, 0.000000000000000) #2 = (-1631.83422339311, 2075.26313989546, 0.000000000000000) delta = (-35.8302922655535, -27.6803886547350, 0.000000000000000) #1 magnitude, inclination, azimuth (deg) = (2640.00000614503, 90.0 000000000000, 127.196247223250) #2 magnitude, inclination, azimuth (deg) = (2640.00000614503, 90.0 000000000000, 128.178905271047) delta magnitude, inclination, azimuth (deg) = (45.2770776432420, 90.0 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 59/173 9/6/2018 capture-orbit-sling 000000000000, -142.312423752851) angle between vectors #1 and #2 (degrees) = 0.982658047797712 Normalized cross product = (0.000000000000000, 0.000000000000000, 1.0 0000000000000)

8. Calculate the delta-v from the chosen moon's orbit to the inclined capture sling orbit. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 60/173 9/6/2018 capture-orbit-sling

In [10]: if cbrs[1] < tolerance: print 'Skipping this section because capture orbit sling has been disabled.' else: #Throw payload directly into the inclined capture orbit print capture_frame_string

#Capture orbit speed at the moon (either Phobos or Deimos) capture_speed_moon = sqrt(mu*(2/a_moon-1/a_capture))

#Capture orbit eccentricity ecc = (apoapsis_capture - periapsis_capture) / (apoapsis_capture + periapsis_capture) print 'capture orbit eccentricity =',ecc

if ecc < tolerance and abs(a_moon - a_capture) < tolerance: ta = 0 ; fpa = 0 ; tper = 0 print 'The capture orbit is circular with the same radius as the orbit of',moon_name,'so the true anomaly (actually, the argument of latitude), flight path angle, and time since periapsis were all set to zero.' print 'In this case, the calculated delta-v should match the circ ular orbit approximation of inclination change, which yields a delta- v of (m/s):',(2*v_moon0.norm()*sin(inc_moon/2)).n() else: #Calculate cosine of capture orbit's true anomaly at the moon (ei ther Phobos or Deimos). var('cos_ta') #Resets cos_ta so it's just a variable again. eq2 = r == a*(1-ecc^2)/(1+ecc*cos_ta) # https://en.wikipedia.org/ wiki/True_anomaly#Radius_from_true_anomaly soln2 = solve(eq2.subs(r=a_moon,a=a_capture),cos_ta) cos_ta = soln2[0].rhs() #Cosine of the true anomaly of the captur e orbit at the moon. ta = my_arccos(cos_ta,'ta') print throw_at_LAN if throw_in > 0: ta = -ta #Two places on the capture orbit are at the moon's radius. Choose to throw OUT away from the planet or throw IN. print 'capture orbit\'s true anomaly at',moon_name,'(degrees) =', (ta*180/pi).n()

#Capture orbit's flight path angle at the moon. var('cos_fpa') #Cosine of flight path angle. cos_fpa = (1 + ecc*cos_ta)/sqrt(1+ecc^2+2*ecc*cos_ta) #cosine of flight path angle at the moon. fpa = my_arccos(cos_fpa,'fpa') print 'capture orbit\'s flight path angle at',moon_name,'=',(fpa* 180/pi).n()

#Convert true anomaly to 'time since periapsis' for input into ke pler2xyz. This calculation is valid ONLY for elliptical orbits. EA = 2*arctan2(sqrt(1-ecc)*sin(ta/2),sqrt(1+ecc)*cos(ta/2)) EA = EA.n() #EA sometimes gives errors if it isn't converted to numerical form. EA = restrict_rad(EA) #Restricts angle to lie in [0,2*pi). MA = EA - ecc*sin(EA) MA = restrict_rad(MA) #Restricts angle to lie in [0,2*pi). http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 61/173 9/6/2018 capture-orbit-sling tper = sqrt(a_capture^3/mu)*MA

#Calculate the delta-v needed to go from the moon straight into the inclined capture orbit, using xyz2kepler and kepler2xyz.

print moon_name,'position vector =',p_moon1.n() print moon_name,'velocity vector =',v_moon1.n()

#Rotated trajectory inc = 0# Now this cell's calculations are done in the capture fram e, so inc = 0 instead of the old value: inc_moon #Incl ination. This one's already in radians. #LAN = 0.0*pi/180 #Longitude of ascending node. Conve rt degrees to radians. LAN2_moon = my_arccos(p_moon1[0]/p_moon1.norm(),'LAN2_moon') #[0] i s x-component. if p_moon1[1] < 0: LAN2_moon = 2*pi - LAN2_moon #[1] is y-componen t. if throw_at_LAN: lon_throw = LAN_moon else: lon_throw = LAN_moon + pi #Otherwise, throw at des cending node. print 'Rendezvous with',moon_name,'happens at longitude (degrees) =',(lon_throw*180/pi).n(),', Alt. calc:',(LAN2_moon*180/pi).n()

AP = -ta #AP = -ta because arg_lat=0 at release. arg_lat is the a ngle between the ascending node and the capture orbit sling. p_in = com_cs_pos0 v_in = com_cs_vel0

print 'Initial capture sling periapsis position vector =',p_in.n() print 'Initial capture sling periapsis velocity vector =',v_in.n()

#https://en.wikipedia.org/wiki/Orbital_elements#Euler_angle_transfo rmations #http://web.archive.org/web/20171123040031/http://www.physics.csbsj u.edu/orbit/orbit.3d.html #"I, J is in the equatorial plane of the central body. I is in the direction of the vernal equinox. J is perpendicular to I and with I defines the reference plane. K is perpendicular to the reference pla ne. Orbital elements of bodies (planets, comets, asteroids,...) in th e solar system usually use the ecliptic as that plane." #"x, y are in the orbital plane and with x in the direction to the pericenter (periapsis). z is perpendicular to the plane of the orbi t. y is mutually perpendicular to x and z." #The inverse rotation is given in the references above. #"the transformation from the I, J, K coordinate frame to the x, y, z frame": rotation_moon_to_capture = rotateZ(-AP)*rotateX(-inc)*rotateZ(-lon_ throw) #The capture orbit's position and velocity vectors were initially d efined in the frame x, y, z. Now we need to rotate to the frame I, J, K. That's the inverse of the rotation given in the references above. So reverse the order and sign of the rotations: rotation_capture_to_moon = rotateZ(lon_throw)*rotateX(inc)*rotateZ( AP) print 'Rotation matrix =' print rotation_capture_to_moon.n() http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 62/173 9/6/2018 capture-orbit-sling

p_in = rotation_capture_to_moon*p_in v_in = rotation_capture_to_moon*v_in print 'Capture sling periapsis position vector after rotation =',p_ in.n() print 'Capture sling periapsis velocity vector after rotation =',v_ in.n()

kepler2 = xyz2kepler(p_in,v_in) (kepler2).n()

print '\nSet tper=',(tper).n(digits=7),' to find position and veloc ity when capture orbit sling is at the same position as',moon_name kepler2[5] = tper #Time since periapsis corresponding to true anoma ly ta. (kepler2).n()

p2,v2 = kepler2xyz(kepler2)

print '\nCompare position vectors:',moon_name,'at release vs. captu re orbit sling when the',moon_name,'sling releases.' print 'These should be the same.' dv = compare_vectors(p_moon1,p2) print if dv.norm() > p_moon1.norm()*tolerance: print warnstring,'Position vectors aren\'t the same!' print failure else: print '\nCompare velocity vectors:',moon_name,'at release vs. cap ture orbit sling when the',moon_name,'sling releases. This is the req uired delta-v.' dv = compare_vectors(v_moon1,v2)

delta_v_m2c_vec = v2 - v_moon1 delta_v_moon_to_capture = delta_v_m2c_vec.norm() print '------' print 'delta-v directly into the inclined capture orbit (m/s) =', delta_v_moon_to_capture.n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 63/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

capture orbit eccentricity = 0.717360803768281 1 capture orbit's true anomaly at Deimos (degrees) = -173.712720193813 capture orbit's flight path angle at Deimos = 15.3109755656732 Deimos position vector = (2.34632000000000e7, 0.000000000000000, 0.00 0000000000000) Deimos velocity vector = (0.000000000000000, 1197.52577832669, 625.51 9182999978) Rendezvous with Deimos happens at longitude (degrees) = 0.00000000000 0000 , Alt. calc: 0.000000000000000 Initial capture sling periapsis position vector = (3.92046738022426e 6, 0.000000000000000, 0.000000000000000) Initial capture sling periapsis velocity vector = (0.000000000000000, 4331.39333469434, 0.000000000000000) Rotation matrix = [-0.993985292993338 -0.109513639848871 0.000000000000000] [ 0.109513639848871 -0.993985292993338 0.000000000000000] [ 0.000000000000000 0.000000000000000 1.00000000000000] Capture sling periapsis position vector after rotation = (-3.89688691 760304e6, 429344.652717125, 0.000000000000000) Capture sling periapsis velocity vector after rotation = (-474.346649 699514, -4305.34127285554, 0.000000000000000) ------Elliptical orbit: using true anomaly. Position vector (km) = (-3896.88691760304, 4 29.344652717125, 0.000000000000000) Velocity vector (km/s) = (-0.474346649699514, -4.30534127285554, 0.000000000000000) Radial distance (km) = 3920.46738022426 Speed (km/s) = 4.33139333469434 Radial velocity (km/s) = -6.08138152874835e-17 Flight path angle (degrees) = 8.53773646251594e-7 Orbital period (hours) = 13.7774796305327 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.69810862795899e10) Specific relative angular momentum (m^2/s) = 1.69810862795899e10 Specific orbital energy (kJ/kg) = -1543.81798121506 Eccentricity vector = (-0.713046088715551, 0.0785607927055758, 0.000000000000000) Eccentricity = 0.717360803768281 , A lt. calc. = 0.717360803768281 Semi-latus rectum (km) = 6732.85701124926 , Al t. calc. = 6732.85701124926 Semi-minor axis (km) = 9663.89991054697 Semi-major axis (km) = 13870.9260162561 Periapsis altitude (km) = 524.467380224260 Periapsis (km) = 3920.46738022426 Apoapsis altitude (km) = 20425.3846522880 Apoapsis (km) = 23821.3846522880 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 64/173 9/6/2018 capture-orbit-sling Argument of periapsis (degrees) = 173.712720193813 , Al t. calc. = 173.712720193813 Mean anomaly (degrees) = -2.20820288623560e-16 Eccentric anomaly (degrees) = -7.81279778486644e-16 True anomaly (degrees) = -1.92584425507893e-15 , Alt. calc. = -2.25887274392255e-6 Time since periapsis (hours) = -8.45096396810961e-18 returns [a, ecc, inc, AP, LAN2, tper, p]

Set tper= 28429.62 to find position and velocity when capture orbit sling is at the same position as Deimos ------Elliptical orbit Position vector (km) = (23463.2000000000, -4.74258423497668e-11, - 0.000000000000000) Velocity vector (km/s) = (-0.198139897653759, 0.723732750843447, 0.00 0000000000000) Radial distance (km) = 23463.2000000000 , Alt. calcs. = 23463.20000 00000 , 23463.2000000000 , 23463.2000000000 Speed (km/s) = 0.750365586688025 Radial velocity (km/s) = -0.198139897653761

Compare position vectors: Deimos at release vs. capture orbit sling w hen the Deimos sling releases. These should be the same. ------Vectors are the same, within tolerance.

Compare velocity vectors: Deimos at release vs. capture orbit sling w hen the Deimos sling releases. This is the required delta-v. ------#1 = (0.000000000000000, 1197.52577832669, 625.519182999978) #2 = (-198.139897653759, 723.732750843447, 0.000000000000000) delta = (-198.139897653759, -473.793027483239, -625.519182999978) #1 magnitude, inclination, azimuth (deg) = (1351.05227066087, 62.4 200000000000, 90.0000000000000) #2 magnitude, inclination, azimuth (deg) = (750.365586688025, 90.0 000000000000, 105.310975565673) delta magnitude, inclination, azimuth (deg) = (809.329043241954, 140. 613771949563, -112.694645962782) angle between vectors #1 and #2 (degrees) = 31.2507196854670 Normalized cross product = (-0.860770210500799, -0.235657321315456, 0.451154376710272) ------delta-v directly into the inclined capture orbit (m/s) = 809.32904324 1954

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 65/173 9/6/2018 capture-orbit-sling

In [11]: num_flag = 1

#Calculate hyperbolic deflection angle for direct moon sling. set_verbose(0) v_direct1 = (speed_moon+v_tip_direct_moon_best_case)/v_moon1.norm()*v _moon1 p_direct1 = p_moon1 kepler_direct = xyz2kepler(p_direct1,v_direct1) deflection_direct = my_arccos(-1/kepler_direct[1],'deflection') - pi/ 2 #"1" is the Kepler index for eccentricity. print 'Direct periapsis vs infinity deflection (degrees)'.ljust(lj), '=',(deflection_direct*180/pi).n()

#What location on the moon's orbit should the direct moon sling throw if the outgoing hyperbolic asymptote needs to lie in the ecliptic pl ane and the sling throws in exactly the same direction as the moon's orbital velocity vector (to maximize the Oberth effect and obtain th e values in Jokic and Longuski 2004 table 3)? var('a_moon1 speed_moon1 vtip1 inc1 def1 loc1') assume(a_moon1>0,speed_moon1>0,vtip1>0) if num_flag != 0: a_moon1 = a_moon ; speed_moon1 = speed_moon ; inc1 = inc_moon p_test0 = vector([a_moon1,0,0]) v_test0 = vector([0,speed_moon1,0]) v_r0 = p_test0.dot_product(v_test0)/p_test0.norm() if hasattr(v_r0, "__simplify_full__"): v_r0 = v_r0.simplify_full() #R adial velocity print 'v_r0 =',v_r0 #inc1=0 #Setting inc1=0 correctly yields 0 == 0, meaning loc1 has no restrictions- throwing at any location loc1 will end up in the eclip tic plane. if num_flag != 0: def1=deflection_direct #Setting def1=0 correctly yi elds "[loc1 == 1/2*pi]" and v_r = 0. p_test = rotateX(inc1)*rotateZ(loc1)*p_test0 v_test = rotateX(inc1)*rotateZ(def1)*rotateZ(loc1)*v_test0 print '\np_test =',p_test print '\nv_test[2] =',v_test[2] v_test2 = rotateX(inc1)*rotateZ(loc1)*v_test0 v_test3 = rotateV(p_test.cross_product(v_test2),def1)*v_test2 #Hyperb olic deflection rotates about the angular momentum vector h = r X v. print '\nv_test3[2] =',v_test3[2] if num_flag == 0: eq_loc = v_test[2] == 0 print '\nEquation for moon orbit location:',eq_loc loc_soln = solve(eq_loc,loc1) print '\nloc_soln =',loc_soln loc_soln2 = arctan(loc_soln[0].rhs()/cos(loc1)) #This is usually ne cessary but check loc_soln to be sure. if is_numeric(loc_soln2): loc_soln2 = loc_soln2.n() print '\nloc_soln2 (degrees) =',(loc_soln2*180/pi) else: print '\nloc_soln2 =',loc_soln2 else: loc_root = find_root(v_test3[2],0,pi) print '\nloc_root (degrees) =',(loc_root*180/pi).n() #The eq_loc method (set num_flag=0) yields "[sin(loc1) == cos(def1)*c http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 66/173 9/6/2018 capture-orbit-sling os(loc1)/sin(def1)]" #That reduces to: tan(loc1) == cot(def1) #That equation means loc1 = pi/2 - def1 because the complementary ang le in that triangle has the adjacent and opposite sides switched. #Another solution is loc1 = 3*pi/2 - def1 because that also has the s ame tangent. #Interestingly, these locations don't depend on the inclination (asid e from the fact that an inclination of zero means that throwing at an y location loc1 will converge on an asymptote in the ecliptic plane.) #The numerical method (set num_flag=1 to use find_root on v_test3 whi ch was rotated using rotateV) agrees with the symbolic method.

Direct periapsis vs infinity deflection (degrees) = 11.9784946835745 v_r0 = 0.000000000000000

p_test = (2.34632000000000e7*cos(loc1), 2.34632000000000e7*cos(0.1532 22222222222*pi)*sin(loc1), 2.34632000000000e7*sin(0.153222222222222*p i)*sin(loc1))

v_test[2] = 1351.05227066087*cos(-1/2*pi + 1.77986049845591)*cos(loc 1)*sin(0.153222222222222*pi) - 1351.05227066087*sin(-1/2*pi + 1.77986 049845591)*sin(0.153222222222222*pi)*sin(loc1)

v_test3[2] = 2702.10454132174*(3.17000096369701e10*cos(0.153222222222 222*pi)*cos(loc1)^2 + 3.17000096369701e10*cos(0.153222222222222*pi)*s in(loc1)^2)*(-3.17000096369701e10*cos(loc1)^2*sin(0.153222222222222*p i) - 3.17000096369701e10*sin(0.153222222222222*pi)*sin(loc1)^2)*cos (0.153222222222222*pi)*cos(loc1)*sin(-1/4*pi + 0.889930249227954)^2/ (abs(3.17000096369701e10*cos(0.153222222222222*pi)*cos(loc1)^2 + 3.17 000096369701e10*cos(0.153222222222222*pi)*sin(loc1)^2)^2 + abs(-3.170 00096369701e10*cos(loc1)^2*sin(0.153222222222222*pi) - 3.170000963697 01e10*sin(0.153222222222222*pi)*sin(loc1)^2)^2) - 1351.05227066087*(2 *(-3.17000096369701e10*cos(loc1)^2*sin(0.153222222222222*pi) - 3.1700 0096369701e10*sin(0.153222222222222*pi)*sin(loc1)^2)^2*sin(-1/4*pi + 0.889930249227954)^2/(abs(3.17000096369701e10*cos(0.153222222222222*p i)*cos(loc1)^2 + 3.17000096369701e10*cos(0.153222222222222*pi)*sin(lo c1)^2)^2 + abs(-3.17000096369701e10*cos(loc1)^2*sin(0.153222222222222 *pi) - 3.17000096369701e10*sin(0.153222222222222*pi)*sin(loc1)^2)^2) - 1)*cos(loc1)*sin(0.153222222222222*pi) + 1351.05227066087*(-3.17000 096369701e10*cos(loc1)^2*sin(0.153222222222222*pi) - 3.17000096369701 e10*sin(0.153222222222222*pi)*sin(loc1)^2)*sin(-1/2*pi + 1.7798604984 5591)*sin(loc1)/sqrt(abs(3.17000096369701e10*cos(0.153222222222222*p i)*cos(loc1)^2 + 3.17000096369701e10*cos(0.153222222222222*pi)*sin(lo c1)^2)^2 + abs(-3.17000096369701e10*cos(loc1)^2*sin(0.153222222222222 *pi) - 3.17000096369701e10*sin(0.153222222222222*pi)*sin(loc1)^2)^2)

loc_root (degrees) = 78.0215053164267

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 67/173 9/6/2018 capture-orbit-sling

In [12]: v_c = sqrt((2*sigma)/(safety*rho)) tether_mass_ratio(v_star) = sqrt(pi)*v_star*exp(v_star^2)*erf(v_star) #sling mass ratio = sling mass divided by payload (+ grapple, ballas t) mass. display(latex(tether_mass_ratio)) print 'characteristic velocity (m/s) =',v_c

numpoints_plot_direct = 4 #Either zero to disable plots, or any integ er >= 2. min_plot_direct = 0 max_plot_direct = 2*pi summary_digits = 5 if numpoints_plot_direct < 4: verbose_direct = 1 else: verbose_direct = 0 lj=40 set_verbose(0) print capture_frame_string

#If throwing from an arbitrary location on the moon's orbit, what del ta-v is needed so the outgoing hyperbolic asymptote lies in the eclip tic plane? #Assume the throw doesn't change the radial velocity- since the moon is treated as being in a circular orbit, that means the radial veloc ity at the throw time is also zero, which also means the throw locati on is also the periapsis location for the outgoing payload hyperbolic trajectory. offsets = [pi/2] #First, calculate the worst-case delta-v for direct moon sling. #Now, if requested, add offsets that result in arglat going from min_ plot_direct to max_plot_direct. for j in range(numpoints_plot_direct): offsets += [j*(max_plot_direct -min_plot_direct)/(numpoints_plot_direct-1)+min_plot_direct - (pi/2 - deflection_direct)] #Loop through offsets to make plots. offsets += [best_case_offset] #Then, calculate the actual delta-v fro m moon for requested direct moon sling throw location. Do this last s o its values are used in the next sections. arglats_direct = [] aims_direct = [] deltavees_direct = [] payload_vinfs_direct = [] payload_lons_direct = [] tether_mass_ratios_direct = [] print 'Throwing payloads from different arguments of latitude:' if(verbose_direct): print else: print '- ' * (numpoints_plot_direct+2)+'| ->',numpoints_plot_di rect+2,'throws to aim:' #This bar shows the number of notifications (". ") that need to be returned until these calculations are finishe d. for loopoff in offsets: if(verbose_direct): print print 'offset for this loop =',loopoff else: print '.', arglat_direct = pi/2 - deflection_direct + loopoff arglats_direct += [(arglat_direct*180/pi).n()] #These are in degree s because their only purpose is to be plotted. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 68/173 9/6/2018 capture-orbit-sling var('a_moon1 speed_moon1 vtip1 inc1 def1 loc1 aim1') assume(a_moon1>0,speed_moon1>0,vtip1>0) num_flag = 0 #if num_flag != 0: #All these variables used to be symbolic (at lea st until assignments below) unless num_flag != 0: a_moon1 = a_moon ; speed_moon1 = speed_moon ; inc1 = inc_moon ; def 1 = deflection_direct p_test0 = vector([a_moon1,0,0]) v_test0 = vector([0,speed_moon1,0]) v_r0 = (p_test0.dot_product(v_test0)/p_test0.norm()) #Radial veloci ty if hasattr(v_r0, "__simplify_full__"): v_r0 = v_r0.simplify_full() #print 'v_r0 =',v_r0 #inc1=0 #This correctly yields "[aim1 == 0]" #def1=0 #This correctly yields v_r = 0 #loc1=pi/2-def1 #This correctly yields "[aim1 == 0]" loc1=arglat_direct #loc1=0.0 #0 #This should also yield an aim1 expression (e.g. aim1 = -inc1) that doesn't depend on the hyperbolic deflection angle def 1, because this position is already in the ecliptic plane so the defl ection angle shouldn't matter there as long as the velocity vector is aimed down/up by an angle of "inc1" to put the velocity vector in th e ecliptic plane as soon as it's released. #aim1=-inc1 p_test1 = rotateX(inc1)*rotateZ(loc1)*p_test0 #This is the last pos ition vector to be calculated, all changes after this point are to th e velocity vectors. v_test1 = rotateX(inc1)*rotateZ(loc1)*v_test0 #This is the velocity of the moon at the location loc1 where it throws. if is_numeric(p_test1): p_test1 = p_test1.n() if is_numeric(v_test1): v_test1 = v_test1.n() if(verbose_direct): print 'p_test1 =',p_test1 #print 'v_test1 =',v_test1 v_r1 = (p_test1.dot_product(v_test1)/p_test1.norm()) #Radial veloci ty if hasattr(v_r1, "__simplify_full__"): v_r1 = v_r1.simplify_full() #print 'v_r1 =',v_r1 #v_test2 = rotateV(p_test1,aim1)*((speed_moon1+vtip1)/v_test1.norm ())*v_test1 #Aim rotates about the position vector so it doesn't chan ge the radial velocity. v_test2 = rotateX(inc1)*rotateZ(loc1+def1)*rotateX(aim1)*v_test0 v_test2b = rotateX(inc1)*rotateZ(loc1)*rotateX(aim1)*v_test0 if is_numeric(v_test2): v_test2 = v_test2.n() #print 'v_test2 =',v_test2 v_r2 = (p_test1.dot_product(v_test2)/p_test1.norm()) #Radial veloci ty if hasattr(v_r2, "__simplify_full__"): v_r2 = v_r2.simplify_full() #print 'v_r2 =',v_r2 v_test3 = rotateV(p_test1.cross_product(v_test2),def1)*v_test2b #Hy perbolic deflection rotates about the angular momentum vector h = r X v. if is_numeric(v_test3): v_test3 = v_test3.n() #print 'v_test3 =',v_test3 #compare_vectors(v_test2,v_test3,num_flag)#The last value is an opt ional numeric flag. 0/1 = symbolic/numeric. #v_r3 = (p_test1.dot_product(v_test3)/p_test1.norm()) #Radial veloc ity http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 69/173 9/6/2018 capture-orbit-sling #if hasattr(v_r3, "__simplify_full__"): v_r3 = v_r3.simplify_full() #print 'v_r3 =',v_r3 v_test4 = rotateV(p_test1,aim1)*v_test1 #print 'v_test4 =',v_test4 v_test5 = rotateV(p_test1.cross_product(v_test4),def1)*v_test4 #print 'v_test5 =',v_test5 aim_soln = find_root(v_test5[2],-pi/2,pi/2) #eq_aim = v_test2[2] == 0 #print 'Equation for aim:',eq_aim #aim_soln = solve(eq_aim,aim1) #print aim_soln#[0].simplify_full() #aim_soln = solve(eq_aim.subs(loc1=pi/2-def1),aim1) if(verbose_direct): print 'aim_soln =',aim_soln #Position and velocity of the moon when the direct moon sling throw s. p_moon2 = p_test1 v_moon2 = v_test1 if(verbose_direct): print moon_name+'\'s velocity vector at throw t ime is aimed at',(restrict_rad(atan2(v_moon2[1],v_moon2[0]))*180/pi). n(),'degrees longitude, relative to',moon_name+'\'s ascending node.' aim_direct = aim_soln aims_direct += [(aim_direct*180/pi).n()] #These are in degrees beca use their only purpose is to be plotted. v_direct = rotateX(inc_moon)*rotateZ(arglat_direct)*rotateX(aim_dir ect)*v_moon0 v_direct *= (speed_moon+v_tip_direct_moon_best_case)/v_direct.norm () if(verbose_direct): print 'Payload\'s velocity vector at throw time is aimed at',(restrict_rad(atan2(v_direct[1],v_direct[0]))*180/pi).n (),'degrees longitude, relative to',moon_name+'\'s ascending node.' delta_v_direct = v_direct - v_moon2 deltavees_direct += [delta_v_direct.norm().n()] tether_mass_ratios_direct += [tether_mass_ratio(delta_v_direct.norm ().n()/v_c)] #Direct moon sling always uses default characteristic ve locity. kepler_test4 = xyz2kepler(p_test1,v_direct) kepler_test_copy = copy.deepcopy(kepler_test4) #Use deepcopy unless you want to copy by reference so changes to the copied list also cha nge the original list. kepler_test_copy[5] = long_time pD_far,vD_far = kepler2xyz(kepler_test_copy) payload_vinfs_direct += [vD_far] payload_lon_direct = restrict_rad(atan2(vD_far[1],vD_far[0])) #Long itude of the payload's outgoing asymptote, relative to the moon's asc ending node. Sagemath expects arctan2(y,x). payload_lons_direct += [(payload_lon_direct*180/pi).n()] #These are in degrees because their only purpose is to be plotted. if(verbose_direct): print 'Payload\'s velocity vector after',(long_time/(year)).n(dig its=summary_digits),'years:',vD_far print 'Payload\'s velocity vector is aimed at',(payload_lon_direc t*180/pi).n(),'degrees longitude, relative to',moon_name+'\'s ascendi ng node.' print 'Numeric deflection angle:' compare_vectors(v_direct,vD_far) if abs(loopoff-pi/2) < tolerance: #If best_case_offset or one of th e plotting offsets equals pi/2, this will run several times. That's o http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 70/173 9/6/2018 capture-orbit-sling kay. v_tip_direct_moon_worst_case = delta_v_direct.norm().n() v_tip_direct_moon = delta_v_direct.norm().n() #This will be assigne d repeatedly, but the last one is the actual requested offset anyway. if(verbose_direct): print moon_name+'\'s argument of latitude at throw (degrees)'.lju st(lj),'=',(arglat_direct*180/pi).n() print 'aim_direct =',(aim_direct*180/pi).n(),'degrees.' print 'delta_v_direct =',delta_v_direct.n() print 'delta_v_direct.norm() =',delta_v_direct.norm().n()

#Discard the first (worst case) and last (requested offset) values to leave only values for the plots. if numpoints_plot_direct > 0: arglats_direct = arglats_direct[1:-1] aims_direct = aims_direct[1:-1] deltavees_direct = deltavees_direct[1:-1] payload_vinfs_direct = payload_vinfs_direct[1:-1] payload_lons_direct = payload_lons_direct[1:-1] tether_mass_ratios_direct = tether_mass_ratios_direct[1:-1] print '\nFinished.'

v_{\star} \ {\mapsto}\ \sqrt{\pi} v_{\star} \operatorname{erf}\left(v _{\star}\right) e^{\left(v_{\star}^{2}\right)}

characteristic velocity (m/s) = 2726.88419920932 Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

Throwing payloads from different arguments of latitude: ------| -> 6 throws to aim: ...... Finished.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 71/173 9/6/2018 capture-orbit-sling

In [13]: #These plots are in a separate cell so they can be reformatted withou t recomputing all those values. if numpoints_plot_direct > 0: x_axis_data = arglats_direct ; x_axis_title = moon_name+'\'s arglat at throw time (degrees)' #x_axis_data = payload_lons_direct ; x_axis_title = 'Payload\'s lon gitude after '+str((long_time/year).n(digits=summary_digits))+' years (degrees)' labels_direct = [] plots_direct = [] labels_direct += ['Aim angle (degrees)'] plots_direct += [line(zip(x_axis_data, aims_direct), rgbcolor='gre en')]#, legend_label = labels_direct[-1])] labels_direct += ['Delta-v (m/s)'] plots_direct += [line(zip(x_axis_data, deltavees_direct), rgbcolor ='red')]#, legend_label = labels_direct[-1])] labels_direct += ['Tether mass ratio'] plots_direct += [line(zip(x_axis_data, tether_mass_ratios_direct), rgbcolor='red')]#, legend_label = labels_direct[-1])] labels_direct += ['Payload longitude (degrees)'] plots_direct += [line(zip(x_axis_data, payload_lons_direct), rgbco lor='green')]#, legend_label = labels_direct[-1])] #labels_direct += ['V-inf magnitude (m/s)'] #Too similar! #plots_direct += [line(zip(x_axis_data, [x.norm() for x in payload _vinfs_direct]), rgbcolor='green')]#, legend_label = labels_direct[- 1])] labels_direct += ['V-inf component out of ecliptic (m/s)'] plots_direct += [line(zip(x_axis_data, [x[2] for x in payload_vinf s_direct]), rgbcolor='green')]#, legend_label = labels_direct[-1])]

#pt3 = line([(target_c_rad,0),(target_c_rad,0.5*v_infs[mis[j]]^2/10 00.0)]) if textonly == 0: for j in range(len(labels_direct)): show(plots_direct[j], axes=tr ue, frame=True, gridlines=false, figsize=6, axes_labels=[x_axis_title ,labels_direct[j]]) else: print 'Textonly =',textonly,'so this image wasn\'t displaye d.'

print 'Maximum v-inf magnitude (m/s) =',max([x.norm() for x in payload_vinfs_direct]) print 'Minimum v-inf magnitude (m/s) =',min([x.norm() for x in payload_vinfs_direct]) print 'Maximum v-inf out of ecliptic magnitude (m/s) =',max([abs(x[2 ]) for x in payload_vinfs_direct]) print 'v_tip_direct_moon_worst_case =',v_tip_direct_ moon_worst_case.n()

#OLD CODE. This was just a guess at the worst case. v_i = vector([speed_moon*cos(inc_moon),0,speed_moon*sin(inc_moon)]).n () v_f = vector([speed_moon+v_tip_direct_moon_best_case,0,0]).n() delta_v = v_f - v_i v_tip_direct_moon_worst_case_OLD = delta_v.norm().n() print 'OLD CODE v_tip_direct_moon_worst_case_OLD =',v_tip_direct_ moon_worst_case_OLD http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 72/173 9/6/2018 capture-orbit-sling

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 73/173 9/6/2018 capture-orbit-sling

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 74/173 9/6/2018 capture-orbit-sling

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 75/173 9/6/2018 capture-orbit-sling

Maximum v-inf magnitude (m/s) = 2640.00000614503 Minimum v-inf magnitude (m/s) = 2640.00000614502 Maximum v-inf out of ecliptic magnitude (m/s) = 0.0000161244219599051 v_tip_direct_moon_worst_case = 2165.14916665712 OLD CODE v_tip_direct_moon_worst_case_OLD = 2154.16953389270

9. Print the moon sling's payload and counterbalance orbits. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 76/173 9/6/2018 capture-orbit-sling

In [14]: set_verbose(2) if cbrs[1] < tolerance: #If cbrs[1]=0, send the payload directly into the specified hyperbo lic escape trajectory: delta_v_from_moon = v_tip_direct_moon print 'Payload is thrown directly from',moon_name,'into a hyperboli c trajectory:' v_ms_1 = v_moon2+delta_v_direct p_ms_1 = p_moon2 keplers[0][0] = xyz2kepler(p_ms_1,v_ms_1) throw_tpers[0][0] = keplers[0][0][5]

#Describe the payload's outgoing hyperbolic asymptote. keplers_copy = copy.deepcopy(keplers) #Use deepcopy unless you want to copy by reference so changes to the copied list also change the o riginal list. keplers_copy[0][0][5] = long_time pD_far,vD_far = kepler2xyz(keplers_copy[0][0]) payload_lon_direct = restrict_rad(atan2(vD_far[1],vD_far[0])) #Long itude of the payload's outgoing asymptote, relative to the moon's asc ending node. Sagemath expects arctan2(y,x). print 'Payload\'s velocity vector after',(keplers_copy[0][0][5]/(36 00*24*365.25)).n(digits=summary_digits),'years:',vD_far print 'Payload\'s velocity vector is aimed at',(payload_lon_direct* 180/pi).n(),'degrees longitude, relative to',moon_name+'\'s ascending node.' payload_lon_string = moon_name+'\'s argument of latitude at throw t ime is '+str((arglat_direct*180/pi).n(digits=summary_digits))+' degre es, ' payload_lon_string += 'which is offset from the best case by '+str ((best_case_offset*180/pi).n(digits=summary_digits))+' degrees. ' payload_lon_string += 'The payload\'s longitude after '+str((long_t ime/year).n(digits=summary_digits))+' years is '+str((payload_lon_dir ect*180/pi).n(digits=summary_digits))+' degrees, ' payload_lon_string += 'and its residual velocity out of the eclipti c plane is '+str((vD_far[2]).n(digits=summary_digits))+' m/s.' print '\n'+payload_lon_string+'\n'

print '\n\nCounterbalance is',cbrs[0].n(digits=summary_digits),'tim es as massive as the payload:' v_ms_2 = v_moon2-delta_v_direct/cbrs[0] p_ms_2 = p_moon2 #Approximate the moon sling's counterbalance and p ayload as though they come from the same point. For more accuracy, ca lculate the angle of the plane the moon sling swings in, then use tha t to calculate the release positions. But if you're gonna do that, mi ght as well also stop assuming the moons are in circular orbits... keplers[0][1] = xyz2kepler(p_ms_2,v_ms_2) throw_tpers[0][1] = keplers[0][1][5] else: #If cbrs[1]>0, send the payload+balance to capture orbit sling: delta_v_from_moon = delta_v_moon_to_capture if throw_in > 0: print 'Payload is thrown IN at',pname,'so it goes into the inclined capture orbit:' else: print 'Payload is thrown OUT at',pname,'so it goes into the inclined capture orbit:' v_ms_1 = v_moon1 + delta_v_m2c_vec http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 77/173 9/6/2018 capture-orbit-sling p_ms_1 = p_moon1 keplers[0][0] = xyz2kepler(p_ms_1,v_ms_1) throw_tpers[0][0] = keplers[0][0][5]

if throw_in > 0: print '\n\nCounterbalance is',cbrs[0].n(digits=sum mary_digits),'times as massive as the payload. It\'s thrown OUT to an other inclined orbit:' else: print '\n\nCounterbalance is',cbrs[0].n(digits=sum mary_digits),'times as massive as the payload. It\'s thrown IN to ano ther inclined orbit:' v_ms_2 = v_moon1 - delta_v_m2c_vec/cbrs[0] p_ms_2 = p_moon1 #Approximate the moon sling's counterbalance and p ayload as though they come from the same point. For more accuracy, ca lculate the angle of the plane the moon sling swings in, then use tha t to calculate the release positions. But if you're gonna do that, mi ght as well also stop assuming the moons are in circular orbits... keplers[0][1] = xyz2kepler(p_ms_2,v_ms_2) throw_tpers[0][1] = keplers[0][1][5] print

if throw_tpers[0][0] < 0: time_from_moon_to_capture_periapsis = -th row_tpers[0][0] #The negative of "time since periapsis". else: time_from_moon_to_capture_periapsis = -th row_tpers[0][0] + 2*pi*sqrt(keplers[0][0][0]^3/mu) #Time until next p eriapsis. #print 'throw_tpers[0][0]/60 =',throw_tpers[0][0]/60,'minutes.' print 'time_from_moon_to_capture_periapsis =',(time_from_moon_to_ca pture_periapsis/60).n(digits=summary_digits),'minutes.' time_from_capture_periapsis_to_moon = period_capture - time_from_mo on_to_capture_periapsis print 'time_from_capture_periapsis_to_moon =',(time_from_capture_pe riapsis_to_moon/60).n(digits=summary_digits),'minutes.' if abs(period_capture - 2*pi*sqrt(keplers[0][0][0]^3/mu)) > toleran ce: print warnstring,'Requested capture orbit period',period_capture/ units,uname,'doesn\'t match calculated capture period',(2*pi*sqrt(kep lers[0][0][0]^3/mu)/units).n(),uname+'.' print failure

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 78/173 9/6/2018 capture-orbit-sling

Payload is thrown IN at Mars so it goes into the inclined capture orb it: ------Elliptical orbit: using true anomaly. Position vector (km) = (23463.2000000000, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (-0.198139897653759, 0.723732750843447, 0.000000000000000) Radial distance (km) = 23463.2000000000 Speed (km/s) = 0.750365586688025 Radial velocity (km/s) = -0.198139897653759 Flight path angle (degrees) = 15.3109755656728 Orbital period (hours) = 13.7774796305327 Specific relative angular momentum vector = (0.000000000000000, - 0.000000000000000, 1.69810862795900e10) Specific relative angular momentum (m^2/s) = 1.69810862795900e10 Specific orbital energy (kJ/kg) = -1543.81798121506 Eccentricity vector = (-0.713046088715551, 0.0785607927055736, 0.000000000000000) Eccentricity = 0.717360803768281 , A lt. calc. = 0.717360803768281 Semi-latus rectum (km) = 6732.85701124928 , Al t. calc. = 6732.85701124928 Semi-minor axis (km) = 9663.89991054698 Semi-major axis (km) = 13870.9260162561 Periapsis altitude (km) = 524.467380224270 Periapsis (km) = 3920.46738022427 Apoapsis altitude (km) = 20425.3846522880 Apoapsis (km) = 23821.3846522880 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 173.712720193813 , Al t. calc. = 173.712720193813 Mean anomaly (degrees) = 206.348450132754 Eccentric anomaly (degrees) = 195.419880880776 True anomaly (degrees) = 186.287279806187 , Al t. calc. = 186.287279806187 Time since periapsis (hours) = 7.89711546804449 returns [a, ecc, inc, AP, LAN2, tper, p]

Counterbalance is 4.0000 times as massive as the payload. It's thrown OUT to another inclined orbit: ------Elliptical orbit: using true anomaly. Position vector (km) = (23463.2000000000, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.0495349744134398, 1.31597403519750, 0.781898978749973) Radial distance (km) = 23463.2000000000 Speed (km/s) = 1.53153758947483 Radial velocity (km/s) = 0.0495349744134398 Flight path angle (degrees) = 1.85345762980858 Orbital period (hours) = 50.1365753583964 Specific relative angular momentum vector = (0.000000000000000, - 1.83458521182064e10, 3.08769619826459e10) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 79/173 9/6/2018 capture-orbit-sling Specific relative angular momentum (m^2/s) = 3.59159723691404e10 Specific orbital energy (kJ/kg) = -652.538544070710 Eccentricity vector = (0.283679096133375, - 0.0357120647312778, -0.0212186762015015) Eccentricity = 0.286704400681545 , A lt. calc. = 0.286704400681544 Semi-latus rectum (km) = 30119.2193683966 , Al t. calc. = 30119.2193683966 Semi-minor axis (km) = 31439.0595360450 Semi-major axis (km) = 32816.7357998695 Periapsis altitude (km) = 20012.0332300433 Periapsis (km) = 23408.0332300433 Apoapsis altitude (km) = 38829.4383696957 Apoapsis (km) = 42225.4383696957 Inclination (degrees) = 30.7170827436030 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 351.669182294540 , Al t. calc. = 351.669182294542 Mean anomaly (degrees) = 4.43133606841860 Eccentric anomaly (degrees) = 6.20760350695309 True anomaly (degrees) = 8.33081770545808 , Al t. calc. = 8.33081770545961 Time since periapsis (hours) = 0.617144485368470 returns [a, ecc, inc, AP, LAN2, tper, p]

time_from_moon_to_capture_periapsis = 352.82 minutes. time_from_capture_periapsis_to_moon = 473.83 minutes.

10. Solve for the ballast mass needed to keep an asymmetric sling's center of mass at the rotational axis. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 80/173 9/6/2018 capture-orbit-sling

In [15]: if cbrs[1] > tolerance: #Skip moon sling (index 0) grapples for now- its required tip accel eration hasn't yet been calculated. #Also skip the moment ballast sling (index 3) because its payload h asn't yet been calculated. for j in range(1,3): #range(n1,n2) goes up to n2-1. payload_masses [j][0] = payload_mass payload_masses [j][1] = payload_mass*cbrs[j] grapple_masses [j][0] = payload_masses[j][0]*grapple_fraction*(o megas[j]^2*radii[j]/g) grapple_masses [j][1] = payload_masses[j][1]*grapple_fraction*(o megas[j]^2*radii[j]/cbrs[j]/g) tip_masses_empty[j][0] = grapple_masses[j][0] tip_masses_full [j][0] = grapple_masses[j][0] + payload_masses[j] [0] tip_masses_empty[j][1] = grapple_masses[j][1] tip_masses_full [j][1] = grapple_masses[j][1] + payload_masses[j] [1]

#Total cross sectional area of sling at distance "x" (in meters) from the hub, which is hopefully also the center of mass. #See eq 10 from Puig-Suari et al. 1995: http://web.archive.org/web/20 171006015505/https://engineering.purdue.edu/people/james.m.longuski. 1/JournalArticles/1995/ATetherSlingforLunarandInterplanetaryExplorati on.pdf #Depends on: # x : distance from center of mass (also called the "hub") in meters # l : radius of the sling in meters # v_tip : tip velocity in m/s # vel_c : characteristic velocity of the sling material in m/s - doesn't overwrite the actual v_c # sgma : tensile strength of the sling material in Pa - missi ng the "i" so it doesn't overwrite the actual "sigma" # pmass : mass (in kg) of everything attached to the end of th e sling, including grapple - doesn't overwrite actual "payload_mass" sling_area(x,l,v_tip,vel_c,sgma,pmass) = pmass*v_tip^2/(sgma*l)*exp(( v_tip/vel_c)^2*(1-x^2/l^2)) #Total cross sectional area of sling, in m^2. sling_diameter(x,l,v_tip,vel_c,sgma,pmass) = 2*sqrt(sling_area(x,l,v_ tip,vel_c,sgma,pmass)/pi) #Equivalent cross sectional diameter, in me ters. print 'Sling area at hub =',sling_area(0,l,v_tip,vel_c,sgma,pmass) print 'Sling area at tip =',sling_area(l,l,v_tip,vel_c,sgma,pmass)

print 'cbrs[0] =',cbrs[0] if cbrs[1] > tolerance: print 'cbrs[1] =',cbrs[1] print 'cbrs[2] =',cbrs[2] var('r w vlc sig pm1 pm2 cbr') assume(r>0,w>0,vlc>0,sig>0,pm1>0,pm2>0,cbr>0) balance1_hub_area = sling_area(0,r,w*r,vlc,sig,pm1) print 'balance1_hub_area =',balance1_hub_area balance2_hub_area = sling_area(0,r/cbr,w*r/cbr,vlc,sig,pm2) print 'balance2_hub_area =',balance2_hub_area

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 81/173 9/6/2018 capture-orbit-sling print '\nSolution 1: Add \"ballast\" mass at tip that isn\'t ever rel eased which makes the sling cross sectional areas (and therefore the forces) equal at the hub.' eqbalance1 = balance1_hub_area == balance2_hub_area solnbalance1 = solve(eqbalance1,pm2) #print 'solnbalance1 = ',solnbalance1 balance_factor(r,w,vlc,sig,cbr) = (solnbalance1[0].rhs()/pm1).simplif y_full() #Divide by pm1 to get the correct ratio of pm2 (counterbalan ce tip mass) to pm1 (payload tip mass). print 'balance_factor =',balance_factor(r,w,vlc,sig,cbr) #more compac t: cbr*e^((r*w/vlc)^2*(1-1/cbr^2)) , where r*w = payload tip speed

print '\n#Solution 2: Divide the shorter arm\'s design\'s characteris tic velocity by factor=cbr. This spreads the mass out over the sling, it\'s not just at the tip.' balance2_reinforced_hub_area = sling_area(0,r/cbr,w*r/cbr,vlc/cbr,sig ,pm2) print 'balance2_reinforced_hub_area =',balance2_reinforced_hub_area eqbalance2 = balance1_hub_area == balance2_reinforced_hub_area solnbalance2 = solve(eqbalance2,pm2) print 'solnbalance2 = ',solnbalance2

if ballast_choice == 0: #Totally ignore balancing problem. Set all ba llast masses to zero and set all characteristic velocities to defaul t. for j in range(num_slings): ballast_masses[j] = [0,0] ; v_cs[j] = [ v_c,v_c] elif ballast_choice == 1: #Set all characteristic velocities to defau lt, put extra ballast mass on the tips of the shorter slings. for j in range(num_slings): v_cs[j] = [v_c,v_c] for j in range(1,3): #Skip moon sling and moment ballast sling. if cbrs[j] > tolerance: if cbrs[j] > 1: ballast_masses[j][0] = 0 ballast_masses[j][1] = tip_masses_full[j][0]*balance_factor(r adii[j],omegas[j],v_cs[j][0],sigma,cbrs[j]) - tip_masses_full[j][1] else: #Reverse balance_factor: use radius/cbr and 1/cbr instea d. ballast_masses[j][1] = 0 ballast_masses[j][0] = tip_masses_full[j][1]*balance_factor(r adii[j]/cbrs[j],omegas[j],v_cs[j][1],sigma,1/cbrs[j]) - tip_masses_fu ll[j][0] tip_masses_empty[j][0] += ballast_masses[j][0] tip_masses_full [j][0] += ballast_masses[j][0] tip_masses_empty[j][1] += ballast_masses[j][1] tip_masses_full [j][1] += ballast_masses[j][1] #That ballast mass is actually the total mass which includes gr apple mass. Total = (1+f)*bm, so gm = bm*f/(1+f) and bm /= 1+f . #There are three sections in this notebook which make this corr ection. This is #1. grapple_masses [j][0] += ballast_masses[j][0]*grapple_fraction *(omegas[j]^2*radii[j]/g)/(1+grapple_fraction*(omegas[j]^2*radii[j]/g )) ballast_masses [j][0] /= 1+grapple_fraction*(omegas[j]^2*radii [j]/g) grapple_masses [j][1] += ballast_masses[j][1]*grapple_fraction *(omegas[j]^2*radii[j]/cbrs[j]/g)/(1+grapple_fraction*(omegas[j]^2*ra http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 82/173 9/6/2018 capture-orbit-sling dii[j]/cbrs[j]/g)) ballast_masses [j][1] /= 1+grapple_fraction*(omegas[j]^2*radii [j]/cbrs[j]/g) elif ballast_choice == 2: #Set all ballast masses to zero, divide cha racteristic velocity of shorter slings by cbrs[j]. for j in range(num_slings): ballast_masses[j] = [0,0] if cbrs[j] > tolerance: if cbrs[j] > 1: v_cs[j][0] = v_c; v_cs[j][1] = v_c/cbrs [j]; else: v_cs[j][0] = v_c*cbrs[j]; v_cs[j][1] = v_c; #Mu ltiply by cbr because *payload* v_c needs to be divided by 1/cbr. else: print '!!!WARNING!!! ballast_choice',ballast_choice,'isn\'t rec ognized!'

mass_ratio_direct_moon = tether_mass_ratio(v_tip_direct_moon/v_c) #Di rect moon sling always uses default characteristic velocity. direct_moon_radius = v_tip_direct_moon^2/tip_accel_max direct_moon_omega = v_tip_direct_moon/direct_moon_radius #Angular ro tation rate of the direct moon sling. if cbrs[1] > tolerance: tether_masses[1][0] = (tip_masses_full[1][0])*tether_mass_ratio(rad ii[1]*omegas[1]/v_cs[1][0]) tether_masses[2][0] = (tip_masses_full[2][0])*tether_mass_ratio(rad ii[2]*omegas[2]/v_cs[2][0]) direct_moon_tether_mass = payload_mass*(1+grapple_fraction*(direct_mo on_omega^2*direct_moon_radius/g))*mass_ratio_direct_moon #Direct moon sling doesn't use ballast. if cbrs[1] > tolerance: print 'mass of capture sling A1 (tons) =',(tether_masses[1][0]/1 000.0).n() print 'mass of capture sling B1 (tons) =',(tether_masses[2][0]/1 000.0).n() print 'mass of direct moon sling (tons) =',(direct_moon_tether_ma ss/1000.0).n()

mass_ratio_direct_moon_worst_case = tether_mass_ratio(v_tip_direct_mo on_worst_case/v_c) #Direct moon sling always uses default characteris tic velocity. direct_moon_radius_worst_case = v_tip_direct_moon_worst_case^2/tip_ac cel_max direct_moon_omega_worst_case = v_tip_direct_moon_worst_case/direct_m oon_radius_worst_case #Angular rotation rate of the direct moon slin g, worst case. print 'mass of direct moon sling (WORST CASE - tons) =',(payload_mass /1000.0*(1.0+grapple_fraction*(direct_moon_omega_worst_case^2*direct_ moon_radius_worst_case/g))*mass_ratio_direct_moon_worst_case).n() #Di rect moon sling doesn't use ballast.

if cbrs[1] > tolerance: tether_masses[1][1] = (tip_masses_full[1][1])*tether_mass_r atio(radii[1]*omegas[1]/cbrs[1]/v_cs[1][1]) print '\nmass of capture sling A\'s counterbalance arm (tons) =',(t ether_masses[1][1]/1000.0).n() tether_masses[2][1] = (tip_masses_full[2][1])*tether_mass_r atio(radii[2]*omegas[2]/cbrs[2]/v_cs[2][1]) print 'mass of capture sling B\'s counterbalance arm (tons) =',(tet http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 83/173 9/6/2018 capture-orbit-sling her_masses[2][1]/1000.0).n() #debug_sling(1) #debug_sling(2) Sling area at hub = pmass*v_tip^2*e^(v_tip^2/vel_c^2)/(l*sgma) Sling area at tip = pmass*v_tip^2/(l*sgma) cbrs[0] = 4.00000000000000 cbrs[1] = 0.992363104788 cbrs[2] = 0.917526189989 balance1_hub_area = pm1*r*w^2*e^(r^2*w^2/vlc^2)/sig balance2_hub_area = pm2*r*w^2*e^(r^2*w^2/(cbr^2*vlc^2))/(c br*sig)

Solution 1: Add "ballast" mass at tip that isn't ever released which makes the sling cross sectional areas (and therefore the forces) equa l at the hub. balance_factor = cbr*e^(r^2*w^2/vlc^2 - r^2*w^2/(cbr^2*vlc^2))

#Solution 2: Divide the shorter arm's design's characteristic velocit y by factor=cbr. This spreads the mass out over the sling, it's not j ust at the tip. balance2_reinforced_hub_area = pm2*r*w^2*e^(r^2*w^2/vlc^2)/(cbr*sig) solnbalance2 = [ pm2 == cbr*pm1 ] mass of capture sling A1 (tons) = 0.358497023470001 mass of capture sling B1 (tons) = 0.479357317433260 mass of direct moon sling (tons) = 1.71348638694640 mass of direct moon sling (WORST CASE - tons) = 2.44039888464366

mass of capture sling A's counterbalance arm (tons) = 0.3610209671389 70 mass of capture sling B's counterbalance arm (tons) = 0.5173693396147 12

11. Define the slings' moments of inertia and rotational kinetic energy. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 84/173 9/6/2018 capture-orbit-sling

In [16]: radii[0] = RDF(delta_v_from_moon^2/tip_accel_max) #if cbrs[0] < 1: radii[0] /= cbrs[0] #If payload moves slower, accel limit applies to the counterbalance arm, not the payload arm. Disabl ed because this is harder to fix for the coplanar configuration, so t he accel limit now applies ONLY to the payload arm. omegas[0] = RDF(delta_v_from_moon/radii[0]) #Angular rotation rate of the moon sling.

#Moment of inertia of a single arm of the sling including payload, re lative to rotation around the end of the sling with the larger area. #See eq 20 from Puig-Suari et al. 1995: http://web.archive.org/web/20 171006015505/https://engineering.purdue.edu/people/james.m.longuski. 1/JournalArticles/1995/ATetherSlingforLunarandInterplanetaryExplorati on.pdf #Depends on: # l : radius of the sling in meters # v_star : tip velocity divided by the characteristic velocity of the sling material in m/s # pmass : mass (in kg) of everything attached to the end of th e sling, including grapple var('l v_star pmass') assume(l>0,v_star>0,pmass>0) sling_moment(l,v_star,pmass) = pmass*l^2*sqrt(pi)/(2*v_star)*exp(v_st ar^2)*erf(v_star)

#Rotational kinetic energy of sling and payload, from equations 23 an d 24 in Puig-Suari et al. 1995. http://web.archive.org/web/2017100601 5505/https://engineering.purdue.edu/people/james.m.longuski.1/Journal Articles/1995/ATetherSlingforLunarandInterplanetaryExploration.pdf #When rotational energy (including payload mass "pmass") is divided b y the sling's characteristic energy pmass*vlc^2 (where "vlc" is the c haracteristic velocity of the sling material), the resulting ratio eq uals one quarter the sling mass ratio. sling_energy(v_tip,vlc,pmass) = pmass*vlc^2*tether_mass_ratio(v_tip/v lc)/4

for j in range(1,3): #Skip moon sling and moment ballast sling. if cbrs[j] > tolerance: moments_full [j][0] = sling_moment(radii[j],radii[j]*omegas[j]/v_ cs[j][0],tip_masses_full[j][0]) moments_full [j][1] = sling_moment(radii[j]/cbrs[j],radii[j]/cbrs [j]*omegas[j]/v_cs[j][1],tip_masses_full[j][1]) moments_empty[j][0] = moments_full[j][0] - payload_masses[j][0]*r adii[j]^2 moments_empty[j][1] = moments_full[j][1] - payload_masses[j][1]*( radii[j]/cbrs[j])^2 #debug_sling(1) #debug_sling(2) print 'Finished defining moment of inertia and rotational kinetic ene rgy.'

Finished defining moment of inertia and rotational kinetic energy.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 85/173 9/6/2018 capture-orbit-sling

12. Solve for the third capture sling which zeroes the total rotational angular momentum of the capture sling system whether it's full or empty. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 86/173 9/6/2018 capture-orbit-sling

In [17]: if cbrs[1] < tolerance: print 'Skipping this section because capture orbit sling has been disabled.' else: #Moments of inertia for capture slings A and B need to have the cor rect ratio whether they're both full or both empty. That ratio is the inverse of the ratio between their desired final angular rotation ra tes at release. This way, the capture sling A+B system has zero rotat ional angular momentum. That means both slings can be spun up (while full) and spun down (while empty) from/to a stationary state. lj = 61 print 'Capture sling A counterbalance ratio'.ljust(lj),':',cbrs[1] print 'Capture sling B counterbalance ratio'.ljust(lj),':',cbrs[2]

print '\nOmegaB/OmegaA'.ljust(lj+1),':',omegas[2]/omegas[1] print 'moments_fullA/moments_fullB'.ljust(lj),':',(sum(moments_full [1])/sum(moments_full[2])).n() print 'moments_emptyA/moments_emptyB'.ljust(lj),':',(sum(moments_em pty[1])/sum(moments_empty[2])).n()

#Is moment of inertia ballast needed for full capture sling A or B? if sum(moments_full[1])/sum(moments_full[2]) < omegas[2]/omegas[1] - tolerance: C_is_on = 1 ; C_is_not_on = 2 #Attached to capture sling A. elif sum(moments_full[1])/sum(moments_full[2]) > omegas[2]/omegas[1 ] + tolerance: C_is_on = 2 ; C_is_not_on = 1 #Attached to capture sli ng B, not attached to capture sling A. else: C_is_on = -1 ; C_is_not_on = -1 #Capture sling C is disabled. if C_is_on >= 0: print '\n',cap1st(descs[3]),'is attached to',descs[C_is_on] mis[3] = mis[C_is_on] cbrs [3] = cbrs[C_is_on] orig_cbrs [3] = orig_cbrs[C_is_on] radii [3] = radii[C_is_on] omegas [3] = omegas[C_is_on] throw_times[3] = throw_times[C_is_on] theta0s [3] = theta0s[C_is_on] throw_tpers[3][0] = throw_tpers[C_is_on][0] throw_tpers[3][1] = throw_tpers[C_is_on][1] throw_pos [3][0] = throw_pos[C_is_on][0] throw_pos [3][1] = throw_pos[C_is_on][1] throw_vel [3][0] = throw_vel[C_is_on][0] throw_vel [3][1] = throw_vel[C_is_on][1]

ballast_moment_full = omegas[C_is_not_on]/omegas[C_is_on]*sum(mom ents_full[C_is_not_on]) - sum(moments_full[C_is_on]) if sum(moments_empty[1])/sum(moments_empty[2]) < omegas[2]/omegas [1]: if C_is_on != 1: print '!!!WARNING!!! Moment of inertia ballast is needed for empty capture sling A, but based on the full moments C _is_on =',C_is_on else: if C_is_on != 2: print '!!!WARNING!!! Moment of inertia ballast is needed for empty capture sling B, but based on the full moments C _is_on =',C_is_on ballast_moment_empty = omegas[C_is_not_on]/omegas[C_is_on]*sum(mo ments_empty[C_is_not_on]) - sum(moments_empty[C_is_on]) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 87/173 9/6/2018 capture-orbit-sling

print '\nRequired moment of inertia for capture sling C full (kg *m^2)'.ljust(lj+1),':',(ballast_moment_full).n() print 'Required moment of inertia for capture sling C empty (kg*m ^2)'.ljust(lj),':',(ballast_moment_empty).n() payload_masses[3][0] = (ballast_moment_full-ballast_moment_empty) /(radii[3]^2 + cbrs[3]*(radii[3]/cbrs[3])^2) payload_masses[3][1] = payload_masses[3][0]*cbrs[3] print '\nCapture sling C payload mass (tons)'.ljust(lj+1),':',(pa yload_masses[3][0]/1000.0).n() print 'Capture sling C balance mass (tons)'.ljust(lj),':',(payloa d_masses[3][1]/1000.0).n() moment_payload_only = payload_masses[3][0]*radii[3]^2 moment_balance_only = payload_masses[3][1]*(radii[3]/cbrs[3])^2 print 'Moment diff, full - empty (kg*m^2)'.ljust(lj),':',(ballast _moment_full-ballast_moment_empty).n(),', Alt. calc: ',(moment_payloa d_only + moment_balance_only).n(),'\n'

var('mmass1') if cbrs[3] > 1: eq_moment = ballast_moment_full == sling_moment(radii[3],radii[ 3]*omegas[3]/v_cs[3][0],mmass1+payload_masses[3][0]) + sling_moment(r adii[3]/cbrs[3],radii[3]/cbrs[3]*omegas[3]/v_cs[3][1],(mmass1+payload _masses[3][0])*balance_factor(radii[3],omegas[3],v_cs[3][0],sigma,cbr s[3])) soln_moment = solve(eq_moment,mmass1) tip_masses_full [3][0] = soln_moment[0].rhs()+payload_masses[ 3][0].n() #Includes grapples and ballast. tip_masses_full [3][1] = tip_masses_full[3][0]*balance_factor (radii[3],omegas[3],v_cs[3][0],sigma,cbrs[3]) #Includes grapples and ballast. else: eq_moment = ballast_moment_full == sling_moment(radii[3],radii[ 3]*omegas[3]/v_cs[3][0],(mmass1+payload_masses[3][1])*balance_factor( radii[3]/cbrs[3],omegas[3],v_cs[3][0],sigma,1/cbrs[3])) + sling_momen t(radii[3]/cbrs[3],radii[3]/cbrs[3]*omegas[3]/v_cs[3][1],mmass1+paylo ad_masses[3][1]) #Reverse balance_factor: use radius/cbr and 1/cbr in stead. soln_moment = solve(eq_moment,mmass1) tip_masses_full [3][1] = soln_moment[0].rhs()+payload_masses[ 3][1].n() #Includes grapples and ballast. tip_masses_full [3][0] = tip_masses_full[3][1]*balance_factor (radii[3]/cbrs[3],omegas[3],v_cs[3][0],sigma,1/cbrs[3]) #Includes gra pples and ballast. tip_masses_empty [3][0] = tip_masses_full[3][0] - payload_masses [3][0] tip_masses_empty [3][1] = tip_masses_full[3][1] - payload_masses [3][1] #These are the grapple masses for the payloads/balances: grapple_masses [3][0] = payload_masses[3][0]*grapple_fraction* (omegas[3]^2*radii[3]/g) grapple_masses [3][1] = payload_masses[3][0]*grapple_fraction* (omegas[3]^2*radii[3]/g/cbrs[3]) #The ballast (and its grapple) is the difference: ballast_masses [3][0] = tip_masses_empty[3][0] - grapple_masse s[3][0] ballast_masses [3][1] = tip_masses_empty[3][1] - grapple_masse http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 88/173 9/6/2018 capture-orbit-sling s[3][1] #That ballast mass is actually the total mass which includes grap ple mass. Total = (1+f)*bm, so gm = bm*f/(1+f) and bm /= 1+f . #There are three sections in this notebook which make this correc tion. This is #2. grapple_masses [3][0] += ballast_masses[3][0]*grapple_fraction* (omegas[3]^2*radii[3]/g)/(1+grapple_fraction*(omegas[3]^2*radii[3]/g )) ballast_masses [3][0] /= 1+grapple_fraction*(omegas[3]^2*radii[ 3]/g) grapple_masses [3][1] += ballast_masses[3][1]*grapple_fraction* (omegas[3]^2*radii[3]/g/cbrs[3])/(1+grapple_fraction*(omegas[3]^2*rad ii[3]/g/cbrs[3])) ballast_masses [3][1] /= 1+grapple_fraction*(omegas[3]^2*radii[ 3]/g/cbrs[3])

tether_masses [3][0] = tip_masses_full[3][0]*tether_mass_rati o(radii[3]*omegas[3]/v_cs[3][0]) tether_masses [3][1] = tip_masses_full[3][1]*tether_mass_rati o(radii[3]/cbrs[3]*omegas[3]/v_cs[3][1])

j=3 if cbrs[j] > tolerance: moments_full [j][0] = sling_moment(radii[j],radii[j]*omegas[j]/ v_cs[j][0],tip_masses_full[j][0]) moments_full [j][1] = sling_moment(radii[j]/cbrs[j],radii[j]/cb rs[j]*omegas[j]/v_cs[j][1],tip_masses_full[j][1]) moments_empty[j][0] = moments_full[j][0] - payload_masses[j][0] *radii[j]^2 moments_empty[j][1] = moments_full[j][1] - payload_masses[j][1] *(radii[j]/cbrs[j])^2 print '\nActual moment of inertia for capture sling C full (kg*m ^2)'.ljust(lj+1),':',(sum(moments_full[j])).n() print 'Actual moment of inertia for capture sling C empty (kg*m^ 2)'.ljust(lj),':',(sum(moments_empty[j])).n()

#debug_sling(3)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 89/173 9/6/2018 capture-orbit-sling

Capture sling A counterbalance ratio : 0.992 363104788 Capture sling B counterbalance ratio : 0.917 526189989

OmegaB/OmegaA : 0.894 361901311 moments_fullA/moments_fullB : 0.577 420455531257 moments_emptyA/moments_emptyB : 0.496 076469368486

Capture sling C is attached to capture sling A

Required moment of inertia for capture sling C full (kg*m^2) : 1.484 61261711015e13 Required moment of inertia for capture sling C empty (kg*m^2) : 5.857 76794921929e12

Capture sling C payload mass (tons) : 0.455 074003194002 Capture sling C balance mass (tons) : 0.451 598650718035 Moment diff, full - empty (kg*m^2) : 8.988 35822188219e12 , Alt. calc: 8.98835822188219e12

Actual moment of inertia for capture sling C full (kg*m^2) : 1.484 61261711015e13 Actual moment of inertia for capture sling C empty (kg*m^2) : 5.857 76794921932e12

13. Solve for the moon sling payload based on mt4ct and the total mass to be thrown. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 90/173 9/6/2018 capture-orbit-sling

In [18]: #Finally, size the moon sling payload based on the total amount that needs to be sent and the value of mt4ct: if cbrs[1] < tolerance: payload_masses[0][0] = payload_mass payload_masses[0][1] = payload_mass*cbrs[0] else: if abs(mt4ct+1) < tolerance: #If mt4ct == -1, automatically size the moon payload to match the largest of the capture payloads or cap ture counterbalances. This way nothing needs to be subdivided. mt4ct = sum(sum(temp) for temp in payload_masses[1:])/max(map(max , *payload_masses[1:])) #This result follows from this equation: sum (sum(temp) for temp in payload_masses[1:])/mt4ct = max(map(max, *payl oad_masses[1:])) - the left hand side is the sum of all payloads and balances thrown by all the capture orbit slings, the right hand side is the largest payload or balance thrown by any of the capture orbit slings. elif abs(mt4ct+2) < tolerance: #If mt4ct == -2, automatically size the moon payload to match the capture payload, so if the capture cou nterbalance is larger it will have to be subdivided. mt4ct = (sum(sum(temp) for temp in payload_masses[1:]))/payload_m asses[1][0] #This result follows from this equation: (sum(sum(temp) f or temp in payload_masses[1:]))/mt4ct = payload_masses[1][0])) elif mt4ct < tolerance: print '!!!!WARNING!!! mt4ct ("moon throws 4 each capture throw") =',mt4ct payload_masses[0][0] = sum(sum(temp) for temp in payload_masses[1 :])/mt4ct payload_masses[0][1] = payload_masses[0][0]*cbrs[0]

mt4ct = RDF(mt4ct) #Otherwise mt4ct.ceil() throws "AttributeError: 'f loat' object has no attribute 'ceil'"

grapple_masses [0][0] = payload_masses[0][0]*grapple_fraction*(omega s[0]^2*radii[0]/g) grapple_masses [0][1] = payload_masses[0][1]*grapple_fraction*(omega s[0]^2*radii[0]/g/cbrs[0])

tip_masses_empty[0][0] = grapple_masses[0][0] tip_masses_full [0][0] = grapple_masses[0][0] + payload_masses[0][0] tip_masses_empty[0][1] = grapple_masses[0][1] tip_masses_full [0][1] = grapple_masses[0][1] + payload_masses[0][1]

#Only address moon sling part of ballast_choice 1; others are already finished. Couldn't derive the moon ballast mass without first derivi ng the moon sling's radius and omega above. if ballast_choice == 1: #Set all characteristic velocities to defaul t, put extra ballast mass on the tips of the shorter slings. if cbrs[0] > 1: ballast_masses[0][0]=0; ballast_masses[0][1] = tip_ masses_full[0][0]*balance_factor(radii[0],omegas[0],v_cs[0][0],sigma, cbrs[0]) - tip_masses_full[0][1] else: ballast_masses[0][1]=0; ballast_masses[0][0] = tip_ masses_full[0][1]*balance_factor(radii[0]/cbrs[0],omegas[0],v_cs[0][1 ],sigma,1/cbrs[0]) - tip_masses_full[0][0] #Reverse balance_factor: u se radius/cbr and 1/cbr instead.

tip_masses_empty[0][0] += ballast_masses[0][0] tip_masses_full [0][0] += ballast_masses[0][0] http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 91/173 9/6/2018 capture-orbit-sling tip_masses_empty[0][1] += ballast_masses[0][1] tip_masses_full [0][1] += ballast_masses[0][1]

#That ballast mass is actually the total mass which includes grapple mass. Total = (1+f)*bm, so gm = bm*f/(1+f) and bm /= 1+f . #There are three sections in this notebook which make this correctio n. This is #2. grapple_masses [0][0] += ballast_masses[0][0]*grapple_fraction*(omeg as[0]^2*radii[0]/g)/(1+grapple_fraction*(omegas[0]^2*radii[0]/g)) ballast_masses [0][0] /= 1+grapple_fraction*(omegas[0]^2*radii[0]/g) grapple_masses [0][1] += ballast_masses[0][1]*grapple_fraction*(omeg as[0]^2*radii[0]/g/cbrs[0])/(1+grapple_fraction*(omegas[0]^2*radii[0] /g/cbrs[0])) ballast_masses [0][1] /= 1+grapple_fraction*(omegas[0]^2*radii[0]/g/ cbrs[0])

mass_ratio_moon1 = tether_mass_ratio(radii[0]*omegas[0]/v_cs[0][0 ]) tether_masses[0][0] = tip_masses_full[0][0]*mass_ratio_moon1 print 'moon sling rotation period (minutes) =',(2*pi/omegas[0]/60).n () print 'moon sling rotation rate (rpm) =',((2*pi/omegas[0]/60)^( -1)).n() if cbrs[1] > tolerance: print 'mass of sling on moon which tosses payload (+ its capture sl ing counterbalance) directly into inclined capture orbit (tons) =',(t ether_masses[0][0]/1000.0).n() print 'sum of masses of both slings (moon sling and JUST capture sl ing A) WITHOUT COUNTERBALANCE SLINGS (tons) =',((tether_masses[0][0]+ tether_masses[1][0])/1000.0).n() print 'fraction of original mass for direct launch from the moon WI THOUT COUNTERBALANCE SLINGS =',((tether_masses[0][0]+tether_masses[1] [0])/direct_moon_tether_mass).n() else: print 'mass of sling on moon which tosses payload directly into hyperbolic escape trajectory (tons) =',(tether_masses[0][0]/1000.0). n()

mass_ratio_moon2 = tether_mass_ratio(radii[0]*omegas[0]/cbrs[0]/v_ cs[0][1]) tether_masses[0][1] = tip_masses_full[0][1]*mass_ratio_moon2 print '\nmass of moon sling\'s counterbalance arm (tons) =',(tether_m asses[0][1]/1000.0).n() j=0 if cbrs[j] > tolerance: moments_full [j][0] = sling_moment(radii[j],radii[j]*omegas[j]/v_cs [j][0],tip_masses_full[j][0]) moments_full [j][1] = sling_moment(radii[j]/cbrs[j],radii[j]/cbrs[j ]*omegas[j]/v_cs[j][1],tip_masses_full[j][1]) moments_empty[j][0] = moments_full[j][0] - payload_masses[j][0]*rad ii[j]^2 moments_empty[j][1] = moments_full[j][1] - payload_masses[j][1]*(ra dii[j]/cbrs[j])^2

#for j in range(num_slings): debug_sling(j)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 92/173 9/6/2018 capture-orbit-sling

moon sling rotation period (minutes) = 8.63942295815758 moon sling rotation rate (rpm) = 0.115748471262860 mass of sling on moon which tosses payload (+ its capture sling count erbalance) directly into inclined capture orbit (tons) = 1.1252448730 7745 sum of masses of both slings (moon sling and JUST capture sling A) WI THOUT COUNTERBALANCE SLINGS (tons) = 1.48374189654745 fraction of original mass for direct launch from the moon WITHOUT COU NTERBALANCE SLINGS = 0.865919862480859

mass of moon sling's counterbalance arm (tons) = 0.289063829557017

In [19]: #Check that the tip_masses make sense. for j in range(num_slings): if cbrs[j] > tolerance: for k in range(2): #Loop through sling arms. if abs(tip_masses_empty[j][k] - ballast_masses[j][k] - grapple_ masses[j][k]) > tolerance: print warnstring,'tip_masses_empty[',j,'][',k,'] =',tip_masse s_empty[j][k],', but ballast_mass + grapple_mass =',ballast_masses[j] [k] + grapple_masses[j][k] print failure if abs(tip_masses_full[j][k] - ballast_masses[j][k] - grapple_m asses[j][k] - payload_masses[j][k]) > tolerance: print warnstring,'tip_masses_full[',j,'][',k,'] =',tip_masses _full[j][k],', but ballast_mass + grapple_mass + payload_mass =',ball ast_masses[j][k] + grapple_masses[j][k] + payload_masses[j][k] print failure print 'Empty and full tip masses match their ballast, grapple and pay load masses.'

Empty and full tip masses match their ballast, grapple and payload ma sses.

14. Check the centers of mass of all slings while empty and full. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 93/173 9/6/2018 capture-orbit-sling

In [20]: print 'cbrs[0] =',cbrs[0] if cbrs[1] > tolerance: print 'cbrs[1] =',cbrs[1] print 'cbrs[2] =',cbrs[2]

print '\nCheck that equivalent sling diameter at the hub (x = 0) is e qual for both arms of moon and capture slings:' lj = 34 #Left justify descriptions by this number of characters. for j in range(num_slings): if cbrs[j] > tolerance: tip_diams[j][0] = sling_diameter(radii[j],radii[j],omegas[j]*radi i[j],v_cs[j][0],sigma,tip_masses_full[j][0]) hub_diams[j][0] = sling_diameter(0,radii[j],omegas[j]*radii[j],v_ cs[j][0],sigma,tip_masses_full[j][0]) hub_diams[j][1] = sling_diameter(0,radii[j]/cbrs[j],omegas[j]*rad ii[j]/cbrs[j],v_cs[j][1],sigma,tip_masses_full[j][1]) tip_diams[j][1] = sling_diameter(radii[j]/cbrs[j],radii[j]/cbrs[j ],omegas[j]*radii[j]/cbrs[j],v_cs[j][1],sigma,tip_masses_full[j][1])

desc = '\n'+cap1st(descs[j])+'1 tip diameter (mm)' ; print desc.l just(lj+1),':',(tip_diams[j][0]*1000).n() desc = cap1st(descs[j])+'1 hub diameter (mm)' ; print desc.ljust( lj),':',(hub_diams[j][0]*1000).n() if abs(hub_diams[j][0] - hub_diams[j][1]) > tolerance: print '!!! WARNING!!! Equivalent sling diameters aren\'t equal at the',descs[j], 'hub!' desc = cap1st(descs[j])+'2 hub diameter (mm)' ; print desc.ljust( lj),':',(hub_diams[j][1]*1000).n() desc = cap1st(descs[j])+'2 tip diameter (mm)' ; print desc.ljust( lj),':',(tip_diams[j][1]*1000).n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 94/173 9/6/2018 capture-orbit-sling

cbrs[0] = 4.00000000000000 cbrs[1] = 0.992363104788 cbrs[2] = 0.917526189989

Check that equivalent sling diameter at the hub (x = 0) is equal for both arms of moon and capture slings:

Moon sling1 tip diameter (mm) : 3.60080122699790 Moon sling1 hub diameter (mm) : 3.76293935359100 Moon sling2 hub diameter (mm) : 3.76293935359101 Moon sling2 tip diameter (mm) : 3.75259516366609

Capture sling A1 tip diameter (mm) : 1.64362808787441 Capture sling A1 hub diameter (mm) : 1.75476136488611 Capture sling A2 hub diameter (mm) : 1.75476136488611 Capture sling A2 tip diameter (mm) : 1.64196741527596

Capture sling B1 tip diameter (mm) : 1.68101986276946 Capture sling B1 hub diameter (mm) : 1.82429962111474 Capture sling B2 hub diameter (mm) : 1.82429962111474 Capture sling B2 tip diameter (mm) : 1.65538740718731

Capture sling C1 tip diameter (mm) : 1.21771873859756 Capture sling C1 hub diameter (mm) : 1.30005431980189 Capture sling C2 hub diameter (mm) : 1.30005431980189 Capture sling C2 tip diameter (mm) : 1.21648839205096

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 95/173 9/6/2018 capture-orbit-sling

In [21]: #Check the centers of mass of all slings while empty and full.

var('x v_tip vel_c sgma ro') assume(x>0,v_tip>0,vel_c>0,sgma>0,ro>0) # v_c = sqrt((2*sigma)/(safety*rho)) # rho = 2*sigma/(safety*v_c^2) #sling_area(x,l,v_tip,vel_c,sgma,pmass) #print 'tether_mass_ratio*pmass =',tether_mass_ratio*pmass #print 'ind integral =',integral(sling_area(x,l,v_tip,vel_c,sgma, pmass)*2*sgma/(vel_c^2),x) integrated_tether_mass(pmass,v_tip,vel_c) = integrate(sling_area(x,l, v_tip,vel_c,sgma,pmass)*2*sgma/(vel_c^2), x, 0, l) #print 'integrated_tether_mass(pmass,v_tip,vel_c) =',integrated_t ether_mass(pmass,v_tip,vel_c)

print '\nSling moment of inertia =',sling_moment #print 'ind integral =', integral(x^2*sling_area(x,l,v_tip,vel_c, sgma,pmass)*2*sgma/(vel_c^2), x) #print 'def integral =',integrate(x^2*sling_area(x,l,v_tip,vel_c, sgma,pmass)*2*sgma/(vel_c^2), x, 0, l)

#print '\nSling center of mass:' sling_center_of_mass(l,v_tip,vel_c) = integrate(x*sling_area(x,l,v_ti p,vel_c,sgma,pmass)*2*sgma/(vel_c^2)/integrated_tether_mass(pmass,v_t ip,vel_c), x, 0, l).simplify_full() #print 'ind integral with rho/ro replaced via characteristic velocity equation =',integral(x*sling_area(x,l,v_tip,vel_c,sgma,pmass)*2*sgm a/(vel_c^2)/integrated_tether_mass(pmass,v_tip,vel_c),x).simplify_ful l() #print 'ind integral in terms of density rho/ro =',integral(x*sling_a rea(x,l,v_tip,vel_c,sgma,pmass)*ro/integrated_tether_mass(pmass,v_ti p,vel_c),x).simplify_full() #In terms of density "ro" (which doesn't overwrite actual "rho"). #print 'def integral =',integrate(x*sling_area(x,l,v_tip,vel_c,sgma,p mass)*2*sgma/(vel_c^2)/integrated_tether_mass(pmass,v_tip,vel_c), x, 0, l).simplify_full() print '\nSling center of mass =',sling_center_of_mass(l,v_tip,vel_ c)

print '\nTreat \"x\" as positive for sling1 (the payload) and negativ e for sling2 (the counterbalance).' lj = 50 #Left justify descriptions by this number of characters.

def com_for_sling_empty(j): """ Input : Sling index "j", otherwise uses global variables.\n Output: Center of mass for empty sling with index j. """ numerator1 = integrate(x*sling_area(x,radii[j],omegas[j]*radii[j],v _cs[j][0],sigma,tip_masses_full[j][0])*2*sigma/(v_cs[j][0]^2), x, 0, radii[j])+tip_masses_empty[j][0]*radii[j]-integrate(x*sling_area(x,ra dii[j]/cbrs[j],omegas[j]*radii[j]/cbrs[j],v_cs[j][1],sigma,tip_masses _full[j][1])*2*sigma/(v_cs[j][1]^2), x, 0, radii[j]/cbrs[j])-tip_mass es_empty[j][1]*radii[j]/cbrs[j] denominator1 = integrated_tether_mass(tip_masses_full[j][1],radii[j ]/cbrs[j]*omegas[j],v_cs[j][1])+tip_masses_empty[j][1]+integrated_tet http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 96/173 9/6/2018 capture-orbit-sling her_mass(tip_masses_full[j][0],radii[j]*omegas[j],v_cs[j][0])+tip_mas ses_empty[j][0] return numerator1/denominator1

def com_for_sling_full(j): """ Input : Sling index "j", otherwise uses global variables.\n Output: Center of mass for full sling with index j. """ numerator1 = integrate(x*sling_area(x,radii[j],omegas[j]*radii[j],v _cs[j][0],sigma,tip_masses_full[j][0])*2*sigma/(v_cs[j][0]^2), x, 0, radii[j])+tip_masses_full[j][0]*radii[j]-integrate(x*sling_area(x,rad ii[j]/cbrs[j],omegas[j]*radii[j]/cbrs[j],v_cs[j][1],sigma,tip_masses_ full[j][1])*2*sigma/(v_cs[j][1]^2), x, 0, radii[j]/cbrs[j])-tip_masse s_full[j][1]*radii[j]/cbrs[j] denominator1 = integrated_tether_mass(tip_masses_full[j][1],radii[j ]/cbrs[j]*omegas[j],v_cs[j][1])+tip_masses_full[j][1]+integrated_teth er_mass(tip_masses_full[j][0],radii[j]*omegas[j],v_cs[j][0])+tip_mass es_full[j][0] return numerator1/denominator1

for j in range(num_slings): if cbrs[j] > tolerance: coms_empty[j] = com_for_sling_empty(j) if abs(coms_empty[j]) < tolerance: desc = '\nCenter of mass for e mpty '+descs[j]+' (m)' ; print desc.ljust(lj+1),':',(coms_empty[j]).n () else: desc = '\n!!!WARNING!!! Center of mass for empty '+descs[j] +' (m)' ; print desc.ljust(lj),':',(coms_empty[j]).n() coms_full[j] = com_for_sling_full(j) if abs(coms_full[j]) < tolerance: desc = 'Center of mass for full '+descs[j]+' (m)' ; print desc.ljust(lj),':',(coms_full[j]).n() else: desc = '!!!WARNING!!! Center of mass for full '+descs[j]+' (m)' ; print desc.ljust(lj),':',(coms_full[j]).n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 97/173 9/6/2018 capture-orbit-sling

Sling moment of inertia = (l, v_star, pmass) |--> 1/2*sqrt(pi)*l^2*pm ass*erf(v_star)*e^(v_star^2)/v_star

Sling center of mass = (l*vel_c*e^(v_tip^2/vel_c^2) - l*vel_c)*e^ (-v_tip^2/vel_c^2)/(sqrt(pi)*v_tip*erf(v_tip/vel_c))

Treat "x" as positive for sling1 (the payload) and negative for sling 2 (the counterbalance).

Center of mass for empty moon sling (m) : -1.5671807988107 5e-12 Center of mass for full moon sling (m) : 0.00000000000000 0

Center of mass for empty capture sling A (m) : 6.08719719972044 e-12 Center of mass for full capture sling A (m) : 4.63295776665470 e-12

Center of mass for empty capture sling B (m) : -4.7793522780813 9e-12 Center of mass for full capture sling B (m) : -8.5726645897095 9e-12

Center of mass for empty capture sling C (m) : 4.33803930189554 e-12 Center of mass for full capture sling C (m) : 8.44056408431307 e-12

15. Calculate required power and solar panel mass for spinning the slings. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 98/173 9/6/2018 capture-orbit-sling

In [22]: #Calculate required power for spinning moon and capture slings, inclu ding the slings themselves. summary_digits = 5 #Many print statements in this notebook will print "summary_digits" significant figures. lj = 50 #Left justify descriptions by this number of characters.

ms_moments_ratio = sum(moments_empty[0])/sum(moments_full[0]) #This i s needed whether or not the capture sling is disabled.

if cbrs[1] < tolerance: if ms_spinup_time < 0: print warnstring,'If cbrs[1] == 0, ms_spinup_time must be > 0!' print failure spinup_times [0] = ms_spinup_time #The spinup time is standardize d to allow for comparisons where the spinup times are always the sam e. spindown_times [0] = ms_spinup_time*ms_moments_ratio ms_cycle_time = spinup_times[0]+spindown_times[0]+ms_attach_ti me desc = 'The moon sling cycle starts at the moment it throws a paylo ad. It takes '+str((spindown_times[0]/units).n(digits=summary_digits ))+' '+uname+' to spin down and then '+str((ms_attach_time/units).n(d igits=summary_digits))+' '+uname+' to attach a new payload. ' desc += 'It takes '+str((spinup_times[0]/units).n(digits=summary_di gits))+' '+uname+' to spin up, which is the end of the cycle. ' desc += 'The moon sling cycle takes '+str((ms_cycle_time/units).n(d igits=summary_digits))+' '+uname+'.' ms_cycle_description = desc ; print ms_cycle_description+'\n' else: #The "cycle time" is how long each sling takes to go through a comp lete cycle, ending in the same state it started in. This determines t he maximum possible throw rate, disregarding interplanetary launch wi ndows. #The moon sling's cycle starts when it throws a payload to the capt ure orbit. Then it spins down, attaches a new payload, then (possibl y) waits in a retracted configuration, shielded by local mass, until the capture orbit resonance allows for another throw, then spins up and throws. #The capture sling's cycle starts when the moon sling throws its fi rst (or only) payload to the capture orbit. After all moon sling payl oads have been thrown, the capture sling rendezvouses with and attach es the last (or only) payload, then (possibly) waits in a retracted c onfiguration, shielded by the counterbalances, until the capture slin g's time to reach periapsis allows for another throw, then it spins u p in time to throw when it reaches periapsis. Then it spins down and (possibly) waits for the capture orbit resonance to allow for receiv ing another payload. At the same time, the moon sling resets so it's ready to throw again. #Can't loop through the slings because each one is treated differen tly. For instance: if C_is_on >= 0: #If you just looped through the capture slings, th ey'd all incorrectly get different spin down times. cs_moments_ratio_C_is_on = (sum(moments_empty[C_is_on])+sum(m oments_empty[3]))/(sum(moments_full[C_is_on])+sum(moments_full[3])) cs_moments_ratio_C_is_not_on = sum(moments_empty[C_is_not_on])/s um(moments_full[C_is_not_on]) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 99/173 9/6/2018 capture-orbit-sling else: print warnstring,'This code requires the 3rd capture sling to be defined. One way to fix this would be to set C_is_on = 1 and C_is_no t_on = 2 even if the 3rd capture sling isn\'t defined. However, that would likely cause unintended side effects. Be careful.' print failure

if ms_spinup_time < 0: if cs_spinup_time > 0: print warnstring,'If ms_spinup_time is < 0, cs_spinup_time also have to be < 0, and vice versa.' print failure extra_ms_cycles = (abs(ms_spinup_time)).ceil() - 1 #I f spinup_time == -1, no extra cycles. If spinup_time == -2, one extra cycle. Etc. num_ms_cycles = extra_ms_cycles + (ms_attach_time/( period_moon*resn_p)).ceil() #If the attach time is 1.1x the revisit t ime (period_moon*resn_p), need to wait 2 full revisit times. ms_cycle_time = period_moon*resn_p*num_ms_cycles spinup_times [0] = (ms_cycle_time-ms_attach_time)/(1+m s_moments_ratio) spindown_times [0] = spinup_times[0]*ms_moments_ratio wait_times [0] = 0 ; wait1 = 0 #Never any wait time for the moon sling when ms_spinup_time < 0. #First, calculate time starting at the launch of the last moon pa yload. The extra time needed to throw all the previous moon payloads (if there are any) will be accounted for later. #How many complete orbits do the capture slings need to go throug h until it's ready to throw at periapsis? extra_cs_orbits = (abs(cs_spinup_time)).ceil() - 1 #I f spinup_time == -1, no extra cycles. If spinup_time == -2, one extra cycle. Etc. num_cs_orbits = extra_cs_orbits + max([0,RDF(((cs_a ttach_time-time_from_moon_to_capture_periapsis)/period_capture)).ceil ()]) #use max() because if the time difference is negative, zero full orbits are needed before the throw. cs_cycle_time = time_from_moon_to_capture_periapsis + num_cs_orbits*period_capture spinup_times [C_is_on] = cs_cycle_time-cs_attach_time #No ne ed for spin-down yet. spinup_times [C_is_not_on] = spinup_times[C_is_on] #Spin up time s are identical. spindown_times[C_is_on] = spinup_times[C_is_on] *cs_moment s_ratio_C_is_on spindown_times[C_is_not_on] = spinup_times[C_is_not_on]*cs_moment s_ratio_C_is_not_on #How long until the next revisit time with the moon? time_to_next_revisit = time_from_capture_periapsis_to_moon + (resn_s - (num_cs_orbits+1)%resn_s)*period_capture #How many times do the capture slings need to pass up orbital res onance revisits until it's spun down for the next payload from the mo on sling? num_cs_cycles = max([0,RDF(((spindown_times[C_is_on ]-time_to_next_revisit)/(period_capture*resn_s))).ceil()]) cs_cycle_time += time_to_next_revisit + num_cs_cycle s*(period_capture*resn_s) if ms_cycle_time < cs_cycle_time: #If the moon sling is ready to http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 100/173 9/6/2018 capture-orbit-sling throw, just need to account for the extra time needed to throw all t he moon payloads OTHER than the last one: cs_cycle_time += ms_cycle_time*(mt4ct.ceil()-1) #The capture sling's cycle starts when the moon sling throws its first (o r only) payload to the capture orbit. So the time needed to spinup th e moon sling to throw its first payload isn't included in the capture sling cycle time. else: #If the moon sling is NOT ready to throw, just need to acco unt for the time needed to throw ALL the moon payloads: cs_cycle_time = ms_cycle_time*(mt4ct.ceil()) spinup_times [3] = spinup_times[C_is_on] #Capture slin g C is attached to the sling "C_is_on". spindown_times [3] = spindown_times[C_is_on] wait_times [C_is_on] = cs_cycle_time - (spinup_times[C_is_ on] +spindown_times[C_is_on] +cs_attach_time) wait_times [C_is_not_on] = cs_cycle_time - (spinup_times[C_is_ not_on]+spindown_times[C_is_not_on]+cs_attach_time) wait_times [3] = wait_times[C_is_on] else: if cs_spinup_time < 0: print warnstring,'If ms_spinup_time is < 0, cs_spinup_time also have to be < 0, and vice versa.' print failure spinup_times [0] = ms_spinup_time #The spinup time is standardized to allow for comparisons where the spinup times are alw ays the same. spindown_times [0] = ms_spinup_time*ms_moments_ratio spinup_times [C_is_on] = cs_spinup_time #The spinup time is standardized to allow for comparisons where the spinup times are alw ays the same. spinup_times [C_is_not_on] = spinup_times[C_is_on] #Spin up time s are identical. spindown_times[C_is_on] = spinup_times[C_is_on] *cs_moment s_ratio_C_is_on spindown_times[C_is_not_on] = spinup_times[C_is_not_on]*cs_moment s_ratio_C_is_not_on spinup_times [3] = spinup_times[C_is_on] #Capture slin g C is attached to the sling "C_is_on". spindown_times [3] = spindown_times[C_is_on] ms_cycle_time = period_moon*resn_p*RDF((ms_attach_t ime+spinup_times[0]+spindown_times[0])/(period_moon*resn_p)).ceil() # If the spinup time is 1.1x the revisit time (period_moon*resn_p), nee d to wait 2 full revisit times. Ideally, one could then increase the spinup time to exactly 2 revisit times, but this way the spinup time is standardized to allow for comparisons where the spinup times are always the same. wait_times [0] = ms_cycle_time - (ms_attach_time+spi nup_times[0]+spindown_times[0]) #First, calculate time starting at the launch of the last moon pa yload. The extra time needed to throw all the previous moon payloads (if there are any) will be accounted for later. #How many complete orbits do the capture slings need to go throug h until it's ready to throw at periapsis? num_cs_orbits = max([0,RDF(((cs_attach_time+spinup_ times[1]-time_from_moon_to_capture_periapsis)/period_capture)).ceil ()]) #use max() because if the time difference is negative, zero full orbits are needed before the throw. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 101/173 9/6/2018 capture-orbit-sling cs_cycle_time = time_from_moon_to_capture_periapsis + num_cs_orbits*period_capture wait1 = cs_cycle_time - (cs_attach_time+spi nup_times[1]) #This wait time happens prior to the throw, so the slin g can be shielded by the counterbalance masses during this wait time. #How long until the next revisit time with the moon? time_to_next_revisit = time_from_capture_periapsis_to_moon + (resn_s - (num_cs_orbits+1)%resn_s)*period_capture #How many times do the capture slings need to pass up orbital res onance revisits until it's spun down for the next payload from the mo on sling? num_cs_cycles = max([0,RDF(((spindown_times[C_is_on ]-time_to_next_revisit)/(period_capture*resn_s))).ceil()]) cs_cycle_time += time_to_next_revisit + num_cs_cycle s*(period_capture*resn_s) if ms_cycle_time < cs_cycle_time: #If the moon sling is ready to throw, just need to account for the extra time needed to throw all t he moon payloads OTHER than the last one: cs_cycle_time += ms_cycle_time*(mt4ct.ceil()-1) #The capture sling's cycle starts when the moon sling throws its first (o r only) payload to the capture orbit. So the time needed to spinup th e moon sling to throw its first payload isn't included in the capture sling cycle time. else: #If the moon sling is NOT ready to throw, replace all this with the time needed to throw ALL the moon payloads: cs_cycle_time = ms_cycle_time*(mt4ct.ceil()) #The following wait times account for both "wait1" which happens prior to the throw, and the wait which happens after the throw. wait_times [C_is_on] = cs_cycle_time - (spinup_times[C_is_ on] +spindown_times[C_is_on] +cs_attach_time) wait_times [C_is_not_on] = cs_cycle_time - (spinup_times[C_is_ not_on]+spindown_times[C_is_not_on]+cs_attach_time) wait_times [3] = wait_times[C_is_on]

if abs(spindown_times[C_is_not_on] - spindown_times[C_is_on]) > tol erance: print warnstring,'Spindown times for the two main capture slings are not the same!' print failure

print desc = 'The moon sling cycle starts at the moment it throws a paylo ad. It takes '+str((spindown_times[0]/units).n(digits=summary_digits ))+' '+uname+' to spin down and then '+str((ms_attach_time/units).n(d igits=summary_digits))+' '+uname+' to attach a new payload. ' if wait_times[0] > tolerance: desc += 'The moon sling then waits (r etracted and shielded) for '+str((wait_times[0]/units).n(digits=summa ry_digits))+' '+uname+'. ' if ms_spinup_time < 0: desc += 'There\'s no wait time because its spinup time was optimi zed for ' if extra_ms_cycles == 1: desc += 'one extra revisit time. ' elif extra_ms_cycles != 0: desc += str(extra_ms_cycles)+' extra r evisit times. ' else: desc += 'the shortest possible cycle t ime. ' desc += 'It takes '+str((spinup_times[0]/units).n(digits=summary_di http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 102/173 9/6/2018 capture-orbit-sling gits))+' '+uname+' to spin up, which is the end of the cycle. ' desc += 'The moon sling cycle takes '+str((ms_cycle_time/units).n(d igits=summary_digits))+' '+uname+', which is '+str((ms_cycle_time/(pe riod_moon*resn_p)).n(digits=summary_digits))+' resonance revisit time s.' ms_cycle_description = desc ; print ms_cycle_description+'\n'

if mt4ct.ceil() != 1: desc = 'The capture sling cycle starts at the moment the moon sl ing throws its first payload to the capture sling. ' desc += 'Each capture throw requires '+str(mt4ct.ceil())+' moon t hrows. The moon sling cycle starts at the moment it throws the first payload, so',str((ms_cycle_time*(mt4ct.ceil()-1)/units).n(digits=sum mary_digits)),uname,'pass until the moon sling throws its last payloa d to the capture sling. ' desc += 'The capture sling takes '+str((cs_attach_time/units).n(d igits=summary_digits))+' '+uname+' to rendezvous with and attach the last payload. ' else: desc = 'The capture sling cycle starts at the moment the moon sl ing throws its payload to the capture sling. ' desc += 'Each capture throw requires one moon throw. ' desc += 'The capture sling takes '+str((cs_attach_time/units).n(d igits=summary_digits))+' '+uname+' to rendezvous with and attach the payload. ' if cs_spinup_time < 0: desc += 'There\'s no wait time because its spinup time was optimi zed for ' if extra_cs_orbits == 1: desc += 'one extra capture orbit. ' elif extra_cs_orbits != 0: desc += str(extra_cs_orbits)+' extra c apture orbits. ' else: desc += 'the shortest possible cycle t ime. ' else: desc += 'The capture sling then waits (retracted and shielded by the counterbalance masses) for '+str((wait1/units).n(digits=summa ry_digits))+' '+uname+'. ' desc += 'It takes '+str((spinup_times[1]/units).n(digits=summary_di gits))+' '+uname+' to spin up and throw at periapsis, ' desc += 'then '+str((spindown_times[1]/units).n(digits=summary_digi ts))+' '+uname+' to spin down. ' if wait_times[1] > tolerance: desc += 'The capture sling then waits (retracted but largely unshielded) for '+str(((wait_times[1]-wait1)/ units).n(digits=summary_digits))+' '+uname+' until the next revisit t ime after the moon sling finishes spinning up, which is the end of th e cycle. ' desc += 'The capture sling cycle takes '+str((cs_cycle_time/units). n(digits=summary_digits))+' '+uname+', which is '+str((cs_cycle_time/ (period_moon*resn_p)).n(digits=summary_digits))+' resonance revisit t imes.' cs_cycle_description = desc ; print cs_cycle_description+'\n'

print 'Technically, if mt4ct isn\'t an integer, the last throw can use a shorter spin up time because the payload is smaller. However, it would take an extreme case to change the time between throws beca use that\'s usually limited by the moon\'s orbital resonance. Before accounting for this, need to decide if the sling keeps using the ful l-size grapple with the smaller payload, or if a modular grapple desi http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 103/173 9/6/2018 capture-orbit-sling gn allows the grapple to be resized "on the fly" so it\'s the right m ass for the smaller payload.'

for j in range(num_slings): if cbrs[j] > tolerance: avg_powers[j] = 0.5*sum(moments_full[j])*omegas[j]^2/spinup_ times[j] avg_powers_alt[j] = (sling_energy(radii[j]*omegas[j],v_cs[j][0], tip_masses_full[j][0]) + sling_energy(radii[j]/cbrs[j]*omegas[j],v_cs [j][1],tip_masses_full[j][1]))/spinup_times[j] avg_torques[j] = (moments_full[j][0]+moments_full[j][1])*omeg as[j]/spinup_times[j]

desc = '\n'+cap1st(descs[j])+' spinup time ('+uname+')' ; print desc.ljust(lj+1),':',(spinup_times[j]/units).n() desc = cap1st(descs[j])+' spindown time ('+uname+')' ; print desc .ljust(lj),':',(spindown_times[j]/units).n() desc = cap1st(descs[j])+' average power (kW)' ; print desc.ljust( lj),':',(avg_powers[j]/1000).n(),', Alt. calc:',(avg_powers_alt[j]/10 00).n() desc = cap1st(descs[j])+' average torque (N*m)' ; print desc.ljus t(lj),':',(avg_torques[j]).n() desc = cap1st(descs[j])+' const accel spinup revolutions' ; pri nt desc.ljust(lj),':',(0.5*(omegas[j]/spinup_times[j])*spinup_times[j ]^2/(2*pi)).n() #theta = 0.5*a*t^2, where a = angular ac celeration. Revolutions = theta/(2*pi) desc = cap1st(descs[j])+' const accel spindown revolutions' ; pri nt desc.ljust(lj),':',((omegas[j]*spindown_times[j]-0.5*(omegas[j]/sp indown_times[j])*spindown_times[j]^2)/(2*pi)).n() #theta = omega0*t - 0.5*a*t^2, where a = angular acceleration. Revolutions = theta/(2*p i)

if cbrs[1] > tolerance: print '\nTotal required average sling power (kW)'.ljust(lj+1),':',(sum(avg_powers)/1000).n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 104/173 9/6/2018 capture-orbit-sling

The moon sling cycle starts at the moment it throws a payload. It tak es 2.5002 days to spin down and then 1.0000 days to attach a new payl oad. The moon sling then waits (retracted and shielded) for 5.4438 da ys. It takes 10.000 days to spin up, which is the end of the cycle. T he moon sling cycle takes 18.944 days, which is 3.0000 resonance revi sit times.

The capture sling cycle starts at the moment the moon sling throws it s payload to the capture sling. Each capture throw requires one moon throw. The capture sling takes 3.0000 days to rendezvous with and att ach the payload. The capture sling then waits (retracted and shielded by the counterbalance masses) for 0.44843 days. It takes 10.000 days to spin up and throw at periapsis, then 3.1398 days to spin down. The capture sling then waits (retracted but largely unshielded) for 2.355 8 days until the next revisit time after the moon sling finishes spin ning up, which is the end of the cycle. The capture sling cycle takes 18.944 days, which is 3.0000 resonance revisit times.

Technically, if mt4ct isn't an integer, the last throw can use a shor ter spin up time because the payload is smaller. However, it would ta ke an extreme case to change the time between throws because that's u sually limited by the moon's orbital resonance. Before accounting for this, need to decide if the sling keeps using the full-size grapple w ith the smaller payload, or if a modular grapple design allows the gr apple to be resized "on the fly" so it's the right mass for the small er payload.

Moon sling spinup time (days) : 10.0000000000000 Moon sling spindown time (days) : 2.50019786600602 Moon sling average power (kW) : 3.04301344198121 , Alt. calc: 3.04301344198121 Moon sling average torque (N*m) : 502099.726314180 Moon sling const accel spinup revolutions : 833.388993092595 Moon sling const accel spindown revolutions : 208.363738208302

Capture sling A spinup time (days) : 10.0000000000000 Capture sling A spindown time (days) : 3.13981188825139 Capture sling A average power (kW) : 1.54810821222550 , Alt. calc: 1.54810821222550 Capture sling A average torque (N*m) : 311330.681650522 Capture sling A const accel spinup revolutions : 683.775075323667 Capture sling A const accel spindown revolutions : 214.692511039124

Capture sling B spinup time (days) : 10.0000000000000 Capture sling B spindown time (days) : 3.13981188825138 Capture sling B average power (kW) : 2.14454779902594 , Alt. calc: 2.14454779902594 Capture sling B average torque (N*m) : 482217.589817167 Capture sling B const accel spinup revolutions : 611.542376435553 Capture sling B const accel spindown revolutions : 192.012802370185

Capture sling C spinup time (days) : 10.0000000000000 Capture sling C spindown time (days) : 3.13981188825139 Capture sling C average power (kW) : 0.84974415143437 3 , Alt. calc: 0.849744151434373 Capture sling C average torque (N*m) : 170886.908166645 Capture sling C const accel spinup revolutions : 683.775075323667 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 105/173 9/6/2018 capture-orbit-sling Capture sling C const accel spindown revolutions : 214.692511039124

Total required average sling power (kW) : 7.58541360466702

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 106/173 9/6/2018 capture-orbit-sling

In [23]: #Estimate required solar panel area at the origin planet's aphelion, assuming 100% efficient solar panels.

#Estimate required solar panel mass, using Juno's solar panels and as suming that Jupiter is at perihelion and the origin planet is at aphe lion. # http://web.archive.org/web/20160813171308/https://www.edn.com/Pdf/V iewPdf?contentItemId=4442465 juno_jupiter_power = 420 #Juno solar panel power in watts after radia tion degradation: https://en.wikipedia.org/wiki/Juno_(spacecraft)#Sol ar_panels juno_pl_power = juno_jupiter_power*(a_jupiter*(1-ecc_jupiter)/a_ pl*(1+ecc_pl))^2 juno_panel_mass = 340 #Juno solar panel mass in kg, from https://e n.wikipedia.org/wiki/Juno_(spacecraft)#Solar_panels juno_panel_area = 3*(2.7*8.9) #in m^2, from https://en.wikipedia.o rg/wiki/Juno_(spacecraft)#Solar_panels

lj = 55 #Left justify descriptions by this number of characters. for j in range(num_slings): if cbrs[j] > tolerance: desc = '\n'+cap1st(descs[j])+' solar panel area, 100% efficiency (m^2)' ; print desc.ljust(lj+1),':',(avg_powers[j]/(solar_constant*( a_earth/a_pl*(1+ecc_pl))^2)).n() junopanel_masses[j] = (avg_powers[j]/juno_pl_power)*juno_panel_ma ss desc = cap1st(descs[j])+' solar panel mass via Juno (tons)' ; pri nt desc.ljust(lj),':',(junopanel_masses[j]/1000.0).n()

if estimate_misc == 0: radiator_masses = [0.0 for i in range(num_slings)] motor_masses = [0.0 for i in range(num_slings)] hub_masses = [0.0 for i in range(num_slings)] estimate_misc_str = '' else: #Extremely crude stand ins for estimates of various other masses. T hese only exist as a reminder to calculate them correctly! #Use deepcopy unless you want to copy by reference so changes to th e copied list also change the original list. radiator_masses = copy.deepcopy(junopanel_masses) motor_masses = copy.deepcopy(junopanel_masses) hub_masses = copy.deepcopy(junopanel_masses) estimate_misc_str = ' = solar panel mass '

# Hoyt 2000: DESIGN AND SIMULATION OF A TETHER BOOST FACILITY FOR LEO -> GTO TRANSPORT http://web.archive.org/web/20171118235227/http://ww w.tethers.com/papers/HOYT_MMOSTT_Final.pdf #see p 65 of PDF, numbered page 6 of article (regarding a tether in E arth orbit): #"In order for the tether facility to reboost its orbit within 30 day s, the facility will require a solar power generation capability of 1 00 kW. Because the facility will pass through the radiation belts fre quently, its solar power system will utilize a concentrator-type sola r panel design, such as the Scarlet design, with 150 mil Aluminum bac kside and 100 mil glass cover slides to shield the arrays from the be lt particles. In order for the solar array to produce the desired pow http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 107/173 9/6/2018 capture-orbit-sling er levels after 10 years of operation, they system will be deployed w ith 137 kW of initial power generation capability. Using Scarlet-type panel technology, this solar array would mass approximately 1,370 k g. The tether facility will collect this solar power during the rough ly 80% of its orbit that it is in the sunlight, and store it in a bat tery system. Then, during perigee pass, it will drive the electrodyna mic tether at an average power level of 300 kW (modulated as to be de scribed later). In order to provide a maximum battery depth-of-discha rge of 30%, the control station will have a battery system with 5,700 A•hr of capacity (120 V power system). Using advanced Li ion batteri es, this will require approximately 4,600 kg of batteries. The contro l system will also require the capability to transform the 120 V batt ery voltage up to the 20+kV needed to drive tether currents on the or der of 15 A."

#see p305 of PDF, numbered page N-5 of article (regarding a tether in Earth orbit): #"In order for the tether facility to reboost its orbit within 30 day s, the facility will require a solar power generation capability of 5.5 kW. Because the facility will pass through the radiation belts f requently, its solar power system will utilize a concentrator-type so lar panel design, such as the Scarlet design, with 150 mil Aluminum b ackside and 100 mil glass cover slides to shield the arrays from the belt particles. In order for the solar array to produce the desired power levels after 10 years of operation, they system will be deploy ed with 7.5 kW of initial power generation capability. Using Scarlet- type panel technology, this solar array would mass approximately 75 k g. The tether facility will collect this solar power during the rough ly 80% of its orbit that it is in the sunlight, and store it in a bat tery system. Then, during perigee pass, it will drive the electrodyna mic tether at an average power level of 300 kW (modulated as to be de scribed later). In order to provide a maximum battery depth-of-discha rge of 30%, the control station will have a battery system with 315 A •hr of capacity (120 V power system). Using advanced Li ion batterie s, this will require approximately 255 kg of batteries."

#Gravity gradient forces increase the capture sling mass over the Mor avec free space tether equation, but that calculation won't be done h ere. #Instead, use the estimate from Hoyt et al. 1999: http://web.archive. org/web/20171118235227/http://www.tethers.com/papers/HOYT_MMOSTT_Fina l.pdf #That article (IAF-99-A.5.10) begins on page 240 of the PDF. Its titl e is RAPID INTERPLANETARY TETHER TRANSPORT SYSTEMS by Hoyt, Forward, Nordley and Uphoff. #Alternative URL for just that one paper: http://web.archive.org/web/ 20161011035046/http://www.tethers.com/papers/InterplanetaryTetherTrns prt.pdf #Turn to page 266 of the first PDF or page 28 of the second PDF (in b oth cases, that's page 27 of the article). #One arm of the Marswhip has a mass of 4609 kg before gravity gradien ts are considered, and 4750 kg after. #That arm of the Marswhip is 427 km long and the Marswhip center of m ass has a periapsis radius of 4025 km. #So this is unlikely to be a severe underestimate of the gravity grad ient effects on the capture orbit sling. Marswhip_gradient_factor = 4750.0/4609.0 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 108/173 9/6/2018 capture-orbit-sling print '\nMarswhip_gradient_factor'.ljust(lj+1),':',Marswhip_gradient_ factor print '!!!!NOTE: This added mass ISN\'T included in calculations for solar panel area, torque, center of mass, or moment of inertia!!' Moon sling solar panel area, 100% efficiency (m^2) : 4.341841221 14847 Moon sling solar panel mass via Juno (tons) : 0.195238608 442057

Capture sling A solar panel area, 100% efficiency (m^2) : 2.208876227 06750 Capture sling A solar panel mass via Juno (tons) : 0.099326045 9854694

Capture sling B solar panel area, 100% efficiency (m^2) : 3.059889879 57666 Capture sling B solar panel mass via Juno (tons) : 0.137593387 608140

Capture sling C solar panel area, 100% efficiency (m^2) : 1.212434402 43416 Capture sling C solar panel mass via Juno (tons) : 0.054519268 0942643

Marswhip_gradient_factor : 1.030592319 37514 !!!!NOTE: This added mass ISN'T included in calculations for solar pa nel area, torque, center of mass, or moment of inertia!!

16. Animate the capture sling throw. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 109/173 9/6/2018 capture-orbit-sling

In [97]: notif = 10 #Only print a notification if the frame number is a multip le of "notif" to avoid printing too many. Also used elsewhere. if cbrs[1] < tolerance: print 'Skipping this section because the capt ure sling has been disabled.' else: print capture_frame_string

def tach(vec): """ Input : Vector in the Sage right hand coordinate system.\n Output: Tuple in the Tachyon left hand coordinate system. """ #NOTE: P = P.transform(scale=[-1,1,1]) # Tachyon does not have a right-hand-rule coordinate system # https://ask.sagemath.org/questi on/7586/the-tachyon-object-used-for-rendering-plots/ #FROM TACHYON DOCS: Note that the coordinates are by default such that z is up, positive y is to the {left} and x is toward you. This is not oriented according to the right hand rule. vec[0] = -vec[0] vec = rotateX(-pi/2)*vec #Since I can't seem to rotate the Mar s texture in Tachyon to match its actual orientation, might as well r otate that polar ice cap out of the way... return tuple(vec)

initial_verbose=get_verbose() set_verbose(0)

#These values are used to automatically place the camera for differ ent capture slings. Also, "longest_capture_arm" is used elsewhere. var('temp1 temp2 temp3') cbradii = [temp1/temp2 for temp1,temp2 in zip(radii[1:3],cbrs[1:3 ])] #Make a list of the capture sling counterbalance radii. Ignore mo on sling and capture sling C. longest_capture_arm = max(radii+cbradii) if periapsis_capture-longest_capture_arm-r_pl < target_alt: longest _arm_string = warnstring #Also used elsewhere. else: longest_arm_string = '' longest_arm_string += 'The longest capture sling arm is '+str((long est_capture_arm/1000.0).n(digits=5))+' km long, so it can reach down to an altitude of '+str(((periapsis_capture-longest_capture_arm-r_pl )/1000.0).n(digits=5))+' km.' print longest_arm_string original_arm = 135150 #The camera height values were originally tun ed for a longest capture arm of 135.15 km. capture_payload_speeds = [temp1*temp2 for temp1,temp2 in zip(radii[ 1:3],omegas[1:3])] #Make a list of the capture sling payload tip spee ds. Ignore moon sling and capture sling C. capture_balance_speeds = [temp1*temp2/temp3 for temp1,temp2,temp3 i n zip(radii[1:3],omegas[1:3],cbrs[1:3])] #Make a list of the capture sling payload tip speeds. Ignore moon sling and capture sling C. highest_capture_payload_speed = max(capture_payload_speeds) #The co unterbalance tip speeds don't really affect the animation, so only co nsider the payload tip speeds. highest_capture_tip_speed = max(capture_payload_speeds+capture_ balance_speeds) print 'Highest capture sling payload tip speed is',highest_capture_ http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 110/173 9/6/2018 capture-orbit-sling payload_speed.n(digits=5),'m/s.' if abs(highest_capture_tip_speed-highest_capture_payload_speed) > t olerance: print 'Highest capture sling tip speed is ',highest_ capture_tip_speed.n(digits=5),'m/s.' original_capture_payload_speed = 1102.9 #The camera zoomout speed w as originally tuned for this payload tip speed.

#Based on example code ( https://gist.githubusercontent.com/william stein/7386062/raw/91eaa632c358a382c3c4c69f1d9de95668302643/gistfile1. txt http://archive.li/DkvSw ) used here in a video by William Stein: https://www.youtube.com/watch?v=lhirRHCW1q0 #Animation posted by Andrey Novoseltsev. https://groups.google.com/ forum/#!topic/sage-notebook/ovxWawqLld0 http://archive.li/HNSZE print warnstring,'FIX orbit lines (which means moving calc_payload_ pos() back up to this cell)!!!! ALSO: CONSIDER AN OPTION THAT FOLLOWS COUNTERBALANCE B AS IT AEROBRAKES OR LIFTS A PACKAGE FROM MARS. WAIT - COUNTERBALANCE B ROTATES IN THE WRONG DIRECTION TO PICK UP A PAYLOA D!' aspectr = 16/9 xresn = 1280 ; yresn = xresn//aspectr fps = 30 #frames per second for the output animation. filesize = 100 #Desired animation file size, in megabytes. V ideos > 15MB fail in CoCalc and videos > 20MB can't be easily emaile d. numframes = -60 #Total number of frames in the output animati on. If negative, that's the target speedup. Speedup is simulation tim espan divided by animation length. #Simulation timespan suggestions for different viewtypes: #( 0) Side view : #( 1) Overhead view going up : -1000 to 1000 #(100) Payload view : -2200 to 1200 start_time = -1000 #Time at the start of the animation, relati ve to the time at which the capture slings reach periapsis. end_time = -start_time #Time at the end of the animation, re lative to the time at which the capture slings reach periapsis. if numframes < 0: numframes = ((end_time-start_time)*fps/abs(numfra mes)).ceil() #Because abs(numframes) is interpreted as the target spe edup, where sim_time = end_time-start_time, and speedup = sim_time/vi d_time, and fps = numframes/vid_time . So: speedup = sim_time*fps/num frames atmo_layers = 0#40 #Set atmo_layers to 0 to turn off the atmosp here for faster rendering, or any positive integer for that number of atmo layers. !!!WARNING!!! THIS ALSO INCREASES THE RAY DEPTH, SO IT DRAMATICALLY SLOWS DOWN THE RENDERING! orbit_lines = 0#10 #Number of lines used to display the largest orbit of the payloads and counterbalances. 0=disable disable_sling_B = 0 #Set to 1 to disable rendering of capture sling B. #Viewtype: #0 = Side view. #1 = Overhead view directly onto the ecliptic/rotation al plane. #2 = Simple overhead view where the camera is always d irectly above the hub. #10 = HUB AND HAWSEPIPE DON'T SHOW UP HERE! View from a nother orbit. Sling overtakes camera. #100 = Payload view - default rotation after throw - cho http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 111/173 9/6/2018 capture-orbit-sling ose payload/balance by setting pc_j and pc_k below. #150 = 3rd persion payload view - default rotation after throw - choose payload/balance by setting pc_j and pc_k below. viewtype = 1 goingup = 1 #For viewtype 1. Set to 1 to watch the sling move up the screen or to 0 to watch the sling move to the right. if goingup: #For viewtype 1. Watch sling move up the scre en. Need to zoom out quickly after periapsis to keep payloads/balance s in view. cam_const1 = 0.2 #For viewtype 1. Controls the height of the c amera above the ecliptic plane while at periapsis. cam_const2 = 2.3 #For viewtype 1. Exponent that controls how q uickly height of the camera above the ecliptic plane increases while moving to/from periapsis. else: #For viewtype 1. Watch the sling move to the right. Need to zoom out while at periapsis to keep shadows in view. cam_const1 = 0.7 #For viewtype 1. Same as cam_const1 above. cam_const2 = 0.9 #For viewtype 1. Same as cam_const2 above. pc_j = 1; pc_k = 0 #For viewtypes > 100. Payload camera sling an d arm indices. pc_j=1/2 means sling A/B. pc_k=0/1 means payload/balan ce. if longest_capture_arm > original_arm: cam_ const1 *= (longest_capture_arm/original_arm)^(0.7) #Raise camera heig ht at periapsis more for slings with longer arms. if highest_capture_tip_speed > original_capture_payload_speed: cam_ const2 *= (highest_capture_tip_speed/original_capture_payload_speed)^ (0.5) #Zoom camera out faster for slings with higher maximum payload tip speeds. if viewtype==1: exagg = 9e3 else: exagg = 4e3 if longest_capture_arm > original_arm: exagg *= (longest_capture_ar m/original_arm)^(0.6) #Exaggerate sizes more for slings with longer a rms. #exagg = 1.0 #Just in case you want to see what it looks l ike with a realistic scale. exagg_warning = warnstring+'Size of lights and payloads/balances, h ub, solar panels, radiators is exaggerated by a factor of '+str(exagg .n(digits=2))+' for visibility.' if exagg > 1+tolerance: print exagg_warning light_rad = 0.1*exagg #Size of lights (their radii, in meter s) along tether. payload_width = 6*light_rad light_spacing = max([10000,light_rad*2]) #Try to space the lights using actual meters, but use light_rad*2 if it's bigger. hub_rad = 3*light_rad #Radius of the hub. hub_length = 20*light_rad #Distance along the hub's rotation a xis from the centers of slings A and B. If set to 2*light_rad, the li ghts touch. solar_width = 25*light_rad #Width of solar panels. radiator_rad = 0.7*solar_width #Radiator radius. light1_color = (0,1,0) #Color of lights on capture sling A. light2_color = (1,0,0) #Color of lights on capture sling B. pc_opacity = 0.0 #If using the payload cam, lights on that sli ng arm use this opacity until the throw, so the payload cam can see. pc_ambient = 0.02 payload0_color = (1.0,1.0,1.0) #Color of payloads. payload1_color = (1.0,1.0,0.0) #Color of counterbalances. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 112/173 9/6/2018 capture-orbit-sling grapple_color = (0.0,0.5,1.0) #Color of grapples. Used to be oran ge: (1.0,0.4,0.0) hub_color = (0.6,0.6,0.6) #Color of hub. radiator_color = (0.7,0.7,0.7) #Color of radiators. solar_color = (0.0,0.0,0.7) #Color of solar panels. solar_ambient = 0.0 #Ambient lighting coefficient for solar panel s. solar_diffuse = 0.9 #Diffuse shading coefficient for solar panel s. solar_specular = 0.1 #Specular shading coefficient for solar panel s. hub_ambient = 0.6 #Ambient lighting coefficient for hub texture s (hub, radiators). hub_diffuse = 0.4 #Diffuse shading coefficient for hub texture s (hub, radiators). payload_ambient = 0.2 #Ambient lighting coefficient for payload and balance textures. payload_diffuse = 0.8 #Diffuse shading coefficient for payload and balance textures. stars_ambient = 0.4 #Controls the ambient brightness of the stars texture, if the starsfile exists. atmo_opacity = 0.001 #These control the appearance of the atmosp here, if atmo_layers > 0. atmo_ambient = 0.0 atmo_diffuse = 0.005 backup_mars = (0.6,0.3,0.2) #Only used to texture Mars if the M ars file isn't found. tiny = 10*tolerance #Apparently two triangles can't share the same vertices, so offset them by a tiny amount for one triangle.

retrograde_anchor = 1 #"1/0" Enables/disables the retrograde anc hor. hawsepipe_multiplier = 3 #This number multiplies the "offset" (calc ulated below) that determines the length of the hub. hawsepipe_coils = 20 #Number of coils in the hawsepipe. Needs to be even. hawsepipe_gap = 0.7 #Fraction of hawsepipe that is open. hawsepipe_supports = 8 #Number of axial supports running along th e length of the hawsepipe. support_width = 10*pi/180 #Width of axial supports running a long the length of the hawsepipe, in radians. anchor_color = (0.0,1.0,1.0) #Color of anchor links. anchor_spread = 2000.0 #The anchor has links extending out t o "spread" time since periapsis. anchor_links = 50 #EVEN number of deployed anchor links. if retrograde_anchor and trajopt < 100: print warnstring,'Disabling retrograde anchor because its geometr y in the perpendicular setup (i.e. trajopt < 100) is much more compli cated.' retrograde_anchor = 0 if retrograde_anchor: if anchor_links % 2 != 0: anchor_links += 1 print warnstring,'The requested anchor_links number was odd so it was increased to',str(anchor_links)+'.' if hawsepipe_coils % 2 != 0: hawsepipe_coils += 1 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 113/173 9/6/2018 capture-orbit-sling print warnstring,'The requested hawsepipe_coils number was odd so it was increased to',str(hawsepipe_coils)+'.' kepler_anchor = copy.deepcopy(kepler_com_cs) #Use deepcopy unless you want to copy by reference so changes to the copied list also cha nge the original list. kepler_anchor[2] = pi #Change inclination to retrograde. hub_stub = hub_length - 4*hub_rad if hub_stub < 2*light_rad: print warnstring,'hub_stub is',hub_stub,', but it needs to be > = light_rad which is',light_rad print failure

if viewtype in [0,10,11]: #No need for a bright hub because we're i n close. hub_ambient = 0.01 hub_diffuse = 0.99 if numframes/notif > 60: notif = (numframes/60).ceil() #Only print a notification if the frame number is a multiple of "notif" to avoid printing too many. Also used elsewhere.

#Size the payloads and counterbalances according to their masses. combined_payload_masses = payload_masses[:num_slings-1] #Don't need deepcopy because we're taking a subset. combined_payload_masses[C_is_on][0] += payload_masses[3][0] combined_payload_masses[C_is_on][1] += payload_masses[3][1] smallest_cs_mass = (min(map(min, *combined_payload_masses[1:]))).n () #Smallest capture sling mass ignores moon sling. payload_density = smallest_cs_mass/(payload_width)^3 #This density makes the least massive payload have the standard payload_width. combined_payload_widths = [[(y/payload_density)^(1/3) for y in x] f or x in combined_payload_masses] #Also size the grapples according to their empty tip masses and the same payload_density as above. combined_grapple_masses = tip_masses_empty[:num_slings-1] #Don't ne ed deepcopy because we're taking a subset. combined_grapple_masses[C_is_on][0] += tip_masses_empty[3][0] combined_grapple_masses[C_is_on][1] += tip_masses_empty[3][1] combined_grapple_heights = copy.deepcopy(combined_grapple_masses) # Use deepcopy unless you want to copy by reference so changes to the c opied list also change the original list. for j in range(1,3): #This ignores the moon sling grapples, but the y're not used in the animation anyway. for k in range(2): #Loop through capture orbit sling arms. combined_grapple_heights[j][k] = 3*(combined_grapple_masses[j][ k]/payload_density)/combined_payload_widths[j][k]^2 #Square pyramid v olume = 1/3*width^2*height: http://mathworld.wolfram.com/SquarePyrami d.html

mars_pos = nullvec sun_pos = a_pl*com_cs_pos0/com_cs_pos0.norm() #Sunlight RGB values from: http://www.vendian.org/mncharity/dir3/st arcolor/sun.html http://archive.li/0jCa2 #http://web.archive.org/web/20171206190251/http://casa.colorado.ed u/~ajsh/colour/Tspectrum.html #https://www.rapidtables.com/convert/color/hex-to-rgb.html #sunlight=(255/255,241/255,237/255) # "#fff1ed - the site, CIE 1931 CMFs, and Rec.709." http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 114/173 9/6/2018 capture-orbit-sling sunlight=(255/255,242/255,239/255) # "#fff2ef - the same chromatici ty, but converted to pixel color using sRGB instead." #sunlight=(255/255,243/255,234/255) # "#fff3ea - my numbers (for co mparison)"

anim_file = 'slings.mp4' #Use a ".gif" extension to play on Androi d. Use ".mp4" or ".ogg" for desktop computers. os.system('rm *.'+anim_file[-3:]) #remove the old animation os.system('rm anim*.png') #remove the old frames filename_pattern = 'anim%04d.png' #Converted textures to PPM format using ImageMagick in a terminal: "convert name.dds name.ppm" starsfile = 'starmap_8k.ppm' #Deep star maps from https://svs.gsfc. nasa.gov/3895 marsfile = 'Mars_2k-050104.ppm' #Mars surface textures from htt p://www.celestiamotherlode.net/catalog/mars.php http://archive.li/rT qZj if os.path.isfile(starsfile): stars_here = 1 else: backup_starsfile = 'starmap_4k.ppm' if os.path.isfile(backup_starsfile): stars_here = 1 print warnstring,'Stars texture file',starsfile,'wasn\'t found, so the backup file',backup_starsfile,'was used.' starsfile = backup_starsfile else: stars_here = 0 print warnstring,'Stars texture files',starsfile,'and',backup_s tarsfile,'weren\'t found, so the sky is textured black.' if os.path.isfile(marsfile): mars_here = 1 else: mars_here = 0 print warnstring,' Mars texture file',marsfile,'wasn\'t found, so Mars is textured reddish-brown.' mars_string = marsfile+' center 0.0 0.0 0.0 rotate 0.0 0.0 0.0 scale 1.0 1.0 1.0' #Really wish these rotations worked... offset = hub_length/2*cs_axis #Disable solar panels for the perpendicular setup (i.e. trajopt < 1 00) because in that case the solar panel geometry is much more compli cated. Even the radiators will be facing the Sun in the perpendicular setup, but keep drawing them because otherwise it's hard to see the hub from a viewpoint along the capture sling axis cs_axis if trajopt >= 100: #Solar panels are oriented along the capture sling axis, and the other vector that's also perpendicular to the sun_pos vector. Note: this is only the "direction to the Sun" if the capture sling center of mass is at (0,0,0) which is the center of Mars here. This fact is ignored because otherwise this other solar panel axis would have to be recalculated in every frame for very little benefit. other_solar_axis = cs_axis.cross_product(sun_pos/sun_pos.norm()) other_solar_axis /= other_solar_axis.norm()

periapsis_clearance = periapsis_capture-r_pl-atmo_alt timespan = RDF(end_time-start_time) #Sling time numframes_REAL = RDF(numframes) anim_length = numframes_REAL/RDF(fps) #Length of the animation, i n seconds. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 115/173 9/6/2018 capture-orbit-sling speedup = timespan/anim_length bitrate = floor(filesize*8*2^20/anim_length) #Bitrate in bits per s econd print anim_file,', resolution:',xresn,'x',yresn,', numframes:',numf rames,', fps:',fps,', simulation timespan:',(timespan/60.0).n(digits= 2),'minutes, video length:',(anim_length).n(digits=2),'seconds, speed up:',(speedup).n(digits=2),', desired filesize:',(filesize).n(digits= 2),'MB, bitrate:',bitrate,'bits/second, viewtype:',viewtype,', atmo_l ayers:',atmo_layers,', orbit_lines:',orbit_lines

if viewtype == 10: #View from another orbit. Sling overtakes camer a. camera_zrot = 0.5*pi/180 camera_pos0 = rotateZ(camera_zrot)*1.005*com_cs_pos0 camera_vel0 = rotateY(0.5*pi/180)*rotateZ(camera_zrot)*0.95*com_c s_vel0 kepler_cam = xyz2kepler(camera_pos0,camera_vel0)

#if orbit_lines > 0: payload_pos = calc_payload_pos(orbit_lines,[1, 2]) #Calculate "orbit_lines" payload/balance positions for capture sl ings, which have indices 1 and 2.

def draw_rectangle(t,rect_texture_string,rect_center,rect_side1,rec t_side2): """ Input : Tachyon scene t, texture string, a position vector of rec tangle's center, two direction vectors of the edges connected to the lower left corner (pointing "right" and then "up").\n Output: Tachyon scene t with an added rectangle made out of trian gles displaced a "tiny" distance from each other to avoid problems in Tachyon where two triangles share the same vertices. """ side1_n = rect_side1/rect_side1.norm() #Points "right". side2_n = rect_side2/rect_side2.norm() #Points "up". rectA1 = rect_center - rect_side1/2 - rect_side2/2 #Rectangle co rners starting at lower left, going clockwise. rectA2 = rectA1 + rect_side2 rectA3 = rectA2 + rect_side1 rectA4 = rectA3 - rect_side2 t.triangle(tach(rectA1-tiny*side1_n),tach(rectA2),tach(rectA3-tin y*side1_n),rect_texture_string) #upper left triangle. t.triangle(tach(rectA1),tach(rectA3),tach(rectA4),rect_texture_st ring) #lower right triangle. return t

def draw_box(t,box_texture_string,box_center,box_side1,box_side2,si de3_length): """ Input : Tachyon scene t, texture string, a position vector of bo x's center, two direction vectors of the edges connected to the top s ide's left near corner (pointing "right" and then "into the screen"). The vector pointing "up" is calculated as the cross product of the "right" and "into the screen" vectors, then its magnitude is scaled to side3_length for the last box dimension.\n Output: Tachyon scene t with an added box made out of rectangles (made out of triangles) displaced a "tiny" distance from each other to avoid problems in Tachyon where two triangles share the same vert http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 116/173 9/6/2018 capture-orbit-sling ices. """ side1_n = box_side1/box_side1.norm() #Points "right". side2_n = box_side2/box_side2.norm() #Points "into the scr een". side3_n = side1_n.cross_product(side2_n) #Points "up". box_side3 = side3_length*side3_n #Top and bottom sides: t = draw_rectangle(t,box_texture_string,box_center + box_side3*( 0.5 + tiny),box_side1,box_side2) t = draw_rectangle(t,box_texture_string,box_center - box_side3*( 0.5 + tiny),box_side1,box_side2) #Front (i.e. "near") and back (i.e. "into the screen") sides: t = draw_rectangle(t,box_texture_string,box_center - box_side2*( 0.5 + tiny),box_side1,box_side3) t = draw_rectangle(t,box_texture_string,box_center + box_side2*( 0.5 + tiny),box_side1,box_side3) #Left and right sides: t = draw_rectangle(t,box_texture_string,box_center - box_side1*( 0.5 + tiny),box_side2,box_side3) t = draw_rectangle(t,box_texture_string,box_center + box_side1*( 0.5 + tiny),box_side2,box_side3) return t

def draw_pyramid(t,pyramid_texture_string,pyramid_center,pyramid_si de1,pyramid_side2,pyramid_height): """ Input : Tachyon scene t, texture string, a position vector of pyr amid's center, two direction vectors of the edges connected to the py ramid base's left near corner (pointing "right" and then "into the sc reen"). The vector pointing "up" is calculated as the cross product o f the "right" and "into the screen" vectors, then its magnitude is sc aled using pyramid_height.\n Output: Tachyon scene t with an added pyramid made out of rectang les (made out of triangles) displaced a "tiny" distance from each oth er to avoid problems in Tachyon where two triangles share the same ve rtices. """ #In retrospect, it probably would have made things easier if the "up" vector were specified, not derived. Instead, maybe the "right" vector could be derived. sides_n = [pyramid_side1/pyramid_side1.norm()] #Points "right". sides_n += [pyramid_side2/pyramid_side2.norm()] #Points "into the screen". sides_n += [sides_n[0].cross_product(sides_n[1])] #Points "up". pyramid_side3 = pyramid_height*sides_n[2] #Base: t = draw_rectangle(t,pyramid_texture_string,pyramid_center - pyra mid_side3*(0.5 + tiny),pyramid_side1,pyramid_side2) #Sides: apex = pyramid_center + 0.5*pyramid_side3 base_vertices = [pyramid_center - 0.5*pyramid_side3 - pyramid_ side1/2 - pyramid_side2/2] #Base vertices starting at near left, goin g clockwise. base_vertices += [base_vertices[0] + pyramid_side2] #far lef http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 117/173 9/6/2018 capture-orbit-sling t base_vertices += [base_vertices[1] + pyramid_side1] #far rig ht base_vertices += [base_vertices[2] - pyramid_side2] #near rig ht #Need to displace vertices because apparently triangles can't sha re the same vertices. displacement_n = [-sides_n[1]] #Front side is first. displacement_n += [-sides_n[0]] #Then left side. displacement_n += [+sides_n[1]] #Then far side. displacement_n += [+sides_n[0]] #Then right side. for sidenum in range(4): t.triangle(tach(base_vertices[sidenum]-t iny*displacement_n[sidenum]),tach(base_vertices[sidenum-1]+tiny*displ acement_n[sidenum]),tach(apex+tiny*displacement_n[sidenum]),pyramid_t exture_string) return t

def draw_hub(t,hub_texture_string,hub_center,hub_axis,hub_radius): """ Input : Tachyon scene t, texture string, a position vector of hu b's center, a vector of the hub's axis (with a magnitude equal to the length of the hub), and the hub radius.\n Output: Tachyon scene t with an added hub. """ if retrograde_anchor: #Draw a hub that doesn't intersect with the hawsepipe. hub_length = hub_axis.norm() hub_axis_n = hub_axis/hub_length t.fcylinder(tach(hub_center+hub_axis/2),tach(hub_center+(hub_ax is/2-hub_stub*hub_axis_n)),hub_radius,hub_texture_string) #top stub w here sling A attaches. t.fcylinder(tach(hub_center-hub_axis/2),tach(hub_center-(hub_ax is/2-hub_stub*hub_axis_n)),hub_radius,hub_texture_string) #bottom stu b where sling B attaches. else: t.fcylinder(tach(hub_center+hub_axis/2),tach(hub_center-hub _axis/2),hub_radius,hub_texture_string) return t

def draw_radiator(t,rad_texture_string,rad_center,rad_axis,rad_radi us): """ Input : Tachyon scene t, texture string, a position vector of rad iator's center, a vector of the radiator's axis, and the radiator rad ius.\n Output: Tachyon scene t with an added radiator. """ t.ring(tach(rad_center),tach(rad_axis),0,rad_radius,rad_texture_s tring) #For some reason this sun shade shows up at a completely differen t place(s) around Mars, and it seems to be oriented with its axis per pendicular to the requested cs_axis/rad_axis. t.fcylinder(tach(rad_c enter+light_rad*rad_axis),tach(rad_center-light_rad*rad_axis),rad_rad ius+tiny,'hub') #Shade for radiator. return t

def draw_hawsepipe(t,hawsepipe_texture_string,hawsepipe_center,haws epipe_axis,hawsepipe_radius,hawsepipe_coils,hawsepipe_gap): http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 118/173 9/6/2018 capture-orbit-sling """ Input : Tachyon scene t, texture string, a position vector of haw sepipe's center, a vector of the hawsepipe's axis (with a magnitude e qual to the length of the hawsepipe), the number of coils and the gap fraction for each coil.\n Output: Tachyon scene t with an added hawsepipe (for the anchor l inks to pass through). """ hawsepipe_length = hawsepipe_axis.norm() hawsepipe_axis_n = hawsepipe_axis/hawsepipe_length #end1 = hawsepipe_center - hawsepipe_axis/2 #end2 = hawsepipe_center + hawsepipe_axis/2 coil_width = hawsepipe_length/hawsepipe_coils*(1-hawsepipe_gap) #centers = hawsepipe_center - hawsepipe_axis/2 + (coil_width/2 + j*hawsepipe_length/hawsepipe_coils)*hawsepipe_axis_n #This used to be "for j in range(hawsepipe_coils)" but then the l ast coil vanished at some point. Hmmm... for j in range(hawsepipe_coils+1): t.fcylinder(tach(hawsepipe_cen ter - hawsepipe_axis/2 + (coil_width/2 + j*hawsepipe_length/hawsepipe _coils)*hawsepipe_axis_n + coil_width/2*hawsepipe_axis_n),tach(hawsep ipe_center - hawsepipe_axis/2 + (coil_width/2 + j*hawsepipe_length/ha wsepipe_coils)*hawsepipe_axis_n - coil_width/2*hawsepipe_axis_n),haws epipe_radius,hawsepipe_texture_string) #Draw axial supports: if hawsepipe_supports: support_center_vec0 = hawsepipe_axis_n.cross_product(cs_axis) support_center_vec0 = hawsepipe_radius*cos(support_width/2)*sup port_center_vec0/support_center_vec0.norm() support_side_vec0 = hawsepipe_radius*2*sin(support_width/2)*c s_axis # https://en.wikipedia.org/wiki/Chord_(geometry)#In_trigonomet ry for j in range(hawsepipe_supports): support_rotate = rotateV(hawsepipe_axis_n,2*pi*(j+0.2)/ha wsepipe_supports) support_center_vec = support_rotate*support_center_vec0 # Rot ate vector pointing from hawsepipe center to support center. support_side_vec = support_rotate*support_side_vec0 # Rot ate vector pointing along the edge of the support. t = draw_rectangle(t,hawsepipe_texture_string,hawsepipe_cente r+support_center_vec,hawsepipe_axis,support_side_vec) #Draw the part of the hub that mounts the hawsepipe: inner_rad = hawsepipe_radius+tiny outer_rad = hawsepipe_radius+hub_rad+tiny t.fcylinder(tach(hawsepipe_center - hub_rad*hawsepipe_axis_n),tac h(hawsepipe_center + hub_rad*hawsepipe_axis_n),inner_rad,'hub') #inne r cylinder t.fcylinder(tach(hawsepipe_center - hub_rad*hawsepipe_axis_n),tac h(hawsepipe_center + hub_rad*hawsepipe_axis_n),outer_rad,'hub') #oute r cylinder t.ring(tach(hawsepipe_center - hub_rad*hawsepipe_axis_n),tach(haw sepipe_axis_n),inner_rad,outer_rad,'hub') #end cap t.ring(tach(hawsepipe_center + hub_rad*hawsepipe_axis_n),tach(haw sepipe_axis_n),inner_rad,outer_rad,'hub') #end cap return t

%time @parallel() #Supposed to be able to put the number of cores to use http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 119/173 9/6/2018 capture-orbit-sling inside these parentheses, but it never seemed to work... def cs_throw_frame(n, filename): """ Input : Index of current frame, and the filename to save this ani mation frame to.\n Output: A single frame of the capture sling throw animation, save d to disk as "filename". """ time = start_time + (RDF(n)/numframes_REAL)*timespan tips_pos, tips_vel = sling_tips(time) kepler_com_cs[5] = time com_cs_pos,com_cs_vel = kepler2xyz(kepler_com_cs) com_cs_pos_n = com_cs_pos/com_cs_pos.norm() #Define a unit vector pointing along the orbital position vector. com_cs_vel_n = com_cs_vel/com_cs_vel.norm() #Define a unit vector pointing along the orbital velocity vector. centers = [-1, com_cs_pos + offset,com_cs_pos - offset] #Centers of mass for slings M,A,B. (Moon sling at index 0 is just a placehold er.) if viewtype == 0: #Side view lookat = mars_pos + (r_pl + 0.1*(com_cs_pos.norm()-r_pl))*( sun_pos/sun_pos.norm()) #Look in the general vicinity of the subsolar point of Mars. camera_pos = com_cs_pos + 2.8*light_spacing*(com_cs_pos-lookat) /(com_cs_pos-lookat).norm()# + vector([0,0,0.01*longest_capture_arm]) up_dir = H_cs_unit elif viewtype == 1: #Overhead view lookat = (r_pl + atmo_alt + 0.6*(com_cs_pos.norm()-r_pl-atm o_alt))*com_cs_pos/com_cs_pos.norm() camera_pos = com_cs_pos + H_cs_unit*cam_const1*((com_cs_pos.nor m()-r_pl)/(periapsis_clearance))^cam_const2*(com_cs_pos.norm()-r_pl) if goingup: up_dir = rotateZ(pi/2)*sun_pos else: up_dir = -sun_pos elif viewtype == 2: #Simple overhead view where the camera is alw ays directly above the hub. lookat = com_cs_pos camera_pos = com_cs_pos + H_cs_unit*longest_capture_arm*2 if goingup: up_dir = rotateZ(pi/2)*sun_pos else: up_dir = -sun_pos elif viewtype == 10: #View from another orbit. Sling overtakes ca mera. kepler_cam[5] = time camera_pos,camera_vel = kepler2xyz(kepler_cam) lookat = com_cs_pos up_dir = H_cs_unit elif viewtype >= 100 and viewtype < 200: #Payload cam tips_vel_n = tips_vel[pc_j][pc_k]/tips_vel[pc_j][pc_k].norm() tips_pos_n = tips_pos[pc_j][pc_k]/tips_pos[pc_j][pc_k].norm() if viewtype == 100: cam_offset = nullvec cam_offset_n = nullvec elif viewtype == 150: cam_offset = payload_width*10*(0.4*cs_axis - 0.3*tips_vel_n + 0.6*tips_pos_n) #Position the payload in the bottom of the screen; leave the hub visible. cam_offset_n = cam_offset/cam_offset.norm() else: http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 120/173 9/6/2018 capture-orbit-sling print warnstring,'viewtype',viewtype,'was not recognized in p ayload cam section!' print failure if time < throw_times[pc_j]: camera_pos = centers[pc_j] + tips_ pos[pc_j][pc_k] + cam_offset else: keplers[pc_j][pc_k][5] = time - throw_times[pc_j] + throw_tp ers[pc_j][pc_k] mass_pos,mass_vel = kepler2xyz(keplers[pc_j][pc_k]) if pc_j == 1: someneg = 1 else: someneg = -1 camera_pos = mass_pos+someneg*offset + cam_offset lookat = camera_pos - tips_pos_n - 1.5*cam_offset_n #Payload ca mera looks back along sling toward the hub. up_dir = cs_axis else: print warnstring,'viewtype',viewtype,'was not recognized!' print failure t = Tachyon(xres=xresn,yres=yresn, aspectratio=aspectr, camera_ce nter=tach(camera_pos), look_at=tach(lookat), updir=tach(up_dir), rayd epth=max([atmo_layers+3,5]), antialiasing=True) t.light(tach(sun_pos), radius_sun, sunlight) #Draw the Sun. t.texture('light1', ambient=1.0, diffuse=0.0, color =light1_color) t.texture('light2', ambient=1.0, diffuse=0.0, color =light2_color) t.texture('light1_pc',ambient=pc_ambient, diffuse=0.0, opaci ty=pc_opacity, color=light1_color) #Mostly transparent so payload cam can see. t.texture('light2_pc',ambient=pc_ambient, diffuse=0.0, opaci ty=pc_opacity, color=light2_color) t.texture('payload0', ambient=payload_ambient, diffuse=payload_di ffuse, color=payload0_color) t.texture('payload1', ambient=payload_ambient, diffuse=payload_di ffuse, color=payload1_color) t.texture('grapple', ambient=payload_ambient, diffuse=payload_di ffuse, color=grapple_color) t.texture('hub', ambient=hub_ambient, diffuse=hub_diffus e, color=hub_color) t.texture('radiator', ambient=hub_ambient, diffuse=hub_diffus e, color=hub_color) t.texture('mirror', ambient=0.0, diffuse=0.0,specul ar=1.0,color=hub_color) t.texture('anchor', ambient=payload_ambient, diffuse=payload_di ffuse, color=anchor_color) t.texture('solar', ambient=solar_ambient, diffuse=solar_diff use, specular=solar_specular, color=solar_color) t = draw_hub(t,'hub',com_cs_pos,2*(offset.norm()+light_rad)*cs_ax is,hub_rad) t = draw_radiator(t,'radiator',centers[1]+light_rad*cs_axis,cs_ax is,radiator_rad) #radiator A t = draw_radiator(t,'radiator',centers[2]-light_rad*cs_axis,-cs_a xis,radiator_rad) #radiator B if trajopt >= 100: #Draw top and bottom solar panels t = draw_rectangle(t,'solar',centers[1]+(light_rad+solar_width/ 2)*cs_axis,solar_width*cs_axis,solar_width*other_solar_axis) t = draw_rectangle(t,'solar',centers[2]-(light_rad+solar_width/ http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 121/173 9/6/2018 capture-orbit-sling 2)*cs_axis,solar_width*cs_axis,solar_width*other_solar_axis) if retrograde_anchor: t = draw_hawsepipe(t,'hub',com_cs_pos,2*hawsepipe_multiplier*of fset.norm()*com_cs_vel_n,hub_rad,hawsepipe_coils,hawsepipe_gap) for j in range(anchor_links): #Loop through all anchor links. kepler_anchor[5] = time + j/anchor_links*anchor_spread anchor_pos,anchor_vel = kepler2xyz(kepler_anchor) t.sphere(tach(anchor_pos), 1.5*light_rad, 'anchor') # This il luminates the planet: t.light(tach(anchor_pos), payload_width/2, anch or_color) for j in range(1,3-disable_sling_B): #Loop through capture orbit slings (include sling B only if it isn't disabled). for k in range(2): #Loop through capture orbit sling arms. lightpos = hub_rad+light_rad #Put the first light right next to the hub. tips_pos_n = tips_pos[j][k]/tips_pos[j][k].norm() #For speed, define a unit vector in the direction of this tip's position. tips_vel_n = tips_vel[j][k]/tips_vel[j][k].norm() #For speed, define a unit vector in the direction of this tip's velocity. #Draw lights along the slings, but stop before getting to the edges of the grapples. while lightpos < tips_pos[j][k].norm() - (combined_payload_wi dths[j][k]/2+combined_grapple_heights[j][k]): if time > throw_times[j] or viewtype != 100 or j != pc_j or k != pc_k: texture_string = 'light%s'%j else: texture_string = 'light%s_pc'%j t.sphere(tach(centers[j] + lightpos*tips_pos_n), light_rad, texture_string) lightpos += light_spacing #Draw the payloads, counterbalances and grapples. grapple_position = centers[j] + tips_pos[j][k] - tips_pos_n*( combined_payload_widths[j][k]+combined_grapple_heights[j][k])/2 grapple_axis1 = (-1)^(j+1)*combined_payload_widths[j][k]*t ips_vel_n #Negative for sling B so it points right using the overhead view. grapple_axis2 = -combined_payload_widths[j][k]*cs_axis #Ne gative for both slings so it points into the screen using the overhea d view. if viewtype != 100 or j != pc_j or k != pc_k: #Skip if this i s the payload with the payload cam. t = draw_pyramid(t,'grapple',grapple_position,grapple_axis1 ,grapple_axis2,combined_grapple_heights[j][k]) if time < throw_times[j]: payload_position = centers[j] + t ips_pos[j][k] else: payload_position = centers[j] + tips_pos[j][k] keplers[j][k][5] = time - throw_times[j] + throw_tpers[j ][k] mass_pos,mass_vel = kepler2xyz(keplers[j][k]) if j == 1: someneg = 1 else: someneg = -1 payload_position = mass_pos+someneg*offset if orbit_lines > 0: #Only draw orbit lines for this paylo ad if it isn't the payload cam. for o_n in range(1,len(payload_pos[j][k])): t.fcylinder(tach((1-tiny)*payload_pos[j][k][o_n-1]),t http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 122/173 9/6/2018 capture-orbit-sling ach(payload_pos[j][k][o_n]),light_rad,'payload%s'%k) t = draw_box(t,'payload%s'%k,payload_position,combined_payl oad_widths[j][k]*tips_vel_n,combined_payload_widths[j][k]*cs_axis,com bined_payload_widths[j][k]) elif time > throw_times[j]: #Render the grapple even if this is the payload with the payload cam, but only render it AFTER the th row. t = draw_pyramid(t,'grapple',grapple_position,grapple_axis1 ,grapple_axis2,combined_grapple_heights[j][k]) #If the commands below are moved above the capture sling commands (i.e. right below the texture definitions) then the hub and hawsepip e both fail to render. This seems like a clue to the underlying probl em, but no time to decipher it. #marsfunc = t.texfunc(type=8, imagefile='Mars_2k-050104.ppm', rot ate=(0,0,0)) #This doesn't actually rotate the texture when (0,0,0) i s changed, but this page implies it should: https://trac.sagemath.or g/ticket/799 #t.texture('mars', imagefile='Mars_2k-050104.ppm', texfunc=marsfu nc) if mars_here: t.texture('mars', ambient=0, imagefile=marsfile, te xfunc=8) else: t.texture('mars', ambient=0, color=backup_mars) t.sphere(tach(mars_pos), r_pl, 'mars') #Draw Mars. if atmo_layers > 0: t.texture('atmo', opacity = atmo_opacity, ambient = atmo_ambien t, diffuse = atmo_diffuse, color=(1,1,1)) for k in range(atmo_layers): t.sphere(tach(mars_pos), r_pl+atmo _alt*(k+1.0)/atmo_layers, 'atmo') if stars_here: t.texture('stars', ambient=stars_ambient, diffuse= 0.0, imagefile=starsfile, texfunc=8) else: t.texture('stars', ambient=0 , diffuse= 0.0, color=(0,0,0)) #t.show() #This won't show the rotations applied below, but appar ently nothing will... t.sphere(tach(sun_pos), a_pl*10, 'stars') #Here the stars are a t exture on a sphere that's 10x larger than the planet's orbit. t_str = t.str() if mars_here: rot_string = 'rotate {:03.1f} {:03.1f} {:03.1f}'.format(0,0,0) new_mars_string = mars_string.replace('rotate 0.0 0.0 0.0',rot _string) #new_mars_string = mars_string.replace(marsfile,starsfile) #jus t testing to see if this works because I can't seem to change the tex ture rotation at all. Texturing Mars with the starfield kind of makes it look like an event horizon... t_str = t_str.replace(mars_string,new_mars_string) #if n == 0: print t_str tachyon_rt(t_str,outfile=filename,verbose=0) #t.save(filename) #Old way of saving with no obvious way to alter the Tachyon string used. if n%notif==0: print '.',

if anim_file[-3:] == 'gif': anim_command = 'ffmpeg -f image2 -r % f -i %s -b:v %d %s' % (fps, filename_pattern, bitrate, anim_file) elif anim_file[-3:] == 'ogg': anim_command = 'ffmpeg -f image2 -r % f -i %s -b:v %d %s' % (fps, filename_pattern, bitrate, anim_file) elif anim_file[-3:] == 'mp4': anim_command = 'ffmpeg -f image2 -r % http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 123/173 9/6/2018 capture-orbit-sling f -i %s -c:a aac -c:v libx264 -pix_fmt yuv420p -movflags faststart - b:v %d %s' % (fps, filename_pattern, bitrate, anim_file) else: print warnstring,'Animation file extension/"',anim_file[-3:], '/" is not supported.'

if numframes > 1: print '\nIf this cell crashes, try running the following command in a terminal before running this cell again (which would delete the frames):' print anim_command,'\n' start = walltime() if numframes > 1: print '-' * (numframes//notif)+'| ->',numframes, 'frames to calculate:' #This bar shows the number of notifications (".") that need to be returned until the rendering is finished. else: print numframes, 'frame to calculate...' len(list(cs_throw_frame([(n,filename_pattern%i) for i, n in enumera te(range(numframes))])))

print '\nFinished rendering frames.' if numframes > 1: print 'Now using ffmpeg to create the animation...' os.system(anim_command)

print '\nwalltime() -start =',walltime() -start,'seconds.' if exagg > 1+tolerance: print exagg_warning

#salvus.file(anim_file) #This doesn't work in Jupyter notebooks. if textonly == 0: display(Image(filename=filename_pattern%0)) else: print 'Textonly =',textonly,'so this image wasn\'t displaye d.' if exagg > 1+tolerance: print exagg_warning #if numframes > 1: #Displaying the animation doesn't seem to work a fter converting to ipython. So in CoCalc, just download the animation from "files". # from IPython.display import HTML # html_string = ''% (xresn, yresn, anim_fi le) # print html_string # HTML(html_string) set_verbose(initial_verbose)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 124/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

!!!WARNING!!! The longest capture sling arm is 135.15 km long, so it can reach down to an altitude of 389.32 km. Highest capture sling payload tip speed is 1102.9 m/s. Highest capture sling tip speed is 1202.06474378 m/s. !!!WARNING!!! FIX orbit lines (which means moving calc_payload_pos() back up to this cell)!!!! ALSO: CONSIDER AN OPTION THAT FOLLOWS COUNT ERBALANCE B AS IT AEROBRAKES OR LIFTS A PACKAGE FROM MARS. !!!WARNING!!! Size of lights and payloads/balances, hub, solar panel s, radiators is exaggerated by a factor of 9000. for visibility. slings.mp4 , resolution: 1280 x 720 , numframes: 1000 , fps: 30 , sim ulation timespan: 33. minutes, video length: 33. seconds, speedup: 6 0. , desired filesize: 100. MB, bitrate: 25165824 bits/second, viewty pe: 1 , atmo_layers: 0 , orbit_lines: 0 CPU times: user 22 µs, sys: 1 µs, total: 23 µs Wall time: 34.1 µs

If this cell crashes, try running the following command in a terminal before running this cell again (which would delete the frames): ffmpeg -f image2 -r 30.000000 -i anim%04d.png -c:a aac -c:v libx264 - pix_fmt yuv420p -movflags faststart -b:v 25165824 slings.mp4

------| -> 1000 f rames to calculate: ...... Finished rendering frames. Now using ffmpeg to create the animation...

walltime() -start = 7227.18821502 seconds. !!!WARNING!!! Size of lights and payloads/balances, hub, solar panel s, radiators is exaggerated by a factor of 9000. for visibility.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 125/173 9/6/2018 capture-orbit-sling

!!!WARNING!!! Size of lights and payloads/balances, hub, solar panel s, radiators is exaggerated by a factor of 9000. for visibility.

In [26]: os.system('rm anim*.png') #remove the old frames

Out[26]: 0

17. Make a 3D plot of the payloads and counterbalances to assess the risk of collisions. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 126/173 9/6/2018 capture-orbit-sling

In [27]: print capture_frame_string initial_verbose=get_verbose() set_verbose(0)

def calc_payload_pos(numpos,sindices): """ Input : Number of positions to calculate, list of sling indices.\n Output: Positions of each sling's payload and counterbalance after being thrown. If they're in elliptical orbits then record positions for complete orbits. """ #What's the longest period of any selected payload or counterbalanc e? payload_periods = [[-1.0 for i in range(2)] for j in range(num_slin gs)] #Periods of payloads or counterbalances. for j in range(num_slings): #Loop through all slings. for k in range(2): #Loop through sling arms. if hasattr(keplers[j][k], "__len__"): #Skip this sling if its k eplers entry doesn't have a length (so it's likely just a number like -1). if keplers[j][k][0] > 0: payload_periods[j][k] = 2*pi*sqrt(ke plers[j][k][0]^3/mu) #'0' is the keplers index for the semi-major axi s. else: payload_periods[j][k] = -1 #Hyperbol ic trajectories don't have periods. selected_payload_periods = [payload_periods[x] for x in sindices] print 'selected_payload_periods =',selected_payload_periods if len(selected_payload_periods) > 1: longest_orbit_period = (max(m ap(max, *selected_payload_periods))).n() else: longest_orbit_period = max(selected_payload_periods[0])

print 'longest_orbit_period (seconds) =',(longest_orbit_period).n() payload_pos = [[[] for i in range(2)] for j in range(num_slings)] # Payload positions at different times. print 'Calculating payload positions.', for j in sindices: #Loop through specified sling indices. for k in range(2): #Loop through sling arms. n=-1 #The while loop increments 'n' at the beginning. Set n=-1 so the first while loop starts calculating at n=0. if payload_periods[j][k] > 0: orbit_timestep = payload_periods[ j][k]/numpos else: orbit_timestep = longest_orbit_period/numpos print '\norbit_timestep for payload ['+str(j)+']['+str(k)+'] =' ,(orbit_timestep/60).n(),'minutes.' while true: n += 1 time = RDF(n)*orbit_timestep #print 'keplers[',j,'][',k,'] =',keplers[j][k] keplers[j][k][5] = time - throw_times[j] + throw_tpers[j][k] mass_pos,mass_vel = kepler2xyz(keplers[j][k]) if j == 0: payload_pos[j][k] += [mass_pos] else: if j == 1: someneg = 1 else: someneg = -1 payload_pos[j][k] += [mass_pos+someneg*offset] if time > orbit_timestep*numpos: break http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 127/173 9/6/2018 capture-orbit-sling if n%(notif*10) == 0: print '.', print print 'time =',time.n() print 'n=',n return payload_pos

from sage.plot.plot3d.shapes import Torus, Sphere Mars_Sphere = Sphere(r_pl,color=(1,0,0)) if moon_name == 'Phobos': moon_color = (1,0,1) other_color = (0,1,0) a_other = a_deimos inc_other = inc_deimos else: moon_color = (0,1,0) other_color = (1,0,1) a_other = a_phobos inc_other = inc_phobos exagg = 1 if exagg > 1+tolerance: print warnstring+'Exclusion zone around moons and payloads is exaggerated by a factor of '+str(exagg)+', just for visibility when r_max is too small.' numpos = 50 #Values much higher than ~50 seem to crash the jmol viewe r. Chosen_Moon_Torus = Torus(a_moon, r_max*exagg, color = moon_color).r otateX(inc_moon) Other_Moon_Torus = Torus(a_other, r_max*exagg, color = other_color). rotateX(inc_other) everything = Mars_Sphere + Chosen_Moon_Torus + Other_Moon_Torus #Select payload index 0 (moon sling) to calculate "numpos" payload/ba lance positions. if cbrs[1] < tolerance: pindices = [0] #If the capture sling is disab led, only plot payloads from the moon sling. else: pindices = [0] #Or take your pick. Plot the moon sling and/or s elect payload indices 1 and 2 (capture slings) to calculate "numpos" payload/balance positions. payload_pos = calc_payload_pos(numpos,pindices) payload_colors = [(0,0,1),(1,1,0)] for j in pindices: #Loop through whichever payload indices were chose n. for k in range(2): for o_n in range(len(payload_pos[j][k])): everything += Sphere(r_max*exagg,color=payload_colors[k]).trans late(payload_pos[j][k][o_n])

if textonly == 0: interactive_plot = 0 #Set to 1 for an interactive (but slow) 3D plo t. Set to 0 for a quick 3D snapshot. if interactive_plot != 0: show(everything, aspect_ratio=(1,1,1), zo om=1.3, frame=false, viewer='jmol') #Default viewer is 'jmol'; also 'tachyon', 'threejs'. else: plotfilename='3D_orbit_plot.png' everything.rotateX(0*pi/180).rotateY(0*pi/180).rotateZ(0*pi/180). save(plotfilename, aspect_ratio=(1,1,1), zoom=1.3, frame=false, figsi ze=8, dpi=300) display(Image(filename=plotfilename)) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 128/173 9/6/2018 capture-orbit-sling else: print 'Textonly =',textonly,'so this image wasn\'t displayed.' set_verbose(initial_verbose) Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

selected_payload_periods = [[15787.8287031397*pi, 57452.2833455143*p i]] longest_orbit_period (seconds) = 180491.671290227 Calculating payload positions. orbit_timestep for payload [0][0] = 16.5329755566393 minutes. . orbit_timestep for payload [0][1] = 60.1638904300756 minutes. . time = 184101.504716031 n= 51

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 129/173 9/6/2018 capture-orbit-sling

18. Calculate mass of equivalent rocket. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 130/173 9/6/2018 capture-orbit-sling

In [28]: summary_digits = 15 #Many print statements in this notebook will prin t "summary_digits" significant figures.

#Each payload has a (potentially different) total rocket delta-v. Fir st, each rocket leaves the moon: delta_vs = [radii[0]*omegas[0]] if cbrs[1] > tolerance: delta_vs *= 2 #If the capture sling is used, duplicate the first en try so there are two rockets, each with the same initial delta-v. #Then each rocket performs an instantaneous burn at periapsis. The first burn was already calculated for the perpendicular sling at to p. assume(v_tip>0,r>0) eq_v_tip2 = v_infs[mis[2]]^2 == 2*((v+v_tip)^2/2 - mu/r) soln_v_tip2 = solve(eq_v_tip2.subs(v=capture_speed_periapsis,r=peri apsis_capture),v_tip) v_tip_capture_payload2 = soln_v_tip2[0].rhs().n() delta_vs[0] += v_tip_capture_payload1 delta_vs[1] += v_tip_capture_payload2

print '\ndelta_vs =',delta_vs

prop_fracs = [exp(x/(g*Isp))-1.0 for x in delta_vs] #These are ratios of propellant mass to payload mass. See eq 17 from Puig-Suari et al. 1995: http://web.archive.org/web/20171006015505/https://engineering. purdue.edu/people/james.m.longuski.1/JournalArticles/1995/ATetherSlin gforLunarandInterplanetaryExploration.pdf They're related to the "p ropellant mass fraction" which is the ratio of propellant mass to the total initial mass of the rocket. https://en.wikipedia.org/wiki/Tsio lkovsky_rocket_equation print '\nprop_fracs =',prop_fracs print '\npayload_masses =',payload_masses if cbrs[1] > tolerance: rocket_payloads = [payload_masses[1][0],payload_masses[2][0]] rocket_payloads[C_is_on-1] += payload_masses[3][0] #Subtract 1 beca use C_is_on usually indexes a list where the 0'th index is the moon s ling. else: rocket_payloads = [payload_mass]

print '\nrocket_payloads =',rocket_payloads rocket_propellants = [temp1*temp2 for temp1,temp2 in zip(rocket_paylo ads,prop_fracs)] print '\nrocket_propellants =',rocket_propellants

if s2p > tolerance: iterations = [0 for x in delta_vs] print '\nIterating to determine structural mass...' while true: #Loop until encountering the break command. if max(iterations) >= 0: rocket_structures = [s2p*x for x in rocket_propellants] for j in range(len(delta_vs)): if iterations[j] >= 0: rocket_propellant = (rocket_payloads[j]+rocket_structures[j ])*prop_fracs[j] #Iterate this rocket's structural mass until its propellant changes by less than "tolerance". http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 131/173 9/6/2018 capture-orbit-sling if abs(rocket_propellant - rocket_propellants[j]) > toleran ce: rocket_propellants[j] = rocket_propellant iterations[j] += 1 else: iterations[j] *= -1 #This rocket's iterations are now negative. When all iterations are negative, break out of the while l oop. print 'rocket_structures =',rocket_structures,', rocket_propell ants =',rocket_propellants else: break print '\nFinished.' print '\niterations =',iterations else: rocket_structures = [0.0 for x in delta_vs]

rocket_desc = '' for j in range(len(delta_vs)): rocket_desc += '\nRocket '+sdescs[j+1]+' sends a '+str((rocket_payl oads[j]/1000).n(summary_digits))+' ton payload through a delta-v of ' +str((delta_vs[j]).n(summary_digits))+' m/s. Its total mass (structur e plus propellant, no payload) is '+str(((rocket_structures[j]+rocket _propellants[j])/1000).n(summary_digits))+' tons. Its structural mass is '+str((rocket_structures[j]/1000).n(summary_digits))+' tons, whic h is '+str((rocket_structures[j]/rocket_propellants[j]*100.0).n(summa ry_digits))+'% of its propellant mass of '+str((rocket_propellants[j] /1000).n(summary_digits))+' tons. Its is '+s tr((rocket_propellants[j]/(rocket_payloads[j]+rocket_structures[j]+ro cket_propellants[j])).n(summary_digits))+' and its i s '+str((rocket_payloads[j]/(rocket_payloads[j]+rocket_structures[j]+ rocket_propellants[j])).n(summary_digits))+'.\n' if j > 0: rocket_desc += '\nTotal rocket mass is '+str(((sum(rocket _structures)+sum(rocket_propellants))/1000).n(summary_digits))+' ton s, or '+str((((sum(rocket_structures)+sum(rocket_propellants))/sum(ro cket_payloads))).n(summary_digits))+' times the combined payload mass of '+str(((sum(rocket_payloads))/1000).n(summary_digits))+' tons.\n' print rocket_desc

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 132/173 9/6/2018 capture-orbit-sling

delta_vs = [1846.1946611564279, 1846.1946611564279]

prop_fracs = [0.6430547940009077, 0.6430547940009077]

payload_masses = [[4816.56194868958, 19266.2477947583], [1455.074 00319400, 1443.96175550632], [1000.00000000000, 917.526189989254], [4 55.074003194002, 451.598650718035]]

rocket_payloads = [1910.14800638800, 1000.00000000000]

rocket_propellants = [1228.32983275908, 643.054794000908]

Iterating to determine structural mass... rocket_structures = [184.249474913862, 96.4582191001361] , rocket_pro pellants = [1346.81234089459, 705.082714214040] rocket_structures = [202.021851134189, 105.762407132106] , rocket_pro pellants = [1358.24095262386, 711.065816932284] rocket_structures = [203.736142893579, 106.659872539843] , rocket_pro pellants = [1359.34333615805, 711.642936365179] rocket_structures = [203.901500423708, 106.746440454777] , rocket_pro pellants = [1359.44967011053, 711.698604277884] rocket_structures = [203.917450516579, 106.754790641683] , rocket_pro pellants = [1359.45992689421, 711.703973905605] rocket_structures = [203.918989034132, 106.755596085841] , rocket_pro pellants = [1359.46091624530, 711.704491850332] rocket_structures = [203.919137436795, 106.755673777550] , rocket_pro pellants = [1359.46101167634, 711.704541810358] rocket_structures = [203.919151751452, 106.755681271554] , rocket_pro pellants = [1359.46102088145, 711.704546629413] rocket_structures = [203.919153132218, 106.755681994412] , rocket_pro pellants = [1359.46102088145, 711.704546629413]

Finished.

iterations = [-8, -8]

Rocket A sends a 1.910 ton payload through a delta-v of 1846. m/s. It s total mass (structure plus propellant, no payload) is 1.563 tons. I ts structural mass is 0.2039 tons, which is 15.00% of its propellant mass of 1.359 tons. Its propellant mass fraction is 0.3914 and its pa yload fraction is 0.5499.

Rocket B sends a 1.000 ton payload through a delta-v of 1846. m/s. It s total mass (structure plus propellant, no payload) is 0.8185 tons. Its structural mass is 0.1068 tons, which is 15.00% of its propellant mass of 0.7117 tons. Its propellant mass fraction is 0.3914 and its p ayload fraction is 0.5499.

Total rocket mass is 2.382 tons, or 0.8185 times the combined payload mass of 2.910 tons.

19. Print summary. (Go back to the top). http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 133/173 9/6/2018 capture-orbit-sling

In [29]: summary_digits = 5 #Many print statements in this notebook will print "summary_digits" significant figures. if cbrs[1] < tolerance: desc_width = 5 #This is used at the very bottom of this cell. desc = 'Summary for a '+str((payload_masses[0][0]/1000.0).n(digits= 3))+' ton payload thrown directly from ' lj = 50 #Left justify descriptions by this number of characters. else: desc_width = 15 #This is used at the very bottom of this cell. desc = 'Summary for a '+str(((payload_masses[C_is_on][0]+payload_ma sses[3][0])/1000.0).n(digits=3))+' ton payload and a '+str((payload_m asses[C_is_not_on][0]/1000.0).n(digits=3))+' ton payload thrown from ' lj = 57 #Left justify descriptions by this number of characters. desc += moon_name+' around '+pname+' by a '+material+' sling with saf ety factor '+str(safety.n(digits=3)) if abs(best_case_offset) > tolerance: best_str = 'worst case ' else: best_str = 'best case ' if cbrs[1] < tolerance: desc +=' on a '+best_str+mnames[mis[0]]+' mis sion with v_inf = '+str(v_infs[mis[0]].n(digits=5)) +' m/s' else: desc +=' on a '+mnames[mis[C_is_on]] +' mis sion with v_inf = '+str(v_infs[mis[C_is_on]].n(digits=5))+' m/s' if cbrs[1] < tolerance or mis[1] == mis[2]: desc += '.\n' else: desc +=' and a '+mnames[mis[C_is_not_on]]+' mission with v_inf = '+str(v_infs[mis[C_is_not_on]].n(digits=5))+' m/s.\n' print desc

if cbrs[1] > tolerance: print traj_string+'\n'

separator = '-' * (lj+2) print '1 = payload arm of sling, 2 = counterbalance arm of sling. Cen ter of mass > 0 if it\'s on the payload arm.' if cbrs[1] > tolerance: print 'A/B = capture orbit sling which releas es its payload farthest/nearest to',pname

for j in range(num_slings): if cbrs[j] > tolerance: if cbrs[j] > dmr: print '\n!!! DANGER !!!',cap1st(descs[j]),'ma ss ratio',cbrs[j],'is higher than the safe limit of',dmr,'!!!' if cbrs[j] < 1/dmr: print '\n!!! DANGER !!!',cap1st(descs[j]),'ma ss ratio',cbrs[j], 'is lower than the safe limit of',1/dmr,'!!!'

if cbrs[1] > tolerance: print '\nCapture orbit is in a',str(resn_s)+':'+str(resn_p),'resona nce with',moon_name+'.' print 'Capture orbit apoapsis (km)'.ljust(lj),':',(apoapsis_capture /1000.0).n(digits=summary_digits) print 'Capture orbit periapsis (km)'.ljust(lj),':',(periapsis_captu re/1000.0).n(digits=summary_digits) print 'Capture orbit periapsis altitude (km)'.ljust(lj),':',((peria psis_capture-r_pl)/1000.0).n(digits=summary_digits) desc = 'Capture orbit period ('+uname+')' ; print desc.ljust(lj), ':',(period_capture/units).n(digits=summary_digits) desc = 'Capture orbit revisit time ('+uname+')' ; print desc.ljust( lj),':',(period_capture*resn_s/units).n(digits=summary_digits) print longest_arm_string #This was written in the animation cell. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 134/173 9/6/2018 capture-orbit-sling

for j in range(num_slings): if cbrs[j] > tolerance: if j>0: somegrad = Marswhip_gradient_factor #Only apply the gradient re inforcement to capture orbit slings. somespace = '' #Only put a space after the moon sling descripti on. else: somegrad = 1.0 somespace = ' ' #Only put a space after the moon sling descript ion. print '\n',separator if j==0 and cbrs[1] > tolerance: print cap1st(descs[j]),'throws a',(payload_masses[j][0]/1000.0) .n(digits=4),'ton payload to the capture slings.' else: if j==0: desc = '' elif j==3: desc = ' (on '+sdescs[C_is_on]+')' elif j==C_is_on: desc = ' (with '+sdescs[3]+')' else: desc = '' print cap1st(descs[j])+desc,'throws a',(payload_masses[j][0]/10 00.0).n(digits=4),'ton payload to v_inf =',(v_infs[mis[j]]).n(digits= 5),'m/s.' print separator wa = '' if radii[j] > r_max+tolerance: wa = warnstring desc = '\n'+wa+cap1st(descs[j])+somespace+'1 length (km)' ; print desc.ljust(lj+1),':',(radii[j]/1000.0).n(digits=summary_digits) wa = '' if radii[j]/cbrs[j] > r_max+tolerance: wa = warnstring desc = wa+cap1st(descs[j])+somespace+'2 length (km)' ; print desc .ljust(lj),':',(radii[j]/cbrs[j]/1000.0).n(digits=summary_digits) if orig_cbrs[j] < -tolerance: cbr_desc = '(derived)' else: cbr_desc = '(specified)' desc = cap1st(descs[j])+' counterbalance ratio '+cbr_desc ; print desc.ljust(lj),':',cbrs[j].n(digits=summary_digits)

desc = '\n'+cap1st(descs[j])+somespace+'1 tip diameter (mm)' ; pr int desc.ljust(lj+1),':',(tip_diams[j][0]*1000).n(digits=summary_digi ts) desc = cap1st(descs[j])+somespace+'1 hub diameter (mm)' ; print d esc.ljust(lj),':',(hub_diams[j][0]*1000).n(digits=summary_digits) if abs(hub_diams[j][0] - hub_diams[j][1]) > tolerance: print '!!! WARNING!!! Equivalent sling diameters aren\'t equal at the',descs[j], 'hub!' desc = cap1st(descs[j])+somespace+'2 hub diameter (mm)' ; print d esc.ljust(lj),':',(hub_diams[j][1]*1000).n(digits=summary_digits) desc = cap1st(descs[j])+somespace+'2 tip diameter (mm)' ; print d esc.ljust(lj),':',(tip_diams[j][1]*1000).n(digits=summary_digits)

desc = '\n'+cap1st(descs[j])+somespace+'1 taper ratio' ; print de sc.ljust(lj+1),':',(hub_diams[j][0]/tip_diams[j][0]).n(digits=summary _digits) desc = cap1st(descs[j])+somespace+'2 taper ratio' ; print desc.lj ust(lj),':',(hub_diams[j][1]/tip_diams[j][1]).n(digits=summary_digits ) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 135/173 9/6/2018 capture-orbit-sling

wa = '' if abs(coms_empty[j]) > tolerance: wa = warnstring desc = '\n'+wa+'Center of mass for empty '+descs[j]+' (m)' ; prin t desc.ljust(lj+1),':',(coms_empty[j]).n(digits=summary_digits) wa = '' if abs(coms_full[j]) > tolerance: wa = warnstring desc = wa+'Center of mass for full '+descs[j]+' (m)' ; print des c.ljust(lj),':',(coms_full[j]).n(digits=summary_digits)

wa = '' if omegas[j]*radii[j] > v_tip_max+tolerance: wa = warnstring desc = '\n'+wa+cap1st(descs[j])+somespace+'1 tip speed (m/s)' ; p rint desc.ljust(lj+1),':',(omegas[j]*radii[j]).n(digits=summary_digit s) wa = '' if omegas[j]^2*radii[j]/g > tip_accel_max/g+tolerance: wa = warns tring desc = wa+cap1st(descs[j])+somespace+'1 tip accel (g\'s)' ; print desc.ljust(lj),':',(omegas[j]^2*radii[j]/g).n(digits=summary_digits) wa = '' if omegas[j]*radii[j]/cbrs[j] > v_tip_max+tolerance: wa = warnstr ing desc = wa+cap1st(descs[j])+somespace+'2 tip speed (m/s)' ; print desc.ljust(lj),':',(omegas[j]*radii[j]/cbrs[j]).n(digits=summary_digi ts) wa = '' if omegas[j]^2*radii[j]/cbrs[j]/g > tip_accel_max/g+tolerance: wa = warnstring desc = wa+cap1st(descs[j])+somespace+'2 tip accel (g\'s)' ; print desc.ljust(lj),':',(omegas[j]^2*radii[j]/cbrs[j]/g).n(digits=summary _digits) desc = cap1st(descs[j])+' rotation period (minutes)' ; print desc .ljust(lj),':',(2*pi/omegas[j]/60).n(digits=summary_digits) desc = cap1st(descs[j])+' full moment of inertia (kg*m^2)' ; pri nt desc.ljust(lj),':',(moments_full[j][0]+moments_full[j][1]).n(digit s=summary_digits) desc = cap1st(descs[j])+' empty moment of inertia (kg*m^2)' ; pri nt desc.ljust(lj),':',(moments_empty[j][0]+moments_empty[j][1]).n(dig its=summary_digits) if j==3: #See if the moment ballast capture sling C does its job. full_ang_mom = (sum(moments_full[3])+sum(moments_full[C_is_on ]))*omegas[3] - sum(moments_full[C_is_not_on])*omegas[C_is_not_on] wa = '' if abs(full_ang_mom) > 0.1: wa = warnstring #"tolerance" is too small here. desc = '\n'+wa+'Full capture sling rot. ang. mom. (kg*m^2/s)' ; print desc.ljust(lj+1),':',(full_ang_mom).n(digits=summary_digits) empty_ang_mom = (sum(moments_empty[3])+sum(moments_empty[C_is_o n]))*omegas[3] - sum(moments_empty[C_is_not_on])*omegas[C_is_not_on] wa = '' if abs(empty_ang_mom) > 0.1: wa = warnstring #"tolerance" is to o small here. desc = wa+'Empty capture sling rot. ang. mom. (kg*m^2/s)' ; pri nt desc.ljust(lj),':',(empty_ang_mom).n(digits=summary_digits) desc = '\n'+cap1st(descs[j])+' tether 1 mass (tons)' ; print desc .ljust(lj+1),':',(tether_masses[j][0]/1000.0).n(digits=summary_digits http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 136/173 9/6/2018 capture-orbit-sling ) if j>0: desc = cap1st(descs[j])+' tether 1 gradient reinforcement (tons)' ; print desc.ljust(lj),':',(tether_masses[j][0]*(somegrad-1. 0)/1000.0).n(digits=summary_digits) if abs(v_cs[j][0]-v_c) > tolerance: desc = cap1st(descs[j])+' tet her 1 safety factor multiplied by' ; print desc.ljust(lj),':',(v_c/v_ cs[j][0])^2,', Alt. calc : ',1/cbrs[j]^2 desc = cap1st(descs[j])+' tether 2 mass (tons)' ; print desc.ljus t(lj),':',(tether_masses[j][1]/1000.0).n(digits=summary_digits) if j>0: desc = cap1st(descs[j])+' tether 2 gradient reinforcement (tons)' ; print desc.ljust(lj),':',(tether_masses[j][1]*(somegrad-1. 0)/1000.0).n(digits=summary_digits) if abs(v_cs[j][1]-v_c) > tolerance: desc = cap1st(descs[j])+' tet her 2 safety factor multiplied by' ; print desc.ljust(lj),':',(v_c/v_ cs[j][1])^2,', Alt. calc : ',1/cbrs[j]^2 desc = cap1st(descs[j])+' grapple 1 mass (tons)' ; print desc.lju st(lj),':',(grapple_masses[j][0]/1000.0).n(digits=summary_digits) if ballast_masses[j][0] > tolerance: desc = cap1st(descs[j])+' ba llast 1 mass (tons)' ; print desc.ljust(lj),':',(ballast_masses[j][0] /1000.0).n(digits=summary_digits) desc = cap1st(descs[j])+' grapple 2 mass (tons)' ; print desc.lju st(lj),':',(grapple_masses[j][1]/1000.0).n(digits=summary_digits) if ballast_masses[j][1] > tolerance: desc = cap1st(descs[j])+' ba llast 2 mass (tons)' ; print desc.ljust(lj),':',(ballast_masses[j][1] /1000.0).n(digits=summary_digits) desc = cap1st(descs[j])+' solar panel mass via Juno (tons)' ; pri nt desc.ljust(lj),':',(junopanel_masses[j]/1000.0).n(digits=summary_d igits) desc = cap1st(descs[j])+' radiator mass '+estimate_misc_str+'(ton s)' ; print desc.ljust(lj),':',(radiator_masses[j]/1000.0).n(digits=s ummary_digits) desc = cap1st(descs[j])+' motor mass '+estimate_misc_str+'(ton s)' ; print desc.ljust(lj),':',(motor_masses[j]/1000.0).n(digits=summ ary_digits) desc = cap1st(descs[j])+' hub mass '+estimate_misc_str+'(ton s)' ; print desc.ljust(lj),':',(hub_masses[j]/1000.0).n(digits=summar y_digits) mfg_mass = sum(tether_masses[j]) + sum(grapple_masses[j]) + junop anel_masses[j] + radiator_masses[j] + motor_masses[j] + hub_masses[j] desc = '\n'+cap1st(descs[j])+' manufactured mass (tons)' ; print desc.ljust(lj+1),':',(mfg_mass/1000.0).n(digits=summary_digits) if ballast_choice == 1: desc = cap1st(descs[j])+' system mass, in cluding ballast (tons)' ; print desc.ljust(lj),':',((mfg_mass+sum(bal last_masses[j]))/1000.0).n(digits=summary_digits)

total_tether_mass = sum(tether_masses[0]) total_grapple_mass = sum(grapple_masses[0]) total_panel_mass = junopanel_masses[0] total_radiator_mass = radiator_masses[0] total_motor_mass = motor_masses[0] total_hub_mass = hub_masses[0] total_ballast_mass = sum(ballast_masses[0]) total_payload_mass = payload_masses[0][0] if cbrs[1] > tolerance: total_tether_mass += (sum(tether_masses[1]) + sum(tether_masses[2 ]) + sum(tether_masses[3]))*Marswhip_gradient_factor total_grapple_mass += sum([sum(grapple_masses[j]) for j in range(1 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 137/173 9/6/2018 capture-orbit-sling ,num_slings)]) total_panel_mass += sum(junopanel_masses[1:]) total_radiator_mass += sum(radiator_masses[1:]) total_motor_mass += sum(motor_masses[1:]) total_hub_mass += sum(hub_masses[1:]) total_ballast_mass += sum([sum(ballast_masses[j]) for j in range(1 ,num_slings)]) total_payload_mass = sum([x[0] for x in payload_masses[1:]]) #No "+=" because that would be double-counting. total_capture_ballast_mass = sum([sum(ballast_masses[j]) for j in r ange(1,num_slings)]) print '\n',separator print 'Total tether mass (tons)'.ljust(lj),':',(total_tether_mass/100 0.0).n(digits=summary_digits) print 'Total grapple mass (tons)'.ljust(lj),':',(total_grapple_mass/1 000.0).n(digits=summary_digits) print 'Total solar panel mass (tons)'.ljust(lj),':',(total_panel_mass /1000.0).n(digits=summary_digits) desc= 'Total radiator mass '+estimate_misc_str+'(tons)' ; print desc. ljust(lj),':',(total_radiator_mass/1000.0).n(digits=summary_digits) desc= 'Total motor mass '+estimate_misc_str+'(tons)' ; print desc. ljust(lj),':',(total_motor_mass/1000.0).n(digits=summary_digits) desc= 'Total hub mass '+estimate_misc_str+'(tons)' ; print desc. ljust(lj),':',(total_hub_mass/1000.0).n(digits=summary_digits) if ballast_choice == 1: print 'Total ballast mass (tons)'.ljust(lj), ':',(total_ballast_mass/1000.0).n(digits=summary_digits) print separator tot_mfg_mass = total_tether_mass + total_grapple_mass + total_panel_m ass + total_radiator_mass + total_motor_mass + total_hub_mass print 'Total manufactured mass (tons)'.ljust(lj),':',(tot_mfg_mass/10 00.0).n(digits=summary_digits) print 'Total manufactured mass / total payload mass'.ljust(lj),':',(t ot_mfg_mass/total_payload_mass).n(digits=summary_digits) if ballast_choice == 1: print 'Total system mass, including ballast (tons)'.ljust(lj),':', ((tot_mfg_mass + total_ballast_mass)/1000.0).n(digits=summary_digits) if cbrs[1] > tolerance: print '\n',cap1st(descs[0]),'needs to throw',(total_capture_balla st_mass/1000.0).n(digits=4),'tons of ballast to the capture sling.' ballast_throws = ((total_capture_ballast_mass/payload_masses[0][0 ]).n(digits=summary_digits)).ceil() print 'This requires',ballast_throws,'throws, so the setup takes' ,(ms_cycle_time*ballast_throws/units).n(digits=4),uname print '\nAfter setup, the moon sling throws to the capture sling every',(ms_cycle_time/units).n(digits=4),uname,'and the capture slin g can throw every',(cs_cycle_time/units).n(digits=4),uname+'.'

print '\n',separator

print rocket_desc

#From Jokic and Longuski 2004 table 3, for single stage rockets: if cbrs[1] < tolerance: print 'JL04 mu_single = moon tether1/propella nt'.ljust(lj),':',(tether_masses[0][0]/sum(rocket_propellants)).n(dig its=summary_digits)

print 'total manufactured mass / total rocket mass'.ljust(lj),':',(to http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 138/173 9/6/2018 capture-orbit-sling t_mfg_mass/(sum(rocket_structures)+sum(rocket_propellants))).n(digits =summary_digits)

print separator

print '\n'+ms_cycle_description

if cbrs[1] > tolerance: print '\n'+cs_cycle_description

print '\n',separator

for j in range(num_slings): if cbrs[j] > tolerance: desc = '\n'+cap1st(descs[j])+' spinup time ('+uname+')' ; print desc.ljust(lj+1),':',(spinup_times[j]/units).n(digits=summary_digits ) desc = cap1st(descs[j])+' spindown time ('+uname+')' ; print desc .ljust(lj),':',(spindown_times[j]/units).n(digits=summary_digits) desc = cap1st(descs[j])+' average power (kW)' ; print desc.ljust( lj),':',(avg_powers[j]/1000).n(digits=summary_digits),', Alt. calc:', (avg_powers_alt[j]/1000).n(digits=summary_digits) desc = cap1st(descs[j])+' average torque (N*m)' ; print desc.ljus t(lj),':',(avg_torques[j]).n(digits=summary_digits) desc = cap1st(descs[j])+' const accel spinup revolutions' ; pri nt desc.ljust(lj),':',(0.5*(omegas[j]/spinup_times[j])*spinup_times[j ]^2/(2*pi)).n(digits=summary_digits) #theta = 0.5*a*t^2, where a = angular acceleration. Revolutions = theta/(2*pi) desc = cap1st(descs[j])+' const accel spindown revolutions' ; pri nt desc.ljust(lj),':',((omegas[j]*spindown_times[j]-0.5*(omegas[j]/sp indown_times[j])*spindown_times[j]^2)/(2*pi)).n(digits=summary_digits ) #theta = omega0*t - 0.5*a*t^2, where a = angular acceleration. Revo lutions = theta/(2*pi)

if cbrs[1] > tolerance: print '\nTotal required average sling power (kW)'.ljust(lj+1),':',(sum(avg_powers)/1000).n(digits=summary_digits ) print separator

total_material = sum(payload_masses[0])*mt4ct #Don't include capture sling masses- that'd be double-counting. print ' ' if abs(mt4ct-1) > tolerance: print 'Moon sling throws',mt4ct.ceil(),'times for each capture slin g throw. Each full-sized moon throw consists of:' print 'Moon sling throws payload mass (tons)'.ljust(lj),':',(payloa d_masses[0][0]/1000.0).n(digits=3),', which is',(100*payload_masses[0 ][0]/total_material).n(digits=3),'% of the total.' print 'Moon sling throws balance mass (tons)'.ljust(lj),':',(payloa d_masses[0][1]/1000.0).n(digits=3),', which is',(100*payload_masses[0 ][1]/total_material).n(digits=3),'% of the total.' print '\nAfter',mt4ct.ceil(),'throws, the moon sling has thrown a t otal of:' print 'Moon sling throws payload mass (tons)'.ljust(lj),':',(payload_ masses[0][0]*mt4ct/1000.0).n(digits=3),', which is',(100*payload_mass es[0][0]*mt4ct/total_material).n(digits=3),'% of the total.' print 'Moon sling throws balance mass (tons)'.ljust(lj),':',(payload_ masses[0][1]*mt4ct/1000.0).n(digits=3),', which is',(100*payload_mass http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 139/173 9/6/2018 capture-orbit-sling es[0][1]*mt4ct/total_material).n(digits=3),'% of the total.'

if cbrs[1] > tolerance: if abs(mt4ct-1) < tolerance: print '\n1 moon payload becomes the ca pture sling payloads and counterbalances:' else: print '\n',mt4ct.n(digits=5),'moon payloads become the captur e sling payloads and counterbalances:' for j in range(1,num_slings): if cbrs[j] > tolerance: desc = cap1st(descs[j])+' throws payload mass (tons)' ; print d esc.ljust(lj),':',str((payload_masses[j][0]/1000.0).n(digits=3)).rjus t(4),', which is',(100*payload_masses[j][0]/total_material).n(digits= 3),'% of the total.' desc = cap1st(descs[j])+' throws balance mass (tons)' ; print d esc.ljust(lj),':',str((payload_masses[j][1]/1000.0).n(digits=3)).rjus t(4),', which is',(100*payload_masses[j][1]/total_material).n(digits= 3),'% of the total.'

print '' for j in range(num_slings-1): #Skip sling C because it's redundant. if cbrs[j] > tolerance: desc = 'Payloads and balances thrown from '+descs[j].ljust(desc_width)+' rotate once every '+str((2*pi/omegas[ j]/60).n(digits=3))+' minutes.' ; print desc

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 140/173 9/6/2018 capture-orbit-sling

Summary for a 1.91 ton payload and a 1.00 ton payload thrown from Dei mos around Mars by a Zylon sling with safety factor 1.00 on a M-E Hoh mann mission with v_inf = 2640.0 m/s.

COPLANAR SETUP. Capture slings release payloads A and B at r = periap sis_capture +/- sling A/B radius. Counterbalance A has a periapsis al titude of 375.79 km and the same semi-latus rectum as the specified c ounterbalance orbit. Counterbalance B aerobrakes at a periapsis altit ude of 110.00 km.

1 = payload arm of sling, 2 = counterbalance arm of sling. Center of mass > 0 if it's on the payload arm. A/B = capture orbit sling which releases its payload farthest/nearest to Mars

Capture orbit is in a 11:5 resonance with Deimos. Capture orbit apoapsis (km) : 23821. Capture orbit periapsis (km) : 3920.5 Capture orbit periapsis altitude (km) : 524.47 Capture orbit period (days) : 0.57406 Capture orbit revisit time (days) : 6.3147 !!!WARNING!!! The longest capture sling arm is 135.15 km long, so it can reach down to an altitude of 389.32 km.

------Moon sling throws a 4.817 ton payload to the capture slings. ------

Moon sling 1 length (km) : 66.770 Moon sling 2 length (km) : 16.693 Moon sling counterbalance ratio (specified) : 4.0000

Moon sling 1 tip diameter (mm) : 3.6008 Moon sling 1 hub diameter (mm) : 3.7629 Moon sling 2 hub diameter (mm) : 3.7629 Moon sling 2 tip diameter (mm) : 3.7526

Moon sling 1 taper ratio : 1.0450 Moon sling 2 taper ratio : 1.0028

Center of mass for empty moon sling (m) : -1.5672e- 12 Center of mass for full moon sling (m) : 0.00000

Moon sling 1 tip speed (m/s) : 809.33 Moon sling 1 tip accel (g's) : 1.0000 Moon sling 2 tip speed (m/s) : 202.33 Moon sling 2 tip accel (g's) : 0.25000 Moon sling rotation period (minutes) : 8.6394 Moon sling full moment of inertia (kg*m^2) : 3.5790e13 Moon sling empty moment of inertia (kg*m^2) : 8.9482e12

Moon sling tether 1 mass (tons) : 1.1252 Moon sling tether 2 mass (tons) : 0.28906 Moon sling grapple 1 mass (tons) : 1.2041 Moon sling grapple 2 mass (tons) : 1.5386 Moon sling ballast 2 mass (tons) : 5.3512 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 141/173 9/6/2018 capture-orbit-sling Moon sling solar panel mass via Juno (tons) : 0.19524 Moon sling radiator mass = solar panel mass (tons) : 0.19524 Moon sling motor mass = solar panel mass (tons) : 0.19524 Moon sling hub mass = solar panel mass (tons) : 0.19524

Moon sling manufactured mass (tons) : 4.9380 Moon sling system mass, including ballast (tons) : 10.289

------Capture sling A (with C) throws a 1.455 ton payload to v_inf = 2640.0 m/s. ------

Capture sling A1 length (km) : 99.186 Capture sling A2 length (km) : 99.949 Capture sling A counterbalance ratio (derived) : 0.99236

Capture sling A1 tip diameter (mm) : 1.6436 Capture sling A1 hub diameter (mm) : 1.7548 Capture sling A2 hub diameter (mm) : 1.7548 Capture sling A2 tip diameter (mm) : 1.6420

Capture sling A1 taper ratio : 1.0676 Capture sling A2 taper ratio : 1.0687

Center of mass for empty capture sling A (m) : 6.0872e-1 2 Center of mass for full capture sling A (m) : 4.6330e-1 2

Capture sling A1 tip speed (m/s) : 986.42 Capture sling A1 tip accel (g's) : 1.0000 Capture sling A2 tip speed (m/s) : 994.01 !!!WARNING!!! Capture sling A2 tip accel (g's) : 1.0077 Capture sling A rotation period (minutes) : 10.530 Capture sling A full moment of inertia (kg*m^2) : 2.7047e13 Capture sling A empty moment of inertia (kg*m^2) : 7.2960e12

Capture sling A tether 1 mass (tons) : 0.35850 Capture sling A tether 1 gradient reinforcement (tons) : 0.010967 Capture sling A tether 2 mass (tons) : 0.36102 Capture sling A tether 2 gradient reinforcement (tons) : 0.011044 Capture sling A grapple 1 mass (tons) : 0.25089 Capture sling A ballast 1 mass (tons) : 0.0035661 Capture sling A grapple 2 mass (tons) : 0.25000 Capture sling A solar panel mass via Juno (tons) : 0.099326 Capture sling A radiator mass = solar panel mass (tons) : 0.099326 Capture sling A motor mass = solar panel mass (tons) : 0.099326 Capture sling A hub mass = solar panel mass (tons) : 0.099326

Capture sling A manufactured mass (tons) : 1.6177 Capture sling A system mass, including ballast (tons) : 1.6213

------Capture sling B throws a 1.000 ton payload to v_inf = 2640.0 m/s. ------

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 142/173 9/6/2018 capture-orbit-sling Capture sling B1 length (km) : 124.00 !!!WARNING!!! Capture sling B2 length (km) : 135.15 Capture sling B counterbalance ratio (derived) : 0.91753

Capture sling B1 tip diameter (mm) : 1.6810 Capture sling B1 hub diameter (mm) : 1.8243 Capture sling B2 hub diameter (mm) : 1.8243 Capture sling B2 tip diameter (mm) : 1.6554

Capture sling B1 taper ratio : 1.0852 Capture sling B2 taper ratio : 1.1020

Center of mass for empty capture sling B (m) : -4.7794e- 12 Center of mass for full capture sling B (m) : -8.5727e- 12

Capture sling B1 tip speed (m/s) : 1102.9 Capture sling B1 tip accel (g's) : 1.0000 !!!WARNING!!! Capture sling B2 tip speed (m/s) : 1202.1 !!!WARNING!!! Capture sling B2 tip accel (g's) : 1.0899 Capture sling B rotation period (minutes) : 11.774 Capture sling B full moment of inertia (kg*m^2) : 4.6842e13 Capture sling B empty moment of inertia (kg*m^2) : 1.4707e13

Capture sling B tether 1 mass (tons) : 0.47936 Capture sling B tether 1 gradient reinforcement (tons) : 0.014665 Capture sling B tether 2 mass (tons) : 0.51737 Capture sling B tether 2 gradient reinforcement (tons) : 0.015828 Capture sling B grapple 1 mass (tons) : 0.26244 Capture sling B ballast 1 mass (tons) : 0.049747 Capture sling B grapple 2 mass (tons) : 0.25000 Capture sling B solar panel mass via Juno (tons) : 0.13759 Capture sling B radiator mass = solar panel mass (tons) : 0.13759 Capture sling B motor mass = solar panel mass (tons) : 0.13759 Capture sling B hub mass = solar panel mass (tons) : 0.13759

Capture sling B manufactured mass (tons) : 2.0595 Capture sling B system mass, including ballast (tons) : 2.1093

------Capture sling C (on A) throws a 0.4551 ton payload to v_inf = 2640.0 m/s. ------

Capture sling C1 length (km) : 99.186 Capture sling C2 length (km) : 99.949 Capture sling C counterbalance ratio (derived) : 0.99236

Capture sling C1 tip diameter (mm) : 1.2177 Capture sling C1 hub diameter (mm) : 1.3001 Capture sling C2 hub diameter (mm) : 1.3001 Capture sling C2 tip diameter (mm) : 1.2165

Capture sling C1 taper ratio : 1.0676 Capture sling C2 taper ratio : 1.0687

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 143/173 9/6/2018 capture-orbit-sling Center of mass for empty capture sling C (m) : 4.3380e-1 2 Center of mass for full capture sling C (m) : 8.4406e-1 2

Capture sling C1 tip speed (m/s) : 986.42 Capture sling C1 tip accel (g's) : 1.0000 Capture sling C2 tip speed (m/s) : 994.01 !!!WARNING!!! Capture sling C2 tip accel (g's) : 1.0077 Capture sling C rotation period (minutes) : 10.530 Capture sling C full moment of inertia (kg*m^2) : 1.4846e13 Capture sling C empty moment of inertia (kg*m^2) : 5.8578e12

Full capture sling rot. ang. mom. (kg*m^2/s) : 0.0002704 6 Empty capture sling rot. ang. mom. (kg*m^2/s) : 0.0002094 2

Capture sling C tether 1 mass (tons) : 0.19678 Capture sling C tether 1 gradient reinforcement (tons) : 0.0060198 Capture sling C tether 2 mass (tons) : 0.19816 Capture sling C tether 2 gradient reinforcement (tons) : 0.0060622 Capture sling C grapple 1 mass (tons) : 0.13771 Capture sling C ballast 1 mass (tons) : 0.095775 Capture sling C grapple 2 mass (tons) : 0.13792 Capture sling C ballast 2 mass (tons) : 0.092402 Capture sling C solar panel mass via Juno (tons) : 0.054519 Capture sling C radiator mass = solar panel mass (tons) : 0.054519 Capture sling C motor mass = solar panel mass (tons) : 0.054519 Capture sling C hub mass = solar panel mass (tons) : 0.054519

Capture sling C manufactured mass (tons) : 0.88865 Capture sling C system mass, including ballast (tons) : 1.0768

------Total tether mass (tons) : 3.5901 Total grapple mass (tons) : 4.0317 Total solar panel mass (tons) : 0.48668 Total radiator mass = solar panel mass (tons) : 0.48668 Total motor mass = solar panel mass (tons) : 0.48668 Total hub mass = solar panel mass (tons) : 0.48668 Total ballast mass (tons) : 5.5927 ------Total manufactured mass (tons) : 9.5685 Total manufactured mass / total payload mass : 3.2880 Total system mass, including ballast (tons) : 15.161

Moon sling needs to throw 0.2415 tons of ballast to the capture slin g. This requires 1 throws, so the setup takes 18.94 days

After setup, the moon sling throws to the capture sling every 18.94 d ays and the capture sling can throw every 18.94 days.

------

Rocket A sends a 1.910 ton payload through a delta-v of 1846. m/s. It http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 144/173 9/6/2018 capture-orbit-sling s total mass (structure plus propellant, no payload) is 1.563 tons. I ts structural mass is 0.2039 tons, which is 15.00% of its propellant mass of 1.359 tons. Its propellant mass fraction is 0.3914 and its pa yload fraction is 0.5499.

Rocket B sends a 1.000 ton payload through a delta-v of 1846. m/s. It s total mass (structure plus propellant, no payload) is 0.8185 tons. Its structural mass is 0.1068 tons, which is 15.00% of its propellant mass of 0.7117 tons. Its propellant mass fraction is 0.3914 and its p ayload fraction is 0.5499.

Total rocket mass is 2.382 tons, or 0.8185 times the combined payload mass of 2.910 tons.

total manufactured mass / total rocket mass : 4.0173 ------

The moon sling cycle starts at the moment it throws a payload. It tak es 2.5002 days to spin down and then 1.0000 days to attach a new payl oad. The moon sling then waits (retracted and shielded) for 5.4438 da ys. It takes 10.000 days to spin up, which is the end of the cycle. T he moon sling cycle takes 18.944 days, which is 3.0000 resonance revi sit times.

The capture sling cycle starts at the moment the moon sling throws it s payload to the capture sling. Each capture throw requires one moon throw. The capture sling takes 3.0000 days to rendezvous with and att ach the payload. The capture sling then waits (retracted and shielded by the counterbalance masses) for 0.44843 days. It takes 10.000 days to spin up and throw at periapsis, then 3.1398 days to spin down. The capture sling then waits (retracted but largely unshielded) for 2.355 8 days until the next revisit time after the moon sling finishes spin ning up, which is the end of the cycle. The capture sling cycle takes 18.944 days, which is 3.0000 resonance revisit times.

------

Moon sling spinup time (days) : 10.000 Moon sling spindown time (days) : 2.5002 Moon sling average power (kW) : 3.0430 , Alt. calc: 3.0430 Moon sling average torque (N*m) : 502100. Moon sling const accel spinup revolutions : 833.39 Moon sling const accel spindown revolutions : 208.36

Capture sling A spinup time (days) : 10.000 Capture sling A spindown time (days) : 3.1398 Capture sling A average power (kW) : 1.5481 , Alt. calc: 1.5481 Capture sling A average torque (N*m) : 311330. Capture sling A const accel spinup revolutions : 683.78 Capture sling A const accel spindown revolutions : 214.69

Capture sling B spinup time (days) : 10.000 Capture sling B spindown time (days) : 3.1398 Capture sling B average power (kW) : 2.1446 , Alt. calc: 2.1446 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 145/173 9/6/2018 capture-orbit-sling Capture sling B average torque (N*m) : 482220. Capture sling B const accel spinup revolutions : 611.54 Capture sling B const accel spindown revolutions : 192.01

Capture sling C spinup time (days) : 10.000 Capture sling C spindown time (days) : 3.1398 Capture sling C average power (kW) : 0.84974 , Alt. calc: 0.84974 Capture sling C average torque (N*m) : 170890. Capture sling C const accel spinup revolutions : 683.78 Capture sling C const accel spindown revolutions : 214.69

Total required average sling power (kW) : 7.5854 ------

Moon sling throws payload mass (tons) : 4.82 , wh ich is 20.0 % of the total. Moon sling throws balance mass (tons) : 19.3 , wh ich is 80.0 % of the total.

1 moon payload becomes the capture sling payloads and counterbalance s: Capture sling A throws payload mass (tons) : 1.46 , wh ich is 6.04 % of the total. Capture sling A throws balance mass (tons) : 1.44 , wh ich is 6.00 % of the total. Capture sling B throws payload mass (tons) : 1.00 , wh ich is 4.15 % of the total. Capture sling B throws balance mass (tons) : 0.918 , w hich is 3.81 % of the total. Capture sling C throws payload mass (tons) : 0.455 , w hich is 1.89 % of the total. Capture sling C throws balance mass (tons) : 0.452 , w hich is 1.88 % of the total.

Payloads and balances thrown from moon sling rotate once every 8.64 minutes. Payloads and balances thrown from capture sling A rotate once every 1 0.5 minutes. Payloads and balances thrown from capture sling B rotate once every 1 1.8 minutes.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 146/173 9/6/2018 capture-orbit-sling

In [30]: #Old code- newer 3D plot above is more useful.

#Examine the possibility of collisions by plotting the position of Ph obos versus the capture orbit sling. #Plot capture sling with its line of apsides pointing out of the scre en, parallel to the intersection between #the ecliptic plane and the plane that Phobos and Deimos orbit in (e. g. Mars's equatorial plane). #Here, the argument of periapsis of the capture sling is 0 degrees. print moon_frame_string

#Calculate cosine of capture orbit's true anomaly at Phobos. var('cos_ta') #Resets cos_ta so it's just a variable again. eq4 = r == a*(1-ecc^2)/(1+ecc*cos_ta) # https://en.wikipedia.org/wik i/True_anomaly#Radius_from_true_anomaly #soln4 = solve(eq4.subs(r=periapsis_phobos,a=a_capture),cos_ta) #capt ure orbit's true anomaly at Phobos soln4 = solve(eq4.subs(r=periapsis_phobos-r_max,a=a_capture),cos_ta) #capture orbit's true anomaly 1 sling length below Phobos (just to be safer) cos_ta = soln4[0].rhs() sin_ta = sqrt(1-cos_ta^2) print 'cosine of capture orbit\'s true anomaly at Phobos =',cos_ta.n ()

gfx = Graphics() #Lots of graphics code on the web says "g = Graphics ()" but that can't be used here because g is already 9.81 m/s^2. frame_size=4.0 #r_max = 2000000 #Just to see what it looks like, redefine r_max, in meters. angle=inc_phobos #Use the lower Phobos angle because those collisions are more likely. gfx += circle((0,0), radius_mars, rgbcolor=(1,0,0)) #Mars is plotted as a red circle. gfx += circle((periapsis_phobos*sin_ta*cos(angle),periapsis_phobos*si n_ta*sin(angle)), r_max, rgbcolor=(1,0,1)) #Max sling extension when sling COM is at Phobos's periapsis. gfx += line([(-frame_size*radius_mars,r_max), (frame_size*radius_mars ,r_max)], rgbcolor=(1,0,1)) #Max sling extension from Phobos or Deimo s. gfx += line([(-b_capture*cos(angle),-b_capture*sin(angle)), (b_captur e*cos(angle),b_capture*sin(angle))]) #Plot limits of capture sling ce nter of mass motion in ecliptic plane.

#Calculate cosine of capture orbit's true anomaly at Deimos. var('cos_ta') #Resets cos_ta so it's just a variable again. eq4 = r == a*(1-ecc^2)/(1+ecc*cos_ta) # https://en.wikipedia.org/wik i/True_anomaly#Radius_from_true_anomaly #soln4 = solve(eq4.subs(r=periapsis_deimos,a=a_capture),cos_ta) #capt ure orbit's true anomaly at Deimos soln4 = solve(eq4.subs(r=periapsis_deimos,a=a_capture),cos_ta) #captu re orbit's true anomaly at Deimos. cos_ta = soln4[0].rhs() sin_ta = sqrt(1-cos_ta^2) print 'cosine of capture orbit\'s true anomaly at Deimos =',cos_ta.n () http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 147/173 9/6/2018 capture-orbit-sling

#Max sling extension when sling COM is at Deimos's periapsis. gfx += circle((periapsis_deimos*sin_ta*cos(angle),periapsis_deimos*si n_ta*sin(angle)), r_max, rgbcolor=(0,0,1))

if textonly == 0: gfx.show(xmin=-frame_size*radius_mars, xmax=frame_s ize*radius_mars, ymin=-frame_size*radius_mars, ymax=frame_size*radius _mars, figsize=[7,7]) else: print 'Textonly =',textonly,'so this image wasn\'t displayed.' Final vectors in this cell are calculated in a frame where Deimos sta ys in the x-y plane and the capture slings are inclined by 27.58 degr ees with a longitude of ascending node arbitrarily set to 0.0000 degr ees.

cosine of capture orbit's true anomaly at Phobos = -0.363741529069014 cosine of capture orbit's true anomaly at Deimos = -0.993853976119485

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 148/173 9/6/2018 capture-orbit-sling

In [31]: #Plot capture sling with its line of apsides lying in the screen, per pendicular to the intersection between #the ecliptic plane and the plane that Phobos and Deimos orbit in (e. g. Mars's equatorial plane). #Here, the argument of periapsis of the capture sling is 90 degrees. print moon_frame_string gfx = Graphics() angle=inc_phobos #Use the lower Phobos angle because those collisions are more likely here. gfx += circle((0,0), radius_mars, rgbcolor=(1,0,0)) #Mars is plotted as a red circle. gfx += circle((a_phobos,0), r_max, rgbcolor=(1,0,1)) #Plot sling arou nd Phobos. #gfx += circle((a_deimos,0), r_max, rgbcolor=(0,0,0)) #Don't bother p lotting Deimos or zooming out to see it. If the sling misses Phobos i t'll certainly miss Deimos. gfx += line([(-periapsis_capture*cos(angle),-periapsis_capture*sin(an gle)), (apoapsis_capture*cos(angle),apoapsis_capture*sin(angle))]) #P lot capture sling center of mass in ecliptic plane. gfx += line([(0,-r_max), (apoapsis_capture,apoapsis_capture*tan(angle )-r_max)]) #Plot sling limits. gfx += line([(0,r_max), (apoapsis_capture,apoapsis_capture*tan(angle) +r_max)]) if textonly == 0: gfx.show(xmin=-periapsis_capture*cos(angle), xmax=( a_phobos+r_max)*1.1, ymin=-2*radius_mars, ymax=2*radius_mars, figsize =[7,7]) else: print 'Textonly =',textonly,'so this image wasn\'t displayed.'

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 149/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where Deimos sta ys in the x-y plane and the capture slings are inclined by 27.58 degr ees with a longitude of ascending node arbitrarily set to 0.0000 degr ees.

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 150/173 9/6/2018 capture-orbit-sling

In [32]: #Capture orbit speed at the moon (either Phobos or Deimos) capture_speed_moon = sqrt(mu*(2/a_moon-1/a_capture))

#Capture orbit eccentricity ecc = (apoapsis_capture - periapsis_capture) / (apoapsis_capture + pe riapsis_capture) print 'capture orbit eccentricity =',ecc

#Calculate cosine of capture orbit's true anomaly at the moon (either Phobos or Deimos). var('cos_ta') #Resets cos_ta so it's just a variable again. var('ta fpa') #true anomaly, flight path anomaly eq3 = r == a*(1-ecc^2)/(1+ecc*cos(ta)) # https://en.wikipedia.org/wik i/True_anomaly#Radius_from_true_anomaly soln3 = solve(eq3.subs(r=a_moon,a=a_capture),ta) ta = soln3[0].rhs() #True anomaly of the capture orbit at the moon. print 'capture orbit\'s true anomaly at the moon =',(ta*180/pi).n()

AP = pi - ta print 'capture orbit\'s argument of periapsis which hits the moon =', (AP*180/pi).n()

#Capture orbit's flight path angle at the moon. fpa = my_arccos((1 + ecc*cos(ta))/sqrt(1+ecc^2+2*ecc*cos(ta)),'fpa') print 'capture orbit\'s flight path angle at the moon =',(fpa*180/pi) .n()

capture orbit eccentricity = 0.717360803768281 capture orbit's true anomaly at the moon = 173.712720193813 capture orbit's argument of periapsis which hits the moon = 6.2872798 0618746 capture orbit's flight path angle at the moon = 15.3109755656732

In [33]: fpa(ta) = arccos((1 + ecc*cos(ta*pi/180))/sqrt(1+ecc^2+2*ecc*cos(ta*p i/180))) angles = 10 if textonly == 0: plot(fpa,(ta,-angles,angles),axes_labels=['true ano maly (degrees)','flight path angle (degrees)']) else: print 'Textonly =',textonly,'so this image wasn\'t displayed.'

In [34]: if textonly == 0: plot(fpa+ta,(ta,-angles,angles),axes_labels=['true anomaly (degree s)','flight path angle + true anomaly (degrees)']) #sum_fpa_ta(ta) = ta + arccos((1 + ecc*cos(ta*pi/180))/sqrt(1+ecc^2 +2*ecc*cos(ta*pi/180))) #plot(sum_fpa_ta,(ta,-angles,angles),axes_labels=['true anomaly (de grees)','flight path angle + true anomaly (degrees)']) else: print 'Textonly =',textonly,'so this image wasn\'t displayed.'

Appendix A: Counterbalance mass ratios above 4.6033 cause collisions. (Go back to the top). http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 151/173 9/6/2018 capture-orbit-sling

In [35]: #Consider an asymmetric double sling where each mass is released with equal and opposite linear momentum. #Does the longer sling (radius r1) hit the heavier mass (m2) after i t's released from the shorter sling (radius r2 < r1)? var('t v1 v2 r1 r2 mass_ratio') mass_ratio = 2 #mass_ratio = 4.60333884875169 #mass_ratio = m2/m1. Since m1*v1 = m2* v2, mass_ratio = v1/v2. Since v1/r1 = v2/r2 (= omega), mass_ratio = r 1/r2. v2 = 1000 r2 = 1000 theta1 = v1*t/r1 - pi #theta (angle) of end of sling that held mass 1, where t=0 is the time of release. theta2 = arctan(v2*t/r2) #theta (angle) of mass 2, where t=0 is the t ime of release.

#If a closed form solution existed, this is how to solve for it: #eq_thetas = theta1 == theta2 #soln_thetas = solve(eq_thetas.subs(v1=mass_ratio*v2, r1 = mass_ratio *r2),t) #soln_thetas #time_collision = soln_thetas[0].rhs() #print 'time after release when thetas are equal =',t.n() #distance_m2 = sqrt(r2^2 + (v2*time_collision)^2) #distance_m2

theta1 = theta1.subs(v1=mass_ratio*v2, r1=mass_ratio*r2) theta2 = theta2.subs(v1=mass_ratio*v2, r1=mass_ratio*r2) theta1 - theta2 time_collision = find_root(theta1 - theta2,0,3*pi/2*mass_ratio*r2/(ma ss_ratio*v2)) time_collision distance_m2 = sqrt(r2^2 + (v2*time_collision)^2) distance_m2 r2*mass_ratio print 'Dangerous mass ratio:',distance_m2/r2.n() Dangerous mass ratio: 4.60333884875169

In [36]: #Thinking about the view from various orbits. How wide is Mars in the sky, in degrees? https://en.wikipedia.org/wiki/Angular_diameter print (2*arcsin(radius_mars/a_deimos)*180/pi).n() print (2*arcsin(radius_mars/a_phobos)*180/pi).n() print (2*arcsin(radius_mars/(radius_mars+lmo_alt))*180/pi).n()

16.6441331547577 42.4709021766231 126.920842043550

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 152/173 9/6/2018 capture-orbit-sling

In [37]: print 'Test xyz2kepler:' #xyz2kepler? #rotateX? #rotateY? #rotateZ? #xyz2spherical? #spherical2xyz? #compare_vectors?

r4t = a_moon #Radial distance for test. cv4t = sqrt(mu/r4t) #Circular speed for test, given r4t. pv4t = sqrt(2*mu/r4t) #Parabolic speed for test, given r4t. #v4t = pv4t*9e-18 #Speed to be used for test THAT CAUS ES LOTS OF PROBLEMS. v4t = pv4t*1.0 #Speed to be used for test.

#Rotated trajectory fpa4t = 0.0*pi/180 #Flight path angle for test. Conver t degrees to radians. inc4t = 0.0*pi/180 #Inclination for test. Convert degr ees to radians. inc4t = inc_moon #Inclination. Already in radians. LAN4t = 100.0*pi/180 #Longitude of ascending node for test. Convert degrees to radians. AP4t = 20.0*pi/180 #Argument of periapsis for test. C onvert degrees to radians. p_in_4t = vector([r4t,0,0]) #Position vector used for test. v_in_4t = vector([0,v4t,0]) #Velocity vector used for test.

#Radial trajectory. #p_in_4t = vector([0,-r4t,0]) #v_in_4t = vector([0,v4t,0])

#Capture orbit, tested at periapsis and apoapsis. #p_in_4t = vector([periapsis_capture,0,0]) #position vector used fo r test. Velocity vector is here: #v_in_4t = vector([0,capture_speed_periapsis,0]) #p_in_4t = vector([-apoapsis_capture,0,0]) #position vector used fo r test. Velocity vector is here: #v_in_4t = vector([0,-capture_speed_apoapsis,0])

#print 'Position vector =',p_in_4t.n() #print 'Velocity vector =',v_in_4t.n()

fpa_rotation = rotateZ(fpa4t) #print 'Flight path angle rotation matrix =' #fpa_rotation.n()

v_in_4to = v_in_4t v_in_4t = fpa_rotation*v_in_4t #print 'Velocity vector =',v_in_4t.n() #dv = compare_vectors(v_in_4to,v_in_4t)

#https://en.wikipedia.org/wiki/Orbital_elements#Euler_angle_transform ations #http://web.archive.org/web/20171123040031/http://www.physics.csbsju. http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 153/173 9/6/2018 capture-orbit-sling edu/orbit/orbit.3d.html #"I, J is in the equatorial plane of the central body. I is in the di rection of the vernal equinox. J is perpendicular to I and with I def ines the reference plane. K is perpendicular to the reference plane. Orbital elements of bodies (planets, comets, asteroids,...) in the s olar system usually use the ecliptic as that plane." #"x, y are in the orbital plane and with x in the direction to the pe ricenter (periapsis). z is perpendicular to the plane of the orbit. y is mutually perpendicular to x and z." #Check- is this orbit not inclined? if inc4t < tolerance or inc4t > pi - tolerance: if abs(LAN4t) > tolerance: #Even if a nonzero LAN is specified, set LAN=0 because that's true by definition for orbits that aren't incli ned. print warnstring,'Since this orbit isn\'t inclined, LAN of',LAN4t ,'was set to zero.' LAN4t=0 #"the transformation from the I, J, K coordinate frame to the x, y, z frame": #inverse_rotation = rotateZ(-AP)*rotateX(-inc)*rotateZ(-LAN) #The capture orbit's position and velocity vectors were initially def ined in the frame x, y, z. Now we need to rotate to the frame I, J, K. That's the inverse of the rotation given in the references above. So reverse the order and sign of the rotations: rotation = rotateZ(LAN4t)*rotateX(inc4t)*rotateZ(AP4t) #print 'Rotation matrix =' #print rotation.n()

p_in_4t = rotation*p_in_4t v_in_4t = rotation*v_in_4t

kepler = xyz2kepler(p_in_4t,v_in_4t) (kepler).n()

print '\nPosition vector at periapsis =',p_in_4t.n() print 'Velocity vector at periapsis =',v_in_4t.n()

print '\nTest kepler2xyz:' #kepler2xyz?

kepler[5] = 10000 #adjust time since periapsis.

p_out_4t,v_out_4t = kepler2xyz(kepler)

print '\nPosition vector after',kepler[5].n(digits=5),'seconds =',p_o ut_4t.n() print 'Velocity vector after',kepler[5].n(digits=5),'seconds =',v_out _4t.n()

#If the first trajectory was parabolic, make a second one that's "jus t barely hyperbolic": v2_in_4t = (1.0+10*tolerance)*v_in_4t kepler2 = xyz2kepler(p_in_4t,v2_in_4t) kepler2[5] = kepler[5] #adjust time since periapsis. p2_out_4t,v2_out_4t = kepler2xyz(kepler2)

xyz2kepler(p_out_4t,v_out_4t) http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 154/173 9/6/2018 capture-orbit-sling

print '\nCompare position vectors:' dv = compare_vectors(p_out_4t,p2_out_4t)

print '\nCompare velocity vectors:' dv = compare_vectors(v_out_4t,v2_out_4t)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 155/173 9/6/2018 capture-orbit-sling

Test xyz2kepler: ------Parabolic trajectory Position vector (km) = (-10833.5478394527, 2 0478.0780800630, 3715.41566949513) Velocity vector (km/s) = (-1.45376858821605, - 0.919909591255915, 0.831268736298597) Radial distance (km) = 23463.2000000000 Speed (km/s) = 1.91067644464357 Radial velocity (km/s) = 8.12910699654139e-17 Flight path angle (degrees) = 0.000000000000000 Specific relative angular momentum vector = (2.04406325973091e10, 3.60423502965454e9, 3.97362712246738e10) Specific relative angular momentum (m^2/s) = 4.48305835559610e10 Specific orbital energy (kJ/kg) = -6.98491930961609e-13 Eccentricity vector = (-0.461725077544953, 0.872774305297787, 0.158350765006270) Eccentricity = 1.00000000000000 , Al t. calc. = 0.999999999999999 Semi-latus rectum (km) = 46926.4000000000 , Al t. calc. = 0.000000000000000 Periapsis altitude (km) = 20067.2000000000 Periapsis (km) = 23463.2000000000 Inclination (degrees) = 27.5800000000000 Longitude of the ascending node (degrees) = 100.000000000000 , Al t. calc. = 100.000000000000 Argument of periapsis (degrees) = 19.9999975851635 , Al t. calc. = 20.0000000000000 Mean anomaly (degrees) = 2.43768914102554e-15 Eccentric anomaly (degrees) = 2.43768914102554e-15 True anomaly (degrees) = 4.87537828205107e-15 , Alt. calc. = 2.41483653945147e-6 Time since periapsis (hours) = 2.90257384956943e-16 returns [a, ecc, inc, AP, LAN2, tper, p]

Position vector at periapsis = (-1.08335478394527e7, 2.04780780800630 e7, 3.71541566949513e6) Velocity vector at periapsis = (-1453.76858821605, -919.909591255915, 831.268736298597)

Test kepler2xyz: ------Parabolic trajectory Position vector (km) = (-23048.7868495659, 8639.34623579946, 11072. 8444292497) Velocity vector (km/s) = (-0.966417705366111, -1.36176599074238, 0.62 0649929569519) Radial distance (km) = 26990.6050760836 , Alt. calcs. = 0.000000000 000000 , 0.000000000000000 , 26990.6050760836 Speed (km/s) = 1.78145337571948 Radial velocity (km/s) = 0.644014755453581

Position vector after 10000. seconds = (-2.30487868495659e7, 8.639346 23579946e6, 1.10728444292497e7) Velocity vector after 10000. seconds = (-966.417705366111, -1361.7659 9074238, 620.649929569519) ------http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 156/173 9/6/2018 capture-orbit-sling Hyperbolic trajectory Position vector (km) = (-10833.5478394527, 2 0478.0780800630, 3715.41566949513) Velocity vector (km/s) = (-1.45378312590194, - 0.919918790351828, 0.831277048985960) Radial distance (km) = 23463.2000000000 Speed (km/s) = 1.91069555140802 Radial velocity (km/s) = -2.03227674913535e-17 Flight path angle (degrees) = 1.20741826972573e-6 Specific relative angular momentum vector = (2.04408370036351e10, 3.60427107200484e9, 3.97366685873860e10) Specific relative angular momentum (m^2/s) = 4.48310318617965e10 Specific orbital energy (kJ/kg) = 0.0365070272954181 Hyperbolic excess velocity at infinity (km/s) = 0.00854482618845089 , C3 (km/s)^2: 0.0000730140545908362 Periapsis vs infinity deflection (degrees) = 89.4875382285622 Eccentricity vector = (-0.461743546640400, 0.872809216444554, 0.158357099068541) Eccentricity = 1.00004000020000 , Al t. calc. = 1.00004000020000 Semi-latus rectum (km) = 46927.3385326926 , Al t. calc. = 46927.3385326521 Semi-minor axis (km) = -5.24657036586308e6 Semi-major axis (km) = -5.86577067114080e8 Periapsis altitude (km) = 20067.2000000098 Periapsis (km) = 23463.2000000098 Inclination (degrees) = 27.5800000000000 Longitude of the ascending node (degrees) = 100.000000000000 , Al t. calc. = 100.000000000000 Argument of periapsis (degrees) = 20.0001064858035 , Al t. calc. = 20.0000000000000 Mean anomaly (degrees) = -2.18026459245927e-22 Eccentric anomaly (degrees) = -5.45063422797031e-18 True anomaly (degrees) = -1.21880800639446e-15 , Alt. calc. = 359.999893514197 Time since periapsis (hours) = -7.25614437670051e-17 returns [a, ecc, inc, AP, LAN2, tper, p] ------Hyperbolic trajectory Position vector (km) = (-23048.9455436952, 8639.21594898299, 11072. 9378802099) Velocity vector (km/s) = (-0.966430751924128, -1.36177442747966, 0.62 0657406060744) Radial distance (km) = 26990.7372294344 , Alt. calcs. = 26990.73722 94129 , 26990.7372294111 , 26990.7372294344 Speed (km/s) = 1.78146950726086 Radial velocity (km/s) = 0.644037514592159 ------Parabolic trajectory Position vector (km) = (-23048.7868495659, 8 639.34623579946, 11072.8444292497) Velocity vector (km/s) = (-0.966417705366111, -1.36176599074238, 0.620649929569519) Radial distance (km) = 26990.6050760836 Speed (km/s) = 1.78145337571948 Radial velocity (km/s) = 0.644014755453581 Flight path angle (degrees) = 21.1930126014065 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 157/173 9/6/2018 capture-orbit-sling Specific relative angular momentum vector = (2.04406325973091e10, 3.60423502965454e9, 3.97362712246738e10) Specific relative angular momentum (m^2/s) = 4.48305835559610e10 Specific orbital energy (kJ/kg) = -2.32830643653870e-13 Eccentricity vector = (-0.461725045476852, 0.872774325589705, 0.158350746669645) Eccentricity = 1.00000000000000 , Al t. calc. = 0.999999999999999 Semi-latus rectum (km) = 46926.4000000000 , Al t. calc. = 0.000000000000000 Periapsis altitude (km) = 20067.2000000000 Periapsis (km) = 23463.2000000000 Inclination (degrees) = 27.5800000000000 Longitude of the ascending node (degrees) = 100.000000000000 , Al t. calc. = 100.000000000000 Argument of periapsis (degrees) = 19.9999975851635 , Al t. calc. = 19.9999975851635 Mean anomaly (degrees) = 23.3288077272363 Eccentric anomaly (degrees) = 22.2155300083768 True anomaly (degrees) = 42.3860252028129 , Al t. calc. = 42.3860252028129 Time since periapsis (hours) = 2.77777777777778 returns [a, ecc, inc, AP, LAN2, tper, p]

Compare position vectors: ------#1 = (-2.30487868495659e7, 8.63934623579946e6, 1.10728444292497e7) #2 = (-2.30489455436952e7, 8.63921594898299e6, 1.10729378802099e7) delta = (-158.694129355252, -130.286816466600, 93.4509602226317) #1 magnitude, inclination, azimuth (deg) = (2.69906050760836e7, 6 5.7795790422118, 159.452561246828) #2 magnitude, inclination, azimuth (deg) = (2.69907372294344e7, 6 5.7794877145005, 159.452974871242) delta magnitude, inclination, azimuth (deg) = (225.591584956874, 65.5 280044514173, -140.614233972311) angle between vectors #1 and #2 (degrees) = 0.000388112202576242 Normalized cross product = (0.455952855757493, 0.0803967904347765, 0. 886365246055131)

Compare velocity vectors: ------#1 = (-966.417705366111, -1361.76599074238, 620.649929569519) #2 = (-966.430751924128, -1361.77442747966, 620.657406060745) delta = (-0.0130465580169812, -0.00843673727740679, 0.007476491225020 25) #1 magnitude, inclination, azimuth (deg) = (1781.45337571948, 69.6 108059769490, -125.362549506137) #2 magnitude, inclination, azimuth (deg) = (1781.46950726086, 69.6 107422826181, -125.362747032490) delta magnitude, inclination, azimuth (deg) = (0.0172420744986282, 6 4.3025483330838, -147.110712056600) angle between vectors #1 and #2 (degrees) = 0.000195804754040588 Normalized cross product = (-0.455952855779612, -0.0803967903679473, -0.886365246049814)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 158/173 9/6/2018 capture-orbit-sling

In [38]: print 'Test rotateV:' #rotateV? var('angle0') angle0 = pi/6 unit0 = vector([1,0,0]) print 'rotateX =\n',rotateX(angle0).n() print 'rotateV =\n',rotateV(unit0,angle0).n() unit0 = vector([0,1,0]) print 'rotateY =\n',rotateY(angle0).n() print 'rotateV =\n',rotateV(unit0,angle0).n() unit0 = vector([0,0,1]) print 'rotateZ =\n',rotateZ(angle0).n() print 'rotateV =\n',rotateV(unit0,angle0).n()

Test rotateV: rotateX = [ 1.00000000000000 0.000000000000000 0.000000000000000] [ 0.000000000000000 0.866025403784439 -0.500000000000000] [ 0.000000000000000 0.500000000000000 0.866025403784439] rotateV = [ 1.00000000000000 0.000000000000000 0.000000000000000] [ 0.000000000000000 0.866025403784439 -0.500000000000000] [ 0.000000000000000 0.500000000000000 0.866025403784439] rotateY = [ 0.866025403784439 0.000000000000000 0.500000000000000] [ 0.000000000000000 1.00000000000000 0.000000000000000] [-0.500000000000000 0.000000000000000 0.866025403784439] rotateV = [ 0.866025403784439 0.000000000000000 0.500000000000000] [ 0.000000000000000 1.00000000000000 0.000000000000000] [-0.500000000000000 0.000000000000000 0.866025403784439] rotateZ = [ 0.866025403784439 -0.500000000000000 0.000000000000000] [ 0.500000000000000 0.866025403784439 0.000000000000000] [ 0.000000000000000 0.000000000000000 1.00000000000000] rotateV = [ 0.866025403784439 -0.500000000000000 0.000000000000000] [ 0.500000000000000 0.866025403784439 0.000000000000000] [ 0.000000000000000 0.000000000000000 1.00000000000000]

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 159/173 9/6/2018 capture-orbit-sling

In [39]: print 'These are old, discarded calculations that predate most of the variables used above such as grapples and ballast, etc.' print capture_frame_string

#Consider throwing the counterbalance mass into an orbit with periaps is at the surface of Mars. #This wastes the energy and momentum in the counterbalance mass and p oses dangers to assets on the surface of Mars, but prevents the count erbalance mass from becoming a possible navigation hazard to spacecra ft, slings or other orbital assets.

a_dump = (periapsis_capture + polar_r_pl)/2 #Use polar radius as peri apsis just to be sure it crashes. v_dump = sqrt(mu*(2/periapsis_capture-1/a_dump)) delta_v_capture_to_dump = capture_speed_periapsis - v_dump

print 'capture orbit speed at periapsis (m/s) =',capture_speed_periap sis print 'dump orbit speed at periapsis (m/s) =',v_circ_at_periapsis print 'delta-v from capture orbit to dump orbit (m/s) =',delta_v_capt ure_to_dump print 'required tip speed of capture sling =',v_tip_capture_payload1 print 'mass ratio (counterbalance mass divided by payload mass) =',v_ tip_capture_payload1/delta_v_capture_to_dump

if v_tip_capture_payload1/delta_v_capture_to_dump > dmr: print '!!! DANGER !!! mass ratio 2',v_tip_capture_payload1/delta_v_ capture_to_dump,'is higher than the safe limit of',dmr,'!!!' if v_tip_capture_payload1/delta_v_capture_to_dump < 1/dmr: print '!!! DANGER !!! mass ratio 2',v_tip_capture_payload1/delta_v_capture_to_d ump,'is lower than the safe limit of',1/dmr,'!!!'

p_i = com_cs_pos0 #Used to be vector([0,-periapsis_capture,0]) v_i = com_cs_vel0 - delta_v_capture_to_dump*com_cs_vel0/com_cs_vel0.n orm() #Used to be vector([capture_speed_periapsis - delta_v_capture_t o_dump,0,0]) kepler = xyz2kepler(p_i,v_i) (kepler).n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 160/173 9/6/2018 capture-orbit-sling

These are old, discarded calculations that predate most of the variab les used above such as grapples and ballast, etc. Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

capture orbit speed at periapsis (m/s) = 4331.39333469434 dump orbit speed at periapsis (m/s) = 3305.19320027322 delta-v from capture orbit to dump orbit (m/s) = 1151.85799394513 required tip speed of capture sling = 1036.86561791447 mass ratio (counterbalance mass divided by payload mass) = 0.90016792 2925287 ------Elliptical orbit: using true anomaly. Position vector (km) = (3920.46738022426, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.000000000000000, 3.17953534074921, 0.000000000000000) Radial distance (km) = 3920.46738022426 Speed (km/s) = 3.17953534074921 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 1.85846088569065 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.24652645876775e10) Specific relative angular momentum (m^2/s) = 1.24652645876775e10 Specific orbital energy (kJ/kg) = -5869.57959959574 Eccentricity vector = (-0.0745912280035342, 0.000000000000000, 0.000000000000000) Eccentricity = 0.0745912280035340 , Alt. calc. = 0.0745912280035342 Semi-latus rectum (km) = 3628.03490398553 , Al t. calc. = 3628.03490398553 Semi-minor axis (km) = 3638.17014020966 Semi-major axis (km) = 3648.33369011213 !!DANGER!! Periapsis is below surface by (km) = 19.7999999999972 Periapsis (km) = 3376.20000000000 Apoapsis altitude (km) = 524.467380224260 Apoapsis (km) = 3920.46738022426 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 180.000000000000 , Al t. calc. = 180.000000000000 Mean anomaly (degrees) = 180.000000000000 Eccentric anomaly (degrees) = 180.000000000000 True anomaly (degrees) = 180.000000000000 , Al t. calc. = 180.000000000000 Time since periapsis (hours) = 0.929230442845326 returns [a, ecc, inc, AP, LAN2, tper, p]

Out[39]: (3.64833369011213e6, 0.0745912280035340, 0.000000000000000, 3.1415926 5358979, 0.000000000000000, 3345.22959424317, 3.62803490398553e6)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 161/173 9/6/2018 capture-orbit-sling

Appendix B: Indirect paths from the moon to the capture orbit. (Go back to the top).

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 162/173 9/6/2018 capture-orbit-sling

In [40]: #Option 1 - throw capture orbit sling directly into the (non-incline d) capture orbit print capture_frame_string

#Capture orbit speed at the moon (either Phobos or Deimos) capture_speed_moon = sqrt(mu*(2/a_moon-1/a_capture))

#Capture orbit eccentricity ecc = (apoapsis_capture - periapsis_capture) / (apoapsis_capture + pe riapsis_capture) print 'capture orbit eccentricity =',ecc

#Calculate cosine of capture orbit's true anomaly at the moon (either Phobos or Deimos). var('r cos_ta') #Resets r and cos_ta so they're variables again rathe r than numbers. eq2 = r == a*(1-ecc^2)/(1+ecc*cos_ta) # https://en.wikipedia.org/wik i/True_anomaly#Radius_from_true_anomaly eq2 soln2 = solve(eq2.subs(r=a_moon,a=a_capture),cos_ta) cos_ta = soln2[0].rhs() #Cosine of the true anomaly of the capture or bit at the moon. print 'cosine of capture orbit\'s true anomaly at the moon =',cos_ta. n() print 'capture orbit\'s true anomaly at the moon (degrees) =',(my_arc cos(cos_ta,'ta')*180/pi).n()

#Capture orbit's flight path angle at the moon. var('cos_fpa') #Cosine of flight path angle. cos_fpa = (1 + ecc*cos_ta)/sqrt(1+ecc^2+2*ecc*cos_ta) #cosine of flig ht path angle at the moon. print 'cosine of capture orbit\'s flight path angle at the moon =',co s_fpa fpa = my_arccos(cos_fpa,'fpa') print 'capture orbit\'s flight path angle at the moon =',(fpa*180/pi) .n()

#Use cosine of flight path angle to calculate delta v from Phobos or Deimos directly into the (non-inclined) capture orbit. delta_v_from_moon = sqrt(capture_speed_moon^2 + speed_moon^2 - 2*capt ure_speed_moon*speed_moon*cos_fpa) print 'delta v from moon directly into the non-inclined capture orbit =',delta_v_from_moon

#Check: v_i = vector([speed_moon,0,0]).n() v_f = rotateZ(my_arccos(cos_fpa,'fpa'))*vector([capture_speed_moon,0, 0]).n() v_i v_f delta_v = v_f - v_i delta_v print 'CHECK: delta-v directly into the non-inclined capture orbit (m/s) =',delta_v.norm()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 163/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where the captur e slings stay in the x-y (ecliptic) plane and Deimos is inclined by 2 7.58 degrees with a longitude of ascending node arbitrarily set to 0. 0000 degrees.

capture orbit eccentricity = 0.717360803768281 cosine of capture orbit's true anomaly at the moon = -0.9939852929933 38 capture orbit's true anomaly at the moon (degrees) = 173.712720193813 cosine of capture orbit's flight path angle at the moon = 0.964506853 303158 capture orbit's flight path angle at the moon = 15.3109755656732 delta v from moon directly into the non-inclined capture orbit = 657. 867159072570 CHECK: delta-v directly into the non-inclined capture orbit (m/s) = 6 57.867159072570

In [41]: #Mass of sling on Phobos or Deimos which launches the capture sling d irectly into the (non-inclined) capture orbit. mass_ratio_moon1 = tether_mass_ratio(delta_v_from_moon/v_c) #Use defa ult characteristic velocity.

print 'characteristic velocity (m/s) =',v_c #Characteristic velocity for a sling with specified tensile strength and density. print 'mass of sling on moon which tosses capture sling directly into (non-inclined) capture orbit (tons) =',payload_mass/1000.0*mass_rati o_moon1.n() print warnstring,'THIS CALC PREDATES GRAPPLE_FRACTION SCALING WITH AC CELERATION! sum of masses of both slings (tons) =',(payload_mass/100 0.0*(1.0+grapple_fraction)*(mass_ratio_moon1+tether_mass_ratio(radii[ 1]*omegas[1]/v_cs[1][0]))).n() print 'fraction of original mass for direct launch from the moon =', ((mass_ratio_moon1+tether_mass_ratio(radii[1]*omegas[1]/v_cs[1][0]))/ mass_ratio_direct_moon).n()

characteristic velocity (m/s) = 2726.88419920932 mass of sling on moon which tosses capture sling directly into (non-i nclined) capture orbit (tons) = 0.121029033534396 !!!WARNING!!! THIS CALC PREDATES GRAPPLE_FRACTION SCALING WITH ACCEL ERATION! sum of masses of both slings (tons) = 0.508509438026693 fraction of original mass for direct launch from the moon = 0.2967688 81212361

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 164/173 9/6/2018 capture-orbit-sling

In [42]: #Option 2 - throw capture orbit sling into orbit with low periapsis b ut apoapsis at the moon, then raise apoapsis at periapsis. #NOTE: This requires more total delta-v than raising apoapsis first, then lowering periapsis. print moon_frame_string

a_temp = (a_moon + periapsis_capture)/2 v_temp_at_moon = sqrt(mu*(2/a_moon-1/a_temp)) delta_v_moon_to_temp = speed_moon - v_temp_at_moon

print 'speed of moon =',speed_moon print 'temporary orbit speed at moon =',v_temp_at_moon print 'delta v from moon =',delta_v_moon_to_temp

p_i = p_moon0 #Used to be vector([0,-a_moon,0]) v_i = v_moon0 - delta_v_moon_to_temp*v_moon0/v_moon0.norm() #Used to be vector([speed_moon - delta_v_moon_to_temp,0,0]) kepler = xyz2kepler(p_i,v_i) (kepler).n()

#Mass of sling on Phobos or Deimos which launches the capture sling i nto the temporary orbit with no apoapsis change. mass_ratio_sling2 = tether_mass_ratio(delta_v_moon_to_temp/v_c)

print '\ncharacteristic velocity (m/s) =',v_c #Characteristic velocit y for a sling with specified tensile strength and density. print warnstring,'THIS CALC PREDATES GRAPPLE_FRACTION SCALING WITH AC CELERATION! mass of sling on moon which tosses capture sling into tem porary orbit (tons) =',payload_mass/1000.0*(1.0+grapple_fraction)*mas s_ratio_sling2.n()

#Necessary delta-v at periapsis to change temporary orbit into captur e orbit. v_temp_at_periapsis = sqrt(mu*(2/periapsis_capture-1/a_temp)) delta_v_temp_to_capture = capture_speed_periapsis - v_temp_at_periaps is

print 'temporary orbit speed at periapsis (m/s) =',v_temp_at_periapsi s print 'capture orbit speed at periapsis (m/s) =',capture_speed_periap sis print 'delta-v from temporary orbit to capture orbit (m/s) =',delta_v _temp_to_capture print 'total delta-v from moon to (non-inclined) capture orbit (m/s) =',delta_v_moon_to_temp + delta_v_temp_to_capture

p_i = com_cs_pos0 #Used to be vector([0,-periapsis_capture,0]) v_i = (v_temp_at_periapsis + delta_v_temp_to_capture)*com_cs_vel0/com _cs_vel0.norm() #Used to be vector([v_temp_at_periapsis + delta_v_tem p_to_capture,0,0]) kepler = xyz2kepler(p_i,v_i) (kepler).n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 165/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where Deimos sta ys in the x-y plane and the capture slings are inclined by 27.58 degr ees with a longitude of ascending node arbitrarily set to 0.0000 degr ees.

speed of moon = 1351.05227066087 temporary orbit speed at moon = 722.953336574843 delta v from moon = 628.098934086028 ------Elliptical orbit: using true anomaly. Position vector (km) = (23463.2000000000, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.000000000000000, 0.722953336574843, 0.000000000000000) Radial distance (km) = 23463.2000000000 Speed (km/s) = 0.722953336574843 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 13.5115140611246 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.69627987267229e10) Specific relative angular momentum (m^2/s) = 1.69627987267229e10 Specific orbital energy (kJ/kg) = -1564.01147462555 Eccentricity vector = (-0.713663818232359, 0.000000000000000, 0.000000000000000) Eccentricity = 0.713663818232359 , A lt. calc. = 0.713663818232359 Semi-latus rectum (km) = 6718.36310005052 , Al t. calc. = 6718.36310005052 Semi-minor axis (km) = 9590.97024474989 Semi-major axis (km) = 13691.8336901121 Periapsis altitude (km) = 524.467380224260 Periapsis (km) = 3920.46738022426 Apoapsis altitude (km) = 20067.2000000000 Apoapsis (km) = 23463.2000000000 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 180.000000000000 , Al t. calc. = 180.000000000000 Mean anomaly (degrees) = 180.000000000000 Eccentric anomaly (degrees) = 180.000000000000 True anomaly (degrees) = 180.000000000000 , Al t. calc. = 180.000000000000 Time since periapsis (hours) = 6.75575703056229 returns [a, ecc, inc, AP, LAN2, tper, p]

characteristic velocity (m/s) = 2726.88419920932 !!!WARNING!!! THIS CALC PREDATES GRAPPLE_FRACTION SCALING WITH ACCEL ERATION! mass of sling on moon which tosses capture sling into tempor ary orbit (tons) = 0.137428797404502 temporary orbit speed at periapsis (m/s) = 4326.72869879931 capture orbit speed at periapsis (m/s) = 4331.39333469434 delta-v from temporary orbit to capture orbit (m/s) = 4.6646358950274 6 total delta-v from moon to (non-inclined) capture orbit (m/s) = 632.7 63569981055 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 166/173 9/6/2018 capture-orbit-sling ------Elliptical orbit: using true anomaly. Position vector (km) = (3920.46738022426, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.000000000000000, 4.33139333469434, 0.000000000000000) Radial distance (km) = 3920.46738022426 Speed (km/s) = 4.33139333469434 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 13.7774796305327 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.69810862795899e10) Specific relative angular momentum (m^2/s) = 1.69810862795899e10 Specific orbital energy (kJ/kg) = -1543.81798121506 Eccentricity vector = (0.717360803768281, 0.000000000000000, 0.000000000000000) Eccentricity = 0.717360803768281 , A lt. calc. = 0.717360803768281 Semi-latus rectum (km) = 6732.85701124926 , Al t. calc. = 6732.85701124927 Semi-minor axis (km) = 9663.89991054697 Semi-major axis (km) = 13870.9260162561 Periapsis altitude (km) = 524.467380224261 Periapsis (km) = 3920.46738022426 Apoapsis altitude (km) = 20425.3846522879 Apoapsis (km) = 23821.3846522879 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Mean anomaly (degrees) = 0.000000000000000 Eccentric anomaly (degrees) = 0.000000000000000 True anomaly (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Time since periapsis (hours) = 0.000000000000000 returns [a, ecc, inc, AP, LAN2, tper, p]

Out[42]: (1.38709260162561e7, 0.717360803768281, 0.000000000000000, 0.00000000 0000000, 0.000000000000000, 0.000000000000000, 6.73285701124926e6)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 167/173 9/6/2018 capture-orbit-sling

In [43]: #Option 3 - throw capture orbit sling into orbit with high apoapsis b ut periapsis at the moon, then lower periapsis at apoapsis. #Similar to the reverse of fig 9 here: http://www.csc.caltech.edu/ref erences/Hopkins-Phobos-Deimos-Paper.pdf print moon_frame_string

a_temp = (a_moon + apoapsis_capture)/2 v_temp_at_moon = sqrt(mu*(2/a_moon-1/a_temp)) delta_v_moon_to_temp = v_temp_at_moon - speed_moon

print 'speed of moon =',speed_moon print 'temporary orbit speed at moon =',v_temp_at_moon print 'delta v from moon =',delta_v_moon_to_temp

#Compare that delta-v to the delta-v needed to enter a parabolic traj ectory. v_escape_at_moon = sqrt(mu*(2/a_moon)) print 'escape delta v from moon =',v_escape_at_moon - speed_moon

p_i = p_moon0 #Used to be vector([0,-a_moon,0]) v_i = v_moon0 + delta_v_moon_to_temp*v_moon0/v_moon0.norm() #Used to be vector([speed_moon + delta_v_moon_to_temp,0,0]) kepler = xyz2kepler(p_i,v_i) (kepler).n()

#Mass of sling on Phobos or Deimos which launches the capture sling i nto the temporary orbit with no periapsis change. mass_ratio_sling2 = tether_mass_ratio(delta_v_moon_to_temp/v_c)

print '\n',warnstring,'THIS CALC PREDATES GRAPPLE_FRACTION SCALING WI TH ACCELERATION! mass of sling on moon which tosses capture sling int o temporary orbit (tons) =',payload_mass/1000.0*(1.0+grapple_fraction )*mass_ratio_sling2.n()

#Necessary delta-v at apoapsis to change temporary orbit into capture orbit. v_temp_at_apoapsis = sqrt(mu*(2/apoapsis_capture-1/a_temp)) delta_v_temp_to_capture = v_temp_at_apoapsis - capture_speed_apoapsis apoapsis_capture_circ_speed = sqrt(mu/apoapsis_capture)

print 'temporary orbit speed at apoapsis (m/s) =',v_temp_at_apoapsis print 'capture orbit speed at apoapsis (m/s) =',capture_speed_apoap sis print 'circular orbit speed at apoapsis (m/s) =',apoapsis_capture_ci rc_speed print 'delta-v from temporary orbit to (non-inclined) capture orbit (m/s) =',delta_v_temp_to_capture print 'total delta-v from moon to (non-inclined) capture orbit (m/s) =',delta_v_moon_to_temp + delta_v_temp_to_capture

p_i = -apoapsis_capture*com_cs_pos0/com_cs_pos0.norm() #Used to be ve ctor([0,-apoapsis_capture,0]) v_i = -(v_temp_at_apoapsis - delta_v_temp_to_capture)*com_cs_vel0/com _cs_vel0.norm() #Used to be vector([v_temp_at_apoapsis - delta_v_temp _to_capture,0,0])

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 168/173 9/6/2018 capture-orbit-sling kepler = xyz2kepler(p_i,v_i) (kepler).n()

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 169/173 9/6/2018 capture-orbit-sling

Final vectors in this cell are calculated in a frame where Deimos sta ys in the x-y plane and the capture slings are inclined by 27.58 degr ees with a longitude of ascending node arbitrarily set to 0.0000 degr ees.

speed of moon = 1351.05227066087 temporary orbit speed at moon = 1356.15978298029 delta v from moon = 5.10751231941663 escape delta v from moon = 559.624173982698 ------Elliptical orbit: using true anomaly. Position vector (km) = (23463.2000000000, 0. 000000000000000, 0.000000000000000) Velocity vector (km/s) = (0.000000000000000, 1.35615978298029, 0.000000000000000) Radial distance (km) = 23463.2000000000 Speed (km/s) = 1.35615978298029 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 30.6581517081681 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 3.18198482200231e10) Specific relative angular momentum (m^2/s) = 3.18198482200231e10 Specific orbital energy (kJ/kg) = -905.757559571325 Eccentricity vector = (0.00757508297729248, 0.000000000000000, 0.000000000000000) Eccentricity = 0.00757508297729508 , Alt. calc. = 0.00757508297729248 Semi-latus rectum (km) = 23640.9356869128 , Al t. calc. = 23640.9356869128 Semi-minor axis (km) = 23641.6139967973 Semi-major axis (km) = 23642.2923261439 Periapsis altitude (km) = 20067.1999999999 Periapsis (km) = 23463.1999999999 Apoapsis altitude (km) = 20425.3846522880 Apoapsis (km) = 23821.3846522880 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Mean anomaly (degrees) = 0.000000000000000 Eccentric anomaly (degrees) = 0.000000000000000 True anomaly (degrees) = 0.000000000000000 , A lt. calc. = 0.0000476280213138561 Time since periapsis (hours) = 0.000000000000000 returns [a, ecc, inc, AP, LAN2, tper, p]

!!!WARNING!!! THIS CALC PREDATES GRAPPLE_FRACTION SCALING WITH ACCEL ERATION! mass of sling on moon which tosses capture sling into tempor ary orbit (tons) = 8.77054294039558e-6 temporary orbit speed at apoapsis (m/s) = 1335.76820510167 capture orbit speed at apoapsis (m/s) = 712.850513412914 circular orbit speed at apoapsis (m/s) = 1340.85640858803 delta-v from temporary orbit to (non-inclined) capture orbit (m/s) = 622.917691688756 total delta-v from moon to (non-inclined) capture orbit (m/s) = 628.0 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 170/173 9/6/2018 capture-orbit-sling 25204008173 ------Elliptical orbit: using true anomaly. Position vector (km) = (-23821.3846522879, - 0.000000000000000, -0.000000000000000) Velocity vector (km/s) = (-0.000000000000000, -0.712850513412914, -0.000000000000000) Radial distance (km) = 23821.3846522879 Speed (km/s) = 0.712850513412914 Radial velocity (km/s) = 0.000000000000000 Flight path angle (degrees) = 0.000000000000000 Orbital period (hours) = 13.7774796305327 Specific relative angular momentum vector = (0.000000000000000, 0.000000000000000, 1.69810862795899e10) Specific relative angular momentum (m^2/s) = 1.69810862795899e10 Specific orbital energy (kJ/kg) = -1543.81798121507 Eccentricity vector = (0.717360803768281, 0.000000000000000, 0.000000000000000) Eccentricity = 0.717360803768281 , A lt. calc. = 0.717360803768281 Semi-latus rectum (km) = 6732.85701124926 , Al t. calc. = 6732.85701124926 Semi-minor axis (km) = 9663.89991054696 Semi-major axis (km) = 13870.9260162561 Periapsis altitude (km) = 524.467380224262 Periapsis (km) = 3920.46738022426 Apoapsis altitude (km) = 20425.3846522879 Apoapsis (km) = 23821.3846522879 Inclination (degrees) = 0.000000000000000 Longitude of the ascending node (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Argument of periapsis (degrees) = 0.000000000000000 , A lt. calc. = 0.000000000000000 Mean anomaly (degrees) = 180.000000000000 Eccentric anomaly (degrees) = 180.000000000000 True anomaly (degrees) = 180.000000000000 , Al t. calc. = 180.000000000000 Time since periapsis (hours) = 6.88873981526634 returns [a, ecc, inc, AP, LAN2, tper, p]

Out[43]: (1.38709260162561e7, 0.717360803768281, 0.000000000000000, 0.00000000 0000000, 0.000000000000000, 24799.4633349588, 6.73285701124926e6)

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 171/173 9/6/2018 capture-orbit-sling

In [44]: #Inclination change - Phobos and Deimos are inclined 26.04 and 27.58 degrees away from the ecliptic plane, respectively. print moon_frame_string

#Circular orbit approximation: delta_v_inclination = (2*capture_speed_apoapsis*sin(inc_moon/2)).n() print 'delta-v to change inclination of capture orbit (m/s) =',delta_ v_inclination

print 'Calculate both burns (lowering periapsis and changing inclinat ion) performed at once.' v_i = vector([v_temp_at_apoapsis,0,0]).n() v_f = vector([capture_speed_apoapsis*cos(inc_moon),capture_speed_apoa psis*sin(inc_moon),0]).n() delta_v = v_f - v_i delta_v print 'delta-v to lower periapsis AND change inclination of capture o rbit (m/s) =',delta_v.norm() angle = (arctan(delta_v[1]/delta_v[0])*180/pi).n() print 'angle of that maneuver (degrees away from chosen moon\'s plan e) =',angle

print '' print 'Calculate both burns (circularizing and changing inclination) performed at once.' v_i2 = vector([v_temp_at_apoapsis,0,0]).n() v_f2 = vector([apoapsis_capture_circ_speed*cos(inc_moon),apoapsis_cap ture_circ_speed*sin(inc_moon),0]).n() delta_v2 = v_f2 - v_i2 delta_v2 print 'delta-v to circularize AND change inclination of capture orbit (m/s) =',delta_v2.norm() angle2 = (arctan(delta_v2[1]/delta_v2[0])*180/pi).n() print 'angle of that maneuver (degrees away from chosen moon\'s plan e) =',angle2

Final vectors in this cell are calculated in a frame where Deimos sta ys in the x-y plane and the capture slings are inclined by 27.58 degr ees with a longitude of ascending node arbitrarily set to 0.0000 degr ees.

delta-v to change inclination of capture orbit (m/s) = 339.8357411612 20 Calculate both burns (lowering periapsis and changing inclination) pe rformed at once. delta-v to lower periapsis AND change inclination of capture orbit (m/s) = 777.453001392517 angle of that maneuver (degrees away from chosen moon's plane) = -25. 1199536970472

Calculate both burns (circularizing and changing inclination) perform ed at once. delta-v to circularize AND change inclination of capture orbit (m/s) = 638.029976179054 angle of that maneuver (degrees away from chosen moon's plane) = -76. 6537601069899 http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 172/173 9/6/2018 capture-orbit-sling

http://localhost:8888/nbconvert/html/capture/capture-orbit-sling.ipynb?download=false 173/173