Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/aerospace/satellite_coverage.tex
51 views
unlisted
1
\documentclass[a4paper, 11pt]{article}
2
\usepackage[utf8]{inputenc}
3
\usepackage[T1]{fontenc}
4
\usepackage{amsmath, amssymb}
5
\usepackage{graphicx}
6
\usepackage{siunitx}
7
\usepackage{booktabs}
8
\usepackage{algorithm2e}
9
\usepackage{subcaption}
10
\usepackage[makestderr]{pythontex}
11
12
% Theorem environments for technical report style
13
\newtheorem{definition}{Definition}
14
\newtheorem{theorem}{Theorem}
15
\newtheorem{remark}{Remark}
16
17
\title{Satellite Coverage Analysis: Ground Coverage, Revisit Times, and Constellation Design\\
18
\large A Comprehensive Study of Earth Observation and Communication Systems}
19
\author{Space Systems Division\\Computational Science Templates}
20
\date{\today}
21
22
\begin{document}
23
\maketitle
24
25
\begin{abstract}
26
This technical report presents a comprehensive analysis of satellite ground coverage for Earth observation and communication missions. We compute instantaneous coverage footprints, revisit times for single satellites and constellations, and analyze Walker constellation parameters for global coverage. The analysis includes elevation angle constraints, slant range calculations, and coverage optimization for various orbital configurations.
27
\end{abstract}
28
29
\section{Executive Summary}
30
Satellite coverage analysis is critical for mission design in Earth observation, communications, and navigation applications. This report analyzes coverage characteristics for various orbital configurations and provides design guidelines for constellation optimization.
31
32
\section{Mathematical Framework}
33
34
\begin{definition}[Coverage Half-Angle]
35
The Earth-central angle from the sub-satellite point to the coverage edge:
36
\begin{equation}
37
\rho = \arccos\left(\frac{R_E}{R_E + h}\cos\varepsilon_{min}\right) - \varepsilon_{min}
38
\end{equation}
39
where $\varepsilon_{min}$ is the minimum elevation angle and $h$ is orbital altitude.
40
\end{definition}
41
42
\subsection{Coverage Area}
43
The instantaneous coverage area on Earth's surface:
44
\begin{equation}
45
A_{cov} = 2\pi R_E^2 (1 - \cos\rho)
46
\end{equation}
47
48
\subsection{Slant Range}
49
The distance from satellite to ground target:
50
\begin{equation}
51
d = R_E \left[\sqrt{\left(\frac{R_E + h}{R_E}\right)^2 - \cos^2\varepsilon} - \sin\varepsilon\right]
52
\end{equation}
53
54
\subsection{Revisit Time}
55
\begin{theorem}[Single Satellite Revisit]
56
For a circular orbit, the maximum revisit time at latitude $\phi$:
57
\begin{equation}
58
T_{revisit} \approx \frac{2\pi R_E \cos\phi}{W \cdot n_{orbits/day}}
59
\end{equation}
60
where $W$ is the swath width and $n_{orbits/day}$ is the number of orbits per day.
61
\end{theorem}
62
63
\subsection{Walker Constellation}
64
A Walker Delta pattern is described by notation $i:T/P/F$:
65
\begin{itemize}
66
\item $i$ = Inclination
67
\item $T$ = Total number of satellites
68
\item $P$ = Number of equally-spaced orbital planes
69
\item $F$ = Relative phasing factor
70
\end{itemize}
71
72
\section{Computational Analysis}
73
74
\begin{pycode}
75
import numpy as np
76
import matplotlib.pyplot as plt
77
from matplotlib.patches import Circle
78
plt.rc('text', usetex=True)
79
plt.rc('font', family='serif')
80
81
np.random.seed(42)
82
83
# Constants
84
R_earth = 6371 # km
85
mu = 398600 # km^3/s^2
86
omega_E = 7.2921e-5 # rad/s (Earth rotation rate)
87
88
# Function to compute coverage parameters
89
def coverage_params(h, elev_min_deg):
90
elev_min = np.deg2rad(elev_min_deg)
91
92
# Earth central half-angle
93
sin_lambda = R_earth / (R_earth + h) * np.cos(elev_min)
94
lambda_0 = np.arcsin(sin_lambda)
95
rho = np.pi/2 - elev_min - lambda_0
96
97
# Coverage area
98
area = 2 * np.pi * R_earth**2 * (1 - np.cos(rho))
99
100
# Swath width
101
swath = 2 * R_earth * rho # Arc length approximation
102
103
# Slant range
104
slant = R_earth * np.sin(rho) / np.cos(elev_min + rho)
105
106
# Nadir angle
107
nadir = np.arcsin(R_earth / (R_earth + h) * np.sin(np.pi/2 + elev_min))
108
109
return np.rad2deg(rho), area, swath, slant, np.rad2deg(nadir)
110
111
# Orbital configurations
112
altitudes = [400, 600, 800, 1200, 2000] # km
113
elev_angles = [5, 10, 15, 20, 30] # degrees
114
115
# Coverage analysis for different altitudes
116
coverage_data = {}
117
for h in altitudes:
118
rho, area, swath, slant, nadir = coverage_params(h, 10)
119
T = 2 * np.pi * np.sqrt((R_earth + h)**3 / mu)
120
n_orbits = 86400 / T
121
coverage_data[h] = {
122
'rho': rho, 'area': area, 'swath': swath,
123
'slant': slant, 'nadir': nadir, 'period': T/60,
124
'orbits_per_day': n_orbits
125
}
126
127
# Ground track simulation
128
def ground_track(h, inc_deg, duration_hours=3, n_points=1000):
129
a = R_earth + h
130
T = 2 * np.pi * np.sqrt(a**3 / mu)
131
t = np.linspace(0, duration_hours * 3600, n_points)
132
133
n = np.sqrt(mu / a**3)
134
inc = np.deg2rad(inc_deg)
135
136
# Simplified ground track (circular orbit)
137
lat = np.rad2deg(np.arcsin(np.sin(inc) * np.sin(n * t)))
138
# Longitude accounting for Earth rotation
139
lon_inertial = np.rad2deg(np.arctan2(np.cos(inc) * np.sin(n * t),
140
np.cos(n * t)))
141
lon = lon_inertial - np.rad2deg(omega_E * t)
142
lon = ((lon + 180) % 360) - 180
143
144
return lat, lon, T
145
146
# Constellation configurations
147
constellations = {
148
'Starlink (Shell 1)': {'T': 1584, 'P': 72, 'h': 550, 'i': 53.0},
149
'OneWeb': {'T': 648, 'P': 18, 'h': 1200, 'i': 87.9},
150
'GPS': {'T': 24, 'P': 6, 'h': 20180, 'i': 55.0},
151
'Iridium': {'T': 66, 'P': 6, 'h': 780, 'i': 86.4},
152
'Galileo': {'T': 24, 'P': 3, 'h': 23222, 'i': 56.0}
153
}
154
155
# Revisit time calculation
156
def revisit_time(h, lat_deg, swath_km):
157
T = 2 * np.pi * np.sqrt((R_earth + h)**3 / mu)
158
n_orbits = 86400 / T
159
160
lat = np.deg2rad(abs(lat_deg))
161
162
# Ground track spacing per day
163
circumference_at_lat = 2 * np.pi * R_earth * np.cos(lat)
164
track_spacing = circumference_at_lat / n_orbits
165
166
if swath_km <= 0:
167
return float('inf')
168
169
# Days to cover
170
days_to_cover = track_spacing / swath_km
171
172
return days_to_cover * 24 # hours
173
174
# Elevation angle effects
175
elev_results = {}
176
for elev in elev_angles:
177
rho, area, swath, slant, nadir = coverage_params(800, elev)
178
elev_results[elev] = {'rho': rho, 'area': area, 'swath': swath, 'slant': slant}
179
180
# Create comprehensive visualization
181
fig = plt.figure(figsize=(14, 12))
182
183
# Plot 1: Coverage vs altitude
184
ax1 = fig.add_subplot(2, 3, 1)
185
alts = list(coverage_data.keys())
186
swaths = [coverage_data[h]['swath'] for h in alts]
187
areas = [coverage_data[h]['area']/1e6 for h in alts]
188
189
ax1.plot(alts, swaths, 'bo-', linewidth=2, markersize=6, label='Swath (km)')
190
ax1_twin = ax1.twinx()
191
ax1_twin.plot(alts, areas, 'rs-', linewidth=2, markersize=6, label='Area (M km$^2$)')
192
ax1.set_xlabel('Altitude (km)')
193
ax1.set_ylabel('Swath Width (km)', color='blue')
194
ax1_twin.set_ylabel('Coverage Area (M km$^2$)', color='red')
195
ax1.set_title('Coverage vs Altitude ($\\varepsilon_{min}=10^\\circ$)')
196
ax1.legend(loc='upper left', fontsize=8)
197
ax1_twin.legend(loc='lower right', fontsize=8)
198
ax1.grid(True, alpha=0.3)
199
200
# Plot 2: Elevation angle effects
201
ax2 = fig.add_subplot(2, 3, 2)
202
elevs = list(elev_results.keys())
203
swath_by_elev = [elev_results[e]['swath'] for e in elevs]
204
slant_by_elev = [elev_results[e]['slant'] for e in elevs]
205
206
x = np.arange(len(elevs))
207
width = 0.35
208
ax2.bar(x - width/2, swath_by_elev, width, label='Swath (km)', color='steelblue')
209
ax2_twin = ax2.twinx()
210
ax2_twin.bar(x + width/2, slant_by_elev, width, label='Slant Range (km)', color='coral')
211
ax2.set_xlabel('Min Elevation (deg)')
212
ax2.set_ylabel('Swath Width (km)', color='steelblue')
213
ax2_twin.set_ylabel('Slant Range (km)', color='coral')
214
ax2.set_xticks(x)
215
ax2.set_xticklabels([f'{e}' for e in elevs])
216
ax2.set_title('Elevation Angle Effects (h=800 km)')
217
ax2.legend(loc='upper left', fontsize=7)
218
ax2_twin.legend(loc='upper right', fontsize=7)
219
220
# Plot 3: Ground track
221
ax3 = fig.add_subplot(2, 3, 3)
222
lat, lon, T = ground_track(700, 98.2, duration_hours=3)
223
# Handle discontinuities
224
split_idx = np.where(np.abs(np.diff(lon)) > 180)[0] + 1
225
segments = np.split(np.arange(len(lon)), split_idx)
226
for seg in segments:
227
if len(seg) > 1:
228
ax3.plot(lon[seg], lat[seg], 'b-', linewidth=1.5, alpha=0.7)
229
ax3.set_xlim(-180, 180)
230
ax3.set_ylim(-90, 90)
231
ax3.set_xlabel('Longitude (deg)')
232
ax3.set_ylabel('Latitude (deg)')
233
ax3.set_title('Sun-Sync Ground Track (3 hrs)')
234
ax3.grid(True, alpha=0.3)
235
236
# Plot 4: Constellation comparison
237
ax4 = fig.add_subplot(2, 3, 4)
238
const_names = list(constellations.keys())
239
n_sats = [constellations[c]['T'] for c in const_names]
240
heights = [constellations[c]['h']/1000 for c in const_names] # Convert to thousands
241
242
x = np.arange(len(const_names))
243
width = 0.35
244
bars1 = ax4.bar(x - width/2, n_sats, width, label='Satellites', color='steelblue', alpha=0.8)
245
ax4_twin = ax4.twinx()
246
bars2 = ax4_twin.bar(x + width/2, heights, width, label='Alt (1000 km)', color='coral', alpha=0.8)
247
ax4.set_xlabel('Constellation')
248
ax4.set_ylabel('Number of Satellites', color='steelblue')
249
ax4_twin.set_ylabel('Altitude (1000 km)', color='coral')
250
ax4.set_xticks(x)
251
ax4.set_xticklabels([c.split()[0] for c in const_names], rotation=45, ha='right')
252
ax4.set_title('Major Constellations')
253
ax4.legend(loc='upper left', fontsize=7)
254
ax4_twin.legend(loc='upper right', fontsize=7)
255
256
# Plot 5: Revisit time vs latitude
257
ax5 = fig.add_subplot(2, 3, 5)
258
lats = np.linspace(0, 80, 50)
259
for h in [400, 800, 1200]:
260
swath = coverage_data.get(h, coverage_data[800])['swath']
261
revisits = [revisit_time(h, lat, swath) for lat in lats]
262
ax5.plot(lats, revisits, linewidth=2, label=f'{h} km')
263
ax5.set_xlabel('Latitude (deg)')
264
ax5.set_ylabel('Revisit Time (hours)')
265
ax5.set_title('Single Satellite Revisit Time')
266
ax5.legend(fontsize=8)
267
ax5.grid(True, alpha=0.3)
268
ax5.set_ylim([0, 150])
269
270
# Plot 6: Coverage footprint comparison
271
ax6 = fig.add_subplot(2, 3, 6)
272
theta = np.linspace(0, 2*np.pi, 100)
273
for h in [400, 800, 1200, 2000]:
274
rho_deg = coverage_data[h]['rho']
275
# Footprint as circle
276
x_fp = rho_deg * np.cos(theta)
277
y_fp = rho_deg * np.sin(theta)
278
ax6.plot(x_fp, y_fp, linewidth=2, label=f'{h} km')
279
ax6.plot(0, 0, 'ko', markersize=8)
280
ax6.set_xlabel('Degrees from nadir')
281
ax6.set_ylabel('Degrees from nadir')
282
ax6.set_title('Coverage Footprint Size')
283
ax6.legend(fontsize=8)
284
ax6.grid(True, alpha=0.3)
285
ax6.set_aspect('equal')
286
287
plt.tight_layout()
288
plt.savefig('satellite_coverage_plot.pdf', bbox_inches='tight', dpi=150)
289
print(r'\begin{center}')
290
print(r'\includegraphics[width=\textwidth]{satellite_coverage_plot.pdf}')
291
print(r'\end{center}')
292
plt.close()
293
294
# Key results
295
h_ref = 800
296
ref_data = coverage_data[h_ref]
297
\end{pycode}
298
299
\section{Algorithm}
300
301
\begin{algorithm}[H]
302
\SetAlgoLined
303
\KwIn{Orbital altitude $h$, minimum elevation $\varepsilon_{min}$}
304
\KwOut{Coverage parameters: $\rho$, $A_{cov}$, swath width, slant range}
305
$\sin\lambda_0 \leftarrow \frac{R_E}{R_E + h} \cos\varepsilon_{min}$\;
306
$\rho \leftarrow \pi/2 - \varepsilon_{min} - \arcsin(\sin\lambda_0)$\;
307
$A_{cov} \leftarrow 2\pi R_E^2 (1 - \cos\rho)$\;
308
$W \leftarrow 2 R_E \rho$\;
309
$d \leftarrow R_E \sin\rho / \cos(\varepsilon_{min} + \rho)$\;
310
\Return{$\rho, A_{cov}, W, d$}
311
\caption{Satellite Coverage Calculation}
312
\end{algorithm}
313
314
\section{Results and Discussion}
315
316
\subsection{Altitude Trade-offs}
317
318
\begin{pycode}
319
print(r'\begin{table}[h]')
320
print(r'\centering')
321
print(r'\caption{Coverage Parameters vs Altitude ($\varepsilon_{min}=10^\circ$)}')
322
print(r'\begin{tabular}{cccccc}')
323
print(r'\toprule')
324
print(r'Altitude & Period & Swath & Coverage & Slant & Orbits/ \\')
325
print(r'(km) & (min) & (km) & (M km$^2$) & (km) & day \\')
326
print(r'\midrule')
327
for h in altitudes:
328
d = coverage_data[h]
329
print(f"{h} & {d['period']:.1f} & {d['swath']:.0f} & {d['area']/1e6:.2f} & {d['slant']:.0f} & {d['orbits_per_day']:.1f} \\\\")
330
print(r'\bottomrule')
331
print(r'\end{tabular}')
332
print(r'\end{table}')
333
\end{pycode}
334
335
For the reference altitude of \py{h_ref} km:
336
\begin{itemize}
337
\item Coverage half-angle: \py{f"{ref_data['rho']:.1f}"}$^\circ$
338
\item Swath width: \py{f"{ref_data['swath']:.0f}"} km
339
\item Instantaneous coverage: \py{f"{ref_data['area']/1e6:.2f}"} million km$^2$
340
\item Orbital period: \py{f"{ref_data['period']:.1f}"} minutes
341
\item Orbits per day: \py{f"{ref_data['orbits_per_day']:.1f}"}
342
\end{itemize}
343
344
\begin{remark}[Altitude Selection Trade-offs]
345
Higher altitudes provide larger coverage footprints but at the cost of:
346
\begin{itemize}
347
\item Increased slant range (reduced resolution)
348
\item Higher launch cost
349
\item Longer signal delay (latency)
350
\end{itemize}
351
LEO constellations like Starlink use lower altitudes (550 km) for low latency, while GPS uses MEO (20,180 km) for fewer satellites to achieve global coverage.
352
\end{remark}
353
354
\subsection{Elevation Angle Effects}
355
356
\begin{pycode}
357
print(r'\begin{table}[h]')
358
print(r'\centering')
359
print(r'\caption{Effect of Minimum Elevation Angle (h=800 km)}')
360
print(r'\begin{tabular}{cccc}')
361
print(r'\toprule')
362
print(r'$\varepsilon_{min}$ (deg) & Swath (km) & Slant Range (km) & Coverage (M km$^2$) \\')
363
print(r'\midrule')
364
for elev in elev_angles:
365
d = elev_results[elev]
366
print(f"{elev} & {d['swath']:.0f} & {d['slant']:.0f} & {d['area']/1e6:.2f} \\\\")
367
print(r'\bottomrule')
368
print(r'\end{tabular}')
369
print(r'\end{table}')
370
\end{pycode}
371
372
\begin{remark}[Elevation Angle Selection]
373
Lower elevation angles increase coverage but degrade link quality due to:
374
\begin{itemize}
375
\item Longer atmospheric path (attenuation, scintillation)
376
\item Higher multipath interference
377
\item Increased geometric dilution of precision (GDOP) for navigation
378
\end{itemize}
379
Typical values: 5-10$^\circ$ for communications, 10-15$^\circ$ for navigation, 20-30$^\circ$ for high-precision applications.
380
\end{remark}
381
382
\subsection{Constellation Design}
383
384
\begin{pycode}
385
print(r'\begin{table}[h]')
386
print(r'\centering')
387
print(r'\caption{Major Constellation Configurations}')
388
print(r'\begin{tabular}{lcccc}')
389
print(r'\toprule')
390
print(r'Constellation & Satellites & Planes & Altitude (km) & Inclination \\')
391
print(r'\midrule')
392
for name, params in constellations.items():
393
print(f"{name} & {params['T']} & {params['P']} & {params['h']} & {params['i']:.1f}$^\\circ$ \\\\")
394
print(r'\bottomrule')
395
print(r'\end{tabular}')
396
print(r'\end{table}')
397
\end{pycode}
398
399
\section{Design Guidelines}
400
401
\subsection{Mission-Specific Recommendations}
402
\begin{itemize}
403
\item \textbf{Earth Observation}: Sun-synchronous LEO (600-800 km), consistent lighting
404
\item \textbf{Communications (Global)}: MEO or large LEO constellations
405
\item \textbf{Navigation}: MEO (20,000+ km) for geometric diversity
406
\item \textbf{Broadband Internet}: Dense LEO for low latency
407
\item \textbf{Polar Coverage}: High-inclination or Molniya orbits
408
\end{itemize}
409
410
\subsection{Constellation Sizing}
411
Minimum satellites for continuous global coverage:
412
\begin{equation}
413
N_{min} \approx \frac{4\pi}{\Omega_{sat}} = \frac{2}{1 - \cos\rho}
414
\end{equation}
415
where $\Omega_{sat}$ is the solid angle covered by one satellite.
416
417
\section{Limitations and Extensions}
418
419
\subsection{Model Limitations}
420
\begin{enumerate}
421
\item \textbf{Spherical Earth}: Does not account for Earth oblateness
422
\item \textbf{No terrain}: Ignores terrain masking effects
423
\item \textbf{Circular orbits}: Eccentric orbits not considered
424
\item \textbf{Simplified overlap}: Inter-satellite links not modeled
425
\end{enumerate}
426
427
\subsection{Possible Extensions}
428
\begin{itemize}
429
\item J2 perturbation for realistic orbit propagation
430
\item Terrain masking with digital elevation models
431
\item Monte Carlo analysis for coverage statistics
432
\item Inter-satellite link topology optimization
433
\end{itemize}
434
435
\section{Conclusion}
436
This analysis demonstrates the key trade-offs in satellite coverage design:
437
\begin{itemize}
438
\item Higher altitudes increase coverage but reduce resolution
439
\item Lower elevation angles expand coverage but degrade link quality
440
\item Constellation design must balance satellite count against coverage needs
441
\item Modern mega-constellations (Starlink, OneWeb) use hundreds of LEO satellites for continuous global coverage with low latency
442
\end{itemize}
443
444
\section*{References}
445
\begin{itemize}
446
\item Wertz, J. R., \& Larson, W. J. (1999). \textit{Space Mission Analysis and Design}. Microcosm Press.
447
\item Walker, J. G. (1984). Satellite constellations. Journal of the British Interplanetary Society.
448
\item Maral, G., \& Bousquet, M. (2009). \textit{Satellite Communications Systems}. Wiley.
449
\end{itemize}
450
451
\end{document}
452
453