1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-20 18:53:47 +01:00

cleanup sorting

This commit is contained in:
J. Nick Koston
2025-10-17 21:03:51 -10:00
parent 5e1ee92754
commit b4ae85cf0f
2 changed files with 33 additions and 21 deletions

View File

@@ -65,32 +65,41 @@ optional<float> SlidingWindowFilter::new_value(float value) {
} }
// SortedWindowFilter // SortedWindowFilter
FixedVector<float> SortedWindowFilter::get_sorted_values_() { FixedVector<float> SortedWindowFilter::get_window_values_() {
// Copy window without NaN values using FixedVector (no heap allocation) // Copy window without NaN values using FixedVector (no heap allocation)
FixedVector<float> sorted_values; // Returns unsorted values - caller will use std::nth_element for partial sorting as needed
sorted_values.init(this->window_count_); FixedVector<float> values;
values.init(this->window_count_);
for (size_t i = 0; i < this->window_count_; i++) { for (size_t i = 0; i < this->window_count_; i++) {
float v = this->window_[i]; float v = this->window_[i];
if (!std::isnan(v)) { if (!std::isnan(v)) {
sorted_values.push_back(v); values.push_back(v);
} }
} }
std::sort(sorted_values.begin(), sorted_values.end()); return values;
return sorted_values;
} }
// MedianFilter // MedianFilter
float MedianFilter::compute_result() { float MedianFilter::compute_result() {
FixedVector<float> sorted_values = this->get_sorted_values_(); FixedVector<float> values = this->get_window_values_();
if (sorted_values.empty()) if (values.empty())
return NAN; return NAN;
size_t size = sorted_values.size(); size_t size = values.size();
size_t mid = size / 2;
if (size % 2) { if (size % 2) {
return sorted_values[size / 2]; // Odd number of elements - use nth_element to find middle element
} else { std::nth_element(values.begin(), values.begin() + mid, values.end());
return (sorted_values[size / 2] + sorted_values[(size / 2) - 1]) / 2.0f; return values[mid];
} }
// Even number of elements - need both middle elements
// Use nth_element to find upper middle element
std::nth_element(values.begin(), values.begin() + mid, values.end());
float upper = values[mid];
// Find the maximum of the lower half (which is now everything before mid)
float lower = *std::max_element(values.begin(), values.begin() + mid);
return (lower + upper) / 2.0f;
} }
// SkipInitialFilter // SkipInitialFilter
@@ -111,13 +120,16 @@ QuantileFilter::QuantileFilter(size_t window_size, size_t send_every, size_t sen
: SortedWindowFilter(window_size, send_every, send_first_at), quantile_(quantile) {} : SortedWindowFilter(window_size, send_every, send_first_at), quantile_(quantile) {}
float QuantileFilter::compute_result() { float QuantileFilter::compute_result() {
FixedVector<float> sorted_values = this->get_sorted_values_(); FixedVector<float> values = this->get_window_values_();
if (sorted_values.empty()) if (values.empty())
return NAN; return NAN;
size_t position = ceilf(sorted_values.size() * this->quantile_) - 1; size_t position = ceilf(values.size() * this->quantile_) - 1;
ESP_LOGVV(TAG, "QuantileFilter(%p)::position: %zu/%zu", this, position + 1, sorted_values.size()); ESP_LOGVV(TAG, "QuantileFilter(%p)::position: %zu/%zu", this, position + 1, values.size());
return sorted_values[position];
// Use nth_element to find the quantile element (O(n) instead of O(n log n))
std::nth_element(values.begin(), values.begin() + position, values.end());
return values[position];
} }
// MinFilter // MinFilter

View File

@@ -95,17 +95,17 @@ class MinMaxFilter : public SlidingWindowFilter {
/** Base class for filters that need a sorted window (Median, Quantile). /** Base class for filters that need a sorted window (Median, Quantile).
* *
* Extends SlidingWindowFilter to provide a helper that creates a sorted copy * Extends SlidingWindowFilter to provide a helper that filters out NaN values.
* of non-NaN values from the window. * Derived classes use std::nth_element for efficient partial sorting.
*/ */
class SortedWindowFilter : public SlidingWindowFilter { class SortedWindowFilter : public SlidingWindowFilter {
public: public:
using SlidingWindowFilter::SlidingWindowFilter; using SlidingWindowFilter::SlidingWindowFilter;
protected: protected:
/// Helper to get sorted non-NaN values from the window /// Helper to get non-NaN values from the window (not sorted - caller will use nth_element)
/// Returns empty FixedVector if all values are NaN /// Returns empty FixedVector if all values are NaN
FixedVector<float> get_sorted_values_(); FixedVector<float> get_window_values_();
}; };
/** Simple quantile filter. /** Simple quantile filter.