pid.go raw

   1  // Package pid defines interfaces for PID controller process variable sources.
   2  // This abstraction allows the PID controller to be used for any dynamic
   3  // adjustment scenario - rate limiting, PoW difficulty adjustment, etc.
   4  package pid
   5  
   6  import "time"
   7  
   8  // ProcessVariable represents a measurable quantity that the PID controller
   9  // regulates. Implementations provide the current value and optional metadata.
  10  type ProcessVariable interface {
  11  	// Value returns the current process variable value.
  12  	// The value should typically be normalized to a range where the setpoint
  13  	// makes sense (e.g., 0.0-1.0 for percentage-based control, or absolute
  14  	// values for things like hash rate or block time).
  15  	Value() float64
  16  
  17  	// Timestamp returns when this measurement was taken.
  18  	// This is used for derivative calculations and staleness detection.
  19  	Timestamp() time.Time
  20  }
  21  
  22  // Source provides process variable measurements to the PID controller.
  23  // Implementations are domain-specific (e.g., memory monitor, hash rate tracker).
  24  type Source interface {
  25  	// Sample returns the current process variable measurement.
  26  	// This should be efficient as it may be called frequently.
  27  	Sample() ProcessVariable
  28  
  29  	// Name returns a human-readable name for this source (for logging/debugging).
  30  	Name() string
  31  }
  32  
  33  // Output represents the result of a PID controller update.
  34  type Output interface {
  35  	// Value returns the computed output value.
  36  	// The interpretation depends on the application:
  37  	// - For rate limiting: delay in seconds
  38  	// - For PoW difficulty: difficulty adjustment factor
  39  	// - For temperature control: heater power level
  40  	Value() float64
  41  
  42  	// Clamped returns true if the output was clamped to limits.
  43  	Clamped() bool
  44  
  45  	// Components returns the individual P, I, D contributions for debugging.
  46  	Components() (p, i, d float64)
  47  }
  48  
  49  // Controller defines the interface for a PID controller.
  50  // This allows for different controller implementations (standard PID,
  51  // PID with filtered derivative, adaptive PID, etc.).
  52  type Controller interface {
  53  	// Update computes the controller output based on the current process variable.
  54  	// Returns the computed output.
  55  	Update(pv ProcessVariable) Output
  56  
  57  	// UpdateValue is a convenience method that takes a raw float64 value.
  58  	// Uses the current time as the timestamp.
  59  	UpdateValue(value float64) Output
  60  
  61  	// Reset clears all internal state (integral accumulator, previous values).
  62  	Reset()
  63  
  64  	// SetSetpoint updates the target value.
  65  	SetSetpoint(setpoint float64)
  66  
  67  	// Setpoint returns the current setpoint.
  68  	Setpoint() float64
  69  
  70  	// SetGains updates the PID gains.
  71  	SetGains(kp, ki, kd float64)
  72  
  73  	// Gains returns the current PID gains.
  74  	Gains() (kp, ki, kd float64)
  75  }
  76  
  77  // Tuning holds PID tuning parameters.
  78  // This can be used for configuration or auto-tuning.
  79  type Tuning struct {
  80  	Kp float64 // Proportional gain
  81  	Ki float64 // Integral gain
  82  	Kd float64 // Derivative gain
  83  
  84  	Setpoint float64 // Target value
  85  
  86  	// Derivative filtering (0.0-1.0, lower = more filtering)
  87  	DerivativeFilterAlpha float64
  88  
  89  	// Anti-windup limits for integral term
  90  	IntegralMin float64
  91  	IntegralMax float64
  92  
  93  	// Output limits
  94  	OutputMin float64
  95  	OutputMax float64
  96  }
  97  
  98  // DefaultTuning returns sensible defaults for a normalized (0-1) process variable.
  99  func DefaultTuning() Tuning {
 100  	return Tuning{
 101  		Kp:                    0.5,
 102  		Ki:                    0.1,
 103  		Kd:                    0.05,
 104  		Setpoint:              0.5,
 105  		DerivativeFilterAlpha: 0.2,
 106  		IntegralMin:           -10.0,
 107  		IntegralMax:           10.0,
 108  		OutputMin:             0.0,
 109  		OutputMax:             1.0,
 110  	}
 111  }
 112  
 113  // SimpleProcessVariable is a basic implementation of ProcessVariable.
 114  type SimpleProcessVariable struct {
 115  	V float64
 116  	T time.Time
 117  }
 118  
 119  // Value returns the process variable value.
 120  func (p SimpleProcessVariable) Value() float64 { return p.V }
 121  
 122  // Timestamp returns when this measurement was taken.
 123  func (p SimpleProcessVariable) Timestamp() time.Time { return p.T }
 124  
 125  // NewProcessVariable creates a SimpleProcessVariable with the current time.
 126  func NewProcessVariable(value float64) SimpleProcessVariable {
 127  	return SimpleProcessVariable{V: value, T: time.Now()}
 128  }
 129  
 130  // NewProcessVariableAt creates a SimpleProcessVariable with a specific time.
 131  func NewProcessVariableAt(value float64, t time.Time) SimpleProcessVariable {
 132  	return SimpleProcessVariable{V: value, T: t}
 133  }
 134