From 4df00f0a63978a78ef9ecd8383b07cfdb810fa7b Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Sat, 28 Sep 2024 09:35:41 +0200 Subject: [PATCH] succd: factor out ringbuffer, do not recalculate average on every request --- succbone/succd/main.go | 2 ++ succbone/succd/process.go | 36 ++++++++++++++++++++++++----- succbone/succd/process_state.go | 41 ++++++++++++--------------------- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/succbone/succd/main.go b/succbone/succd/main.go index 99635ad..23427b6 100644 --- a/succbone/succd/main.go +++ b/succbone/succd/main.go @@ -28,6 +28,8 @@ func main() { d := daemon{} d.daemonState.rpOn = true + d.daemonState.piraniVolts3.limit = 3 + d.daemonState.piraniVolts100.limit = 100 d.aboveRough.threshold = float64(flagPressureThresholdRough) d.aboveHigh.threshold = float64(flagPressureThresholdHigh) diff --git a/succbone/succd/process.go b/succbone/succd/process.go index bd3f85e..b285fab 100644 --- a/succbone/succd/process.go +++ b/succbone/succd/process.go @@ -66,6 +66,33 @@ func (t *thresholdOutput) process(value float64) { } } +// ringbufferInput accumulates analog data up to limit samples, and calculates +// an average. +type ringbufferInput struct { + data []float32 + limit uint + avg float32 +} + +func (r *ringbufferInput) process(input float32) { + // TODO(q3k): use actual ringbuffer + // TODO(q3k): optimize average calculation + // TODO(q3k): precalculate value in mbar + r.data = append(r.data, input) + trim := len(r.data) - int(r.limit) + if trim > 0 { + r.data = r.data[trim:] + } + avg := float32(0.0) + for _, v := range r.data { + avg += v + } + if len(r.data) != 0 { + avg /= float32(len(r.data)) + } + r.avg = avg +} + // process runs the pain acquisition and control loop of succd. func (d *daemon) process(ctx context.Context) { ticker := time.NewTicker(time.Millisecond * 100) @@ -96,12 +123,9 @@ func (d *daemon) processOnce(_ context.Context) error { d.mu.Lock() defer d.mu.Unlock() - // Process pirani ringbuffer. - d.adcPiraniVolts = append(d.adcPiraniVolts, v) - trim := len(d.adcPiraniVolts) - 100 - if trim > 0 { - d.adcPiraniVolts = d.adcPiraniVolts[trim:] - } + // Process pirani ringbuffers. + d.piraniVolts3.process(v) + d.piraniVolts100.process(v) d.pumpdown.process() d.vent.process() diff --git a/succbone/succd/process_state.go b/succbone/succd/process_state.go index 0bbb09e..8ed1c41 100644 --- a/succbone/succd/process_state.go +++ b/succbone/succd/process_state.go @@ -7,11 +7,11 @@ import "math" type daemonState struct { safety safetyStatus - // adcPiraniVolts is a moving window of read ADC values, used to calculate a - // moving average. - adcPiraniVolts []float32 - rpOn bool - dpOn bool + piraniVolts100 ringbufferInput + piraniVolts3 ringbufferInput + + rpOn bool + dpOn bool vent momentaryOutput pumpdown momentaryOutput @@ -42,20 +42,18 @@ const ( piraniDetectionDisconnected = iota ) +func piraniVoltsToMbar(v float32) float32 { + // Per Pirani probe docs. + bar := math.Pow(10.0, float64(v)-8.5) + return float32(bar * 1000.0) +} + // piraniDetection guesses whether the pirani gauge is connected. func (d *daemonState) piraniDetection() piraniDetection { - if len(d.adcPiraniVolts) < 3 { + if len(d.piraniVolts3.data) < int(d.piraniVolts3.limit) { return piraniDetectionUnknown } - volts := float32(0.0) - for _, v := range d.adcPiraniVolts[len(d.adcPiraniVolts)-3:] { - volts += v - } - volts /= 3.0 - - bar := math.Pow(10.0, float64(volts)-8.5) - mbar := float32(bar * 1000.0) - + mbar := piraniVoltsToMbar(d.piraniVolts3.avg) if mbar < 4e-6 { return piraniDetectionDisconnected } @@ -63,17 +61,8 @@ func (d *daemonState) piraniDetection() piraniDetection { } func (d *daemonState) pirani() (volts float32, mbar float32) { - volts = 0.0 - for _, v := range d.adcPiraniVolts { - volts += v - } - if len(d.adcPiraniVolts) != 0 { - volts /= float32(len(d.adcPiraniVolts)) - } - - // Per Pirani probe docs. - bar := math.Pow(10.0, float64(volts)-8.5) - mbar = float32(bar * 1000.0) + volts = d.piraniVolts100.avg + mbar = piraniVoltsToMbar(volts) return }