In [2]:
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import os

Launch Video Analysis¶

Since the IMU saturated during launch, let's see if we can get more information from the video. We have high-speed video from the launch, which we will use to estimate the apogee and time of flight of the rocket.

In [3]:
# Read the timestamps of each frame from the previously stored export file

timestamps = pd.read_csv("cansat-slowmo-full/snippet.csv")
timestamps
Out[3]:
frame_index timestamp
0 1 28.560000
1 2 28.564167
2 3 28.576667
3 4 28.580833
4 5 28.585000
... ... ...
301 302 30.397917
302 303 30.402083
303 304 30.414583
304 305 30.418750
305 306 30.443750

306 rows × 2 columns

In [4]:
# The first sign of movement was in frame 268 - so we use the mean of timestamp from frame 267 and 268 as the launch time
launch_time = (timestamps.loc[timestamps['frame_index'] == 267, 'timestamp'].values[0] + 
               timestamps.loc[timestamps['frame_index'] == 268, 'timestamp'].values[0]) / 2
timestamps['mission_time'] = timestamps['timestamp'] - launch_time
timestamps['dt'] = timestamps['mission_time'].diff().fillna(0)
timestamps
Out[4]:
frame_index timestamp mission_time dt
0 1 28.560000 -1.631667 0.000000
1 2 28.564167 -1.627500 0.004167
2 3 28.576667 -1.615000 0.012500
3 4 28.580833 -1.610834 0.004166
4 5 28.585000 -1.606667 0.004167
... ... ... ... ...
301 302 30.397917 0.206250 0.004167
302 303 30.402083 0.210416 0.004166
303 304 30.414583 0.222916 0.012500
304 305 30.418750 0.227083 0.004167
305 306 30.443750 0.252083 0.025000

306 rows × 4 columns

