Classroom Glossary Public page

RF-301 Week 8 — SATCOM: LEO Communications, Weather Satellites, Link Budgets

1,167 words

"The satellite channel is among the most challenging wireless environments: propagation delays of 250 ms or more, Doppler shifts of tens of kHz at LEO, and path losses of 150-170 dB at L-band. The receiver must be designed for the channel, not against it." — Sklar, Digital Communications, 3rd ed., Ch 16


Lecture (90 min)

6.1 Orbital Mechanics and the SATCOM Channel

Before you can design a SATCOM receiver, you need to understand the channel it must operate across. The satellite channel is determined by the orbit.

Four orbital regimes:

Orbit Altitude Period Propagation delay Doppler Examples
LEO 160-2000 km 90-130 min 2-14 ms ±40 kHz at L-band ISS, Starlink, Iridium, NOAA/POES
MEO 2000-35786 km 2-24 hr 50-150 ms ±5 kHz at L-band GPS, Galileo, GLONASS
GEO 35786 km (fixed) 24 hr ~250 ms one-way ~0 (fixed position) DirecTV, GOES, Intelsat
HEO Elliptical; perigee 500-1000 km, apogee varies Varies Varies High at perigee Molniya, Tundra

Doppler shift at LEO: A satellite at 800 km altitude has an orbital velocity of ~7500 m/s. At L-band (1575 MHz for GPS, 137 MHz for NOAA):

Δf = f₀ · v_r / c

where v_r is the radial velocity (projection of orbital velocity along the line of sight)

At the horizon (approaching pass): v_r ≈ +7500 m/s → Δf = +137e6 × 7500/3e8 ≈ +3.4 kHz (NOAA APT) At zenith: v_r ≈ 0 → Δf ≈ 0 At the other horizon (departing pass): v_r ≈ -7500 m/s → Δf ≈ -3.4 kHz

The Doppler shift changes continuously during a pass: from +3.4 kHz at horizon to 0 at zenith to -3.4 kHz at the other horizon. A SATCOM receiver must track this Doppler shift dynamically.

import numpy as np
from datetime import datetime, timezone

# Simple Doppler model for a circular LEO pass
def doppler_during_pass(elevation_deg_array, f0_hz, altitude_km=800):
    """Compute Doppler shift at each elevation angle during a pass."""
    # Earth radius
    R_e = 6371.0  # km
    mu = 3.986e14  # m^3/s^2 (Earth's gravitational parameter)
    
    r = (R_e + altitude_km) * 1e3  # orbital radius in m
    v_orbital = np.sqrt(mu / r)     # orbital velocity m/s
    
    c = 3e8  # m/s
    
    # Radial velocity as a function of elevation
    # v_r = v_orbital * cos(elevation) (approximate for overhead pass)
    el_rad = np.deg2rad(elevation_deg_array)
    v_r = v_orbital * np.cos(el_rad)  # radial component
    
    # At horizon (el=0): maximum radial velocity; at zenith (el=90): v_r=0
    # Approaching pass: v_r positive (approaching) → blueshift
    # After zenith: v_r negative → redshift
    # Flip sign for the departing half
    
    doppler_hz = f0_hz * v_r / c
    return doppler_hz

# NOAA APT pass example
el_angles = np.concatenate([np.linspace(0, 90, 50), np.linspace(90, 0, 50)])
doppler = doppler_during_pass(el_angles, f0_hz=137.5e6, altitude_km=800)

# Signs: approaching half positive, departing half negative
doppler[:50] = np.abs(doppler[:50])
doppler[50:] = -np.abs(doppler[50:])

import matplotlib.pyplot as plt
plt.figure(figsize=(10, 4))
plt.plot(np.arange(len(el_angles)), doppler / 1e3)
plt.xlabel('Sample (proportional to time)')
plt.ylabel('Doppler shift (kHz)')
plt.title('NOAA APT Doppler during overhead pass (f₀=137.5 MHz)')
plt.grid(True)
plt.savefig('doppler_pass.png', dpi=150)
print(f"Max Doppler at horizon: ±{np.max(np.abs(doppler))/1e3:.2f} kHz")

6.2 Link Budget: From Satellite to Ground Receiver

A SATCOM link budget accounts for all the gains and losses between the transmitter and the receiver.

Link budget equation:

Received power [dBW] = EIRP_tx + G_rx - FSPL - L_atm - L_misc

where:
  EIRP_tx  = transmit power + transmit antenna gain [dBW]
  G_rx     = receive antenna gain [dBi]
  FSPL     = free space path loss = 20·log₁₀(4πd/λ) [dB]
  L_atm    = atmospheric attenuation [dB]
  L_misc   = other losses (rain fade, pointing loss, etc.) [dB]

Figure of merit G/T: The key receiver quality metric for SATCOM is G/T (gain-to-noise-temperature ratio, pronounced "G over T"):

G/T [dB/K] = G_rx [dBi] - 10·log₁₀(T_sys [K])

