mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-30 18:12:28 +00:00
TimeSeries: median, holdtime for event-aggregator
git-svn-id: https://svn.fhem.de/fhem/trunk@10907 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
a2dca22ceb
commit
6797a8f4e6
@ -37,6 +37,13 @@
|
|||||||
# 23.07.2015 Jens Beyer (jensb at forum dot fhem dot de)
|
# 23.07.2015 Jens Beyer (jensb at forum dot fhem dot de)
|
||||||
# new: method getValue
|
# new: method getValue
|
||||||
#
|
#
|
||||||
|
# 24.01.2016 knxhm at forum dot fhem dot de & Jens Beyer (jensb at forum dot fhem dot de)
|
||||||
|
# new: property median (out)
|
||||||
|
#
|
||||||
|
# 29.01.2016 Jens Beyer (jensb at forum dot fhem dot de)
|
||||||
|
# modified: method elapsed reverted to version from 2015-01-31 to provide downsampling and buffering through fhem.pl
|
||||||
|
# modified: method _housekeeping does not reset time series if hold time is specified
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
package TimeSeries;
|
package TimeSeries;
|
||||||
@ -89,7 +96,8 @@ sub new() {
|
|||||||
n => 0, # size of sample (non time weighted) or number of intervals (time weighted)
|
n => 0, # size of sample (non time weighted) or number of intervals (time weighted)
|
||||||
mean => undef, # arithmetic mean of values
|
mean => undef, # arithmetic mean of values
|
||||||
sd => undef, # standard deviation of values
|
sd => undef, # standard deviation of values
|
||||||
integral => undef, # integral area of all values in the series
|
integral => undef, # sum (holdTime undefined) or integral area (holdTime defined) of all values
|
||||||
|
median => undef, # median of all values (method must be "none" and holdTime must be defined)
|
||||||
_t0 => undef, # same as t0; moved to _t on reset
|
_t0 => undef, # same as t0; moved to _t on reset
|
||||||
_t => undef, # same as t but survives a reset
|
_t => undef, # same as t but survives a reset
|
||||||
_v => undef, # same as v but survives a reset
|
_v => undef, # same as v but survives a reset
|
||||||
@ -112,6 +120,7 @@ sub reset() {
|
|||||||
$self->{mean}= undef;
|
$self->{mean}= undef;
|
||||||
$self->{sd}= undef;
|
$self->{sd}= undef;
|
||||||
$self->{integral}= 0;
|
$self->{integral}= 0;
|
||||||
|
$self->{median}= undef;
|
||||||
$self->{_M}= undef;
|
$self->{_M}= undef;
|
||||||
$self->{_S}= undef;
|
$self->{_S}= undef;
|
||||||
$self->{_t0}= $self->{_t};
|
$self->{_t0}= $self->{_t};
|
||||||
@ -248,21 +257,12 @@ sub _updatestat($$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# has autoreset or holdTime period elapsed?
|
# has autoreset period elapsed?
|
||||||
|
# used by fhem.pl for downsampling
|
||||||
#
|
#
|
||||||
sub elapsed($$) {
|
sub elapsed($$) {
|
||||||
my ($self, $t)= @_;
|
my ($self, $t)= @_;
|
||||||
|
return defined($self->{autoreset}) && defined($self->{_t0}) && ($t - $self->{_t0} >= $self->{autoreset});
|
||||||
my $duration;
|
|
||||||
if (defined($self->{autoreset})) {
|
|
||||||
$duration = $self->{autoreset};
|
|
||||||
#main::Debug("TimeSeries::elapsed: autoreset=$duration\n");
|
|
||||||
} elsif (defined($self->{holdTime})) {
|
|
||||||
$duration = $self->{holdTime};
|
|
||||||
#main::Debug("TimeSeries::elapsed: holdTime=$duration\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return defined($duration) && defined($self->{_t0}) && ($t - $self->{_t0} >= $duration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -271,15 +271,13 @@ sub elapsed($$) {
|
|||||||
sub _housekeeping($) {
|
sub _housekeeping($) {
|
||||||
my ($self, $t)= @_;
|
my ($self, $t)= @_;
|
||||||
|
|
||||||
if ($self->elapsed($t)) {
|
if($self->elapsed($t) && !defined($self->{holdTime})) {
|
||||||
if (defined($self->{autoreset})) {
|
|
||||||
#main::Debug("TimeSeries::_housekeeping: reset\n");
|
#main::Debug("TimeSeries::_housekeeping: reset\n");
|
||||||
$self->reset();
|
$self->reset();
|
||||||
} else {
|
} elsif(defined($self->{holdTime}) && defined($self->{_t0}) && ($t - $self->{_t0} >= $self->{holdTime})) {
|
||||||
#main::Debug("TimeSeries::_housekeeping: trimToHoldTime\n");
|
#main::Debug("TimeSeries::_housekeeping: trimToHoldTime\n");
|
||||||
$self->trimToHoldTime();
|
$self->trimToHoldTime();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -312,6 +310,17 @@ sub add($$$) {
|
|||||||
if($self->{method} eq "none") {
|
if($self->{method} eq "none") {
|
||||||
# no time-weighting
|
# no time-weighting
|
||||||
$self->_updatestat($v);
|
$self->_updatestat($v);
|
||||||
|
|
||||||
|
# median
|
||||||
|
if(defined($self->{holdTime})) {
|
||||||
|
my @sortedVSeries = sort {$TimeSeries::a <=> $TimeSeries::b} @{$self->{vSeries}};
|
||||||
|
my $center = int($self->{count} / 2);
|
||||||
|
if($self->{count} % 2 == 0) {
|
||||||
|
$self->{median} = ($sortedVSeries[$center - 1] + $sortedVSeries[$center]) / 2;
|
||||||
|
} else {
|
||||||
|
$self->{median} = $sortedVSeries[$center];
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
# time-weighting
|
# time-weighting
|
||||||
if(defined($self->{_t})) {
|
if(defined($self->{_t})) {
|
||||||
@ -356,6 +365,7 @@ sub add($$$) {
|
|||||||
$self->{sd}= sqrt($self->{_S}/ ($n-1)) / $T if($n> 1);
|
$self->{sd}= sqrt($self->{_S}/ ($n-1)) / $T if($n> 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#main::Debug(Dumper($self)); ###
|
#main::Debug(Dumper($self)); ###
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,6 +435,7 @@ sub selftest() {
|
|||||||
if ($tsb->{mean} != 1.0) { $success = 0; main::Debug("unweighed block autoreset test failed: mean mismatch $tsb->{mean}/1.0\n"); }
|
if ($tsb->{mean} != 1.0) { $success = 0; main::Debug("unweighed block autoreset test failed: mean mismatch $tsb->{mean}/1.0\n"); }
|
||||||
if (!defined($tsb->{sd}) || $tsb->{sd} ne "0.4") { $success = 0; main::Debug("unweighed block autoreset test failed: sd mismatch $tsb->{sd}/0.4\n"); }
|
if (!defined($tsb->{sd}) || $tsb->{sd} ne "0.4") { $success = 0; main::Debug("unweighed block autoreset test failed: sd mismatch $tsb->{sd}/0.4\n"); }
|
||||||
if ($tsb->{integral} != 2.0) { $success = 0; main::Debug("unweighed block autoreset test failed: sum mismatch $tsb->{integral}/2.0\n"); }
|
if ($tsb->{integral} != 2.0) { $success = 0; main::Debug("unweighed block autoreset test failed: sum mismatch $tsb->{integral}/2.0\n"); }
|
||||||
|
|
||||||
$tsb->reset();
|
$tsb->reset();
|
||||||
$tsb->{_t0} = undef;
|
$tsb->{_t0} = undef;
|
||||||
$tsb->{_t} = undef;
|
$tsb->{_t} = undef;
|
||||||
@ -465,6 +476,7 @@ sub selftest() {
|
|||||||
if ($tsm->{mean} != 1.0) { $success = 0; main::Debug("unweighed moving add test failed: mean mismatch $tsm->{mean}/1.0\n"); }
|
if ($tsm->{mean} != 1.0) { $success = 0; main::Debug("unweighed moving add test failed: mean mismatch $tsm->{mean}/1.0\n"); }
|
||||||
if (!defined($tsm->{sd}) || $tsm->{sd} ne sqrt(0.13/2)) { $success = 0; main::Debug("unweighed moving add test failed: sd mismatch $tsm->{sd}/0.254950975679639\n"); }
|
if (!defined($tsm->{sd}) || $tsm->{sd} ne sqrt(0.13/2)) { $success = 0; main::Debug("unweighed moving add test failed: sd mismatch $tsm->{sd}/0.254950975679639\n"); }
|
||||||
if ($tsm->{integral} != 3.0) { $success = 0; main::Debug("unweighed moving add test failed: sum mismatch $tsm->{integral}/3.0\n"); }
|
if ($tsm->{integral} != 3.0) { $success = 0; main::Debug("unweighed moving add test failed: sum mismatch $tsm->{integral}/3.0\n"); }
|
||||||
|
if ($tsm->{median} != 1.0) { $success = 0; main::Debug("unweighed moving add test failed: median mismatch $tsm->{median}/1.0\n"); }
|
||||||
sleep(3);
|
sleep(3);
|
||||||
$tsm->add($now+1, 1.0);
|
$tsm->add($now+1, 1.0);
|
||||||
$tsm->add($now+2, 0.8);
|
$tsm->add($now+2, 0.8);
|
||||||
@ -480,6 +492,8 @@ sub selftest() {
|
|||||||
if ($tsm->{mean} != 1.0) { $success = 0; main::Debug("unweighed moving holdTime test failed: mean mismatch $tsm->{mean}/1.0\n"); }
|
if ($tsm->{mean} != 1.0) { $success = 0; main::Debug("unweighed moving holdTime test failed: mean mismatch $tsm->{mean}/1.0\n"); }
|
||||||
if (!defined($tsm->{sd}) || $tsm->{sd} ne sqrt(0.13/2)) { $success = 0; main::Debug("unweighed moving holdTime test failed: sd mismatch $tsm->{sd}/0.254950975679639\n"); }
|
if (!defined($tsm->{sd}) || $tsm->{sd} ne sqrt(0.13/2)) { $success = 0; main::Debug("unweighed moving holdTime test failed: sd mismatch $tsm->{sd}/0.254950975679639\n"); }
|
||||||
if ($tsm->{integral} != 3.0) { $success = 0; main::Debug("unweighed moving holdTime test failed: sum mismatch $tsm->{integral}/3.0\n"); }
|
if ($tsm->{integral} != 3.0) { $success = 0; main::Debug("unweighed moving holdTime test failed: sum mismatch $tsm->{integral}/3.0\n"); }
|
||||||
|
if ($tsm->{median} != 1.0) { $success = 0; main::Debug("unweighed block autoreset test failed: median mismatch $tsm->{median}/1.0\n"); }
|
||||||
|
|
||||||
$tsm->reset();
|
$tsm->reset();
|
||||||
$tsm->{method} = 'const';
|
$tsm->{method} = 'const';
|
||||||
$tsm->{holdTime} = 5;
|
$tsm->{holdTime} = 5;
|
||||||
@ -557,6 +571,8 @@ B<TimeSeries> is a perl module to feed time/value data points and get some stati
|
|||||||
re-evaluated each time a data point is added. Note that this may require significant amounts
|
re-evaluated each time a data point is added. Note that this may require significant amounts
|
||||||
of memory depending on the sample rate and the holdTime.
|
of memory depending on the sample rate and the holdTime.
|
||||||
|
|
||||||
|
If method is none and holdtime is defined then the median of the values will be calculated additionally.
|
||||||
|
|
||||||
It is also possible to define autoreset and holdtime at the same time. In this case the data buffer
|
It is also possible to define autoreset and holdtime at the same time. In this case the data buffer
|
||||||
is enabled and will be cleared each time an autoreset occurs, independent of the value of holdtime.
|
is enabled and will be cleared each time an autoreset occurs, independent of the value of holdtime.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user