In [5]:
ts_records = timestamps.set_index('frame_index').to_dict(orient="records")
ts_records
Out[5]:
[{'timestamp': 28.56, 'mission_time': -1.6316670000000038, 'dt': 0.0},
 {'timestamp': 28.564167,
  'mission_time': -1.6275000000000013,
  'dt': 0.004167000000002474},
 {'timestamp': 28.576667,
  'mission_time': -1.615000000000002,
  'dt': 0.01249999999999929},
 {'timestamp': 28.580833,
  'mission_time': -1.610834000000004,
  'dt': 0.004165999999997894},
 {'timestamp': 28.585,
  'mission_time': -1.6066670000000016,
  'dt': 0.004167000000002474},
 {'timestamp': 28.589167,
  'mission_time': -1.6025000000000027,
  'dt': 0.004166999999998922},
 {'timestamp': 28.593333,
  'mission_time': -1.5983340000000013,
  'dt': 0.004166000000001446},
 {'timestamp': 28.5975,
  'mission_time': -1.5941670000000023,
  'dt': 0.004166999999998922},
 {'timestamp': 28.601667,
  'mission_time': -1.5900000000000034,
  'dt': 0.004166999999998922},
 {'timestamp': 28.605833,
  'mission_time': -1.585834000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 28.61,
  'mission_time': -1.581667000000003,
  'dt': 0.004166999999998922},
 {'timestamp': 28.614167,
  'mission_time': -1.5775000000000041,
  'dt': 0.004166999999998922},
 {'timestamp': 28.618333,
  'mission_time': -1.5733340000000027,
  'dt': 0.004166000000001446},
 {'timestamp': 28.630833,
  'mission_time': -1.5608340000000034,
  'dt': 0.01249999999999929},
 {'timestamp': 28.635,
  'mission_time': -1.556667000000001,
  'dt': 0.004167000000002474},
 {'timestamp': 28.6475,
  'mission_time': -1.5441670000000016,
  'dt': 0.01249999999999929},
 {'timestamp': 28.651667,
  'mission_time': -1.5400000000000027,
  'dt': 0.004166999999998922},
 {'timestamp': 28.664167,
  'mission_time': -1.5275000000000034,
  'dt': 0.01249999999999929},
 {'timestamp': 28.668333,
  'mission_time': -1.523334000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 28.6725,
  'mission_time': -1.519167000000003,
  'dt': 0.004166999999998922},
 {'timestamp': 28.685,
  'mission_time': -1.5066670000000038,
  'dt': 0.01249999999999929},
 {'timestamp': 28.689167,
  'mission_time': -1.5025000000000013,
  'dt': 0.004167000000002474},
 {'timestamp': 28.701667,
  'mission_time': -1.490000000000002,
  'dt': 0.01249999999999929},
 {'timestamp': 28.705833,
  'mission_time': -1.485834000000004,
  'dt': 0.004165999999997894},
 {'timestamp': 28.71,
  'mission_time': -1.4816670000000016,
  'dt': 0.004167000000002474},
 {'timestamp': 28.714167,
  'mission_time': -1.4775000000000027,
  'dt': 0.004166999999998922},
 {'timestamp': 28.718333,
  'mission_time': -1.4733340000000013,
  'dt': 0.004166000000001446},
 {'timestamp': 28.7225,
  'mission_time': -1.4691670000000023,
  'dt': 0.004166999999998922},
 {'timestamp': 28.726667,
  'mission_time': -1.4650000000000034,
  'dt': 0.004166999999998922},
 {'timestamp': 28.730833,
  'mission_time': -1.460834000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 28.735,
  'mission_time': -1.456667000000003,
  'dt': 0.004166999999998922},
 {'timestamp': 28.739167,
  'mission_time': -1.4525000000000041,
  'dt': 0.004166999999998922},
 {'timestamp': 28.743333,
  'mission_time': -1.4483340000000027,
  'dt': 0.004166000000001446},
 {'timestamp': 28.7475,
  'mission_time': -1.4441670000000038,
  'dt': 0.004166999999998922},
 {'timestamp': 28.76,
  'mission_time': -1.431667000000001,
  'dt': 0.012500000000002842},
 {'timestamp': 28.764167,
  'mission_time': -1.427500000000002,
  'dt': 0.004166999999998922},
 {'timestamp': 28.776667,
  'mission_time': -1.4150000000000027,
  'dt': 0.01249999999999929},
 {'timestamp': 28.780833,
  'mission_time': -1.4108340000000013,
  'dt': 0.004166000000001446},
 {'timestamp': 28.785,
  'mission_time': -1.4066670000000023,
  'dt': 0.004166999999998922},
 {'timestamp': 28.801667,
  'mission_time': -1.3900000000000041,
  'dt': 0.01666699999999821},
 {'timestamp': 28.80625,
  'mission_time': -1.385417000000004,
  'dt': 0.004583000000000226},
 {'timestamp': 28.810417,
  'mission_time': -1.3812500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 28.822917,
  'mission_time': -1.3687500000000021,
  'dt': 0.01249999999999929},
 {'timestamp': 28.827083,
  'mission_time': -1.3645840000000042,
  'dt': 0.004165999999997894},
 {'timestamp': 28.830833,
  'mission_time': -1.360834000000004,
  'dt': 0.003750000000000142},
 {'timestamp': 28.835,
  'mission_time': -1.3566670000000016,
  'dt': 0.004167000000002474},
 {'timestamp': 28.839167,
  'mission_time': -1.3525000000000027,
  'dt': 0.004166999999998922},
 {'timestamp': 28.843333,
  'mission_time': -1.3483340000000013,
  'dt': 0.004166000000001446},
 {'timestamp': 28.8475,
  'mission_time': -1.3441670000000023,
  'dt': 0.004166999999998922},
 {'timestamp': 28.851667,
  'mission_time': -1.3400000000000034,
  'dt': 0.004166999999998922},
 {'timestamp': 28.855833,
  'mission_time': -1.335834000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 28.86,
  'mission_time': -1.331667000000003,
  'dt': 0.004166999999998922},
 {'timestamp': 28.864583,
  'mission_time': -1.3270840000000028,
  'dt': 0.004583000000000226},
 {'timestamp': 28.86875,
  'mission_time': -1.322917000000004,
  'dt': 0.004166999999998922},
 {'timestamp': 28.88125,
  'mission_time': -1.310417000000001,
  'dt': 0.012500000000002842},
 {'timestamp': 28.885,
  'mission_time': -1.306667000000001,
  'dt': 0.003750000000000142},
 {'timestamp': 28.8975,
  'mission_time': -1.2941670000000016,
  'dt': 0.01249999999999929},
 {'timestamp': 28.901667,
  'mission_time': -1.2900000000000027,
  'dt': 0.004166999999998922},
 {'timestamp': 28.905833,
  'mission_time': -1.2858340000000013,
  'dt': 0.004166000000001446},
 {'timestamp': 28.918333,
  'mission_time': -1.273334000000002,
  'dt': 0.01249999999999929},
 {'timestamp': 28.9225,
  'mission_time': -1.269167000000003,
  'dt': 0.004166999999998922},
 {'timestamp': 28.935417,
  'mission_time': -1.2562500000000014,
  'dt': 0.012917000000001622},
 {'timestamp': 28.939583,
  'mission_time': -1.2520840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 28.94375,
  'mission_time': -1.247917000000001,
  'dt': 0.004167000000002474},
 {'timestamp': 28.95625,
  'mission_time': -1.2354170000000018,
  'dt': 0.01249999999999929},
 {'timestamp': 28.960417,
  'mission_time': -1.2312500000000028,
  'dt': 0.004166999999998922},
 {'timestamp': 28.964583,
  'mission_time': -1.2270840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 28.96875,
  'mission_time': -1.2229170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 28.972917,
  'mission_time': -1.2187500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 28.977083,
  'mission_time': -1.214584000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 28.98125,
  'mission_time': -1.2104170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 28.985417,
  'mission_time': -1.2062500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 28.989583,
  'mission_time': -1.2020840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 28.99375,
  'mission_time': -1.197917000000004,
  'dt': 0.004166999999998922},
 {'timestamp': 28.997917,
  'mission_time': -1.1937500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.002083,
  'mission_time': -1.1895840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.014583,
  'mission_time': -1.1770840000000042,
  'dt': 0.01249999999999929},
 {'timestamp': 29.01875,
  'mission_time': -1.1729170000000018,
  'dt': 0.004167000000002474},
 {'timestamp': 29.03125,
  'mission_time': -1.1604170000000025,
  'dt': 0.01249999999999929},
 {'timestamp': 29.035417,
  'mission_time': -1.1562500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.039583,
  'mission_time': -1.152084000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 29.052083,
  'mission_time': -1.1395840000000028,
  'dt': 0.01249999999999929},
 {'timestamp': 29.05625,
  'mission_time': -1.135417000000004,
  'dt': 0.004166999999998922},
 {'timestamp': 29.06875,
  'mission_time': -1.122917000000001,
  'dt': 0.012500000000002842},
 {'timestamp': 29.072917,
  'mission_time': -1.1187500000000021,
  'dt': 0.004166999999998922},
 {'timestamp': 29.077083,
  'mission_time': -1.1145840000000042,
  'dt': 0.004165999999997894},
 {'timestamp': 29.089583,
  'mission_time': -1.1020840000000014,
  'dt': 0.012500000000002842},
 {'timestamp': 29.09375,
  'mission_time': -1.0979170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.097917,
  'mission_time': -1.0937500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.102083,
  'mission_time': -1.089584000000002,
  'dt': 0.004166000000001446},
 {'timestamp': 29.10625,
  'mission_time': -1.0854170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.110417,
  'mission_time': -1.0812500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.114583,
  'mission_time': -1.0770840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.11875,
  'mission_time': -1.072917000000004,
  'dt': 0.004166999999998922},
 {'timestamp': 29.122917,
  'mission_time': -1.0687500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.127083,
  'mission_time': -1.0645840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.139583,
  'mission_time': -1.0520840000000042,
  'dt': 0.01249999999999929},
 {'timestamp': 29.14375,
  'mission_time': -1.0479170000000018,
  'dt': 0.004167000000002474},
 {'timestamp': 29.15625,
  'mission_time': -1.0354170000000025,
  'dt': 0.01249999999999929},
 {'timestamp': 29.160417,
  'mission_time': -1.0312500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.172917,
  'mission_time': -1.0187500000000007,
  'dt': 0.012500000000002842},
 {'timestamp': 29.177083,
  'mission_time': -1.0145840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.18125,
  'mission_time': -1.010417000000004,
  'dt': 0.004166999999998922},
 {'timestamp': 29.19375,
  'mission_time': -0.997917000000001,
  'dt': 0.012500000000002842},
 {'timestamp': 29.197917,
  'mission_time': -0.9937500000000021,
  'dt': 0.004166999999998922},
 {'timestamp': 29.210417,
  'mission_time': -0.9812500000000028,
  'dt': 0.01249999999999929},
 {'timestamp': 29.214583,
  'mission_time': -0.9770840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.21875,
  'mission_time': -0.9729170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.222917,
  'mission_time': -0.9687500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.227083,
  'mission_time': -0.9645840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.23125,
  'mission_time': -0.9604170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.235417,
  'mission_time': -0.9562500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.239583,
  'mission_time': -0.9520840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.24375,
  'mission_time': -0.9479170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.247917,
  'mission_time': -0.9437500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.252083,
  'mission_time': -0.9395840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.264583,
  'mission_time': -0.9270840000000042,
  'dt': 0.01249999999999929},
 {'timestamp': 29.26875,
  'mission_time': -0.9229170000000018,
  'dt': 0.004167000000002474},
 {'timestamp': 29.272917,
  'mission_time': -0.9187500000000028,
  'dt': 0.004166999999998922},
 {'timestamp': 29.285417,
  'mission_time': -0.9062500000000036,
  'dt': 0.01249999999999929},
 {'timestamp': 29.289583,
  'mission_time': -0.9020840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.302083,
  'mission_time': -0.8895840000000028,
  'dt': 0.01249999999999929},
 {'timestamp': 29.30625,
  'mission_time': -0.8854170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.31875,
  'mission_time': -0.872917000000001,
  'dt': 0.012500000000002842},
 {'timestamp': 29.322917,
  'mission_time': -0.8687500000000021,
  'dt': 0.004166999999998922},
 {'timestamp': 29.327083,
  'mission_time': -0.8645840000000042,
  'dt': 0.004165999999997894},
 {'timestamp': 29.339583,
  'mission_time': -0.8520840000000014,
  'dt': 0.012500000000002842},
 {'timestamp': 29.34375,
  'mission_time': -0.8479170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.347917,
  'mission_time': -0.8437500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.352083,
  'mission_time': -0.8395840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.35625,
  'mission_time': -0.8354170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.360417,
  'mission_time': -0.8312500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.364583,
  'mission_time': -0.8270840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.36875,
  'mission_time': -0.8229170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.372917,
  'mission_time': -0.8187500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.377083,
  'mission_time': -0.8145840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.38125,
  'mission_time': -0.810417000000001,
  'dt': 0.004167000000002474},
 {'timestamp': 29.385417,
  'mission_time': -0.8062500000000021,
  'dt': 0.004166999999998922},
 {'timestamp': 29.397917,
  'mission_time': -0.7937500000000028,
  'dt': 0.01249999999999929},
 {'timestamp': 29.402083,
  'mission_time': -0.7895840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.414583,
  'mission_time': -0.7770840000000021,
  'dt': 0.01249999999999929},
 {'timestamp': 29.41875,
  'mission_time': -0.7729170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.422917,
  'mission_time': -0.7687500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.435417,
  'mission_time': -0.7562500000000014,
  'dt': 0.01249999999999929},
 {'timestamp': 29.439583,
  'mission_time': -0.7520840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.452083,
  'mission_time': -0.7395840000000042,
  'dt': 0.01249999999999929},
 {'timestamp': 29.45625,
  'mission_time': -0.7354170000000018,
  'dt': 0.004167000000002474},
 {'timestamp': 29.460417,
  'mission_time': -0.7312500000000028,
  'dt': 0.004166999999998922},
 {'timestamp': 29.464583,
  'mission_time': -0.7270840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.46875,
  'mission_time': -0.7229170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.472917,
  'mission_time': -0.7187500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.477083,
  'mission_time': -0.7145840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.48125,
  'mission_time': -0.7104170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.485417,
  'mission_time': -0.7062500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.489583,
  'mission_time': -0.7020840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.49375,
  'mission_time': -0.6979170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.50625,
  'mission_time': -0.685417000000001,
  'dt': 0.012500000000002842},
 {'timestamp': 29.510417,
  'mission_time': -0.6812500000000021,
  'dt': 0.004166999999998922},
 {'timestamp': 29.522917,
  'mission_time': -0.6687500000000028,
  'dt': 0.01249999999999929},
 {'timestamp': 29.527083,
  'mission_time': -0.6645840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.53125,
  'mission_time': -0.6604170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.54375,
  'mission_time': -0.6479170000000032,
  'dt': 0.01249999999999929},
 {'timestamp': 29.547917,
  'mission_time': -0.6437500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.560417,
  'mission_time': -0.6312500000000014,
  'dt': 0.01249999999999929},
 {'timestamp': 29.564583,
  'mission_time': -0.6270840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.56875,
  'mission_time': -0.622917000000001,
  'dt': 0.004167000000002474},
 {'timestamp': 29.585417,
  'mission_time': -0.6062500000000028,
  'dt': 0.01666699999999821},
 {'timestamp': 29.589583,
  'mission_time': -0.6020840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.59375,
  'mission_time': -0.5979170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.597917,
  'mission_time': -0.5937500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.602083,
  'mission_time': -0.5895840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.60625,
  'mission_time': -0.5854170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.610417,
  'mission_time': -0.5812500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.614583,
  'mission_time': -0.5770840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.61875,
  'mission_time': -0.5729170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.622917,
  'mission_time': -0.5687500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.627083,
  'mission_time': -0.5645840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.639583,
  'mission_time': -0.5520840000000042,
  'dt': 0.01249999999999929},
 {'timestamp': 29.64375,
  'mission_time': -0.5479170000000018,
  'dt': 0.004167000000002474},
 {'timestamp': 29.65625,
  'mission_time': -0.5354170000000025,
  'dt': 0.01249999999999929},
 {'timestamp': 29.660417,
  'mission_time': -0.5312500000000036,
  'dt': 0.004166999999998922},
 {'timestamp': 29.664583,
  'mission_time': -0.5270840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.677083,
  'mission_time': -0.5145840000000028,
  'dt': 0.01249999999999929},
 {'timestamp': 29.68125,
  'mission_time': -0.5104170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.69375,
  'mission_time': -0.49791700000000105,
  'dt': 0.012500000000002842},
 {'timestamp': 29.697917,
  'mission_time': -0.49375000000000213,
  'dt': 0.004166999999998922},
 {'timestamp': 29.702083,
  'mission_time': -0.48958400000000424,
  'dt': 0.004165999999997894},
 {'timestamp': 29.71875,
  'mission_time': -0.4729170000000025,
  'dt': 0.016667000000001764},
 {'timestamp': 29.722917,
  'mission_time': -0.46875000000000355,
  'dt': 0.004166999999998922},
 {'timestamp': 29.727083,
  'mission_time': -0.4645840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.73125,
  'mission_time': -0.4604170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.735417,
  'mission_time': -0.4562500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.739583,
  'mission_time': -0.4520840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.74375,
  'mission_time': -0.4479170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.747917,
  'mission_time': -0.4437500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.752083,
  'mission_time': -0.4395840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.75625,
  'mission_time': -0.43541700000000105,
  'dt': 0.004167000000002474},
 {'timestamp': 29.760417,
  'mission_time': -0.43125000000000213,
  'dt': 0.004166999999998922},
 {'timestamp': 29.772917,
  'mission_time': -0.41875000000000284,
  'dt': 0.01249999999999929},
 {'timestamp': 29.777083,
  'mission_time': -0.4145840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.78125,
  'mission_time': -0.4104170000000025,
  'dt': 0.004166999999998922},
 {'timestamp': 29.79375,
  'mission_time': -0.3979170000000032,
  'dt': 0.01249999999999929},
 {'timestamp': 29.797917,
  'mission_time': -0.3937500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.810417,
  'mission_time': -0.3812500000000014,
  'dt': 0.01249999999999929},
 {'timestamp': 29.814583,
  'mission_time': -0.3770840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.81875,
  'mission_time': -0.37291700000000105,
  'dt': 0.004167000000002474},
 {'timestamp': 29.83125,
  'mission_time': -0.36041700000000176,
  'dt': 0.01249999999999929},
 {'timestamp': 29.835417,
  'mission_time': -0.35625000000000284,
  'dt': 0.004166999999998922},
 {'timestamp': 29.847917,
  'mission_time': -0.34375000000000355,
  'dt': 0.01249999999999929},
 {'timestamp': 29.852083,
  'mission_time': -0.3395840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 29.85625,
  'mission_time': -0.3354170000000032,
  'dt': 0.004166999999998922},
 {'timestamp': 29.860417,
  'mission_time': -0.3312500000000007,
  'dt': 0.004167000000002474},
 {'timestamp': 29.864583,
  'mission_time': -0.3270840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.86875,
  'mission_time': -0.3229170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.872917,
  'mission_time': -0.3187500000000014,
  'dt': 0.004167000000002474},
 {'timestamp': 29.877083,
  'mission_time': -0.3145840000000035,
  'dt': 0.004165999999997894},
 {'timestamp': 29.88125,
  'mission_time': -0.31041700000000105,
  'dt': 0.004167000000002474},
 {'timestamp': 29.885417,
  'mission_time': -0.30625000000000213,
  'dt': 0.004166999999998922},
 {'timestamp': 29.889583,
  'mission_time': -0.30208400000000424,
  'dt': 0.004165999999997894},
 {'timestamp': 29.89375,
  'mission_time': -0.29791700000000176,
  'dt': 0.004167000000002474},
 {'timestamp': 29.90625,
  'mission_time': -0.2854170000000025,
  'dt': 0.01249999999999929},
 {'timestamp': 29.910417,
  'mission_time': -0.28125000000000355,
  'dt': 0.004166999999998922},
 {'timestamp': 29.922917,
  'mission_time': -0.2687500000000007,
  'dt': 0.012500000000002842},
 {'timestamp': 29.927083,
  'mission_time': -0.2645840000000028,
  'dt': 0.004165999999997894},
 {'timestamp': 29.93125,
  'mission_time': -0.2604170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.94375,
  'mission_time': -0.24791700000000105,
  'dt': 0.012500000000002842},
 {'timestamp': 29.947917,
  'mission_time': -0.24375000000000213,
  'dt': 0.004166999999998922},
 {'timestamp': 29.960417,
  'mission_time': -0.23125000000000284,
  'dt': 0.01249999999999929},
 {'timestamp': 29.964583,
  'mission_time': -0.2270840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 29.96875,
  'mission_time': -0.22291700000000247,
  'dt': 0.004166999999998922},
 {'timestamp': 29.972917,
  'mission_time': -0.21875000000000355,
  'dt': 0.004166999999998922},
 {'timestamp': 29.985417,
  'mission_time': -0.2062500000000007,
  'dt': 0.012500000000002842},
 {'timestamp': 29.989583,
  'mission_time': -0.20208400000000282,
  'dt': 0.004165999999997894},
 {'timestamp': 29.99375,
  'mission_time': -0.1979170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 29.997917,
  'mission_time': -0.19375000000000142,
  'dt': 0.004167000000002474},
 {'timestamp': 30.002083,
  'mission_time': -0.18958400000000353,
  'dt': 0.004165999999997894},
 {'timestamp': 30.00625,
  'mission_time': -0.18541700000000105,
  'dt': 0.004167000000002474},
 {'timestamp': 30.010417,
  'mission_time': -0.18125000000000213,
  'dt': 0.004166999999998922},
 {'timestamp': 30.014583,
  'mission_time': -0.17708400000000424,
  'dt': 0.004165999999997894},
 {'timestamp': 30.01875,
  'mission_time': -0.17291700000000176,
  'dt': 0.004167000000002474},
 {'timestamp': 30.022917,
  'mission_time': -0.16875000000000284,
  'dt': 0.004166999999998922},
 {'timestamp': 30.035417,
  'mission_time': -0.15625000000000355,
  'dt': 0.01249999999999929},
 {'timestamp': 30.039583,
  'mission_time': -0.1520840000000021,
  'dt': 0.004166000000001446},
 {'timestamp': 30.04375,
  'mission_time': -0.14791700000000318,
  'dt': 0.004166999999998922},
 {'timestamp': 30.060417,
  'mission_time': -0.13125000000000142,
  'dt': 0.016667000000001764},
 {'timestamp': 30.064583,
  'mission_time': -0.12708400000000353,
  'dt': 0.004165999999997894},
 {'timestamp': 30.06875,
  'mission_time': -0.12291700000000105,
  'dt': 0.004167000000002474},
 {'timestamp': 30.08125,
  'mission_time': -0.11041700000000176,
  'dt': 0.01249999999999929},
 {'timestamp': 30.085417,
  'mission_time': -0.10625000000000284,
  'dt': 0.004166999999998922},
 {'timestamp': 30.089583,
  'mission_time': -0.1020840000000014,
  'dt': 0.004166000000001446},
 {'timestamp': 30.102083,
  'mission_time': -0.0895840000000021,
  'dt': 0.01249999999999929},
 {'timestamp': 30.10625,
  'mission_time': -0.08541700000000318,
  'dt': 0.004166999999998922},
 {'timestamp': 30.110417,
  'mission_time': -0.08125000000000071,
  'dt': 0.004167000000002474},
 {'timestamp': 30.114583,
  'mission_time': -0.07708400000000282,
  'dt': 0.004165999999997894},
 {'timestamp': 30.11875,
  'mission_time': -0.0729170000000039,
  'dt': 0.004166999999998922},
 {'timestamp': 30.122917,
  'mission_time': -0.06875000000000142,
  'dt': 0.004167000000002474},
 {'timestamp': 30.127083,
  'mission_time': -0.06458400000000353,
  'dt': 0.004165999999997894},
 {'timestamp': 30.13125,
  'mission_time': -0.06041700000000105,
  'dt': 0.004167000000002474},
 {'timestamp': 30.135417,
  'mission_time': -0.05625000000000213,
  'dt': 0.004166999999998922},
 {'timestamp': 30.139583,
  'mission_time': -0.05208400000000424,
  'dt': 0.004165999999997894},
 {'timestamp': 30.14375,
  'mission_time': -0.047917000000001764,
  'dt': 0.004167000000002474},
 {'timestamp': 30.147917,
  'mission_time': -0.04375000000000284,
  'dt': 0.004166999999998922},
 {'timestamp': 30.160417,
  'mission_time': -0.03125000000000355,
  'dt': 0.01249999999999929},
 {'timestamp': 30.164583,
  'mission_time': -0.027084000000002106,
  'dt': 0.004166000000001446},
 {'timestamp': 30.16875,
  'mission_time': -0.022917000000003185,
  'dt': 0.004166999999998922},
 {'timestamp': 30.18125,
  'mission_time': -0.010417000000003895,
  'dt': 0.01249999999999929},
 {'timestamp': 30.185417,
  'mission_time': -0.006250000000001421,
  'dt': 0.004167000000002474},
 {'timestamp': 30.197917,
  'mission_time': 0.006249999999997868,
  'dt': 0.01249999999999929},
 {'timestamp': 30.202083,
  'mission_time': 0.010415999999995762,
  'dt': 0.004165999999997894},
 {'timestamp': 30.214583,
  'mission_time': 0.022915999999998604,
  'dt': 0.012500000000002842},
 {'timestamp': 30.21875,
  'mission_time': 0.027082999999997526,
  'dt': 0.004166999999998922},
 {'timestamp': 30.222917,
  'mission_time': 0.031249999999996447,
  'dt': 0.004166999999998922},
 {'timestamp': 30.235417,
  'mission_time': 0.04374999999999929,
  'dt': 0.012500000000002842},
 {'timestamp': 30.239583,
  'mission_time': 0.04791599999999718,
  'dt': 0.004165999999997894},
 {'timestamp': 30.24375,
  'mission_time': 0.052082999999996105,
  'dt': 0.004166999999998922},
 {'timestamp': 30.247917,
  'mission_time': 0.05624999999999858,
  'dt': 0.004167000000002474},
 {'timestamp': 30.252083,
  'mission_time': 0.06041599999999647,
  'dt': 0.004165999999997894},
 {'timestamp': 30.25625,
  'mission_time': 0.06458299999999895,
  'dt': 0.004167000000002474},
 {'timestamp': 30.260417,
  'mission_time': 0.06874999999999787,
  'dt': 0.004166999999998922},
 {'timestamp': 30.264583,
  'mission_time': 0.07291599999999576,
  'dt': 0.004165999999997894},
 {'timestamp': 30.26875,
  'mission_time': 0.07708299999999824,
  'dt': 0.004167000000002474},
 {'timestamp': 30.272917,
  'mission_time': 0.08124999999999716,
  'dt': 0.004166999999998922},
 {'timestamp': 30.277083,
  'mission_time': 0.0854159999999986,
  'dt': 0.004166000000001446},
 {'timestamp': 30.28125,
  'mission_time': 0.08958299999999753,
  'dt': 0.004166999999998922},
 {'timestamp': 30.29375,
  'mission_time': 0.10208299999999682,
  'dt': 0.01249999999999929},
 {'timestamp': 30.297917,
  'mission_time': 0.10624999999999929,
  'dt': 0.004167000000002474},
 {'timestamp': 30.302083,
  'mission_time': 0.11041599999999718,
  'dt': 0.004165999999997894},
 {'timestamp': 30.314583,
  'mission_time': 0.12291599999999647,
  'dt': 0.01249999999999929},
 {'timestamp': 30.31875,
  'mission_time': 0.12708299999999895,
  'dt': 0.004167000000002474},
 {'timestamp': 30.33125,
  'mission_time': 0.13958299999999824,
  'dt': 0.01249999999999929},
 {'timestamp': 30.335417,
  'mission_time': 0.14374999999999716,
  'dt': 0.004166999999998922},
 {'timestamp': 30.339583,
  'mission_time': 0.1479159999999986,
  'dt': 0.004166000000001446},
 {'timestamp': 30.352083,
  'mission_time': 0.1604159999999979,
  'dt': 0.01249999999999929},
 {'timestamp': 30.35625,
  'mission_time': 0.16458299999999682,
  'dt': 0.004166999999998922},
 {'timestamp': 30.36875,
  'mission_time': 0.1770829999999961,
  'dt': 0.01249999999999929},
 {'timestamp': 30.372917,
  'mission_time': 0.18124999999999858,
  'dt': 0.004167000000002474},
 {'timestamp': 30.377083,
  'mission_time': 0.18541599999999647,
  'dt': 0.004165999999997894},
 {'timestamp': 30.38125,
  'mission_time': 0.18958299999999895,
  'dt': 0.004167000000002474},
 {'timestamp': 30.385417,
  'mission_time': 0.19374999999999787,
  'dt': 0.004166999999998922},
 {'timestamp': 30.389583,
  'mission_time': 0.19791599999999576,
  'dt': 0.004165999999997894},
 {'timestamp': 30.39375,
  'mission_time': 0.20208299999999824,
  'dt': 0.004167000000002474},
 {'timestamp': 30.397917,
  'mission_time': 0.20624999999999716,
  'dt': 0.004166999999998922},
 {'timestamp': 30.402083,
  'mission_time': 0.2104159999999986,
  'dt': 0.004166000000001446},
 {'timestamp': 30.414583,
  'mission_time': 0.2229159999999979,
  'dt': 0.01249999999999929},
 {'timestamp': 30.41875,
  'mission_time': 0.22708299999999682,
  'dt': 0.004166999999998922},
 {'timestamp': 30.44375,
  'mission_time': 0.25208299999999895,
  'dt': 0.02500000000000213}]
