Space Policy Basics (Licensing, Spectrum, Debris)

Module 4: Human Factors, Regulations, and Space Policy

This notebook explains why policy and regulation matter to engineers.

What you’ll learn

  • The main “policy buckets” that shape space missions
  • Who does what (FAA, FCC, ITU, UNOOSA, and international equivalents)
  • A tiny toy risk model you can tweak (for intuition)

Important note

This is not legal advice — it’s an engineering‑focused overview to build intuition.


1) Why policy shows up in engineering

Even a technically perfect spacecraft can fail as a mission if: - It can’t get a launch / reentry license - It can’t legally use radio spectrum - It creates unacceptable debris risk - It violates planetary protection or safety rules

So teams treat policy like another set of constraints — like mass, power, and delta‑V.

Code
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

plt.style.use("dark_background")
plt.rcParams.update(
    {
        "figure.dpi": 110,
        "axes.titlesize": 14,
        "axes.labelsize": 12,
        "xtick.labelsize": 10,
        "ytick.labelsize": 10,
        "legend.fontsize": 10,
    }
)

ACCENT = "#00d4ff"
DANGER = "#ff4d6d"
GOOD = "#7CFC00"

print("Environment ready. Let's look at policy constraints.")
Environment ready. Let's look at policy constraints.

2) The big policy buckets (engineering view)

Here are common categories engineers track:

  • Launch / reentry licensing
    • Safety analysis, hazard areas, flight termination systems, environmental review
  • Spectrum (radio communications)
    • Frequency coordination, interference avoidance, ground station licensing
  • Orbital debris mitigation
    • End‑of‑life disposal, collision avoidance, passivation, conjunction operations
  • Remote sensing rules (Earth observation)
    • Imaging licensing, data handling, export controls
  • Planetary protection (Moon/Mars missions)
    • Contamination control requirements that affect spacecraft design and operations

Different countries have different agencies, but the structure is similar.

3) A toy “policy risk” score (for intuition)

Real compliance is not a single number, but it’s still useful to build intuition.

We’ll make a very simple score from 0–100: - 0–30: low - 30–60: medium - 60–100: high

You’ll see how mission choices (orbit altitude, comms, imaging, crewed vs uncrewed) change the score.

Code
MISSION_PRESETS = {
    "LEO communications satellite": {
        "crewed": False,
        "earth_observation": False,
        "uses_spectrum": True,
        "altitude_km": 550,
        "disposal_plan": True,
    },
    "LEO Earth observation satellite": {
        "crewed": False,
        "earth_observation": True,
        "uses_spectrum": True,
        "altitude_km": 650,
        "disposal_plan": True,
    },
    "GEO communications satellite": {
        "crewed": False,
        "earth_observation": False,
        "uses_spectrum": True,
        "altitude_km": 35786,
        "disposal_plan": True,
    },
    "Crewed capsule (LEO)": {
        "crewed": True,
        "earth_observation": False,
        "uses_spectrum": True,
        "altitude_km": 400,
        "disposal_plan": True,
    },
    "Mars transfer spacecraft": {
        "crewed": False,
        "earth_observation": False,
        "uses_spectrum": True,
        "altitude_km": 0,
        "disposal_plan": False,
        "planetary_protection": True,
    },
}


def policy_risk_score(mission: dict) -> dict:
    """Return a breakdown + total score (0-100). Toy model for learning only."""
    breakdown = {}

    # Launch / reentry licensing tends to be more intense for crewed or high-energy missions.
    launch = 15
    if mission.get("crewed"):
        launch += 20
    if mission.get("planetary_protection"):
        launch += 10
    breakdown["launch_reentry"] = min(30, launch)

    # Spectrum always matters if you transmit.
    spectrum = 15 if mission.get("uses_spectrum") else 0
    breakdown["spectrum"] = spectrum

    # Earth observation tends to add licensing + data controls.
    eo = 15 if mission.get("earth_observation") else 0
    breakdown["remote_sensing"] = eo

    # Debris risk is often higher in crowded LEO and depends on disposal plan.
    alt = mission.get("altitude_km", 0)
    debris = 5
    if 300 <= alt <= 2000:  # crowded LEO
        debris += 20
    elif alt > 2000:
        debris += 10

    if not mission.get("disposal_plan", False):
        debris += 15
    breakdown["debris"] = min(35, debris)

    total = sum(breakdown.values())
    return {"breakdown": breakdown, "total": min(100, total)}


