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