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

Running the Example

python examples/relativity_demo.py

See Also