{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "source": [
        "#1a\n",
        "!pip install ipywidgets\n",
        "# We use ipywidgets here to create interactive sliders that let us easily adjust simulation parameters (like satellite count, collision rate, and cleanup rate) and instantly see how those changes affect the Kessler Syndrome model over time"
      ],
      "metadata": {
        "id": "UCJWJEomAp_Z"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#1b\n",
        "!pip install matplotlib moviepy numpy\n"
      ],
      "metadata": {
        "id": "WPpj6M6OLXsW"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#2a\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from matplotlib.animation import FuncAnimation\n",
        "from IPython.display import HTML\n",
        "\n",
        "# --- Constants ---\n",
        "GM = 398600  # Gravitational constant times mass of Earth (scaled for sim)\n",
        "earth_radius = 30\n",
        "dt = 0.1  # Simulation time step in seconds\n",
        "\n",
        "# --- Satellite Setup ---\n",
        "num_sats = 8\n",
        "sat_radii = np.random.uniform(90, 110, num_sats)\n",
        "angles = np.linspace(0, 2*np.pi, num_sats, endpoint=False)\n",
        "sat_positions = np.stack([sat_radii * np.cos(angles), sat_radii * np.sin(angles)], axis=-1)\n",
        "\n",
        "sat_directions = np.stack([-np.sin(angles), np.cos(angles)], axis=-1)\n",
        "sat_speeds = np.sqrt(GM / sat_radii)\n",
        "sat_velocities = sat_directions * sat_speeds[:, None]\n",
        "\n",
        "# Add slight orbital instability\n",
        "sat_velocities[0] *= 0.85\n",
        "sat_colors = ['blue'] * num_sats\n",
        "sat_sizes = np.ones(num_sats) * 3\n",
        "\n",
        "# --- Debris ---\n",
        "debris_positions, debris_velocities = [], []\n",
        "debris_colors, debris_sizes = [], []\n",
        "\n",
        "# --- Tracking ---\n",
        "object_counts = []\n",
        "\n",
        "# --- Plot Setup ---\n",
        "fig, ax = plt.subplots(figsize=(6, 6))\n",
        "ax.set_xlim(-200, 200)\n",
        "ax.set_ylim(-200, 200)\n",
        "ax.set_aspect('equal')\n",
        "ax.axis('off')\n",
        "\n",
        "earth = plt.Circle((0, 0), earth_radius, color='blue', alpha=0.3)\n",
        "ax.add_artist(earth)\n",
        "scat = ax.scatter([], [], s=[], c=[])\n",
        "\n",
        "# --- Update Function ---\n",
        "def update(frame):\n",
        "    global sat_positions, sat_velocities, sat_colors, sat_sizes\n",
        "    global debris_positions, debris_velocities, debris_colors, debris_sizes\n",
        "\n",
        "    # Update satellites\n",
        "    for i in range(len(sat_positions)):\n",
        "        r_vec = -sat_positions[i]\n",
        "        r = np.linalg.norm(r_vec)\n",
        "        acc = GM * r_vec / r**3\n",
        "        sat_velocities[i] += acc * dt\n",
        "        sat_positions[i] += sat_velocities[i] * dt\n",
        "\n",
        "    # Update debris\n",
        "    for i in range(len(debris_positions)):\n",
        "        r_vec = -debris_positions[i]\n",
        "        r = np.linalg.norm(r_vec)\n",
        "        acc = GM * r_vec / r**3\n",
        "        debris_velocities[i] += acc * dt\n",
        "        debris_positions[i] += debris_velocities[i] * dt\n",
        "\n",
        "    # Detect satellite-satellite collisions\n",
        "    new_debris = []\n",
        "    collided = set()\n",
        "    for i in range(len(sat_positions)):\n",
        "        for j in range(i+1, len(sat_positions)):\n",
        "            if np.linalg.norm(sat_positions[i] - sat_positions[j]) < 5:\n",
        "                collided.update([i, j])\n",
        "                for _ in range(6):\n",
        "                    vel = sat_velocities[i] + np.random.normal(0, 1.5, 2)\n",
        "                    new_debris.append((sat_positions[i].copy(), vel))\n",
        "\n",
        "    # Remove collided satellites\n",
        "    if collided:\n",
        "        mask = np.array([i not in collided for i in range(len(sat_positions))])\n",
        "        sat_positions = sat_positions[mask]\n",
        "        sat_velocities = sat_velocities[mask]\n",
        "        sat_sizes = sat_sizes[mask]\n",
        "        sat_colors = [sat_colors[i] for i in range(len(mask)) if mask[i]]\n",
        "\n",
        "    # Add debris\n",
        "    for pos, vel in new_debris:\n",
        "        debris_positions.append(pos)\n",
        "        debris_velocities.append(vel)\n",
        "        debris_colors.append('red')\n",
        "        debris_sizes.append(2)\n",
        "\n",
        "    # Combine all object data\n",
        "    all_pos = [*sat_positions] + debris_positions\n",
        "    all_col = [*sat_colors] + debris_colors\n",
        "    all_siz = list(sat_sizes) + debris_sizes\n",
        "\n",
        "    object_counts.append(len(all_pos))\n",
        "    scat.set_offsets(np.array(all_pos))\n",
        "    scat.set_sizes(np.array(all_siz) * 10)\n",
        "    scat.set_color(all_col)\n",
        "\n",
        "    # Time counter\n",
        "    sim_time = frame * dt\n",
        "    ax.set_title(f\"Time: {sim_time:.1f} s | Objects: {len(all_pos)}\")\n",
        "\n",
        "    return scat,\n",
        "\n",
        "# --- Animation ---\n",
        "ani = FuncAnimation(fig, update, frames=1000, interval=100, blit=True)\n",
        "HTML(ani.to_jshtml())"
      ],
      "metadata": {
        "id": "tl-eFJx_I3NV"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#2b\n",
        "# 📊 Plot object count over time\n",
        "plt.figure(figsize=(8, 4))\n",
        "plt.plot(object_counts, color='red')\n",
        "plt.title(\"Total Number of Orbiting Objects Over Time\")\n",
        "plt.xlabel(\"Time (frames)\")\n",
        "plt.ylabel(\"Number of Objects (Satellites + Debris)\")\n",
        "plt.grid(True)\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "NkWSE5vEg-Mr"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#2c\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "# Kessler Syndrome Simulation Function\n",
        "def run_kessler_sim(initial_sats, collision_chance, debris_per_collision, cleanup_rate, years=50):\n",
        "    satellites = [initial_sats]\n",
        "    debris = [0]\n",
        "    total_objects = [initial_sats]\n",
        "\n",
        "    for year in range(1, years + 1):\n",
        "        current_sats = satellites[-1]\n",
        "        current_debris = debris[-1]\n",
        "        current_total = current_sats + current_debris\n",
        "\n",
        "        expected_collisions = collision_chance * (current_total ** 2) / 2\n",
        "        expected_collisions = min(expected_collisions, current_sats)\n",
        "\n",
        "        new_debris = expected_collisions * debris_per_collision\n",
        "        debris_decay = current_debris * cleanup_rate\n",
        "\n",
        "        next_sats = current_sats - expected_collisions\n",
        "        next_debris = current_debris + new_debris - debris_decay\n",
        "        next_total = next_sats + next_debris\n",
        "\n",
        "        satellites.append(next_sats)\n",
        "        debris.append(next_debris)\n",
        "        total_objects.append(next_total)\n",
        "\n",
        "    return satellites, debris, total_objects\n",
        "\n",
        "# Timeframe\n",
        "years = 50\n",
        "years_range = list(range(0, years + 1))\n",
        "threshold = 10000  # Kessler congestion threshold\n",
        "\n",
        "# Scenario 1: No Cleanup, Dense Orbits\n",
        "sat1, deb1, tot1 = run_kessler_sim(\n",
        "    initial_sats=3000,\n",
        "    collision_chance=1e-4,\n",
        "    debris_per_collision=300,\n",
        "    cleanup_rate=0.0,\n",
        "    years=years\n",
        ")\n",
        "\n",
        "# Scenario 2: Active Cleanup\n",
        "sat2, deb2, tot2 = run_kessler_sim(\n",
        "    initial_sats=1500,\n",
        "    collision_chance=5e-6,\n",
        "    debris_per_collision=100,\n",
        "    cleanup_rate=0.05,\n",
        "    years=years\n",
        ")\n",
        "\n",
        "# Plotting side-by-side\n",
        "fig, axs = plt.subplots(1, 2, figsize=(16, 6), sharey=True)\n",
        "fig.suptitle(\"Kessler Syndrome Comparison: Catastrophic vs Controlled\")\n",
        "\n",
        "# Scenario 1 Plot\n",
        "axs[0].plot(years_range, sat1, label=\"Satellites\", color=\"blue\")\n",
        "axs[0].plot(years_range, deb1, label=\"Debris\", color=\"red\")\n",
        "axs[0].plot(years_range, tot1, label=\"Total Objects\", color=\"black\", linestyle=\"--\")\n",
        "axs[0].axhline(threshold, color='purple', linestyle=':', linewidth=2, label=\"Threshold (10,000)\")\n",
        "axs[0].set_title(\"No Cleanup\")\n",
        "axs[0].set_xlabel(\"Years\")\n",
        "axs[0].set_ylabel(\"Number of Objects in Orbit\")\n",
        "axs[0].grid(True)\n",
        "axs[0].legend()\n",
        "\n",
        "# Scenario 2 Plot\n",
        "axs[1].plot(years_range, sat2, label=\"Satellites\", color=\"blue\")\n",
        "axs[1].plot(years_range, deb2, label=\"Debris\", color=\"red\")\n",
        "axs[1].plot(years_range, tot2, label=\"Total Objects\", color=\"black\", linestyle=\"--\")\n",
        "axs[1].axhline(threshold, color='purple', linestyle=':', linewidth=2, label=\"Threshold (10,000)\")\n",
        "axs[1].set_title(\"With Cleanup\")\n",
        "axs[1].set_xlabel(\"Years\")\n",
        "axs[1].grid(True)\n",
        "axs[1].legend()\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "vJIPMkItAqEA"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#3a\n",
        "\n",
        "# This simulation models satellite collisions and orbital debris\n",
        "# inspired by the Kessler Syndrome.\n",
        "\n",
        "# Student Tasks\n",
        "# - Change initial_satellites\n",
        "# - Tweak debris_per_collision\n",
        "# - Try different too_close_threshold values\n",
        "# - Stop when debris > 100\n",
        "# - Graph debris vs. time and write a hypothesis\n",
        "\n",
        "# Simulation Parameters (Editable by Students)\n",
        "initial_satellites = 30      # Number of satellites at start\n",
        "debris_per_collision = 2     # Debris created per collision\n",
        "too_close_threshold = 5     # Proximity threshold (in km)\n",
        "max_debris_limit = 100       # Stop simulation at this many debris\n",
        "\n",
        "import random\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "# Each satellite or debris has a 2D position\n",
        "def random_position():\n",
        "    return [random.uniform(0, 100), random.uniform(0, 100)]\n",
        "\n",
        "# Initialize satellites\n",
        "satellites = [random_position() for _ in range(initial_satellites)]\n",
        "debris = []\n",
        "debris_count_over_time = []\n",
        "\n",
        "# Simulation Loop\n",
        "max_steps = 50  # You can increase this if needed\n",
        "for step in range(max_steps):\n",
        "    new_debris = []\n",
        "\n",
        "    # Check each pair of satellites for collision\n",
        "    for i in range(len(satellites)):\n",
        "        for j in range(i + 1, len(satellites)):\n",
        "            sat1 = satellites[i]\n",
        "            sat2 = satellites[j]\n",
        "            dx = sat1[0] - sat2[0]\n",
        "            dy = sat1[1] - sat2[1]\n",
        "            distance = (dx**2 + dy**2)**0.5\n",
        "            if distance < too_close_threshold:\n",
        "                # Collision occurred\n",
        "                for _ in range(debris_per_collision):\n",
        "                    new_debris.append(random_position())\n",
        "\n",
        "    debris.extend(new_debris)\n",
        "    debris_count_over_time.append(len(debris))\n",
        "\n",
        "    # Stop if debris exceeds maximum\n",
        "    if len(debris) > max_debris_limit:\n",
        "        print(f\"Simulation stopped at step {step} — debris exceeded limit ({len(debris)})\")\n",
        "        break\n",
        "\n",
        "# --- Plot Debris Over Time ---\n",
        "plt.figure(figsize=(8, 4))\n",
        "plt.plot(debris_count_over_time, marker='o', linestyle='-', color='red')\n",
        "plt.title('Orbital Debris Over Time')\n",
        "plt.xlabel('Time Step')\n",
        "plt.ylabel('Debris Count')\n",
        "plt.grid(True)\n",
        "plt.show()\n",
        "\n",
        "# Hypothesis Prompt\n",
        "print(\"Write Your Hypothesis:\")\n",
        "print(\"What caused the rapid increase in orbital debris?\")\n",
        "print(\"- Was there a chain reaction of collisions?\")\n",
        "print(\"- Did your threshold or debris-per-collision value contribute?\")\n",
        "print(\"- How might this relate to the Kessler Syndrome in real life?\")\n",
        "\n"
      ],
      "metadata": {
        "id": "H7XW_s0ukKVn"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#4a\n",
        "import random\n",
        "import matplotlib.pyplot as plt\n",
        "import ipywidgets as widgets\n",
        "from IPython.display import display\n",
        "\n",
        "# === CONFIGURATION ===\n",
        "too_close_thresholds = {'LEO': 10, 'MEO': 15, 'GEO': 20}\n",
        "debris_per_collision = 5\n",
        "max_debris_limit = 500\n",
        "max_steps = 100\n",
        "leo_lifetime = 20\n",
        "\n",
        "launch_cost = {'LEO': 2_000_000, 'MEO': 3_000_000, 'GEO': 5_000_000}\n",
        "insurance_cost = 500_000\n",
        "insurance_refund = 1_000_000\n",
        "launch_budget = 100_000_000\n",
        "\n",
        "orbital_layers = ['LEO', 'MEO', 'GEO']\n",
        "layer_bounds = {'LEO': (0, 50), 'MEO': (50, 100), 'GEO': (100, 150)}\n",
        "\n",
        "satellites = []\n",
        "debris = []\n",
        "debris_count_over_time = []\n",
        "active_satellites_over_time = []\n",
        "\n",
        "solar_activity_chance = 0.1\n",
        "debris_decay_rate = 0.3\n",
        "\n",
        "current_policy = 'no_policy'\n",
        "step_counter = 0\n",
        "risk_streak = 0\n",
        "low_risk_goal = 10\n",
        "gps_goal = 3\n",
        "gps_launched = 0\n",
        "missions_success = []\n",
        "\n",
        "launched_layers = set()\n",
        "launch_history = []\n",
        "cascade_triggered = False\n",
        "insurance_payouts = 0\n",
        "expired_leo_sats = 0\n",
        "\n",
        "satellite_types = ['Weather', 'Comms', 'GPS']\n",
        "purpose_counts = {'Weather': 0, 'Comms': 0, 'GPS': 0}\n",
        "\n",
        "# === RANDOMIZED MISSIONS CONFIG ===\n",
        "all_missions = {\n",
        "    'GPS_3_by_20': {\n",
        "        'desc': 'Launch 3 GPS satellites by Step 20',\n",
        "        'check': lambda: gps_launched >= gps_goal and step_counter >= 20,\n",
        "    },\n",
        "    '10x_LOW_RISK': {\n",
        "        'desc': 'Maintain LOW risk for 10 consecutive steps',\n",
        "        'check': lambda: risk_streak >= low_risk_goal,\n",
        "    },\n",
        "    'All_3_Layers': {\n",
        "        'desc': 'Launch into all 3 orbits (LEO, MEO, GEO)',\n",
        "        'check': lambda: len(launched_layers) == 3,\n",
        "    },\n",
        "    'Balanced_Deployment': {\n",
        "        'desc': 'Launch at least 5 satellites into each orbit',\n",
        "        'check': lambda: all(launch_history.count(layer) >= 5 for layer in orbital_layers),\n",
        "    },\n",
        "    'Insurance_Strategist': {\n",
        "        'desc': 'Get 3 insurance payouts without exceeding medium risk',\n",
        "        'check': lambda: insurance_payouts >= 3 and all(d < 150 for d in debris_count_over_time),\n",
        "    },\n",
        "    'Skip_Steps': {\n",
        "        'desc': 'Skip launching for 5 total steps',\n",
        "        'check': lambda: launch_history.count(\"none\") >= 5,\n",
        "    },\n",
        "    'No_Cascades': {\n",
        "        'desc': 'Avoid collision cascades entirely',\n",
        "        'check': lambda: not cascade_triggered,\n",
        "    },\n",
        "    'Debris_Dropper': {\n",
        "        'desc': 'Reduce debris below 50 after passing 200',\n",
        "        'check': lambda: any(\n",
        "            d < 50 for i, d in enumerate(debris_count_over_time)\n",
        "            if max(debris_count_over_time[:i+1], default=0) > 200\n",
        "        ),\n",
        "    },\n",
        "    'Danger_Survivor': {\n",
        "        'desc': 'Survive 5 CRITICAL risk steps without exceeding max debris',\n",
        "        'check': lambda: sum(d > 300 for d in debris_count_over_time) >= 5 and len(debris) <= max_debris_limit,\n",
        "    },\n",
        "    'Sustainable_Skies': {\n",
        "        'desc': 'End with <100 debris and >10 satellites',\n",
        "        'check': lambda: len(debris) < 100 and len(satellites) > 10,\n",
        "    },\n",
        "    'Natural_Retirement': {\n",
        "        'desc': 'Let 3 LEO satellites expire naturally',\n",
        "        'check': lambda: expired_leo_sats >= 3,\n",
        "    },\n",
        "    'Orbital_Streak': {\n",
        "        'desc': 'Launch into same orbit 5 times in a row',\n",
        "        'check': lambda: any(launch_history[i:i+5].count(launch_history[i]) == 5 for i in range(len(launch_history)-4)),\n",
        "    },\n",
        "    'GPS_Priority': {\n",
        "        'desc': 'Keep GPS as ≥30% of active satellites',\n",
        "        'check': lambda: len(satellites) > 0 and (sum(1 for s in satellites if s['purpose'] == 'GPS') / len(satellites)) >= 0.3,\n",
        "    }\n",
        "}\n",
        "\n",
        "selected_missions = random.sample(list(all_missions.keys()), 3)\n",
        "mission_status = {k: False for k in selected_missions}\n",
        "\n",
        "# === UI SETUP ===\n",
        "def update_mission_display():\n",
        "    html = \"<b>🎯 Missions:</b><br>\"\n",
        "    for key in selected_missions:\n",
        "        status = \"✅\" if mission_status[key] else \"⬜\"\n",
        "        html += f\"{status} {all_missions[key]['desc']}<br>\"\n",
        "    missions_box.value = html\n",
        "\n",
        "# === HELPERS ===\n",
        "def random_position_in_layer(layer):\n",
        "    x = random.uniform(0, 100)\n",
        "    y_range = layer_bounds[layer]\n",
        "    y = random.uniform(*y_range)\n",
        "    return x, y\n",
        "\n",
        "def random_satellite(layer, insured=False):\n",
        "    pos = random_position_in_layer(layer)\n",
        "    purpose = random.choice(satellite_types)\n",
        "    return {\n",
        "        'x': pos[0],\n",
        "        'y': pos[1],\n",
        "        'layer': layer,\n",
        "        'purpose': purpose,\n",
        "        'age': 0,\n",
        "        'insured': insured\n",
        "    }\n",
        "\n",
        "def check_collision(s1, s2):\n",
        "    if s1['layer'] != s2['layer']:\n",
        "        return False\n",
        "    dx = s1['x'] - s2['x']\n",
        "    dy = s1['y'] - s2['y']\n",
        "    distance = (dx**2 + dy**2)**0.5\n",
        "    threshold = too_close_thresholds[s1['layer']]\n",
        "    return distance < threshold\n",
        "\n",
        "def simulate_step(launch_layer=None, insure=False):\n",
        "    global step_counter, satellites, debris, launch_budget\n",
        "    global gps_launched, risk_streak, insurance_payouts, expired_leo_sats, cascade_triggered\n",
        "\n",
        "    if step_counter >= max_steps or len(debris) > max_debris_limit:\n",
        "        print(\"⚠️ Simulation ended or max debris exceeded.\")\n",
        "        disable_buttons()\n",
        "        show_plot()\n",
        "        return\n",
        "\n",
        "    print(f\"\\n--- Step {step_counter+1} ---\")\n",
        "\n",
        "    if launch_layer in orbital_layers:\n",
        "        cost = launch_cost[launch_layer] + (insurance_cost if insure else 0)\n",
        "        if launch_budget >= cost:\n",
        "            sat = random_satellite(launch_layer, insured=insure)\n",
        "            satellites.append(sat)\n",
        "            purpose_counts[sat['purpose']] += 1\n",
        "            launch_budget -= cost\n",
        "            launch_history.append(launch_layer)\n",
        "            launched_layers.add(launch_layer)\n",
        "            if sat['purpose'] == 'GPS':\n",
        "                gps_launched += 1\n",
        "            print(f\"🛰️ Launched {sat['purpose']} satellite into {launch_layer} (-${{cost:,}}, {'insured' if insure else 'uninsured'})\")\n",
        "        else:\n",
        "            print(\"🚫 Not enough budget to launch.\")\n",
        "    elif launch_layer == \"none\":\n",
        "        launch_history.append(\"none\")\n",
        "        print(\"⏭️ No satellite launched this step.\")\n",
        "\n",
        "    # === Decay old LEO satellites ===\n",
        "    remaining = []\n",
        "    for sat in satellites:\n",
        "        sat['age'] += 1\n",
        "        if sat['layer'] == 'LEO' and sat['age'] >= leo_lifetime:\n",
        "            expired_leo_sats += 1\n",
        "            purpose_counts[sat['purpose']] -= 1\n",
        "            print(f\"☠️ LEO satellite expired (age {sat['age']})\")\n",
        "        else:\n",
        "            remaining.append(sat)\n",
        "    satellites[:] = remaining\n",
        "\n",
        "    # === Collisions ===\n",
        "    new_debris = []\n",
        "    collided = set()\n",
        "    collision_count = 0\n",
        "    for i in range(len(satellites)):\n",
        "        for j in range(i + 1, len(satellites)):\n",
        "            if check_collision(satellites[i], satellites[j]):\n",
        "                collided.update([i, j])\n",
        "                for _ in range(debris_per_collision):\n",
        "                    x, y = random_position_in_layer(satellites[i]['layer'])\n",
        "                    new_debris.append({'x': x, 'y': y, 'layer': satellites[i]['layer']})\n",
        "                collision_count += 1\n",
        "\n",
        "    if collision_count >= 3:\n",
        "        cascade_triggered = True\n",
        "        print(\"⚠️ Collision cascade started!\")\n",
        "\n",
        "    for idx in collided:\n",
        "        sat = satellites[idx]\n",
        "        if sat['insured']:\n",
        "            launch_budget += insurance_refund\n",
        "            insurance_payouts += 1\n",
        "            print(\"💵 Insurance payout for lost satellite.\")\n",
        "\n",
        "    satellites = [s for idx, s in enumerate(satellites) if idx not in collided]\n",
        "    debris.extend(new_debris)\n",
        "\n",
        "    # === Solar activity decay ===\n",
        "    if random.random() < solar_activity_chance:\n",
        "        old_len = len(debris)\n",
        "        debris = [d for d in debris if d['layer'] != 'LEO' or random.random() > debris_decay_rate]\n",
        "        print(f\"🌞 Solar storm! Debris reduced from {old_len} to {len(debris)}\")\n",
        "\n",
        "    # === Metrics + Risk ===\n",
        "    debris_count_over_time.append(len(debris))\n",
        "    active_satellites_over_time.append(len(satellites))\n",
        "    step_counter += 1\n",
        "\n",
        "    risk = \"🟢 LOW\"\n",
        "    if len(debris) > 150:\n",
        "        risk = \"🟡 MEDIUM\"\n",
        "    if len(debris) > 300:\n",
        "        risk = \"🔴 CRITICAL\"\n",
        "\n",
        "    if risk == \"🟢 LOW\":\n",
        "        risk_streak += 1\n",
        "    else:\n",
        "        risk_streak = 0\n",
        "\n",
        "    print(f\"📊 Debris: {len(debris)} | Satellites: {len(satellites)} | Budget: ${{launch_budget:,}} | Risk: {risk}\")\n",
        "\n",
        "    # === Check Missions ===\n",
        "    for key in selected_missions:\n",
        "        if not mission_status[key] and all_missions[key]['check']():\n",
        "            mission_status[key] = True\n",
        "\n",
        "    update_mission_display()\n",
        "\n",
        "    if len(debris) > max_debris_limit or step_counter >= max_steps:\n",
        "        end_simulation()\n",
        "\n",
        "def end_simulation():\n",
        "    disable_buttons()\n",
        "    score = len(satellites) - len(debris)\n",
        "    print(f\"🏁 Final Score: {score} (Satellites - Debris)\")\n",
        "    print(\"--- Mission Results ---\")\n",
        "    for k in selected_missions:\n",
        "        status = \"✅\" if mission_status[k] else \"❌\"\n",
        "        print(f\"{status} {all_missions[k]['desc']}\")\n",
        "    show_plot()\n",
        "\n",
        "def disable_buttons():\n",
        "    button_leo_ins.disabled = True\n",
        "    button_meo_ins.disabled = True\n",
        "    button_geo_ins.disabled = True\n",
        "    button_leo.disabled = True\n",
        "    button_meo.disabled = True\n",
        "    button_geo.disabled = True\n",
        "    button_none.disabled = True\n",
        "\n",
        "def show_plot():\n",
        "    plt.figure(figsize=(10, 4))\n",
        "    plt.plot(debris_count_over_time, label='Debris Count', color='red')\n",
        "    plt.plot(active_satellites_over_time, label='Active Satellites', color='green')\n",
        "    plt.xlabel('Time Step')\n",
        "    plt.ylabel('Count')\n",
        "    plt.title('Simulation Outcome')\n",
        "    plt.grid(True)\n",
        "    plt.legend()\n",
        "    plt.show()\n",
        "\n",
        "# === BUTTONS ===\n",
        "button_leo = widgets.Button(description=\"LEO ($2M)\")\n",
        "button_meo = widgets.Button(description=\"MEO ($3M)\")\n",
        "button_geo = widgets.Button(description=\"GEO ($5M)\")\n",
        "button_leo_ins = widgets.Button(description=\"LEO + Insurance ($2.5M)\")\n",
        "button_meo_ins = widgets.Button(description=\"MEO + Insurance ($3.5M)\")\n",
        "button_geo_ins = widgets.Button(description=\"GEO + Insurance ($5.5M)\")\n",
        "button_none = widgets.Button(description=\"Skip ❌\")\n",
        "\n",
        "button_leo.on_click(lambda b: simulate_step('LEO', insure=False))\n",
        "button_meo.on_click(lambda b: simulate_step('MEO', insure=False))\n",
        "button_geo.on_click(lambda b: simulate_step('GEO', insure=False))\n",
        "button_leo_ins.on_click(lambda b: simulate_step('LEO', insure=True))\n",
        "button_meo_ins.on_click(lambda b: simulate_step('MEO', insure=True))\n",
        "button_geo_ins.on_click(lambda b: simulate_step('GEO', insure=True))\n",
        "button_none.on_click(lambda b: simulate_step('none'))\n",
        "\n",
        "missions_box = widgets.HTML()\n",
        "update_mission_display()\n",
        "\n",
        "display(widgets.VBox([\n",
        "    missions_box,\n",
        "    widgets.HBox([button_leo, button_meo, button_geo]),\n",
        "    widgets.HBox([button_leo_ins, button_meo_ins, button_geo_ins]),\n",
        "    button_none\n",
        "]))\n",
        "\n",
        "print(\"⬇️ Choose your launch. You can insure satellites or skip.\")"
      ],
      "metadata": {
        "id": "A7JDV7d9ZqwS"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#4b\n",
        "# Step 1: Install FFmpeg for saving animations\n",
        "!apt-get install -y ffmpeg\n",
        "\n",
        "# Step 2: Import libraries\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from matplotlib.animation import FuncAnimation\n",
        "from mpl_toolkits.mplot3d import Axes3D\n",
        "\n",
        "# === Configuration ===\n",
        "np.random.seed(42)\n",
        "num_fragments = 200\n",
        "zones = np.random.choice(['LEO', 'MEO', 'GEO'], size=num_fragments, p=[0.6, 0.25, 0.15])\n",
        "altitudes = {'LEO': 7000, 'MEO': 17000, 'GEO': 42164}  # km from Earth center\n",
        "zone_colors = {'LEO': 'blue', 'MEO': 'green', 'GEO': 'orange'}\n",
        "angular_velocities = {'LEO': 0.002, 'MEO': 0.001, 'GEO': 0.0005}  # radians/sec\n",
        "\n",
        "# Assign orbital parameters\n",
        "radii = np.array([altitudes[z] for z in zones])\n",
        "omegas = np.array([angular_velocities[z] for z in zones])\n",
        "initial_angles = np.random.uniform(0, 2 * np.pi, size=num_fragments)\n",
        "inclinations = np.radians(np.random.uniform(-30, 30, size=num_fragments))  # random tilt\n",
        "colors = [zone_colors[z] for z in zones]\n",
        "\n",
        "# Earth sphere\n",
        "earth_radius = 6371\n",
        "u = np.linspace(0, 2 * np.pi, 50)\n",
        "v = np.linspace(0, np.pi, 50)\n",
        "x_earth = earth_radius * np.outer(np.cos(u), np.sin(v))\n",
        "y_earth = earth_radius * np.outer(np.sin(u), np.sin(v))\n",
        "z_earth = earth_radius * np.outer(np.ones(np.size(u)), np.cos(v))\n",
        "\n",
        "# === Plot Setup ===\n",
        "fig = plt.figure(figsize=(10, 8))\n",
        "ax = fig.add_subplot(111, projection='3d')\n",
        "ax.set_xlim(-50000, 50000)\n",
        "ax.set_ylim(-50000, 50000)\n",
        "ax.set_zlim(-50000, 50000)\n",
        "ax.set_box_aspect([1, 1, 1])\n",
        "ax.set_xlabel(\"X (km)\")\n",
        "ax.set_ylabel(\"Y (km)\")\n",
        "ax.set_zlabel(\"Z (km)\")\n",
        "ax.view_init(elev=25, azim=30)\n",
        "ax.plot_surface(x_earth, y_earth, z_earth, color='lightblue', alpha=0.3, linewidth=0)\n",
        "\n",
        "# Zone Labels\n",
        "for z, alt in altitudes.items():\n",
        "    ax.text(0, 0, alt, f\"{z}\", color=zone_colors[z], fontsize=10)\n",
        "\n",
        "# Debris dots + trails\n",
        "sc = ax.scatter([], [], [], s=15, alpha=0.8)\n",
        "trail_len = 10\n",
        "trail_lines = [ax.plot([], [], [], lw=0.5, color=zone_colors[z])[0] for z in zones]\n",
        "trail_history = np.zeros((num_fragments, trail_len, 3))\n",
        "\n",
        "# === Animation Update Function ===\n",
        "def update(frame):\n",
        "    t = frame * 100\n",
        "    theta = initial_angles + omegas * t\n",
        "    x = radii * np.cos(theta)\n",
        "    y = radii * np.sin(theta)\n",
        "    z = radii * np.sin(inclinations) * np.sin(theta)\n",
        "\n",
        "    # Debris update\n",
        "    sc._offsets3d = (x, y, z)\n",
        "    sc.set_color(colors)\n",
        "\n",
        "    # Trail update\n",
        "    trail_history[:, :-1] = trail_history[:, 1:]\n",
        "    trail_history[:, -1, 0] = x\n",
        "    trail_history[:, -1, 1] = y\n",
        "    trail_history[:, -1, 2] = z\n",
        "\n",
        "    for i, line in enumerate(trail_lines):\n",
        "        line.set_data(trail_history[i, :, 0], trail_history[i, :, 1])\n",
        "        line.set_3d_properties(trail_history[i, :, 2])\n",
        "\n",
        "    ax.view_init(elev=25, azim=30 + frame * 1.2)\n",
        "    ax.set_title(f\"Orbital Debris Rings (t = {t} s)\", fontsize=14)\n",
        "    return sc, *trail_lines\n",
        "\n",
        "# === Generate Animation ===\n",
        "anim = FuncAnimation(fig, update, frames=90, interval=120, blit=False)\n",
        "anim.save(\"orbital_debris_rings_final.mp4\", writer=\"ffmpeg\", fps=15)\n",
        "\n",
        "# === Download the video ===\n",
        "from google.colab import files\n",
        "files.download(\"orbital_debris_rings_final.mp4\")\n"
      ],
      "metadata": {
        "id": "775haMkzfHdY"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#https://en.wikipedia.org/wiki/2024_YR4#:~:text=This%20was%20the%20second%2Dhighest,impact%20on%201%20March%202025.\n",
        "#5a\n",
        "import numpy as np\n",
        "from datetime import datetime\n",
        "#A dictionary (asteroid_data) stores simplified orbital parameters of 2024 YR4, such as:\n",
        "#Eccentricity, semi-major axis, inclination, perihelion distance, etc.\n",
        "#These are used as inputs for position and velocity calculations.\n",
        "# Define the orbital elements of 2024 YR4 (as an example) for the collision risk calculation\n",
        "asteroid_data = {\n",
        "    'e': 0.6615483387208111,  # Eccentricity\n",
        "    'a': 2.515868595636145,  # Semi-major axis in AU\n",
        "    'q': 0.8514999057531932,  # Perihelion in AU\n",
        "    'i': 3.408174626160702,  # Inclination in degrees\n",
        "    'node': 271.365616491282,  # Longitude of ascending node in degrees\n",
        "    'peri': 134.3613646975317,  # Argument of perihelion in degrees\n",
        "    'M': 40.40255371661091,  # Mean anomaly in degrees\n",
        "    'tp': 2460636.917563124088,  # Time of perihelion passage (Modified Julian Date)\n",
        "    'period': 1457.573144717252,  # Orbital period in days\n",
        "    'n': 0.2469858897337429,  # Mean motion (deg/day)\n",
        "    'Q': 4.180237285519097,  # Aphelion distance in AU\n",
        "}\n",
        "\n",
        "# Define the current date and time (March 8, 2025)\n",
        "current_time = datetime(2025, 3, 8)\n",
        "\n",
        "# Function to calculate distance and relative velocity for the asteroid (simplified for this example)\n",
        "def calculate_asteroid_position_velocity(data, time_since_perihelion):\n",
        "    \"\"\"Calculate the asteroid's position and velocity given the time since perihelion.\"\"\"\n",
        "    # Using simplified orbital mechanics (Keplerian orbit assumptions)\n",
        "    mean_anomaly = (data['M'] + data['n'] * time_since_perihelion) % 360  # Mean anomaly\n",
        "    # For simplicity, we will assume circular orbits (ignoring eccentricity for this simple model)\n",
        "    # Calculate the distance based on semi-major axis for circular orbit\n",
        "    distance = data['a']  # In AU\n",
        "    velocity = np.sqrt((1 / distance) * (1 / data['a']))  # Simplified velocity for circular orbit\n",
        "    return distance, velocity\n",
        "\n",
        "# Function to estimate collision risk based on distance, relative velocity, and impact probability\n",
        "def estimate_collision_risk(distance, relative_velocity, impact_probability, safe_distance=10.0, max_velocity=3.0):\n",
        "    \"\"\"Estimate the risk of collision with Earth based on distance, velocity, and impact probability.\"\"\"\n",
        "    # Adjust the risk based on the tracking confidence and impact probability\n",
        "    if impact_probability > 0:\n",
        "        # Calculate risk based on distance and velocity first\n",
        "        collision_risk = 0.0\n",
        "        if distance < safe_distance:\n",
        "            velocity_magnitude = np.linalg.norm(relative_velocity)\n",
        "            if velocity_magnitude > max_velocity:\n",
        "                collision_risk = 1.0\n",
        "            else:\n",
        "                collision_risk = velocity_magnitude / max_velocity\n",
        "        # Now factor in the impact probability (could be from initial tracking or a worst-case scenario)\n",
        "        collision_risk *= impact_probability  # Weight the collision risk by the impact probability\n",
        "        return collision_risk\n",
        "    else:\n",
        "        return 0.0  # No risk if probability is zero or unknown\n",
        "\n",
        "# Example calculation (time since perihelion as an example)\n",
        "time_since_perihelion = (current_time - datetime(2024, 11, 22)).days  # Days since perihelion on Nov 22, 2024\n",
        "\n",
        "# Simplified asteroid position and velocity calculation (for demonstration purposes)\n",
        "distance, velocity = calculate_asteroid_position_velocity(asteroid_data, time_since_perihelion)\n",
        "\n",
        "# Example impact probability (say 3.1% as a worst-case scenario initially, now reduced)\n",
        "impact_probability = 0.031  # 3.1% chance, adjusted over time\n",
        "\n",
        "# Now, calculate the collision risk\n",
        "collision_risk = estimate_collision_risk(distance, velocity, impact_probability)\n",
        "\n",
        "# Display the result\n",
        "print(f\"Collision risk for asteroid 2024 YR4: {collision_risk:.4f}\")"
      ],
      "metadata": {
        "id": "8iG9pXsCAqGZ"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}