NLP seems to be doing pretty well
This commit is contained in:
@@ -12,8 +12,13 @@ unit-test-job:
|
|||||||
- julia --project=julia/Project.toml -E 'using Pkg; Pkg.test()'
|
- julia --project=julia/Project.toml -E 'using Pkg; Pkg.test()'
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- julia/plots/plot_test_earth.html
|
- julia/plots/nlp_test_1_phase.html
|
||||||
- julia/plots/plot_test_sun.html
|
- julia/plots/nlp_test_2_phase.html
|
||||||
- julia/plots/nlp_test_earth.html
|
- julia/plots/plot_test_mission.html
|
||||||
- julia/plots/nlp_test_sun.html
|
- julia/plots/plot_test_mission_guess.html
|
||||||
|
- julia/plots/plot_test_path.html
|
||||||
|
- julia/plots/plot_test_paths.html
|
||||||
|
- julia/plots/plot_test_planet_move.html
|
||||||
|
- julia/plots/plot_test_planet_no_move.html
|
||||||
|
- julia/plots/plot_test_planet_solar_system.html
|
||||||
expire_in: 10 weeks
|
expire_in: 10 weeks
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ module Thesis
|
|||||||
include("./bodies.jl")
|
include("./bodies.jl")
|
||||||
include("./constants.jl")
|
include("./constants.jl")
|
||||||
include("./spacecraft.jl")
|
include("./spacecraft.jl")
|
||||||
include("./mission.jl")
|
|
||||||
include("./conversions.jl")
|
|
||||||
include("./lamberts.jl")
|
|
||||||
include("./plotting.jl")
|
|
||||||
include("./inner_loop/laguerre-conway.jl")
|
include("./inner_loop/laguerre-conway.jl")
|
||||||
include("./inner_loop/propagator.jl")
|
include("./inner_loop/propagator.jl")
|
||||||
|
include("./mission.jl")
|
||||||
|
include("./conversions.jl")
|
||||||
|
include("./plotting.jl")
|
||||||
include("./inner_loop/nlp_solver.jl")
|
include("./inner_loop/nlp_solver.jl")
|
||||||
# include("./inner_loop/monotonic_basin_hopping.jl")
|
# include("./inner_loop/monotonic_basin_hopping.jl")
|
||||||
# include("./outer_loop.jl")
|
# include("./outer_loop.jl")
|
||||||
|
include("./lamberts.jl")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
export state
|
export state, period
|
||||||
|
|
||||||
struct Body
|
struct Body
|
||||||
μ::Float64
|
μ::Float64
|
||||||
r::Float64 # radius
|
r::Float64 # radius
|
||||||
color::String
|
color::String
|
||||||
id::Int # SPICE id
|
id::Int # SPICE id
|
||||||
|
a::Float64 # semi-major axis
|
||||||
|
line_color::AbstractString
|
||||||
|
name::AbstractString
|
||||||
end
|
end
|
||||||
|
|
||||||
function state(p::Body, t::DateTime, add_v∞::Vector{Float64}=[0., 0., 0.], add_mass::Float64=1e10)
|
period(b::Body) = 2π * √(b.a^3 / Sun.μ)
|
||||||
|
|
||||||
|
function state(p::Body, t::DateTime, add_v∞::Vector{T}=[0., 0., 0.], add_mass::Float64=1e10) where T <: Real
|
||||||
time = utc2et(Dates.format(t,"yyyy-mm-ddTHH:MM:SS"))
|
time = utc2et(Dates.format(t,"yyyy-mm-ddTHH:MM:SS"))
|
||||||
[ spkssb(p.id, time, "ECLIPJ2000"); 0.0 ] + [ zeros(3); add_v∞; add_mass ]
|
[ spkssb(p.id, time, "ECLIPJ2000"); 0.0 ] + [ zeros(3); add_v∞; add_mass ]
|
||||||
end
|
end
|
||||||
|
|
||||||
function state(p::Body, t::Float64, add_v∞::Vector{Float64}=[0., 0., 0.], add_mass::Float64=1e10)
|
function state(p::Body, t::S, add_v∞::Vector{T}=[0., 0., 0.], add_mass::Float64=1e10) where {T,S <: Real}
|
||||||
[ spkssb(p.id, t, "ECLIPJ2000"); 0.0 ] + [ zeros(3); add_v∞; add_mass ]
|
[ spkssb(p.id, t, "ECLIPJ2000"); 0.0 ] + [ zeros(3); add_v∞; add_mass ]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ export Jupiter, Saturn, Uranus, Neptune, Pluto
|
|||||||
export G, AU, init_STM, hour, day, year, second
|
export G, AU, init_STM, hour, day, year, second
|
||||||
export Pathlist
|
export Pathlist
|
||||||
|
|
||||||
const Sun = Body(1.32712440018e11, 696000., "Electric", 10)
|
const Sun = Body(1.32712440018e11, 696000., "YlOrRd", 10, 0., "", "Sun")
|
||||||
const Mercury = Body(2.2032e4, 2439., "heat", 1)
|
const Mercury = Body(2.2032e4, 2439., "YlOrRd", 1, 57909083., "#FF2299", "Mercury")
|
||||||
const Venus = Body(3.257e5, 6052., "turbid", 2)
|
const Venus = Body(3.257e5, 6052., "Portland", 2, 108208601., "#FFCC00", "Venus")
|
||||||
const Earth = Body(3.986004415e5, 6378.1363, "Blues", 399)
|
const Earth = Body(3.986004415e5, 6378.1363, "Earth", 399, 149598023., "#0055FF", "Earth")
|
||||||
const Moon = Body(4.902799e3, 1738., "Greys", 301)
|
const Mars = Body(4.305e4, 3397.2, "Reds", 4, 227939186., "#FF0000", "Mars")
|
||||||
const Mars = Body(4.305e4, 3397.2, "Reds", 4)
|
const Jupiter = Body(1.266865361e8, 71492., "YlOrRd", 5, 778298361., "#FF7700", "Jupiter")
|
||||||
const Jupiter = Body(1.266865361e8, 71492., "solar", 5)
|
const Saturn = Body(3.794e7, 60268., "Portland", 6, 1429394133., "#FFFF00", "Saturn")
|
||||||
const Saturn = Body(3.794e7, 60268., "turbid", 6)
|
const Uranus = Body(5.794e6, 25559., "YlGnBu", 7, 2875038615., "#0000FF", "Uranus")
|
||||||
const Uranus = Body(5.794e6, 25559., "haline", 7)
|
const Neptune = Body(6.809e6, 24764., "Blues", 8, 4504449769., "#6666FF", "Neptune")
|
||||||
const Neptune = Body(6.809e6, 24764., "ice", 8)
|
const Pluto = Body(9e2, 1151., "Picnic", 9, 5915799000., "#FFCCCC", "Pluto")
|
||||||
const Pluto = Body(9e2, 1151., "matter", 9)
|
|
||||||
|
|
||||||
const G = 6.67430e-20 #universal gravity parameter
|
const G = 6.67430e-20 #universal gravity parameter
|
||||||
const AU = 149597870.691 #km
|
const AU = 149597870.691 #km
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ struct V∞_Error <: Exception
|
|||||||
v∞_in::AbstractVector
|
v∞_in::AbstractVector
|
||||||
v∞_out::AbstractVector
|
v∞_out::AbstractVector
|
||||||
end
|
end
|
||||||
Base.showerror(io::IO, e::V∞_Error) = print(io, "v∞s weren't the same: ", e.v∞_in, " and ", e.v∞_out)
|
Base.showerror(io::IO, e::V∞_Error) = print(io, """v∞s weren't the same:
|
||||||
|
v1 = $(e.v∞_in) and
|
||||||
|
v2 = $(e.v∞_out)
|
||||||
|
difference: $(norm(e.v∞_in) - norm(e.v∞_out))""")
|
||||||
|
|
||||||
struct PropOne_Error <: Exception
|
struct PropOne_Error <: Exception
|
||||||
ΔV_unit::AbstractVector
|
ΔV_unit::AbstractVector
|
||||||
@@ -31,3 +34,13 @@ struct Mission_Creation_Error{T} <: Exception where T <: Real
|
|||||||
num_entries::T
|
num_entries::T
|
||||||
end
|
end
|
||||||
Base.showerror(io::IO, e::Mission_Creation_Error) = print(io, "Tried to create a mission with $(e.num_entries) entries")
|
Base.showerror(io::IO, e::Mission_Creation_Error) = print(io, "Tried to create a mission with $(e.num_entries) entries")
|
||||||
|
|
||||||
|
struct Planet_Match_Error{T} <: Exception where T <: Real
|
||||||
|
state::Vector{T}
|
||||||
|
planet::Vector{T}
|
||||||
|
end
|
||||||
|
Base.showerror(io::IO, e::Planet_Match_Error) = print(io, """"
|
||||||
|
Spacecraft didn't make it to planet
|
||||||
|
spacecraft_pos = $(e.state)
|
||||||
|
planet_pos = $(e.planet)
|
||||||
|
difference = $(abs(norm(e.state - e.planet)))""")
|
||||||
|
|||||||
@@ -5,19 +5,36 @@ export solve_mission
|
|||||||
struct Result
|
struct Result
|
||||||
converged::Bool
|
converged::Bool
|
||||||
info::Symbol
|
info::Symbol
|
||||||
sol::Matrix{Float64}
|
sol::Mission_Guess
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function constraint_bounds(guess::Mission_Guess)
|
||||||
|
low_constraint = Vector{Float64}()
|
||||||
|
high_constraint = Vector{Float64}()
|
||||||
|
|
||||||
|
for phase in guess.phases
|
||||||
|
if phase != guess.phases[end]
|
||||||
|
push!(low_constraint, -100., -100., -100., -0.005, -0.005, -0.005, -30., 100.)
|
||||||
|
push!(high_constraint, 100., 100., 100., 0.005, 0.005, 0.005, 30., 100_000.)
|
||||||
|
else
|
||||||
|
push!(low_constraint, -100., -100., -100., 0.)
|
||||||
|
push!(high_constraint, 100., 100., 100., guess.start_mass - guess.sc.dry_mass)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return low_constraint, high_constraint
|
||||||
|
|
||||||
|
end
|
||||||
"""
|
"""
|
||||||
This function will take an initial guess to a mission profile and leverage Ipopt to solve the mission
|
This function will take an initial guess to a mission profile and leverage Ipopt to solve the mission
|
||||||
|
|
||||||
|
NOTE: This function will output a proper mission if it succeeded and just re-return the original
|
||||||
|
guess otherwise
|
||||||
"""
|
"""
|
||||||
function solve_mission( guess::Mission_Guess;
|
function solve_mission( guess::Mission_Guess,
|
||||||
tol=1e-8,
|
launch_window::Vector{DateTime},
|
||||||
|
latest_arrival::DateTime;
|
||||||
|
tol=1e-10,
|
||||||
verbose::Bool=false )
|
verbose::Bool=false )
|
||||||
# First we need to define a function that propagates the mission all the way through
|
|
||||||
# The function takes in a constraints vector (g) and an input vector (x) and spits out
|
|
||||||
# the objective function. In my case, the objective will be constant.
|
|
||||||
#
|
|
||||||
|
|
||||||
# First we define our starting point
|
# First we define our starting point
|
||||||
x0 = Vector(guess)
|
x0 = Vector(guess)
|
||||||
@@ -52,59 +69,39 @@ function solve_mission( guess::Mission_Guess;
|
|||||||
goal = state(current_planet, time, v∞_in)
|
goal = state(current_planet, time, v∞_in)
|
||||||
final = prop(reshape(thrusts, (n,3)), start, guess.sc, tof)[2]
|
final = prop(reshape(thrusts, (n,3)), start, guess.sc, tof)[2]
|
||||||
if flyby != flybys[end]
|
if flyby != flybys[end]
|
||||||
g[1+8i:6+8i] .= final[1:6] .- goal[1:6]
|
g[1+8i:6+8i] .= (final[1:6] .- goal[1:6]) .* [1., 1., 1., 10_000., 10_000., 10_000.]
|
||||||
g[7+8i] = norm(v∞_out) - norm(v∞_in)
|
g[7+8i] = (norm(v∞_out) - norm(v∞_in)) * 10_000.
|
||||||
δ = acos( ( v∞_in ⋅ v∞_out ) / ( norm(v∞_in) * norm(v∞_out) ) )
|
δ = acos( ( v∞_in ⋅ v∞_out ) / ( norm(v∞_in) * norm(v∞_out) ) )
|
||||||
g[8+8i] = (current_planet.μ/(v∞_in ⋅ v∞_in)) * ( 1/sin(δ/2) - 1 )
|
g[8+8i] = (current_planet.μ/(v∞_in ⋅ v∞_in)) * ( 1/sin(δ/2) - 1 ) - current_planet.r
|
||||||
else
|
else
|
||||||
g[8length(flybys)+1:8length(flybys)+3] .= final[1:3] .- goal[1:3]
|
g[8*(length(flybys)-1)+1:8*(length(flybys)-1)+3] .= final[1:3] .- goal[1:3]
|
||||||
g[8length(flybys)+4] = final[7]
|
g[8*(length(flybys)-1)+4] = final[7] - guess.sc.dry_mass
|
||||||
end
|
end
|
||||||
start = state(current_planet, time, v∞_out, final[7])
|
start = state(current_planet, time, v∞_out, final[7])
|
||||||
i += 1
|
i += 1
|
||||||
|
end
|
||||||
return 1.0
|
return 1.0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
max_time = Dates.datetime2unix(latest_arrival) - Dates.datetime2unix(launch_window[1])
|
||||||
|
lower_x = lowest_mission_vector(launch_window, length(guess.phases), n)
|
||||||
|
upper_x = highest_mission_vector(launch_window, max_time, length(guess.phases), n)
|
||||||
|
num_constraints = 8*(length(guess.phases)-1) + 4
|
||||||
|
g_low, g_high = constraint_bounds(guess)
|
||||||
|
ipopt_options = Dict("constr_viol_tol" => tol,
|
||||||
|
"acceptable_constr_viol_tol" => 100tol,
|
||||||
|
"max_iter" => 10_000,
|
||||||
|
"max_cpu_time" => 300.,
|
||||||
|
"print_level" => 5)
|
||||||
|
options = Options(solver=IPOPT(ipopt_options), derivatives=ForwardFD())
|
||||||
|
|
||||||
|
x, _, info = minimize(optimizer!, x0, num_constraints, lower_x, upper_x, g_low, g_high, options)
|
||||||
|
if verbose println(info) end
|
||||||
|
|
||||||
|
if info in [:Solve_Succeeded, :Solved_To_Acceptable_Level]
|
||||||
|
return Mission(x, guess.sc, guess.start_mass, flybys)
|
||||||
|
else
|
||||||
|
return guess
|
||||||
end
|
end
|
||||||
g = zeros(8length(flybys)+4)
|
|
||||||
optimizer!(g,x0)
|
|
||||||
return g
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# n = size(x0)[1]
|
|
||||||
|
|
||||||
# function f!(g,x)
|
|
||||||
# try
|
|
||||||
# g[1:6] .= prop(reshape(x,(n,3)), start, craft, tof, primary)[2][1:6]
|
|
||||||
# return 1.
|
|
||||||
# catch e
|
|
||||||
# if isa(e,LaGuerreConway_Error) || isa(e, Mass_Error) || isa(e, PropOne_Error)
|
|
||||||
# return 10_000.
|
|
||||||
# else
|
|
||||||
# rethrow
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# lower_x = -1 * ones(3n)
|
|
||||||
# upper_x = ones(3n)
|
|
||||||
# num_constraints = 6
|
|
||||||
# filename = "ipopt_"*string(rand(UInt))
|
|
||||||
# ipopt_options = Dict("constr_viol_tol" => tol,
|
|
||||||
# "acceptable_constr_viol_tol" => 1e-4,
|
|
||||||
# "max_iter" => 10_000,
|
|
||||||
# "max_cpu_time" => 30.,
|
|
||||||
# "print_level" => 0,
|
|
||||||
# "output_file" => filename)
|
|
||||||
# options = Options(solver=IPOPT(ipopt_options),
|
|
||||||
# derivatives=ForwardAD())
|
|
||||||
# x, _, info = minimize(f!, vec(x0), num_constraints, lower_x, upper_x, final[1:6], final[1:6], options)
|
|
||||||
# rm(filename)
|
|
||||||
# if info in [:Solve_Succeeded, :Solved_To_Acceptable_Level]
|
|
||||||
# return Result(true, info, reshape(x,(n,3)))
|
|
||||||
# else
|
|
||||||
# if verbose println(info) end
|
|
||||||
# return Result(false, info, x0)
|
|
||||||
# end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -56,7 +56,20 @@ function prop(ΔVs::Matrix{T},
|
|||||||
for k in 1:7 push!(states[k], interpolated_state[k]) end
|
for k in 1:7 push!(states[k], interpolated_state[k]) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
try
|
||||||
state = prop_one(ΔVs[i,:], state, craft, time/n, primary)
|
state = prop_one(ΔVs[i,:], state, craft, time/n, primary)
|
||||||
|
catch e
|
||||||
|
if isa(e,PropOne_Error)
|
||||||
|
for val in e.ΔV_unit
|
||||||
|
# If this isn't true, then we just let it slide
|
||||||
|
if abs(val) > 1.0001
|
||||||
|
rethrow()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
rethrow()
|
||||||
|
end
|
||||||
|
end
|
||||||
for j in 1:7 push!(states[j], state[j]) end
|
for j in 1:7 push!(states[j], state[j]) end
|
||||||
state[7] >= craft.dry_mass || throw(Mass_Error(state[7]))
|
state[7] >= craft.dry_mass || throw(Mass_Error(state[7]))
|
||||||
end
|
end
|
||||||
@@ -68,4 +81,4 @@ end
|
|||||||
"""
|
"""
|
||||||
Convenience function for propagating a state with no thrust
|
Convenience function for propagating a state with no thrust
|
||||||
"""
|
"""
|
||||||
prop(x::Vector{Float64}, t::Float64, p::Body=Sun) = prop(zeros(1000,3), [x;1e10], no_thrust, t, p)[1]
|
prop(x::Vector{Float64}, t::Float64, p::Body=Sun) = prop(zeros(1000,3), [x;1.], no_thrust, t, p)[1]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export Phase, Mission_Guess, Mission, Bad_Mission
|
export Phase, Mission_Guess, Mission, Bad_Mission
|
||||||
export test_phase1, test_phase2
|
export test_phase1, test_phase2
|
||||||
export test_mg, test_mission
|
export test_mg
|
||||||
export Vector
|
export Vector
|
||||||
|
|
||||||
struct Phase
|
struct Phase
|
||||||
@@ -11,7 +11,7 @@ struct Phase
|
|||||||
thrust_profile::Matrix{Float64}
|
thrust_profile::Matrix{Float64}
|
||||||
end
|
end
|
||||||
|
|
||||||
const test_phase1 = Phase(Venus, [9., 10., 0.], [10., 9, 0.], 1.30464e7, zeros(20,3))
|
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_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]
|
const test_phases = [test_phase1, test_phase2]
|
||||||
|
|
||||||
@@ -46,12 +46,6 @@ function Mission_Guess(sc::Sc, mass::Float64, date::DateTime, v∞::Vector{Float
|
|||||||
mass_used += mass_consumption(sc, phase)
|
mass_used += mass_consumption(sc, phase)
|
||||||
mass - mass_used > sc.dry_mass || throw(Mass_Error(mass - mass_used))
|
mass - mass_used > sc.dry_mass || throw(Mass_Error(mass - mass_used))
|
||||||
v∞_in, v∞_out = phase.v∞_in, phase.v∞_out
|
v∞_in, v∞_out = phase.v∞_in, phase.v∞_out
|
||||||
if phase != phases[end]
|
|
||||||
norm(v∞_in) ≈ norm(v∞_out) || 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_Guess(sc, mass, date, v∞, phases, false)
|
Mission_Guess(sc, mass, date, v∞, phases, false)
|
||||||
end
|
end
|
||||||
@@ -101,6 +95,32 @@ function Base.Vector(g::Mission_Guess)
|
|||||||
return result
|
return result
|
||||||
end
|
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)
|
const test_mg = Mission_Guess(bepi, 12_000., DateTime(1992,11,19), [-3.4,1.2,0.1], test_phases)
|
||||||
|
|
||||||
struct Mission
|
struct Mission
|
||||||
@@ -113,26 +133,46 @@ struct Mission
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Constructor for a mission. Generally mission guesses are converged
|
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})
|
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
|
# First do some checks to make sure that it's valid
|
||||||
mass_used = 0
|
if !force
|
||||||
|
time = date
|
||||||
|
current_planet = Earth
|
||||||
|
start = state(current_planet, time, v∞, mass)
|
||||||
for phase in phases
|
for phase in phases
|
||||||
mass_used += mass_consumption(sc, phase)
|
final = prop(phase.thrust_profile, start, sc, phase.tof)[2]
|
||||||
mass - mass_used > sc.dry_mass || throw(Mass_Error(mass - mass_used))
|
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
|
v∞_in, v∞_out = phase.v∞_in, phase.v∞_out
|
||||||
if phase != phases[end]
|
if phase != phases[end]
|
||||||
norm(v∞_in) ≈ norm(v∞_out) || throw(V∞_Error(v∞_in, v∞_out))
|
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) ) )
|
δ = acos( ( v∞_in ⋅ v∞_out ) / ( norm(v∞_in) * norm(v∞_out) ) )
|
||||||
periapsis = (phase.planet.μ/(v∞_in ⋅ v∞_in)) * ( 1/sin(δ/2) - 1 )
|
periapsis = (phase.planet.μ/(v∞_in ⋅ v∞_in)) * ( 1/sin(δ/2) - 1 )
|
||||||
periapsis > 1.1phase.planet.r || throw(HitPlanet_Error())
|
periapsis > 1.1phase.planet.r || throw(HitPlanet_Error())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
Mission(sc, mass, date, v∞, phases, true)
|
Mission(sc, mass, date, v∞, phases, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
const test_mission = Mission(bepi, 12_000., DateTime(1992,11,19), [-3.4,1.2,0.1], test_phases)
|
"""
|
||||||
|
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
|
struct Bad_Mission
|
||||||
message::String
|
message::String
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
using Random, PlotlyJS, Base.Iterators
|
using Random, PlotlyJS, Base.Iterators
|
||||||
|
|
||||||
export plot_orbits
|
export plot
|
||||||
|
|
||||||
function random_color()
|
function random_color()
|
||||||
num1 = rand(0:255)
|
num1 = rand(0:255)
|
||||||
@@ -12,51 +12,41 @@ function random_color()
|
|||||||
return "#"*string(num1, base=16, pad=2)*string(num2, base=16, pad=2)*string(num3, base=16, pad=2)
|
return "#"*string(num1, base=16, pad=2)*string(num2, base=16, pad=2)*string(num3, base=16, pad=2)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_true_max(mat::Vector{Array{Float64,2}})
|
# Some convenience functions for maximums
|
||||||
return maximum(abs.(flatten(mat)))
|
"""
|
||||||
|
Gets the maximum absolute value of a path
|
||||||
|
"""
|
||||||
|
function Base.maximum(path::Vector{Vector{Float64}})
|
||||||
|
return maximum( abs.( reduce( vcat,path ) ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
function plot_orbits(paths::Vector{Vector{Vector{Float64}}},
|
"""
|
||||||
primary::Body=Sun;
|
Gets the maximum absolute value of a list of paths
|
||||||
labels::Vector{String}=Vector{String}(),
|
"""
|
||||||
title::String="Spacecraft Position",
|
function Base.maximum(paths::Vector{Vector{Vector{Float64}}})
|
||||||
colors::Vector{String}=Vector{String}())
|
return maximum( abs.( reduce( vcat,reduce( vcat,paths ) ) ) )
|
||||||
|
|
||||||
N = 32
|
|
||||||
θ = collect(range(0,length=N,stop=2π))
|
|
||||||
ϕ = collect(range(0,length=N,stop=π))
|
|
||||||
x = cos.(θ) * sin.(ϕ)'
|
|
||||||
y = sin.(θ) * sin.(ϕ)'
|
|
||||||
z = repeat(cos.(ϕ)',outer=[N, 1])
|
|
||||||
ps = primary.r .* (x,y,z)
|
|
||||||
x_p,y_p,z_p = ps
|
|
||||||
|
|
||||||
t1 = []
|
|
||||||
for i = 1:length(paths)
|
|
||||||
path = paths[i]
|
|
||||||
label = labels != [] ? labels[i] : "orbit"
|
|
||||||
color = colors != [] ? colors[i] : random_color()
|
|
||||||
push!(t1, scatter3d(;x=(path[1]),y=(path[2]),z=(path[3]),
|
|
||||||
mode="lines", name=label, line_color=color, line_width=3))
|
|
||||||
push!(t1, scatter3d(;x=([path[1][1]]),y=([path[2][1]]),z=([path[3][1]]),
|
|
||||||
mode="markers", showlegend=false,
|
|
||||||
marker=attr(color=color, size=3, symbol="circle")))
|
|
||||||
push!(t1, scatter3d(;x=([path[1][end]]),y=([path[2][end]]),z=([path[3][end]]),
|
|
||||||
mode="markers", showlegend=false,
|
|
||||||
marker=attr(color=color, size=3, symbol="diamond")))
|
|
||||||
end
|
end
|
||||||
limit = max(maximum(abs.(flatten(flatten(paths)))),
|
|
||||||
maximum(abs.(flatten(ps)))) * 1.1
|
|
||||||
|
|
||||||
t2 = surface(;x=(x_p),
|
"""
|
||||||
y=(y_p),
|
Gets the maximum absolute value of a surface
|
||||||
z=(z_p),
|
"""
|
||||||
showscale=false,
|
function Base.maximum(xs::Matrix{Float64},ys::Matrix{Float64},zs::Matrix{Float64})
|
||||||
colorscale = primary.color)
|
return maximum([ vec(xs), vec(ys), vec(zs) ])
|
||||||
|
end
|
||||||
|
|
||||||
layout = Layout(title=title,
|
"""
|
||||||
width=1000,
|
Basically rewriting Julia at this point
|
||||||
height=600,
|
"""
|
||||||
|
Base.getindex(x::Nothing, i::Int) = nothing
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generates a layout that works for most plots. Requires a title and a limit.
|
||||||
|
"""
|
||||||
|
function standard_layout(limit::Float64, title::AbstractString)
|
||||||
|
limit *= 1.1
|
||||||
|
Layout(title=title,
|
||||||
|
# width=1400,
|
||||||
|
# height=800,
|
||||||
paper_bgcolor="rgba(5,10,40,1.0)",
|
paper_bgcolor="rgba(5,10,40,1.0)",
|
||||||
plot_bgcolor="rgba(100,100,100,0.01)",
|
plot_bgcolor="rgba(100,100,100,0.01)",
|
||||||
scene = attr(xaxis = attr(autorange = false,range=[-limit,limit]),
|
scene = attr(xaxis = attr(autorange = false,range=[-limit,limit]),
|
||||||
@@ -64,8 +54,239 @@ function plot_orbits(paths::Vector{Vector{Vector{Float64}}},
|
|||||||
zaxis = attr(autorange = false,range=[-limit,limit]),
|
zaxis = attr(autorange = false,range=[-limit,limit]),
|
||||||
aspectratio=attr(x=1,y=1,z=1),
|
aspectratio=attr(x=1,y=1,z=1),
|
||||||
aspectmode="manual"))
|
aspectmode="manual"))
|
||||||
|
end
|
||||||
|
|
||||||
p = Plot([t1...,t2],layout)
|
"""
|
||||||
plot(p)
|
A fundamental function to generate a plot for a single orbit path
|
||||||
|
"""
|
||||||
|
function gen_plot(path::Vector{Vector{Float64}};
|
||||||
|
label::Union{AbstractString, Nothing} = nothing,
|
||||||
|
color::AbstractString = random_color(),
|
||||||
|
markers=true)
|
||||||
|
|
||||||
|
# Plot the orbit
|
||||||
|
if label === nothing
|
||||||
|
path_trace = scatter3d(;x=(path[1]),y=(path[2]),z=(path[3]),
|
||||||
|
mode="lines", showlegend=false, line_color=color, line_width=3)
|
||||||
|
else
|
||||||
|
path_trace = scatter3d(;x=(path[1]),y=(path[2]),z=(path[3]),
|
||||||
|
mode="lines", name=label, line_color=color, line_width=3)
|
||||||
|
end
|
||||||
|
traces = [ path_trace ]
|
||||||
|
|
||||||
|
# Plot the markers
|
||||||
|
if markers
|
||||||
|
push!(traces, scatter3d(;x=([path[1][1]]),y=([path[2][1]]),z=([path[3][1]]),
|
||||||
|
mode="markers", showlegend=false,
|
||||||
|
marker=attr(color=color, size=3, symbol="circle")))
|
||||||
|
push!(traces, scatter3d(;x=([path[1][end]]),y=([path[2][end]]),z=([path[3][end]]),
|
||||||
|
mode="markers", showlegend=false,
|
||||||
|
marker=attr(color=color, size=3, symbol="diamond")))
|
||||||
|
end
|
||||||
|
|
||||||
|
return traces, maximum(path[1:6])
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
A fundamental function to plot a single orbit path
|
||||||
|
"""
|
||||||
|
function plot(path::Vector{Vector{Float64}};
|
||||||
|
color::AbstractString = random_color(),
|
||||||
|
title::AbstractString = "Single Orbit Path",
|
||||||
|
markers=true)
|
||||||
|
|
||||||
|
# Generate the plot
|
||||||
|
p, limit = gen_plot(path, color=color, markers=markers)
|
||||||
|
|
||||||
|
# Now use the standard layout
|
||||||
|
layout = standard_layout(limit, title)
|
||||||
|
|
||||||
|
# Generate the plot
|
||||||
|
PlotlyJS.plot( PlotlyJS.Plot( p, layout ) )
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
This will generate the plot of a planetary body in it's correct colors and position in a
|
||||||
|
sun-centered frame at a certain time
|
||||||
|
|
||||||
|
If the body is the sun, no time is necessary
|
||||||
|
|
||||||
|
An optional prop time is allowed, which will also provide an orbit
|
||||||
|
"""
|
||||||
|
function gen_plot(b::Body,
|
||||||
|
time::Union{DateTime, Nothing}=nothing,
|
||||||
|
prop_time::Union{Float64, Nothing}=nothing;
|
||||||
|
scale::Int=100)
|
||||||
|
|
||||||
|
# Gotta be a little weird with Sun Scale
|
||||||
|
if b == Sun && scale > 25 scale = 25 end
|
||||||
|
|
||||||
|
# This is how many points to poll to generate the sphere
|
||||||
|
N = 32
|
||||||
|
θ = collect(range(0,length=N,stop=2π))
|
||||||
|
ϕ = collect(range(0,length=N,stop=π))
|
||||||
|
|
||||||
|
# Convert polar -> cartesian
|
||||||
|
unscaled_xs = cos.(θ) * sin.(ϕ)'
|
||||||
|
unscaled_ys = sin.(θ) * sin.(ϕ)'
|
||||||
|
unscaled_zs = repeat(cos.(ϕ)',outer=[N, 1])
|
||||||
|
|
||||||
|
# Scale by the size of the body (and a scaling factor for visualization)
|
||||||
|
xs,ys,zs = scale * b.r .* (unscaled_xs,unscaled_ys,unscaled_zs)
|
||||||
|
|
||||||
|
# Add an offset if we're not plotting the sun
|
||||||
|
body_state = zeros(6)
|
||||||
|
if b != Sun body_state = state(b, time) end
|
||||||
|
|
||||||
|
max_value = maximum(xs .+ body_state[1], ys .+ body_state[2], zs .+ body_state[3])
|
||||||
|
|
||||||
|
# Generate the surface trace
|
||||||
|
body_surface = surface(;x = xs .+ body_state[1],
|
||||||
|
y = ys .+ body_state[2],
|
||||||
|
z = zs .+ body_state[3],
|
||||||
|
showscale = false,
|
||||||
|
colorscale = b.color)
|
||||||
|
outputs = [ body_surface ]
|
||||||
|
|
||||||
|
if prop_time !== nothing
|
||||||
|
p, maybe_max = gen_plot(prop(body_state, prop_time), label=b.name, color=b.line_color, markers=false)
|
||||||
|
push!(outputs, p...)
|
||||||
|
if maybe_max > max_value max_value = maybe_max end
|
||||||
|
end
|
||||||
|
|
||||||
|
return outputs, max_value
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
This will plot a planetary body in it's correct colors and position in a
|
||||||
|
sun-centered frame at a certain time
|
||||||
|
|
||||||
|
If the body is the sun, no time is necessary
|
||||||
|
|
||||||
|
An optional prop time is allowed, which will also provide an orbit
|
||||||
|
"""
|
||||||
|
function plot(b::Body,
|
||||||
|
time::Union{DateTime, Nothing}=nothing,
|
||||||
|
prop_time::Union{Float64, Nothing}=nothing;
|
||||||
|
title::AbstractString="Single Planet Plot",
|
||||||
|
scale::Int=100)
|
||||||
|
|
||||||
|
# Generate the plot
|
||||||
|
p, limit = gen_plot(b,time,prop_time,scale=scale)
|
||||||
|
|
||||||
|
# Now use the standard layout
|
||||||
|
layout = standard_layout(limit, title)
|
||||||
|
|
||||||
|
# Generate the plot
|
||||||
|
PlotlyJS.plot( PlotlyJS.Plot( p, layout ) )
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
This will plot a vector of planetary bodies at a certain time
|
||||||
|
|
||||||
|
The Sun is plotted automatically
|
||||||
|
|
||||||
|
An optional orbits bool is allowed, which shows the orbits
|
||||||
|
"""
|
||||||
|
function plot(bodies::Vector{Body},
|
||||||
|
time::Union{DateTime, Nothing};
|
||||||
|
orbits::Bool=true,
|
||||||
|
title::AbstractString="Multiple Planet Plot",
|
||||||
|
scale::Int=100)
|
||||||
|
|
||||||
|
# Generate the plots
|
||||||
|
trace, limit = gen_plot(Sun, scale=scale)
|
||||||
|
traces = [ trace... ]
|
||||||
|
for b in bodies
|
||||||
|
if orbits
|
||||||
|
trace, new_limit = gen_plot(b,time,period(b),scale=scale)
|
||||||
|
else
|
||||||
|
trace, new_limit = gen_plot(b,time,scale=scale)
|
||||||
|
end
|
||||||
|
push!(traces, trace...)
|
||||||
|
limit = max(limit, new_limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Now use the standard layout
|
||||||
|
layout = standard_layout(limit, title)
|
||||||
|
|
||||||
|
# Generate the plot
|
||||||
|
PlotlyJS.plot( PlotlyJS.Plot( traces, layout ) )
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generic plotting function for a bunch of paths. Useful for debugging.
|
||||||
|
"""
|
||||||
|
function plot(paths::Vector{Vector{Vector{Float64}}};
|
||||||
|
labels::Union{Vector{String},Nothing}=nothing,
|
||||||
|
colors::Union{Vector{String},Nothing}=nothing,
|
||||||
|
markers::Bool=true,
|
||||||
|
title::String="Spacecraft Position")
|
||||||
|
|
||||||
|
# Generate the path traces
|
||||||
|
color = colors === nothing ? random_color() : colors[1]
|
||||||
|
trace, limit = gen_plot(paths[1],label=labels[1],color=color,markers=markers)
|
||||||
|
traces = [ trace... ]
|
||||||
|
for i = 2:length(paths)
|
||||||
|
color = colors === nothing ? random_color() : colors[i]
|
||||||
|
trace, new_limit = gen_plot(paths[i],label=labels[i],color=color,markers=markers)
|
||||||
|
push!(traces, trace...)
|
||||||
|
limit = max(limit, new_limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the sun trace
|
||||||
|
push!(traces, gen_plot(Sun)[1]...)
|
||||||
|
|
||||||
|
# Determine layout details
|
||||||
|
layout = standard_layout(limit, title)
|
||||||
|
|
||||||
|
# Plot
|
||||||
|
PlotlyJS.plot( PlotlyJS.Plot( traces, layout ) )
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function plot(m::Union{Mission, Mission_Guess}; title::String="Mision Plot")
|
||||||
|
# First plot the earth
|
||||||
|
# Then plot phase plus planet phase
|
||||||
|
time = m.launch_date
|
||||||
|
mass = m.start_mass
|
||||||
|
current_planet = Earth
|
||||||
|
earth_trace, limit = gen_plot(current_planet, time, year)
|
||||||
|
start = state(current_planet, time, m.launch_v∞, mass)
|
||||||
|
traces = [ earth_trace... ]
|
||||||
|
|
||||||
|
i = 1
|
||||||
|
for phase in m.phases
|
||||||
|
# First do the path
|
||||||
|
path, final = prop(phase.thrust_profile, start, m.sc, phase.tof, interpolate=true)
|
||||||
|
mass = final[7]
|
||||||
|
time += Dates.Second(floor(phase.tof))
|
||||||
|
current_planet = phase.planet
|
||||||
|
start = state(current_planet, time, phase.v∞_out, mass)
|
||||||
|
path_trace, new_limit = gen_plot(path, label="Phase "*string(i))
|
||||||
|
push!(traces, path_trace...)
|
||||||
|
limit = max(limit, new_limit)
|
||||||
|
|
||||||
|
# Then the planet
|
||||||
|
planet_trace, new_limit = gen_plot(current_planet, time, -phase.tof)
|
||||||
|
push!(traces, planet_trace...)
|
||||||
|
limit = max(limit, new_limit)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the sun trace
|
||||||
|
push!(traces, gen_plot(Sun)[1]...)
|
||||||
|
|
||||||
|
# Determine layout details
|
||||||
|
layout = standard_layout(limit, title)
|
||||||
|
|
||||||
|
# Plot
|
||||||
|
PlotlyJS.plot( PlotlyJS.Plot( traces, layout ) )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ end
|
|||||||
|
|
||||||
const test_sc = Sc(8000., 0.00025/(2000*0.00981), 0.00025, 50, 0.9)
|
const test_sc = Sc(8000., 0.00025/(2000*0.00981), 0.00025, 50, 0.9)
|
||||||
const bepi = Sc(8000., 2*0.00025/(2000*0.00981), 0.00025, 2, 0.9)
|
const bepi = Sc(8000., 2*0.00025/(2000*0.00981), 0.00025, 2, 0.9)
|
||||||
const no_thrust = Sc(8000., 0.01, 0., 0, 0.)
|
const no_thrust = Sc(0., 0.01, 0., 0, 0.)
|
||||||
|
|||||||
@@ -1,36 +1,48 @@
|
|||||||
@testset "Phase" begin
|
@testset "Phase" begin
|
||||||
|
|
||||||
|
using PlotlyJS: savefig
|
||||||
|
|
||||||
println("Testing NLP solver")
|
println("Testing NLP solver")
|
||||||
|
|
||||||
# We'll start by testing the mission_guess -> vector function
|
# Test the optimizer for a one-phase mission
|
||||||
vec = Vector(test_mg)
|
# The lambert's solver said this should be pretty valid
|
||||||
@test typeof(vec) == Vector{Float64}
|
launch_window = [DateTime(1992,11,1), DateTime(1992,12,1)]
|
||||||
|
latest_arrival = DateTime(1993,6,1)
|
||||||
# Now we go in the other direction
|
|
||||||
flybys = [ p.planet for p in test_mg.phases ]
|
|
||||||
guess = Mission_Guess(vec, test_mg.sc, test_mg.start_mass, flybys)
|
|
||||||
@test typeof(guess) == Mission_Guess
|
|
||||||
@test guess.sc == test_mg.sc
|
|
||||||
@test guess.start_mass == test_mg.start_mass
|
|
||||||
@test guess.launch_date == test_mg.launch_date
|
|
||||||
@test guess.launch_v∞ == test_mg.launch_v∞
|
|
||||||
@test guess.converged == test_mg.converged
|
|
||||||
for i in 1:length(guess.phases)
|
|
||||||
@test guess.phases[i].planet == test_mg.phases[i].planet
|
|
||||||
@test guess.phases[i].v∞_in == test_mg.phases[i].v∞_in
|
|
||||||
@test guess.phases[i].v∞_out == test_mg.phases[i].v∞_out
|
|
||||||
@test guess.phases[i].tof == test_mg.phases[i].tof
|
|
||||||
@test guess.phases[i].thrust_profile == test_mg.phases[i].thrust_profile
|
|
||||||
end
|
|
||||||
|
|
||||||
# Now we test an example run of the basic "inner function"
|
|
||||||
leave, arrive = DateTime(1992,11,19), DateTime(1993,4,1)
|
leave, arrive = DateTime(1992,11,19), DateTime(1993,4,1)
|
||||||
|
test_leave = DateTime(1992,11,12)
|
||||||
earth_state = state(Earth, leave)
|
earth_state = state(Earth, leave)
|
||||||
venus_state = state(Venus, arrive)
|
venus_state = state(Venus, arrive)
|
||||||
v∞_out, v∞_in, tof = Thesis.lamberts(Earth, Venus, leave, arrive)
|
v∞_out, v∞_in, tof = Thesis.lamberts(Earth, Venus, leave, arrive)
|
||||||
phase = Phase(Venus, v∞_in, v∞_in, tof, zeros(20,3))
|
# We can get the thrust profile and tof pretty wrong and still be ok
|
||||||
guess = Mission_Guess(bepi, 12_000., leave, v∞_out, [phase])
|
phase = Phase(Venus, 1.1v∞_in, v∞_in, 0.9*tof, 0.1*ones(20,3))
|
||||||
g = solve_mission(guess)
|
guess = Mission_Guess(bepi, 12_000., test_leave, 0.9*v∞_out, [phase])
|
||||||
println(g)
|
m = solve_mission(guess, launch_window, latest_arrival, verbose=true)
|
||||||
|
@test typeof(m) == Mission
|
||||||
|
@test m.converged == true
|
||||||
|
|
||||||
|
# Now we can plot the results to check visually
|
||||||
|
p = plot(m, title="NLP Test Solution")
|
||||||
|
savefig(p,"../plots/nlp_test_1_phase.html")
|
||||||
|
|
||||||
|
# Now we can look at a more complicated trajectory
|
||||||
|
flybys = [Earth, Venus, Mars]
|
||||||
|
launch_window = [DateTime(2021,10,1), DateTime(2021,12,1)]
|
||||||
|
latest_arrival = DateTime(2023,1,1)
|
||||||
|
dates = [DateTime(2021,11,1), DateTime(2022,3,27), DateTime(2022,8,28)]
|
||||||
|
phases = Vector{Phase}()
|
||||||
|
launch_v∞, _, tof1 = Thesis.lamberts(flybys[1], flybys[2], dates[1], dates[2])
|
||||||
|
println(launch_v∞)
|
||||||
|
for i in 1:length(dates)-2
|
||||||
|
v∞_out1, v∞_in1, tof1 = Thesis.lamberts(flybys[i], flybys[i+1], dates[i], dates[i+1])
|
||||||
|
v∞_out2, v∞_in2, tof2 = Thesis.lamberts(flybys[i+1], flybys[i+2], dates[i+1], dates[i+2])
|
||||||
|
push!(phases, Phase(flybys[i+1], v∞_in1, v∞_out2, tof1, 0.01*ones(20,3)))
|
||||||
|
end
|
||||||
|
v∞_out, v∞_in, tof = Thesis.lamberts(flybys[end-1], flybys[end], dates[end-1], dates[end])
|
||||||
|
push!(phases, Phase(flybys[end], v∞_in, v∞_in, tof, 0.01*ones(20,3)))
|
||||||
|
# This isn't quite right. V∞ discrepancy is too big
|
||||||
|
guess = Mission_Guess(bepi, 12_000., dates[1], launch_v∞, phases)
|
||||||
|
m = solve_mission(guess, launch_window, latest_arrival, verbose=true)
|
||||||
|
p = plot(m, title="NLP Test Solution (2 Phases)")
|
||||||
|
savefig(p,"../plots/nlp_test_2_phase.html")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,34 +2,55 @@
|
|||||||
|
|
||||||
println("Testing plotting features")
|
println("Testing plotting features")
|
||||||
|
|
||||||
using PlotlyJS
|
using PlotlyJS: savefig, SyncPlot
|
||||||
|
|
||||||
# Plot an earth plot
|
# Generate a couple of useful paths
|
||||||
T = rand(2hour : 1 : 4hour)
|
|
||||||
revs = 30
|
|
||||||
n = revs*100
|
|
||||||
|
|
||||||
start = gen_orbit(T, 12_000., Earth)
|
|
||||||
thrust = spiral(0.9, n, start, test_sc, revs*T, Earth)
|
|
||||||
path = prop(thrust, start, test_sc, revs*T, Earth)[1]
|
|
||||||
|
|
||||||
p = plot_orbits([path], Earth)
|
|
||||||
savefig(p,"../plots/plot_test_earth.html")
|
|
||||||
@test typeof(p) == PlotlyJS.SyncPlot
|
|
||||||
|
|
||||||
# Now change a little bit and plot around the Sun
|
|
||||||
# This also checks that the spacecraft are configured right:
|
|
||||||
# They really shouldn't run out of fuel in 4 years
|
|
||||||
T = rand(year : hour : 4year)
|
T = rand(year : hour : 4year)
|
||||||
tof = 4year
|
tof = 4year
|
||||||
|
n = 4_000
|
||||||
start = gen_orbit(T, 12_000.)
|
start = gen_orbit(T, 12_000.)
|
||||||
thrust = spiral(0.9, n, start, bepi, tof)
|
thrust = spiral(0.9, n, start, bepi, tof)
|
||||||
sun_paths = Vector{Vector{Vector{Float64}}}()
|
spiral_path = prop(thrust, start, bepi, tof)[1]
|
||||||
push!(sun_paths, prop(zeros(100,3), start, bepi, tof)[1])
|
no_thrust_path = prop(zeros(n,3), start, bepi, tof)[1]
|
||||||
push!(sun_paths, prop(thrust, start, bepi, tof)[1])
|
paths = [ spiral_path, no_thrust_path ]
|
||||||
p = plot_orbits(sun_paths)
|
|
||||||
savefig(p,"../plots/plot_test_sun.html")
|
|
||||||
@test typeof(p) == PlotlyJS.SyncPlot
|
|
||||||
|
|
||||||
|
# First let's plot a single basic path
|
||||||
|
p = plot(spiral_path)
|
||||||
|
savefig(p,"../plots/plot_test_path.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
|
# Next I just want to plot a planet both not moving...
|
||||||
|
p = plot(Jupiter, Dates.now(), title="Jupiter")
|
||||||
|
savefig(p,"../plots/plot_test_planet_nomove.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
|
# ...and moving
|
||||||
|
p = plot(Venus, Dates.now(), 0.95period(Venus), title="Venus")
|
||||||
|
savefig(p,"../plots/plot_test_planet_move.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
|
# Next I want to plot the entire solar system
|
||||||
|
p = plot([Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto],
|
||||||
|
Dates.now(),
|
||||||
|
title="The Solar System")
|
||||||
|
savefig(p,"../plots/plot_test_solar_system.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
|
# Next I want to plot a vector of states
|
||||||
|
p = plot(paths,
|
||||||
|
labels = ["No Thrust Orbit","Thrust Spiral"],
|
||||||
|
title = "Vector of Paths Plot Test")
|
||||||
|
savefig(p,"../plots/plot_test_paths.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
|
# And finally, I'd like to plot a mission guess...
|
||||||
|
p = plot(test_mg)
|
||||||
|
savefig(p,"../plots/plot_test_mission_guess.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
|
# ...and a mission
|
||||||
|
p = plot(Mission(test_mg))
|
||||||
|
savefig(p,"../plots/plot_test_mission.html")
|
||||||
|
@test typeof(p) == SyncPlot
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ catch
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "All Tests" begin
|
@testset "All Tests" begin
|
||||||
# include("plotting.jl")
|
include("plotting.jl")
|
||||||
# include("inner_loop/laguerre-conway.jl")
|
include("inner_loop/laguerre-conway.jl")
|
||||||
# include("inner_loop/propagator.jl")
|
include("inner_loop/propagator.jl")
|
||||||
include("inner_loop/nlp_solver.jl")
|
include("inner_loop/nlp_solver.jl")
|
||||||
# include("inner_loop/monotonic_basin_hopping.jl")
|
# include("inner_loop/monotonic_basin_hopping.jl")
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user