In [6]:
start_image = 268
end_image = 288

images = [
    Image.open(f'cansat-slowmo-full/frame_{i:04d}.png')
    for i in range(start_image, end_image+1)
] 

cropped_images = []
for i in range(len(images)):
    row = ts_records[start_image + i - 1]
    ts = row['mission_time']
    left = 850 + ts * 350
    right = left + 100
    image = images[i]
    image = image.crop((left, 0, right, image.height))
    cropped_images.append(dict(image=image, row=row, left=left, right=right, frame_index=start_image + i - 1))

scaling = 20_000
num_images = len(cropped_images)
max_time = timestamps[timestamps['frame_index'] <= end_image]['mission_time'].max()
total_width = scaling * max_time
total_height = cropped_images[0]['image'].height

combined_image = Image.new('RGB', (int(total_width), total_height))

for i, img in enumerate(cropped_images):
    row = img['row']
    ts = row['mission_time']
    left = ts*scaling - 125
    img['paste_left'] = left
    combined_image.paste(img['image'], (int(left), 0))

combined_image.save("060-launch-analysis-no-labels.png")

font = ImageFont.truetype("freefont/FreeSansBold.ttf", size=24)  
draw = ImageDraw.Draw(combined_image)

# Add labels
for i, img in enumerate(cropped_images):
    row = img['row']
    ts = row['mission_time']
    dt = row['dt']
    frame_index = img['frame_index']
    draw.text((20+img['paste_left'], 930), f"{ts:.3f}", fill="white", font=font)
    draw.text((20+img['paste_left'], 960), f"{dt:.3f}", fill="white", font=font)
    draw.text((20+img['paste_left'], 990), f"{frame_index}", fill="white", font=font)


