From 6e8b983f598303d5c33285576fc76da3edeff7c6 Mon Sep 17 00:00:00 2001 From: myles-parfeniuk Date: Sun, 17 Nov 2024 15:01:33 -0800 Subject: [PATCH] CallbackTests, Stability, Activity, and ActivityEnable enums --- README.md | 4 +- include/BNO08x.hpp | 18 +--- include/BNO08xTestHelper.hpp | 71 ++++++++++++-- include/BNO08xTestSuite.hpp | 17 +++- include/BNO08x_global_types.hpp | 39 ++++++++ source/BNO08x.cpp | 22 +++-- test/CallbackTests.cpp | 167 ++++++++++++++++++++++++++++++++ test/SingleReportTests.cpp | 10 +- 8 files changed, 309 insertions(+), 39 deletions(-) create mode 100644 test/CallbackTests.cpp diff --git a/README.md b/README.md index 6e46c84..7afcbb5 100644 --- a/README.md +++ b/README.md @@ -241,9 +241,9 @@ It can be used to verify some of the basic features of a BNO08x device.

(back to top)

### Adding Tests -Tests are implemented with the [esp-idf unity unit testing component](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/unit-tests.html). +Tests are implemented with the [unity unit testing component](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/unit-tests.html). -To add a test, create a new .cpp file, or modify one of the existing ones in `esp32_BNO08x/test/()`. +To add a test, create a new .cpp file, or modify one of the existing ones in `esp32_BNO08x/test/`. Follow the existing test structure as an example, use the `TEST_CASE(){}` macro. Any tests added will automatically be detected at build time. diff --git a/include/BNO08x.hpp b/include/BNO08x.hpp index f8033ea..5b2cd46 100644 --- a/include/BNO08x.hpp +++ b/include/BNO08x.hpp @@ -69,7 +69,7 @@ class BNO08x void enable_tap_detector(uint32_t time_between_reports); void enable_step_counter(uint32_t time_between_reports); void enable_stability_classifier(uint32_t time_between_reports); - void enable_activity_classifier(uint32_t time_between_reports, uint32_t activities_to_enable, uint8_t (&activity_confidence_vals)[9]); + void enable_activity_classifier(uint32_t time_between_reports, ActivityClassifierEnable activities_to_enable, uint8_t (&activity_confidence_vals)[9]); void enable_raw_mems_gyro(uint32_t time_between_reports); void enable_raw_mems_accelerometer(uint32_t time_between_reports); void enable_raw_mems_magnetometer(uint32_t time_between_reports); @@ -179,8 +179,8 @@ class BNO08x uint8_t get_tap_detector(); uint16_t get_step_count(); - int8_t get_stability_classifier(); - uint8_t get_activity_classifier(); + Stability get_stability_classifier(); + Activity get_activity_classifier(); // Metadata functions int16_t get_Q1(uint16_t record_ID); @@ -203,18 +203,6 @@ class BNO08x static const constexpr uint16_t FRS_RECORD_ID_ROTATION_VECTOR = 0xE30BU; ///< Rotation vector record ID, to be passed in metadata functions like get_Q1() - // Activity classifier bits - static const constexpr uint16_t ACTIVITY_CLASSIFIER_UNKNOWN_EN = (1U << 0U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_IN_VEHICLE_EN = (1U << 1U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_ON_BICYCLE_EN = (1U << 2U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_ON_FOOT_EN = (1U << 3U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_STILL_EN = (1U << 4U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_TILTING_EN = (1U << 5U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_WALKING_EN = (1U << 6U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_RUNNING_EN = (1U << 7U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_ON_STAIRS_EN = (1U << 8U); - static const constexpr uint16_t ACTIVITY_CLASSIFIER_ALL_EN = 0x1FU; - static const constexpr uint8_t TARE_AXIS_ALL = 0x07U; ///< Tare all axes (used with tare now command) static const constexpr uint8_t TARE_AXIS_Z = 0x04U; ///< Tar yaw axis only (used with tare now command) diff --git a/include/BNO08xTestHelper.hpp b/include/BNO08xTestHelper.hpp index 6364131..8f728ff 100644 --- a/include/BNO08xTestHelper.hpp +++ b/include/BNO08xTestHelper.hpp @@ -7,7 +7,6 @@ #include "stdio.h" #include "BNO08x.hpp" - /** * @class BNO08xTestHelper * @brief BNO08x unit test helper class. @@ -73,8 +72,8 @@ class BNO08xTestHelper uint16_t raw_mems_gyro_z; uint16_t step_count; - uint8_t stability_classifier; - uint8_t activity_classifier; + Stability stability_classifier; + Activity activity_classifier; } imu_report_data_t; @@ -602,8 +601,8 @@ class BNO08xTestHelper test_imu->tap_detector = TEST_VAL_UINT8; test_imu->step_count = TEST_VAL_UINT16; - test_imu->stability_classifier = TEST_VAL_UINT8; - test_imu->activity_classifier = TEST_VAL_UINT8; + test_imu->stability_classifier = static_cast(Stability::UNDEFINED); + test_imu->activity_classifier = static_cast(Activity::UNDEFINED); test_imu->mems_raw_accel_X = TEST_VAL_UINT16; test_imu->mems_raw_accel_Y = TEST_VAL_UINT16; @@ -638,7 +637,67 @@ class BNO08xTestHelper case BNO08xAccuracy::UNDEFINED: return "UNDEFINED"; default: - return "UNKNOWN"; // For undefined cases or future-proofing + return "INVALID"; } }; + + /** + * @brief Converts Stability enum class object to string. + * + * @param stability Stability object to convert to string. + * + * @return The resulting string conversion. + */ + static const char* BNO08xStability_to_str(Stability stability) + { + switch (stability) + { + case Stability::UNKNOWN: + return "UNKNOWN"; + case Stability::ON_TABLE: + return "ON TABLE"; + case Stability::STATIONARY: + return "STATIONARY"; + case Stability::UNDEFINED: + return "UNDEFINED"; + default: + return "INVALID"; + } + } + + /** + * @brief Converts Activity enum class object to string. + * + * @param activity Activity object to convert to string. + * + * @return The resulting string conversion. + */ + static const char* BNO08xActivity_to_str(Activity activity) + { + switch (activity) + { + case Activity::UNKNOWN: + return "UNKNOWN"; + case Activity::IN_VEHICLE: + return "IN VEHICLE"; + case Activity::ON_BICYCLE: + return "ON BICYCLE"; + case Activity::ON_FOOT: + return "ON FOOT"; + case Activity::STILL: + return "STILL"; + case Activity::TILTING: + return "TILTING"; + case Activity::WALKING: + return "WALKING"; + case Activity::RUNNING: + return "RUNNING"; + case Activity::ON_STAIRS: + return "ON STAIRS"; + case Activity::UNDEFINED: + return "UNDEFINED"; + default: + return "INVALID"; + } + } }; \ No newline at end of file diff --git a/include/BNO08xTestSuite.hpp b/include/BNO08xTestSuite.hpp index 9f93250..f20f0ae 100644 --- a/include/BNO08xTestSuite.hpp +++ b/include/BNO08xTestSuite.hpp @@ -13,7 +13,6 @@ #include "unity.h" #include "BNO08xTestHelper.hpp" - /** * @class BNO08xTestSuite * @brief BNO08x unit test launch point class. @@ -38,6 +37,7 @@ class BNO08xTestSuite run_init_deinit_tests(false); run_single_report_tests(false); run_multi_report_tests(false); + run_callback_tests(false); UNITY_END(); } @@ -86,4 +86,19 @@ class BNO08xTestSuite print_end_tests_banner("multi_report_tests"); } + + static void run_callback_tests(bool call_unity_end_begin = true) + { + print_begin_tests_banner("run_callback_tests"); + + if (call_unity_end_begin) + UNITY_BEGIN(); + + unity_run_tests_by_tag("[CallbackTests]", false); + + if (call_unity_end_begin) + UNITY_END(); + + print_end_tests_banner("run_callback_tests"); + } }; \ No newline at end of file diff --git a/include/BNO08x_global_types.hpp b/include/BNO08x_global_types.hpp index e84f34b..6bda742 100644 --- a/include/BNO08x_global_types.hpp +++ b/include/BNO08x_global_types.hpp @@ -30,6 +30,45 @@ enum class BNO08xResetReason }; using IMUResetReason = BNO08xResetReason; // legacy version compatibility +/// @brief Activity Classifier enable bits passed to enable_activity_classifier() +enum class ActivityClassifierEnable +{ + UNKNOWN = (1U << 0U), + IN_VEHICLE = (1U << 1U), + ON_BICYCLE = (1U << 2U), + ON_FOOT = (1U << 3U), + STILL = (1U << 4U), + TILTING = (1U << 5U), + WALKING = (1U << 6U), + RUNNING = (1U << 7U), + ON_STAIRS = (1U << 8U), + ALL = 0x1FU +}; + +/// @brief Activity states returned from get_activity_classifier() +enum class Activity +{ + UNKNOWN = 0, // 0 = unknown + IN_VEHICLE = 1, // 1 = in vehicle + ON_BICYCLE = 2, // 2 = on bicycle + ON_FOOT = 3, // 3 = on foot + STILL = 4, // 4 = still + TILTING = 5, // 5 = tilting + WALKING = 6, // 6 = walking + RUNNING = 7, // 7 = running + ON_STAIRS = 8, // 8 = on stairs + UNDEFINED = 9 // used for unit tests +}; + +/// @brief Stability states returned from get_stability_classifier() +enum class Stability +{ + UNKNOWN = 0, // 0 = unknown + ON_TABLE = 1, // 1 = on table + STATIONARY = 2, // 2 = stationary + UNDEFINED = 3 // used for unit tests +}; + /// @brief IMU configuration settings passed into constructor typedef struct bno08x_config_t { diff --git a/source/BNO08x.cpp b/source/BNO08x.cpp index b1bea04..8b43499 100644 --- a/source/BNO08x.cpp +++ b/source/BNO08x.cpp @@ -2173,10 +2173,12 @@ void BNO08x::enable_stability_classifier(uint32_t time_between_reports) * @param activity_confidence_vals Returned activity level confidences. * @return void, nothing to return */ -void BNO08x::enable_activity_classifier(uint32_t time_between_reports, uint32_t activities_to_enable, uint8_t (&activity_confidence_vals)[9]) +void BNO08x::enable_activity_classifier( + uint32_t time_between_reports, ActivityClassifierEnable activities_to_enable, uint8_t (&activity_confidence_vals)[9]) { activity_confidences = activity_confidence_vals; // Store pointer to array - enable_report(SENSOR_REPORT_ID_PERSONAL_ACTIVITY_CLASSIFIER, time_between_reports, EVT_GRP_RPT_ACTIVITY_CLASSIFIER_BIT, activities_to_enable); + enable_report(SENSOR_REPORT_ID_PERSONAL_ACTIVITY_CLASSIFIER, time_between_reports, EVT_GRP_RPT_ACTIVITY_CLASSIFIER_BIT, + static_cast(activities_to_enable)); } /** @@ -3301,9 +3303,9 @@ uint16_t BNO08x::get_step_count() * * @return The current stability (0 = unknown, 1 = on table, 2 = stationary) */ -int8_t BNO08x::get_stability_classifier() +Stability BNO08x::get_stability_classifier() { - return stability_classifier; + return static_cast(stability_classifier); } /** @@ -3320,9 +3322,9 @@ int8_t BNO08x::get_stability_classifier() * 7 = runnning * 8 = on stairs */ -uint8_t BNO08x::get_activity_classifier() +Activity BNO08x::get_activity_classifier() { - return activity_classifier; + return static_cast(activity_classifier); } /** @@ -3792,9 +3794,9 @@ void BNO08x::data_proc_task() } /** - * @brief Launches spi_task and data_proc_task on constructor call. + * @brief Launches spi_task and data_proc_task on constructor call. * - * @return ESP_OK if tasks successfully created. + * @return ESP_OK if tasks successfully created. */ esp_err_t BNO08x::launch_tasks() { @@ -3834,9 +3836,9 @@ esp_err_t BNO08x::launch_tasks() } /** - * @brief Deletes spi_task and data_proc_task safely on deconstructor call. + * @brief Deletes spi_task and data_proc_task safely on deconstructor call. * - * @return ESP_OK if tasks successfully deleted. + * @return ESP_OK if tasks successfully deleted. */ esp_err_t BNO08x::kill_all_tasks() { diff --git a/test/CallbackTests.cpp b/test/CallbackTests.cpp new file mode 100644 index 0000000..f80538f --- /dev/null +++ b/test/CallbackTests.cpp @@ -0,0 +1,167 @@ +#include "unity.h" +#include "../include/BNO08xTestHelper.hpp" + +TEST_CASE("BNO08x Driver Creation for [CallbackTests] Tests", "[CallbackTests]") +{ + const constexpr char* TEST_TAG = "BNO08x Driver Creation for [CallbackTests] Tests"; + BNO08x* imu = nullptr; + + BNO08xTestHelper::print_test_msg(TEST_TAG, "Creating & initializing BNO08x driver."); + BNO08xTestHelper::create_test_imu(); + imu = BNO08xTestHelper::get_test_imu(); + + // ensure IMU initialized successfully + TEST_ASSERT_EQUAL(true, imu->initialize()); +} + +TEST_CASE("Callback Receives New Data (Single Report)", "[CallbackTests]") +{ + const constexpr char* TEST_TAG = "Callback Receives New Data (Single Report)"; + BNO08x* imu = nullptr; + BNO08xTestHelper::imu_report_data_t report_data; + BNO08xTestHelper::imu_report_data_t prev_report_data; + const constexpr uint8_t RX_REPORT_TRIAL_CNT = 5; + const constexpr uint32_t REPORT_PERIOD = 100000UL; // 100ms + bool new_data = false; + char msg_buff[200] = {}; + + imu = BNO08xTestHelper::get_test_imu(); + + // reset all data used in report test + BNO08xTestHelper::reset_all_imu_data_to_test_defaults(); + BNO08xTestHelper::update_report_data(&report_data); + + // register a callback and check for new data within it + imu->register_cb( + [&imu, &new_data, &report_data, &prev_report_data, &msg_buff]() + { + static int cb_execution_cnt = 0; + + /* callback code */ + + cb_execution_cnt++; + // check if new data was received from enabled report(s) + BNO08xTestHelper::update_report_data(&report_data); + + if (BNO08xTestHelper::accelerometer_data_is_new(&report_data, &prev_report_data)) + { + new_data = true; + + sprintf(msg_buff, + "Rx Data Trial %d Success: AngularAccel: aX: %.2lf accel aY: %.2lf accel aZ: " + "%.2lf Accuracy %s", + cb_execution_cnt, report_data.accel_x, report_data.accel_y, report_data.accel_z, + BNO08xTestHelper::BNO08xAccuracy_to_str(report_data.accel_accuracy)); + + BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff); + } + }); + + BNO08xTestHelper::print_test_msg(TEST_TAG, "Enabling report(s) and checking for new data through subscribed callback."); + imu->enable_accelerometer(REPORT_PERIOD); + + for (int i = 0; i < RX_REPORT_TRIAL_CNT; i++) + { + if (imu->data_available()) + { + // callbacks should ALWAYS execute before data_available returns true, therefore something has gone wrong if new_data is not set to true + TEST_ASSERT_EQUAL(true, new_data); + + // reset all data used in report test + new_data = false; + BNO08xTestHelper::reset_all_imu_data_to_test_defaults(); + BNO08xTestHelper::update_report_data(&report_data); + } + } + + BNO08xTestHelper::print_test_msg(TEST_TAG, "Test complete, disabling report(s)."); + imu->disable_accelerometer(); +} + +TEST_CASE("Callback Receives New Data (Dual Report)", "[CallbackTests]") +{ + const constexpr char* TEST_TAG = "Callback Receives New Data (Dual Report)"; + BNO08x* imu = nullptr; + BNO08xTestHelper::imu_report_data_t report_data; + BNO08xTestHelper::imu_report_data_t prev_report_data; + const constexpr uint8_t ENABLED_REPORT_CNT = 2; + const constexpr uint8_t RX_REPORT_TRIAL_CNT = 2 * ENABLED_REPORT_CNT; + const constexpr uint32_t REPORT_PERIOD = 100000UL; // 100ms + bool new_data[2] = {false, false}; + char msg_buff[200] = {}; + + imu = BNO08xTestHelper::get_test_imu(); + + // reset all data used in report test + BNO08xTestHelper::reset_all_imu_data_to_test_defaults(); + BNO08xTestHelper::update_report_data(&report_data); + + // register a callback and check for new data within it + imu->register_cb( + [&imu, &new_data, &report_data, &prev_report_data, &msg_buff]() + { + static int cb_execution_cnt = 0; + + /* callback code */ + + cb_execution_cnt++; + // check if new data was received from enabled report(s) + BNO08xTestHelper::update_report_data(&report_data); + + if (BNO08xTestHelper::accelerometer_data_is_new(&report_data, &prev_report_data)) + { + new_data[0] = true; + + sprintf(msg_buff, + "Rx Data Trial %d Success: AngularAccel: aX: %.2lf accel aY: %.2lf accel aZ: " + "%.2lf Accuracy %s", + cb_execution_cnt, report_data.accel_x, report_data.accel_y, report_data.accel_z, + BNO08xTestHelper::BNO08xAccuracy_to_str(report_data.accel_accuracy)); + + BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff); + } + + if (BNO08xTestHelper::linear_accelerometer_data_is_new(&report_data, &prev_report_data)) + { + new_data[1] = true; + + sprintf(msg_buff, + "Rx Data Trial %d Success: LinearAccel: laX: %.2lf laY: %.2lf laZ: " + "%.2lf Accuracy: %s", + (cb_execution_cnt + 1), report_data.lin_accel_x, report_data.lin_accel_y, report_data.lin_accel_z, + BNO08xTestHelper::BNO08xAccuracy_to_str(report_data.lin_accel_accuracy)); + + BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff); + } + }); + + BNO08xTestHelper::print_test_msg(TEST_TAG, "Enabling report(s) and checking for new data through subscribed callback."); + imu->enable_accelerometer(REPORT_PERIOD); + imu->enable_linear_accelerometer(REPORT_PERIOD); + + for (int i = 0; i < RX_REPORT_TRIAL_CNT; i++) + { + if (imu->data_available()) + { + // callbacks should ALWAYS execute before data_available returns true + BNO08xTestHelper::reset_all_imu_data_to_test_defaults(); + BNO08xTestHelper::update_report_data(&report_data); + } + } + + // check that the callback received new data for all reports after all trials have completed + TEST_ASSERT_EQUAL(true, new_data[0]); + TEST_ASSERT_EQUAL(true, new_data[1]); + + BNO08xTestHelper::print_test_msg(TEST_TAG, "Test complete, disabling report(s)."); + imu->disable_accelerometer(); + imu->disable_linear_accelerometer(); +} + +TEST_CASE("BNO08x Driver Cleanup for [CallbackTests] Tests", "[CallbackTests]") +{ + const constexpr char* TEST_TAG = "BNO08x Driver Cleanup for [CallbackTests] Tests"; + BNO08xTestHelper::print_test_msg(TEST_TAG, "Destroying BNO08x Driver."); + + BNO08xTestHelper::destroy_test_imu(); +} diff --git a/test/SingleReportTests.cpp b/test/SingleReportTests.cpp index 310ee75..f409e4d 100644 --- a/test/SingleReportTests.cpp +++ b/test/SingleReportTests.cpp @@ -1150,7 +1150,7 @@ TEST_CASE("Enable/Disable Stability Classifier", "[SingleReportEnableDisable]") // assert that new data from respective report has been received TEST_ASSERT_EQUAL(true, new_data); - sprintf(msg_buff, "Rx Data Trial %d Success: StabilityClassifier: %d", (i + 1), report_data.stability_classifier); + sprintf(msg_buff, "Rx Data Trial %d Success: StabilityClassifier: %s", (i + 1), BNO08xTestHelper::BNO08xStability_to_str(report_data.stability_classifier)); BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff); @@ -1180,7 +1180,7 @@ TEST_CASE("Enable/Disable Stability Classifier", "[SingleReportEnableDisable]") // assert that no new data from respective report has been received TEST_ASSERT_NOT_EQUAL(true, new_data); - sprintf(msg_buff, "No Rx Data Trial %d Success: StabilityClassifierDefault: %d", (i + 1), report_data.stability_classifier); + sprintf(msg_buff, "No Rx Data Trial %d Success: StabilityClassifierDefault: %s", (i + 1), BNO08xTestHelper::BNO08xStability_to_str(report_data.stability_classifier)); BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff); @@ -1214,7 +1214,7 @@ TEST_CASE("Enable/Disable Activity Classifier", "[SingleReportEnableDisable]") BNO08xTestHelper::print_test_msg(TEST_TAG, "Report enabled testing phase started."); /*enable respective report to test and ensure it reports new data */ - imu->enable_activity_classifier(5*REPORT_PERIOD, BNO08x::ACTIVITY_CLASSIFIER_ALL_EN, activity_confidence_vals); + imu->enable_activity_classifier(5*REPORT_PERIOD, ActivityClassifierEnable::ALL, activity_confidence_vals); for (int i = 0; i < RX_REPORT_TRIAL_CNT; i++) { @@ -1232,7 +1232,7 @@ TEST_CASE("Enable/Disable Activity Classifier", "[SingleReportEnableDisable]") // assert that new data from respective report has been received TEST_ASSERT_EQUAL(true, new_data); - sprintf(msg_buff, "Rx Data Trial %d Success: ActivityClassifier: %d", (i + 1), report_data.activity_classifier); + sprintf(msg_buff, "Rx Data Trial %d Success: ActivityClassifier: %s", (i + 1), BNO08xTestHelper::BNO08xActivity_to_str(report_data.activity_classifier)); BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff); @@ -1262,7 +1262,7 @@ TEST_CASE("Enable/Disable Activity Classifier", "[SingleReportEnableDisable]") // assert that no new data from respective report has been received TEST_ASSERT_NOT_EQUAL(true, new_data); - sprintf(msg_buff, "No Rx Data Trial %d Success: ActivityClassifierDefault: %d", (i + 1), report_data.activity_classifier); + sprintf(msg_buff, "No Rx Data Trial %d Success: ActivityClassifierDefault: %s", (i + 1), BNO08xTestHelper::BNO08xActivity_to_str(report_data.activity_classifier)); BNO08xTestHelper::print_test_msg(TEST_TAG, msg_buff);