Skip to content

Commit 45187b4

Browse files
committed
Refactor ExponentialMovingAverageFilter to use a templated input type.
This requires a pull request be merged to ArduinoSTL before it will build on AVR-based platforms (e.g. UNO). mike-matera/ArduinoSTL#52
1 parent b9a07b9 commit 45187b4

File tree

5 files changed

+93
-42
lines changed

5 files changed

+93
-42
lines changed

examples/exponential-moving-average/exponential-moving-average.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
const uint8_t kInputPin = 0;
44
const uint8_t kLedPin = 13;
5-
ExponentialMovingAverageFilter<uint32_t> *input;
5+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input;
66

77
void setup() {
88
pinMode(kInputPin, INPUT_PULLUP);
9-
input = new ExponentialMovingAverageFilter<uint32_t>(filter_functions::ForAnalogRead<kInputPin>(), 255);
9+
input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(filter_functions::ForAnalogRead<kInputPin>(), 255);
1010
input->SetLogToSerial(true);
1111
input->SetMinRunInterval(50);
1212

src/exponential-moving-average-filter.h

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "filter.h"
55

6+
#include <type_traits>
7+
68
// An exponential moving average filter. This uses only integer (32-bit) math.
79
// This supports up to 24-bit inputs.
810
//
@@ -13,47 +15,54 @@
1315
//
1416
// An alpha of 255 means that the filter returns the current value of the input.
1517
// An alpha of 0 means the filtered value changes very slowly.
16-
template <typename OutputType>
17-
class ExponentialMovingAverageFilter : public Filter<uint32_t, OutputType> {
18-
using Filter<uint32_t, OutputType>::sensor_value_;
18+
template <typename InputType, typename OutputType>
19+
class ExponentialMovingAverageFilter : public Filter<InputType, OutputType> {
20+
using Filter<InputType, OutputType>::sensor_value_;
1921

2022
public:
21-
ExponentialMovingAverageFilter(uint32_t (*const ReadFromSensor)(),
22-
uint8_t alpha);
23-
ExponentialMovingAverageFilter(uint32_t (*const ReadFromSensor)(),
24-
uint8_t alpha,
25-
OutputType (*Convert)(uint32_t input));
23+
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
24+
InputType alpha);
25+
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
26+
InputType alpha,
27+
OutputType (*Convert)(InputType input));
2628

2729
protected:
28-
uint32_t DoRun() override;
30+
InputType DoRun() override;
2931

3032
private:
31-
uint32_t average_ = 0;
33+
InputType average_ = 0;
3234

33-
const uint8_t alpha_;
35+
const InputType alpha_;
3436
};
3537

