#[derive(Clone, Debug)]
pub struct Agc {
bandwidth: f32,
min_gain: f32,
max_gain: f32,
locked: bool,
gain: f32,
}
impl Agc {
pub fn new(bandwidth: f32, min_gain: f32, max_gain: f32) -> Self {
Self {
bandwidth: f32::clamp(bandwidth, 0.0f32, 1.0f32),
min_gain,
max_gain,
locked: false,
gain: f32::min(1.0f32, min_gain),
}
}
pub fn reset(&mut self) {
self.gain = 1.0f32;
self.locked = false;
}
#[inline]
pub fn input(&mut self, input: f32) -> f32 {
let out = input * self.gain;
self.gain += (!self.locked as u8 as f32) * (1.0f32 - out.abs()) * self.bandwidth;
self.gain = f32::clamp(self.gain, self.min_gain, self.max_gain);
out
}
pub fn lock(&mut self, lock: bool) {
self.locked = lock;
}
pub fn gain(&self) -> f32 {
self.gain
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn test_agc() {
let mut agc = Agc::new(0.05, 0.0, 1.0e6);
let mut val = 0.0f32;
for _i in 0..256 {
val = agc.input(-2.0f32);
}
assert_approx_eq!(agc.gain, 0.5f32);
assert_approx_eq!(val, -1.0f32);
agc.reset();
agc.lock(true);
for _i in 0..16 {
val = agc.input(-2.0f32);
}
assert_eq!(agc.gain, 1.0f32);
assert_approx_eq!(val, -2.0f32);
}
}