In [1]:
import plotly.express as px
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

Mission Milestones¶

This is the 4th notebook in the series. Earlier parts:

  • ./010-video-export.html - for exporting still frames and timecodes for the videos
  • ./020-video-analysis.html - for analyzing the video frames to match mission time with key events
  • ./030-flight-data.html - for analyzing the flight data

Here we will compare the flight data with the mission timeline

In [2]:
df = pd.read_csv("mission-timeline.csv")
df
Out[2]:
Unnamed: 0 Frame Time (s) Description ∆T (s)
0 0 25 18.145000 First frame showing rocket plume 0.000000
1 1 31 18.245000 Rocket has cleared the viewport 0.100000
2 2 91 19.245000 Bottle re-acquired 1.100000
3 3 176 20.663333 Rocket at apogee, starting to descend 2.518333
4 4 208 21.196667 Rocket clearly descending and leaning right 3.051667
5 5 245 21.813333 Rocket starts executing flip to nose down 3.668333
6 6 265 22.146667 Rocket pointing downwards, precessing around z... 4.001667
7 7 388 24.198333 Possible rocket crash into building 6.053333

We also found that the launch moment according to the onboard clock was 442461 ms.

In [3]:
LAUNCH_TIME = 442461

Let's look how the video compares with the data¶

We can load in the sensor data and plot the key events from the video

In [4]:
baro = pd.read_csv('data/BarometerData-20250405-162645.csv')
baro = baro.iloc[11:]
baro = baro[baro['timestamp'] > 440000]
baro['mission_time'] = (baro.timestamp - LAUNCH_TIME) / 1000
baro = baro.set_index('mission_time')
baro.head()
Out[4]:
time timestamp pressure altitude average_altitude temperature
mission_time
-2.428 16:20:54.830000 440033 102139.593750 0.210183 0.046781 28.620001
-2.175 16:20:55.083000 440286 102143.968750 -0.150846 0.055706 28.629999
-1.923 16:20:55.335000 440538 102141.726562 0.034192 0.055847 28.650000
-1.669 16:20:55.589000 440792 102138.843750 0.272032 0.047770 28.629999
-1.419 16:20:55.839000 441042 102140.531250 0.132747 0.044345 28.639999
In [5]:
imu = pd.read_csv('data/AccelData-20250405-162645.csv')
imu = imu.iloc[1000:]
imu = imu[imu['timestamp'] > 440000]
imu['mission_time'] = (imu.timestamp - LAUNCH_TIME) / 1000
imu = imu.set_index('mission_time')
imu['total_accel'] = np.sqrt(imu['accelX']**2 + imu['accelY']**2 + imu['accelZ']**2)
imu.head()
Out[5]:
time timestamp accelX accelY accelZ gyroX gyroY gyroZ temperature total_accel
mission_time
-2.459 16:20:54.799000 440002 -0.081984 -0.155672 -1.013576 -1.40 -2.24 2.24 42.7500 1.028733
-2.451 16:20:54.807000 440010 -0.083448 -0.093208 -0.195688 -1.19 -2.59 1.82 42.7500 0.232261
-2.443 16:20:54.815000 440018 -0.122976 -0.087352 -1.002840 -1.82 -2.73 -8.12 42.6875 1.014121
-2.435 16:20:54.823000 440026 -0.131272 -0.179584 -0.770064 -2.80 -0.91 -4.20 42.6875 0.801549
-2.420 16:20:54.838000 440041 0.058072 -0.064904 -1.726544 0.49 -1.96 12.88 42.6875 1.728739

Let's create a function to annotate the plot with the mission milestones

In [6]:
def annotate_plot(fig, y):

    for _, row in df.iterrows():
        fig.add_vline(
            x=row['∆T (s)'],
            line_dash="dash",
            line_color="red",
        )
        fig.add_annotation(
            x=row['∆T (s)'],
            y=y,
            text=row['Description'],
            showarrow=False,
            textangle=-90,
            font=dict(color="grey", size=10),
            xanchor="left",
            yanchor="top"
        )

Flight profile¶

Let's first examine how our "video" milestones compare to the real flight data

In [7]:
filtered_data = baro['altitude']

fig = px.scatter(x=filtered_data.index, y=filtered_data.values, 
                 title="Rocket Altitude Measurements",
                 labels={"x": "Time (s)", "y": "Altitude (m)"})

fig.update_traces(mode='lines+markers')

fig.update_layout(height=1000)
fig.update_xaxes(range=[-0.2, 7])
fig.update_yaxes(range=[-1, 100])

annotate_plot(fig, 90)

fig.show()