Last commit not found
raw
history blame
6.58 kB
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
# ===============================
# Streamlit Interface Setup
# ===============================
st.title("Spin Launch & Orbital Attachment Simulation")
st.markdown(
"""
This simulation demonstrates a two‑phase launch:
1. **Spin‑Launch Phase:** A payload is accelerated from an elevated platform.
2. **Orbital Phase:** At a chosen altitude, the payload “docks” with a human‑rated upper stage by
instantly setting its velocity to that of a circular orbit.
Adjust the parameters in the sidebar and click "Run Simulation" to see the result.
"""
)
# Sidebar parameters for simulation
st.sidebar.header("Simulation Parameters")
# Time and integration parameters:
dt = st.sidebar.number_input("Time Step (s)", min_value=0.01, max_value=1.0, value=0.1, step=0.01)
t_max = st.sidebar.number_input("Total Simulation Time (s)", min_value=100, max_value=5000, value=1000, step=100)
# Launch conditions:
initial_altitude_km = st.sidebar.number_input("Initial Altitude (km)", min_value=1, max_value=100, value=50, step=1)
initial_velocity = st.sidebar.number_input("Initial Velocity (m/s)", min_value=100, max_value=5000, value=1200, step=100)
# Docking/Orbital attachment altitude:
docking_altitude_km = st.sidebar.number_input("Docking Altitude (km)", min_value=1, max_value=500, value=100, step=1)
# Button to run simulation
run_sim = st.sidebar.button("Run Simulation")
if run_sim:
# ===============================
# Physical Constants and Conversions
# ===============================
mu = 3.986e14 # Earth's gravitational parameter, m^3/s^2
R_E = 6.371e6 # Earth's radius, m
# Convert altitude values from km to m:
initial_altitude = initial_altitude_km * 1000.0 # initial altitude above Earth's surface (m)
docking_altitude = docking_altitude_km * 1000.0 # docking altitude above Earth's surface (m)
r_dock = R_E + docking_altitude # docking radius from Earth's center (m)
# ===============================
# Initial Conditions for the Simulation
# ===============================
# Starting at a point along the x-axis at a radial distance (Earth's radius + initial altitude)
initial_position = np.array([R_E + initial_altitude, 0.0])
# Initial velocity is chosen to be radial (pointing outward) at the chosen speed.
initial_velocity_vec = np.array([initial_velocity, 0.0])
# ===============================
# Define the Acceleration Function
# ===============================
def acceleration(pos):
"""
Compute acceleration due to Earth's gravity at position pos (in m).
"""
r = np.linalg.norm(pos)
return -mu * pos / r**3
# ===============================
# Run the Simulation Loop
# ===============================
states = [] # Will store tuples of (time, x, y, phase)
phase = 1 # Phase 1: spin-launch, Phase 2: orbital phase after docking
t = 0.0
pos = initial_position.copy()
vel = initial_velocity_vec.copy()
docking_done = False
docking_event_time = None
docking_event_coords = None
while t < t_max:
# --- Check for the Docking Event ---
# When the payload's distance from Earth's center exceeds the docking radius
# and it is still moving outward (positive radial velocity), we trigger the docking.
if phase == 1 and not docking_done and np.linalg.norm(pos) >= r_dock and vel.dot(pos) > 0:
docking_done = True
phase = 2 # Switch to orbital phase
r_current = np.linalg.norm(pos)
# Compute the circular orbital speed at the current radius:
v_circ = np.sqrt(mu / r_current)
# For a prograde orbit, choose a tangential direction (perpendicular to the radial vector):
tangential_dir = np.array([-pos[1], pos[0]]) / r_current
vel = v_circ * tangential_dir # Instantaneously change velocity for orbital insertion
docking_event_time = t
docking_event_coords = pos.copy()
st.write(f"**Docking Event:** t = {t:.1f} s, Altitude = {(r_current - R_E)/1000:.1f} km, Circular Speed = {v_circ:.1f} m/s")
# Record current state: (time, x, y, phase)
states.append((t, pos[0], pos[1], phase))
# --- Propagate the state using simple Euler integration ---
a = acceleration(pos)
pos = pos + vel * dt
vel = vel + a * dt
t += dt
# Convert states to a NumPy array for easier slicing:
states = np.array(states) # columns: time, x, y, phase
# ===============================
# Create the Trajectory Plot with Matplotlib
# ===============================
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_aspect('equal')
ax.set_xlabel("x (km)")
ax.set_ylabel("y (km)")
ax.set_title("Trajectory: Spin Launch and Orbital Attachment")
# Draw Earth as a blue circle (Earth's radius in km)
earth = plt.Circle((0, 0), R_E / 1000, color='blue', alpha=0.3, label="Earth")
ax.add_artist(earth)
# Define plot limits (extend to show orbit, e.g., Earth's radius plus 300 km):
max_extent = (R_E + 300e3) / 1000 # in km
ax.set_xlim(-max_extent, max_extent)
ax.set_ylim(-max_extent, max_extent)
# Separate the recorded states into spin-launch and orbital phases:
spin_phase = states[states[:, 3] == 1]
orbital_phase = states[states[:, 3] == 2]
# Plot the spin-launch phase in red:
if spin_phase.size > 0:
ax.plot(spin_phase[:, 1] / 1000, spin_phase[:, 2] / 1000, 'r-', label="Spin‑Launch Phase")
# Plot the orbital phase in green:
if orbital_phase.size > 0:
ax.plot(orbital_phase[:, 1] / 1000, orbital_phase[:, 2] / 1000, 'g-', label="Orbital Phase")
# Mark the launch start:
ax.plot(initial_position[0] / 1000, initial_position[1] / 1000, 'ko', label="Launch Start")
# Mark the docking event, if it occurred:
if docking_done and docking_event_coords is not None:
ax.plot(docking_event_coords[0] / 1000, docking_event_coords[1] / 1000, 'bo', markersize=8, label="Docking Event")
ax.legend()
st.pyplot(fig)
st.markdown("**Note:** This is a simplified simulation. In a realistic system, the spin‑launch phase, docking maneuver, and orbital insertion would be modeled with far more complexity (including aerodynamic forces, gradual burns, and detailed guidance dynamics).")