Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,25 +1,27 @@
|
|
1 |
import streamlit as st
|
2 |
import numpy as np
|
3 |
import matplotlib.pyplot as plt
|
|
|
|
|
4 |
|
5 |
# ===============================
|
6 |
# Streamlit Interface Setup
|
7 |
# ===============================
|
8 |
-
st.title("Spin Launch & Orbital Attachment Simulation")
|
9 |
|
10 |
st.markdown(
|
11 |
"""
|
12 |
This simulation demonstrates a two‑phase launch:
|
13 |
|
14 |
1. **Spin‑Launch Phase:** A payload is accelerated from an elevated platform.
|
15 |
-
2. **Orbital Phase:** At a
|
16 |
instantly setting its velocity to that of a circular orbit.
|
17 |
|
18 |
-
Adjust the parameters
|
19 |
"""
|
20 |
)
|
21 |
|
22 |
-
# Sidebar parameters for simulation
|
23 |
st.sidebar.header("Simulation Parameters")
|
24 |
|
25 |
# Time and integration parameters:
|
@@ -33,7 +35,7 @@ initial_velocity = st.sidebar.number_input("Initial Velocity (m/s)", min_value=1
|
|
33 |
# Docking/Orbital attachment altitude:
|
34 |
docking_altitude_km = st.sidebar.number_input("Docking Altitude (km)", min_value=1, max_value=500, value=100, step=1)
|
35 |
|
36 |
-
# Button to run simulation
|
37 |
run_sim = st.sidebar.button("Run Simulation")
|
38 |
|
39 |
if run_sim:
|
@@ -60,17 +62,18 @@ if run_sim:
|
|
60 |
# Define the Acceleration Function
|
61 |
# ===============================
|
62 |
def acceleration(pos):
|
63 |
-
"""
|
64 |
-
Compute acceleration due to Earth's gravity at position pos (in m).
|
65 |
-
"""
|
66 |
r = np.linalg.norm(pos)
|
67 |
return -mu * pos / r**3
|
68 |
|
69 |
# ===============================
|
70 |
# Run the Simulation Loop
|
71 |
# ===============================
|
72 |
-
|
73 |
-
phase = 1
|
|
|
|
|
|
|
74 |
t = 0.0
|
75 |
pos = initial_position.copy()
|
76 |
vel = initial_velocity_vec.copy()
|
@@ -81,68 +84,84 @@ if run_sim:
|
|
81 |
while t < t_max:
|
82 |
# --- Check for the Docking Event ---
|
83 |
# When the payload's distance from Earth's center exceeds the docking radius
|
84 |
-
# and it
|
85 |
if phase == 1 and not docking_done and np.linalg.norm(pos) >= r_dock and vel.dot(pos) > 0:
|
86 |
docking_done = True
|
87 |
-
phase = 2 #
|
88 |
r_current = np.linalg.norm(pos)
|
89 |
# Compute the circular orbital speed at the current radius:
|
90 |
v_circ = np.sqrt(mu / r_current)
|
91 |
-
# For a prograde orbit, choose a tangential
|
92 |
tangential_dir = np.array([-pos[1], pos[0]]) / r_current
|
93 |
-
vel = v_circ * tangential_dir #
|
94 |
docking_event_time = t
|
95 |
docking_event_coords = pos.copy()
|
96 |
st.write(f"**Docking Event:** t = {t:.1f} s, Altitude = {(r_current - R_E)/1000:.1f} km, Circular Speed = {v_circ:.1f} m/s")
|
97 |
|
98 |
-
# Record current state: (time, x, y, phase)
|
99 |
states.append((t, pos[0], pos[1], phase))
|
100 |
|
101 |
-
# --- Propagate the
|
102 |
a = acceleration(pos)
|
103 |
pos = pos + vel * dt
|
104 |
vel = vel + a * dt
|
105 |
t += dt
|
106 |
|
107 |
-
# Convert states to a NumPy array for easier slicing
|
108 |
states = np.array(states) # columns: time, x, y, phase
|
109 |
|
110 |
# ===============================
|
111 |
-
# Create the
|
112 |
# ===============================
|
113 |
fig, ax = plt.subplots(figsize=(8, 8))
|
114 |
ax.set_aspect('equal')
|
115 |
ax.set_xlabel("x (km)")
|
116 |
ax.set_ylabel("y (km)")
|
117 |
-
ax.set_title("Trajectory: Spin Launch
|
118 |
|
119 |
# Draw Earth as a blue circle (Earth's radius in km)
|
120 |
earth = plt.Circle((0, 0), R_E / 1000, color='blue', alpha=0.3, label="Earth")
|
121 |
ax.add_artist(earth)
|
122 |
|
123 |
-
#
|
124 |
max_extent = (R_E + 300e3) / 1000 # in km
|
125 |
ax.set_xlim(-max_extent, max_extent)
|
126 |
ax.set_ylim(-max_extent, max_extent)
|
127 |
|
128 |
-
#
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
import numpy as np
|
3 |
import matplotlib.pyplot as plt
|
4 |
+
from matplotlib.animation import FuncAnimation
|
5 |
+
import streamlit.components.v1 as components
|
6 |
|
7 |
# ===============================
|
8 |
# Streamlit Interface Setup
|
9 |
# ===============================
|
10 |
+
st.title("Spin Launch & Orbital Attachment Simulation (Animated)")
|
11 |
|
12 |
st.markdown(
|
13 |
"""
|
14 |
This simulation demonstrates a two‑phase launch:
|
15 |
|
16 |
1. **Spin‑Launch Phase:** A payload is accelerated from an elevated platform.
|
17 |
+
2. **Orbital Phase:** At a specified altitude, the payload “docks” with a human‑rated upper stage by
|
18 |
instantly setting its velocity to that of a circular orbit.
|
19 |
|
20 |
+
Adjust the parameters on the sidebar and click **Run Simulation**.
|
21 |
"""
|
22 |
)
|
23 |
|
24 |
+
# Sidebar parameters for simulation:
|
25 |
st.sidebar.header("Simulation Parameters")
|
26 |
|
27 |
# Time and integration parameters:
|
|
|
35 |
# Docking/Orbital attachment altitude:
|
36 |
docking_altitude_km = st.sidebar.number_input("Docking Altitude (km)", min_value=1, max_value=500, value=100, step=1)
|
37 |
|
38 |
+
# Button to run simulation:
|
39 |
run_sim = st.sidebar.button("Run Simulation")
|
40 |
|
41 |
if run_sim:
|
|
|
62 |
# Define the Acceleration Function
|
63 |
# ===============================
|
64 |
def acceleration(pos):
|
65 |
+
"""Compute acceleration due to Earth's gravity at position pos (in m)."""
|
|
|
|
|
66 |
r = np.linalg.norm(pos)
|
67 |
return -mu * pos / r**3
|
68 |
|
69 |
# ===============================
|
70 |
# Run the Simulation Loop
|
71 |
# ===============================
|
72 |
+
# We'll record (time, x, y, phase)
|
73 |
+
# phase = 1: spin‑launch phase
|
74 |
+
# phase = 2: orbital phase (after docking)
|
75 |
+
states = []
|
76 |
+
phase = 1
|
77 |
t = 0.0
|
78 |
pos = initial_position.copy()
|
79 |
vel = initial_velocity_vec.copy()
|
|
|
84 |
while t < t_max:
|
85 |
# --- Check for the Docking Event ---
|
86 |
# When the payload's distance from Earth's center exceeds the docking radius
|
87 |
+
# and it's still moving outward, trigger the docking event.
|
88 |
if phase == 1 and not docking_done and np.linalg.norm(pos) >= r_dock and vel.dot(pos) > 0:
|
89 |
docking_done = True
|
90 |
+
phase = 2 # switch to orbital phase
|
91 |
r_current = np.linalg.norm(pos)
|
92 |
# Compute the circular orbital speed at the current radius:
|
93 |
v_circ = np.sqrt(mu / r_current)
|
94 |
+
# For a prograde orbit, choose a tangential (perpendicular) direction:
|
95 |
tangential_dir = np.array([-pos[1], pos[0]]) / r_current
|
96 |
+
vel = v_circ * tangential_dir # instantaneous burn to circular orbit
|
97 |
docking_event_time = t
|
98 |
docking_event_coords = pos.copy()
|
99 |
st.write(f"**Docking Event:** t = {t:.1f} s, Altitude = {(r_current - R_E)/1000:.1f} km, Circular Speed = {v_circ:.1f} m/s")
|
100 |
|
101 |
+
# Record the current state: (time, x, y, phase)
|
102 |
states.append((t, pos[0], pos[1], phase))
|
103 |
|
104 |
+
# --- Propagate the State (Euler Integration) ---
|
105 |
a = acceleration(pos)
|
106 |
pos = pos + vel * dt
|
107 |
vel = vel + a * dt
|
108 |
t += dt
|
109 |
|
110 |
+
# Convert the recorded states to a NumPy array for easier slicing.
|
111 |
states = np.array(states) # columns: time, x, y, phase
|
112 |
|
113 |
# ===============================
|
114 |
+
# Create the Animation Using Matplotlib
|
115 |
# ===============================
|
116 |
fig, ax = plt.subplots(figsize=(8, 8))
|
117 |
ax.set_aspect('equal')
|
118 |
ax.set_xlabel("x (km)")
|
119 |
ax.set_ylabel("y (km)")
|
120 |
+
ax.set_title("Trajectory: Spin Launch & Orbital Attachment")
|
121 |
|
122 |
# Draw Earth as a blue circle (Earth's radius in km)
|
123 |
earth = plt.Circle((0, 0), R_E / 1000, color='blue', alpha=0.3, label="Earth")
|
124 |
ax.add_artist(earth)
|
125 |
|
126 |
+
# Set plot limits to show the trajectory (e.g., up to Earth's radius + 300 km)
|
127 |
max_extent = (R_E + 300e3) / 1000 # in km
|
128 |
ax.set_xlim(-max_extent, max_extent)
|
129 |
ax.set_ylim(-max_extent, max_extent)
|
130 |
|
131 |
+
# Initialize the trajectory line and payload marker for animation:
|
132 |
+
trajectory_line, = ax.plot([], [], 'r-', lw=2, label="Trajectory")
|
133 |
+
payload_marker, = ax.plot([], [], 'ko', markersize=5, label="Payload")
|
134 |
+
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes, fontsize=10)
|
135 |
+
|
136 |
+
def init():
|
137 |
+
trajectory_line.set_data([], [])
|
138 |
+
payload_marker.set_data([], [])
|
139 |
+
time_text.set_text('')
|
140 |
+
return trajectory_line, payload_marker, time_text
|
141 |
+
|
142 |
+
def update(frame):
|
143 |
+
# Update the trajectory with all positions up to the current frame.
|
144 |
+
t_val = states[frame, 0]
|
145 |
+
x_vals = states[:frame+1, 1] / 1000 # convert m to km
|
146 |
+
y_vals = states[:frame+1, 2] / 1000 # convert m to km
|
147 |
+
trajectory_line.set_data(x_vals, y_vals)
|
148 |
+
payload_marker.set_data(states[frame, 1] / 1000, states[frame, 2] / 1000)
|
149 |
+
time_text.set_text(f"Time: {t_val:.1f} s")
|
150 |
+
return trajectory_line, payload_marker, time_text
|
151 |
+
|
152 |
+
# Create the animation. The interval (in ms) can be adjusted for speed.
|
153 |
+
anim = FuncAnimation(fig, update, frames=len(states), init_func=init, interval=20, blit=True)
|
154 |
+
|
155 |
+
# Convert the animation to an HTML5 video.
|
156 |
+
video_html = anim.to_html5_video()
|
157 |
+
|
158 |
+
st.markdown("### Simulation Animation")
|
159 |
+
components.html(video_html, height=500)
|
160 |
+
|
161 |
+
st.markdown(
|
162 |
+
"""
|
163 |
+
**Note:** This is a highly simplified simulation. In a real-world scenario, the spin‑launch, docking,
|
164 |
+
and orbital insertion phases would involve much more complex physics including aerodynamics, non‑instantaneous burns,
|
165 |
+
and detailed guidance and control.
|
166 |
+
"""
|
167 |
+
)
|