# Add lines
line_spacing = 50  # Adjust the spacing as needed
for y in range(0, combined_image.height-150, line_spacing):
    draw.line([(0, y), (combined_image.width, y)], fill="white", width=1)


combined_image.save("060-launch-analysis.png")
combined_image
Out[6]:
No description has been provided for this image

Correcting for perspective distortion¶

In the image above we see that the bottle towards the top of the image is smaller than the bottle towards the bottom of the image. This is due to perspective distortion.

We can use the above image to measure the height of the bottle in each slice and then use this to correct for perspective distortion.

To do this I use a tool called Napari to manually measure the height of the bottle in each slice and save these as a csv file.

In [7]:
from collections import defaultdict

measurements = pd.read_csv("060-launch-analysis-measurements.csv").to_dict(orient="records")
mission_times = timestamps['mission_time'].to_dict()

lines = defaultdict(dict)
for m in measurements:
    lines[m['index']][m['vertex-index']] = m

def to_line_data(l, ix):
    p0 = l[0]
    p1 = l[1]
    # length = np.sqrt((p0['axis-1'] - p1['axis-1'])**2 + (p0['axis-0'] - p1['axis-0'])**2)
    length = p1['axis-0'] - p0['axis-0'] # We really only consider the vertical length here
    centre = (p0['axis-1'] + p1['axis-1']) / 2, (p0['axis-0'] + p1['axis-0']) / 2

    frame_id = ix + 267
    return {
        'line_id': ix,
        'y0': p0['axis-0'],
        'x0': p0['axis-1'],
        'y1': p1['axis-0'],
        'x1': p1['axis-1'],
        'yc': centre[1],
        'length': length,
        'centre': centre,
        'frame_id': frame_id,
        'time': mission_times[frame_id],
    }