where T_sys is the system noise temperature = T_antenna + T_LNA + contribution from losses

Higher G/T = better receiver for satellite work. A consumer dish (60 cm, G ≈ 35 dBi, T_sys ≈ 100 K) has G/T ≈ 15 dB/K. A professional SATCOM terminal (1.8 m, G ≈ 45 dBi, T_sys ≈ 50 K) has G/T ≈ 28 dB/K.

def link_budget(
    P_tx_dBW, G_tx_dBi, freq_hz, distance_km, G_rx_dBi, T_sys_K,
    L_atm_dB=0.5, L_misc_dB=1.0, data_rate_bps=None
):
    """Compute SATCOM link budget."""
    import numpy as np
    
    c = 3e8  # m/s
    wavelength = c / freq_hz
    
    # EIRP
    EIRP_dBW = P_tx_dBW + G_tx_dBi
    
    # Free space path loss
    d_m = distance_km * 1e3
    FSPL_dB = 20 * np.log10(4 * np.pi * d_m / wavelength)
    
    # Received power
    P_rx_dBW = EIRP_dBW + G_rx_dBi - FSPL_dB - L_atm_dB - L_misc_dB
    
    # G/T
    GT_dBpK = G_rx_dBi - 10 * np.log10(T_sys_K)
    
    # System noise power density: k*T_sys
    k_dB = -228.6  # dBW/K/Hz (Boltzmann's constant)
    N0_dBW_Hz = k_dB + 10 * np.log10(T_sys_K)
    
    # C/N0 (carrier-to-noise density ratio)
    CN0_dBHz = P_rx_dBW - N0_dBW_Hz
    
    results = {
        'EIRP_dBW': EIRP_dBW,
        'FSPL_dB': FSPL_dB,
        'P_rx_dBW': P_rx_dBW,
        'GT_dBpK': GT_dBpK,
        'CN0_dBHz': CN0_dBHz,
    }
    
    if data_rate_bps is not None:
        Eb_N0_dB = CN0_dBHz - 10 * np.log10(data_rate_bps)
        results['Eb_N0_dB'] = Eb_N0_dB
    
    return results

# NOAA POES APT link budget (137.5 MHz, NOAA 15/18/19)
result = link_budget(
    P_tx_dBW=10*np.log10(5),   # 5 W transmit power
    G_tx_dBi=0,                 # omnidirectional satellite antenna
    freq_hz=137.5e6,
    distance_km=800,
    G_rx_dBi=0,                 # V-dipole ground antenna; approximately omnidirectional
    T_sys_K=300,                # elevated sky noise at VHF
    data_rate_bps=4160          # APT: 2400 line/min × 2080 pixels/line ÷ ... ~ 4160 bps
)

for k, v in result.items():
    print(f"  {k:15s}: {v:.1f}")

6.3 NOAA APT: A Working SATCOM Receive System in Under $30

NOAA's POES satellites (NOAA-15, -18, -19) broadcast weather images continuously on 137.5 MHz (NOAA-15), 137.6250 MHz (NOAA-18), and 137.1 MHz (NOAA-19) using Automatic Picture Transmission (APT). The signal is:

  • Modulation: FM with ±17 kHz deviation
  • Subcarrier: 2400 Hz AM-modulated subcarrier carrying the video
  • Frame rate: 2 lines/second (120 lines/minute of satellite travel)
  • Image format: Two side-by-side channels (channel A: visible or near-IR; channel B: thermal IR)
  • Transmit power: ~5 W from an omnidirectional antenna
  • Signal visible: Passes overhead every ~100 minutes; signal audible as the classic "fax sounds"

Receive setup:

  • Antenna: V-dipole (two 53 cm elements at 120° angle) for 137 MHz
  • SDR: RTL-SDR (any model; $25-35)
  • Software: noaa-apt (free, open-source APT decoder) or GNU Radio + custom flowgraph

Doppler tracking: Because the satellite's signal shifts ±3.4 kHz during the pass, the FM discriminator in the RTL-SDR receiver handles it naturally (FM discriminators have bandwidth wider than the Doppler shift). No Doppler pre-correction is needed for APT reception -- the FM bandwidth absorbs the Doppler.

APT demodulation pipeline (GNU Radio flowgraph sketch):

[RTL-SDR Source, 2.4 MSPS] 
  → [Low Pass Filter, 40 kHz BW] 
  → [FM Demodulator, ±17 kHz deviation]
  → [Low Pass Filter, 5 kHz BW] 
  → [AM Envelope Detector, 2400 Hz subcarrier]
  → [Low Pass Filter, 2 kHz BW]
  → [Rational Resampler → 4160 SPS]
  → [WAV File Sink OR noaa-apt decoder input]

The Academy Flowgraph tool is useful here for sketching the block chain before implementing in GNU Radio. The Filter, FM Demod, and Envelope blocks in the browser tool visualize the signal transformation at each stage.


6.4 Architecture Comparison Sidebar: SATCOM Constellations

