use pot_head::{Config, HysteresisMode, NoiseFilter, PotHead, ResponseCurve, SnapZone};
#[cfg(feature = "grab-mode")]
use pot_head::GrabMode;
static EMPTY_SNAP_ZONES: [SnapZone<f32>; 0] = [];
#[test]
fn test_pothead_with_ema_filter() {
static CONFIG: Config<u16, f32> = Config {
input_min: 0_u16,
input_max: 4095_u16,
output_min: 0.0_f32,
output_max: 1.0_f32,
hysteresis: HysteresisMode::none(),
curve: ResponseCurve::Linear,
filter: NoiseFilter::ExponentialMovingAverage { alpha: 0.3 },
snap_zones: &EMPTY_SNAP_ZONES,
#[cfg(feature = "grab-mode")]
grab_mode: GrabMode::None,
};
let mut pot = PotHead::new(&CONFIG).expect("Valid config");
let out1 = pot.process(0);
assert_eq!(out1, 0.0);
let out2 = pot.process(4095);
assert!((out2 - 0.3).abs() < 0.001, "Expected ~0.3, got {}", out2);
let out3 = pot.process(4095);
assert!((out3 - 0.51).abs() < 0.01, "Expected ~0.51, got {}", out3);
}
#[cfg(feature = "moving-average")]
#[test]
fn test_pothead_with_moving_average_filter() {
static CONFIG: Config<u16, f32> = Config {
input_min: 0_u16,
input_max: 100_u16,
output_min: 0.0_f32,
output_max: 1.0_f32,
hysteresis: HysteresisMode::none(),
curve: ResponseCurve::Linear,
filter: NoiseFilter::MovingAverage { window_size: 3 },
snap_zones: &EMPTY_SNAP_ZONES,
#[cfg(feature = "grab-mode")]
grab_mode: GrabMode::None,
};
let mut pot = PotHead::new(&CONFIG).expect("Valid config");
assert_eq!(pot.process(0), 0.0); assert_eq!(pot.process(30), 0.15); assert_eq!(pot.process(60), 0.3);
let out = pot.process(90); assert!((out - 0.6).abs() < 0.001, "Expected 0.6, got {}", out);
}
#[test]
fn test_filter_smooths_noisy_input() {
static CONFIG: Config<u16, u16> = Config {
input_min: 0_u16,
input_max: 1000_u16,
output_min: 0_u16,
output_max: 1000_u16,
hysteresis: HysteresisMode::none(),
curve: ResponseCurve::Linear,
filter: NoiseFilter::ExponentialMovingAverage { alpha: 0.2 },
snap_zones: &EMPTY_SNAP_ZONES,
#[cfg(feature = "grab-mode")]
grab_mode: GrabMode::None,
};
let mut pot = PotHead::new(&CONFIG).expect("Valid config");
let noisy_samples = [500, 510, 490, 505, 495, 500, 498, 502];
let mut outputs = Vec::new();
for &sample in &noisy_samples {
outputs.push(pot.process(sample));
}
let output_slice = &outputs[outputs.len() - 4..];
let mean: f32 = output_slice.iter().map(|&x| x as f32).sum::<f32>() / 4.0;
let variance: f32 = output_slice
.iter()
.map(|&x| {
let diff = x as f32 - mean;
diff * diff
})
.sum::<f32>()
/ 4.0;
assert!(
variance < 20.0,
"Variance {} too high - filtering not working",
variance
);
}
#[test]
fn test_no_filter_passes_through() {
static CONFIG: Config<u16, f32> = Config {
input_min: 0_u16,
input_max: 100_u16,
output_min: 0.0_f32,
output_max: 1.0_f32,
hysteresis: HysteresisMode::none(),
curve: ResponseCurve::Linear,
filter: NoiseFilter::None,
snap_zones: &EMPTY_SNAP_ZONES,
#[cfg(feature = "grab-mode")]
grab_mode: GrabMode::None,
};
let mut pot = PotHead::new(&CONFIG).expect("Valid config");
assert_eq!(pot.process(0), 0.0);
assert_eq!(pot.process(50), 0.5);
assert_eq!(pot.process(100), 1.0);
}
#[test]
fn test_filter_combined_with_hysteresis() {
static CONFIG: Config<u16, f32> = Config {
input_min: 0_u16,
input_max: 1000_u16,
output_min: 0.0_f32,
output_max: 1.0_f32,
hysteresis: HysteresisMode::ChangeThreshold { threshold: 0.1 },
curve: ResponseCurve::Linear,
filter: NoiseFilter::ExponentialMovingAverage { alpha: 0.5 },
snap_zones: &EMPTY_SNAP_ZONES,
#[cfg(feature = "grab-mode")]
grab_mode: GrabMode::None,
};
let mut pot = PotHead::new(&CONFIG).expect("Valid config");
let initial = pot.process(500);
assert!((initial - 0.5).abs() < 0.001);
let out2 = pot.process(550); assert_eq!(out2, initial);
let out3 = pot.process(800);
assert!(out3 > initial);
}