Skip to content

Commit

Permalink
Refactor ExponentialMovingAverageFilter to use a templated input type.
Browse files Browse the repository at this point in the history
This requires a pull request be merged to ArduinoSTL before it will
build on AVR-based platforms (e.g. UNO).
mike-matera/ArduinoSTL#52
  • Loading branch information
ademuri committed Feb 8, 2020
1 parent b9a07b9 commit 45187b4
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

const uint8_t kInputPin = 0;
const uint8_t kLedPin = 13;
ExponentialMovingAverageFilter<uint32_t> *input;
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input;

void setup() {
pinMode(kInputPin, INPUT_PULLUP);
input = new ExponentialMovingAverageFilter<uint32_t>(filter_functions::ForAnalogRead<kInputPin>(), 255);
input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(filter_functions::ForAnalogRead<kInputPin>(), 255);
input->SetLogToSerial(true);
input->SetMinRunInterval(50);

Expand Down
71 changes: 40 additions & 31 deletions src/exponential-moving-average-filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "filter.h"

#include <type_traits>

// An exponential moving average filter. This uses only integer (32-bit) math.
// This supports up to 24-bit inputs.
//
Expand All @@ -13,47 +15,54 @@
//
// An alpha of 255 means that the filter returns the current value of the input.
// An alpha of 0 means the filtered value changes very slowly.
template <typename OutputType>
class ExponentialMovingAverageFilter : public Filter<uint32_t, OutputType> {
using Filter<uint32_t, OutputType>::sensor_value_;
template <typename InputType, typename OutputType>
class ExponentialMovingAverageFilter : public Filter<InputType, OutputType> {
using Filter<InputType, OutputType>::sensor_value_;

public:
ExponentialMovingAverageFilter(uint32_t (*const ReadFromSensor)(),
uint8_t alpha);
ExponentialMovingAverageFilter(uint32_t (*const ReadFromSensor)(),
uint8_t alpha,
OutputType (*Convert)(uint32_t input));
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
InputType alpha);
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
InputType alpha,
OutputType (*Convert)(InputType input));

protected:
uint32_t DoRun() override;
InputType DoRun() override;

private:
uint32_t average_ = 0;
InputType average_ = 0;

const uint8_t alpha_;
const InputType alpha_;
};

template <typename OutputType>
ExponentialMovingAverageFilter<OutputType>::ExponentialMovingAverageFilter(
uint32_t (*const ReadFromSensor)(), uint8_t alpha)
: Filter<uint32_t, OutputType>(ReadFromSensor), alpha_(alpha) {}

template <typename OutputType>
ExponentialMovingAverageFilter<OutputType>::ExponentialMovingAverageFilter(
uint32_t (*const ReadFromSensor)(), uint8_t alpha,
OutputType (*Convert)(uint32_t input))
: Filter<uint32_t, OutputType>(ReadFromSensor, Convert), alpha_(alpha) {}

