export Phase, Mission_Guess, Mission, Bad_Mission export test_phase1, test_phase2 export test_mg export Vector struct Phase planet::Body v∞_in::Vector{Float64} v∞_out::Vector{Float64} tof::Float64 thrust_profile::Matrix{Float64} end const test_phase1 = Phase(Venus, [√20., -√80., 0.], [√50., -√50., 0.], 1.30464e7, zeros(20,3)) const test_phase2 = Phase(Jupiter, [0.3, 7.1, 0.2], [0.3, 7.1, 0.2], 3.9year, zeros(20,3)) const test_phases = [test_phase1, test_phase2] """ A convenience function for calculating mass usage given a certain thrust profile """ function mass_consumption(sc::Sc, phase::Phase) weighted_thrusting_time = 0.0 n = size(phase.thrust_profile)[1] for i in 1:size(phase.thrust_profile,1) weighted_thrusting_time += norm(phase.thrust_profile[i,:]) * phase.tof/n end return weighted_thrusting_time*sc.mass_flow_rate end struct Mission_Guess sc::Sc start_mass::Float64 launch_date::DateTime launch_v∞::Vector{Float64} phases::Vector{Phase} converged::Bool end """ Constructor for a mission guess. Generally mission guesses are not converged """ function Mission_Guess(sc::Sc, mass::Float64, date::DateTime, v∞::Vector{Float64}, phases::Vector{Phase}) # First do some checks to make sure that it's valid mass_used = 0 for phase in phases mass_used += mass_consumption(sc, phase) mass - mass_used > sc.dry_mass || throw(Mass_Error(mass - mass_used)) v∞_in, v∞_out = phase.v∞_in, phase.v∞_out end Mission_Guess(sc, mass, date, v∞, phases, false) end """ This is the opposite of the function below. It takes a vector and any other necessary information and outputs a mission guess """ function Mission_Guess(x::Vector{Float64}, sc::Sc, mass::Float64, flybys::Vector{Body}) # Variable mission params launch_date = Dates.unix2datetime(x[1]) launch_v∞ = x[2:4] # Try to intelligently determine n three_n = ((length(x)-4)/length(flybys)) - 7 three_n % 3 == 0 || throw(Mission_Creation_Error(three_n)) n::Int = three_n/3 # Build the phases i = 0 phases = Vector{Phase}() for flyby in flybys phase_params = x[ 5 + (3n+7) * i : 5 + (3n+7) * (i+1) - 1 ] v∞_in = phase_params[1:3] v∞_out = phase_params[4:6] tof = phase_params[7] thrusts = reshape(phase_params[8:end], (n,3)) push!(phases, Phase(flyby, v∞_in, v∞_out, tof, thrusts)) i += 1 end return Mission_Guess(sc, mass, launch_date, launch_v∞, phases, false) end """ This is a function to convert a mission guess into an nlp-ready vector. It doesn't contain all information from the guess though. """ function Base.Vector(g::Mission_Guess) result = Vector{Float64}() push!(result, Dates.datetime2unix(g.launch_date)) push!(result, g.launch_v∞...) for phase in g.phases push!(result,phase.v∞_in...) push!(result,phase.v∞_out...) push!(result,phase.tof) push!(result,phase.thrust_profile...) end return result end function lowest_mission_vector(launch_window::Vector{DateTime}, num_phases::Int, n::Int) result = Vector{Float64}() push!(result, Dates.datetime2unix(launch_window[1])) push!(result, -10*ones(3)...) for i in 1:num_phases push!(result, -10*ones(3)...) push!(result, -10*ones(3)...) push!(result, 86_400.0) push!(result, -1 * ones(n,3)...) end return result end function highest_mission_vector(launch_window::Vector{DateTime}, mission_length::Float64, num_phases::Int, n::Int) result = Vector{Float64}() push!(result, Dates.datetime2unix(launch_window[2])) push!(result, 10*ones(3)...) for i in 1:num_phases push!(result, 10*ones(3)...) push!(result, 10*ones(3)...) push!(result, mission_length) push!(result, ones(n,3)...) end return result end const test_mg = Mission_Guess(bepi, 12_000., DateTime(1992,11,19), [-3.4,1.2,0.1], test_phases) struct Mission sc::Sc start_mass::Float64 launch_date::DateTime launch_v∞::Vector{Float64} phases::Vector{Phase} converged::Bool end """ Constructor for a mission. Generally mission guesses are converged, so we check everything """ function Mission(sc::Sc, mass::Float64, date::DateTime, v∞::Vector{Float64}, phases::Vector{Phase}; force=false) # First do some checks to make sure that it's valid if !force time = date current_planet = Earth start = state(current_planet, time, v∞, mass) for phase in phases final = prop(phase.thrust_profile, start, sc, phase.tof)[2] mass = final[7] mass > sc.dry_mass || throw(Mass_Error(mass - mass_used)) current_planet = phase.planet time += Dates.Second(floor(phase.tof)) p_pos = state(current_planet, time)[1:3] abs(norm(final[1:3] - p_pos)) < √30_000. || throw(Planet_Match_Error(final[1:3], p_pos)) v∞_in, v∞_out = phase.v∞_in, phase.v∞_out if phase != phases[end] abs(norm(v∞_in) - norm(v∞_out)) < 0.005 || throw(V∞_Error(v∞_in, v∞_out)) δ = acos( ( v∞_in ⋅ v∞_out ) / ( norm(v∞_in) * norm(v∞_out) ) ) periapsis = (phase.planet.μ/(v∞_in ⋅ v∞_in)) * ( 1/sin(δ/2) - 1 ) periapsis > 1.1phase.planet.r || throw(HitPlanet_Error()) end end end Mission(sc, mass, date, v∞, phases, true) end """ BE CAREFUL!! This just makes a guess converged, whether true or not """ function Mission(g::Mission_Guess) return Mission(g.sc, g.start_mass, g.launch_date, g.launch_v∞, g.phases, force=true) end function Mission(x::Vector{Float64}, sc::Sc, mass::Float64, flybys::Vector{Body}) guess = Mission_Guess(x, sc, mass, flybys::Vector{Body}) return Mission(guess) end struct Bad_Mission message::String converged::Bool end Bad_Mission(s::String) = Bad_Mission(s,false) Bad_Mission(s::Symbol) = Bad_Mission(String(s),false)