diff --git a/tp/space.jl b/tp/space.jl new file mode 100644 index 0000000000000000000000000000000000000000..1f9c72aaf5b65ac8ff8f09316865a8daefccd9ee --- /dev/null +++ b/tp/space.jl @@ -0,0 +1,849 @@ +module Space + + using Plots + using Plots.PlotMeasures + using SparseArrays + using Printf + using DifferentialEquations + using LinearAlgebra # norm + + export animation + + # -------------------------------------------------------------------------------------------- + x0 = [-42272.67, 0, 0, -5796.72] # état initial + μ = 5.1658620912*1e12 + rf = 42165.0 + t0 = 0 + global γ_max = nothing + global F_max = nothing + + # Maximizing control + function control(p) + u = zeros(eltype(p),2) + u[1] = p[3]*γ_max/norm(p[3:4]) + u[2] = p[4]*γ_max/norm(p[3:4]) + return u + end + + # -------------------------------------------------------------------------------------------- + # Données pour la trajectoire durant le transfert + mutable struct Transfert + initial_adjoint + duration + flow + end + + # données pour la trajectoire pré-transfert + mutable struct PreTransfert + initial_point + duration + end + + # données pour la trajectoire post-transfert + mutable struct PostTransfert + duration + end + + # -------------------------------------------------------------------------------------------- + # Default options for flows + # -------------------------------------------------------------------------------------------- + function __abstol() + return 1e-10 + end + + function __reltol() + return 1e-10 + end + + function __saveat() + return [] + end + + # -------------------------------------------------------------------------------------------- + # + # Vector Field + # + # -------------------------------------------------------------------------------------------- + struct VectorField f::Function end + + function (vf::VectorField)(x) # https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects + return vf.f(x) + end + + # Flow of a vector field + function Flow(vf::VectorField) + + function rhs!(dx, x, dummy, t) + dx[:] = vf(x) + end + + function f(tspan, x0; abstol=__abstol(), reltol=__reltol(), saveat=__saveat()) + ode = ODEProblem(rhs!, x0, tspan) + sol = solve(ode, Tsit5(), abstol=abstol, reltol=reltol, saveat=saveat) + return sol + end + + function f(t0, x0, t; abstol=__abstol(), reltol=__reltol(), saveat=__saveat()) + sol = f((t0, t), x0; abstol=abstol, reltol=reltol, saveat=saveat) + n = size(x0, 1) + return sol[1:n, end] + end + + return f + + end + + # Kepler's law + function kepler(x) + n = size(x, 1) + dx = zeros(eltype(x), n) + x1 = x[1] + x2 = x[2] + x3 = x[3] + x4 = x[4] + dsat = norm(x[1:2]); r3 = dsat^3; + dx[1] = x3 + dx[2] = x4 + dx[3] = -μ*x1/r3 + dx[4] = -μ*x2/r3 + return dx + end + + KeplerFlow = Flow(VectorField(kepler)); + + function animation(p0, tf, f, γ_max; fps=10, nFrame=100) + # + t_pre_transfert = 2*5.302395297743802 + x_pre_transfert = x0 + pre_transfert_data = PreTransfert(x_pre_transfert, t_pre_transfert) + # + transfert_data = Transfert(p0, tf, f) + # + t_post_transfert = 20 + post_transfert_data = PostTransfert(t_post_transfert) + # + __animation(pre_transfert_data, transfert_data, post_transfert_data, KeplerFlow, γ_max, fps=fps, nFrame=nFrame) + end + + function __animation(pre_transfert_data, transfert_data, post_transfert_data, f0, _γ_max; fps=10, nFrame=200) + + # + global F_max = _γ_max*2000*10^3/3600^2 + global γ_max = _γ_max + + # pré-transfert + t_pre_transfert = pre_transfert_data.duration + x_pre_transfert = pre_transfert_data.initial_point + + # transfert + p0_transfert = transfert_data.initial_adjoint + tf_transfert = transfert_data.duration + f = transfert_data.flow + + # post-trasfert + t_post_transfert = post_transfert_data.duration + + # On calcule les orbites initiale et finale + r0 = norm(x0[1:2]) + v0 = norm(x0[3:4]) + a = 1.0/(2.0/r0-v0*v0/μ) + t1 = r0*v0*v0/μ - 1.0; + t2 = (x0[1:2]'*x0[3:4])/sqrt(a*μ); + e_ellipse = norm([t1 t2]) + p_orb = a*(1-e_ellipse^2); + n_theta = 151 + Theta = range(0.0, stop=2*pi, length=n_theta) + X1_orb_init = zeros(n_theta) + X2_orb_init = zeros(n_theta) + X1_orb_arr = zeros(n_theta) + X2_orb_arr = zeros(n_theta) + + for i in 1:n_theta + theta = Theta[i] + r_orb = p_orb/(1+e_ellipse*cos(theta)); + # Orbite initiale + X1_orb_init[i] = r_orb*cos(theta); + X2_orb_init[i] = r_orb*sin(theta); + # Orbite finale + X1_orb_arr[i] = rf*cos(theta) ; + X2_orb_arr[i] = rf*sin(theta); + end + + # Centre de la fenêtre + cx = 40000 + cy = -7000 + + # Taille de la fenêtre + w = 1600 + h = 900 + ρ = h/w + + # Limites de la fenêtre + ee = 0.5 + xmin = minimum([X1_orb_init; X1_orb_arr]); xmin = xmin - ee * abs(xmin); + xmax = maximum([X1_orb_init; X1_orb_arr]); xmax = xmax + ee * abs(xmax); + ymin = minimum([X2_orb_init; X2_orb_arr]); ymin = ymin - ee * abs(ymin); + ymax = maximum([X2_orb_init; X2_orb_arr]); ymax = ymax + ee * abs(ymax); + + Δy = ymax-ymin + Δx = Δy/ρ + Δn = Δx - (xmax-xmin) + xmin = xmin - Δn/2 + xmax = xmax + Δn/2 + + # Trajectoire pré-transfert + traj_pre_transfert = f0((0.0, t_pre_transfert), x_pre_transfert) + times_pre_transfert = traj_pre_transfert.t + n_pre_transfert = size(times_pre_transfert, 1) + x1_pre_transfert = [traj_pre_transfert[1, j] for j in 1:n_pre_transfert ] + x2_pre_transfert = [traj_pre_transfert[2, j] for j in 1:n_pre_transfert ] + v1_pre_transfert = [traj_pre_transfert[3, j] for j in 1:n_pre_transfert ] + v2_pre_transfert = [traj_pre_transfert[4, j] for j in 1:n_pre_transfert ] + + # Trajectoire pendant le transfert + traj_transfert = f((t0, tf_transfert), x0, p0_transfert) + times_transfert = traj_transfert.t + n_transfert = size(times_transfert, 1) + x1_transfert = [traj_transfert[1, j] for j in 1:n_transfert ] + x2_transfert = [traj_transfert[2, j] for j in 1:n_transfert ] + v1_transfert = [traj_transfert[3, j] for j in 1:n_transfert ] + v2_transfert = [traj_transfert[4, j] for j in 1:n_transfert ] + u_transfert = zeros(2, length(times_transfert)) + for j in 1:n_transfert + u_transfert[:,j] = control(traj_transfert[5:8, j]) + end + u_transfert = u_transfert./γ_max # ||u|| ≤ 1 + + # post-transfert + x_post_transfert = traj_transfert[:,end] + + # Trajectoire post-transfert + traj_post_transfert = f0((0.0, t_post_transfert), x_post_transfert) + times_post_transfert = traj_post_transfert.t + n_post_transfert = size(times_post_transfert, 1) + x1_post_transfert = [traj_post_transfert[1, j] for j in 1:n_post_transfert ] + x2_post_transfert = [traj_post_transfert[2, j] for j in 1:n_post_transfert ] + v1_post_transfert = [traj_post_transfert[3, j] for j in 1:n_post_transfert ] + v2_post_transfert = [traj_post_transfert[4, j] for j in 1:n_post_transfert ] + + # Angles de rotation du satellite pendant le pré-transfert + # Et poussée normalisée entre 0 et 1 + θ0 = atan(u_transfert[2, 1], u_transfert[1, 1]) + θ_pre_transfert = range(π/2, mod(θ0, 2*π), length=n_pre_transfert) + F_pre_transfert = zeros(1, n_pre_transfert) + + # Angles de rotation du satellite pendant le transfert + θ_transfert = atan.(u_transfert[2, :], u_transfert[1, :]) + F_transfert = zeros(1, n_transfert) + for j in 1:n_transfert + F_transfert[j] = norm(u_transfert[:,j]) + end + + # Angles de rotation du satellite pendant le post-transfert + θ1 = atan(u_transfert[2, end], u_transfert[1, end]) + θ2 = atan(-x2_post_transfert[end], -x1_post_transfert[end]) + θ_post_transfert = range(mod(θ1, 2*π), mod(θ2, 2*π), length=n_post_transfert) + F_post_transfert = zeros(1, n_post_transfert) + + # Etoiles + Δx = xmax-xmin + Δy = ymax-ymin + ρ = Δy/Δx + S = stars(ρ) + + # nombre total d'images + nFrame = min(nFrame, n_pre_transfert+n_transfert+n_post_transfert); + + # Pour l'affichage de la trajectoire globale + times = [times_pre_transfert[1:end-1]; + times_pre_transfert[end].+times_transfert[1:end-1]; + times_pre_transfert[end].+times_transfert[end].+times_post_transfert[1:end]] + x1 = [x1_pre_transfert[1:end-1]; x1_transfert[1:end-1]; x1_post_transfert[:]] + x2 = [x2_pre_transfert[1:end-1]; x2_transfert[1:end-1]; x2_post_transfert[:]] + v1 = [v1_pre_transfert[1:end-1]; v1_transfert[1:end-1]; v1_post_transfert[:]] + v2 = [v2_pre_transfert[1:end-1]; v2_transfert[1:end-1]; v2_post_transfert[:]] + θ = [ θ_pre_transfert[1:end-1]; θ_transfert[1:end-1]; θ_post_transfert[:]] + F = [ F_pre_transfert[1:end-1]; F_transfert[1:end-1]; F_post_transfert[:]] + + # plot thrust on/off + th = [BitArray(zeros(n_pre_transfert-1)); + BitArray(ones(n_transfert-1)); + BitArray(zeros(n_post_transfert))] + + # plot trajectory + pt = [BitArray(ones(n_pre_transfert-1)); + BitArray(ones(n_transfert-1)); + BitArray(zeros(n_post_transfert))] + + # Contrôle sur la trajectoire totale + u_total = hcat([zeros(2, n_pre_transfert-1), + u_transfert[:, 1:n_transfert-1], + zeros(2, n_post_transfert)]...) + + # temps total + temps_transfert_global = times[end] + + # pas de temps pour le transfert global + if nFrame>1 + Δt = temps_transfert_global/(nFrame-1) + else + Δt = 0.0 + end + + # opacités des orbites initiale et finale + op_initi = [range(0.0, 1.0, length=n_pre_transfert-1); + range(1.0, 0.0, length=n_transfert-1); + zeros(n_post_transfert)] + op_final = [zeros(n_pre_transfert-1); + range(0.0, 1.0, length=n_transfert-1); + range(1.0, 0.0, length=int(n_post_transfert/4)); + zeros(n_post_transfert-int(n_post_transfert/4))] + + println("Fps = ", fps) + println("NFrame = ", nFrame) + println("Vitesse = ", nFrame/(temps_transfert_global*fps)) + println("Durée totale de la mission = ", temps_transfert_global, " h") + + # animation + anim = @animate for i ∈ 1:nFrame + + # Δt : pas de temps + # time_current : temps courant de la mission totale à l'itération i + # i_current : indice tel que times[i_current] = time_current + # w, h : width, height de la fenêtre + # xmin, xmax, ymin, ymax : limites des axes du plot principal + # X1_orb_init, X2_orb_init : coordonnées de l'orbite initiale + # X1_orb_arr, X2_orb_arr : coordonnées de l'orbite finale + # cx, cy : coordonées du centre de l'affichage du tranfert + # S : data pour les étoiles + # Δx, Δy : xmax-xmin, ymax-ymin + # times : tous les temps de la mission complète, ie pre-transfert, transfert et post-transfert + # x1, x2 : vecteur de positions du satellite + # θ : vecteur d'orientations du satellite + # th : vecteur de booléens - thrust on/off + # u_total : vecteur de contrôles pour toute la mission + # F_max, γ_max : poussée max + # subplot_current : valeur du subplot courant + # cam_x, cam_y : position de la caméra + # cam_zoom : zoom de la caméra + + cam_x = cx + cam_y = cy + cam_zoom = 1 + + time_current = (i-1)*Δt + i_current = argmin(abs.(times.-time_current)) + + px = background(w, h, xmin, xmax, ymin, ymax, + X1_orb_init, X2_orb_init, X1_orb_arr, X2_orb_arr, + cx, cy, S, Δx, Δy, cam_x, cam_y, cam_zoom, + op_initi[i_current], op_final[i_current], times, time_current) + + trajectoire!(px, times, x1, x2, θ, F, th, time_current, cx, cy, pt) + + subplot_current = 2 + subplot_current = panneau_control!(px, time_current, times, u_total, + F_max, subplot_current) + + subplot_current = panneau_information!(px, subplot_current, time_current, + i_current, x1, x2, v1, v2, θ, F_max, tf_transfert, X1_orb_init, + X2_orb_init, X1_orb_arr, X2_orb_arr) + + end + + # enregistrement + #gif(anim, "transfert-temps-min-original.mp4", fps=fps); + gif(anim, "transfert-temps-min.gif", fps=fps); + + end; + + # -------------------------------------------------------------------------------------------- + # Preliminaries + int(x) = floor(Int, x); + rayon_Terre = 6371; + sc_sat = 1000; # scaling for the satellite + + # -------------------------------------------------------------------------------------------- + # + # Satellite + # + # -------------------------------------------------------------------------------------------- + # Corps du satellite + @userplot SatBody + @recipe function f(cp::SatBody) + x, y = cp.args + seriestype --> :shape + fillcolor --> :goldenrod1 + linecolor --> :goldenrod1 + x, y + end + + @userplot SatLine + @recipe function f(cp::SatLine) + x, y = cp.args + linecolor --> :black + x, y + end + + # Bras du satellite + @userplot SatBras + @recipe function f(cp::SatBras) + x, y = cp.args + seriestype --> :shape + fillcolor --> :goldenrod1 + linecolor --> :white + x, y + end + + # panneau + @userplot PanBody + @recipe function f(cp::PanBody) + x, y = cp.args + seriestype --> :shape + fillcolor --> :dodgerblue4 + linecolor --> :black + x, y + end + + # flamme + @userplot Flamme + @recipe function f(cp::Flamme) + x, y = cp.args + seriestype --> :shape + fillcolor --> :darkorange1 + linecolor --> false + x, y + end + + function satellite!(pl; subplot=1, thrust=false, position=[0;0], scale=1, rotate=0, thrust_val=1.0) + + # Fonctions utiles + R(θ) = [ cos(θ) -sin(θ) + sin(θ) cos(θ)] # rotation + T(x, v) = x.+v # translation + H(λ, x) = λ.*x # homotéthie + SA(x, θ, c) = c .+ 2*[cos(θ);sin(θ)]'*(x.-c).*[cos(θ);sin(θ)]-(x.-c) # symétrie axiale + + # + O = position + Rθ = R(rotate) + + # Paramètres + α = π/10.0 # angle du tube, par rapport à l'horizontal + β = π/2-π/40 # angle des bras, par rapport à l'horizontal + Lp = scale.*1.4*cos(α) # longueur d'un panneau + lp = scale.*2.6*sin(α) # largueur d'un panneau + + # Param bras du satellite + lb = scale.*3*cos(α) # longueur des bras + eb = scale.*cos(α)/30 # demi largeur des bras + xb = 0.0 + yb = scale.*sin(α) + + # Paramètres corps du satellite + t = range(-α, α, length=50) + x = scale.*cos.(t); + y = scale.*sin.(t); + Δx = scale.*cos(α) + Δy = scale.*sin(α) + + # Paramètres flamme + hF = yb # petite hauteur + HF = 2*hF # grande hauteur + LF = 5.5*Δx # longueur + + # Dessin bras du satellite + M = T(Rθ*[ [xb-eb, xb+eb, xb+eb+lb*cos(β), xb-eb+lb*cos(β), xb-eb]'; + [yb, yb, yb+lb*sin(β), yb+lb*sin(β), yb]'], O); + satbras!(pl, M[1,:], M[2,:], subplot=subplot) + M = T(Rθ*[ [xb-eb, xb+eb, xb+eb+lb*cos(β), xb-eb+lb*cos(β), xb-eb]'; + -[yb, yb, yb+lb*sin(β), yb+lb*sin(β), yb']'], O); + satbras!(pl, M[1,:], M[2,:], subplot=subplot) + + # Dessin corps du satellite + M = T(Rθ*[ x'; y'], O); satbody!(pl, M[1,:], M[2,:], subplot=subplot) # bord droit + M = T(Rθ*[-x'; y'], O); satbody!(pl, M[1,:], M[2,:], subplot=subplot) # bord gauche + M = T(Rθ*[scale.*[-cos(α), cos(α), cos(α), -cos(α)]' + scale.*[-sin(α), -sin(α), sin(α), sin(α)]'], O); + satbody!(pl, M[1,:], M[2,:], subplot=subplot) # interieur + + M = T(Rθ*[ x'; y'], O); satline!(pl, M[1,:], M[2,:], subplot=subplot) # bord droit + M = T(Rθ*[-x'; y'], O); satline!(pl, M[1,:], M[2,:], subplot=subplot) # bord gauche + M = T(Rθ*[ x'.-2*Δx; y'], O); satline!(pl, M[1,:], M[2,:], subplot=subplot) # bord gauche (droite) + M = T(Rθ*[ scale.*[-cos(α), cos(α)]'; + scale.*[ sin(α), sin(α)]'], O); + satline!(pl, M[1,:], M[2,:], subplot=subplot) # haut + M = T(Rθ*[ scale.*[-cos(α), cos(α)]'; + -scale.*[ sin(α), sin(α)]'], O); + satline!(pl, M[1,:], M[2,:], subplot=subplot) # bas + + # Panneau + ep = (lb-3*lp)/6 + panneau = [0 Lp Lp 0 + 0 0 lp lp] + + ey = 3*eb # eloignement des panneaux au bras + vy = [cos(β-π/2); sin(β-π/2)] .* ey + v0 = [0; yb] + v1 = 2*ep*[cos(β); sin(β)] + v2 = (3*ep+lp)*[cos(β); sin(β)] + v3 = (4*ep+2*lp)*[cos(β); sin(β)] + + pa1 = T(R(β-π/2)*panneau, v0+v1+vy); pa = T(Rθ*pa1, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa2 = T(R(β-π/2)*panneau, v0+v2+vy); pa = T(Rθ*pa2, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa3 = T(R(β-π/2)*panneau, v0+v3+vy); pa = T(Rθ*pa3, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + + pa4 = SA(pa1, β, [xb; yb]); pa = T(Rθ*pa4, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa5 = SA(pa2, β, [xb; yb]); pa = T(Rθ*pa5, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa6 = SA(pa3, β, [xb; yb]); pa = T(Rθ*pa6, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + + pa7 = SA(pa1, 0, [0; 0]); pa = T(Rθ*pa7, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa8 = SA(pa2, 0, [0; 0]); pa = T(Rθ*pa8, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa9 = SA(pa3, 0, [0; 0]); pa = T(Rθ*pa9, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + + pa10 = SA(pa7, -β, [xb; -yb]); pa = T(Rθ*pa10, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa11 = SA(pa8, -β, [xb; -yb]); pa = T(Rθ*pa11, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + pa12 = SA(pa9, -β, [xb; -yb]); pa = T(Rθ*pa12, O); panbody!(pl, pa[1,:], pa[2,:], subplot=subplot) + + # Dessin flamme + if thrust + lthrust_max = 8000.0 + origin = T(Rθ*[Δx, 0.0], O) + thrust_ = thrust_val*Rθ*[lthrust_max, 0.0] + quiver!(pl, [origin[1]], [origin[2]], color=:red, + quiver=([thrust_[1]], [thrust_[2]]), linewidth=1, subplot=subplot) + end + + end; + + # -------------------------------------------------------------------------------------------- + # + # Stars + # + # -------------------------------------------------------------------------------------------- + @userplot StarsPlot + @recipe function f(cp::StarsPlot) + xo, yo, Δx, Δy, I, A, m, n = cp.args + seriestype --> :scatter + seriescolor --> :white + seriesalpha --> A + markerstrokewidth --> 0 + markersize --> 2*I[3] + xo.+I[2].*Δx./n, yo.+I[1].*Δy./m + end + + mutable struct Stars + s + i + m + n + a + end + + # Etoiles + function stars(ρ) + n = 200 # nombre de colonnes + m = int(ρ*n) # nombre de lignes + d = 0.03 + s = sprand(m, n, d) + i = findnz(s) + s = dropzeros(s) + # alpha + amin = 0.6 + amax = 1.0 + Δa = amax-amin + a = amin.+rand(length(i[3])).*Δa + return Stars(s, i, m, n, a) + end; + + # -------------------------------------------------------------------------------------------- + # + # Trajectory + # + # -------------------------------------------------------------------------------------------- + @userplot TrajectoryPlot + @recipe function f(cp::TrajectoryPlot) + t, x, y, tf = cp.args + n = argmin(abs.(t.-tf)) + inds = 1:n + seriescolor --> :white + linewidth --> range(0.0, 3.0, length=n) + seriesalpha --> range(0.0, 1.0, length=n) + aspect_ratio --> 1 + label --> false + x[inds], y[inds] + end + + function trajectoire!(px, times, x1, x2, θ, F, thrust, t_current, cx, cy, pt) + + i_current = argmin(abs.(times.-t_current)) + + # Trajectoire + if t_current>0 && pt[i_current] + trajectoryplot!(px, times, cx.+x1, cy.+x2, t_current) + end + + # Satellite + satellite!(px, position=[cx+x1[i_current]; cy+x2[i_current]], scale=sc_sat, + rotate=θ[i_current], thrust=thrust[i_current], thrust_val=F[i_current]) + + end; + + # -------------------------------------------------------------------------------------------- + # + # Background + # + # -------------------------------------------------------------------------------------------- + # Décor + function background(w, h, xmin, xmax, ymin, ymax, + X1_orb_init, X2_orb_init, X1_orb_arr, X2_orb_arr, + cx, cy, S, Δx, Δy, cam_x, cam_y, cam_zoom, opi, opf, times, time_current) + + # Fond + px = plot(background_color=:gray8, legend = false, aspect_ratio=:equal, + size = (w, h), framestyle = :none, + left_margin=-25mm, bottom_margin=-10mm, top_margin=-10mm, right_margin=-10mm, + xlims=(xmin, xmax), ylims=(ymin, ymax) #, + #camera=(0, 90), xlabel = "x", ylabel= "y", zlabel = "z", right_margin=25mm + ) + + # Etoiles + starsplot!(px, xmin, ymin, Δx, Δy, S.i, S.a, S.m, S.n) + + starsplot!(px, xmin-Δx, ymin-Δy, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin, ymin-Δy, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin, ymin+Δy, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin-Δx, ymin, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin+Δx, ymin, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin-Δx, ymin+Δy, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin, ymin+Δy, Δx, Δy, S.i, S.a, S.m, S.n) + starsplot!(px, xmin+Δx, ymin+Δy, Δx, Δy, S.i, S.a, S.m, S.n) + + # Orbite initiale + nn = length(X2_orb_init) + plot!(px, cx.+X1_orb_init, cy.+X2_orb_init, color = :olivedrab1, linewidth=2, alpha=opi) + + # Orbite finale + nn = length(X2_orb_init) + plot!(px, cx.+X1_orb_arr, cy.+X2_orb_arr, color = :turquoise1, linewidth=2, alpha=opf) + + # Terre + θ = range(0.0, 2π, length=100) + rT = rayon_Terre #- 1000 + xT = cx .+ rT .* cos.(θ) + yT = cy .+ rT .* sin.(θ) + nn = length(xT) + plot!(px, xT, yT, color = :dodgerblue1, seriestype=:shape, linewidth=0) + + # Soleil + i_current = argmin(abs.(times.-time_current)) + e = π/6 + β = range(π/4+e, π/4-e, length=length(times)) + ρ = sqrt((0.8*xmax-cx)^2+(0.8*ymax-cy)^2) + cxS = cx + ρ * cos(β[i_current]) + cyS = cy + ρ * sin(β[i_current]) + θ = range(0.0, 2π, length=100) + rS = 2000 + xS = cxS .+ rS .* cos.(θ) + yS = cyS .+ rS .* sin.(θ) + plot!(px, xS, yS, color = :gold, seriestype=:shape, linewidth=0) + + # Point de départ + #plot!(px, [cx+x0[1]], [cy+x0[2]], seriestype=:scatter, color = :white, markerstrokewidth=0) + + # Cadre + #plot!(px, [xmin, xmax, xmax, xmin, xmin], [ymin, ymin, ymax, ymax, ymin], color=:white) + + # Angle + #plot!(px, camera=(30,90)) + + return px + + end; + + function panneau_control!(px, time_current, times, u, F_max, subplot_current) + + # We set the (optional) position relative to top-left of the 1st subplot. + # The call is `bbox(x, y, width, height, origin...)`, + # where numbers are treated as "percent of parent". + Δt_control = 2 #0.1*times[end] + + xx = 0.08 + yy = 0.1 + plot!(px, times, F_max.*u[1,:], + inset = (1, bbox(xx, yy, 0.2, 0.15, :top, :left)), + subplot = subplot_current, + bg_inside = nothing, + color=:red, legend=:true, yguidefontrotation=-90, yguidefonthalign = :right, + xguidefontsize=14, legendfontsize=14, + ylims=(-F_max,F_max), + xlims=(time_current-Δt_control, time_current+1.0*Δt_control), + ylabel="u₁ [N]", label="" + ) + + plot!(px, + [time_current, time_current], [-F_max,F_max], + subplot = subplot_current, label="") + + plot!(px, times, F_max.*u[2,:], + inset = (1, bbox(xx, yy+0.2, 0.2, 0.15, :top, :left)), + #ticks = nothing, + subplot = subplot_current+1, + bg_inside = nothing, + color=:red, legend=:true, yguidefontrotation=-90, yguidefonthalign = :right, + xguidefontsize=14, legendfontsize=14, + ylims=(-F_max,F_max), + xlims=(time_current-Δt_control, time_current+1.0*Δt_control), + ylabel="u₂ [N]", xlabel="temps [h]", label="" + ) + + plot!(px, + [time_current, time_current], [-F_max,F_max], + subplot = subplot_current+1, label="") + + return subplot_current+2 + end; + + @enum PB tfmin=1 consomin=2 + + function panneau_information!(px, subplot_current, time_current, i_current, + x1, x2, v1, v2, θ, F_max, tf_transfert, X1_orb_init, X2_orb_init, X1_orb_arr, X2_orb_arr; + pb=tfmin, tf_min=0.0, conso=[]) + + # panneaux information + xx = 0.06 + yy = 0.3 + plot!(px, + inset = (1, bbox(xx, yy, 0.2, 0.15, :bottom, :left)), + subplot=subplot_current, + bg_inside = nothing, legend=:false, + framestyle=:none + ) + + a = 0.0 + b = 0.0 + Δtxt = 0.3 + + txt = @sprintf("Temps depuis départ [h] = %3.2f", time_current) + plot!(px, subplot = subplot_current, + annotation=((a, b+3*Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + + txt = @sprintf("Vitesse satellite [km/h] = %5.2f", sqrt(v1[i_current]^2+v2[i_current]^2)) + plot!(px, subplot = subplot_current, + annotation=((a, b+2*Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + + txt = @sprintf("Distance à la Terre [km] = %5.2f", sqrt(x1[i_current]^2+x2[i_current]^2)-rayon_Terre) + plot!(px, subplot = subplot_current, + annotation=((a, b+Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + + txt = @sprintf("Poussée maximale [N] = %5.2f", F_max) + plot!(px, subplot = subplot_current, + annotation=((a, b-0*Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + + txt = @sprintf("Durée du transfert [h] = %5.2f", tf_transfert) + plot!(px, subplot = subplot_current, + annotation=((a, b-1*Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + + if pb==consomin + txt = @sprintf("Durée et consommation comparées") + plot!(px, subplot = subplot_current, + annotation=((a, b-2*Δtxt, txt)), + annotationcolor=:green, annotationfontsize=16, + annotationhalign=:left) + + txt = @sprintf("au problème en temps minimal :") + plot!(px, subplot = subplot_current, + annotation=((a, b-2.75*Δtxt, txt)), + annotationcolor=:green, annotationfontsize=16, + annotationhalign=:left) + + txt = @sprintf("Durée du transfert [relatif] = %5.2f", tf_transfert/tf_min) + plot!(px, subplot = subplot_current, + annotation=((a, b-3.75*Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + + txt = @sprintf("Consommation [relative] = %5.2f", conso[i_current]./tf_min) + plot!(px, subplot = subplot_current, + annotation=((a, b-4.75*Δtxt, txt)), + annotationcolor=:white, annotationfontsize=14, + annotationhalign=:left) + end + + subplot_current = subplot_current+1 + plot!(px, + inset = (1, bbox(0.3, 0.03, 0.5, 0.1, :top, :left)), + subplot = subplot_current, + bg_inside = nothing, legend=:false, aspect_ratio=:equal, + xlims=(-4, 4), + framestyle=:none + ) + + rmax = 3*maximum(sqrt.(X1_orb_init.^2+X2_orb_init.^2)) + plot!(px, subplot = subplot_current, + 0.04.+X1_orb_init./rmax, X2_orb_init./rmax.-0.03, + color = :olivedrab1, linewidth=2) + + rmax = 7*maximum(sqrt.(X1_orb_arr.^2+X2_orb_arr.^2)) + plot!(px, subplot = subplot_current, + 3.3.+X1_orb_arr./rmax, X2_orb_arr./rmax.-0.03, + color = :turquoise1, linewidth=2) + + satellite!(px, subplot=subplot_current, + position=[0.67; 0.28], scale=0.08, + rotate=π/2) + + # Terre + θ = range(0.0, 2π, length=100) + rT = 0.1 + xT = 3.55 .+ rT .* cos.(θ) + yT = 0.28 .+ rT .* sin.(θ) + plot!(px, subplot=subplot_current, xT, yT, color = :dodgerblue1, seriestype=:shape, linewidth=0) + + s1 = "Transfert orbital du satellite autour de la Terre\n" + s2 = "de l'orbite elliptique vers l'orbite circulaire\n" + if pb==tfmin + s3 = "en temps minimal" + elseif pb==consomin + s3 = "en consommation minimale" + else + error("pb pb") + end + txt = s1 * s2 * s3 + plot!(px, subplot = subplot_current, + annotation=((0, 0, txt)), + annotationcolor=:white, annotationfontsize=18, + annotationhalign=:center) + + # Realisation + subplot_current = subplot_current+1 + xx = 0.0 + yy = 0.02 + plot!(px, + inset = (1, bbox(xx, yy, 0.12, 0.05, :bottom, :right)), + subplot = subplot_current, + bg_inside = nothing, legend=:false, + framestyle=:none + ) + + s1 = "Réalisation : Olivier Cots (2022)" + txt = s1 + plot!(px, subplot = subplot_current, + annotation=((0, 0, txt)), + annotationcolor=:gray, annotationfontsize=8, annotationhalign=:left) + + return subplot_current+1 + + end; + +end # module \ No newline at end of file