lines =[to_line_data(l, ix) for ix, l in lines.items()]

line_df = pd.DataFrame(lines)
line_df
Out[7]:
line_id y0 x0 y1 x1 yc length centre frame_id time
0 0 735.359000 50.431026 924.83594 42.816807 830.097470 189.476940 (46.6239165, 830.09747) 267 0.006250
1 1 733.309100 131.258880 927.47144 124.688470 830.390270 194.162340 (127.973675, 830.39027) 268 0.010416
2 2 719.544860 377.842400 913.12180 370.813960 816.333330 193.576940 (374.32818, 816.3333299999999) 269 0.022916
3 3 712.809270 461.013100 906.38600 454.277200 809.597635 193.576730 (457.64515, 809.597635) 270 0.027083
4 4 702.391850 543.625900 893.36615 535.544560 797.879000 190.974300 (539.58523, 797.8789999999999) 271 0.031250
5 5 651.679500 791.860350 839.42633 781.480040 745.552915 187.746830 (786.670195, 745.552915) 272 0.043750
6 6 629.113830 876.256160 815.95776 864.973300 722.535795 186.843930 (870.61473, 722.535795) 273 0.047916
7 7 602.486270 959.749300 787.07400 950.722800 694.780135 184.587730 (955.23605, 694.780135) 274 0.052083
8 8 572.699500 1040.985800 754.12790 1031.508200 663.413700 181.428400 (1036.2469999999998, 663.4137) 275 0.056250
9 9 537.948300 1127.186900 718.02280 1117.709700 627.985550 180.074500 (1122.4483, 627.98555) 276 0.060416
10 10 500.489230 1210.680000 677.85596 1200.299800 589.172595 177.366730 (1205.4899, 589.172595) 277 0.064583
11 11 461.022400 1296.229000 638.00410 1285.119000 549.513250 176.981700 (1290.674, 549.51325) 278 0.068750
12 12 420.033100 1381.272200 589.73650 1370.163100 504.884800 169.703400 (1375.71765, 504.8848) 279 0.072916
13 13 375.213070 1466.698500 544.15020 1454.440600 459.681635 168.937130 (1460.56955, 459.681635) 280 0.077083
14 14 328.477630 1552.124800 498.18090 1540.248500 413.329265 169.703270 (1546.18665, 413.32926499999996) 281 0.081250
15 15 282.508330 1638.700200 448.76404 1628.357200 365.636185 166.255710 (1633.5286999999998, 365.636185) 282 0.085416
16 16 232.708270 1723.360400 397.04846 1712.634400 314.878365 164.340190 (1717.9974, 314.878365) 283 0.089583
17 17 86.901470 1978.973000 249.60226 1972.849900 168.251865 162.700790 (1975.91145, 168.251865) 284 0.102083
18 18 39.228405 2065.571800 197.55544 2058.095700 118.391923 158.327035 (2061.83375, 118.3919225) 285 0.106250
19 19 -2.321506 2149.546400 149.00763 2145.081500 73.343062 151.329136 (2147.3139499999997, 73.343062) 286 0.110416
In [8]:
lines
Out[8]:
[{'line_id': 0,
  'y0': 735.359,
  'x0': 50.431026,
  'y1': 924.83594,
  'x1': 42.816807,
  'yc': 830.09747,
  'length': 189.47694,
  'centre': (46.6239165, 830.09747),
  'frame_id': 267,
  'time': 0.006249999999997868},
 {'line_id': 1,
  'y0': 733.3091,
  'x0': 131.25888,
  'y1': 927.47144,
  'x1': 124.68847,
  'yc': 830.39027,
  'length': 194.16234000000009,
  'centre': (127.973675, 830.39027),
  'frame_id': 268,
  'time': 0.010415999999995762},
 {'line_id': 2,
  'y0': 719.54486,
  'x0': 377.8424,
  'y1': 913.1218,
  'x1': 370.81396,
  'yc': 816.3333299999999,
  'length': 193.57694000000004,
  'centre': (374.32818, 816.3333299999999),
  'frame_id': 269,
  'time': 0.022915999999998604},
 {'line_id': 3,
  'y0': 712.80927,
  'x0': 461.0131,
  'y1': 906.386,
  'x1': 454.2772,
  'yc': 809.597635,
  'length': 193.57673,
  'centre': (457.64515, 809.597635),
  'frame_id': 270,
  'time': 0.027082999999997526},
 {'line_id': 4,
  'y0': 702.39185,
  'x0': 543.6259,
  'y1': 893.36615,
  'x1': 535.54456,
  'yc': 797.8789999999999,
  'length': 190.97429999999997,
  'centre': (539.58523, 797.8789999999999),
  'frame_id': 271,
  'time': 0.031249999999996447},
 {'line_id': 5,
  'y0': 651.6795,
  'x0': 791.86035,
  'y1': 839.42633,
  'x1': 781.48004,
  'yc': 745.552915,
  'length': 187.74683000000005,
  'centre': (786.670195, 745.552915),
  'frame_id': 272,
  'time': 0.04374999999999929},
 {'line_id': 6,
  'y0': 629.11383,
  'x0': 876.25616,
  'y1': 815.95776,
  'x1': 864.9733,
  'yc': 722.535795,
  'length': 186.84393,
  'centre': (870.61473, 722.535795),
  'frame_id': 273,
  'time': 0.04791599999999718},
 {'line_id': 7,
  'y0': 602.48627,
  'x0': 959.7493,
  'y1': 787.074,
  'x1': 950.7228,
  'yc': 694.780135,
  'length': 184.58772999999997,
  'centre': (955.23605, 694.780135),
  'frame_id': 274,
  'time': 0.052082999999996105},
 {'line_id': 8,
  'y0': 572.6995,
  'x0': 1040.9858,
  'y1': 754.1279,
  'x1': 1031.5082,
  'yc': 663.4137,
  'length': 181.4284,
  'centre': (1036.2469999999998, 663.4137),
  'frame_id': 275,
  'time': 0.05624999999999858},
 {'line_id': 9,
  'y0': 537.9483,
  'x0': 1127.1869,
  'y1': 718.0228,
  'x1': 1117.7097,
  'yc': 627.98555,
  'length': 180.07449999999994,
  'centre': (1122.4483, 627.98555),
  'frame_id': 276,
  'time': 0.06041599999999647},
 {'line_id': 10,
  'y0': 500.48923,
  'x0': 1210.68,
  'y1': 677.85596,
  'x1': 1200.2998,
  'yc': 589.172595,
  'length': 177.36672999999996,
  'centre': (1205.4899, 589.172595),
  'frame_id': 277,
  'time': 0.06458299999999895},
 {'line_id': 11,
  'y0': 461.0224,
  'x0': 1296.229,
  'y1': 638.0041,
  'x1': 1285.119,
  'yc': 549.51325,
  'length': 176.9817,
  'centre': (1290.674, 549.51325),
  'frame_id': 278,
  'time': 0.06874999999999787},
 {'line_id': 12,
  'y0': 420.0331,
  'x0': 1381.2722,
  'y1': 589.7365,
  'x1': 1370.1631,
  'yc': 504.8848,
  'length': 169.7034,
  'centre': (1375.71765, 504.8848),
  'frame_id': 279,
  'time': 0.07291599999999576},
 {'line_id': 13,
  'y0': 375.21307,
  'x0': 1466.6985,
  'y1': 544.1502,
  'x1': 1454.4406,
  'yc': 459.681635,
  'length': 168.93713000000002,
  'centre': (1460.56955, 459.681635),
  'frame_id': 280,
  'time': 0.07708299999999824},
 {'line_id': 14,
  'y0': 328.47763,
  'x0': 1552.1248,
  'y1': 498.1809,
  'x1': 1540.2485,
  'yc': 413.32926499999996,
  'length': 169.70327000000003,
  'centre': (1546.18665, 413.32926499999996),
  'frame_id': 281,
  'time': 0.08124999999999716},
 {'line_id': 15,
  'y0': 282.50833,
  'x0': 1638.7002,
  'y1': 448.76404,
  'x1': 1628.3572,
  'yc': 365.636185,
  'length': 166.25571000000002,
  'centre': (1633.5286999999998, 365.636185),
  'frame_id': 282,
  'time': 0.0854159999999986},
 {'line_id': 16,
  'y0': 232.70827,
  'x0': 1723.3604,
  'y1': 397.04846,
  'x1': 1712.6344,
  'yc': 314.878365,
  'length': 164.34018999999998,
  'centre': (1717.9974, 314.878365),
  'frame_id': 283,
  'time': 0.08958299999999753},
 {'line_id': 17,
  'y0': 86.90147,
  'x0': 1978.973,
  'y1': 249.60226,
  'x1': 1972.8499,
  'yc': 168.251865,
  'length': 162.70078999999998,
  'centre': (1975.91145, 168.251865),
  'frame_id': 284,
  'time': 0.10208299999999682},
 {'line_id': 18,
  'y0': 39.228405,
  'x0': 2065.5718,
  'y1': 197.55544,
  'x1': 2058.0957,
  'yc': 118.3919225,
  'length': 158.327035,
  'centre': (2061.83375, 118.3919225),
  'frame_id': 285,
  'time': 0.10624999999999929},
 {'line_id': 19,
  'y0': -2.321506,
  'x0': 2149.5464,
  'y1': 149.00763,
  'x1': 2145.0815,
  'yc': 73.343062,
  'length': 151.329136,
  'centre': (2147.3139499999997, 73.343062),
  'frame_id': 286,
  'time': 0.11041599999999718}]
