add tap_detection_algo unit tests
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
class accelerometer {
|
class accelerometer {
|
||||||
public:
|
public:
|
||||||
struct data{
|
struct data {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float z;
|
float z;
|
||||||
@@ -13,25 +13,49 @@ public:
|
|||||||
c_50_hz,
|
c_50_hz,
|
||||||
c_100_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;
|
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 {
|
class adxl_345 : public accelerometer {
|
||||||
public:
|
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");
|
printf("adxl_345: setting sampling rate\r\n");
|
||||||
|
i2c_.write();
|
||||||
|
return error::ok;
|
||||||
}
|
}
|
||||||
data get_data() override {
|
data get_data() override {
|
||||||
return data{0.02f, 0.981f, 0.03f};
|
return data{0.02f, 0.981f, 0.03f};
|
||||||
}
|
}
|
||||||
|
private:
|
||||||
|
i2c &i2c_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class tap_detection_algo {
|
class tap_detection_algo {
|
||||||
public:
|
public:
|
||||||
tap_detection_algo(accelerometer &accel) :
|
tap_detection_algo(accelerometer &accel) :
|
||||||
accel_(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 () {
|
bool run () {
|
||||||
auto accel_data = accel_.get_data();
|
auto accel_data = accel_.get_data();
|
||||||
@@ -45,7 +69,8 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
adxl_345 accel;
|
i2c_stm32 i2c1;
|
||||||
|
adxl_345 accel(i2c1);
|
||||||
tap_detection_algo algo(accel);
|
tap_detection_algo algo(accel);
|
||||||
algo.run();
|
algo.run();
|
||||||
|
|
||||||
|
|||||||
131
Chapter18/tap_detection_algo.cpp
Normal file
131
Chapter18/tap_detection_algo.cpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<data>& 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<data> samples_;
|
||||||
|
size_t index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test 1: No tap is detected if there is no significant change between samples.
|
||||||
|
TEST(TapDetectionAlgoTest, NoTapWhenNoSignificantChange) {
|
||||||
|
std::vector<accelerometer::data> 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<accelerometer::data> 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<accelerometer::data> 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user