"""Figure for ballistic-trajectories-bates.qmd (img/fig-bal-errors.png): the influence coefficients.

(a) psi(gamma) at Q = 0.879 (r_bo = 6578 km, v_bo = 7.3 km/s) with the tangent at a low, the maximum-range and a high point:
    Delta psi = (d psi/d gamma) Delta gamma, so the error is zero at the top and of opposite sign on the two branches.
(b) The miss, in km on the ground, caused by one arcminute of flight-path angle, one kilometre of burnout radius, and one m/s of burnout speed.
(c) The cross-range error produced by a lateral displacement of the burnout point (Delta X cos psi) and by an azimuth error (Delta A sin psi).

Run from the sfm-slides directory:   python3 codes/fig-bal-errors.py
"""
import numpy as np
import _figfont                                # all figure text at one scale
import matplotlib.pyplot as plt
from _manstyle import INK, MAROON, BLUE, GREEN, AMBER, PURPLE, despine
from _ballistic import *

D = np.pi / 180
r, v = 6578.137, 7.3
Q = Q_of(r, v)
g = np.linspace(2, 88, 800) * D
ps = psi_of(Q, g)
fig, ax = plt.subplots(1, 3, figsize=(15.4, 5.0))
A = ax[0]
A.plot(np.degrees(g), np.degrees(ps), color=INK, lw=2.4)
gm = (np.pi - psi_max(Q)) / 4
for gv, c, nm in ((12 * D, GREEN, "low"), (gm, PURPLE, "max"), (55 * D, MAROON, "high")):
    p0 = psi_of(Q, gv); sl = dpsi_dgamma(p0, gv)
    xs = np.array([-9, 9]) + np.degrees(gv)
    A.plot(xs, np.degrees(p0) + sl * (xs - np.degrees(gv)), color=c, lw=2.0)
    A.scatter([np.degrees(gv)], [np.degrees(p0)], color=c, s=40, zorder=6)
    pos = {"low": (14, 60), "max": (19, 113), "high": (58, 70)}[nm]
    A.text(pos[0], pos[1], f"{nm}: slope {sl:+.2f}", color=c, fontsize=10, ha="center" if nm == "max" else "left")
A.set_xlabel(r"$\gamma$ [deg]"); A.set_ylabel(r"$\psi$ [deg]"); A.set_xlim(0, 90); A.set_ylim(0, 125)
A.set_title(r"(a)  $\partial\psi/\partial\gamma$ is the slope", fontsize=11.5, loc="left"); despine(A)
B = ax[1]
dg = dpsi_dgamma(ps, g) * (np.pi / 180 / 60) * RE              # km per arcmin
dr = dpsi_dr(r, v, ps, g) * 1.0 * RE                             # km per km of burnout radius
dv = dpsi_dv(r, v, ps, g) * 1e-3 * RE                            # km per m/s
B.plot(np.degrees(g), dg, color=BLUE, lw=2.2, label=r"per $1'$ of $\gamma$  [km]")
B.plot(np.degrees(g), dr, color=GREEN, lw=2.2, label=r"per 1 km of $r_{bo}$  [km/km]")
B.plot(np.degrees(g), dv, color=AMBER, lw=2.2, label=r"per 1 m/s of $v_{bo}$  [km per m/s]")
B.axhline(0, color=INK, lw=0.8); B.axvline(np.degrees(gm), color=PURPLE, lw=1.0, ls=":")
B.text(np.degrees(gm) + 1, 7.5, r"$\gamma_{max}$", color=PURPLE, fontsize=10.5)
B.set_ylim(-4.5, 9); B.set_xlim(0, 90); B.set_xlabel(r"$\gamma$ [deg]"); B.set_ylabel("miss on the ground [km]")
B.legend(loc="upper right", fontsize=9.5, frameon=False, bbox_to_anchor=(1.0, 0.92))
B.set_title(r"(b)  the three in-plane influence coefficients", fontsize=11.5, loc="left"); despine(B)
C = ax[2]
pp = np.linspace(0, 360, 721)
C.plot(pp, np.cos(pp * D), color=BLUE, lw=2.2, label=r"$\Delta C/\Delta X=\cos\psi$ (displaced burnout point)")
C.plot(pp, np.sin(pp * D), color=MAROON, lw=2.2, label=r"$\Delta C/\Delta A=\sin\psi$ (azimuth error)")
C.axhline(0, color=INK, lw=0.8)
for x in (90, 180, 270): C.axvline(x, color=INK, lw=0.5, ls=":")
C.set_xlim(0, 360); C.set_ylim(-1.35, 1.8); C.set_xticks(range(0, 361, 90)); C.set_xlabel(r"free-flight range angle $\psi$ [deg]")
C.legend(loc="upper right", fontsize=9.5, frameon=False)
C.set_title("(c)  cross-range error", fontsize=11.5, loc="left"); despine(C)
fig.tight_layout()
fig.savefig("img/fig-bal-errors.png", dpi=150, bbox_inches="tight")
print(f"Q={Q:.4f}, psi_max={np.degrees(psi_max(Q)):.2f}, gamma_max={np.degrees(gm):.2f}")
