Skip to content

Commit

Permalink
Template the input type of median filter.
Browse files Browse the repository at this point in the history
This allows specifying the input type.
  • Loading branch information
ademuri committed Feb 8, 2020
1 parent 2bace1c commit b9a07b9
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 71 deletions.
4 changes: 2 additions & 2 deletions examples/median-filter/median-filter.ino
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;
MedianFilter<uint32_t, 5> *filter;
MedianFilter<uint32_t, uint32_t, 5> *filter;

void setup() {
pinMode(kInputPin, INPUT_PULLUP);
filter = new MedianFilter<uint32_t, 5>(filter_functions::ForAnalogRead<kInputPin>());
filter = new MedianFilter<uint32_t, uint32_t, 5>(filter_functions::ForAnalogRead<kInputPin>());
filter->SetLogToSerial(true);

pinMode(kLedPin, OUTPUT);
Expand Down
42 changes: 22 additions & 20 deletions src/median-filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
#include "filter.h"

namespace median_filter {
template <typename ValueType>
struct ValuePoint {
uint32_t value;
ValueType value;
uint8_t index;
};
} // namespace median_filter
Expand All @@ -28,21 +29,21 @@ struct ValuePoint {
// iterate through the buffer at most once.
//
// For even sizes, this returns the lower median.
template <typename OutputType, uint8_t size>
class MedianFilter : public Filter<uint32_t, OutputType> {
using Filter<uint32_t, OutputType>::sensor_value_;
template <typename InputType, typename OutputType, uint8_t size>
class MedianFilter : public Filter<InputType, OutputType> {
using Filter<InputType, OutputType>::sensor_value_;

public:
MedianFilter(uint32_t (*const ReadFromSensor)());
MedianFilter(uint32_t (*const ReadFromSensor)(),
OutputType (*Convert)(uint32_t input));
MedianFilter(InputType (*const ReadFromSensor)());
MedianFilter(InputType (*const ReadFromSensor)(),
OutputType (*Convert)(InputType input));

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

private:
// Sorted list of recent values
std::list<median_filter::ValuePoint> history_;
std::list<median_filter::ValuePoint<InputType>> history_;

// The index of the next value to insert. Used to keep <size> elements in the
// list.
Expand All @@ -53,17 +54,18 @@ class MedianFilter : public Filter<uint32_t, OutputType> {
bool history_full_ = false;
};

template <typename OutputType, uint8_t size>
MedianFilter<OutputType, size>::MedianFilter(uint32_t (*const ReadFromSensor)())
: Filter<uint32_t, OutputType>(ReadFromSensor) {}
template <typename InputType, typename OutputType, uint8_t size>
MedianFilter<InputType, OutputType, size>::MedianFilter(
InputType (*const ReadFromSensor)())
: Filter<InputType, OutputType>(ReadFromSensor) {}

template <typename OutputType, uint8_t size>
MedianFilter<OutputType, size>::MedianFilter(
uint32_t (*const ReadFromSensor)(), OutputType (*Convert)(uint32_t input))
: Filter<uint32_t, OutputType>(ReadFromSensor, Convert) {}
template <typename InputType, typename OutputType, uint8_t size>
MedianFilter<InputType, OutputType, size>::MedianFilter(
InputType (*const ReadFromSensor)(), OutputType (*Convert)(InputType input))
: Filter<InputType, OutputType>(ReadFromSensor, Convert) {}

template <typename OutputType, uint8_t size>
uint32_t MedianFilter<OutputType, size>::DoRun() {
template <typename InputType, typename OutputType, uint8_t size>
InputType MedianFilter<InputType, OutputType, size>::DoRun() {
// Initial case: history has 0 or 1 elements
if (history_.empty()) {
history_.push_back({sensor_value_, ring_buffer_index_});
Expand All @@ -86,7 +88,7 @@ uint32_t MedianFilter<OutputType, size>::DoRun() {
history_full_ = true;
}

std::list<median_filter::ValuePoint>::iterator it = history_.begin();
auto it = history_.begin();
uint8_t i = 0;
bool inserted = false;
while (it != history_.end()) {
Expand Down Expand Up @@ -121,7 +123,7 @@ uint32_t MedianFilter<OutputType, size>::DoRun() {

// typical case: history is full
uint8_t element_index_to_remove = ring_buffer_index_;
std::list<median_filter::ValuePoint>::iterator it = history_.begin();
auto it = history_.begin();
uint8_t i = 0;
bool inserted = false;
bool removed = false;
Expand Down
24 changes: 12 additions & 12 deletions test/exponential-moving-average-filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,29 @@ namespace exponential_moving_average_filter_test {

TEST(ExponentialMovingAverageFilter, alpha_half) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 128);
std::vector<InputOutput<uint32_t>> data = {
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 100, 4, 1024},
{1024, 100, 1024},
{0, 100, 0, 1020},
{0, 100, 0},
};
RunDataTest(filter, data);
RunDataTest(filter, data, setAnalogRead);
}

TEST(ExponentialMovingAverageFilter, alpha_full) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
std::vector<InputOutput<uint32_t>> data = {
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 100, 1024},
{0, 100, 0},
};
RunDataTest(filter, data);
RunDataTest(filter, data, setAnalogRead);
}

TEST(ExponentialMovingAverageFilter, alpha_low) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 0);
std::vector<InputOutput<uint32_t>> data = {
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 10, 4, 50},
{1024, 990, 4, 1024},
Expand All @@ -39,12 +39,12 @@ TEST(ExponentialMovingAverageFilter, alpha_low) {
{0, 1000, 0, 1020},
{0, 100, 0},
};
RunDataTest(filter, data);
RunDataTest(filter, data, setAnalogRead);
}

TEST(ExponentialMovingAverageFilter, impulse) {
ExponentialMovingAverageFilter<uint32_t> *filter = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 127);
std::vector<InputOutput<uint32_t>> data = {
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{0, 10, 0},
{1024, 1, 512},
{0, 1, 256},
Expand All @@ -69,7 +69,7 @@ TEST(ExponentialMovingAverageFilter, impulse) {
{1024, 1, 1023},
{1024, 1, 1024},
};
RunDataTest(filter, data);
RunDataTest(filter, data, setAnalogRead);
}

float analogInToVoltage(uint32_t analogIn) {
Expand All @@ -78,24 +78,24 @@ float analogInToVoltage(uint32_t analogIn) {

TEST(ExponentialMovingAverageFilter, convert) {
ExponentialMovingAverageFilter<float> *filter = new ExponentialMovingAverageFilter<float>(analogRead, 127, analogInToVoltage);
std::vector<InputOutput<float>> data = {
std::vector<InputOutput<uint32_t, float>> data = {
{0, 10, 0.0},
{1023, 100, 0.0, 3.3},
{1023, 10, 3.3},
};
RunDataTest(filter, data);
RunDataTest(filter, data, setAnalogRead);
}

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);
std::vector<InputOutput<uint32_t>> data = {
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{16777215, 500, 0, 16777215},
{16777215, 100, 16777215},
};
RunDataTest(filter, data);
RunDataTest(filter, data, setAnalogRead);
}

}
8 changes: 4 additions & 4 deletions test/filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ namespace filter_test {
TEST(Filter, SetMinRunInterval_default) {
// Filter that returns the sensor value immediately
ExponentialMovingAverageFilter<uint32_t> *input = new ExponentialMovingAverageFilter<uint32_t>(analogRead, 255);
std::vector<InputOutput<uint32_t>> data = {
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{1, 1, 1},
{2, 1, 2},
{3, 1, 3},
{4, 1, 4},
{5, 1, 5},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

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

Expand All @@ -32,7 +32,7 @@ TEST(Filter, SetMinRunInterval_10) {

{3, 10, 3},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(Filter, SetMinRunInterval_updates) {
Expand Down
60 changes: 36 additions & 24 deletions test/median-filter-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,54 @@
namespace median_filter_input_test {

TEST(MedianFilter, loading_in_order) {
MedianFilter<uint32_t, 5> *input = new MedianFilter<uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 5> *input = new MedianFilter<uint32_t, uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{100, 1, 100},
{200, 1, 100},
{300, 1, 200},
{400, 1, 200},
{500, 1, 300},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, loading_reverse_order) {
MedianFilter<uint32_t, 5> *input = new MedianFilter<uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 5> *input = new MedianFilter<uint32_t, uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{500, 1, 500},
{400, 1, 400},
{300, 1, 400},
{200, 1, 300},
{100, 1, 300},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, loading_random_order) {
MedianFilter<uint32_t, 5> *input = new MedianFilter<uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 5> *input = new MedianFilter<uint32_t, uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{500, 1, 500},
{100, 1, 100},
{400, 1, 400},
{300, 1, 300},
{200, 1, 300},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, steady_state_step_function) {
MedianFilter<uint32_t, 5> *input = new MedianFilter<uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 5> *input = new MedianFilter<uint32_t, uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{100, 5, 100},
{200, 2, 100},
{200, 10, 200},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, steady_state_impulse) {
MedianFilter<uint32_t, 5> *input = new MedianFilter<uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 5> *input = new MedianFilter<uint32_t, uint32_t, 5>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{100, 10, 100},
{200, 2, 100},
{100, 10, 100},
Expand All @@ -63,43 +63,55 @@ TEST(MedianFilter, steady_state_impulse) {
{100, 2, 200},
{100, 10, 100},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

// Not a reasonable size, but behavior should be consistent
TEST(MedianFilter, size_2) {
MedianFilter<uint32_t, 2> *input = new MedianFilter<uint32_t, 2>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 2> *input = new MedianFilter<uint32_t, uint32_t, 2>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{100, 1, 100},
{200, 1, 100},
{200, 2, 200},
{100, 5, 100},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, size_3) {
MedianFilter<uint32_t, 3> *input = new MedianFilter<uint32_t, 3>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 3> *input = new MedianFilter<uint32_t, uint32_t, 3>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{100, 2, 100},
{200, 1, 100},
{200, 2, 200},
{100, 1, 200},
{100, 5, 100},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, size_255) {
MedianFilter<uint32_t, 255> *input = new MedianFilter<uint32_t, 255>(analogRead);
std::vector<InputOutput<uint32_t>> data = {
MedianFilter<uint32_t, uint32_t, 255> *input = new MedianFilter<uint32_t, uint32_t, 255>(analogRead);
std::vector<InputOutput<uint32_t, uint32_t>> data = {
{100, 255, 100},
{200, 127, 100},
{200, 1, 200},
{100, 127, 200},
{100, 1, 100},
};
RunDataTest(input, data);
RunDataTest(input, data, setAnalogRead);
}

TEST(MedianFilter, float) {
MedianFilter<float, float, 255> *input = new MedianFilter<float, float, 255>(floatRead);
std::vector<InputOutput<float, float>> data = {
{1.0, 255, 1.0},
{2.0, 127, 1.0},
{2.0, 1, 2.0},
{1.0, 127, 2.0},
{1.0, 1, 1.0},
};
RunDataTest(input, data, setFloatRead);
}

}
13 changes: 13 additions & 0 deletions test/run-data-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,16 @@ uint32_t analogReadValue = 0;
uint32_t analogRead() {
return analogReadValue;
}

void setAnalogRead(uint32_t value) {
analogReadValue = value;
}

float floatReadValue = 0.0;
float floatRead() {
return floatReadValue;
}

void setFloatRead(float value) {
floatReadValue = value;
}
Loading

0 comments on commit b9a07b9

Please sign in to comment.