"""Q4: a radial burn leaks energy at first order when it is mis-pointed; a tangential one does not.

Left: the velocity triangle of the single radial impulse that rotates the apse line
by 30 deg (a = 12000 km, e = 0.4), with a pointing error drawn 15x too large.
Right: the semi-major-axis error against the pointing error for that radial burn and for a
tangential burn of the same size at periapsis.

Run from the sfm-slides directory:   python3 codes/fig-mq-apse-pointing.py
"""
import numpy as np
import matplotlib.pyplot as plt
from _manstyle import CREAM, INK, MAROON, BLUE, GREEN, AMBER, PURPLE, despine
from _mqcommon import mu, conic_xy

a, e = 12000.0, 0.4
p = a*(1 - e*e)
eta = np.radians(30.0)
sq = np.sqrt(mu/p)
nu = eta/2
rc = p/(1 + e*np.cos(nu))
vr, vth = sq*e*np.sin(nu), sq*(1 + e*np.cos(nu))
dv = 2*vr                                     # 1.302 km/s

fig, (axL, axM, axR) = plt.subplots(1, 3, figsize=(17.0, 5.6), gridspec_kw=dict(width_ratios=[1.0, 0.95, 1.05]))

# ---- A : orbits and the velocity triangle -------------------------------------
x, y = conic_xy(a, e)
axL.plot(x, y, color=BLUE, lw=2.0, label="initial orbit")
x, y = conic_xy(a, e, w=eta)
axL.plot(x, y, color=GREEN, lw=2.0, label=r"final orbit ($\eta=30^\circ$)")
P = np.array([rc*np.cos(nu), rc*np.sin(nu)])
axL.plot(*P, "o", color=INK, ms=10, zorder=6)
axL.annotate("crossing\n(burn point)", P, P + np.array([4500, 3500]), fontsize=10.5, color=INK,
             arrowprops=dict(arrowstyle="-", color=INK, lw=0.8))
axL.plot(0, 0, "o", color=INK, ms=8)
axL.set_aspect("equal"); axL.axis("off")
axL.set_xlim(-24000, 19000); axL.set_ylim(-14500, 14500)
axL.legend(frameon=False, fontsize=10.5, loc="upper left")
axL.set_title(r"Rotating the apse line by $30^\circ$ ($a=12\,000$ km, $e=0.4$)", fontsize=11.5)

# ---- B : the velocity triangle at the crossing, in the (v_theta, v_r) plane ---------
ep = np.radians(20)                           # exaggerated pointing error
v0 = np.array([vth, vr]); v1 = np.array([vth, -vr])
v2 = v0 + dv*np.array([np.sin(ep), -np.cos(ep)])
O = np.array([0.0, 0.0])
def arr(p_, q_, col, lw=2.2, ls="-"):
    axM.annotate("", xy=q_, xytext=p_, arrowprops=dict(arrowstyle="-|>", color=col, lw=lw, ls=ls, shrinkA=0, shrinkB=0))
arr(O, v0, BLUE); arr(O, v1, GREEN)
arr(v0, v1, AMBER, 3.0); arr(v0, v2, MAROON, 2.0, "--")
axM.text(vth*0.5, vr*0.5 + 0.35, r"before burn" "\n" r"$(v_\theta,\ v_r)$", color=BLUE, fontsize=10.5, ha="center")
axM.text(vth*0.5, -vr*0.5 - 0.55, "nominal, after burn\n" r"$(v_\theta,\ -v_r)$", color=GREEN, fontsize=10.5, ha="center", va="top")
axM.text(vth + 0.35, 0.0, r"commanded $\Delta v=2v_r$" "\n(radial)", color=AMBER, fontsize=10.5, va="center")
axM.text(v2[0] + 0.3, v2[1] - 0.15, "executed (error $\\varepsilon$ drawn 20$^\\circ$):\n" r"leaks $\Delta v\sin\varepsilon$ along $v_\theta$", color=MAROON, fontsize=10, va="top")
axM.set_xlim(-0.4, 14.5); axM.set_ylim(-2.8, 1.6)
axM.set_xlabel(r"$v_\theta$  [km/s]"); axM.set_ylabel(r"$v_r$  [km/s]")
axM.set_title(r"Velocity triangle at the crossing ($\nu=15^\circ$)", fontsize=11.5)
despine(axM)

# ---- C : the error against the pointing error --------------------------------------
T = 2*np.pi*np.sqrt(a**3/mu)
eps = np.radians(np.logspace(-2, 0.7, 200))
dE_rad = vth*dv*np.sin(eps)
da_rad = 2*a**2*dE_rad/mu
rp = a*(1 - e)
vp = np.sqrt(mu*(2/rp - 1/a))
dE_tan = vp*dv*(1 - np.cos(eps))
da_tan = 2*a**2*dE_tan/mu
axR.loglog(np.degrees(eps), da_rad, color=MAROON, lw=2.6, label=r"radial burn of 1.302 km/s (apse rotation): $\propto\varepsilon$")
axR.loglog(np.degrees(eps), da_tan, color=BLUE, lw=2.6, label=r"tangential burn of the same size, at periapsis: $\propto\varepsilon^{2}$")
da10 = (2/3)*a*10/T
axR.axhline(da10, color=INK, lw=1.0, ls=(0, (5, 4)))
axR.text(0.0105, da10*1.15, r"$\delta T=10$ s per revolution", fontsize=10, color=INK)
for d_, col in ((0.3, AMBER),):
    axR.plot(d_, 2*a**2*vth*dv*np.radians(d_)/mu, "o", color=MAROON, ms=8)
    axR.plot(d_, 2*a**2*vp*dv*(1 - np.cos(np.radians(d_)))/mu, "o", color=BLUE, ms=8)
axR.annotate(r"$0.3^\circ$: $\delta a=43$ km" "\n" r"($\delta T=70$ s)", (0.3, 43), (0.045, 90), fontsize=10, color=MAROON,
             arrowprops=dict(arrowstyle="-", color=MAROON, lw=0.8))
axR.set_xlabel(r"in-plane pointing error $\varepsilon$  [deg]")
axR.set_ylabel(r"error in the semi-major axis, $|\delta a|$  [km]")
axR.set_xlim(0.01, 5); axR.set_ylim(1e-3, 1e3)
axR.legend(frameon=False, fontsize=10, loc="lower right")
axR.set_title("Which burns forgive a pointing error?", fontsize=11.5)
despine(axR)

plt.tight_layout()
fig.savefig("img/fig-mq-apse-pointing.png", dpi=160, bbox_inches="tight")
plt.close(fig)
print("wrote img/fig-mq-apse-pointing.png")
