Relativistic Effects
This example demonstrates relativistic corrections for precision applications.
Overview
Relativistic effects are significant for:
GNSS timing: Satellite clock corrections
Deep space navigation: Signal propagation delays
Precision timing: Gravitational time dilation
Astrometry: Light deflection near Sun
Effects Covered
- Gravitational Time Dilation
Clocks run slower in stronger gravity
GPS satellite clocks run ~45 μs/day fast
Essential for GNSS accuracy
- Special Relativistic Time Dilation
Moving clocks run slower
GPS satellites: ~7 μs/day slow
Combined effect: ~38 μs/day fast
Gravitational Potential: Gravity decreases with altitude, affecting time dilation for satellites at different orbital heights.
- Geodetic Precession
Spin axis precession in curved spacetime
De Sitter precession: ~1.9 arcsec/year
Lense-Thirring: frame dragging
Orbital Motion: Relativistic effects accumulate over orbital periods, requiring corrections for precision applications.
- Shapiro Delay
Light delay in gravitational field
Solar conjunction corrections
Affects planetary radar
Planetary Positions: Shapiro delay corrections are essential for ranging to planets, especially during solar conjunction.
Code Highlights
The example demonstrates:
Time dilation computation with
gravitational_time_dilation()Shapiro delay with
shapiro_delay()Geodetic precession with
geodetic_precession()Combined corrections for satellite clocks
Source Code
1"""Relativistic effects in orbital mechanics and space systems.
2
3This example demonstrates practical applications of general relativity
4and special relativity in modern space systems, including:
5
61. Gravitational time dilation (GPS, atomic clocks)
72. Perihelion precession (Mercury, binary pulsars)
83. Shapiro delay (interplanetary communication)
94. Post-Newtonian orbital corrections
105. Proper time in gravitational fields
116. Lense-Thirring frame-dragging effects
12
13These effects are essential for high-precision positioning, timing,
14and fundamental physics tests.
15"""
16
17import os
18from pathlib import Path
19
20import numpy as np
21import plotly.graph_objects as go
22
23from pytcl.astronomical.relativity import (
24 AU,
25 C_LIGHT,
26 G_GRAV,
27 GM_EARTH,
28 GM_SUN,
29 geodetic_precession,
30 gravitational_time_dilation,
31 lense_thirring_precession,
32 post_newtonian_acceleration,
33 proper_time_rate,
34 relativistic_range_correction,
35 schwarzschild_precession_per_orbit,
36 schwarzschild_radius,
37 shapiro_delay,
38)
39
40SHOW_PLOTS = os.environ.get("PYTCL_SHOW_PLOTS", "1") != "0"
41OUTPUT_DIR = Path(__file__).resolve().parent / "output"
42
43
44def plot_precession_effects() -> None:
45 """Visualize relativistic precession effects for different orbits."""
46 # Schwarzschild precession for different orbital radii around the Sun
47 # Using Mercury-like eccentricity (0.206)
48 radii_au = np.linspace(0.4, 0.7, 50) # Mercury to Venus range
49 radii_m = radii_au * AU
50 mercury_eccentricity = 0.20563593
51
52 # Compute precession for each radius (in arcsec per century)
53 precessions = np.array(
54 [
55 schwarzschild_precession_per_orbit(r_m, mercury_eccentricity, GM_SUN)
56 * 36525
57 * 3600
58 for r_m in radii_m
59 ]
60 )
61
62 fig = go.Figure()
63
64 fig.add_trace(
65 go.Scatter(
66 x=radii_au,
67 y=precessions,
68 mode="lines+markers",
69 name="Schwarzschild Precession",
70 line=dict(color="steelblue", width=2),
71 marker=dict(size=6),
72 hovertemplate="<b>Orbital Radius</b><br>%{x:.2f} AU<br>"
73 "<b>Precession</b><br>%{y:.2f} arcsec/century<extra></extra>",
74 )
75 )
76
77 # Mercury actual position
78 mercury_precession = (
79 schwarzschild_precession_per_orbit(0.387 * AU, mercury_eccentricity, GM_SUN)
80 * 36525
81 * 3600
82 )
83 fig.add_trace(
84 go.Scatter(
85 x=[0.387],
86 y=[mercury_precession],
87 mode="markers+text",
88 name="Mercury (Observed)",
89 marker=dict(size=12, color="red", symbol="star"),
90 text=['Mercury\n43"/century'],
91 textposition="top center",
92 hovertemplate="<b>Mercury</b><br>Radius: 0.387 AU<br>Precession: %{y:.2f} arcsec/century<extra></extra>",
93 )
94 )
95
96 fig.update_layout(
97 title="Relativistic Perihelion Precession Around the Sun",
98 xaxis_title="Orbital Radius (AU)",
99 yaxis_title="Precession (arcsec/century)",
100 hovermode="x unified",
101 height=500,
102 plot_bgcolor="rgba(240,240,240,0.5)",
103 showlegend=True,
104 )
105
106 if SHOW_PLOTS:
107 fig.show()
108 else:
109 fig.write_html(
110 str(OUTPUT_DIR / "relativity_demo.html"),
111 include_plotlyjs="cdn",
112 div_id="relativity_demo",
113 )
114
115
116def plot_time_dilation_with_altitude() -> None:
117 """Visualize gravitational and special relativistic time dilation."""
118 altitudes_km = np.linspace(0, 35786, 100) # From Earth surface to geostationary
119 altitudes_m = altitudes_km * 1e3 + 6.371e6 # Convert to meters from center
120
121 # Orbital velocities for circular orbits at each altitude
122 orbital_velocities = np.sqrt(GM_EARTH / altitudes_m)
123
124 # Time dilation factors
125 grav_dilation = np.array(
126 [gravitational_time_dilation(r, GM_EARTH) for r in altitudes_m]
127 )
128 sr_effect = (orbital_velocities**2) / (2.0 * C_LIGHT**2)
129 net_rate = grav_dilation - sr_effect
130
131 fig = go.Figure()
132
133 fig.add_trace(
134 go.Scatter(
135 x=altitudes_km,
136 y=(1 - grav_dilation) * 1e9,
137 mode="lines",
138 name="Gravitational Effect",
139 line=dict(color="blue", width=2),
140 hovertemplate="<b>Altitude:</b> %{x:.0f} km<br>"
141 "<b>Time gain rate:</b> %{y:.2f} ns/s<extra></extra>",
142 )
143 )
144
145 fig.add_trace(
146 go.Scatter(
147 x=altitudes_km,
148 y=sr_effect * 1e9,
149 mode="lines",
150 name="Special Relativistic Effect",
151 line=dict(color="red", width=2),
152 hovertemplate="<b>Altitude:</b> %{x:.0f} km<br>"
153 "<b>Time loss rate:</b> %{y:.2f} ns/s<extra></extra>",
154 )
155 )
156
157 fig.add_trace(
158 go.Scatter(
159 x=altitudes_km,
160 y=(1 - net_rate) * 1e9,
161 mode="lines",
162 name="Net Effect",
163 line=dict(color="green", width=2.5, dash="dash"),
164 hovertemplate="<b>Altitude:</b> %{x:.0f} km<br>"
165 "<b>Net time rate:</b> %{y:.2f} ns/s<extra></extra>",
166 )
167 )
168
169 # Mark GPS orbit
170 gps_alt = 20200
171 idx_gps = np.argmin(np.abs(altitudes_km - gps_alt))
172 fig.add_vline(
173 x=gps_alt,
174 line_dash="dash",
175 line_color="gray",
176 annotation_text="GPS Orbit",
177 annotation_position="top right",
178 )
179
180 fig.update_layout(
181 title="Relativistic Time Dilation vs Altitude",
182 xaxis_title="Altitude (km)",
183 yaxis_title="Time Rate Difference (ns/s)",
184 hovermode="x unified",
185 height=550,
186 plot_bgcolor="rgba(240,240,240,0.5)",
187 legend=dict(x=0.65, y=0.05),
188 )
189
190 if SHOW_PLOTS:
191 fig.show()
192 else:
193 fig.write_html(
194 str(OUTPUT_DIR / "relativity_demo.html"),
195 include_plotlyjs="cdn",
196 div_id="relativity_demo",
197 )
198
199
200def example_gps_time_dilation():
201 """Demonstrate time dilation effects in GPS satellites."""
202 print("=" * 70)
203 print("EXAMPLE 1: GPS Time Dilation Effects")
204 print("=" * 70)
205
206 # Show visualization
207 plot_time_dilation_with_altitude()
208
209 # GPS orbital parameters
210 r_gps = 26.56e6 # meters (~20,200 km altitude)
211 v_gps = 3870.0 # m/s (circular orbit velocity)
212
213 # Compute dilation factors
214 dilation = gravitational_time_dilation(r_gps, GM_EARTH)
215
216 # Special relativistic effect
217 sr_effect = (v_gps**2) / (2.0 * C_LIGHT**2)
218
219 # General relativistic effect
220 gr_effect = GM_EARTH / (C_LIGHT**2 * r_gps)
221
222 # Proper time rate
223 rate = proper_time_rate(v_gps, r_gps, GM_EARTH)
224
225 print(f"\nGPS Satellite Orbital Parameters:")
226 print(f" Altitude: {(r_gps - 6.371e6) / 1e3:.1f} km")
227 print(f" Orbital velocity: {v_gps:.1f} m/s")
228 print(f" Orbital period: ~12 hours")
229
230 print(f"\nTime Dilation Effects:")
231 print(f" Gravitational time dilation factor: {dilation:.15f}")
232 print(f" (Time runs slower in gravity field)")
233
234 print(f"\nTime Rate Comparison (per day):")
235 print(f" Special relativistic effect: -{sr_effect * 86400 * 1e9:.1f} ns/day")
236 print(f" (Satellite moving fast, slows down time)")
237 print(f" General relativistic effect: +{gr_effect * 86400 * 1e9:.1f} ns/day")
238 print(f" (Weaker gravity field, speeds up time)")
239 print(f" Net effect: {(1 - rate) * 86400 * 1e9:.1f} ns/day")
240 print(f" (Net toward weaker field = time speeds up in orbit)")
241
242 print(f"\nPractical Impact:")
243 total_daily_shift = (1 - rate) * 86400
244 print(
245 f" Without correction, GPS clock would drift: {total_daily_shift:.1f} seconds/day"
246 )
247 print(
248 f" This would cause positioning error: {total_daily_shift * C_LIGHT / 2:.0f} meters/day"
249 )
250 print(
251 f" GPS atomic clocks must be pre-offset by {-total_daily_shift * 1e6:.1f} microseconds/day"
252 )
253
254 # Time dilation vs altitude
255 print(f"\n" + "-" * 70)
256 print("Time dilation effect at different altitudes:")
257 print("-" * 70)
258
259 altitudes = [0, 300, 500, 1000, 5000, 20200, 35786] # km
260
261 for alt_km in altitudes:
262 r = 6.371e6 + alt_km * 1000
263 # Approximate circular orbit velocity
264 v = np.sqrt(GM_EARTH / r)
265 time_rate = proper_time_rate(v, r, GM_EARTH)
266 daily_shift = (1 - time_rate) * 86400
267
268 if alt_km == 0:
269 alt_name = "Earth surface"
270 elif alt_km == 35786:
271 alt_name = "Geostationary"
272 elif alt_km == 20200:
273 alt_name = "GPS orbit"
274 else:
275 alt_name = ""
276
277 print(f" {alt_km:>6} km {alt_name:<20} {daily_shift:>8.2f} seconds/day")
278
279
280def example_mercury_precession():
281 """Mercury's perihelion precession: a test of General Relativity."""
282 print("\n" + "=" * 70)
283 print("EXAMPLE 2: Mercury's Perihelion Precession")
284 print("=" * 70)
285
286 # Show visualization
287 plot_precession_effects()
288
289 # Mercury orbital elements
290 a_mercury = 0.38709927 * AU # Semi-major axis
291 e_mercury = 0.20563593 # Eccentricity
292 orbital_period = 87.969 # days
293
294 # Compute GR precession per orbit
295 precession_rad = schwarzschild_precession_per_orbit(a_mercury, e_mercury, GM_SUN)
296 precession_arcsec = precession_rad * 206265 # Convert radians to arcseconds
297
298 # Compute precession per century
299 orbits_per_century = 36525 / orbital_period
300 precession_per_century = precession_arcsec * orbits_per_century
301
302 print(f"\nMercury Orbital Parameters:")
303 print(f" Semi-major axis: {a_mercury / AU:.8f} AU = {a_mercury / 1e9:.3f} Gm")
304 print(f" Eccentricity: {e_mercury:.8f}")
305 print(f" Orbital period: {orbital_period:.3f} days")
306 print(f" Perturbing body: Sun (GM = {GM_SUN:.3e} m³/s²)")
307
308 print(f"\nGeneral Relativistic Perihelion Precession:")
309 print(f" Per orbit: {precession_arcsec:.4f} arcseconds")
310 print(f" Per century: {precession_per_century:.2f} arcseconds")
311
312 print(f"\nHistorical Context:")
313 print(f" Einstein's prediction (1916): ~43 arcsec/century")
314 print(f" Observed (Le Verrier, 1859): 5600 ± 30 arcsec/century")
315 print(f" (includes Newtonian planetary perturbations)")
316 print(f" Newtonian precession: ~5557 arcsec/century")
317 print(f" GR contribution: ~43 arcsec/century")
318 print(f" Calculated: {precession_per_century:.1f} arcsec/century OK")
319 print(f"\nThis was Einstein's first experimental confirmation of GR!")
320
321
322def example_shapiro_delay():
323 """Shapiro delay in interplanetary communication."""
324 print("\n" + "=" * 70)
325 print("EXAMPLE 3: Shapiro Delay in Interplanetary Communication")
326 print("=" * 70)
327
328 print(f"\nScenario: Earth-Sun-Spacecraft geometry at superior conjunction")
329 print(f"(Spacecraft is on opposite side of Sun from Earth)")
330
331 # Geometry at superior conjunction
332 earth_pos = np.array([1.496e11, 0.0, 0.0]) # 1 AU
333 spacecraft_pos = np.array([-(1.496e11 + 0.8e11), 0.0, 0.0]) # ~1.8 AU away
334 sun_pos = np.array([0.0, 0.0, 0.0])
335
336 # Compute Shapiro delay
337 delay = shapiro_delay(earth_pos, spacecraft_pos, sun_pos, GM_SUN)
338
339 # Distance from Earth to spacecraft
340 distance = np.linalg.norm(earth_pos - spacecraft_pos)
341
342 # Nominal light travel time without Shapiro delay
343 light_travel = distance / C_LIGHT
344
345 print(f"\nParameters:")
346 print(f" Earth distance from Sun: {np.linalg.norm(earth_pos) / AU:.3f} AU")
347 print(
348 f" Spacecraft distance from Sun: {np.linalg.norm(spacecraft_pos) / AU:.3f} AU"
349 )
350 print(
351 f" Earth-spacecraft distance: {distance / AU:.3f} AU = {distance / 1.496e11:.3f} AU"
352 )
353
354 print(f"\nRanging Measurement:")
355 print(f" Signal travel time (geometric): {light_travel:.3f} seconds")
356 print(f" Shapiro delay (GR correction): {delay * 1e6:.1f} microseconds")
357 print(f" Total propagation time: {light_travel + delay:.3f} seconds")
358 print(f" Error if uncorrected: {delay * C_LIGHT / 2:.0f} meters")
359
360 print(f"\nPractical Impact:")
361 print(f" Mariner 10 Venus flybys: Shapiro delay ~50 microseconds")
362 print(f" Cassini Saturn probe: Shapiro delay ~100+ microseconds")
363 print(f" New Horizons: Shapiro delay ~200+ microseconds at aphelion")
364 print(
365 f"\nWithout Shapiro delay correction, spacecraft navigation would be off by kilometers!"
366 )
367
368 # Shapiro delay vs Sun distance
369 print(f"\n" + "-" * 70)
370 print("Shapiro delay at different distances from Sun:")
371 print("-" * 70)
372
373 # Earth at various distances
374 for au_dist in [0.5, 1.0, 2.0, 5.0]:
375 earth_var = np.array([au_dist * AU, 0.0, 0.0])
376 craft = np.array([-(au_dist * AU + 0.8 * AU), 0.0, 0.0])
377
378 delay_var = shapiro_delay(earth_var, craft, sun_pos, GM_SUN)
379 print(f" Earth at {au_dist:.1f} AU: {delay_var * 1e6:.1f} microseconds")
380
381
382def example_post_newtonian_acceleration():
383 """Post-Newtonian orbital corrections."""
384 print("\n" + "=" * 70)
385 print("EXAMPLE 4: Post-Newtonian Orbital Corrections")
386 print("=" * 70)
387
388 # Low Earth Orbit satellite
389 r = 6.678e6 # ~300 km altitude
390
391 # Circular orbit velocity
392 v = np.sqrt(GM_EARTH / r)
393
394 # Set up position and velocity vectors
395 r_vec = np.array([r, 0.0, 0.0])
396 v_vec = np.array([0.0, v, 0.0])
397
398 # Compute accelerations
399 a_newt = -GM_EARTH / r**2 * np.array([1.0, 0.0, 0.0])
400 a_total = post_newtonian_acceleration(r_vec, v_vec, GM_EARTH)
401 a_pn = a_total - a_newt
402
403 # Compute relative correction
404 correction_magnitude = np.linalg.norm(a_pn)
405 relative_correction = correction_magnitude / np.linalg.norm(a_newt)
406
407 print(f"\nLEO Satellite Parameters:")
408 print(f" Altitude: {(r - 6.371e6) / 1e3:.0f} km")
409 print(f" Orbital velocity: {v:.1f} m/s")
410 print(f" Orbital period: {2 * np.pi * r / v / 60:.1f} minutes")
411
412 print(f"\nAcceleration Comparison:")
413 print(f" Newtonian acceleration: {np.linalg.norm(a_newt):.6f} m/s²")
414 print(f" Post-Newtonian correction: {correction_magnitude:.3e} m/s²")
415 print(
416 f" Relative correction: {relative_correction * 1e6:.1f} ppm (parts per million)"
417 )
418
419 print(f"\nOrbit Impact Over One Day:")
420 orbital_period = 2 * np.pi * r / v
421 daily_orbits = 86400 / orbital_period
422
423 # Accumulated error: dv = a*t
424 velocity_error = correction_magnitude * 86400
425
426 # Approximate range error (v*t / 2)
427 range_error = velocity_error * 86400 / 2
428
429 print(f" Orbits per day: {daily_orbits:.1f}")
430 print(f" Velocity error accumulation: {velocity_error:.3e} m/s")
431 print(f" Position error: ~{range_error:.3f} meters")
432
433 print(f"\nPractical Impact:")
434 print(f" For LEO satellites (e.g., ISS, TDRSS):")
435 print(f" - PN effects are measurable but small (ppm level)")
436 print(f" - Other perturbations (gravity harmonics, drag) dominate")
437 print(f" - PN corrections important for ultra-precise orbit determination")
438 print(f" For high-precision applications (LAGEOS, GPS):")
439 print(f" - PN corrections must be included in force models")
440
441
442def example_geodetic_precession():
443 """Geodetic (de Sitter) precession of orbital plane."""
444 print("\n" + "=" * 70)
445 print("EXAMPLE 5: Geodetic Precession")
446 print("=" * 70)
447
448 # Different orbits
449 orbits = [
450 ("ISS", 6.678e6, 0.0, np.radians(51.6)),
451 ("LAGEOS", 12.27e6, 0.0045, np.radians(109.9)),
452 ("Polar", 6.678e6, 0.0, np.radians(90.0)),
453 ]
454
455 print(f"\nGeodetic Precession (causes orbital plane to rotate):")
456 print(f" Formula: deltaomega_geodetic = 3pi GM / (c² a (1-e²)) per orbit")
457 print(f" (Positive = prograde; magnitude independent of inclination)")
458 print("-" * 70)
459 print(f"{'Orbit':<12} {'Altitude':<12} {'Inclination':<16} {'Precession':<20}")
460 print("-" * 70)
461
462 for name, a, e, inc in orbits:
463 prec = geodetic_precession(a, e, inc, GM_EARTH)
464 alt_km = (a - 6.371e6) / 1e3
465 inc_deg = np.degrees(inc)
466
467 # Convert to degrees per year
468 orbital_period = 2 * np.pi * np.sqrt(a**3 / GM_EARTH)
469 orbits_per_year = 365.25 * 86400 / orbital_period
470 prec_per_year = prec * orbits_per_year * 206265 # arcsec/year
471
472 print(
473 f"{name:<12} {alt_km:>7.0f} km {inc_deg:>6.1f}° {prec_per_year:>8.2f} arcsec/year"
474 )
475
476 # De Sitter precession of the Earth-Moon gyroscope orbiting the Sun
477 prec_sun = geodetic_precession(AU, 0.0167, 0.0, GM_SUN)
478 per_century = prec_sun * 100 * 206265 # ~100 orbits per century
479 print(f"\nDe Sitter precession of the Earth-Moon system around the Sun:")
480 print(f" {per_century:.2f} arcsec/century (confirmed by lunar laser ranging)")
481
482 print(f"\nPhysical Interpretation:")
483 print(f" - Geodetic precession arises from parallel transport of velocity")
484 print(f" - Also called de Sitter precession (discovered 1916)")
485 print(f" - Related to Lense-Thirring effect (frame dragging)")
486 print(f" - Magnitude depends only on orbit size and eccentricity, not inclination")
487
488
489def example_lense_thirring_precession():
490 """Lense-Thirring (frame-dragging) effect on orbital node."""
491 print("\n" + "=" * 70)
492 print("EXAMPLE 6: Lense-Thirring Effect (Frame-Dragging)")
493 print("=" * 70)
494
495 # LAGEOS satellite parameters
496 a = 12.27e6 # Semi-major axis
497 e = 0.0045
498 i = np.radians(109.9)
499 L_earth = 5.86e33 # Earth's spin angular momentum (kg·m²/s)
500
501 # Compute Lense-Thirring nodal precession rate (rad/s)
502 rate = lense_thirring_precession(a, e, i, L_earth, GM_EARTH)
503
504 # Convert to observable amounts
505 orbital_period = 2 * np.pi * np.sqrt(a**3 / GM_EARTH)
506 precession_per_orbit = rate * orbital_period # radians per orbit
507 mas_per_year = rate * 86400 * 365.25 * 206265 * 1e3
508
509 print(f"\nLAGEOS Satellite (Test of General Relativity):")
510 print(
511 f" Semi-major axis: {a / 1e6:.2f} Mm = {(a - 6.371e6) / 1e3:.0f} km altitude"
512 )
513 print(f" Eccentricity: {e:.4f}")
514 print(f" Inclination: {np.degrees(i):.2f}°")
515 print(
516 f" Orbital period: {orbital_period / 60:.0f} minutes = {orbital_period / 3600:.2f} hours"
517 )
518
519 print(f"\nLense-Thirring Effect:")
520 print(f" Nodal precession rate: {rate:.3e} rad/s (prograde)")
521 print(
522 f" Precession per orbit: {precession_per_orbit * 206265 * 1e6:.1f} microarcseconds"
523 )
524 print(f" Precession per year: {mas_per_year:.1f} milliarcseconds")
525 print(f" Detection method: Laser ranging (~mm precision on altitude)")
526
527 print(f"\nHistorical Context:")
528 print(f" - Predicted by Lense & Thirring (1918)")
529 print(f" - Represents frame-dragging effect of rotating body")
530 print(f" - LAGEOS confirmed at ~20% accuracy (1998)")
531 print(f" - Gravity Probe B tested at higher precision (~0.5%)")
532
533 print(f"\nPhysical Interpretation:")
534 print(f" - Earth's rotation 'drags' spacetime around it")
535 print(f" - Causes orbits to precess even though not purely axisymmetric")
536 print(f" - Similar to electromagnetic induction but for gravity")
537
538
539def example_relativistic_range_correction():
540 """Relativistic corrections to ranging measurements."""
541 print("\n" + "=" * 70)
542 print("EXAMPLE 7: Relativistic Range Corrections")
543 print("=" * 70)
544
545 print(f"\nRanging measurement technique:")
546 print(f" - Send light signal to reflector (satellite or corner cube)")
547 print(f" - Measure round-trip travel time: t = 2d/c")
548 print(f" - Compute distance: d = ct/2")
549 print(f"\nSpacetime curvature (Shapiro delay) makes the measured range")
550 print(f"slightly longer than the geometric distance between the endpoints:")
551 print(f" deltarho = (2GM/c²) ln((r1 + r2 + rho) / (r1 + r2 - rho))")
552
553 # Lunar laser ranging: ground station to a lunar retroreflector,
554 # through Earth's gravitational field
555 r_station = 6.371e6 # Ground station geocentric radius (m)
556 r_moon = 3.844e8 # Lunar retroreflector geocentric radius (m)
557 rho_moon = r_moon - r_station
558 r_corr_moon = relativistic_range_correction(r_station, r_moon, rho_moon, GM_EARTH)
559
560 print(f"\nLunar Laser Ranging (LLR):")
561 print(f" Target: Apollo 11, 14, 15 retroreflectors on Moon")
562 print(f" Range: {rho_moon / 1e3:,.0f} km")
563 print(f" Relativistic correction: {r_corr_moon * 1e3:.1f} mm (one-way)")
564 print(f" Precision of LLR: ~2-3 cm")
565 print(f" The correction exceeds the measurement precision,")
566 print(f" so it must be modeled in the ranging analysis.")
567
568 # GPS pseudoranging: satellite at zenith above the station
569 r_gps = 26.561e6 # GPS orbit radius (m)
570 rho_gps = r_gps - r_station
571 r_corr_gps = relativistic_range_correction(r_station, r_gps, rho_gps, GM_EARTH)
572
573 print(f"\nGPS Pseudoranging (satellite at zenith):")
574 print(f" Range: {rho_gps / 1e3:,.0f} km")
575 print(f" Relativistic correction: {r_corr_gps * 1e3:.2f} mm")
576
577 print(f"\n" + "-" * 70)
578 print("Shapiro range correction, ground station to overhead target:")
579 print("-" * 70)
580 print(f"{'Target':<22} {'Range (km)':<14} {'Correction (mm)':<18}")
581 print("-" * 70)
582
583 targets = {
584 "ISS (400 km)": 6.771e6,
585 "GPS (20,200 km)": 26.56e6,
586 "GEO (35,800 km)": 42.16e6,
587 "Moon (384,000 km)": 3.844e8,
588 }
589
590 for name, r_target in targets.items():
591 rho = r_target - r_station
592 corr = relativistic_range_correction(r_station, r_target, rho, GM_EARTH)
593 print(f"{name:<22} {rho / 1e3:>10,.0f} {corr * 1e3:>14.2f}")
594
595 # Ranging through the Sun's field: Earth to the solar photosphere
596 r_sun_surface = 6.96e8 # Solar radius (m)
597 rho_sun = AU - r_sun_surface
598 corr_sun = relativistic_range_correction(AU, r_sun_surface, rho_sun, GM_SUN)
599 print("-" * 70)
600 print(f"\nRanging from Earth down to the solar surface (through the Sun's")
601 print(f"own field) picks up ~= {corr_sun / 1e3:.1f} km of Shapiro correction —")
602 print(f"this is why the delay dominates interplanetary ranging near")
603 print(f"superior conjunction.")
604
605
606if __name__ == "__main__":
607 """Run all relativity examples."""
608 print("\n")
609 print("+" + "=" * 68 + "+")
610 print("|" + " " * 68 + "|")
611 print("|" + " Relativistic Effects in Space Systems".center(68) + "|")
612 print("|" + " " * 68 + "|")
613 print("+" + "=" * 68 + "+")
614
615 # Run examples
616 example_gps_time_dilation()
617 example_mercury_precession()
618 example_shapiro_delay()
619 example_post_newtonian_acceleration()
620 example_geodetic_precession()
621 example_lense_thirring_precession()
622 example_relativistic_range_correction()
623
624 OUTPUT_DIR = Path("docs/_static/images/examples")
625 OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
626
627 print("\n" + "=" * 70)
628 print("All relativity examples completed successfully!")
629 print("=" * 70 + "\n")
Running the Example
python examples/relativity_demo.py
See Also
Orbital Mechanics - Orbital propagation
High-Precision Ephemeris - Planetary positions
INS/GNSS Navigation - GNSS applications