Spaces:
Sleeping
Sleeping
Last commit not found
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).") | |