184 lines
5.7 KiB
Julia
184 lines
5.7 KiB
Julia
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)
|
|
|