Path: blob/main/latex-templates/templates/aerospace/orbital_mechanics.tex
51 views
unlisted
\documentclass[a4paper, 11pt]{article}1\usepackage[utf8]{inputenc}2\usepackage[T1]{fontenc}3\usepackage{amsmath, amssymb}4\usepackage{graphicx}5\usepackage{siunitx}6\usepackage{booktabs}7\usepackage{algorithm2e}8\usepackage{subcaption}9\usepackage[makestderr]{pythontex}1011% Theorem environments for textbook style12\newtheorem{definition}{Definition}13\newtheorem{theorem}{Theorem}14\newtheorem{example}{Example}15\newtheorem{remark}{Remark}1617\title{Orbital Mechanics: Hohmann Transfers, Orbital Elements, and Ground Tracks\\18\large A Comprehensive Analysis of Spacecraft Trajectory Design}19\author{Astrodynamics Division\\Computational Science Templates}20\date{\today}2122\begin{document}23\maketitle2425\begin{abstract}26This textbook-style analysis presents the fundamentals of orbital mechanics for spacecraft mission design. We examine Hohmann transfer orbits between circular orbits, compute orbital elements from state vectors, and generate ground tracks for various orbit types. The analysis covers delta-v budgets for LEO-to-GEO transfers, interplanetary trajectory concepts, and the effects of orbital inclination on ground coverage patterns.27\end{abstract}2829\section{Introduction}30Orbital mechanics provides the mathematical foundation for spacecraft trajectory design. Understanding orbital transfers, perturbations, and ground coverage is essential for mission planning, satellite constellation design, and interplanetary exploration.3132\begin{definition}[Keplerian Elements]33The six classical orbital elements are:34\begin{enumerate}35\item $a$ - Semi-major axis (orbit size)36\item $e$ - Eccentricity (orbit shape)37\item $i$ - Inclination (orbital plane tilt)38\item $\Omega$ - Right ascension of ascending node (RAAN)39\item $\omega$ - Argument of periapsis40\item $\nu$ - True anomaly (position in orbit)41\end{enumerate}42\end{definition}4344\section{Mathematical Framework}4546\subsection{Vis-Viva Equation}47The fundamental relation between orbital velocity and position:48\begin{equation}49v = \sqrt{\mu \left(\frac{2}{r} - \frac{1}{a}\right)}50\end{equation}51where $\mu = GM$ is the standard gravitational parameter.5253\subsection{Hohmann Transfer}54\begin{theorem}[Hohmann Transfer Delta-V]55The minimum delta-v for transfer between two coplanar circular orbits:56\begin{align}57\Delta v_1 &= \sqrt{\frac{\mu}{r_1}}\left(\sqrt{\frac{2r_2}{r_1 + r_2}} - 1\right) \\58\Delta v_2 &= \sqrt{\frac{\mu}{r_2}}\left(1 - \sqrt{\frac{2r_1}{r_1 + r_2}}\right)59\end{align}60\end{theorem}6162\subsection{Transfer Time}63The time of flight for a Hohmann transfer is half the period of the transfer ellipse:64\begin{equation}65t_{transfer} = \pi\sqrt{\frac{a_t^3}{\mu}} = \pi\sqrt{\frac{(r_1 + r_2)^3}{8\mu}}66\end{equation}6768\subsection{Orbital Elements from State Vectors}69Given position $\mathbf{r}$ and velocity $\mathbf{v}$:70\begin{align}71\mathbf{h} &= \mathbf{r} \times \mathbf{v} \quad \text{(angular momentum)} \\72\mathbf{e} &= \frac{\mathbf{v} \times \mathbf{h}}{\mu} - \frac{\mathbf{r}}{r} \quad \text{(eccentricity vector)}73\end{align}7475\section{Computational Analysis}7677\begin{pycode}78import numpy as np79import matplotlib.pyplot as plt80from mpl_toolkits.mplot3d import Axes3D81plt.rc('text', usetex=True)82plt.rc('font', family='serif')8384np.random.seed(42)8586# Physical constants87mu_earth = 3.986004418e14 # Earth's gravitational parameter (m^3/s^2)88R_earth = 6.371e6 # Earth's radius (m)89omega_earth = 7.2921159e-5 # Earth's rotation rate (rad/s)9091# Orbital radii92orbits = {93'LEO': R_earth + 400e3,94'MEO': R_earth + 20200e3,95'GEO': 42164e3,96'Lunar': 384400e397}9899# Function to compute circular orbital velocity100def circular_velocity(r):101return np.sqrt(mu_earth / r)102103# Function to compute Hohmann transfer parameters104def hohmann_transfer(r1, r2):105a_transfer = (r1 + r2) / 2106107# Velocities108v1_circular = circular_velocity(r1)109v2_circular = circular_velocity(r2)110v1_transfer = np.sqrt(mu_earth * (2/r1 - 1/a_transfer))111v2_transfer = np.sqrt(mu_earth * (2/r2 - 1/a_transfer))112113# Delta-v114dv1 = abs(v1_transfer - v1_circular)115dv2 = abs(v2_circular - v2_transfer)116dv_total = dv1 + dv2117118# Transfer time119t_transfer = np.pi * np.sqrt(a_transfer**3 / mu_earth)120121return {122'dv1': dv1, 'dv2': dv2, 'dv_total': dv_total,123't_transfer': t_transfer, 'a_transfer': a_transfer124}125126# Compute transfers from LEO to various orbits127transfers = {}128r_leo = orbits['LEO']129for name, r_target in orbits.items():130if name != 'LEO':131transfers[name] = hohmann_transfer(r_leo, r_target)132133# Ground track computation134def compute_ground_track(a, e, i, omega, Omega, n_orbits=3, n_points=1000):135"""Compute ground track for an orbit."""136T = 2 * np.pi * np.sqrt(a**3 / mu_earth) # Orbital period137t = np.linspace(0, n_orbits * T, n_points)138139# Mean motion and mean anomaly140n = np.sqrt(mu_earth / a**3)141M = n * t142143# Solve Kepler's equation (simplified for small e)144E = M + e * np.sin(M) # First-order approximation145nu = 2 * np.arctan2(np.sqrt(1+e) * np.sin(E/2), np.sqrt(1-e) * np.cos(E/2))146147# Orbital radius148r = a * (1 - e * np.cos(E))149150# Position in orbital plane151x_orb = r * np.cos(nu)152y_orb = r * np.sin(nu)153154# Rotation matrices155cos_O, sin_O = np.cos(Omega), np.sin(Omega)156cos_i, sin_i = np.cos(i), np.sin(i)157cos_w, sin_w = np.cos(omega), np.sin(omega)158159# Transform to ECI coordinates160x_eci = (cos_O*cos_w - sin_O*sin_w*cos_i)*x_orb + (-cos_O*sin_w - sin_O*cos_w*cos_i)*y_orb161y_eci = (sin_O*cos_w + cos_O*sin_w*cos_i)*x_orb + (-sin_O*sin_w + cos_O*cos_w*cos_i)*y_orb162z_eci = (sin_w*sin_i)*x_orb + (cos_w*sin_i)*y_orb163164# Convert to latitude/longitude (accounting for Earth rotation)165lat = np.arcsin(z_eci / r)166lon = np.arctan2(y_eci, x_eci) - omega_earth * t167168# Wrap longitude to [-180, 180]169lon = np.rad2deg(lon)170lon = ((lon + 180) % 360) - 180171lat = np.rad2deg(lat)172173return lon, lat, T174175# Define orbit types for ground track comparison176orbit_configs = {177'ISS (LEO)': {'a': R_earth + 420e3, 'e': 0.0001, 'i': np.deg2rad(51.6)},178'Sun-Sync': {'a': R_earth + 700e3, 'e': 0.001, 'i': np.deg2rad(98.2)},179'GEO': {'a': 42164e3, 'e': 0.0001, 'i': np.deg2rad(0.1)},180'Molniya': {'a': 26600e3, 'e': 0.74, 'i': np.deg2rad(63.4)}181}182183ground_tracks = {}184for name, config in orbit_configs.items():185lon, lat, T = compute_ground_track(186config['a'], config['e'], config['i'],187omega=0, Omega=0, n_orbits=2 if name != 'GEO' else 1188)189ground_tracks[name] = {'lon': lon, 'lat': lat, 'T': T}190191# Bi-elliptic transfer comparison192def bielliptic_transfer(r1, r2, r_intermediate):193"""Compute bi-elliptic transfer delta-v."""194a1 = (r1 + r_intermediate) / 2195a2 = (r_intermediate + r2) / 2196197v1 = circular_velocity(r1)198v1_t = np.sqrt(mu_earth * (2/r1 - 1/a1))199dv1 = abs(v1_t - v1)200201v_int_1 = np.sqrt(mu_earth * (2/r_intermediate - 1/a1))202v_int_2 = np.sqrt(mu_earth * (2/r_intermediate - 1/a2))203dv2 = abs(v_int_2 - v_int_1)204205v2_t = np.sqrt(mu_earth * (2/r2 - 1/a2))206v2 = circular_velocity(r2)207dv3 = abs(v2 - v2_t)208209return dv1 + dv2 + dv3210211# Compare Hohmann vs bi-elliptic for different intermediate radii212r_ratios = np.linspace(1.1, 20, 50)213hohmann_dv = transfers['GEO']['dv_total']214bielliptic_dvs = []215for ratio in r_ratios:216r_int = orbits['GEO'] * ratio217dv = bielliptic_transfer(orbits['LEO'], orbits['GEO'], r_int)218bielliptic_dvs.append(dv)219220# Create comprehensive visualization221fig = plt.figure(figsize=(14, 12))222223# Plot 1: Hohmann transfer orbits224ax1 = fig.add_subplot(2, 3, 1)225theta = np.linspace(0, 2*np.pi, 200)226227# Earth228earth = plt.Circle((0, 0), R_earth/1e6, color='blue', alpha=0.6)229ax1.add_patch(earth)230231# LEO232ax1.plot(orbits['LEO']/1e6 * np.cos(theta), orbits['LEO']/1e6 * np.sin(theta),233'g-', linewidth=2, label='LEO')234235# GEO236ax1.plot(orbits['GEO']/1e6 * np.cos(theta), orbits['GEO']/1e6 * np.sin(theta),237'r-', linewidth=2, label='GEO')238239# Transfer ellipse240a_t = transfers['GEO']['a_transfer']241e_t = (orbits['GEO'] - orbits['LEO']) / (orbits['GEO'] + orbits['LEO'])242r_t = a_t * (1 - e_t**2) / (1 + e_t * np.cos(theta))243theta_half = theta[theta <= np.pi]244r_t_half = a_t * (1 - e_t**2) / (1 + e_t * np.cos(theta_half))245ax1.plot(r_t_half/1e6 * np.cos(theta_half), r_t_half/1e6 * np.sin(theta_half),246'orange', linestyle='--', linewidth=2, label='Transfer')247248ax1.set_xlim(-50, 50)249ax1.set_ylim(-50, 50)250ax1.set_xlabel('x (Mm)')251ax1.set_ylabel('y (Mm)')252ax1.set_title('Hohmann Transfer: LEO to GEO')253ax1.legend(fontsize=8)254ax1.set_aspect('equal')255ax1.grid(True, alpha=0.3)256257# Plot 2: Delta-v comparison258ax2 = fig.add_subplot(2, 3, 2)259targets = list(transfers.keys())260dv1s = [transfers[t]['dv1'] for t in targets]261dv2s = [transfers[t]['dv2'] for t in targets]262263x = np.arange(len(targets))264width = 0.35265ax2.bar(x - width/2, np.array(dv1s)/1000, width, label=r'$\Delta v_1$', color='steelblue')266ax2.bar(x + width/2, np.array(dv2s)/1000, width, label=r'$\Delta v_2$', color='coral')267ax2.set_xlabel('Target Orbit')268ax2.set_ylabel(r'$\Delta v$ (km/s)')269ax2.set_title('Delta-v Requirements from LEO')270ax2.set_xticks(x)271ax2.set_xticklabels(targets)272ax2.legend(fontsize=8)273ax2.grid(True, alpha=0.3, axis='y')274275# Plot 3: Transfer times276ax3 = fig.add_subplot(2, 3, 3)277times = [transfers[t]['t_transfer']/3600 for t in targets]278colors = plt.cm.viridis(np.linspace(0.2, 0.8, len(targets)))279bars = ax3.bar(targets, times, color=colors)280ax3.set_xlabel('Target Orbit')281ax3.set_ylabel('Transfer Time (hours)')282ax3.set_title('Hohmann Transfer Duration')283for bar, t in zip(bars, times):284if t > 100:285ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height(),286f'{t/24:.1f}d', ha='center', va='bottom', fontsize=8)287else:288ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height(),289f'{t:.1f}h', ha='center', va='bottom', fontsize=8)290ax3.grid(True, alpha=0.3, axis='y')291292# Plot 4: Ground tracks293ax4 = fig.add_subplot(2, 3, 4)294track_colors = plt.cm.Set1(np.linspace(0, 0.8, len(ground_tracks)))295for (name, track), color in zip(ground_tracks.items(), track_colors):296# Split track at discontinuities297lon = track['lon']298lat = track['lat']299# Simple discontinuity detection300split_idx = np.where(np.abs(np.diff(lon)) > 180)[0] + 1301segments = np.split(np.arange(len(lon)), split_idx)302for j, seg in enumerate(segments):303if len(seg) > 1:304label = name if j == 0 else None305ax4.plot(lon[seg], lat[seg], color=color, linewidth=1.5,306alpha=0.7, label=label)307ax4.set_xlim(-180, 180)308ax4.set_ylim(-90, 90)309ax4.set_xlabel('Longitude (deg)')310ax4.set_ylabel('Latitude (deg)')311ax4.set_title('Ground Tracks (2 orbits)')312ax4.legend(fontsize=7, loc='lower left')313ax4.grid(True, alpha=0.3)314315# Plot 5: Hohmann vs Bi-elliptic316ax5 = fig.add_subplot(2, 3, 5)317ax5.plot(r_ratios, np.array(bielliptic_dvs)/1000, 'b-', linewidth=2, label='Bi-elliptic')318ax5.axhline(y=hohmann_dv/1000, color='r', linestyle='--', linewidth=2, label='Hohmann')319ax5.set_xlabel(r'$r_{intermediate}/r_{GEO}$')320ax5.set_ylabel(r'Total $\Delta v$ (km/s)')321ax5.set_title('Hohmann vs Bi-elliptic Transfer')322ax5.legend(fontsize=8)323ax5.grid(True, alpha=0.3)324325# Plot 6: Orbital period vs altitude326ax6 = fig.add_subplot(2, 3, 6)327altitudes = np.linspace(200, 40000, 100) * 1e3328radii = R_earth + altitudes329periods = 2 * np.pi * np.sqrt(radii**3 / mu_earth) / 3600 # hours330ax6.plot(altitudes/1e3, periods, 'b-', linewidth=2)331ax6.axhline(y=24, color='r', linestyle='--', alpha=0.7, label='24 hr (GEO)')332ax6.axhline(y=12, color='g', linestyle='--', alpha=0.7, label='12 hr (Molniya)')333ax6.set_xlabel('Altitude (km)')334ax6.set_ylabel('Orbital Period (hours)')335ax6.set_title('Period vs Altitude')336ax6.legend(fontsize=8)337ax6.grid(True, alpha=0.3)338ax6.set_xlim(0, 40000)339340plt.tight_layout()341plt.savefig('orbital_mechanics_plot.pdf', bbox_inches='tight', dpi=150)342print(r'\begin{center}')343print(r'\includegraphics[width=\textwidth]{orbital_mechanics_plot.pdf}')344print(r'\end{center}')345plt.close()346347# Key results348geo_transfer = transfers['GEO']349\end{pycode}350351\section{Algorithm: State Vector to Orbital Elements}352353\begin{algorithm}[H]354\SetAlgoLined355\KwIn{Position $\mathbf{r}$, velocity $\mathbf{v}$}356\KwOut{Classical orbital elements $(a, e, i, \Omega, \omega, \nu)$}357$\mathbf{h} \leftarrow \mathbf{r} \times \mathbf{v}$ \tcp{Angular momentum}358$\mathbf{n} \leftarrow \hat{k} \times \mathbf{h}$ \tcp{Node vector}359$\mathbf{e} \leftarrow \frac{1}{\mu}[(\|\mathbf{v}\|^2 - \frac{\mu}{r})\mathbf{r} - (\mathbf{r} \cdot \mathbf{v})\mathbf{v}]$\;360$a \leftarrow -\frac{\mu}{2\mathcal{E}}$ where $\mathcal{E} = \frac{v^2}{2} - \frac{\mu}{r}$\;361$e \leftarrow \|\mathbf{e}\|$\;362$i \leftarrow \arccos(h_z / \|\mathbf{h}\|)$\;363$\Omega \leftarrow \arccos(n_x / \|\mathbf{n}\|)$\;364$\omega \leftarrow \arccos(\mathbf{n} \cdot \mathbf{e} / (n \cdot e))$\;365$\nu \leftarrow \arccos(\mathbf{e} \cdot \mathbf{r} / (e \cdot r))$\;366\Return{$a, e, i, \Omega, \omega, \nu$}367\caption{Orbital Elements from State Vector}368\end{algorithm}369370\section{Results and Discussion}371372\subsection{Transfer Performance}373374\begin{pycode}375# Generate results table376print(r'\begin{table}[h]')377print(r'\centering')378print(r'\caption{Hohmann Transfer Parameters from LEO}')379print(r'\begin{tabular}{lcccc}')380print(r'\toprule')381print(r'Target & $\Delta v_1$ & $\Delta v_2$ & Total $\Delta v$ & Transfer Time \\')382print(r' & (km/s) & (km/s) & (km/s) & \\')383print(r'\midrule')384for target in ['MEO', 'GEO', 'Lunar']:385t = transfers[target]386time_str = f"{t['t_transfer']/3600:.1f} hr" if t['t_transfer'] < 86400 else f"{t['t_transfer']/86400:.1f} days"387print(f"{target} & {t['dv1']/1000:.3f} & {t['dv2']/1000:.3f} & {t['dv_total']/1000:.3f} & {time_str} \\\\")388print(r'\bottomrule')389print(r'\end{tabular}')390print(r'\end{table}')391\end{pycode}392393\begin{example}[LEO to GEO Transfer]394For a spacecraft in 400 km LEO transferring to GEO:395\begin{itemize}396\item Initial velocity: \py{f"{circular_velocity(orbits['LEO'])/1000:.3f}"} km/s397\item First burn (perigee): $\Delta v_1 = $ \py{f"{geo_transfer['dv1']/1000:.3f}"} km/s398\item Second burn (apogee): $\Delta v_2 = $ \py{f"{geo_transfer['dv2']/1000:.3f}"} km/s399\item Total $\Delta v$: \py{f"{geo_transfer['dv_total']/1000:.3f}"} km/s400\item Transfer time: \py{f"{geo_transfer['t_transfer']/3600:.2f}"} hours401\end{itemize}402\end{example}403404\subsection{Ground Track Analysis}405406\begin{pycode}407# Ground track table408print(r'\begin{table}[h]')409print(r'\centering')410print(r'\caption{Orbital Characteristics and Ground Coverage}')411print(r'\begin{tabular}{lccc}')412print(r'\toprule')413print(r'Orbit Type & Altitude & Period & Inclination \\')414print(r' & (km) & (min) & (deg) \\')415print(r'\midrule')416for name, config in orbit_configs.items():417alt = (config['a'] - R_earth) / 1000418period = ground_tracks[name]['T'] / 60419inc = np.rad2deg(config['i'])420print(f"{name} & {alt:.0f} & {period:.1f} & {inc:.1f} \\\\")421print(r'\bottomrule')422print(r'\end{tabular}')423print(r'\end{table}')424\end{pycode}425426\begin{remark}[Ground Track Patterns]427\begin{itemize}428\item \textbf{LEO}: Ground track shifts westward each orbit due to Earth rotation429\item \textbf{Sun-synchronous}: Maintains constant local solar time at equator crossing430\item \textbf{GEO}: Appears stationary over a fixed longitude431\item \textbf{Molniya}: Figure-eight pattern with extended dwell time at high latitudes432\end{itemize}433\end{remark}434435\subsection{Bi-elliptic Transfers}436For transfers with radius ratio $r_2/r_1 > 11.94$, the bi-elliptic transfer becomes more efficient than Hohmann. However, it requires significantly longer transfer time.437438\section{Applications}439440\subsection{Mission Design Considerations}441\begin{itemize}442\item \textbf{Communications}: GEO provides continuous coverage of specific regions443\item \textbf{Navigation}: MEO constellations (GPS, Galileo) balance coverage and signal strength444\item \textbf{Earth Observation}: Sun-synchronous LEO maintains consistent lighting445\item \textbf{High-latitude coverage}: Molniya orbits serve polar regions446\end{itemize}447448\section{Limitations and Extensions}449450\subsection{Model Limitations}451\begin{enumerate}452\item \textbf{Two-body}: Neglects perturbations (J2, drag, third-body)453\item \textbf{Impulsive burns}: Assumes instantaneous velocity changes454\item \textbf{Coplanar}: No plane change maneuvers included455\item \textbf{Simplified Kepler}: First-order solution for eccentric anomaly456\end{enumerate}457458\subsection{Possible Extensions}459\begin{itemize}460\item Include J2 perturbation for realistic orbit propagation461\item Combined plane change and transfer maneuvers462\item Low-thrust trajectory optimization463\item Interplanetary patched conics464\end{itemize}465466\section{Conclusion}467This analysis demonstrates fundamental orbital mechanics principles:468\begin{itemize}469\item Hohmann transfers provide minimum-fuel solutions for coplanar circular orbits470\item LEO-to-GEO requires \py{f"{geo_transfer['dv_total']/1000:.2f}"} km/s total delta-v471\item Ground track patterns depend strongly on orbital period and inclination472\item Bi-elliptic transfers can outperform Hohmann for large radius ratios473\end{itemize}474475\section*{Further Reading}476\begin{itemize}477\item Vallado, D. A. (2013). \textit{Fundamentals of Astrodynamics and Applications}. Microcosm Press.478\item Bate, R. R., Mueller, D. D., \& White, J. E. (1971). \textit{Fundamentals of Astrodynamics}. Dover.479\item Curtis, H. D. (2020). \textit{Orbital Mechanics for Engineering Students}. Butterworth-Heinemann.480\end{itemize}481482\end{document}483484485