diff --git a/libnymea-core/integrations/thingmanagerimplementation.cpp b/libnymea-core/integrations/thingmanagerimplementation.cpp index 6ec1820d..8a0f257f 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.cpp +++ b/libnymea-core/integrations/thingmanagerimplementation.cpp @@ -2063,7 +2063,6 @@ void ThingManagerImplementation::loadThingStates(Thing *thing) } else { thing->setStateValue(stateType.id(), stateType.defaultValue()); } - qWarning() << "-----" << stateType.name() << stateType.filter(); thing->setStateValueFilter(stateType.id(), stateType.filter()); } settings.endGroup(); diff --git a/libnymea/integrations/pluginmetadata.cpp b/libnymea/integrations/pluginmetadata.cpp index 5bbd997e..bbf08c08 100644 --- a/libnymea/integrations/pluginmetadata.cpp +++ b/libnymea/integrations/pluginmetadata.cpp @@ -480,7 +480,6 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) if (st.contains("filter")) { QString filter = st.value("filter").toString(); if (filter == "adaptive") { - qWarning() << "++++++++++++++++++++++++++++" << stateType.name(); stateType.setFilter(Types::StateValueFilterAdaptive); } else if (!filter.isEmpty()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" state type \"" + stateTypeName + "\" has invalid filter value \"" + filter + "\". Supported filters are: \"adaptive\""); diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp index c2273ffb..63b0af84 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.cpp @@ -10,161 +10,105 @@ StateValueFilterAdaptive::StateValueFilterAdaptive() void StateValueFilterAdaptive::addValue(const QVariant &value) { qCDebug(dcStateValueFilter()) << "Adding value:" << value.toDouble(); - m_values.prepend(value.toDouble()); - m_inputValues++; + m_inputValues.prepend(value.toDouble()); + m_inputValueCount++; update(); } QVariant StateValueFilterAdaptive::filteredValue() const { - return m_filteredValue; + return m_outputValue; } void StateValueFilterAdaptive::update() { - -// while (m_values.count() > m_windowSize + 1) { -// m_values.removeLast(); -// } - -// if (m_values.isEmpty()) { -// m_filteredValue = 0; -// return; -// } - -// if (m_values.count() == 1) { -// m_filteredValue = m_values.first(); -// m_outputValues++; -// return; -// } - - - -//// m_filteredValue = m_values.first(); -//// m_outputValues++; - - -// double currentValue = m_values.first(); -// if (currentValue == 0) { -// m_filteredValue = 0; -// return; -// } - - -// // Calculate average of history, for all values and for all but the last one -// double sum = 0; -// for (int i = 1; i < m_values.count(); i++) { -// sum += m_values.at(i); -// } -// double average = sum / (m_values.count() - 1); - -// double absoluteJitter = currentValue - average; -// double relativeJitter = absoluteJitter / currentValue; - - -// // Outside of jitter window... Forward value directly -// if (qAbs(relativeJitter) > m_averageJitter * 3) { -// m_filteredValue = m_values.first(); -// m_values.clear(); -// m_values.prepend(m_filteredValue); -// m_outputValues++; -// qCDebug(dcStateValueFilter()) << "Updating output"; -// } else { - -// } -// // Adjust average jitter -// m_averageJitter = ((m_averageJitter * m_windowSize) + qAbs(relativeJitter)) / (m_windowSize + 1); - - -// qCDebug(dcStateValueFilter()) << "input" << currentValue << "output" << m_filteredValue << "average" << average << "jitter:" << absoluteJitter << "relative" << relativeJitter << "avg" << m_averageJitter; -// qCDebug(dcStateValueFilter()) << "Filter input values:" << m_inputValues << "output values:" << m_outputValues << "compression ratio:" << (1.0 * m_inputValues / m_outputValues); - - -// return; - - - - - - while (m_values.count() > m_windowSize) { - m_values.removeLast(); + while (m_inputValues.count() > m_windowSize) { + m_inputValues.removeLast(); } - if (m_values.isEmpty()) { - m_filteredValue = 0; + if (m_inputValues.isEmpty()) { + m_outputValue = 0; return; } - if (m_values.count() == 1) { + if (m_inputValues.count() == 1) { // Not enough data - m_filteredValue = m_values.first(); - m_outputValues++; + m_outputValue = m_inputValues.first(); + m_outputValueCount++; return; } + double currentValue = m_inputValues.first(); + if (qFuzzyCompare(currentValue, 0)) { + m_outputValue = 0; + return; + } // Calculate average of history, for all values and for all but the last one double sum = 0; - for (int i = 0; i < m_values.count(); i++) { - sum += m_values.at(i); + for (int i = 0; i < m_inputValues.count(); i++) { + sum += m_inputValues.at(i); } - double currentValue = m_values.first(); - if (qFuzzyCompare(currentValue, 0)) { - m_filteredValue = 0; - return; - } - - double filteredValue = sum / m_values.count(); - double previousFilteredValue = (sum - m_values.first()) / (m_values.count() - 1); + double filteredValue = sum / m_inputValues.count(); + double previousFilteredValue = (sum - m_inputValues.first()) / (m_inputValues.count() - 1); if (qFuzzyCompare(previousFilteredValue, 0)) { - m_filteredValue = m_values.first(); - m_outputValues++; + m_outputValue = m_inputValues.first(); + m_outputValueCount++; return; } // Calculate change ratio of the last value compared to the previous one, unflitered and filtered - double changeRatio = 1 - qAbs(currentValue / previousFilteredValue); + double changeRatioToAverage = 1 - qAbs(currentValue / previousFilteredValue); + double changeRatioToCurrentOutput = 1 - qAbs(currentValue / m_outputValue); double changeRatioFiltered = 1 - qAbs(filteredValue / previousFilteredValue); - // Add deviation of actual value vs filtered value up to have an idea how much we're off - m_totalDeviation += changeRatioFiltered - changeRatio; - // If the unfiltered value changes for more than 3 times the standard deviation of the jittering values // it's a 99% chance a big change happened that's not jitter (e.g turned on/off) // Discard the history and follow the new value right away - if (qAbs(changeRatio) > m_standardDeviation * 3) { - m_values.clear(); - m_values.prepend(currentValue); + if (qAbs(changeRatioToAverage) > m_standardDeviation * 3) { + m_inputValues.clear(); + m_inputValues.prepend(currentValue); m_totalDeviation = 0; - if (!qFuzzyCompare(m_filteredValue, filteredValue)) { - m_filteredValue = currentValue; - qCDebug(dcStateValueFilter()) << "Updating output value:" << m_filteredValue << "(input exceeds max jitter)"; - m_outputValues++; + if (!qFuzzyCompare(m_outputValue, filteredValue)) { + m_outputValue = currentValue; + qCDebug(dcStateValueFilter()) << "Updating output value:" << m_outputValue << "(input exceeds max jitter)"; + m_outputValueCount++; } - // If the filtered value changed for 5 percent or more, follow slowly - // In order to not get stuck on being off for 5% forever, also move closer - // to the new value when the deviation exceeds max deviation - } else if (qAbs(changeRatioFiltered) > m_standardDeviation || qAbs(m_totalDeviation) > m_maxTotalDeviation) { - m_totalDeviation = 0; - if (!qFuzzyCompare(m_filteredValue, filteredValue)) { - qCDebug(dcStateValueFilter()) << "Updating output value:" << filteredValue << "(drift compensation)"; - m_filteredValue = filteredValue; - m_outputValues++; + // We're considering it jitter + } else { + // Add up the deviation from the current actual value to the currently filtered value + m_totalDeviation += changeRatioToCurrentOutput; + + // If the filtered value changed for more than the the standard deviation, follow slowly + // In order to not get stuck on being off for the standard deviation forever, also move closer + // to the new value when the summed up deviation exceeds the maximum allowed total deviation + if (qAbs(changeRatioFiltered) > m_standardDeviation || qAbs(m_totalDeviation) > m_maxTotalDeviation) { + m_totalDeviation = 0; + if (!qFuzzyCompare(m_outputValue, filteredValue)) { + qCDebug(dcStateValueFilter()) << "Updating output value:" << filteredValue << "(drift compensation)"; + m_outputValue = filteredValue; + m_outputValueCount++; + } } + + // Poor mans solution to calculate standard deviation. Not as precise, but much faster than looping over history again + m_standardDeviation = ((m_standardDeviation * m_windowSize) + qAbs(changeRatioToAverage)) / (m_windowSize + 1); } - // Poor mans solution to calculate standard deviation. Not as precise, but much faster than looping over history again - m_standardDeviation = ((m_standardDeviation * m_windowSize) + qAbs(changeRatio)) / (m_windowSize + 1); - qWarning(dcStateValueFilter()) << "New:" << currentValue << "Old:" << previousFilteredValue << "Filtered:" << filteredValue << "ratio:" << changeRatio << "filteredRatio" << changeRatioFiltered << "deviation" << m_totalDeviation << "averageJitter" << m_averageJitter; + // correct stats on overflow of counters - if (m_inputValues < m_outputValues) { - m_outputValues = 0; + if (m_inputValueCount < m_outputValueCount) { + m_outputValueCount = 0; } - qCDebug(dcStateValueFilter()) << "Filter input values:" << m_inputValues << "output values:" << m_outputValues << "compression ratio:" << (1.0 * m_inputValues / m_outputValues); + qCDebug(dcStateValueFilter()) << "Filter statistics for" << this; + qCDebug(dcStateValueFilter()) << "Input:" << currentValue << "AVG:" << previousFilteredValue << "Filtered:" << filteredValue; + qCDebug(dcStateValueFilter()) << "Change ratios: Input/average:" << changeRatioToAverage << "Filtered/average:" << changeRatioFiltered << "Input/output:" << changeRatioToCurrentOutput; + qCDebug(dcStateValueFilter()) << "Std deviation:" << m_standardDeviation << "Total deviation:" << m_totalDeviation; + qCDebug(dcStateValueFilter()) << "Compression ratio:" << (1.0 * m_inputValueCount / m_outputValueCount) << "(" << m_outputValueCount << "/" << m_inputValueCount << ")"; } diff --git a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h index c9ce5b6e..bc07f899 100644 --- a/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h +++ b/libnymea/integrations/statevaluefilters/statevaluefilteradaptive.h @@ -15,18 +15,19 @@ private: void update(); private: - QList m_values; + QList m_inputValues; int m_windowSize = 20; double m_standardDeviation = 0.05; - double m_maxTotalDeviation = 1; + double m_maxTotalDeviation = 0.4; - double m_filteredValue = 0; double m_totalDeviation = 0; + double m_outputValue = 0; + // Stats for debugging - quint64 m_inputValues = 0; - quint64 m_outputValues = 0; + quint64 m_inputValueCount = 0; + quint64 m_outputValueCount = 0; };