mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-12 11:17:06 +01:00
sns: change magnitude filter
This commit is contained in:
@@ -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<sensor::Unit> 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<sensor::Filter> 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<MagnitudeReadHandler>;
|
||||
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<LastFilter>();
|
||||
break;
|
||||
return sensor::Filter::Last;
|
||||
case MAGNITUDE_EVENT:
|
||||
case MAGNITUDE_DIGITAL:
|
||||
filter = std::make_unique<MaxFilter>();
|
||||
break;
|
||||
return sensor::Filter::Max;
|
||||
case MAGNITUDE_COUNT:
|
||||
case MAGNITUDE_ENERGY_DELTA:
|
||||
filter = std::make_unique<SumFilter>();
|
||||
break;
|
||||
return sensor::Filter::Sum;
|
||||
case MAGNITUDE_GEIGER_CPM:
|
||||
case MAGNITUDE_GEIGER_SIEVERT:
|
||||
filter = std::make_unique<MovingAverageFilter>();
|
||||
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<LastFilter>();
|
||||
break;
|
||||
case sensor::Filter::Max:
|
||||
out = std::make_unique<MaxFilter>();
|
||||
break;
|
||||
case sensor::Filter::Sum:
|
||||
out = std::make_unique<SumFilter>();
|
||||
break;
|
||||
case sensor::Filter::MovingAverage:
|
||||
out = std::make_unique<MovingAverageFilter>();
|
||||
break;
|
||||
case sensor::Filter::Median:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filter) {
|
||||
filter = std::make_unique<MedianFilter>();
|
||||
if (!out) {
|
||||
out = std::make_unique<MedianFilter>();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user