diff --git a/Chapter18/accelerometer_interface.cpp b/Chapter18/accelerometer_interface.cpp index af37787..3525837 100644 --- a/Chapter18/accelerometer_interface.cpp +++ b/Chapter18/accelerometer_interface.cpp @@ -3,7 +3,7 @@ class accelerometer { public: - struct data{ + struct data { float x; float y; float z; @@ -13,25 +13,49 @@ public: c_50_hz, c_100_hz, }; - virtual void set_sampling_rate(sampling_rate) = 0; + enum error { + ok, + not_supported + }; + virtual error set_sampling_rate(sampling_rate) = 0; virtual data get_data() = 0; }; +class i2c { +public: + virtual void write() = 0; +}; + +class i2c_stm32 : public i2c { +public: + void write() override { + printf("i2c::write...\r\n"); + } +}; + class adxl_345 : public accelerometer { public: - void set_sampling_rate(sampling_rate) override { + adxl_345(i2c &i2c_obj) : i2c_(i2c_obj) {} + error set_sampling_rate(sampling_rate) override { printf("adxl_345: setting sampling rate\r\n"); + i2c_.write(); + return error::ok; } data get_data() override { return data{0.02f, 0.981f, 0.03f}; } +private: + i2c &i2c_; }; class tap_detection_algo { public: tap_detection_algo(accelerometer &accel) : accel_(accel) { - accel_.set_sampling_rate(accelerometer::sampling_rate::c_100_hz); + auto err = accel_.set_sampling_rate(accelerometer::sampling_rate::c_100_hz); + if(err == accelerometer::error::not_supported) { + // try another sampling rate and adapt + } } bool run () { auto accel_data = accel_.get_data(); @@ -45,7 +69,8 @@ private: }; int main() { - adxl_345 accel; + i2c_stm32 i2c1; + adxl_345 accel(i2c1); tap_detection_algo algo(accel); algo.run(); diff --git a/Chapter18/tap_detection_algo.cpp b/Chapter18/tap_detection_algo.cpp new file mode 100644 index 0000000..7aac6be --- /dev/null +++ b/Chapter18/tap_detection_algo.cpp @@ -0,0 +1,131 @@ +#include +#include +#include + +class accelerometer { +public: + struct data { + float x; + float y; + float z; + }; + + enum class sampling_rate { + c_20_hz, + c_50_hz, + c_100_hz, + }; + + enum error { + ok, + not_supported + }; + + virtual error set_sampling_rate(sampling_rate) = 0; + virtual data get_data() = 0; +}; + +class tap_detection_algo { +public: + tap_detection_algo(accelerometer &accel) + : accel_(accel), first_sample_(true) {} + + bool run() { + auto current = accel_.get_data(); + + if (first_sample_) { + prev_ = current; + first_sample_ = false; + return false; + } + + bool tap = (std::fabs(current.x - prev_.x) > c_threshold) || + (std::fabs(current.y - prev_.y) > c_threshold) || + (std::fabs(current.z - prev_.z) > c_threshold); + prev_ = current; + return tap; + } + +private: + static constexpr float c_threshold = 0.5f; + accelerometer &accel_; + accelerometer::data prev_; + bool first_sample_ = true; +}; + +class fake_accel : public accelerometer { +public: + fake_accel(const std::vector& samples) + : samples_(samples), index_(0) {} + + error set_sampling_rate(sampling_rate) override { + return error::ok; + } + + data get_data() override { + if (index_ < samples_.size()) { + return samples_[index_++]; + } + return samples_.back(); + } + +private: + std::vector samples_; + size_t index_; +}; + +// Test 1: No tap is detected if there is no significant change between samples. +TEST(TapDetectionAlgoTest, NoTapWhenNoSignificantChange) { + std::vector samples = { + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f} + }; + fake_accel fakeAccel(samples); + tap_detection_algo algo(fakeAccel); + + // The first call initializes the previous reading. + EXPECT_FALSE(algo.run()); + // The subsequent calls show no significant change. + EXPECT_FALSE(algo.run()); + EXPECT_FALSE(algo.run()); +} + +// Test 2: A tap is detected when there is a sudden change in the readings. +TEST(TapDetectionAlgoTest, DetectTapOnSuddenChange) { + std::vector samples = { + {0.0f, 1.0f, 0.0f}, // initial reading + {0.0f, 1.0f, 0.0f}, // no change -> false + {0.0f, 2.0f, 0.0f} // significant change on y axis (delta = 1.0 > 0.5) -> true + }; + fake_accel fakeAccel(samples); + tap_detection_algo algo(fakeAccel); + + EXPECT_FALSE(algo.run()); + EXPECT_FALSE(algo.run()); + EXPECT_TRUE(algo.run()); +} + +// Test 3: Detect multiple tap events in a sequence. +TEST(TapDetectionAlgoTest, MultipleTapsDetection) { + std::vector samples = { + {0.0f, 1.0f, 0.0f}, // initial reading + {0.0f, 1.0f, 0.0f}, // no change -> false + {0.0f, 2.0f, 0.0f}, // tap detected (change: 1.0 > threshold) + {0.0f, 2.1f, 0.0f}, // small change -> false + {0.0f, 3.0f, 0.0f} // tap detected (change: 0.9 > threshold) + }; + fake_accel fakeAccel(samples); + tap_detection_algo algo(fakeAccel); + + EXPECT_FALSE(algo.run()); + EXPECT_FALSE(algo.run()); + EXPECT_TRUE(algo.run()); + EXPECT_FALSE(algo.run()); + EXPECT_TRUE(algo.run()); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file