From 86457187486dd9f2b94cbf1678e8073c6d056309 Mon Sep 17 00:00:00 2001 From: Rahix Date: Fri, 4 Oct 2024 22:33:22 +0200 Subject: [PATCH] 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. --- succbone/succd/process_blocks.go | 9 ++++++++- succbone/succd/process_blocks_test.go | 25 +++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/succbone/succd/process_blocks.go b/succbone/succd/process_blocks.go index 0b37193..ae1294d 100644 --- a/succbone/succd/process_blocks.go +++ b/succbone/succd/process_blocks.go @@ -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) diff --git a/succbone/succd/process_blocks_test.go b/succbone/succd/process_blocks_test.go index 4dd028c..24b75f6 100644 --- a/succbone/succd/process_blocks_test.go +++ b/succbone/succd/process_blocks_test.go @@ -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) {