use_control_signal/
lib.rs1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct ControlSignal {
20 value: f64,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum ControlSignalError {
25 InvalidValue,
26 InvalidBounds,
27 InvalidFactor,
28}
29
30impl ControlSignal {
31 pub fn new(value: f64) -> Result<Self, ControlSignalError> {
32 if !value.is_finite() {
33 return Err(ControlSignalError::InvalidValue);
34 }
35
36 Ok(Self { value })
37 }
38
39 pub fn value(&self) -> f64 {
40 self.value
41 }
42
43 pub fn clamp(self, min: f64, max: f64) -> Result<Self, ControlSignalError> {
44 Self::new(clamp_signal(self.value, min, max)?)
45 }
46
47 pub fn scale(self, factor: f64) -> Result<Self, ControlSignalError> {
48 if !factor.is_finite() {
49 return Err(ControlSignalError::InvalidFactor);
50 }
51
52 let value = self.value * factor;
53 Self::new(value)
54 }
55}
56
57pub fn clamp_signal(value: f64, min: f64, max: f64) -> Result<f64, ControlSignalError> {
58 if !value.is_finite() || !min.is_finite() || !max.is_finite() {
59 return Err(ControlSignalError::InvalidValue);
60 }
61
62 if min > max {
63 return Err(ControlSignalError::InvalidBounds);
64 }
65
66 Ok(value.clamp(min, max))
67}
68
69pub fn saturate(value: f64, limit: f64) -> Result<f64, ControlSignalError> {
70 if !limit.is_finite() || limit < 0.0 {
71 return Err(ControlSignalError::InvalidBounds);
72 }
73
74 clamp_signal(value, -limit, limit)
75}
76
77#[cfg(test)]
78mod tests {
79 use super::{ControlSignal, ControlSignalError, clamp_signal, saturate};
80
81 #[test]
82 fn clamps_and_scales_signals() {
83 let signal = ControlSignal::new(12.0).unwrap().clamp(0.0, 10.0).unwrap();
84
85 assert_eq!(signal.value(), 10.0);
86 assert_eq!(signal.scale(0.5).unwrap().value(), 5.0);
87 assert_eq!(clamp_signal(-2.0, 0.0, 3.0).unwrap(), 0.0);
88 }
89
90 #[test]
91 fn saturates_signals() {
92 assert_eq!(saturate(7.0, 5.0).unwrap(), 5.0);
93 assert_eq!(saturate(-7.0, 5.0).unwrap(), -5.0);
94 }
95
96 #[test]
97 fn rejects_invalid_values() {
98 assert_eq!(
99 ControlSignal::new(f64::NAN),
100 Err(ControlSignalError::InvalidValue)
101 );
102 assert_eq!(
103 clamp_signal(1.0, 2.0, 1.0),
104 Err(ControlSignalError::InvalidBounds)
105 );
106 assert_eq!(
107 ControlSignal::new(1.0).unwrap().scale(f64::NAN),
108 Err(ControlSignalError::InvalidFactor)
109 );
110 assert_eq!(saturate(1.0, -1.0), Err(ControlSignalError::InvalidBounds));
111 }
112}