36-
template <typename OutputType>
37-
ExponentialMovingAverageFilter<OutputType>::ExponentialMovingAverageFilter(
38-
uint32_t (*const ReadFromSensor)(), uint8_t alpha)
39-
: Filter<uint32_t, OutputType>(ReadFromSensor), alpha_(alpha) {}
40-
41-
template <typename OutputType>
42-
ExponentialMovingAverageFilter<OutputType>::ExponentialMovingAverageFilter(
43-
uint32_t (*const ReadFromSensor)(), uint8_t alpha,
44-
OutputType (*Convert)(uint32_t input))
45-
: Filter<uint32_t, OutputType>(ReadFromSensor, Convert), alpha_(alpha) {}
46-
47-
template <typename OutputType>
48-
uint32_t ExponentialMovingAverageFilter<OutputType>::DoRun() {
49-
uint32_t old_average = average_;
50-
average_ = (sensor_value_ * (alpha_ + 1) + (average_ * (255 - alpha_))) / 256;
51-
if (old_average == average_ && sensor_value_ != average_) {
52-
if (sensor_value_ > average_) {
53-
average_++;
54-
} else if (sensor_value_ < average_) {
55-
average_--;
38+
template <typename InputType, typename OutputType>
39+
ExponentialMovingAverageFilter<InputType, OutputType>::
40+
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
41+
InputType alpha)
42+
: Filter<InputType, OutputType>(ReadFromSensor), alpha_(alpha) {}
43+
44+
template <typename InputType, typename OutputType>
45+
ExponentialMovingAverageFilter<InputType, OutputType>::
46+
ExponentialMovingAverageFilter(InputType (*const ReadFromSensor)(),
47+
InputType alpha,
48+
OutputType (*Convert)(InputType input))
49+
: Filter<InputType, OutputType>(ReadFromSensor, Convert), alpha_(alpha) {}
50+
51+
template <typename InputType, typename OutputType>
52+
InputType ExponentialMovingAverageFilter<InputType, OutputType>::DoRun() {
53+
InputType old_average = average_;
54+
if (std::is_integral<InputType>::value) {
55+
average_ =
56+
(sensor_value_ * (alpha_ + 1) + (average_ * (255 - alpha_))) / 256;
57+
if (old_average == average_ && sensor_value_ != average_) {
58+
if (sensor_value_ > average_) {
59+
average_++;
60+
} else if (sensor_value_ < average_) {
61+
average_--;
62+
}
5663
}
64+
} else {
65+
average_ = (sensor_value_ * (alpha_) + (average_ * (1.0 - alpha_)));
5766
}
5867
return average_;
5968
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "gtest/gtest.h"
2+
3+
#include <cstdio>
4+
#include "../src/exponential-moving-average-filter.h"
5+
#include "run-data-test.h"
6+
7+
namespace exponential_moving_average_filter_test {
8+
9+
TEST(ExponentialMovingAverageFloatFilter, alpha_half) {
10+
ExponentialMovingAverageFilter<float, float> *filter = new ExponentialMovingAverageFilter<float, float>(floatRead, 0.5);
11+
std::vector<InputOutput<float, float>> data = {
12+
{0, 10, 0},
13+
{1.0, 100, 0.4, 1.0},
14+
{1.0, 100, 1.0},
15+
{0, 100, 0, 0.6},
16+
{0, 100, 0, 0.001},
17+
};
18+
RunDataTest(filter, data, setFloatRead);
19+
}
20+
21+
TEST(ExponentialMovingAverageFloatFilter, alpha_full) {
22+
ExponentialMovingAverageFilter<float, float> *filter = new ExponentialMovingAverageFilter<float, float>(floatRead, 1.0);
23+
std::vector<InputOutput<float, float>> data = {
24+
{0, 10, 0},
25+
{10.0, 100, 10.0},
26+
{0, 100, 0},
27+
};
28+
RunDataTest(filter, data, setFloatRead);
29+
}
30+
31+
TEST(ExponentialMovingAverageFloatFilter, alpha_low) {
32+
ExponentialMovingAverageFilter<float, float> *filter = new ExponentialMovingAverageFilter<float, float>(floatRead, 0.001);
33+
std::vector<InputOutput<float, float>> data = {
34+
{0, 10, 0},
35+
{1.0, 500, 0, 0.5},
36+
{1.0, 5000, 0, 1.0},
37+
{1.0, 100, 0.99, 1.0},
38+
};
39+
RunDataTest(filter, data, setFloatRead);
40+
}
41+
42+
}

test/exponential-moving-average-filter-test.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace exponential_moving_average_filter_test {
88

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

2121
TEST(ExponentialMovingAverageFilter, alpha_full) {
22-
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
22+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
2323
std::vector<InputOutput<uint32_t, uint32_t>> data = {
2424
{0, 10, 0},
2525
{1024, 100, 1024},
@@ -29,7 +29,7 @@ TEST(ExponentialMovingAverageFilter, alpha_full) {
2929
}
3030

3131
TEST(ExponentialMovingAverageFilter, alpha_low) {
32-
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 0);
32+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 0);
3333
std::vector<InputOutput<uint32_t, uint32_t>> data = {
3434
{0, 10, 0},
3535
{1024, 10, 4, 50},
@@ -43,7 +43,7 @@ TEST(ExponentialMovingAverageFilter, alpha_low) {
4343
}
4444

4545
TEST(ExponentialMovingAverageFilter, impulse) {
46-
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 127);
46+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 127);
4747
std::vector<InputOutput<uint32_t, uint32_t>> data = {
4848
{0, 10, 0},
4949
{1024, 1, 512},
@@ -77,7 +77,7 @@ float analogInToVoltage(uint32_t analogIn) {
7777
}
7878

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

93-
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 127);
93+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 127);
9494
std::vector<InputOutput<uint32_t, uint32_t>> data = {
9595
{16777215, 500, 0, 16777215},
9696
{16777215, 100, 16777215},

test/filter-test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace filter_test {
88

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

2222
TEST(Filter, SetMinRunInterval_10) {
2323
// Filter that returns the sensor value immediately
24-
ExponentialMovingAverageFilter<uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
24+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
2525
input->SetMinRunInterval(10);
2626
std::vector<InputOutput<uint32_t, uint32_t>> data = {
2727
{1, 1, 1},
@@ -36,7 +36,7 @@ TEST(Filter, SetMinRunInterval_10) {
3636
}
3737

3838
TEST(Filter, SetMinRunInterval_updates) {
39-
ExponentialMovingAverageFilter<uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
39+
ExponentialMovingAverageFilter<uint32_t, uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t, uint32_t>(analogRead, 255);
4040
input->SetMinRunInterval(10);
4141
input->SetMillis(0);
4242
analogReadValue = 1;

0 commit comments

Comments
 (0)