Skip to main content

embedded_dsp/
types.rs

1//! Data types, status codes, complex structures, and fixed-point helper types.
2
3#[cfg(feature = "fixed")]
4pub use fixed::types::{I1F7 as q7, I1F15 as q15, I1F31 as q31, I16F16};
5
6#[cfg(feature = "fixed")]
7/// Q8.7 fixed-point type (8 integer bits, 7 fractional bits, `i16`-backed).
8pub type Q8F7 = fixed::FixedI16<fixed::types::extra::U7>;
9
10#[cfg(feature = "fixed")]
11/// Q2.14 fixed-point type (2 integer bits, 14 fractional bits, `i16`-backed).
12pub type Q2F14 = fixed::FixedI16<fixed::types::extra::U14>;
13
14#[cfg(not(feature = "fixed"))]
15pub use fallback::*;
16
17#[cfg(not(feature = "fixed"))]
18mod fallback {
19    pub trait FixedNum: Copy {
20        fn to_raw_fixed(self, frac: u32, min_val: i64, max_val: i64, saturate: bool) -> i64;
21        fn from_raw_fixed(raw: i64, frac: u32) -> Self;
22    }
23
24    impl FixedNum for f32 {
25        #[inline(always)]
26        fn to_raw_fixed(self, frac: u32, min_val: i64, max_val: i64, saturate: bool) -> i64 {
27            (self as f64).to_raw_fixed(frac, min_val, max_val, saturate)
28        }
29        #[inline(always)]
30        fn from_raw_fixed(raw: i64, frac: u32) -> Self {
31            f64::from_raw_fixed(raw, frac) as f32
32        }
33    }
34
35    impl FixedNum for f64 {
36        #[inline(always)]
37        fn to_raw_fixed(self, frac: u32, min_val: i64, max_val: i64, saturate: bool) -> i64 {
38            if self.is_nan() {
39                return 0;
40            }
41            let scale = (1u64 << frac) as f64;
42            let scaled = self * scale;
43            if saturate {
44                if scaled >= max_val as f64 {
45                    return max_val;
46                }
47                if scaled <= min_val as f64 {
48                    return min_val;
49                }
50            }
51            let sign = if scaled < 0.0 { -1i64 } else { 1i64 };
52            let abs_val = if scaled < 0.0 { -scaled } else { scaled };
53            let abs_int = abs_val as i64;
54            let frac_part = abs_val - (abs_int as f64);
55            let rounded_abs = if frac_part > 0.5 {
56                abs_int + 1
57            } else if frac_part < 0.5 {
58                abs_int
59            } else {
60                if (abs_int & 1) != 0 {
61                    abs_int + 1
62                } else {
63                    abs_int
64                }
65            };
66            let res = sign.wrapping_mul(rounded_abs);
67            if saturate {
68                res.clamp(min_val, max_val)
69            } else {
70                res
71            }
72        }
73
74        #[inline(always)]
75        fn from_raw_fixed(raw: i64, frac: u32) -> Self {
76            (raw as f64) / ((1u64 << frac) as f64)
77        }
78    }
79
80    macro_rules! impl_fixed_num_int {
81        ($($t:ty),*) => {
82            $(
83                impl FixedNum for $t {
84                    #[inline(always)]
85                    fn to_raw_fixed(self, frac: u32, min_val: i64, max_val: i64, saturate: bool) -> i64 {
86                        let shifted = (self as i64).wrapping_shl(frac);
87                        if saturate {
88                            shifted.clamp(min_val, max_val)
89                        } else {
90                            shifted
91                        }
92                    }
93                    #[inline(always)]
94                    fn from_raw_fixed(raw: i64, frac: u32) -> Self {
95                        (raw >> frac) as $t
96                    }
97                }
98            )*
99        };
100    }
101    impl_fixed_num_int!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
102
103    macro_rules! define_fallback_type {
104        ($name:ident, $raw:ident, $wide:ident, $frac:expr) => {
105            #[allow(non_camel_case_types)]
106            #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
107            #[repr(transparent)]
108            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
109            #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110            #[cfg_attr(feature = "serde", serde(transparent))]
111            pub struct $name(pub $raw);
112
113            impl $name {
114                pub const ZERO: Self = Self(0);
115                pub const MAX: Self = Self($raw::MAX);
116                pub const MIN: Self = Self($raw::MIN);
117
118                #[inline(always)]
119                pub const fn from_bits(bits: $raw) -> Self {
120                    Self(bits)
121                }
122
123                #[inline(always)]
124                pub const fn to_bits(self) -> $raw {
125                    self.0
126                }
127
128                #[inline(always)]
129                pub fn from_num<T: FixedNum>(v: T) -> Self {
130                    Self(
131                        T::to_raw_fixed(v, $frac, $raw::MIN as i64, $raw::MAX as i64, false)
132                            as $raw,
133                    )
134                }
135
136                #[inline(always)]
137                pub fn saturating_from_num<T: FixedNum>(v: T) -> Self {
138                    Self(
139                        T::to_raw_fixed(v, $frac, $raw::MIN as i64, $raw::MAX as i64, true) as $raw,
140                    )
141                }
142
143                #[inline(always)]
144                pub fn to_num<T: FixedNum>(self) -> T {
145                    T::from_raw_fixed(self.0 as i64, $frac)
146                }
147
148                #[inline(always)]
149                pub const fn saturating_add(self, rhs: Self) -> Self {
150                    Self(self.0.saturating_add(rhs.0))
151                }
152
153                #[inline(always)]
154                pub const fn saturating_sub(self, rhs: Self) -> Self {
155                    Self(self.0.saturating_sub(rhs.0))
156                }
157
158                #[inline(always)]
159                pub fn saturating_mul(self, rhs: Self) -> Self {
160                    let prod = ((self.0 as $wide) * (rhs.0 as $wide)) >> $frac;
161                    Self(prod.clamp($raw::MIN as $wide, $raw::MAX as $wide) as $raw)
162                }
163
164                #[inline(always)]
165                pub const fn saturating_neg(self) -> Self {
166                    Self(self.0.saturating_neg())
167                }
168
169                #[inline(always)]
170                pub const fn saturating_abs(self) -> Self {
171                    Self(self.0.saturating_abs())
172                }
173
174                #[inline(always)]
175                pub const fn abs(self) -> Self {
176                    Self(self.0.wrapping_abs())
177                }
178
179                #[inline(always)]
180                pub const fn wrapping_add(self, rhs: Self) -> Self {
181                    Self(self.0.wrapping_add(rhs.0))
182                }
183
184                #[inline(always)]
185                pub const fn wrapping_sub(self, rhs: Self) -> Self {
186                    Self(self.0.wrapping_sub(rhs.0))
187                }
188
189                #[inline(always)]
190                pub const fn wrapping_neg(self) -> Self {
191                    Self(self.0.wrapping_neg())
192                }
193
194                #[inline(always)]
195                pub fn wrapping_mul(self, rhs: Self) -> Self {
196                    let prod = (self.0 as $wide).wrapping_mul(rhs.0 as $wide);
197                    Self((prod >> $frac) as $raw)
198                }
199
200                #[inline(always)]
201                pub const fn wrapping_mul_int(self, n: i32) -> Self {
202                    Self(self.0.wrapping_mul(n as $raw))
203                }
204
205                #[inline(always)]
206                pub fn wrapping_div(self, rhs: Self) -> Self {
207                    if rhs.0 == 0 {
208                        return Self(0);
209                    }
210                    let a = (self.0 as $wide) << $frac;
211                    let b = rhs.0 as $wide;
212                    Self((a / b) as $raw)
213                }
214
215                #[inline(always)]
216                pub fn wrapping_div_int(self, n: i32) -> Self {
217                    if n == 0 {
218                        return Self(0);
219                    }
220                    Self(self.0.wrapping_div(n as $raw))
221                }
222
223                #[inline(always)]
224                pub fn recip(self) -> Self {
225                    if self.0 == 0 {
226                        return Self::MAX;
227                    }
228                    let one_shifted = 1i64 << (2 * $frac);
229                    Self((one_shifted / (self.0 as i64)) as $raw)
230                }
231
232                #[inline(always)]
233                pub fn checked_div(self, rhs: Self) -> Option<Self> {
234                    if rhs.0 == 0 {
235                        return None;
236                    }
237                    let a = (self.0 as $wide) << $frac;
238                    let b = rhs.0 as $wide;
239                    let res = a / b;
240                    if res > $raw::MAX as $wide || res < $raw::MIN as $wide {
241                        None
242                    } else {
243                        Some(Self(res as $raw))
244                    }
245                }
246            }
247
248            impl FixedNum for $name {
249                #[inline(always)]
250                fn to_raw_fixed(
251                    self,
252                    frac: u32,
253                    min_val: i64,
254                    max_val: i64,
255                    saturate: bool,
256                ) -> i64 {
257                    let shifted = if frac >= $frac {
258                        (self.0 as i64) << (frac - $frac)
259                    } else {
260                        (self.0 as i64) >> ($frac - frac)
261                    };
262                    if saturate {
263                        shifted.clamp(min_val, max_val)
264                    } else {
265                        shifted
266                    }
267                }
268
269                #[inline(always)]
270                fn from_raw_fixed(raw: i64, frac: u32) -> Self {
271                    let bits = if $frac >= frac {
272                        (raw << ($frac - frac)) as $raw
273                    } else {
274                        (raw >> (frac - $frac)) as $raw
275                    };
276                    Self(bits)
277                }
278            }
279
280            impl core::ops::Add for $name {
281                type Output = Self;
282                #[inline(always)]
283                fn add(self, rhs: Self) -> Self {
284                    Self(self.0.wrapping_add(rhs.0))
285                }
286            }
287
288            impl core::ops::AddAssign for $name {
289                #[inline(always)]
290                fn add_assign(&mut self, rhs: Self) {
291                    self.0 = self.0.wrapping_add(rhs.0);
292                }
293            }
294
295            impl core::ops::Sub for $name {
296                type Output = Self;
297                #[inline(always)]
298                fn sub(self, rhs: Self) -> Self {
299                    Self(self.0.wrapping_sub(rhs.0))
300                }
301            }
302
303            impl core::ops::SubAssign for $name {
304                #[inline(always)]
305                fn sub_assign(&mut self, rhs: Self) {
306                    self.0 = self.0.wrapping_sub(rhs.0);
307                }
308            }
309
310            impl core::ops::Mul for $name {
311                type Output = Self;
312                #[inline(always)]
313                fn mul(self, rhs: Self) -> Self {
314                    let prod = (self.0 as $wide) * (rhs.0 as $wide);
315                    Self((prod >> $frac) as $raw)
316                }
317            }
318
319            impl core::ops::MulAssign for $name {
320                #[inline(always)]
321                fn mul_assign(&mut self, rhs: Self) {
322                    *self = *self * rhs;
323                }
324            }
325
326            impl core::ops::Neg for $name {
327                type Output = Self;
328                #[inline(always)]
329                fn neg(self) -> Self {
330                    Self(self.0.wrapping_neg())
331                }
332            }
333
334            impl core::fmt::Display for $name {
335                fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
336                    write!(f, "{}", self.0)
337                }
338            }
339
340            macro_rules! impl_cmp_int {
341                ($int:ident) => {
342                    impl PartialEq<$int> for $name {
343                        #[inline(always)]
344                        fn eq(&self, other: &$int) -> bool {
345                            let other_fixed = (*other as i64) << $frac;
346                            (self.0 as i64) == other_fixed
347                        }
348                    }
349                    impl PartialOrd<$int> for $name {
350                        #[inline(always)]
351                        fn partial_cmp(&self, other: &$int) -> Option<core::cmp::Ordering> {
352                            let other_fixed = (*other as i64) << $frac;
353                            (self.0 as i64).partial_cmp(&other_fixed)
354                        }
355                    }
356                    impl PartialEq<$name> for $int {
357                        #[inline(always)]
358                        fn eq(&self, other: &$name) -> bool {
359                            other.eq(self)
360                        }
361                    }
362                    impl PartialOrd<$name> for $int {
363                        #[inline(always)]
364                        fn partial_cmp(&self, other: &$name) -> Option<core::cmp::Ordering> {
365                            let self_fixed = (*self as i64) << $frac;
366                            self_fixed.partial_cmp(&(other.0 as i64))
367                        }
368                    }
369                };
370            }
371            impl_cmp_int!(i8);
372            impl_cmp_int!(i16);
373            impl_cmp_int!(i32);
374            impl_cmp_int!(i64);
375            impl_cmp_int!(isize);
376            impl_cmp_int!(u8);
377            impl_cmp_int!(u16);
378            impl_cmp_int!(u32);
379            impl_cmp_int!(u64);
380            impl_cmp_int!(usize);
381        };
382    }
383
384    define_fallback_type!(q7, i8, i32, 7);
385    define_fallback_type!(q15, i16, i32, 15);
386    define_fallback_type!(q31, i32, i64, 31);
387    define_fallback_type!(Q8F7, i16, i32, 7);
388    define_fallback_type!(Q2F14, i16, i32, 14);
389    define_fallback_type!(I16F16, i32, i64, 16);
390}
391
392/// Wide accumulator type used for dot products, sums-of-squares, and other
393/// reductions that need headroom beyond `i32`. This is a plain integer, not
394/// a Q1.63 fixed-point value: nothing here divides by a scale factor, and
395/// several call sites divide by an element count, which would be meaningless
396/// under a `[-1.0, 1.0)`-range fixed-point interpretation.
397#[allow(non_camel_case_types)]
398pub type q63 = i64;
399#[allow(non_camel_case_types)]
400pub type f32_t = f32;
401#[allow(non_camel_case_types)]
402pub type f64_t = f64;
403
404/// Error status returned by functions in `embedded-dsp`.
405#[derive(Debug, Clone, Copy, PartialEq, Eq)]
406#[repr(i8)]
407pub enum Status {
408    /// Operation succeeded without error.
409    Success = 0,
410    /// One or more arguments are invalid.
411    ArgumentError = -1,
412    /// Length of data buffer is invalid or mismatching.
413    LengthError = -2,
414    /// Matrix dimensions are incompatible.
415    SizeMismatch = -3,
416    /// NaN or Infinity was produced during computation.
417    NanInf = -4,
418    /// Matrix is singular and cannot be inverted.
419    Singular = -5,
420    /// Test or verification failed.
421    TestFailure = -6,
422    /// Matrix decomposition failed.
423    DecompositionFailure = -7,
424}
425
426/// Representation of a complex number with real and imaginary components.
427#[derive(Debug, Clone, Copy, PartialEq, Default)]
428#[repr(C)]
429pub struct Complex<T> {
430    pub real: T,
431    pub imag: T,
432}
433
434impl<T> Complex<T> {
435    #[inline(always)]
436    pub const fn new(real: T, imag: T) -> Self {
437        Self { real, imag }
438    }
439}
440
441/// Helper function for saturating multiplication in Q15 format.
442#[inline(always)]
443pub fn q15_mult(a: q15, b: q15) -> q15 {
444    a.saturating_mul(b)
445}
446
447/// Helper function for saturating multiplication in Q31 format.
448#[inline(always)]
449pub fn q31_mult(a: q31, b: q31) -> q31 {
450    a.saturating_mul(b)
451}
452
453/// Helper function for saturating multiplication in Q7 format.
454#[inline(always)]
455pub fn q7_mult(a: q7, b: q7) -> q7 {
456    a.saturating_mul(b)
457}
458
459/// Helper function for saturating division in Q15 format.
460#[inline(always)]
461fn saturating_div_q15(a: q15, b: q15) -> q15 {
462    if b == q15::ZERO {
463        if a >= q15::ZERO { q15::MAX } else { q15::MIN }
464    } else {
465        match a.checked_div(b) {
466            Some(v) => v,
467            None => {
468                if (a >= q15::ZERO) == (b >= q15::ZERO) {
469                    q15::MAX
470                } else {
471                    q15::MIN
472                }
473            }
474        }
475    }
476}
477
478/// Helper function for saturating division in Q31 format.
479#[inline(always)]
480fn saturating_div_q31(a: q31, b: q31) -> q31 {
481    if b == q31::ZERO {
482        if a >= q31::ZERO { q31::MAX } else { q31::MIN }
483    } else {
484        match a.checked_div(b) {
485            Some(v) => v,
486            None => {
487                if (a >= q31::ZERO) == (b >= q31::ZERO) {
488                    q31::MAX
489                } else {
490                    q31::MIN
491                }
492            }
493        }
494    }
495}
496
497// ─────────────────────────────────────────────────────────────────────────────
498// Unified DSP Sample Trait
499// ─────────────────────────────────────────────────────────────────────────────
500
501/// Unified numerical sample trait implemented for floating-point sample types.
502///
503/// Enables writing generic filters, delay lines, oscillators, and processing blocks that operate
504/// seamlessly with `f32` and `f64`.
505pub trait DspSample:
506    Copy
507    + Default
508    + PartialEq
509    + PartialOrd
510    + core::ops::Add<Output = Self>
511    + core::ops::Sub<Output = Self>
512    + core::ops::Mul<Output = Self>
513    + core::ops::Neg<Output = Self>
514{
515    /// Additive identity (`0.0`).
516    const ZERO: Self;
517    /// Multiplicative identity or normalized unity (`1.0`).
518    const ONE: Self;
519
520    /// Saturating addition.
521    fn sat_add(self, rhs: Self) -> Self;
522    /// Saturating subtraction.
523    fn sat_sub(self, rhs: Self) -> Self;
524    /// Saturating multiplication.
525    fn sat_mul(self, rhs: Self) -> Self;
526    /// Saturating division.
527    fn sat_div(self, rhs: Self) -> Self;
528    /// Absolute value.
529    fn abs_val(self) -> Self;
530    /// Convert to floating-point `f32`.
531    fn to_f32(self) -> f32;
532    /// Convert from floating-point `f32`.
533    fn from_f32(val: f32) -> Self;
534}
535
536impl DspSample for f32 {
537    const ZERO: Self = 0.0;
538    const ONE: Self = 1.0;
539
540    #[inline(always)]
541    fn sat_add(self, rhs: Self) -> Self {
542        self + rhs
543    }
544
545    #[inline(always)]
546    fn sat_sub(self, rhs: Self) -> Self {
547        self - rhs
548    }
549
550    #[inline(always)]
551    fn sat_mul(self, rhs: Self) -> Self {
552        self * rhs
553    }
554
555    #[inline(always)]
556    fn sat_div(self, rhs: Self) -> Self {
557        self / rhs
558    }
559
560    #[inline(always)]
561    fn abs_val(self) -> Self {
562        if self < 0.0 { -self } else { self }
563    }
564
565    #[inline(always)]
566    fn to_f32(self) -> f32 {
567        self
568    }
569
570    #[inline(always)]
571    fn from_f32(val: f32) -> Self {
572        val
573    }
574}
575
576impl DspSample for f64 {
577    const ZERO: Self = 0.0;
578    const ONE: Self = 1.0;
579
580    #[inline(always)]
581    fn sat_add(self, rhs: Self) -> Self {
582        self + rhs
583    }
584
585    #[inline(always)]
586    fn sat_sub(self, rhs: Self) -> Self {
587        self - rhs
588    }
589
590    #[inline(always)]
591    fn sat_mul(self, rhs: Self) -> Self {
592        self * rhs
593    }
594
595    #[inline(always)]
596    fn sat_div(self, rhs: Self) -> Self {
597        self / rhs
598    }
599
600    #[inline(always)]
601    fn abs_val(self) -> Self {
602        if self < 0.0 { -self } else { self }
603    }
604
605    #[inline(always)]
606    fn to_f32(self) -> f32 {
607        self as f32
608    }
609
610    #[inline(always)]
611    fn from_f32(val: f32) -> Self {
612        val as f64
613    }
614}
615
616impl DspSample for q15 {
617    const ZERO: Self = Self::ZERO;
618    const ONE: Self = Self::MAX;
619
620    #[inline(always)]
621    fn sat_add(self, rhs: Self) -> Self {
622        self.saturating_add(rhs)
623    }
624
625    #[inline(always)]
626    fn sat_sub(self, rhs: Self) -> Self {
627        self.saturating_sub(rhs)
628    }
629
630    #[inline(always)]
631    fn sat_mul(self, rhs: Self) -> Self {
632        self.saturating_mul(rhs)
633    }
634
635    #[inline(always)]
636    fn sat_div(self, rhs: Self) -> Self {
637        saturating_div_q15(self, rhs)
638    }
639
640    #[inline(always)]
641    fn abs_val(self) -> Self {
642        self.saturating_abs()
643    }
644
645    #[inline(always)]
646    fn to_f32(self) -> f32 {
647        self.to_num()
648    }
649
650    #[inline(always)]
651    fn from_f32(val: f32) -> Self {
652        Self::saturating_from_num(val)
653    }
654}
655
656impl DspSample for q31 {
657    const ZERO: Self = Self::ZERO;
658    const ONE: Self = Self::MAX;
659
660    #[inline(always)]
661    fn sat_add(self, rhs: Self) -> Self {
662        self.saturating_add(rhs)
663    }
664
665    #[inline(always)]
666    fn sat_sub(self, rhs: Self) -> Self {
667        self.saturating_sub(rhs)
668    }
669
670    #[inline(always)]
671    fn sat_mul(self, rhs: Self) -> Self {
672        self.saturating_mul(rhs)
673    }
674
675    #[inline(always)]
676    fn sat_div(self, rhs: Self) -> Self {
677        saturating_div_q31(self, rhs)
678    }
679
680    #[inline(always)]
681    fn abs_val(self) -> Self {
682        self.saturating_abs()
683    }
684
685    #[inline(always)]
686    fn to_f32(self) -> f32 {
687        self.to_num()
688    }
689
690    #[inline(always)]
691    fn from_f32(val: f32) -> Self {
692        Self::saturating_from_num(val)
693    }
694}
695
696// ─────────────────────────────────────────────────────────────────────────────
697// Complex Number Operations
698// ─────────────────────────────────────────────────────────────────────────────
699
700impl<T: core::ops::Add<Output = T>> core::ops::Add for Complex<T> {
701    type Output = Self;
702    #[inline(always)]
703    fn add(self, rhs: Self) -> Self {
704        Self {
705            real: self.real + rhs.real,
706            imag: self.imag + rhs.imag,
707        }
708    }
709}
710
711impl<T: core::ops::Sub<Output = T>> core::ops::Sub for Complex<T> {
712    type Output = Self;
713    #[inline(always)]
714    fn sub(self, rhs: Self) -> Self {
715        Self {
716            real: self.real - rhs.real,
717            imag: self.imag - rhs.imag,
718        }
719    }
720}
721
722impl<T: core::ops::Neg<Output = T>> core::ops::Neg for Complex<T> {
723    type Output = Self;
724    #[inline(always)]
725    fn neg(self) -> Self {
726        Self {
727            real: -self.real,
728            imag: -self.imag,
729        }
730    }
731}
732
733impl<T: Copy + core::ops::Add<Output = T> + core::ops::Sub<Output = T> + core::ops::Mul<Output = T>>
734    core::ops::Mul for Complex<T>
735{
736    type Output = Self;
737    #[inline(always)]
738    fn mul(self, rhs: Self) -> Self {
739        Self {
740            real: self.real * rhs.real - self.imag * rhs.imag,
741            imag: self.real * rhs.imag + self.imag * rhs.real,
742        }
743    }
744}
745
746impl<T: Copy + core::ops::Mul<Output = T>> core::ops::Mul<T> for Complex<T> {
747    type Output = Self;
748    #[inline(always)]
749    fn mul(self, scalar: T) -> Self {
750        Self {
751            real: self.real * scalar,
752            imag: self.imag * scalar,
753        }
754    }
755}