Skip to content

Commit 85a0eff

Browse files
CopilotaZira371
andcommitted
Refactor acceptance tests with Bella Lui parameters and named constants
Co-authored-by: aZira371 <[email protected]>
1 parent 7eab61d commit 85a0eff

File tree

2 files changed

+143
-52
lines changed

2 files changed

+143
-52
lines changed

tests/acceptance/test_3dof_flight.py

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
- Comparison between 3 DOF and 6 DOF simulations
1010
- Validation of key flight metrics
1111
12-
The tests use realistic rocket configurations and scenarios to ensure the
13-
robustness of the 3 DOF implementation.
12+
The tests use realistic rocket configurations based on the Bella Lui rocket
13+
to ensure the robustness of the 3 DOF implementation.
1414
1515
All fixtures are defined in tests/fixtures/flight/flight_fixtures.py.
1616
"""
@@ -19,12 +19,31 @@
1919

2020
from rocketpy import Flight
2121

22+
# Test tolerance constants
23+
MIN_APOGEE_ALTITUDE = 500 # meters
24+
MAX_APOGEE_ALTITUDE = 3000 # meters
25+
MIN_APOGEE_TIME = 5 # seconds
26+
MAX_APOGEE_TIME = 60 # seconds
27+
MIN_VELOCITY = 50 # m/s
28+
MAX_VELOCITY = 400 # m/s
29+
APOGEE_SPEED_RATIO = 0.3 # Max ratio of apogee speed to max speed
30+
MAX_LATERAL_TO_ALTITUDE_RATIO = 0.5 # Max lateral displacement vs altitude ratio
31+
QUATERNION_CHANGE_TOLERANCE = 0.1 # Max quaternion change without weathercocking
32+
WEATHERCOCK_COEFFICIENTS = [0.0, 0.5, 1.0, 2.0] # Test weathercock coefficients
33+
WEATHERCOCK_RANGE_THRESHOLD = 1.0 # Minimum apogee difference (meters)
34+
LATERAL_INCREASE_THRESHOLD = 0.5 # Minimum lateral increase (meters)
35+
LAUNCH_INCLINATION = 85 # degrees from horizontal
36+
LAUNCH_HEADING = 0 # degrees
37+
MASS_TOLERANCE = 0.001 # kg
38+
THRUST_TOLERANCE = 1e-6 # N
39+
2240

2341
def test_3dof_flight_basic_trajectory(flight_3dof_no_weathercock):
2442
"""Test that 3 DOF flight produces reasonable trajectory.
2543
2644
This test validates that the basic 3 DOF flight simulation produces
27-
physically reasonable results for key flight metrics.
45+
physically reasonable results for key flight metrics using Bella Lui
46+
based rocket parameters.
2847
2948
Parameters
3049
----------
@@ -33,21 +52,24 @@ def test_3dof_flight_basic_trajectory(flight_3dof_no_weathercock):
3352
"""
3453
flight = flight_3dof_no_weathercock
3554

36-
# Validate apogee is reasonable (between 500m and 3000m)
55+
# Validate apogee is reasonable
3756
apogee_altitude = flight.apogee - flight.env.elevation
38-
assert 500 < apogee_altitude < 3000, (
39-
f"Apogee altitude {apogee_altitude:.1f} m is outside expected range"
57+
assert MIN_APOGEE_ALTITUDE < apogee_altitude < MAX_APOGEE_ALTITUDE, (
58+
f"Apogee altitude {apogee_altitude:.1f} m is outside expected range "
59+
f"[{MIN_APOGEE_ALTITUDE}, {MAX_APOGEE_ALTITUDE}]"
4060
)
4161

42-
# Validate apogee time is reasonable (between 5s and 60s)
43-
assert 5 < flight.apogee_time < 60, (
44-
f"Apogee time {flight.apogee_time:.1f} s is outside expected range"
62+
# Validate apogee time is reasonable
63+
assert MIN_APOGEE_TIME < flight.apogee_time < MAX_APOGEE_TIME, (
64+
f"Apogee time {flight.apogee_time:.1f} s is outside expected range "
65+
f"[{MIN_APOGEE_TIME}, {MAX_APOGEE_TIME}]"
4566
)
4667

4768
# Validate maximum velocity is reasonable (subsonic to low supersonic)
4869
max_velocity = flight.max_speed
49-
assert 50 < max_velocity < 400, (
50-
f"Maximum velocity {max_velocity:.1f} m/s is outside expected range"
70+
assert MIN_VELOCITY < max_velocity < MAX_VELOCITY, (
71+
f"Maximum velocity {max_velocity:.1f} m/s is outside expected range "
72+
f"[{MIN_VELOCITY}, {MAX_VELOCITY}]"
5173
)
5274

5375
# Validate impact velocity is reasonable (with no parachute, terminal velocity)
@@ -78,9 +100,9 @@ def test_3dof_flight_energy_conservation(flight_3dof_no_weathercock):
78100
max_speed = flight.max_speed
79101

80102
# Apogee speed should be significantly less than max speed
81-
assert apogee_speed < 0.3 * max_speed, (
103+
assert apogee_speed < APOGEE_SPEED_RATIO * max_speed, (
82104
f"Apogee speed {apogee_speed:.1f} m/s should be much less than "
83-
f"max speed {max_speed:.1f} m/s"
105+
f"max speed {max_speed:.1f} m/s (ratio < {APOGEE_SPEED_RATIO})"
84106
)
85107

86108
# Potential energy at apogee should be positive
@@ -112,9 +134,10 @@ def test_3dof_flight_lateral_motion_no_weathercock(flight_3dof_no_weathercock):
112134

113135
# Lateral displacement should be reasonable (not too extreme)
114136
apogee_altitude = flight.apogee - flight.env.elevation
115-
assert lateral_displacement < 0.5 * apogee_altitude, (
137+
assert lateral_displacement < MAX_LATERAL_TO_ALTITUDE_RATIO * apogee_altitude, (
116138
f"Lateral displacement {lateral_displacement:.1f} m seems too large "
117-
f"compared to apogee altitude {apogee_altitude:.1f} m"
139+
f"compared to apogee altitude {apogee_altitude:.1f} m "
140+
f"(ratio > {MAX_LATERAL_TO_ALTITUDE_RATIO})"
118141
)
119142

120143

@@ -142,11 +165,13 @@ def test_3dof_weathercocking_affects_trajectory(
142165

143166
# They should be reasonably close but not identical
144167
apogee_difference = abs(apogee_no_wc - apogee_with_wc)
145-
assert apogee_difference > 0.1, "Weathercocking should affect apogee altitude"
168+
assert apogee_difference > LATERAL_INCREASE_THRESHOLD, (
169+
f"Weathercocking should affect apogee altitude (difference: {apogee_difference:.2f} m)"
170+
)
146171

147172
# Both should still be in reasonable range
148-
assert 500 < apogee_no_wc < 3000
149-
assert 500 < apogee_with_wc < 3000
173+
assert MIN_APOGEE_ALTITUDE < apogee_no_wc < MAX_APOGEE_ALTITUDE
174+
assert MIN_APOGEE_ALTITUDE < apogee_with_wc < MAX_APOGEE_ALTITUDE
150175

151176

152177
def test_3dof_weathercocking_coefficient_stored(flight_3dof_with_weathercock):
@@ -257,16 +282,17 @@ def test_3dof_flight_quaternion_evolution_no_weathercock(flight_3dof_no_weatherc
257282

258283
# Without weathercocking, quaternion change should be minimal
259284
# (allowing for some numerical drift)
260-
assert quat_change < 0.1, (
261-
f"Quaternion change {quat_change:.6f} is too large without weathercocking"
285+
assert quat_change < QUATERNION_CHANGE_TOLERANCE, (
286+
f"Quaternion change {quat_change:.6f} is too large without weathercocking "
287+
f"(tolerance: {QUATERNION_CHANGE_TOLERANCE})"
262288
)
263289

264290

265291
def test_3dof_flight_mass_variation(flight_3dof_no_weathercock):
266292
"""Test that rocket mass varies correctly during flight.
267293
268294
The rocket mass should decrease during motor burn and remain constant
269-
after burnout.
295+
after burnout. Based on Bella Lui motor with ~2.43s burn time.
270296
271297
Parameters
272298
----------
@@ -281,8 +307,8 @@ def test_3dof_flight_mass_variation(flight_3dof_no_weathercock):
281307
# Get mass during burn (at t=1s)
282308
mass_during_burn = flight.rocket.total_mass(1.0)
283309