template <typename OutputType>
uint32_t ExponentialMovingAverageFilter<OutputType>::DoRun() {
uint32_t old_average = average_;
average_ = (sensor_value_ * (alpha_ + 1) + (average_ * (255 - alpha_))) / 256;
if (old_average == average_ && sensor_value_ != average_) {
if (sensor_value_ > average_) {
average_++;
} else if (sensor_value_ < average_) {
average_--;
template <typename InputType, typename OutputType>
ExponentialMovingAverageFilter<InputType, OutputType>::
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
InputType alpha)
: Filter<InputType, OutputType>(ReadFromSensor), alpha_(alpha) {}

template <typename InputType, typename OutputType>
ExponentialMovingAverageFilter<InputType, OutputType>::
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
InputType alpha,
OutputType (*Convert)(InputType input))
: Filter<InputType, OutputType>(ReadFromSensor, Convert), alpha_(alpha) {}

template <typename InputType, typename OutputType>
InputType ExponentialMovingAverageFilter<InputType, OutputType>::DoRun() {
InputType old_average = average_;
if (std::is_integral<InputType>::value) {
average_ =
(sensor_value_ * (alpha_ + 1) + (average_ * (255 - alpha_))) / 256;
if (old_average == average_ && sensor_value_ != average_) {
if (sensor_value_ > average_) {
average_++;
} else if (sensor_value_ < average_) {
average_--;
}
}
} else {
average_ = (sensor_value_ * (alpha_) + (average_ * (1.0 - alpha_)));
}
return average_;
}
Expand Down
42 changes: 42 additions & 0 deletions test/exponential-moving-average-filter-float-test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "gtest/gtest.h"

#include <cstdio>
#include "../src/exponential-moving-average-filter.h"
#include "run-data-test.h"

namespace exponential_moving_average_filter_test {

TEST(ExponentialMovingAverageFloatFilter, alpha_half) {
ExponentialMovingAverageFilter<float, float> *filter = new ExponentialMovingAverageFilter<float, float>(floatRead, 0.5);
std::vector<InputOutput<float, float>> data = {
{0, 10, 0},
{1.0, 100, 0.4, 1.0},
{1.0, 100, 1.0},
{0, 100, 0, 0.6},
{0, 100, 0, 0.001},
};
RunDataTest(filter, data, setFloatRead);
}

TEST(ExponentialMovingAverageFloatFilter, alpha_full) {
ExponentialMovingAverageFilter<float, float> *filter = new ExponentialMovingAverageFilter<float, float>(floatRead, 1.0);
std::vector<InputOutput<float, float>> data = {
{0, 10, 0},
{10.0, 100, 10.0},
{0, 100, 0},
};
RunDataTest(filter, data, setFloatRead);
}

TEST(ExponentialMovingAverageFloatFilter, alpha_low) {
ExponentialMovingAverageFilter<float, float> *filter = new ExponentialMovingAverageFilter<float, float>(floatRead, 0.001);
std::vector<InputOutput<float, float>> data = {
{0, 10, 0},
{1.0, 500, 0, 0.5},
{1.0, 5000, 0, 1.0},
{1.0, 100, 0.99, 1.0},
};
RunDataTest(filter, data, setFloatRead);
}

}
12 changes: 6 additions & 6 deletions test/exponential-moving-average-filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace exponential_moving_average_filter_test {

TEST(ExponentialMovingAverageFilter, alpha_half) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 128);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 128);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 100, 4, 1024},
Expand All @@ -19,7 +19,7 @@ TEST(ExponentialMovingAverageFilter, alpha_half) {
}

TEST(ExponentialMovingAverageFilter, alpha_full) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 100, 1024},
Expand All @@ -29,7 +29,7 @@ TEST(ExponentialMovingAverageFilter, alpha_full) {
}

TEST(ExponentialMovingAverageFilter, alpha_low) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 0);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 0);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 10, 4, 50},
Expand All @@ -43,7 +43,7 @@ TEST(ExponentialMovingAverageFilter, alpha_low) {
}

TEST(ExponentialMovingAverageFilter, impulse) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 127);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 127);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 1, 512},
Expand Down Expand Up @@ -77,7 +77,7 @@ float analogInToVoltage(uint32_t analogIn) {
}

TEST(ExponentialMovingAverageFilter, convert) {
ExponentialMovingAverageFilter<float> *filter = new ExponentialMovingAverageFilter<float>(analogRead, 127, analogInToVoltage);
ExponentialMovingAverageFilter<uint32_t, float> *filter = new ExponentialMovingAverageFilter<uint32_t, float>(analogRead, 127, analogInToVoltage);
std::vector<InputOutput<uint32_t, float>> data = {
{0, 10, 0.0},
{1023, 100, 0.0, 3.3},
Expand All @@ -90,7 +90,7 @@ TEST(ExponentialMovingAverageFilter, filter_range) {
// Tests that the filter supports 24-bit filters without overflow.
// Note: 2 ^ 24 = 16777216

ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 127);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 127);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{16777215, 500, 0, 16777215},
{16777215, 100, 16777215},
Expand Down
6 changes: 3 additions & 3 deletions test/filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace filter_test {

TEST(Filter, SetMinRunInterval_default) {
// Filter that returns the sensor value immediately
ExponentialMovingAverageFilter<uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{1, 1, 1},
{2, 1, 2},
Expand All @@ -21,7 +21,7 @@ TEST(Filter, SetMinRunInterval_default) {

TEST(Filter, SetMinRunInterval_10) {
// Filter that returns the sensor value immediately
ExponentialMovingAverageFilter<uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
input->SetMinRunInterval(10);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{1, 1, 1},
Expand All @@ -36,7 +36,7 @@ TEST(Filter, SetMinRunInterval_10) {
}

TEST(Filter, SetMinRunInterval_updates) {
ExponentialMovingAverageFilter<uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
input->SetMinRunInterval(10);
input->SetMillis(0);
analogReadValue = 1;
Expand Down

0 comments on commit 45187b4

Please sign in to comment.