56 """Test constant thrust command."""
58 command_type=CommandType.THRUST_DESIRED,
59 thrust_desired=5000.0,
62 F_des = _compute_thrust_command(cmd, horizon_N=10, dt=0.01)
65 self.assertEqual(len(F_des), 10)
66 np.testing.assert_array_equal(F_des, 5000.0)
69 """Test piecewise thrust schedule."""
71 command_type=CommandType.THRUST_DESIRED,
79 F_des = _compute_thrust_command(cmd, horizon_N=10, dt=0.01)
82 self.assertEqual(len(F_des), 10)
84 self.assertAlmostEqual(F_des[0], 1000.0, places=1)
86 self.assertAlmostEqual(F_des[-1], 3000.0, places=1)
89 """Test altitude command produces reasonable thrust."""
91 command_type=CommandType.ALTITUDE_GOAL,
96 F_des = _compute_altitude_command(
97 self.
nav, cmd.altitude_goal, horizon_N=10, dt=0.01, cfg=self.
cfg
101 self.assertEqual(len(F_des), 10)
102 self.assertTrue(np.all(F_des > 0))
106 self.assertTrue(np.all(F_des >= 500.0))
107 self.assertTrue(np.all(F_des <= 1e5))
110 """Test altitude command when below goal increases thrust."""
112 nav_low =
NavState(h=50.0, vz=5.0, theta=0.0, mass_estimate=100.0)
114 command_type=CommandType.ALTITUDE_GOAL,
118 F_des_low = _compute_altitude_command(
119 nav_low, cmd.altitude_goal, horizon_N=10, dt=0.01, cfg=self.
cfg
123 nav_at_goal =
NavState(h=200.0, vz=0.0, theta=0.0, mass_estimate=100.0)
124 F_des_at_goal = _compute_altitude_command(
125 nav_at_goal, cmd.altitude_goal, horizon_N=10, dt=0.01, cfg=self.
cfg
129 self.assertGreater(np.mean(F_des_low), np.mean(F_des_at_goal))
132 """Test thrust projection respects bounds."""
139 F_ref = _project_thrust(15000.0, F_min, F_max, F_prev, dt, self.
cfg)
140 self.assertAlmostEqual(F_ref, F_max, places=1)
143 F_ref = _project_thrust(500.0, F_min, F_max, F_prev, dt, self.
cfg)
144 self.assertAlmostEqual(F_ref, F_min, places=1)
147 F_ref = _project_thrust(6000.0, F_min, F_max, F_prev, dt, self.
cfg)
148 self.assertGreaterEqual(F_ref, F_min)
149 self.assertLessEqual(F_ref, F_max)
152 """Test thrust projection respects slew rate limit."""
159 self.
cfg.thrust_slew_max = 10000.0
163 F_ref = _project_thrust(F_des, F_min, F_max, F_prev, dt, self.
cfg)
166 max_allowed = F_prev + self.
cfg.thrust_slew_max * dt
167 self.assertLessEqual(F_ref, max_allowed + 1.0)
171 F_ref = _project_thrust(F_des, F_min, F_max, F_prev, dt, self.
cfg)
172 self.assertAlmostEqual(F_ref, F_des, places=1)
175 """Test thrust bounds estimation."""
189 F_min, F_max = _estimate_thrust_bounds(
190 state, self.
meas, self.
cfg,
None, eng_est
194 self.assertGreaterEqual(F_min, 0.0)
195 self.assertGreater(F_max, F_min)
198 self.assertGreater(F_max, eng_est.F)
201 """Test build_reference with thrust command mode."""
203 command_type=CommandType.THRUST_DESIRED,
204 thrust_desired=5000.0,
208 engine_wrapper = Mock(spec=EngineWrapper)
218 engine_wrapper.estimate_from_pressures = Mock(return_value=eng_est)
220 ref = build_reference(
226 engine_wrapper=engine_wrapper,
230 self.assertIsInstance(ref, Reference)
231 self.assertEqual(len(ref.F_ref), 10)
232 self.assertEqual(len(ref.MR_ref), 10)
233 self.assertEqual(len(ref.F_min), 10)
234 self.assertEqual(len(ref.F_max), 10)
235 self.assertEqual(len(ref.feasible), 10)
238 MR_mid = (self.
cfg.MR_min + self.
cfg.MR_max) / 2.0
239 np.testing.assert_array_almost_equal(ref.MR_ref, MR_mid, decimal=2)
242 self.assertTrue(np.all(ref.F_ref >= ref.F_min))
243 self.assertTrue(np.all(ref.F_ref <= ref.F_max))
246 """Test build_reference with altitude command mode."""
248 command_type=CommandType.ALTITUDE_GOAL,
253 engine_wrapper = Mock(spec=EngineWrapper)
263 engine_wrapper.estimate_from_pressures = Mock(return_value=eng_est)
265 ref = build_reference(
271 engine_wrapper=engine_wrapper,
275 self.assertIsInstance(ref, Reference)
276 self.assertEqual(len(ref.F_ref), 10)
279 self.assertTrue(np.all(ref.F_ref > 0))
282 self.assertTrue(np.all(ref.F_ref >= ref.F_min))
283 self.assertTrue(np.all(ref.F_ref <= ref.F_max))
286 """Test build_reference respects slew rate across horizon."""
288 command_type=CommandType.THRUST_DESIRED,
289 thrust_desired=20000.0,
293 self.
cfg.thrust_slew_max = 10000.0
296 engine_wrapper = Mock(spec=EngineWrapper)
306 engine_wrapper.estimate_from_pressures = Mock(return_value=eng_est)
309 ref = build_reference(
315 engine_wrapper=engine_wrapper,
316 F_ref_prev=F_ref_prev,
320 for k
in range(1, len(ref.F_ref)):
321 dF = abs(ref.F_ref[k] - ref.F_ref[k - 1])
322 dF_max_allowed = self.
cfg.thrust_slew_max * self.
cfg.dt
323 self.assertLessEqual(dF, dF_max_allowed + 1.0)