In [9]:
annotated_image = Image.open("060-launch-analysis.png")
draw = ImageDraw.Draw(annotated_image)

for m in lines:
    draw.line([(m['x0'], m['y0']), (m['x1'], m['y1'])], fill="red", width=2)
    draw.circle([m['centre'][0], m['centre'][1]], 5, fill="blue")

annotated_image
Out[9]:
No description has been provided for this image

So, let's see how the length of the bottle changes with height. Pixels in the image are measured from top (0) to bottom (1080) - so I've flipped the x-axis so that the points appear in the 'time' order as in the image.

In [10]:
line_df.set_index('yc')['length'].plot(marker='o', xlim=(900, 0), xlabel='Height in Image (pixels)', ylabel='Length (pixels)')
Out[10]:
<Axes: xlabel='Height in Image (pixels)', ylabel='Length (pixels)'>
No description has been provided for this image

We can indeed see that the length of the bottle decreases with height.

In [11]:
x = line_df['yc'].values
y = line_df['length'].values

# Linear fit: degree = 1
linear_coeffs = np.polyfit(x, y, deg=1)
linear_fit = np.poly1d(linear_coeffs)

# Plot
fig, ax = plt.subplots()
plt.plot(x, y, 'o', label='Data', markersize=6)
plt.plot(x, linear_fit(x), label='Linear Fit', linestyle='--')
ax.set_xlim(900, 0)

