Skip to content

Commit 6df04c3

Browse files
TST: adds prometheus apogee acceptance test
1 parent 597cd22 commit 6df04c3

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from rocketpy import Flight
2+
from rocketpy.simulation.flight_data_importer import FlightDataImporter
3+
4+
5+
def test_prometheus_rocket_data_asserts_acceptance(
6+
environment_spaceport_america_2023, prometheus_rocket
7+
):
8+
"""Tests the Prometheus rocket flight data against acceptance criteria.
9+
10+
This function simulates a rocket flight using the given environment and
11+
rocket parameters, then compares the simulated apogee with real flight data
12+
to ensure the relative error is within acceptable thresholds.
13+
14+
Parameters
15+
----------
16+
environment_spaceport_america_2023 : Environment
17+
An environment configuration for Spaceport America in 2023.
18+
prometheus_rocket : Rocket
19+
The Prometheus rocket configuration.
20+
21+
Raises
22+
------
23+
AssertionError
24+
If the relative error between the simulated apogee and the real apogee
25+
exceeds the threshold.
26+
"""
27+
# Define relative error threshold (defined manually based on data)
28+
apogee_threshold = 7.5 / 100
29+
30+
# Simulate the flight
31+
test_flight = Flight(
32+
rocket=prometheus_rocket,
33+
environment=environment_spaceport_america_2023,
34+
inclination=80,
35+
heading=75,
36+
rail_length=5.18,
37+
)
38+
39+
# Read the flight data
40+
columns_map = {
41+
"time": "time",
42+
"altitude": "z",
43+
"height": "altitude",
44+
"acceleration": "acceleration",
45+
"pressure": "pressure",
46+
"accel_x": "ax",
47+
"accel_y": "ay",
48+
"accel_z": "az",
49+
"latitude": "latitude",
50+
"longitude": "longitude",
51+
}
52+
53+
altimeter_data = FlightDataImporter(
54+
name="Telemetry Mega",
55+
paths="data/prometheus/2022-06-24-serial-6583-flight-0003-TeleMega.csv",
56+
columns_map=columns_map,
57+
units=None,
58+
interpolation="linear",
59+
extrapolation="zero",
60+
delimiter=",",
61+
encoding="utf-8",
62+
)
63+
64+
# Calculate errors and assert values
65+
real_apogee = altimeter_data.altitude.max
66+
rocketpy_apogee = test_flight.apogee - test_flight.env.elevation
67+
a_error = abs(real_apogee - rocketpy_apogee)
68+
r_error = a_error / real_apogee
69+
70+
assert r_error < apogee_threshold, f"Apogee relative error is {r_error*100:.2f}%"

tests/fixtures/environment/environment_fixtures.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,30 @@ def env_analysis():
7171
)
7272

7373
return env_analysis
74+
75+
76+
@pytest.fixture
77+
def environment_spaceport_america_2023():
78+
"""Creates an Environment object for Spaceport America with a 2023 launch
79+
conditions.
80+
81+
Returns
82+
-------
83+
rocketpy.Environment
84+
Environment object configured for Spaceport America in 2023.
85+
"""
86+
env = Environment(
87+
latitude=32.939377,
88+
longitude=-106.911986,
89+
elevation=1401,
90+
)
91+
env.set_date(date=(2023, 6, 24, 9), timezone="America/Denver")
92+
93+
env.set_atmospheric_model(
94+
type="Reanalysis",
95+
file="data/weather/spaceport_america_pressure_levels_2023_hourly.nc",
96+
dictionary="ECMWF",
97+
)
98+
99+
env.max_expected_height = 6000
100+
return env

tests/fixtures/motor/generic_motor_fixtures.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,27 @@ def generic_motor():
2828
)
2929

3030
return motor
31+
32+
33+
@pytest.fixture
34+
def generic_motor_cesaroni_M1520():
35+
"""Defines a Cesaroni M1520 motor for the Prometheus rocket using the
36+
GenericMotor class.
37+
38+
Returns
39+
-------
40+
GenericMotor
41+
The Cesaroni M1520 motor for the Prometheus rocket.
42+
"""
43+
return GenericMotor(
44+
# burn specs: https://www.thrustcurve.org/simfiles/5f4294d20002e900000006b1/
45+
thrust_source="data/motors/cesaroni/Cesaroni_7579M1520-P.eng",
46+
burn_time=4.897,
47+
propellant_initial_mass=3.737,
48+
dry_mass=2.981,
49+
# casing specs: Pro98 3G Gen2 casing
50+
chamber_radius=0.064,
51+
chamber_height=0.548,
52+
chamber_position=0.274,
53+
nozzle_radius=0.027,
54+
)

