diff --git a/examples/median-filter/median-filter.ino b/examples/median-filter/median-filter.ino index e1686e5..3b5e0f6 100644 --- a/examples/median-filter/median-filter.ino +++ b/examples/median-filter/median-filter.ino @@ -2,11 +2,11 @@ const uint8_t kInputPin = 0; const uint8_t kLedPin = 13; -MedianFilter *filter; +MedianFilter *filter; void setup() { pinMode(kInputPin, INPUT_PULLUP); - filter = new MedianFilter(filter_functions::ForAnalogRead()); + filter = new MedianFilter(filter_functions::ForAnalogRead()); filter->SetLogToSerial(true); pinMode(kLedPin, OUTPUT); diff --git a/src/median-filter.h b/src/median-filter.h index c19de3c..7ecfb6c 100644 --- a/src/median-filter.h +++ b/src/median-filter.h @@ -14,8 +14,9 @@ #include "filter.h" namespace median_filter { +template struct ValuePoint { - uint32_t value; + ValueType value; uint8_t index; }; } // namespace median_filter @@ -28,21 +29,21 @@ struct ValuePoint { // iterate through the buffer at most once. // // For even sizes, this returns the lower median. -template -class MedianFilter : public Filter { - using Filter::sensor_value_; +template +class MedianFilter : public Filter { + using Filter::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 history_; + std::list> history_; // The index of the next value to insert. Used to keep elements in the // list. @@ -53,17 +54,18 @@ class MedianFilter : public Filter { bool history_full_ = false; }; -template -MedianFilter::MedianFilter(uint32_t (*const ReadFromSensor)()) - : Filter(ReadFromSensor) {} +template +MedianFilter::MedianFilter( + InputType (*const ReadFromSensor)()) + : Filter(ReadFromSensor) {} -template -MedianFilter::MedianFilter( - uint32_t (*const ReadFromSensor)(), OutputType (*Convert)(uint32_t input)) - : Filter(ReadFromSensor, Convert) {} +template +MedianFilter::MedianFilter( + InputType (*const ReadFromSensor)(), OutputType (*Convert)(InputType input)) + : Filter(ReadFromSensor, Convert) {} -template -uint32_t MedianFilter::DoRun() { +template +InputType MedianFilter::DoRun() { // Initial case: history has 0 or 1 elements if (history_.empty()) { history_.push_back({sensor_value_, ring_buffer_index_}); @@ -86,7 +88,7 @@ uint32_t MedianFilter::DoRun() { history_full_ = true; } - std::list::iterator it = history_.begin(); + auto it = history_.begin(); uint8_t i = 0; bool inserted = false; while (it != history_.end()) { @@ -121,7 +123,7 @@ uint32_t MedianFilter::DoRun() { // typical case: history is full uint8_t element_index_to_remove = ring_buffer_index_; - std::list::iterator it = history_.begin(); + auto it = history_.begin(); uint8_t i = 0; bool inserted = false; bool removed = false; diff --git a/test/exponential-moving-average-filter-test.cpp b/test/exponential-moving-average-filter-test.cpp index 9a96830..fdda68d 100644 --- a/test/exponential-moving-average-filter-test.cpp +++ b/test/exponential-moving-average-filter-test.cpp @@ -8,29 +8,29 @@ namespace exponential_moving_average_filter_test { TEST(ExponentialMovingAverageFilter, alpha_half) { ExponentialMovingAverageFilter *filter = new ExponentialMovingAverageFilter(analogRead, 128); - std::vector> data = { + std::vector> 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 *filter = new ExponentialMovingAverageFilter(analogRead, 255); - std::vector> data = { + std::vector> data = { {0, 10, 0}, {1024, 100, 1024}, {0, 100, 0}, }; - RunDataTest(filter, data); + RunDataTest(filter, data, setAnalogRead); } TEST(ExponentialMovingAverageFilter, alpha_low) { ExponentialMovingAverageFilter *filter = new ExponentialMovingAverageFilter(analogRead, 0); - std::vector> data = { + std::vector> data = { {0, 10, 0}, {1024, 10, 4, 50}, {1024, 990, 4, 1024}, @@ -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 *filter = new ExponentialMovingAverageFilter(analogRead, 127); - std::vector> data = { + std::vector> data = { {0, 10, 0}, {1024, 1, 512}, {0, 1, 256}, @@ -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) { @@ -78,12 +78,12 @@ float analogInToVoltage(uint32_t analogIn) { TEST(ExponentialMovingAverageFilter, convert) { ExponentialMovingAverageFilter *filter = new ExponentialMovingAverageFilter(analogRead, 127, analogInToVoltage); - std::vector> data = { + std::vector> 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) { @@ -91,11 +91,11 @@ TEST(ExponentialMovingAverageFilter, filter_range) { // Note: 2 ^ 24 = 16777216 ExponentialMovingAverageFilter *filter = new ExponentialMovingAverageFilter(analogRead, 127); - std::vector> data = { + std::vector> data = { {16777215, 500, 0, 16777215}, {16777215, 100, 16777215}, }; - RunDataTest(filter, data); + RunDataTest(filter, data, setAnalogRead); } } diff --git a/test/filter-test.cpp b/test/filter-test.cpp index 3625b0d..b948e77 100644 --- a/test/filter-test.cpp +++ b/test/filter-test.cpp @@ -9,21 +9,21 @@ namespace filter_test { TEST(Filter, SetMinRunInterval_default) { // Filter that returns the sensor value immediately ExponentialMovingAverageFilter *input = new ExponentialMovingAverageFilter(analogRead, 255); - std::vector> data = { + std::vector> 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 *input = new ExponentialMovingAverageFilter(analogRead, 255); input->SetMinRunInterval(10); - std::vector> data = { + std::vector> data = { {1, 1, 1}, {2, 9, 1}, @@ -32,7 +32,7 @@ TEST(Filter, SetMinRunInterval_10) { {3, 10, 3}, }; - RunDataTest(input, data); + RunDataTest(input, data, setAnalogRead); } TEST(Filter, SetMinRunInterval_updates) { diff --git a/test/median-filter-test.cpp b/test/median-filter-test.cpp index b83fea2..563b41f 100644 --- a/test/median-filter-test.cpp +++ b/test/median-filter-test.cpp @@ -7,54 +7,54 @@ namespace median_filter_input_test { TEST(MedianFilter, loading_in_order) { - MedianFilter *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> 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 *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> 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 *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> 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 *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> data = { {100, 5, 100}, {200, 2, 100}, {200, 10, 200}, }; - RunDataTest(input, data); + RunDataTest(input, data, setAnalogRead); } TEST(MedianFilter, steady_state_impulse) { - MedianFilter *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> data = { {100, 10, 100}, {200, 2, 100}, {100, 10, 100}, @@ -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 *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> 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 *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> 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 *input = new MedianFilter(analogRead); - std::vector> data = { + MedianFilter *input = new MedianFilter(analogRead); + std::vector> 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 *input = new MedianFilter(floatRead); + std::vector> 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); } } diff --git a/test/run-data-test.cpp b/test/run-data-test.cpp index cae3d04..9d33c4a 100644 --- a/test/run-data-test.cpp +++ b/test/run-data-test.cpp @@ -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; +} diff --git a/test/run-data-test.h b/test/run-data-test.h index a420097..743e3be 100644 --- a/test/run-data-test.h +++ b/test/run-data-test.h @@ -8,21 +8,20 @@ #include "../src/filter.h" -template +template struct InputOutput { - uint32_t input; + I input; uint32_t duration_millis; O lower_bound; O upper_bound; - InputOutput(uint32_t input, uint32_t duration_millis, O exact_value) + InputOutput(I input, uint32_t duration_millis, O exact_value) : input(input), duration_millis(duration_millis), lower_bound(exact_value), upper_bound(exact_value) {} - InputOutput(uint32_t input, uint32_t duration_millis, O lower_bound, - O upper_bound) + InputOutput(I input, uint32_t duration_millis, O lower_bound, O upper_bound) : input(input), duration_millis(duration_millis), lower_bound(lower_bound), @@ -31,10 +30,15 @@ struct InputOutput { extern uint32_t analogReadValue; extern uint32_t analogRead(); +extern void setAnalogRead(uint32_t value); -template -void RunDataTest(Filter* filter, - std::vector> data) { +extern float floatReadValue; +extern float floatRead(); +extern void setFloatRead(float value); + +template +void RunDataTest(Filter* filter, std::vector> data, + void (*setSensorValue)(I value)) { uint32_t millis = 0; uint32_t point_index = 0; for (auto point : data) { @@ -44,7 +48,7 @@ void RunDataTest(Filter* filter, << point.input << ")"; filter->SetMillis(millis); - analogReadValue = point.input; + setSensorValue(point.input); filter->Run(); EXPECT_GE(filter->GetFilteredValue(), point.lower_bound)