succd: Add hysteresis feature to thresholdOutput blocks

Add a hysteresis value that can be optionally configured for
thresholdOutput blocks.  This will hopefully help to prevent jumping
outputs from feedback that is caused by the thresholdOutput itself.
This commit is contained in:
Rahix 2024-10-04 22:33:22 +02:00
parent 051a4bec94
commit 0757ec58a8
2 changed files with 29 additions and 5 deletions

View file

@ -30,13 +30,20 @@ type thresholdOutput struct {
debounce time.Time
// threshold is the setpoint of the block.
threshold float64
// hysteresis around the process setpoint (min/max is threshold +- hysteresis)
hysteresis float64
}
func (t *thresholdOutput) process(value float64) {
if time.Now().Before(t.debounce) {
return
}
new := value > t.threshold
new := t.output
if t.output {
new = value > (t.threshold - t.hysteresis)
} else {
new = value > (t.threshold + t.hysteresis)
}
if new != t.output {
t.output = new
t.debounce = time.Now().Add(time.Second * 5)

View file

@ -26,20 +26,37 @@ func TestMomentaryOutput(t *testing.T) {
func TestThresholdOutput(t *testing.T) {
to := thresholdOutput{
threshold: 1,
threshold: 1,
hysteresis: 0.2,
}
to.process(0)
to.process(0.7)
if to.output {
t.Fatalf("output shouldn't have triggered")
}
to.process(2)
to.process(1)
if to.output {
t.Fatalf("output shouldn't have triggered")
}
to.process(1.3)
if !to.output {
t.Fatalf("output should have triggered")
}
to.process(0)
to.process(1)
if !to.output {
t.Fatalf("output should have triggered")
}
to.process(0.7)
if !to.output {
t.Fatalf("output should have triggered (in debounce)")
}
// let debounce timeout pass
time.Sleep(time.Second * 6)
to.process(0.7)
if to.output {
t.Fatalf("output shouldn't have triggered")
}
}
func TestRingbufferInput(t *testing.T) {