def plot_breakdown(label: str):
    result = policy_risk_score(MISSION_PRESETS[label])
    parts = result["breakdown"]

    # Keep category order stable (visual consistency)
    order = ["launch_reentry", "spectrum", "remote_sensing", "debris"]
    names = [k for k in order if k in parts]
    values = np.array([parts[k] for k in names])

    labels_pretty = {
        "launch_reentry": "Launch/Reentry",
        "spectrum": "Spectrum",
        "remote_sensing": "Remote sensing",
        "debris": "Debris",
    }
    colors = {
        "launch_reentry": ACCENT,
        "spectrum": "#ffd166",
        "remote_sensing": "#c77dff",
        "debris": DANGER,
    }

    display_names = [labels_pretty.get(k, k) for k in names]
    bar_colors = [colors.get(k, ACCENT) for k in names]

    fig, ax = plt.subplots(figsize=(11, 3.9))
    bars = ax.barh(display_names, values, color=bar_colors, alpha=0.85)
    ax.set_xlim(0, 40)
    ax.set_title(f"Toy policy risk breakdown — {label}\nTotal score: {result['total']}/100")
    ax.set_xlabel("toy risk points")
    ax.grid(True, axis="x", alpha=0.25)

    for b in bars:
        ax.text(b.get_width() + 0.6, b.get_y() + b.get_height() / 2, f"{b.get_width():.0f}", va="center")

    plt.tight_layout()
    plt.show()


plot_breakdown("LEO Earth observation satellite")

4) Compare mission presets (visual overview)

A single mission breakdown is useful.

But learners usually understand faster when they can compare multiple missions at once.

We’ll plot a stacked breakdown for all presets: - each bar = one mission - each segment = one policy bucket

Code
order = ["launch_reentry", "spectrum", "remote_sensing", "debris"]
labels_pretty = {
    "launch_reentry": "Launch/Reentry",
    "spectrum": "Spectrum",
    "remote_sensing": "Remote sensing",
    "debris": "Debris",
}
colors = {
    "launch_reentry": ACCENT,
    "spectrum": "#ffd166",
    "remote_sensing": "#c77dff",
    "debris": DANGER,
}

rows = []
for name, m in MISSION_PRESETS.items():
    out = policy_risk_score(m)
    row = {"mission": name, "total": out["total"]}
    for k in order:
        row[k] = out["breakdown"].get(k, 0)
    rows.append(row)

df_all = pd.DataFrame(rows).set_index("mission")
df_all = df_all.sort_values("total")

fig, ax = plt.subplots(figsize=(12, 5.2))
left = np.zeros(len(df_all))

for k in order:
    vals = df_all[k].values
    ax.barh(df_all.index, vals, left=left, color=colors[k], alpha=0.85, label=labels_pretty[k])
    left = left + vals

# Total labels
for i, (mission, row) in enumerate(df_all.iterrows()):
    ax.text(row["total"] + 1.0, i, f"{int(row['total'])}", va="center")

ax.set_xlim(0, 100)
ax.set_title("Toy policy risk comparison across mission presets")
ax.set_xlabel("toy risk score contribution (0–100)")
ax.grid(True, axis="x", alpha=0.25)
ax.legend(loc="lower right")
plt.tight_layout()
plt.show()

5) What can I do next?

  • Run the full project (more scenarios + structured categories):
cd src/Module_04_Human_Factors/Projects/Space_Policy_Calculator
pip install -r requirements.txt
python calculator.py
  • Try these quick exercises:
    • Change the preset in plot_breakdown(...) to:

      • "GEO communications satellite"
      • "Crewed capsule (LEO)"
      • "Mars transfer spacecraft"
    • Which category dominates for each, and why?

    • Add one new mission preset (your choice) and see where it lands in the stacked comparison.


References to explore: - UNOOSA debris guidelines - ITU spectrum coordination basics - National licensing regulators (FAA in the US, and equivalents elsewhere)