use super::components::WdfComponent;
pub struct SeriesAdaptor<A: WdfComponent, B: WdfComponent> {
pub left: A,
pub right: B,
pub gamma: f32,
port_resistance: f32,
pub b_left: f32,
pub b_right: f32,
}
impl<A: WdfComponent, B: WdfComponent> SeriesAdaptor<A, B> {
pub fn new(left: A, right: B) -> Self {
let r_left = left.port_resistance();
let r_right = right.port_resistance();
let r_total = r_left + r_right;
Self {
left,
right,
gamma: r_left / r_total,
port_resistance: r_total,
b_left: 0.0,
b_right: 0.0,
}
}
pub fn update_impedance(&mut self) {
let r_left = self.left.port_resistance();
let r_right = self.right.port_resistance();
let r_total = r_left + r_right;
self.gamma = r_left / r_total;
self.port_resistance = r_total;
}
pub fn gamma(&self) -> f32 {
self.gamma
}
}
impl<A: WdfComponent, B: WdfComponent> WdfComponent for SeriesAdaptor<A, B> {
#[inline]
fn incident(&mut self, a: f32) {
let b_l = self.left.reflected();
let b_r = self.right.reflected();
self.b_left = b_l;
self.b_right = b_r;
let diff = a - (b_l + b_r);
let a_left = b_l + self.gamma * diff;
let a_right = b_r + (1.0 - self.gamma) * diff;
self.left.incident(a_left);
self.right.incident(a_right);
}
#[inline]
fn reflected(&self) -> f32 {
let b_l = self.left.reflected();
let b_r = self.right.reflected();
b_l + b_r
}
#[inline]
fn port_resistance(&self) -> f32 {
self.port_resistance
}
}
impl<A: WdfComponent, B: WdfComponent> SeriesAdaptor<A, B> {
#[inline]
pub fn reflected_cached(&mut self) -> f32 {
self.b_left = self.left.reflected();
self.b_right = self.right.reflected();
self.b_left + self.b_right
}
}
pub struct ParallelAdaptor<A: WdfComponent, B: WdfComponent> {
pub left: A,
pub right: B,
pub alpha: f32,
port_resistance: f32,
pub b_left: f32,
pub b_right: f32,
}
impl<A: WdfComponent, B: WdfComponent> ParallelAdaptor<A, B> {
pub fn new(left: A, right: B) -> Self {
let r_left = left.port_resistance();
let r_right = right.port_resistance();
let r_sum = r_left + r_right;
Self {
left,
right,
alpha: r_right / r_sum, port_resistance: r_left * r_right / r_sum,
b_left: 0.0,
b_right: 0.0,
}
}
pub fn update_impedance(&mut self) {
let r_left = self.left.port_resistance();
let r_right = self.right.port_resistance();
let r_sum = r_left + r_right;
self.alpha = r_right / r_sum;
self.port_resistance = r_left * r_right / r_sum;
}
pub fn alpha(&self) -> f32 {
self.alpha
}
#[inline]
pub fn reflected_cached(&mut self) -> f32 {
self.b_left = self.left.reflected();
self.b_right = self.right.reflected();
self.alpha * self.b_left + (1.0 - self.alpha) * self.b_right
}
}
impl<A: WdfComponent, B: WdfComponent> WdfComponent for ParallelAdaptor<A, B> {
#[inline]
fn incident(&mut self, a: f32) {
let b_l = self.left.reflected();
let b_r = self.right.reflected();
self.b_left = b_l;
self.b_right = b_r;
let b_root = self.alpha * b_l + (1.0 - self.alpha) * b_r;
let a_left = a + b_root - b_l;
let a_right = a + b_root - b_r;
self.left.incident(a_left);
self.right.incident(a_right);
}
#[inline]
fn reflected(&self) -> f32 {
let b_l = self.left.reflected();
let b_r = self.right.reflected();
self.alpha * b_l + (1.0 - self.alpha) * b_r
}
#[inline]
fn port_resistance(&self) -> f32 {
self.port_resistance
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sim::wdf::components::{WdfCapacitor, WdfInductor, WdfResistor};
#[test]
fn test_series_adaptor_two_resistors() {
let r1 = WdfResistor::new(1000.0);
let r2 = WdfResistor::new(1000.0);
let series = SeriesAdaptor::new(r1, r2);
assert!((series.port_resistance() - 2000.0).abs() < 1e-3);
assert!((series.gamma() - 0.5).abs() < 1e-6);
assert_eq!(series.reflected(), 0.0);
}
#[test]
fn test_parallel_adaptor_two_resistors() {
let r1 = WdfResistor::new(1000.0);
let r2 = WdfResistor::new(1000.0);
let par = ParallelAdaptor::new(r1, r2);
assert!((par.port_resistance() - 500.0).abs() < 1e-3);
assert!((par.alpha() - 0.5).abs() < 1e-6);
assert_eq!(par.reflected(), 0.0);
}
#[test]
fn test_series_rc_step_response() {
let r_val = 1000.0;
let c_val = 1e-6;
let tau = r_val * c_val; let fs = 48000.0;
let resistor = WdfResistor::new(r_val);
let mut capacitor = WdfCapacitor::new(c_val);
capacitor.set_sample_rate(fs);
let mut adaptor = SeriesAdaptor::new(resistor, capacitor);
adaptor.update_impedance();
let vs = 1.0;
let samples_at_tau = (tau * fs) as usize;
for _ in 0..samples_at_tau {
let b_tree = adaptor.reflected_cached();
let a_root = vs - b_tree;
adaptor.incident(a_root);
}
let b_tree = adaptor.reflected_cached();
let _a_root = vs - b_tree;
let resistor2 = WdfResistor::new(r_val);
let mut capacitor2 = WdfCapacitor::new(c_val);
capacitor2.set_sample_rate(fs);
let mut adaptor2 = SeriesAdaptor::new(resistor2, capacitor2);
adaptor2.update_impedance();
let mut vc_at_tau = 0.0f32;
for n in 0..(samples_at_tau * 5) {
let b_tree = adaptor2.reflected_cached();
let a_root = vs - b_tree;
let diff = a_root - (adaptor2.b_left + adaptor2.b_right);
let a_c = adaptor2.b_right + (1.0 - adaptor2.gamma) * diff;
let vc = a_c + adaptor2.b_right;
if n == samples_at_tau {
vc_at_tau = vc;
}
adaptor2.incident(a_root);
}
let expected = vs * (1.0 - (-1.0f32).exp()); let error = (vc_at_tau - expected).abs();
assert!(
error < 0.02,
"RC step response at tau: vc={vc_at_tau}, expected={expected}, error={error}"
);
}
#[test]
fn test_parallel_rl() {
let r_val = 1000.0;
let l_val = 0.01;
let fs = 44100.0;
let resistor = WdfResistor::new(r_val);
let mut inductor = WdfInductor::new(l_val);
inductor.set_sample_rate(fs);
let par = ParallelAdaptor::new(resistor, inductor);
let r_l = 2.0 * l_val * fs;
let expected_r = r_val * r_l / (r_val + r_l);
assert!(
(par.port_resistance() - expected_r).abs() / expected_r < 1e-4,
"Parallel RL resistance: got {}, expected {}",
par.port_resistance(),
expected_r
);
}
#[test]
fn test_series_adaptor_energy_conservation() {
let r1 = 1000.0;
let r2 = 500.0;
let res1 = WdfResistor::new(r1);
let res2 = WdfResistor::new(r2);
let mut adaptor = SeriesAdaptor::new(res1, res2);
let a_root = 1.0;
let b_root = adaptor.reflected_cached(); let r_root = adaptor.port_resistance();
adaptor.incident(a_root);
let p_root = (a_root * a_root - b_root * b_root) / (4.0 * r_root);
let gamma = r1 / (r1 + r2);
let a_left = gamma * a_root;
let a_right = (1.0 - gamma) * a_root;
let p_left = (a_left * a_left) / (4.0 * r1);
let p_right = (a_right * a_right) / (4.0 * r2);
let balance = (p_root - p_left - p_right).abs();
assert!(
balance < 1e-6,
"Energy not conserved: p_root={p_root}, p_left={p_left}, p_right={p_right}, diff={balance}"
);
}
#[test]
fn test_parallel_adaptor_energy_conservation() {
let r1 = 1000.0;
let r2 = 500.0;
let res1 = WdfResistor::new(r1);
let res2 = WdfResistor::new(r2);
let mut adaptor = ParallelAdaptor::new(res1, res2);
let a_root = 1.0;
let b_root = adaptor.reflected_cached(); let r_root = adaptor.port_resistance();
adaptor.incident(a_root);
let p_root = (a_root * a_root - b_root * b_root) / (4.0 * r_root);
let b_root_val = 0.0;
let a_left = a_root + b_root_val - 0.0;
let a_right = a_root + b_root_val - 0.0;
let p_left = (a_left * a_left) / (4.0 * r1);
let p_right = (a_right * a_right) / (4.0 * r2);
let balance = (p_root - p_left - p_right).abs();
assert!(
balance < 1e-6,
"Energy not conserved: p_root={p_root}, p_left={p_left}, p_right={p_right}, diff={balance}"
);
}
#[test]
fn test_nested_adaptors() {
let r1 = WdfResistor::new(100.0);
let r2 = WdfResistor::new(200.0);
let r3 = WdfResistor::new(150.0);
let series = SeriesAdaptor::new(r1, r2);
let parallel = ParallelAdaptor::new(series, r3);
assert!((parallel.port_resistance() - 100.0).abs() < 1e-3);
assert_eq!(parallel.reflected(), 0.0);
}
#[test]
fn test_update_impedance() {
let mut cap = WdfCapacitor::new(1e-6);
cap.set_sample_rate(44100.0);
let res = WdfResistor::new(1000.0);
let mut adaptor = SeriesAdaptor::new(res, cap);
let r_before = adaptor.port_resistance();
adaptor.right.set_sample_rate(96000.0);
adaptor.update_impedance();
let r_after = adaptor.port_resistance();
assert!(
(r_before - r_after).abs() > 1.0,
"Port resistance should change after sample rate update"
);
}
#[test]
fn test_rc_lowpass_frequency_response() {
let r_val = 1000.0;
let c_val = 100e-9; let fs = 192000.0;
let test_freqs = [100.0, 500.0, 1000.0, 1591.0, 5000.0, 10000.0];
for &freq in &test_freqs {
let resistor = WdfResistor::new(r_val);
let mut capacitor = WdfCapacitor::new(c_val);
capacitor.set_sample_rate(fs);
let mut adaptor = SeriesAdaptor::new(resistor, capacitor);
adaptor.update_impedance();
let num_samples = (4.0 * fs / freq) as usize; let measure_start = num_samples / 2;
let mut max_vc = 0.0f32;
for n in 0..num_samples {
let t = n as f32 / fs;
let vs = (2.0 * std::f32::consts::PI * freq * t).sin();
let b_tree = adaptor.reflected_cached();
let a_root = vs - b_tree;
let diff = a_root - (adaptor.b_left + adaptor.b_right);
let a_c = adaptor.b_right + (1.0 - adaptor.gamma) * diff;
let vc = a_c + adaptor.b_right;
if n >= measure_start {
max_vc = max_vc.max(vc.abs());
}
adaptor.incident(a_root);
}
let omega = 2.0 * std::f32::consts::PI * freq;
let rc = r_val * c_val;
let h_analytical = 1.0 / (1.0 + (omega * rc).powi(2)).sqrt();
let error = (max_vc - h_analytical).abs();
let rel_error = error / h_analytical;
assert!(
rel_error < 0.10,
"RC frequency response at {freq} Hz: measured={max_vc:.4}, \
analytical={h_analytical:.4}, relative error={rel_error:.4}"
);
}
}
}