# Compute R-squared
linear_pred = linear_fit(x)
ss_res_linear = np.sum((y - linear_pred) ** 2)
ss_tot_linear = np.sum((y - np.mean(y)) ** 2)
r_squared_linear = 1 - (ss_res_linear / ss_tot_linear) 

ax.text(200,190, fr"$y = {linear_coeffs[0]:0.3f}x + {linear_coeffs[1]:0.3f}$", fontsize=12, color='red', ha='right')
ax.text(200,187, fr"$R^2 = {r_squared_linear:0.3f}$", fontsize=12, color='red', ha='right')
ax.set_title("Linear Fit");
No description has been provided for this image
In [12]:
# Quadratic fit: degree = 2
poly2_coeffs = np.polyfit(x, y, deg=2)
poly2_fit = np.poly1d(poly2_coeffs)

fig, ax = plt.subplots()
ax.plot(x, y, 'o', label='Data', markersize=6)
ax.plot(x, poly2_fit(x), label='Quadratic Fit', linestyle=':')
ax.set_xlim(900, 0)

# Compute R-squared
poly2_pred = poly2_fit(x)
ss_res_poly2 = np.sum((y - poly2_pred) ** 2)
ss_tot_poly2 = np.sum((y - np.mean(y)) ** 2)
r_squared_poly2 = 1 - (ss_res_poly2 / ss_tot_poly2) 

ax.text(200,190, fr"$y = {poly2_coeffs[0]:0.3f}x^2 + {poly2_coeffs[1]:0.3f}x + {poly2_coeffs[2]:0.3f}$", fontsize=12, color='red', ha='right')
ax.text(200,187, fr"$R^2 = {r_squared_poly2:0.3f}$", fontsize=12, color='red', ha='right')
ax.set_title("Quadratic Fit");
No description has been provided for this image

Mesh Warp Image¶

We can now use our quadratic fit to undistort the image. Basically we now how a function for how the 'real' length of the bottle relates to the height in the image. We can use this to create a y-distortion map for each pixel in the image.

We then use the mesh warp transform to apply this distortion to the image.

In [13]:
def undistort(x, y):
    factor = poly2_fit(y) / poly2_fit(0)
    y_pred = y * factor
    return x, y_pred

def build_mesh(size, grid_size):
    width, height = size
    mesh = []

    dx = width / grid_size
    dy = height / grid_size

    for j in range(grid_size):  # Note: Outer loop is Y (rows)
        # Source rectangle corners
        x0 = 0
        y0 = int(j * dy)
        x1 = width
        y1 = int((j + 1) * dy)

        # Source rectangle (left, top, right, bottom)
        src_rect = (x0, y0, x1, y1)

        # Destination rectangle corners after undistortion
        top_left = undistort(x0, y0)
        top_right = undistort(x1, y0)
        bottom_right = undistort(x1, y1)
        bottom_left = undistort(x0, y1)

        # Pillow expects the destination quad as:
        # (top-left.x, top-left.y, top-right.x, top-right.y, bottom-right.x, bottom-right.y, bottom-left.x, bottom-left.y)
        dst_quad = (
            top_left[0], top_left[1],
            bottom_left[0], bottom_left[1],
            bottom_right[0], bottom_right[1],
            top_right[0], top_right[1],
        )

        mesh.append((src_rect, dst_quad))

    return mesh

def simple(size):
    width, height = size
    factor = poly2_fit(height) / poly2_fit(0)
    

    return [
        [
            (0, 0, width, height),
            (0, 0, 0, height*factor, width, height*factor, width, 0),
        ]
    ]

# Load and transform image
img = Image.open("060-launch-analysis-no-labels.png")
mesh = build_mesh(img.size, grid_size=500)
# mesh = simple(img.size)
undistorted = img.transform(img.size, Image.MESH, mesh, Image.BICUBIC)

font = ImageFont.truetype("freefont/FreeSansBold.ttf", size=24)  
draw = ImageDraw.Draw(undistorted)

# Add labels
for i, img in enumerate(cropped_images):
    row = img['row']
    ts = row['mission_time']
    dt = row['dt']
    frame_index = img['frame_index']
    draw.text((20+img['paste_left'], 930), f"{ts:.3f}", fill="white", font=font)
    draw.text((20+img['paste_left'], 960), f"{dt:.3f}", fill="white", font=font)
    draw.text((20+img['paste_left'], 990), f"{frame_index}", fill="white", font=font)


# Add lines
line_spacing = 50  # Adjust the spacing as needed
for y in range(0, undistorted.height-150, line_spacing):
    draw.line([(0, y), (undistorted.width, y)], fill="white", width=1)

undistorted
Out[13]:
No description has been provided for this image

We can now see that the bottle is roughly three divisions long throughout the image... it's not perfect, but let's see if it's good enough.

Let's update our measurements to use the corrected measurements.

Calculations¶

The "rocket" is about 50cm long - so we can use our measurements to calculate the height of the rocket at any point in the image.

In [14]:
ROCKET_LENGTH = 0.50

calc_df = line_df.copy()
calc_df['length_calc'] = poly2_fit(calc_df['yc'])
calc_df['scale'] = ROCKET_LENGTH / calc_df['length_calc']

# Delta height - is yc minus yc of the previous line
calc_df['dl'] = -calc_df['yc'].diff().fillna(0)
calc_df['dh'] = calc_df['dl'] * calc_df['scale']
calc_df['dt'] = calc_df['time'].diff().fillna(0)

# Velocity is delta height divided by delta time
calc_df['v'] = calc_df['dh'] / calc_df['dt']

# Acceleration is delta velocity divided by delta time
calc_df['a'] = calc_df['v'].diff().fillna(0) / calc_df['dt']

# G-Force is acceleration divided by gravity
calc_df['g'] = calc_df['a'] / 9.81

calc_df.fillna(0, inplace=True)

