[sensor] Pack ThrottleAverageFilter have_nan_ into n_ bitfield

Saves 4 B per ThrottleAverageFilter instance (28 B → 24 B on 32-bit).
have_nan_ is a single boolean that previously cost 4 B due to padding;
fold it into the high bit of n_ as a 31-bit + 1-bit bitfield.

To guarantee n_ cannot overflow under realistic configurations, cap
the YAML time_period at 24 h. At a pessimistic 1 kHz source rate the
counter peaks at 86.4M, leaving 25x headroom against 2^31. Anyone
needing multi-day rolling averages should be using a different
filtering strategy anyway.

Breaking change: configurations with throttle_average periods longer
than 24 h will now fail validation. No real-world configs are
expected to use such values.
This commit is contained in:
J. Nick Koston
2026-04-30 08:11:31 -05:00
parent a8b0133ec1
commit bf917144c4
2 changed files with 11 additions and 3 deletions
+6 -1
View File
@@ -564,7 +564,12 @@ async def exponential_moving_average_filter_to_code(config, filter_id):
@FILTER_REGISTRY.register(
"throttle_average", ThrottleAverageFilter, cv.positive_time_period_milliseconds
"throttle_average",
ThrottleAverageFilter,
cv.All(
cv.positive_time_period_milliseconds,
cv.Range(max=cv.TimePeriod(hours=24)),
),
)
async def throttle_average_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config)
+5 -2
View File
@@ -266,9 +266,12 @@ class ThrottleAverageFilter : public Filter, public Component {
protected:
float sum_{0.0f};
unsigned int n_{0};
uint32_t time_period_;
bool have_nan_{false};
// Sample count packed with NaN-seen flag in a single 32-bit word.
// n_ is bounded by YAML cap on time_period_ (24 h) × max plausible source
// rate (1 kHz) = 86.4M ≪ 2^31, so 31 bits has 25x headroom.
uint32_t n_ : 31 {0};
uint32_t have_nan_ : 1 {0};
};
using lambda_filter_t = std::function<optional<float>(float)>;