tests/fixtures/rockets/rocket_fixtures.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import numpy as np
12
import pytest
23

34
from rocketpy import Rocket
@@ -276,3 +277,81 @@ def dimensionless_calisto(kg, m, dimensionless_cesaroni_m1670):
276277
)
277278
example_rocket.add_motor(dimensionless_cesaroni_m1670, position=(-1.373) * m)
278279
return example_rocket
280+
281+
282+
@pytest.fixture
283+
def prometheus_rocket(generic_motor_cesaroni_M1520):
284+
"""Create a simple object of the Rocket class to be used in the tests. This
285+
is the Prometheus rocket, a rocket documented in the Flight Examples section
286+
of the RocketPy documentation.
287+
288+
Parameters
289+
----------
290+
generic_motor_cesaroni_M1520 : GenericMotor
291+
An object of the GenericMotor class. This is a pytest fixture too.
292+
"""
293+
294+
def prometheus_cd_at_ma(mach):
295+
"""Gives the drag coefficient of the rocket at a given mach number."""
296+
if mach <= 0.15:
297+
return 0.422
298+
elif mach <= 0.45:
299+
return 0.422 + (mach - 0.15) * (0.38 - 0.422) / (0.45 - 0.15)
300+
elif mach <= 0.77:
301+
return 0.38 + (mach - 0.45) * (0.32 - 0.38) / (0.77 - 0.45)
302+
elif mach <= 0.82:
303+
return 0.32 + (mach - 0.77) * (0.3 - 0.32) / (0.82 - 0.77)
304+
elif mach <= 0.88:
305+
return 0.3 + (mach - 0.82) * (0.3 - 0.3) / (0.88 - 0.82)
306+
elif mach <= 0.94:
307+
return 0.3 + (mach - 0.88) * (0.32 - 0.3) / (0.94 - 0.88)
308+
elif mach <= 0.99:
309+
return 0.32 + (mach - 0.94) * (0.37 - 0.32) / (0.99 - 0.94)
310+
elif mach <= 1.04:
311+
return 0.37 + (mach - 0.99) * (0.44 - 0.37) / (1.04 - 0.99)
312+
elif mach <= 1.24:
313+
return 0.44 + (mach - 1.04) * (0.43 - 0.44) / (1.24 - 1.04)
314+
elif mach <= 1.33:
315+
return 0.43 + (mach - 1.24) * (0.42 - 0.43) / (1.33 - 1.24)
316+
elif mach <= 1.49:
317+
return 0.42 + (mach - 1.33) * (0.39 - 0.42) / (1.49 - 1.33)
318+
else:
319+
return 0.39
320+
321+
prometheus = Rocket(
322+
radius=0.06985, # 5.5" diameter circle
323+
mass=13.93,
324+
inertia=(
325+
4.87,
326+
4.87,
327+
0.05,
328+
),
329+
power_off_drag=prometheus_cd_at_ma,
330+
power_on_drag=lambda x: prometheus_cd_at_ma(x) * 1.02, # 5% increase in drag
331+
center_of_mass_without_motor=0.9549,
332+
coordinate_system_orientation="tail_to_nose",
333+
)
334+
335+
prometheus.set_rail_buttons(0.69, 0.21, 60)
336+
337+
prometheus.add_motor(motor=generic_motor_cesaroni_M1520, position=0)
338+
nose_cone = prometheus.add_nose(length=0.742, kind="Von Karman", position=2.229)
339+
fin_set = prometheus.add_trapezoidal_fins(
340+
n=3,
341+
span=0.13,
342+
root_chord=0.268,
343+
tip_chord=0.136,
344+
position=0.273,
345+
sweep_length=0.066,
346+
)
347+
drogue_chute = prometheus.add_parachute(
348+
"Drogue",
349+
cd_s=1.6 * np.pi * 0.3048**2, # Cd = 1.6, D_chute = 24 in
350+
trigger="apogee",
351+
)
352+
main_chute = prometheus.add_parachute(
353+
"Main",
354+
cd_s=2.2 * np.pi * 0.9144**2, # Cd = 2.2, D_chute = 72 in
355+
trigger=457.2, # 1500 ft
356+
)
357+
return prometheus

0 commit comments

Comments
 (0)