Skip to main content

rill_core_wdf/
lib.rs

1//! Wave Digital Filter (WDF) core — elements, adapters, and analysis
2//! for analog circuit modeling.
3//!
4//! All types are generic over [`rill_core::Transcendental`], supporting both `f32`
5//! and `f64`. The SIMD module (behind the `simd` feature) uses the
6//! [`rill_core_dsp`](https://docs.rs/rill-core-dsp) vector infrastructure.
7//!
8//! # Design
9//!
10//! WDF elements are built around the [`WdfElement`] trait, which defines a
11//! port resistance, wave processing, and state update cycle. Elements can be
12//! combined via [`SeriesAdapter`] and [`ParallelAdapter`] to form arbitrary
13//! linear circuits. Nonlinear elements like [`Diode`] use Newton-Raphson
14//! iteration for implicit solution.
15//!
16//! References:
17//! - A. Fettweis, "Wave Digital Filters: Theory and Practice" (1986)
18//! - K. J. Werner et al., "An Improved and Generalized Diode Clipper
19//!   Model for Wave Digital Filters" (2015)
20
21#![warn(missing_docs)]
22#![deny(unsafe_code)]
23
24use rill_core::Transcendental;
25
26mod adapters;
27/// Frequency response and distortion analysis
28pub mod analysis;
29mod constants;
30mod elements;
31
32#[cfg(feature = "simd")]
33pub mod simd;
34
35/// WDF-based filter models
36pub mod filters;
37
38pub use adapters::{ParallelAdapter, SeriesAdapter};
39pub use elements::{Capacitor, Diode, Inductor, Resistor};
40
41/// Wave port type for WDF adapters
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub enum PortType {
44    /// Series connection
45    Series,
46    /// Parallel connection
47    Parallel,
48    /// Reflection port
49    Reflection,
50}
51
52/// Wave variables: a (incident), b (reflected)
53#[derive(Debug, Clone, Copy)]
54pub struct WaveVariables<T: Transcendental> {
55    /// Incident wave
56    pub a: T,
57    /// Reflected wave
58    pub b: T,
59}
60
61impl<T: Transcendental> WaveVariables<T> {
62    /// Create zero wave variables
63    pub fn new() -> Self {
64        Self {
65            a: T::ZERO,
66            b: T::ZERO,
67        }
68    }
69
70    /// Compute voltage and current from wave variables
71    pub fn to_voltage_current(&self, port_resistance: T) -> (T, T) {
72        let two = T::from_f32(2.0);
73        let v = (self.a + self.b) / two;
74        let i = (self.a - self.b) / (two * port_resistance);
75        (v, i)
76    }
77
78    /// Compute wave variables from voltage and current
79    pub fn from_voltage_current(v: T, i: T, port_resistance: T) -> Self {
80        let a = v + port_resistance * i;
81        let b = v - port_resistance * i;
82        Self { a, b }
83    }
84}
85
86impl<T: Transcendental> Default for WaveVariables<T> {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92/// Base WDF element trait
93///
94/// Every WDF element has a port resistance and processes incident
95/// waves to produce reflected waves.
96pub trait WdfElement<T: Transcendental>: Send + Sync {
97    /// Port resistance
98    fn port_resistance(&self) -> T;
99
100    /// Process incident wave, return reflected wave
101    fn process_incident(&mut self, a: T) -> T;
102
103    /// Update internal state (called after wave computation)
104    fn update_state(&mut self);
105
106    /// Current voltage across the element
107    fn voltage(&self) -> T;
108
109    /// Current current through the element
110    fn current(&self) -> T;
111
112    /// Reset to initial state
113    fn reset(&mut self);
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_wave_variables() {
122        let wv: WaveVariables<f64> = WaveVariables::new();
123        assert_eq!(wv.a, 0.0);
124        assert_eq!(wv.b, 0.0);
125    }
126
127    #[test]
128    fn test_wave_to_voltage_current() {
129        let wv: WaveVariables<f64> = WaveVariables { a: 2.0, b: 0.5 };
130        let (v, i) = wv.to_voltage_current(100.0);
131        assert!((v - 1.25).abs() < 1e-10);
132        assert!((i - 0.0075).abs() < 1e-10);
133    }
134
135    #[test]
136    fn test_voltage_current_to_wave() {
137        let wv: WaveVariables<f64> = WaveVariables::from_voltage_current(1.0, 0.01, 100.0);
138        assert!((wv.a - 2.0).abs() < 1e-10);
139        assert!((wv.b - 0.0).abs() < 1e-10);
140    }
141}