Source code for ssapy_toolkit.Orbital_Mechanics.transfer_rendezvous

import numpy as np
from scipy.optimize import differential_evolution
from ssapy import rv, Orbit, SciPyPropagator, AccelKepler
from ..Time_Functions import get_times, Time
from ..Plots import rendezvous_plot


[docs] def transfer_rendezvous(orbit1, orbit2, tol=1000, max_iter=1000, plot=False, status=False): """ Finds the delta-v that leads to a transfer rendezvous with a moving target orbit. Uses a global gradient-free search to minimize rendezvous position error. Parameters ---------- orbit1 : ssapy.Orbit Initial orbit of the chaser at departure epoch. orbit2 : ssapy.Orbit Target orbit (any epoch). Will be propagated to rendezvous epoch. tol : float, optional Position tolerance (m). Default is 1000 m. max_iter : int, optional Maximum optimization iterations. Default is 1000. plot : bool, optional If True, plots the final transfer. Default is False. status : bool, optional If True, prints optimization info. Default is False. Returns ------- dict Output including delta-v vectors, orbits, trajectory arrays, and optional plot. """ r1 = orbit1.r v1 = orbit1.v t0 = Time(orbit1.t, format='gps') def propagate(delta_v_in): v_transfer0 = v1 + delta_v_in orb_tr = Orbit(r=r1, v=v_transfer0, t=t0) try: period = orb_tr.period if np.isinf(period) or period > 1e7: period = 48 * 3600 except OverflowError: period = 48 * 3600 times = get_times(duration=(float(period), 'sec'), freq=(1, 'sec'), t0=t0) try: r_traj, v_traj = rv(orb_tr, time=times) except RuntimeError: r_traj, v_traj = rv(orb_tr, time=times, propagator=SciPyPropagator(AccelKepler())) try: r2_traj, v2_traj = rv(orbit2, time=times) except RuntimeError: r2_traj, v2_traj = rv(orbit2, time=times, propagator=SciPyPropagator(AccelKepler())) distances = np.linalg.norm(r_traj - r2_traj, axis=1) idx_min = np.argmin(distances) return (r_traj[idx_min], v_traj[idx_min], r2_traj[idx_min], v2_traj[idx_min], times[idx_min], r_traj[:idx_min+1], v_traj[:idx_min+1]) def error_func(delta_v_try): r_arr, _, r2_arr, _, _, *_ = propagate(delta_v_try) return np.linalg.norm(r_arr - r2_arr) bounds = [(-2000, 2000)] * 3 result_opt = differential_evolution(error_func, bounds, tol=1e-3, maxiter=max_iter, disp=status) delta_v = result_opt.x # Final propagation using optimal delta_v r_arr, v_arr, r2_arr, v2_arr, t_arr, r_seq, v_seq = propagate(delta_v) error = np.linalg.norm(r_arr - r2_arr) # Build final transfer orbit v_transfer0 = v1 + delta_v orb_tr = Orbit(r=r1, v=v_transfer0, t=t0) try: period = orb_tr.period if np.isinf(period) or period > 1e7: period = 2 * 3600 except OverflowError: period = 2 * 3600 times_full = get_times(duration=(float(period), 'sec'), freq=(1, 'sec'), t0=t0) try: r_full, v_full = rv(orb_tr, time=times_full) except RuntimeError: r_full, v_full = rv(orb_tr, time=times_full, propagator=SciPyPropagator(AccelKepler())) try: r2_full, v2_full = rv(orbit2, time=times_full) except RuntimeError: r2_full, v2_full = rv(orbit2, time=times_full, propagator=SciPyPropagator(AccelKepler())) dist_full = np.linalg.norm(r_full - r2_full, axis=1) idx_closest = np.argmin(dist_full) r_transfer = r_full[: idx_closest + 1] v_transfer = v_full[: idx_closest + 1] tof = times_full[idx_closest].gps - t0.gps delta_v2 = v2_arr - v_arr delta_v2_mag = np.linalg.norm(delta_v2) delta_v1_mag = np.linalg.norm(delta_v) orb_initial = Orbit(r=r1, v=v1, t=t0) orb_final = Orbit(r=r2_arr, v=v2_arr, t=t_arr) result = { 'initial': orb_initial, 'final': orb_final, 'transfer': orb_tr, '|delta_v1|': delta_v1_mag, '|delta_v2|': delta_v2_mag, 'delta_v1': delta_v, 'delta_v2': delta_v2, 'r_transfer': r_transfer, 'v_transfer': v_transfer, 'tof': tof, 'error': error } if status: print(f"Δv₁ = {delta_v1_mag:.6f} m/s, Δv₂ = {delta_v2_mag:.6f} m/s, TOF = {tof/60:.2f} min, Final error = {error:.3f} m") if plot: fig = rendezvous_plot( orbit1.r, orbit1.v, r_transfer, v_transfer, orbit2.r, orbit2.v, title=( f"Transfer Rendezvous\n" f"TOF: {tof/60:.1f} min | " f"|Δv₁| = {delta_v1_mag/1e3:.3f} km/s, " f"|Δv₂| = {delta_v2_mag/1e3:.3f} km/s" ), ) result['fig'] = fig return result