1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum FeedbackDirection {
19 Positive,
20 Negative,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct FeedbackLoop {
25 pub gain: f64,
26 pub direction: FeedbackDirection,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum FeedbackError {
31 InvalidGain,
32}
33
34impl FeedbackLoop {
35 pub fn new(gain: f64, direction: FeedbackDirection) -> Result<Self, FeedbackError> {
36 if !gain.is_finite() {
37 return Err(FeedbackError::InvalidGain);
38 }
39
40 Ok(Self { gain, direction })
41 }
42
43 pub fn apply(&self, input: f64, feedback: f64) -> f64 {
44 match self.direction {
45 FeedbackDirection::Positive => input + self.gain * feedback,
46 FeedbackDirection::Negative => input - self.gain * feedback,
47 }
48 }
49}
50
51pub fn negative_feedback(input: f64, feedback: f64, gain: f64) -> Result<f64, FeedbackError> {
52 Ok(FeedbackLoop::new(gain, FeedbackDirection::Negative)?.apply(input, feedback))
53}
54
55pub fn positive_feedback(input: f64, feedback: f64, gain: f64) -> Result<f64, FeedbackError> {
56 Ok(FeedbackLoop::new(gain, FeedbackDirection::Positive)?.apply(input, feedback))
57}
58
59#[cfg(test)]
60mod tests {
61 use super::{
62 FeedbackDirection, FeedbackError, FeedbackLoop, negative_feedback, positive_feedback,
63 };
64
65 #[test]
66 fn applies_negative_feedback() {
67 let loop_gain = FeedbackLoop::new(0.5, FeedbackDirection::Negative).unwrap();
68
69 assert_eq!(loop_gain.apply(10.0, 2.0), 9.0);
70 assert_eq!(negative_feedback(10.0, 2.0, 0.5).unwrap(), 9.0);
71 }
72
73 #[test]
74 fn applies_positive_feedback() {
75 let loop_gain = FeedbackLoop::new(0.5, FeedbackDirection::Positive).unwrap();
76
77 assert_eq!(loop_gain.apply(10.0, 2.0), 11.0);
78 assert_eq!(positive_feedback(10.0, 2.0, 0.5).unwrap(), 11.0);
79 }
80
81 #[test]
82 fn rejects_non_finite_gain() {
83 assert_eq!(
84 FeedbackLoop::new(f64::NAN, FeedbackDirection::Negative),
85 Err(FeedbackError::InvalidGain)
86 );
87 assert_eq!(
88 positive_feedback(10.0, 2.0, f64::INFINITY),
89 Err(FeedbackError::InvalidGain)
90 );
91 }
92}