diff --git a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h index 48c77d84..cb7f3e2f 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h +++ b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h @@ -42,6 +42,7 @@ struct NumberPost { bool useMaxRateValue; // consistencyChecksEnabled; enables consistency checks; uses maxRate and maxRateType t_RateType RateType; // maxRateType; affects how the value of maxRate is used for comparing the current and previous value bool ErrorMessage; // FIXME: not used; can be removed + int ChangeRateThreshold; // threshold parameter for negative rate detection bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings. bool checkDigitIncreaseConsistency; // extendedConsistencyCheck; performs an additional consistency check to avoid wrong readings diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp index 3a0b1f75..03ae787a 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp @@ -484,6 +484,32 @@ void ClassFlowPostProcessing::handleMaxRateValue(string _decsep, string _value) } } +void ClassFlowPostProcessing::handleChangeRateThreshold(string _decsep, string _value) { + string _digit, _decpos; + int _pospunkt = _decsep.find_first_of("."); + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + + if (_pospunkt > -1) { + _digit = _decsep.substr(0, _pospunkt); + } + else { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) { + int _zwdc = 2; + + if (isStringNumeric(_value)) { + _zwdc = std::stof(_value); + } + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { + NUMBERS[j]->ChangeRateThreshold = _zwdc; + } + } +} + bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) { std::vector splitted; int _n; @@ -530,6 +556,10 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) if ((toUpper(_param) == "PREVALUEUSE") && (splitted.size() > 1)) { PreValueUse = alphanumericToBoolean(splitted[1]); } + + if ((toUpper(_param) == "CHANGERATETHRESHOLD") && (splitted.size() > 1)) { + handleChangeRateThreshold(splitted[0], splitted[1]); + } if ((toUpper(_param) == "CHECKDIGITINCREASECONSISTENCY") && (splitted.size() > 1)) { if (alphanumericToBoolean(splitted[1])) { @@ -627,6 +657,7 @@ void ClassFlowPostProcessing::InitNUMBERS() { _number->DecimalShiftInitial = 0; _number->isExtendedResolution = false; _number->AnalogDigitalTransitionStart=9.2; + _number->ChangeRateThreshold = 2; _number->FlowRateAct = 0; // m3 / min _number->PreValue = 0; // last value read out well @@ -833,25 +864,28 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) { ESP_LOGD(TAG, "After checkDigitIncreaseConsistency: Value %f", NUMBERS[j]->Value); #endif - if (!NUMBERS[j]->AllowNegativeRates) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name); + if (PreValueUse && NUMBERS[j]->PreValueOkay) { + if (NUMBERS[j]->Nachkomma > 0) { + double _difference1 = (NUMBERS[j]->PreValue - (NUMBERS[j]->ChangeRateThreshold / pow(10, NUMBERS[j]->Nachkomma))); + double _difference2 = (NUMBERS[j]->PreValue + (NUMBERS[j]->ChangeRateThreshold / pow(10, NUMBERS[j]->Nachkomma))); + + if ((NUMBERS[j]->Value >= _difference1) && (NUMBERS[j]->Value <= _difference2)) { + NUMBERS[j]->Value = NUMBERS[j]->PreValue; + NUMBERS[j]->ReturnValue = std::to_string(NUMBERS[j]->PreValue); + } + } + + if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name); - if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { - // more debug if extended resolution is on, see #2447 - if (NUMBERS[j]->isExtendedResolution) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Neg: value=" + std::to_string(NUMBERS[j]->Value) + if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { + // more debug if extended resolution is on, see #2447 + if (NUMBERS[j]->isExtendedResolution) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Neg: value=" + std::to_string(NUMBERS[j]->Value) + ", preValue=" + std::to_string(NUMBERS[j]->PreValue) + ", preToll=" + std::to_string(NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma)))); - } + } - // Include inaccuracy of 0.2 for isExtendedResolution. - if ((NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution) - // not extended resolution allows -1 on the lowest digit - || (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(1/pow(10, NUMBERS[j]->Nachkomma))) && !NUMBERS[j]->isExtendedResolution)) { - NUMBERS[j]->Value = NUMBERS[j]->PreValue; - NUMBERS[j]->ReturnValue = to_string(NUMBERS[j]->PreValue); - } - else { NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " "; NUMBERS[j]->Value = NUMBERS[j]->PreValue; NUMBERS[j]->ReturnValue = ""; @@ -863,48 +897,48 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) { continue; } } - } - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After AllowNegativeRates: Value %f", NUMBERS[j]->Value); - #endif + #ifdef SERIAL_DEBUG + ESP_LOGD(TAG, "After AllowNegativeRates: Value %f", NUMBERS[j]->Value); + #endif - // LastValueTimeDifference = LastValueTimeDifference / 60; // in minutes - LastPreValueTimeDifference = LastPreValueTimeDifference / 60; // in minutes - NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / LastPreValueTimeDifference; - NUMBERS[j]->ReturnRateValue = to_string(NUMBERS[j]->FlowRateAct); + // LastValueTimeDifference = LastValueTimeDifference / 60; // in minutes + LastPreValueTimeDifference = LastPreValueTimeDifference / 60; // in minutes + NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / LastPreValueTimeDifference; + NUMBERS[j]->ReturnRateValue = to_string(NUMBERS[j]->FlowRateAct); - if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay) { - double _ratedifference; + if ((NUMBERS[j]->useMaxRateValue) && (NUMBERS[j]->Value != NUMBERS[j]->PreValue)) { + double _ratedifference; - if (NUMBERS[j]->RateType == RateChange) { - _ratedifference = NUMBERS[j]->FlowRateAct; - } - else { - // TODO: - // Since I don't know if this is desired, I'll comment it out first. - // int roundDifference = (int)(round(LastPreValueTimeDifference / LastValueTimeDifference)); // calculate how many rounds have passed since NUMBERS[j]->timeLastPreValue was set - // _ratedifference = ((NUMBERS[j]->Value - NUMBERS[j]->PreValue) / ((int)(round(LastPreValueTimeDifference / LastValueTimeDifference)))); // Difference per round, as a safeguard in case a reading error(Neg. Rate - Read: or Rate too high - Read:) occurs in the meantime - _ratedifference = (NUMBERS[j]->Value - NUMBERS[j]->PreValue); - } + if (NUMBERS[j]->RateType == RateChange) { + _ratedifference = NUMBERS[j]->FlowRateAct; + } + else { + // TODO: + // Since I don't know if this is desired, I'll comment it out first. + // int roundDifference = (int)(round(LastPreValueTimeDifference / LastValueTimeDifference)); // calculate how many rounds have passed since NUMBERS[j]->timeLastPreValue was set + // _ratedifference = ((NUMBERS[j]->Value - NUMBERS[j]->PreValue) / ((int)(round(LastPreValueTimeDifference / LastValueTimeDifference)))); // Difference per round, as a safeguard in case a reading error(Neg. Rate - Read: or Rate too high - Read:) occurs in the meantime + _ratedifference = (NUMBERS[j]->Value - NUMBERS[j]->PreValue); + } - if (abs(_ratedifference) > abs(NUMBERS[j]->MaxRateValue)) { - NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " - Rate: " + RundeOutput(_ratedifference, NUMBERS[j]->Nachkomma); - NUMBERS[j]->Value = NUMBERS[j]->PreValue; - NUMBERS[j]->ReturnValue = ""; - NUMBERS[j]->ReturnRateValue = ""; - NUMBERS[j]->timeStampLastValue = imagetime; + if (abs(_ratedifference) > abs(NUMBERS[j]->MaxRateValue)) { + NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " - Rate: " + RundeOutput(_ratedifference, NUMBERS[j]->Nachkomma); + NUMBERS[j]->Value = NUMBERS[j]->PreValue; + NUMBERS[j]->ReturnValue = ""; + NUMBERS[j]->ReturnRateValue = ""; + NUMBERS[j]->timeStampLastValue = imagetime; - string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw); - WriteDataLog(j); - continue; + string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw); + WriteDataLog(j); + continue; + } } - } #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After MaxRateCheck: Value %f", NUMBERS[j]->Value); #endif + } NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); NUMBERS[j]->PreValue = NUMBERS[j]->Value; @@ -949,11 +983,9 @@ void ClassFlowPostProcessing::WriteDataLog(int _index) { digital = flowDigit->getReadoutRawString(_index); } - LogFile.WriteToData(timezw, NUMBERS[_index]->name, - NUMBERS[_index]->ReturnRawValue, NUMBERS[_index]->ReturnValue, NUMBERS[_index]->ReturnPreValue, - NUMBERS[_index]->ReturnRateValue, NUMBERS[_index]->ReturnChangeAbsolute, - NUMBERS[_index]->ErrorMessageText, - digital, analog); + LogFile.WriteToData(timezw, NUMBERS[_index]->name, NUMBERS[_index]->ReturnRawValue, NUMBERS[_index]->ReturnValue, NUMBERS[_index]->ReturnPreValue, + NUMBERS[_index]->ReturnRateValue, NUMBERS[_index]->ReturnChangeAbsolute, NUMBERS[_index]->ErrorMessageText, digital, analog); + ESP_LOGD(TAG, "WriteDataLog: %s, %s, %s, %s, %s", NUMBERS[_index]->ReturnRawValue.c_str(), NUMBERS[_index]->ReturnValue.c_str(), NUMBERS[_index]->ErrorMessageText.c_str(), digital.c_str(), analog.c_str()); } diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h index aae28e6a..8782a317 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h @@ -21,11 +21,9 @@ protected: bool ErrorMessage; bool IgnoreLeadingNaN; // SPECIAL CASE for User Gustl ??? - ClassFlowCNNGeneral* flowAnalog; ClassFlowCNNGeneral* flowDigit; - string FilePreValue; ClassFlowTakeImage *flowTakeImage; @@ -43,19 +41,16 @@ protected: void handleMaxRateType(string _decsep, string _value); void handleAnalogDigitalTransitionStart(string _decsep, string _value); void handleAllowNegativeRate(string _decsep, string _value); + void handleChangeRateThreshold(string _decsep, string _value); std::string GetStringReadouts(general); void WriteDataLog(int _index); - - - public: bool PreValueUse; std::vector NUMBERS; - ClassFlowPostProcessing(std::vector* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit); virtual ~ClassFlowPostProcessing(){}; bool ReadParameter(FILE* pfile, string& aktparamgraph); diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.ChangeRateThreshold.md b/param-docs/parameter-pages/PostProcessing/NUMBER.ChangeRateThreshold.md new file mode 100644 index 00000000..9a4d0e14 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.ChangeRateThreshold.md @@ -0,0 +1,25 @@ +# Parameter `.ChangeRateThreshold` +Default Value: `2` + +Range: `1` .. `9`. + +Threshold parameter for change rate detection.
+This parameter is intended to compensate for small reading fluctuations that occur when the meter does not change its value for a long time (e.g. at night) or slightly turns backwards. This can eg. happen on watermeters. + +It is only applied to the last digit of the read value (See example below). +If the read value is within PreValue +/- Threshold, no further calculation is carried out and the Value/Prevalue remains at the old value. + +Example: + + Smallest ROI provides value for 0.000x + ChangeRateThreshold = 2 + + Extended Resolution disabled: + PreValue: 123.456'7 >>> Threshold = +/- 0.000'2 + Comparative value >>> max = 123.456'9 and min = 123.456'5 + + Extended Resolution enabled: + PreValue: 123.456'78 >>> Threshold = +/- 0.000'02 + Comparative value >>> max = 123.456'80 and min = 123.456'76 + +![](img/ChangeRateThreshold.png) diff --git a/param-docs/parameter-pages/img/ChangeRateThreshold.png b/param-docs/parameter-pages/img/ChangeRateThreshold.png new file mode 100644 index 00000000..f05640e9 Binary files /dev/null and b/param-docs/parameter-pages/img/ChangeRateThreshold.png differ diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 70e4e46d..eb8a3316 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -66,6 +66,7 @@ main.ana4 155 328 92 92 false [PostProcessing] main.DecimalShift = 0 main.AnalogDigitalTransitionStart = 9.2 +main.ChangeRateThreshold = 2 PreValueUse = true PreValueAgeStartup = 720 main.AllowNegativeRates = false diff --git a/sd-card/html/edit_config_template.html b/sd-card/html/edit_config_template.html index 1b3c1770..70f2f29e 100644 --- a/sd-card/html/edit_config_template.html +++ b/sd-card/html/edit_config_template.html @@ -969,6 +969,19 @@ $TOOLTIP_PostProcessing_NUMBER.MaxRateType + + + + + + + + + $TOOLTIP_PostProcessing_NUMBER.ChangeRateThreshold + + @@ -2163,6 +2176,7 @@ function UpdateInputIndividual(sel) { // ReadParameter(param, "PostProcessing", "PreValueUse", false, NUNBERSAkt); ReadParameter(param, "PostProcessing", "DecimalShift", true, NUNBERSAkt); ReadParameter(param, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "ChangeRateThreshold", true, NUNBERSAkt); ReadParameter(param, "PostProcessing", "MaxRateValue", true, NUNBERSAkt); ReadParameter(param, "PostProcessing", "MaxRateType", true, NUNBERSAkt); ReadParameter(param, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); @@ -2180,6 +2194,7 @@ function UpdateInputIndividual(sel) { // WriteParameter(param, category, "PostProcessing", "PreValueUse", false, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "DecimalShift", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "ChangeRateThreshold", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "MaxRateValue", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "MaxRateType", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); diff --git a/sd-card/html/img/ChangeRateThreshold.png b/sd-card/html/img/ChangeRateThreshold.png new file mode 100644 index 00000000..f05640e9 Binary files /dev/null and b/sd-card/html/img/ChangeRateThreshold.png differ diff --git a/sd-card/html/readconfigparam.js b/sd-card/html/readconfigparam.js index 0a49399c..9fe6126c 100644 --- a/sd-card/html/readconfigparam.js +++ b/sd-card/html/readconfigparam.js @@ -173,12 +173,13 @@ function ParseConfig() { category[catname]["found"] = false; param[catname] = new Object(); ParamAddValue(param, catname, "DecimalShift", 1, true); - ParamAddValue(param, catname, "AnalogDigitalTransitionStart", 1, true); + ParamAddValue(param, catname, "AnalogDigitalTransitionStart", 1, true, "9.2"); + ParamAddValue(param, catname, "ChangeRateThreshold", 1, true, "2"); // ParamAddValue(param, catname, "PreValueUse", 1, true, "true"); ParamAddValue(param, catname, "PreValueUse"); ParamAddValue(param, catname, "PreValueAgeStartup"); ParamAddValue(param, catname, "AllowNegativeRates", 1, true, "false"); - ParamAddValue(param, catname, "MaxRateValue", 1, true); + ParamAddValue(param, catname, "MaxRateValue", 1, true, "0.05"); ParamAddValue(param, catname, "MaxRateType", 1, true); ParamAddValue(param, catname, "ExtendedResolution", 1, true, "false"); ParamAddValue(param, catname, "IgnoreLeadingNaN", 1, true, "false");