Orbit Altitude Path loss at 1 GHz Doppler at L-band Coverage per satellite Latency Named deployments
LEO 160-2000 km ~153-162 dB ±40 kHz Local footprint (~3000 km) 2-14 ms Starlink (Ku/Ka), Iridium (L), NOAA (VHF), ISS
MEO 2000-35786 km ~170-180 dB ±5 kHz Regional (8000-10000 km) 50-150 ms GPS (L1/L2), Galileo, GLONASS, O3b
GEO 35786 km ~183 dB ~0 Global (except poles) ~250 ms one-way DirecTV, GOES, Intelsat, SES
HEO Variable (elliptical) Variable High at perigee Extended polar coverage Variable Molniya (12-hr orbit), Tundra (24-hr)

Design tradeoffs:

LEO is the current growth frontier (Starlink, OneWeb, Amazon Kuiper). Lower path loss and lower latency make LEO ideal for broadband. The challenge: a single LEO satellite is only visible for ~10 minutes; global coverage requires hundreds to thousands of satellites and inter-satellite links (ISLs).

GEO has been dominant for broadcast and television since the 1970s. A single GEO satellite covers one-third of the Earth's surface -- three satellites cover global (except poles). The 250 ms latency is acceptable for video broadcast but problematic for interactive applications.

Iridium is the archetype of a LEO satellite telephone network: 66 satellites in near-polar LEO, each with a ~10 minute overhead window, handing off calls between satellites via ISLs. The Iridium constellation uses TDMA/FDMA on L-band (1616-1626.5 MHz) and is receivable with an SDR for metadata (burst timing, frame structure) even without decrypting the payload.


6.5 gr-satellites and the SATCOM Decoder Ecosystem

gr-satellites is a GNU Radio out-of-tree module that implements decoders for 100+ satellite protocols: amateur satellites (AO-73, FUNCUBE-1), NOAA APT and HRPT, ISS APRS, and others.

# List available satellite decoders
python3 -m satellites --list

# Receive and decode a pass in real time
# (requires RTL-SDR connected; substitute correct frequency for your satellite)
python3 -m satellites NOAA-15 --wavfile noaa15_pass.wav

# Or with live SDR
python3 -m satellites NOAA-18 --sdr rtlsdr --freq 137.9125e6 --samp-rate 2.4e6

gr-leo provides orbital mechanics integration: given TLE (Two-Line Elements) for a satellite, it computes the satellite's position and Doppler correction in real time and feeds the Doppler-corrected signal to the decoder.

# Fetch current TLE for NOAA-19 from Celestrak
import urllib.request
import json

tle_url = "https://celestrak.org/SOCRATES/query.php?ID=NOAA-19&format=json"
# (Use local TLE file for lab work to avoid API dependency)

# Example TLE for NOAA-19 (update before use)
tle_name = "NOAA 19"
tle_line1 = "1 33591U 09005A   24100.00000000  .00000000  00000-0  00000-0 0  9999"
tle_line2 = "2 33591  99.1000 100.0000 0013000 100.0000 260.0000 14.12000000000000"

print(f"TLE loaded for: {tle_name}")

6.6 Anchor Weave: Sklar Ch 16

Bernard Sklar's Digital Communications (3rd ed.) Ch 16 (Satellite Communications) carries the SATCOM link budget framework and the comparison of orbital regimes at advanced depth. Sklar's treatment of the link margin, rain fade statistics, and frequency band selection (L/S/C/X/Ku/Ka) is the reference for Lab 6's link budget worksheet.

The Sklar link budget discipline: Sklar structures every SATCOM link as a three-part calculation: uplink (ground to satellite), bent-pipe transponder, downlink (satellite to ground). For a receive-only application like NOAA APT, only the downlink is relevant -- but understanding the full link is necessary for designing SATCOM transmitters.


Lab Introduction

Lab 6 (20 pts): NOAA APT weather-satellite reception with V-dipole + RTL-SDR; full demodulator reimplementation in GNU Radio. Students receive a real NOAA pass, decode the APT image, then implement the demodulation pipeline in GNU Radio from the captured IQ recording. See labs/lab-6.md.

Independent Practice

  1. Predict the next NOAA-18 or NOAA-19 pass over your location using predict (a CLI orbital predictor) or Heavens-Above.com. Record the elevation, azimuth at AOS/TCA/LOS, and expected signal duration
  2. Compute the link budget for a 2.4 GHz WiFi signal at 100 m range (1 mW transmit power, 2 dBi antennas). Compare to the NOAA APT link budget -- which has more path loss? Why can you receive NOAA with a $30 setup from 800 km, but WiFi from 100 m requires careful antenna alignment?
  3. The Iridium NEXT constellation transmits at 1621.25 MHz. Given a LEO altitude of 781 km and an SDR with -100 dBm sensitivity: what receive antenna gain is required to close the link for Iridium voice traffic (data rate ~4.8 kbps)?