STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_flight_liftoff_mass.py
Go to the documentation of this file.
1"""The vehicle flies at the liftoff mass it was weighed at.
2
3Judged by RocketPy's own mass bookkeeping (``rocket.total_mass(0)``), not by the arithmetic that
4sets the airframe: the airframe is set from the booked motor, propellants and gases, and RocketPy
5sums its tanks and gases independently.
6"""
7
8from __future__ import annotations
9
10import contextlib
11import copy
12import io
13
14import pytest
15from scipy.interpolate import interp1d
16
17from tests import flight_cases as fc
18
19LB = 0.45359237
20
21
22def _fly(liftoff=None):
23 from ui.flight_sim import setup_flight
24
25 cfg = copy.deepcopy(fc.roomy_tanks(fc.shipped()))
26 t, F, mO, mF = fc.header_curve()
27 cfg.thrust.burn_time = float(t[-1])
28 f = lambda y: interp1d(t, y, bounds_error=False, fill_value=0.0) # noqa: E731
29 with contextlib.redirect_stdout(io.StringIO()):
30 return setup_flight(cfg, f(F), f(mO), f(mF), liftoff_mass=liftoff)
31
32
33@pytest.fixture(scope="module")
35 return _fly()
36
37
39 budget = as_configured["mass_budget"]
40 assert budget["airframe_source"] == "config"
41 assert as_configured["flight"].rocket.total_mass(0.0) == pytest.approx(budget["liftoff_kg"], abs=1e-3)
42
43
45 target = 190.0 * LB
46 heavier = _fly(target)
47 assert heavier["flight"].rocket.total_mass(0.0) == pytest.approx(target, abs=1e-3)
48 budget = heavier["mass_budget"]
49 assert budget["airframe_source"] == "liftoff mass"
50 assert budget["liftoff_kg"] == pytest.approx(target, abs=1e-9)
51 # Only the airframe moved: the motor, propellants and gases are what the burn carries.
52 for key in ("motor_dry_kg", "oxidizer_kg", "fuel_kg", "pressurant_kg", "ullage_gas_kg"):
53 assert budget[key] == pytest.approx(as_configured["mass_budget"][key], rel=1e-12), key
54 assert (target > as_configured["mass_budget"]["liftoff_kg"]) == (heavier["apogee"] < as_configured["apogee"])
55
56
58 with pytest.raises(ValueError, match="leaves nothing for the airframe"):
59 _fly(as_configured["mass_budget"]["motor_dry_kg"])
60
61
62def _fly_with(liftoff=None, gas=None):
63 from ui.flight_sim import setup_flight
64
65 cfg = copy.deepcopy(fc.roomy_tanks(fc.shipped()))
66 t, F, mO, mF = fc.header_curve()
67 cfg.thrust.burn_time = float(t[-1])
68 f = lambda y: interp1d(t, y, bounds_error=False, fill_value=0.0) # noqa: E731
69 with contextlib.redirect_stdout(io.StringIO()):
70 return setup_flight(cfg, f(F), f(mO), f(mF), liftoff_mass=liftoff, ullage_gas_kg=gas)
71
72
74 own = as_configured["mass_budget"]["ullage_gas_kg"]
75 gas = own + 0.515 # the drawing's larger tanks hold more gas at T-0
76 flown = _fly_with(gas=gas)
77 total = flown["flight"].rocket.total_mass(0.0)
78 assert total == pytest.approx(as_configured["flight"].rocket.total_mass(0.0) + 0.515, abs=1e-3)
79 assert flown["mass_budget"]["ullage_gas_kg"] == pytest.approx(gas)
80 assert flown["mass_budget"]["airframe_kg"] == pytest.approx(as_configured["mass_budget"]["airframe_kg"])
81 # Weighed: the gas is inside the weight, so it comes out of the airframe, not on top of it.
82 weighed = _fly_with(liftoff=190.0 * LB, gas=gas)
83 assert weighed["flight"].rocket.total_mass(0.0) == pytest.approx(190.0 * LB, abs=1e-3)
test_the_drawings_ullage_gas_flies_with_the_vehicle(as_configured)
test_the_budget_is_rocketpys_own_total(as_configured)
_fly_with(liftoff=None, gas=None)
test_a_liftoff_mass_lighter_than_the_motor_is_refused(as_configured)
test_a_weighed_vehicle_lifts_off_at_its_weight_and_flies_lower(as_configured)