From 09c0750c693b5f0546e7fb3ba8329b8c8dfc19f7 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 11 Jun 2022 22:23:06 +0300 Subject: [PATCH] sns: change magnitude filter --- code/espurna/sensor.cpp | 135 ++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 32 deletions(-) diff --git a/code/espurna/sensor.cpp b/code/espurna/sensor.cpp index a4f4bf43..b6c9bd4a 100644 --- a/code/espurna/sensor.cpp +++ b/code/espurna/sensor.cpp @@ -236,6 +236,24 @@ namespace { //-------------------------------------------------------------------------------- +namespace sensor { + +enum class Filter : int { + Last, + Max, + Median, + MovingAverage, + Sum, +}; + +struct ReadValue { + double raw; + double processed; + double filtered; +}; + +} // namespace sensor + namespace { class BaseSensorPtr { @@ -291,19 +309,20 @@ public: Magnitude(Magnitude&& other) noexcept = default; Magnitude& operator=(Magnitude&&) noexcept = default; - Magnitude(BaseSensorPtr, BaseFilterPtr, unsigned char slot, unsigned char type); + Magnitude(BaseSensorPtr, unsigned char slot, unsigned char type); BaseSensorPtr sensor; // Sensor object, *cannot be empty* - BaseFilterPtr filter; // Filter object, *could be empty* - - unsigned char slot; // Sensor slot # taken by the magnitude, used to access the measurement - unsigned char type; // Type of measurement, returned by the BaseSensor::type(slot) + unsigned char slot; // Sensor slot # taken by the magnitude, used to access the measurement + unsigned char type; // Type of measurement, returned by the BaseSensor::type(slot) unsigned char index_global; // N'th magnitude of it's type, across all of the active sensors sensor::Unit units { sensor::Unit::None }; // Units of measurement unsigned char decimals { 0u }; // Number of decimals in textual representation + sensor::Filter filter_type { sensor::Filter::Median }; // Instead of using raw value, filter it through a filter object + BaseFilterPtr filter; // *cannot be empty* + double last { sensor::Value::Unknown }; // Last raw value from sensor (unfiltered) double reported { sensor::Value::Unknown }; // Last reported value double min_delta { 0.0 }; // Minimum value change to report @@ -329,12 +348,6 @@ unsigned char Magnitude::_counts[MAGNITUDE_MAX] = {0}; namespace sensor { -struct ReadValue { - double raw; - double processed; - double filtered; -}; - // Base units // TODO: attach ratio to the class instead of hard-coded scaling rules? // TODO: implement through a single class and allow direct access to the ::value @@ -778,6 +791,8 @@ alignas(4) static constexpr char Reference[] PROGMEM = "Reference"; alignas(4) static constexpr char Total[] PROGMEM = "Total"; +alignas(4) static constexpr char Filter[] PROGMEM = "Filter"; + } // namespace } // namespace suffix @@ -898,6 +913,20 @@ static constexpr ::settings::options::Enumeration SensorUnitOption {sensor::Unit::None, None}, }; +alignas(4) static constexpr char Last[] PROGMEM = "last"; +alignas(4) static constexpr char Max[] PROGMEM = "max"; +alignas(4) static constexpr char Median[] PROGMEM = "median"; +alignas(4) static constexpr char MovingAverage[] PROGMEM = "moving-average"; +alignas(4) static constexpr char Sum[] PROGMEM = "sum"; + +static constexpr ::settings::options::Enumeration SensorFilterOptions[] PROGMEM { + {sensor::Filter::Last, Last}, + {sensor::Filter::Max, Max}, + {sensor::Filter::Median, Median}, + {sensor::Filter::MovingAverage, MovingAverage}, + {sensor::Filter::Sum, Sum}, +}; + } // namespace template <> @@ -909,6 +938,15 @@ String serialize(sensor::Unit unit) { return serialize(SensorUnitOptions, unit); } +template <> +sensor::Filter convert(const String& value) { + return convert(SensorFilterOptions, value, sensor::Filter::Median); +} + +String serialize(sensor::Filter filter) { + return serialize(SensorFilterOptions, filter); +} + } // namespace internal } // namespace settings @@ -1174,39 +1212,55 @@ using MagnitudeReadHandlers = std::forward_list; MagnitudeReadHandlers _magnitude_read_handlers; MagnitudeReadHandlers _magnitude_report_handlers; -BaseFilterPtr _magnitudeCreateFilter(unsigned char type) { - BaseFilterPtr filter; - +sensor::Filter _magnitudeDefaultFilter(unsigned char type) { switch (type) { case MAGNITUDE_IAQ: case MAGNITUDE_IAQ_STATIC: case MAGNITUDE_ENERGY: - filter = std::make_unique(); - break; + return sensor::Filter::Last; case MAGNITUDE_EVENT: case MAGNITUDE_DIGITAL: - filter = std::make_unique(); - break; + return sensor::Filter::Max; case MAGNITUDE_COUNT: case MAGNITUDE_ENERGY_DELTA: - filter = std::make_unique(); - break; + return sensor::Filter::Sum; case MAGNITUDE_GEIGER_CPM: case MAGNITUDE_GEIGER_SIEVERT: - filter = std::make_unique(); + return sensor::Filter::MovingAverage; + } + + return sensor::Filter::Median; +} + +BaseFilterPtr _magnitudeFilter(sensor::Filter filter) { + BaseFilterPtr out; + + switch (filter) { + case sensor::Filter::Last: + out = std::make_unique(); + break; + case sensor::Filter::Max: + out = std::make_unique(); + break; + case sensor::Filter::Sum: + out = std::make_unique(); + break; + case sensor::Filter::MovingAverage: + out = std::make_unique(); + break; + case sensor::Filter::Median: break; } - if (!filter) { - filter = std::make_unique(); + if (!out) { + out = std::make_unique(); } - return filter; + return out; } -Magnitude::Magnitude(BaseSensorPtr sensor, BaseFilterPtr filter, unsigned char slot, unsigned char type) : +Magnitude::Magnitude(BaseSensorPtr sensor, unsigned char slot, unsigned char type) : sensor(std::move(sensor)), - filter(std::move(filter)), slot(slot), type(type), index_global(_counts[type]) @@ -1768,10 +1822,20 @@ String _sensorQueryHandler(::settings::StringView key) { } } - auto expected = keys::get(magnitude, suffix::Units); - if (key == expected) { - out = ::settings::internal::serialize(magnitude.units); - break; + { + auto expected = keys::get(magnitude, suffix::Filter); + if (key == expected) { + out = ::settings::internal::serialize(magnitude.filter_type); + break; + } + } + + { + auto expected = keys::get(magnitude, suffix::Units); + if (key == expected) { + out = ::settings::internal::serialize(magnitude.units); + break; + } } } @@ -3300,10 +3364,8 @@ void _sensorInit() { // Initialize sensor magnitudes for (unsigned char magnitude_slot = 0; magnitude_slot < sensor->count(); ++magnitude_slot) { const auto magnitude_type = sensor->type(magnitude_slot); - auto filter = _magnitudeCreateFilter(magnitude_type); _magnitudes.emplace_back( sensor, // every magnitude is bound to it's sensor - std::move(filter), // store, sum, average, etc. value for reporting magnitude_slot, // id of the magnitude, unique to the sensor magnitude_type // cache type as well, no need to call type(slot) again ); @@ -3352,8 +3414,17 @@ void _sensorConfigure() { _sensor_real_time = sensor::settings::realTimeValues(); // Update magnitude config, filter sizes and reset energy if needed + // TODO: namespace and various helpers need some naming tweaks... { for (auto& magnitude : _magnitudes) { + // Only initialized once, notify about reset requirement? + if (!magnitude.filter) { + magnitude.filter_type = getSetting( + ::sensor::settings::keys::get(magnitude, ::sensor::settings::suffix::Filter), + _magnitudeDefaultFilter(magnitude.type)); + magnitude.filter = _magnitudeFilter(magnitude.filter_type); + } + // Some filters must be able store up to a certain amount of readings. if (magnitude.filter->capacity() != _sensor_report_every) { magnitude.filter->resize(_sensor_report_every);