calc_df
Out[14]:
line_id y0 x0 y1 x1 yc length centre frame_id time length_calc scale dl dh dt v a g
0 0 735.359000 50.431026 924.83594 42.816807 830.097470 189.476940 (46.6239165, 830.09747) 267 0.006250 193.233290 0.002588 -0.000000 -0.000000 0.000000 0.000000 0.000000 0.000000
1 1 733.309100 131.258880 927.47144 124.688470 830.390270 194.162340 (127.973675, 830.39027) 268 0.010416 193.254042 0.002587 -0.292800 -0.000758 0.004166 -0.181842 0.000000 0.000000
2 2 719.544860 377.842400 913.12180 370.813960 816.333330 193.576940 (374.32818, 816.3333299999999) 269 0.022916 192.263126 0.002601 14.056940 0.036557 0.012500 2.924521 248.509028 25.332215
3 3 712.809270 461.013100 906.38600 454.277200 809.597635 193.576730 (457.64515, 809.597635) 270 0.027083 191.792185 0.002607 6.735695 0.017560 0.004167 4.214034 309.458244 31.545183
4 4 702.391850 543.625900 893.36615 535.544560 797.879000 190.974300 (539.58523, 797.8789999999999) 271 0.031250 190.978839 0.002618 11.718635 0.030680 0.004167 7.362720 755.624238 77.025916
5 5 651.679500 791.860350 839.42633 781.480040 745.552915 187.746830 (786.670195, 745.552915) 272 0.043750 187.439886 0.002668 52.326085 0.139581 0.012500 11.166478 304.300657 31.019435
6 6 629.113830 876.256160 815.95776 864.973300 722.535795 186.843930 (870.61473, 722.535795) 273 0.047916 185.931200 0.002689 23.017120 0.061897 0.004166 14.857627 886.017451 90.317783
7 7 602.486270 959.749300 787.07400 950.722800 694.780135 184.587730 (955.23605, 694.780135) 274 0.052083 184.150945 0.002715 27.755660 0.075361 0.004167 18.085233 774.563520 78.956526
8 8 572.699500 1040.985800 754.12790 1031.508200 663.413700 181.428400 (1036.2469999999998, 663.4137) 275 0.056250 182.190448 0.002744 31.366435 0.086081 0.004167 20.657895 617.389420 62.934701
9 9 537.948300 1127.186900 718.02280 1117.709700 627.985550 180.074500 (1122.4483, 627.98555) 276 0.060416 180.041612 0.002777 35.428150 0.098389 0.004166 23.617087 710.319668 72.407713
10 10 500.489230 1210.680000 677.85596 1200.299800 589.172595 177.366730 (1205.4899, 589.172595) 277 0.064583 177.767272 0.002813 38.812955 0.109168 0.004167 26.198197 619.416995 63.141386
11 11 461.022400 1296.229000 638.00410 1285.119000 549.513250 176.981700 (1290.674, 549.51325) 278 0.068750 175.529517 0.002849 39.659345 0.112971 0.004167 27.110772 219.000426 22.324202
12 12 420.033100 1381.272200 589.73650 1370.163100 504.884800 169.703400 (1375.71765, 504.8848) 279 0.072916 173.115551 0.002888 44.628450 0.128898 0.004166 30.940438 919.266971 93.707133
13 13 375.213070 1466.698500 544.15020 1454.440600 459.681635 168.937130 (1460.56955, 459.681635) 280 0.077083 170.782948 0.002928 45.203165 0.132341 0.004167 31.759294 196.509743 20.031574
14 14 328.477630 1552.124800 498.18090 1540.248500 413.329265 169.703270 (1546.18665, 413.32926499999996) 281 0.081250 168.508562 0.002967 46.352370 0.137537 0.004167 33.006272 299.250744 30.504663
15 15 282.508330 1638.700200 448.76404 1628.357200 365.636185 166.255710 (1633.5286999999998, 365.636185) 282 0.085416 166.292600 0.003007 47.693080 0.143401 0.004166 34.421769 339.773626 34.635436
16 16 232.708270 1723.360400 397.04846 1712.634400 314.878365 164.340190 (1717.9974, 314.878365) 283 0.089583 164.072622 0.003047 50.757820 0.154681 0.004167 37.120460 647.633978 66.017735
17 17 86.901470 1978.973000 249.60226 1972.849900 168.251865 162.700790 (1975.91145, 168.251865) 284 0.102083 158.461126 0.003155 146.626500 0.462658 0.012500 37.012611 -8.627917 -0.879502
18 18 39.228405 2065.571800 197.55544 2058.095700 118.391923 158.327035 (2061.83375, 118.3919225) 285 0.106250 156.824247 0.003188 49.859943 0.158968 0.004167 38.149168 272.751827 27.803448
19 19 -2.321506 2149.546400 149.00763 2145.081500 73.343062 151.329136 (2147.3139499999997, 73.343062) 286 0.110416 155.463711 0.003216 45.048861 0.144885 0.004166 34.778073 -809.192132 -82.486456
In [26]:
fit_df = calc_df[(calc_df.time > 0.015) & (calc_df.time < 0.1)]

x = fit_df['time'].values
y = fit_df['v'].values

# Linear fit: degree = 1
linear_coeffs = np.polyfit(x, y, deg=1)
linear_fit = np.poly1d(linear_coeffs)

# Plot
plt.plot(calc_df['time'].values, calc_df['v'].values, 'o', label='Data', markersize=6)
plt.plot(x, linear_fit(x), label='Linear Fit', linestyle='--')

a, c = linear_coeffs
x_intercept = -c / a
print(f"linear_coeffs: {linear_coeffs}")
print(f"x-intercept: {x_intercept:.4f} s")
linear_coeffs: [533.24435989  -9.76507529]
x-intercept: 0.0183 s
No description has been provided for this image

So it looks like our takeoff estimate is off by about 0.018s. Anyway, assuming we have a constant acceleration of 533.24/9.81 = 54.36 g, then we can estimate the altitude after different thrust times as:

In [30]:
def altitude(t):
    h = a * t**2 / 2
    print(f"altitude({t:.2f}) = {h:.1f} m")

altitude(0.07)
altitude(0.08)
altitude(0.09)
altitude(0.07) = 1.3 m
altitude(0.08) = 1.7 m
altitude(0.09) = 2.2 m

From dynamic analysis we found that the "launch" speed was 32.19 m/s - let's figure out the time and altitude was at this point.

In [31]:
v = 32.19     # target speed in m/s
t = v / a
print(f"Time to reach {v} m/s: {t:.4f} seconds")
Time to reach 32.19 m/s: 0.0604 seconds

This is shorter than we had previously estimated, but we also found that we assumed a launch 0.0183 s before the fit indicates, that means a MECO still at around 0.8s which isn't that far off our estimate of 0.9 to 1.1 seconds. This also matches quite well when we can see from the video analysis that the water "exhaust" dies down.

Back of the envelope flight parameters¶

(We have more accurate measures in some of the other notebooks)

The middle of the flight seems to have a fairly constant acceleration - let's see if we can estimate the apogee from this. If we assume this acceleration continued until t=0.085s which seems to correlate reasonably well with our IMU readings, then we have an initial velocity of:

In [16]:
v_final = linear_fit(0.085)
v_final 
Out[16]:
np.float64(35.560695302234166)

With a velocity of 47m/s we can estimate the apogee as:

v**2 / (2 * g)

In [103]:
apogee = v_final**2 / (2 * 9.81)
apogee
Out[103]:
np.float64(64.45275486128129)
In [104]:
apogee_time = v_final / 9.81
apogee_time
Out[104]:
np.float64(3.624943455885236)

Both of theasure measurements correlate well with the flight computer data.

In [ ]: