blob: 79454ab7f610c983a377a35d2d6cb88df1c0691d [file] [log] [blame]
James Kuszmaul04a343c2023-02-20 16:38:22 -08001#include "y2023/localizer/localizer.h"
2
Austin Schuh99f7c6a2024-06-25 22:07:44 -07003#include "absl/flags/declare.h"
4#include "absl/flags/flag.h"
5#include "absl/flags/reflection.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "gtest/gtest.h"
7
James Kuszmaul04a343c2023-02-20 16:38:22 -08008#include "aos/events/logging/log_writer.h"
9#include "aos/events/simulated_event_loop.h"
10#include "frc971/control_loops/drivetrain/drivetrain_test_lib.h"
11#include "frc971/control_loops/drivetrain/localizer_generated.h"
12#include "frc971/control_loops/pose.h"
13#include "frc971/vision/target_map_generated.h"
James Kuszmaul9c3db182024-02-09 22:02:18 -080014#include "frc971/vision/target_map_utils.h"
James Kuszmaul04a343c2023-02-20 16:38:22 -080015#include "y2023/constants/simulated_constants_sender.h"
16#include "y2023/control_loops/drivetrain/drivetrain_base.h"
17#include "y2023/localizer/status_generated.h"
James Kuszmaul04a343c2023-02-20 16:38:22 -080018
Austin Schuh99f7c6a2024-06-25 22:07:44 -070019ABSL_FLAG(std::string, output_folder, "",
20 "If set, logs all channels to the provided logfile.");
21ABSL_DECLARE_FLAG(double, max_distance_to_target);
James Kuszmaul04a343c2023-02-20 16:38:22 -080022
23namespace y2023::localizer::testing {
24
25using frc971::control_loops::drivetrain::Output;
26
27class LocalizerTest : public ::testing::Test {
28 protected:
29 static constexpr uint64_t kTargetId = 1;
30 LocalizerTest()
31 : configuration_(aos::configuration::ReadConfig("y2023/aos_config.json")),
32 event_loop_factory_(&configuration_.message()),
33 roborio_node_([this]() {
34 // Get the constants sent before anything else happens.
35 // It has nothing to do with the roborio node.
36 SendSimulationConstants(&event_loop_factory_, 7971,
37 "y2023/constants/test_constants.json");
38 return aos::configuration::GetNode(&configuration_.message(),
39 "roborio");
40 }()),
41 imu_node_(
42 aos::configuration::GetNode(&configuration_.message(), "imu")),
43 camera_node_(
44 aos::configuration::GetNode(&configuration_.message(), "pi1")),
45 dt_config_(frc971::control_loops::drivetrain::testing::
46 GetTestDrivetrainConfig()),
47 localizer_event_loop_(
48 event_loop_factory_.MakeEventLoop("localizer", imu_node_)),
49 localizer_(localizer_event_loop_.get(), dt_config_),
50 drivetrain_plant_event_loop_(event_loop_factory_.MakeEventLoop(
51 "drivetrain_plant", roborio_node_)),
52 drivetrain_plant_imu_event_loop_(
53 event_loop_factory_.MakeEventLoop("drivetrain_plant", imu_node_)),
54 drivetrain_plant_(drivetrain_plant_event_loop_.get(),
55 drivetrain_plant_imu_event_loop_.get(), dt_config_,
56 std::chrono::microseconds(500)),
57 roborio_test_event_loop_(
58 event_loop_factory_.MakeEventLoop("test", roborio_node_)),
59 imu_test_event_loop_(
60 event_loop_factory_.MakeEventLoop("test", imu_node_)),
61 camera_test_event_loop_(
62 event_loop_factory_.MakeEventLoop("test", camera_node_)),
63 logger_test_event_loop_(
64 event_loop_factory_.GetNodeEventLoopFactory("logger")
65 ->MakeEventLoop("test")),
66 constants_fetcher_(imu_test_event_loop_.get()),
67 output_sender_(
68 roborio_test_event_loop_->MakeSender<Output>("/drivetrain")),
69 target_sender_(
70 camera_test_event_loop_->MakeSender<frc971::vision::TargetMap>(
71 "/camera")),
72 control_sender_(roborio_test_event_loop_->MakeSender<
73 frc971::control_loops::drivetrain::LocalizerControl>(
74 "/drivetrain")),
75 output_fetcher_(
76 roborio_test_event_loop_
77 ->MakeFetcher<frc971::controls::LocalizerOutput>("/localizer")),
78 status_fetcher_(
79 imu_test_event_loop_->MakeFetcher<Status>("/localizer")) {
Austin Schuh99f7c6a2024-06-25 22:07:44 -070080 absl::SetFlag(&FLAGS_max_distance_to_target, 100.0);
James Kuszmaul04a343c2023-02-20 16:38:22 -080081 {
82 aos::TimerHandler *timer = roborio_test_event_loop_->AddTimer([this]() {
83 {
84 auto builder = output_sender_.MakeBuilder();
85 auto output_builder = builder.MakeBuilder<Output>();
86 output_builder.add_left_voltage(output_voltages_(0));
87 output_builder.add_right_voltage(output_voltages_(1));
88 builder.CheckOk(builder.Send(output_builder.Finish()));
89 }
90 });
91 roborio_test_event_loop_->OnRun([timer, this]() {
Philipp Schradera6712522023-07-05 20:25:11 -070092 timer->Schedule(roborio_test_event_loop_->monotonic_now(),
93 std::chrono::milliseconds(5));
James Kuszmaul04a343c2023-02-20 16:38:22 -080094 });
95 }
96 {
97 // Sanity check that the test calibration files look like what we
98 // expect.
99 CHECK_EQ("pi1", constants_fetcher_.constants()
100 .cameras()
101 ->Get(0)
102 ->calibration()
103 ->node_name()
104 ->string_view());
105 const Eigen::Matrix<double, 4, 4> H_robot_camera =
106 frc971::control_loops::drivetrain::FlatbufferToTransformationMatrix(
107 *constants_fetcher_.constants()
108 .cameras()
109 ->Get(0)
110 ->calibration()
111 ->fixed_extrinsics());
112
113 CHECK_EQ(kTargetId, constants_fetcher_.constants()
114 .target_map()
115 ->target_poses()
116 ->Get(0)
117 ->id());
118 const Eigen::Matrix<double, 4, 4> H_field_target = PoseToTransform(
119 constants_fetcher_.constants().target_map()->target_poses()->Get(0));
120 // For reference, the camera should pointed straight forwards on the
121 // robot, offset by 1 meter.
122 aos::TimerHandler *timer = camera_test_event_loop_->AddTimer(
123 [this, H_robot_camera, H_field_target]() {
124 if (!send_targets_) {
125 return;
126 }
127 const frc971::control_loops::Pose robot_pose(
128 {drivetrain_plant_.GetPosition().x(),
129 drivetrain_plant_.GetPosition().y(), 0.0},
milind-ub45c6d92023-03-11 18:56:15 -0800130 drivetrain_plant_.state()(2, 0) + implied_yaw_error_);
James Kuszmaul04a343c2023-02-20 16:38:22 -0800131
132 const Eigen::Matrix<double, 4, 4> H_field_camera =
133 robot_pose.AsTransformationMatrix() * H_robot_camera;
134 const Eigen::Matrix<double, 4, 4> H_camera_target =
135 H_field_camera.inverse() * H_field_target;
136
137 const Eigen::Quaterniond quat(H_camera_target.block<3, 3>(0, 0));
138 const Eigen::Vector3d translation(
139 H_camera_target.block<3, 1>(0, 3));
140
141 auto builder = target_sender_.MakeBuilder();
142 frc971::vision::Quaternion::Builder quat_builder(*builder.fbb());
143 quat_builder.add_w(quat.w());
144 quat_builder.add_x(quat.x());
145 quat_builder.add_y(quat.y());
146 quat_builder.add_z(quat.z());
147 auto quat_offset = quat_builder.Finish();
148 frc971::vision::Position::Builder position_builder(*builder.fbb());
149 position_builder.add_x(translation.x());
150 position_builder.add_y(translation.y());
151 position_builder.add_z(translation.z());
152 auto position_offset = position_builder.Finish();
153
154 frc971::vision::TargetPoseFbs::Builder target_builder(
155 *builder.fbb());
156 target_builder.add_id(send_target_id_);
157 target_builder.add_position(position_offset);
158 target_builder.add_orientation(quat_offset);
milind-u7a7f6662023-02-26 16:41:29 -0800159 target_builder.add_pose_error(pose_error_);
milind-u08fb9722023-03-25 18:23:59 -0700160 target_builder.add_pose_error_ratio(pose_error_ratio_);
James Kuszmaul04a343c2023-02-20 16:38:22 -0800161 auto target_offset = target_builder.Finish();
162
163 auto targets_offset = builder.fbb()->CreateVector({target_offset});
164 frc971::vision::TargetMap::Builder map_builder(*builder.fbb());
165 map_builder.add_target_poses(targets_offset);
166 map_builder.add_monotonic_timestamp_ns(
167 std::chrono::duration_cast<std::chrono::nanoseconds>(
168 camera_test_event_loop_->monotonic_now().time_since_epoch())
169 .count());
170
171 builder.CheckOk(builder.Send(map_builder.Finish()));
172 });
173 camera_test_event_loop_->OnRun([timer, this]() {
Philipp Schradera6712522023-07-05 20:25:11 -0700174 timer->Schedule(camera_test_event_loop_->monotonic_now(),
175 std::chrono::milliseconds(50));
James Kuszmaul04a343c2023-02-20 16:38:22 -0800176 });
177 }
178
179 localizer_control_send_timer_ =
180 roborio_test_event_loop_->AddTimer([this]() {
181 auto builder = control_sender_.MakeBuilder();
182 auto control_builder = builder.MakeBuilder<
183 frc971::control_loops::drivetrain::LocalizerControl>();
184 control_builder.add_x(localizer_control_x_);
185 control_builder.add_y(localizer_control_y_);
186 control_builder.add_theta(localizer_control_theta_);
187 control_builder.add_theta_uncertainty(0.01);
188 control_builder.add_keep_current_theta(false);
189 builder.CheckOk(builder.Send(control_builder.Finish()));
190 });
191
192 // Get things zeroed.
193 event_loop_factory_.RunFor(std::chrono::seconds(10));
194 CHECK(status_fetcher_.Fetch());
195 CHECK(status_fetcher_->imu()->zeroed());
196
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700197 if (!absl::GetFlag(FLAGS_output_folder).empty()) {
James Kuszmaul04a343c2023-02-20 16:38:22 -0800198 logger_event_loop_ =
199 event_loop_factory_.MakeEventLoop("logger", imu_node_);
200 logger_ = std::make_unique<aos::logger::Logger>(logger_event_loop_.get());
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700201 logger_->StartLoggingOnRun(absl::GetFlag(FLAGS_output_folder));
James Kuszmaul04a343c2023-02-20 16:38:22 -0800202 }
203 }
204
205 void SendLocalizerControl(double x, double y, double theta) {
206 localizer_control_x_ = x;
207 localizer_control_y_ = y;
208 localizer_control_theta_ = theta;
Philipp Schradera6712522023-07-05 20:25:11 -0700209 localizer_control_send_timer_->Schedule(
James Kuszmaul04a343c2023-02-20 16:38:22 -0800210 roborio_test_event_loop_->monotonic_now());
211 }
212 ::testing::AssertionResult IsNear(double expected, double actual,
213 double epsilon) {
214 if (std::abs(expected - actual) < epsilon) {
215 return ::testing::AssertionSuccess();
216 } else {
217 return ::testing::AssertionFailure()
218 << "Expected " << expected << " but got " << actual
219 << " with a max difference of " << epsilon
220 << " and an actual difference of " << std::abs(expected - actual);
221 }
222 }
223 ::testing::AssertionResult VerifyEstimatorAccurate(double eps) {
224 const Eigen::Matrix<double, 5, 1> true_state = drivetrain_plant_.state();
225 ::testing::AssertionResult result(true);
226 status_fetcher_.Fetch();
227 if (!(result = IsNear(status_fetcher_->state()->x(), true_state(0), eps))) {
228 return result;
229 }
230 if (!(result = IsNear(status_fetcher_->state()->y(), true_state(1), eps))) {
231 return result;
232 }
233 if (!(result =
234 IsNear(status_fetcher_->state()->theta(), true_state(2), eps))) {
235 return result;
236 }
237 return result;
238 }
239
240 aos::FlatbufferDetachedBuffer<aos::Configuration> configuration_;
241 aos::SimulatedEventLoopFactory event_loop_factory_;
242 const aos::Node *const roborio_node_;
243 const aos::Node *const imu_node_;
244 const aos::Node *const camera_node_;
245 const frc971::control_loops::drivetrain::DrivetrainConfig<double> dt_config_;
246 std::unique_ptr<aos::EventLoop> localizer_event_loop_;
247 Localizer localizer_;
248
249 std::unique_ptr<aos::EventLoop> drivetrain_plant_event_loop_;
250 std::unique_ptr<aos::EventLoop> drivetrain_plant_imu_event_loop_;
251 frc971::control_loops::drivetrain::testing::DrivetrainSimulation
252 drivetrain_plant_;
253
254 std::unique_ptr<aos::EventLoop> roborio_test_event_loop_;
255 std::unique_ptr<aos::EventLoop> imu_test_event_loop_;
256 std::unique_ptr<aos::EventLoop> camera_test_event_loop_;
257 std::unique_ptr<aos::EventLoop> logger_test_event_loop_;
258
259 frc971::constants::ConstantsFetcher<Constants> constants_fetcher_;
260
261 aos::Sender<Output> output_sender_;
262 aos::Sender<frc971::vision::TargetMap> target_sender_;
263 aos::Sender<frc971::control_loops::drivetrain::LocalizerControl>
264 control_sender_;
265 aos::Fetcher<frc971::controls::LocalizerOutput> output_fetcher_;
266 aos::Fetcher<Status> status_fetcher_;
267
268 Eigen::Vector2d output_voltages_ = Eigen::Vector2d::Zero();
269
270 aos::TimerHandler *localizer_control_send_timer_;
271
272 bool send_targets_ = false;
273
274 double localizer_control_x_ = 0.0;
275 double localizer_control_y_ = 0.0;
276 double localizer_control_theta_ = 0.0;
277
278 std::unique_ptr<aos::EventLoop> logger_event_loop_;
279 std::unique_ptr<aos::logger::Logger> logger_;
280
281 uint64_t send_target_id_ = kTargetId;
milind-u7a7f6662023-02-26 16:41:29 -0800282 double pose_error_ = 1e-7;
milind-u08fb9722023-03-25 18:23:59 -0700283 double pose_error_ratio_ = 0.1;
milind-ub45c6d92023-03-11 18:56:15 -0800284 double implied_yaw_error_ = 0.0;
James Kuszmaul04a343c2023-02-20 16:38:22 -0800285
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700286 absl::FlagSaver flag_saver_;
James Kuszmaul04a343c2023-02-20 16:38:22 -0800287};
288
289// Test a simple scenario with no errors where the robot should just drive
290// straight forwards.
291TEST_F(LocalizerTest, Nominal) {
292 output_voltages_ << 1.0, 1.0;
293 event_loop_factory_.RunFor(std::chrono::seconds(2));
294 CHECK(output_fetcher_.Fetch());
295 CHECK(status_fetcher_.Fetch());
296 // The two can be different because they may've been sent at different
297 // times.
298 EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-2);
299 EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-6);
300 EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
301 1e-6);
302 // Confirm that we did indeed drive forwards (and straight), as expected.
303 EXPECT_LT(0.1, output_fetcher_->x());
304 EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
305 EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-10);
306 EXPECT_NEAR(0.0, status_fetcher_->state()->left_voltage_error(), 1e-1);
307 EXPECT_NEAR(0.0, status_fetcher_->state()->right_voltage_error(), 1e-1);
308
309 // And check that we actually think that we are near where the simulator
310 // says we are.
311 EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
312}
313
314// Confirm that when the robot drives backwards that we localize correctly.
315TEST_F(LocalizerTest, NominalReverse) {
316 output_voltages_ << -1.0, -1.0;
317 event_loop_factory_.RunFor(std::chrono::seconds(2));
318 CHECK(output_fetcher_.Fetch());
319 CHECK(status_fetcher_.Fetch());
320 // The two can be different because they may've been sent at different
321 // times.
322 EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-2);
323 EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-6);
324 EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
325 1e-6);
326 // Confirm that we did indeed drive backwards (and straight), as expected.
327 EXPECT_GT(-0.1, output_fetcher_->x());
328 EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
329 EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-10);
330 EXPECT_NEAR(0.0, status_fetcher_->state()->left_voltage_error(), 1e-1);
331 EXPECT_NEAR(0.0, status_fetcher_->state()->right_voltage_error(), 1e-1);
332
333 // And check that we actually think that we are near where the simulator
334 // says we are.
335 EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
336}
337
338// Confirm that when the robot turns counter-clockwise that we localize
339// correctly.
340TEST_F(LocalizerTest, NominalSpinInPlace) {
341 output_voltages_ << -1.0, 1.0;
James Kuszmaul456774b2023-03-08 21:29:15 -0800342 // Go 1 ms over 2 sec to make sure we actually see relatively recent messages
343 // on each channel.
344 event_loop_factory_.RunFor(std::chrono::milliseconds(2001));
James Kuszmaul04a343c2023-02-20 16:38:22 -0800345 CHECK(output_fetcher_.Fetch());
346 CHECK(status_fetcher_.Fetch());
347 // The two can be different because they may've been sent at different
348 // times.
349 EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-6);
350 EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-6);
351 EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
352 1e-2);
353 // Confirm that we did indeed turn counter-clockwise.
354 EXPECT_NEAR(0.0, output_fetcher_->x(), 1e-10);
355 EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
356 EXPECT_LT(0.1, output_fetcher_->theta());
357 EXPECT_NEAR(0.0, status_fetcher_->state()->left_voltage_error(), 1e-1);
358 EXPECT_NEAR(0.0, status_fetcher_->state()->right_voltage_error(), 1e-1);
359
360 // And check that we actually think that we are near where the simulator
361 // says we are.
362 EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
363}
364
365// Confirm that when the robot drives in a curve that we localize
366// successfully.
367TEST_F(LocalizerTest, NominalCurve) {
368 output_voltages_ << 2.0, 3.0;
369 event_loop_factory_.RunFor(std::chrono::seconds(4));
370 CHECK(output_fetcher_.Fetch());
371 CHECK(status_fetcher_.Fetch());
372 // The two can be different because they may've been sent at different
373 // times.
374 EXPECT_NEAR(output_fetcher_->x(), status_fetcher_->state()->x(), 1e-2);
375 EXPECT_NEAR(output_fetcher_->y(), status_fetcher_->state()->y(), 1e-2);
376 EXPECT_NEAR(output_fetcher_->theta(), status_fetcher_->state()->theta(),
377 1e-2);
378 // Confirm that we did indeed drive in a rough, counter-clockwise, curve.
379 EXPECT_LT(0.1, output_fetcher_->x());
380 EXPECT_LT(0.1, output_fetcher_->y());
381 EXPECT_LT(0.1, output_fetcher_->theta());
382
383 // And check that we actually think that we are near where the simulator
384 // says we are.
385 EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
386}
387
388// Tests that, in the presence of a non-zero voltage error, that we correct
389// for it.
390TEST_F(LocalizerTest, VoltageErrorDisabled) {
391 output_voltages_ << 0.0, 0.0;
392 drivetrain_plant_.set_left_voltage_offset(2.0);
393 drivetrain_plant_.set_right_voltage_offset(2.0);
394
395 event_loop_factory_.RunFor(std::chrono::seconds(2));
396 CHECK(output_fetcher_.Fetch());
397 CHECK(status_fetcher_.Fetch());
398 // We should've just ended up driving straight forwards.
399 EXPECT_LT(0.1, output_fetcher_->x());
400 EXPECT_NEAR(0.0, output_fetcher_->y(), 1e-10);
401 EXPECT_NEAR(0.0, output_fetcher_->theta(), 1e-10);
402 EXPECT_NEAR(2.0, status_fetcher_->state()->left_voltage_error(), 1.0);
403 EXPECT_NEAR(2.0, status_fetcher_->state()->right_voltage_error(), 1.0);
404
405 // And check that we actually think that we are near where the simulator
406 // says we are.
407 EXPECT_TRUE(VerifyEstimatorAccurate(0.05));
408}
409
410// Tests that image corrections in the nominal case (no errors) causes no
411// issues.
412TEST_F(LocalizerTest, NominalImageCorrections) {
413 output_voltages_ << 3.0, 2.0;
414 send_targets_ = true;
415
416 event_loop_factory_.RunFor(std::chrono::seconds(4));
417 CHECK(status_fetcher_.Fetch());
418 EXPECT_TRUE(VerifyEstimatorAccurate(1e-2));
419 ASSERT_TRUE(status_fetcher_->has_statistics());
420 ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
421 ASSERT_EQ(status_fetcher_->statistics()->Get(0)->total_candidates(),
422 status_fetcher_->statistics()->Get(0)->total_accepted());
423 ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
424}
425
426// Tests that image corrections when there is an error at the start results
427// in us actually getting corrected over time.
428TEST_F(LocalizerTest, ImageCorrections) {
429 output_voltages_ << 0.0, 0.0;
430 drivetrain_plant_.mutable_state()->x() = 2.0;
431 drivetrain_plant_.mutable_state()->y() = 2.0;
432 SendLocalizerControl(5.0, 3.0, 0.0);
433 event_loop_factory_.RunFor(std::chrono::seconds(4));
434 CHECK(output_fetcher_.Fetch());
435 ASSERT_NEAR(5.0, output_fetcher_->x(), 1e-5);
436 ASSERT_NEAR(3.0, output_fetcher_->y(), 1e-5);
437 ASSERT_NEAR(0.0, output_fetcher_->theta(), 1e-5);
438
439 send_targets_ = true;
440
441 event_loop_factory_.RunFor(std::chrono::seconds(4));
442 CHECK(status_fetcher_.Fetch());
443 EXPECT_TRUE(VerifyEstimatorAccurate(0.1));
444 ASSERT_TRUE(status_fetcher_->has_statistics());
445 ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
446 ASSERT_EQ(status_fetcher_->statistics()->Get(0)->total_candidates(),
447 status_fetcher_->statistics()->Get(0)->total_accepted());
448 ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
449}
450
451// Tests that we correctly reject an invalid target.
452TEST_F(LocalizerTest, InvalidTargetId) {
453 output_voltages_ << 0.0, 0.0;
454 send_targets_ = true;
455 send_target_id_ = 100;
456
457 event_loop_factory_.RunFor(std::chrono::seconds(4));
458 CHECK(status_fetcher_.Fetch());
459 ASSERT_TRUE(status_fetcher_->has_statistics());
460 ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
461 ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
462 ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
463 ASSERT_EQ(status_fetcher_->statistics()
464 ->Get(0)
465 ->rejection_reasons()
466 ->Get(static_cast<size_t>(RejectionReason::NO_SUCH_TARGET))
467 ->count(),
468 status_fetcher_->statistics()->Get(0)->total_candidates());
469}
470
milind-u7a7f6662023-02-26 16:41:29 -0800471// Tests that we correctly reject a detection with a high pose error.
472TEST_F(LocalizerTest, HighPoseError) {
473 output_voltages_ << 0.0, 0.0;
474 send_targets_ = true;
475 // Send the minimum pose error to be rejected
476 constexpr double kEps = 1e-9;
477 pose_error_ = 1e-6 + kEps;
478
479 event_loop_factory_.RunFor(std::chrono::seconds(4));
480 CHECK(status_fetcher_.Fetch());
481 ASSERT_TRUE(status_fetcher_->has_statistics());
482 ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
483 ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
484 ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
485 ASSERT_EQ(status_fetcher_->statistics()
486 ->Get(0)
487 ->rejection_reasons()
488 ->Get(static_cast<size_t>(RejectionReason::HIGH_POSE_ERROR))
489 ->count(),
490 status_fetcher_->statistics()->Get(0)->total_candidates());
491}
492
milind-ub45c6d92023-03-11 18:56:15 -0800493// Tests that we correctly reject a detection with a high implied yaw error.
494TEST_F(LocalizerTest, HighImpliedYawError) {
495 output_voltages_ << 0.0, 0.0;
496 send_targets_ = true;
497 implied_yaw_error_ = 31.0 * M_PI / 180.0;
498
499 event_loop_factory_.RunFor(std::chrono::seconds(4));
500 CHECK(status_fetcher_.Fetch());
501 ASSERT_TRUE(status_fetcher_->has_statistics());
502 ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
503 ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
504 ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
505 ASSERT_EQ(
506 status_fetcher_->statistics()
507 ->Get(0)
508 ->rejection_reasons()
509 ->Get(static_cast<size_t>(RejectionReason::HIGH_IMPLIED_YAW_ERROR))
510 ->count(),
511 status_fetcher_->statistics()->Get(0)->total_candidates());
512}
513
milind-u08fb9722023-03-25 18:23:59 -0700514// Tests that we correctly reject a detection with a high pose error ratio.
515TEST_F(LocalizerTest, HighPoseErrorRatio) {
516 output_voltages_ << 0.0, 0.0;
517 send_targets_ = true;
518 // Send the minimum pose error to be rejected
519 constexpr double kEps = 1e-9;
520 pose_error_ratio_ = 0.4 + kEps;
521
522 event_loop_factory_.RunFor(std::chrono::seconds(4));
523 CHECK(status_fetcher_.Fetch());
524 ASSERT_TRUE(status_fetcher_->has_statistics());
525 ASSERT_EQ(4u /* number of cameras */, status_fetcher_->statistics()->size());
526 ASSERT_EQ(0, status_fetcher_->statistics()->Get(0)->total_accepted());
527 ASSERT_LT(10, status_fetcher_->statistics()->Get(0)->total_candidates());
528 ASSERT_EQ(
529 status_fetcher_->statistics()
530 ->Get(0)
531 ->rejection_reasons()
532 ->Get(static_cast<size_t>(RejectionReason::HIGH_POSE_ERROR_RATIO))
533 ->count(),
534 status_fetcher_->statistics()->Get(0)->total_candidates());
535}
536
James Kuszmaul04a343c2023-02-20 16:38:22 -0800537} // namespace y2023::localizer::testing