284-
# Get mass after burnout (at t=5s, after 3.5s burn time)
285-
post_burn_mass = flight.rocket.total_mass(5.0)
310+
# Get mass after burnout (at t=4s, after ~2.43s burn time)
311+
post_burn_mass = flight.rocket.total_mass(4.0)
286312

287313
# Initial mass should be greater than mass during burn
288314
assert initial_mass > mass_during_burn, "Mass should decrease during burn"
@@ -294,19 +320,20 @@ def test_3dof_flight_mass_variation(flight_3dof_no_weathercock):
294320
)
295321

296322
# Mass should remain constant after burnout
297-
# Check at t=6s and t=7s to verify constant mass
323+
# Check at t=5s and t=6s to verify constant mass
324+
mass_at_5s = flight.rocket.total_mass(5.0)
298325
mass_at_6s = flight.rocket.total_mass(6.0)
299-
mass_at_7s = flight.rocket.total_mass(7.0)
300-
assert abs(mass_at_6s - mass_at_7s) < 0.001, (
301-
"Mass should remain constant after burnout"
326+
assert abs(mass_at_5s - mass_at_6s) < MASS_TOLERANCE, (
327+
f"Mass should remain constant after burnout (difference: {abs(mass_at_5s - mass_at_6s):.4f} kg, "
328+
f"tolerance: {MASS_TOLERANCE} kg)"
302329
)
303330

304331

305332
def test_3dof_flight_thrust_profile(flight_3dof_no_weathercock):
306333
"""Test that thrust profile is correct for point mass motor.
307334
308-
For a constant thrust motor, thrust should be constant during burn
309-
and zero after burnout.
335+
For the Bella Lui motor (K828FJ), thrust should be positive during burn
336+
(~2.43s) and zero after burnout.
310337
311338
Parameters
312339
----------
@@ -318,14 +345,17 @@ def test_3dof_flight_thrust_profile(flight_3dof_no_weathercock):
318345
# Get thrust during burn (at t=1s)
319346
thrust_during_burn = flight.rocket.motor.thrust(1.0)
320347

321-
# Get thrust after burnout (at t=5s)
322-
thrust_after_burnout = flight.rocket.motor.thrust(5.0)
348+
# Get thrust after burnout (at t=4s, after ~2.43s burn time)
349+
thrust_after_burnout = flight.rocket.motor.thrust(4.0)
323350

324351
# Thrust during burn should be positive
325352
assert thrust_during_burn > 0, "Thrust should be positive during burn"
326353

327354
# Thrust after burnout should be zero
328-
assert abs(thrust_after_burnout) < 1e-6, "Thrust should be zero after burnout"
355+
assert abs(thrust_after_burnout) < THRUST_TOLERANCE, (
356+
f"Thrust should be zero after burnout (got {thrust_after_burnout:.9f} N, "
357+
f"tolerance: {THRUST_TOLERANCE} N)"
358+
)
329359

330360

331361
def test_3dof_flight_reproducibility(
@@ -347,8 +377,8 @@ def test_3dof_flight_reproducibility(
347377
rocket=acceptance_point_mass_rocket,
348378
environment=example_spaceport_env,
349379
rail_length=5.0,
350-
inclination=85,
351-
heading=0,
380+
inclination=LAUNCH_INCLINATION,
381+
heading=LAUNCH_HEADING,
352382
simulation_mode="3 DOF",
353383
weathercock_coeff=0.5,
354384
)
@@ -357,8 +387,8 @@ def test_3dof_flight_reproducibility(
357387
rocket=acceptance_point_mass_rocket,
358388
environment=example_spaceport_env,
359389
rail_length=5.0,
360-
inclination=85,
361-
heading=0,
390+
inclination=LAUNCH_INCLINATION,
391+
heading=LAUNCH_HEADING,
362392
simulation_mode="3 DOF",
363393
weathercock_coeff=0.5,
364394
)
@@ -390,16 +420,16 @@ def test_3dof_flight_different_weathercock_coefficients(
390420
acceptance_point_mass_rocket : rocketpy.PointMassRocket
391421
Rocket fixture for testing.
392422
"""
393-
coefficients = [0.0, 0.5, 1.0, 2.0]
423+
coefficients = WEATHERCOCK_COEFFICIENTS
394424
flights = []
395425

396426
for coeff in coefficients:
397427
flight = Flight(
398428
rocket=acceptance_point_mass_rocket,
399429
environment=example_spaceport_env,
400430
rail_length=5.0,
401-
inclination=85,
402-
heading=0,
431+
inclination=LAUNCH_INCLINATION,
432+
heading=LAUNCH_HEADING,
403433
simulation_mode="3 DOF",
404434
weathercock_coeff=coeff,
405435
)
@@ -408,15 +438,16 @@ def test_3dof_flight_different_weathercock_coefficients(
408438
# All flights should have reasonable apogees
409439
for flight, coeff in zip(flights, coefficients):
410440
apogee = flight.apogee - flight.env.elevation
411-
assert 500 < apogee < 3000, (
412-
f"Apogee {apogee:.1f} m with weathercock_coeff={coeff} is outside expected range"
441+
assert MIN_APOGEE_ALTITUDE < apogee < MAX_APOGEE_ALTITUDE, (
442+
f"Apogee {apogee:.1f} m with weathercock_coeff={coeff} is outside expected range "
443+
f"[{MIN_APOGEE_ALTITUDE}, {MAX_APOGEE_ALTITUDE}]"
413444
)
414445

415446
# Apogees should vary with weathercock coefficient
416447
# Calculate the range of apogees to ensure they're different
417448
apogees = [f.apogee for f in flights]
418449
apogee_range = max(apogees) - min(apogees)
419-
assert apogee_range > 1.0, (
450+
assert apogee_range > WEATHERCOCK_RANGE_THRESHOLD, (
420451
f"Different weathercock coefficients should produce different apogees. "
421-
f"Range was only {apogee_range:.2f} m"
452+
f"Range was only {apogee_range:.2f} m (threshold: {WEATHERCOCK_RANGE_THRESHOLD} m)"
422453
)

tests/fixtures/flight/flight_fixtures.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -367,29 +367,89 @@ def flight_flat(example_plain_env, cesaroni_m1670):
367367

368368
# 3 DOF Flight Fixtures
369369
# These fixtures are for testing the 3 DOF flight simulation mode
370+
# Based on Bella Lui rocket parameters for realistic acceptance testing
371+
372+
373+
@pytest.fixture
374+
def bella_lui_point_mass_motor():
375+
"""Create a point mass motor based on Bella Lui rocket (K828FJ motor).
376+
377+
The Bella Lui rocket used an AeroTech K828FJ motor with the following specs:
378+
- Total impulse: ~2157 N·s
379+
- Average thrust: ~888 N
380+
- Burn time: ~2.43 s
381+
- Propellant mass: ~1.373 kg
382+
- Dry mass: ~1 kg
383+
384+
Returns
385+
-------
386+
rocketpy.PointMassMotor
387+
A point mass motor with Bella Lui rocket parameters.
388+
"""
389+
return PointMassMotor(
390+
thrust_source="data/motors/aerotech/AeroTech_K828FJ.eng",
391+
dry_mass=1.0, # kg
392+
propellant_initial_mass=1.373, # kg
393+
burn_time=2.43, # s
394+
)
395+
396+
397+
@pytest.fixture
398+
def bella_lui_point_mass_rocket(bella_lui_point_mass_motor):
399+
"""Create a point mass rocket based on Bella Lui rocket parameters.
400+
401+
The Bella Lui rocket specifications:
402+
- Radius: 156 mm (diameter 312 mm)
403+
- Dry mass (without motor): ~17.227 kg
404+
- Power-off drag coefficient: ~0.43
405+
- Power-on drag coefficient: ~0.43
406+
407+
Parameters
408+
----------
409+
bella_lui_point_mass_motor : rocketpy.PointMassMotor
410+
The motor to be added to the rocket.
411+
412+
Returns
413+
-------
414+
rocketpy.PointMassRocket
415+
A point mass rocket with Bella Lui parameters.
416+
"""
417+
rocket = PointMassRocket(
418+
radius=0.156, # 156 mm radius
419+
mass=17.227, # kg without motor
420+
center_of_mass_without_motor=0,
421+
power_off_drag=0.43,
422+
power_on_drag=0.43,
423+
)
424+
rocket.add_motor(bella_lui_point_mass_motor, position=0)
425+
return rocket
370426

371427

372428
@pytest.fixture
373429
def acceptance_point_mass_motor():
374430
"""Create a realistic point mass motor for acceptance testing.
375431
432+
This is an alias for bella_lui_point_mass_motor to maintain compatibility.
433+
376434
Returns
377435
-------
378436
rocketpy.PointMassMotor
379437
A point mass motor with realistic thrust and mass properties.
380438
"""
381439
return PointMassMotor(
382-
thrust_source=1500, # 1500 N constant thrust
383-
dry_mass=2.5, # 2.5 kg dry mass
384-
propellant_initial_mass=3.0, # 3.0 kg propellant
385-
burn_time=3.5, # 3.5 s burn time
440+
thrust_source="data/motors/aerotech/AeroTech_K828FJ.eng",
441+
dry_mass=1.0,
442+
propellant_initial_mass=1.373,
443+
burn_time=2.43,
386444
)
387445

388446

389447
@pytest.fixture
390448
def acceptance_point_mass_rocket(acceptance_point_mass_motor):
391449
"""Create a realistic point mass rocket for acceptance testing.
392450
451+
This is an alias for bella_lui_point_mass_rocket to maintain compatibility.
452+
393453
Parameters
394454
----------
395455
acceptance_point_mass_motor : rocketpy.PointMassMotor
@@ -401,11 +461,11 @@ def acceptance_point_mass_rocket(acceptance_point_mass_motor):
401461
A point mass rocket with realistic dimensions and properties.
402462
"""
403463
rocket = PointMassRocket(
404-
radius=0.0635, # 127 mm diameter (5 inches)
405-
mass=5.0, # 5 kg without motor
406-
center_of_mass_without_motor=0.5,
407-
power_off_drag=0.45,
408-
power_on_drag=0.50,
464+
radius=0.156,
465+
mass=17.227,
466+
center_of_mass_without_motor=0,
467+
power_off_drag=0.43,
468+
power_on_drag=0.43,
409469
)
410470
rocket.add_motor(acceptance_point_mass_motor, position=0)
411471
return rocket

0 commit comments

Comments
 (0)