Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions src/sensesp/transforms/moving_average.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,19 @@ MovingAverage::MovingAverage(int sample_size, float multiplier,
}

void MovingAverage::set(const float& input) {
// So the first value to be included in the average doesn't default to 0.0
if (!initialized_) {
// Seed the whole buffer with the first value so the average doesn't start
// out diluted by zeros.
buf_.assign(sample_size_, input);
output_ = input;
sum_ = static_cast<float>(sample_size_) * input;
initialized_ = true;
} else {
// Subtract 1/nth of the oldest value and add 1/nth of the newest value
output_ += -multiplier_ * buf_[ptr_] / sample_size_;
output_ += multiplier_ * input / sample_size_;

// Save the most recent input, then advance to the next storage location.
// When storage location n is reached, start over again at 0.
// Swap the oldest buffered value out of the running sum for the newest.
sum_ += input - buf_[ptr_];
buf_[ptr_] = input;
ptr_ = (ptr_ + 1) % sample_size_;
}
output_ = multiplier_ * sum_ / sample_size_;
notify();
}

Expand Down
1 change: 1 addition & 0 deletions src/sensesp/transforms/moving_average.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class MovingAverage : public FloatTransform {
int ptr_ = 0;
int sample_size_;
float multiplier_;
float sum_ = 0;
bool initialized_;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* @file moving_average_multiplier_test.cpp
* @brief Regression tests for MovingAverage's multiplier (#1003).
*
* The documented contract is y = multiplier * (1/n) * sum(last n inputs):
* the multiplier scales the averaged output, including the seeded first
* sample and a steady input stream.
*/

#include <Arduino.h>

#include "sensesp/system/lambda_consumer.h"
#include "sensesp/transforms/moving_average.h"
#include "unity.h"

using namespace sensesp;

void test_multiplier_scales_seeded_first_sample() {
MovingAverage ma(2, 2.0); // output = 2.0 * mean

float received = -1.0f;
LambdaConsumer<float> consumer([&received](float v) { received = v; });
ma.connect_to(&consumer);

// First sample seeds the whole buffer (mean == input), scaled by 2.0.
ma.set(10.0f);
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 20.0f, received);
}

void test_multiplier_applies_to_steady_input() {
MovingAverage ma(4, 3.0); // output = 3.0 * mean

float received = -1.0f;
LambdaConsumer<float> consumer([&received](float v) { received = v; });
ma.connect_to(&consumer);

// A constant input of 5.0 has mean 5.0; the multiplier must still apply.
for (int i = 0; i < 6; i++) {
ma.set(5.0f);
}
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 15.0f, received);
}

void test_multiplier_tracks_windowed_mean() {
MovingAverage ma(2, 2.0);

float received = -1.0f;
LambdaConsumer<float> consumer([&received](float v) { received = v; });
ma.connect_to(&consumer);

ma.set(10.0f); // buffer [10,10], mean 10 -> 20
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 20.0f, received);

ma.set(20.0f); // buffer [20,10], mean 15 -> 30
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 30.0f, received);

ma.set(30.0f); // buffer [20,30], mean 25 -> 50
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 50.0f, received);
}

void test_default_multiplier_is_plain_average() {
MovingAverage ma(4, 1.0);

float received = -1.0f;
LambdaConsumer<float> consumer([&received](float v) { received = v; });
ma.connect_to(&consumer);

// Buffer initializes to all 4.0; push three 8.0s: (8+8+8+4)/4 = 7.0.
ma.set(4.0f);
ma.set(8.0f);
ma.set(8.0f);
ma.set(8.0f);
TEST_ASSERT_FLOAT_WITHIN(0.0001f, 7.0f, received);
}

void setup() {
delay(2000);

UNITY_BEGIN();

RUN_TEST(test_multiplier_scales_seeded_first_sample);
RUN_TEST(test_multiplier_applies_to_steady_input);
RUN_TEST(test_multiplier_tracks_windowed_mean);
RUN_TEST(test_default_multiplier_is_plain_average);

UNITY_END();
}

void loop() {}
Loading