From da69d0c76e9b2e424d856a3e0f95e2bfad077d07 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Sat, 14 Mar 2020 23:08:57 +0100 Subject: [PATCH] Implemented generic up and downsampler --- .gitignore | 3 ++ Cargo.toml | 10 ++++ src/amdemod.rs | 43 +++++++++++++++++ src/firfilter.rs | 47 ++++++++++++++++++ src/lib.rs | 3 ++ src/resamplers.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 226 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/amdemod.rs create mode 100644 src/firfilter.rs create mode 100644 src/lib.rs create mode 100644 src/resamplers.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0b8605f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rusty-dsp" +version = "0.1.0" +authors = ["LongHairedHacker "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +num = "*" diff --git a/src/amdemod.rs b/src/amdemod.rs new file mode 100644 index 0000000..62f993c --- /dev/null +++ b/src/amdemod.rs @@ -0,0 +1,43 @@ +use std::iter::FromIterator; + +pub struct SquaringAMDemodulator<'a> { + iterator: Box + 'a>, +} + +impl<'a> SquaringAMDemodulator<'a> { + pub fn from(iterator1: I) -> SquaringAMDemodulator<'a> + where + I: Iterator + 'a, + { + SquaringAMDemodulator { + iterator: Box::new(iterator1), + } + } +} + +impl<'a> Iterator for SquaringAMDemodulator<'a> { + type Item = f32; + + fn next(&mut self) -> Option { + match self.iterator.next() { + Some(x) => Some((x * x).sqrt()), + None => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_amdemod() { + let test_data = vec![-1_f32, 2_f32, -3_f32, 4_f32, -5_f32]; + + let demod = SquaringAMDemodulator::from(test_data.into_iter()); + + let result_data = Vec::from_iter(demod); + assert_eq!(result_data.len(), 5); + assert_eq!(result_data, [1_f32, 2_f32, 3_f32, 4_f32, 5_f32]); + } +} diff --git a/src/firfilter.rs b/src/firfilter.rs new file mode 100644 index 0000000..39baae9 --- /dev/null +++ b/src/firfilter.rs @@ -0,0 +1,47 @@ +pub struct FIRFilter<'a> { + coeffs: &'a [f32], + state: Vec, + pos: usize, + iterator: Box + 'a>, +} + +impl<'a> FIRFilter<'a> { + pub fn from(iterator: I, coeffs: &'a [f32]) -> FIRFilter<'a> + where + I: Iterator + 'a, + { + let mut state = Vec::new(); + for _ in 0..coeffs.len() { + state.push(0.0); + } + + FIRFilter { + coeffs: coeffs, + state: state, + pos: 0, + iterator: Box::new(iterator), + } + } +} + +impl<'a> Iterator for FIRFilter<'a> { + type Item = f32; + + fn next(&mut self) -> Option { + let cur = match self.iterator.next() { + Some(x) => x, + None => return None, + }; + + self.pos = (self.pos + 1) % self.coeffs.len(); + self.state[self.pos] = cur; + + let mut result = 0.0; + for i in 0..self.coeffs.len() { + let pos = (self.pos + self.coeffs.len() - i) % self.coeffs.len(); + result += self.state[pos] * self.coeffs[i]; + } + + Some(result) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..51dd74e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +mod amdemod; +mod firfilter; +mod resamplers; diff --git a/src/resamplers.rs b/src/resamplers.rs new file mode 100644 index 0000000..0097535 --- /dev/null +++ b/src/resamplers.rs @@ -0,0 +1,120 @@ +use num::traits::{Num, NumCast}; +use std::iter::FromIterator; + +pub struct Upsampler<'a, NumType> +where + NumType: Num + Clone, +{ + factor: u16, + state: u16, + sample: Option, + iterator: Box + 'a>, +} + +impl<'a, NumType> Upsampler<'a, NumType> +where + NumType: Num + Clone, +{ + pub fn from(iterator: I, factor: u16) -> Upsampler<'a, NumType> + where + I: Iterator + 'a, + { + Upsampler { + factor: factor, + state: 0, + sample: Some(NumType::zero()), + iterator: Box::new(iterator), + } + } +} + +impl<'a, NumType> Iterator for Upsampler<'a, NumType> +where + NumType: Num + Clone, +{ + type Item = NumType; + + fn next(&mut self) -> Option { + if self.state == 0 { + self.sample = self.iterator.next(); + } + self.state = (self.state + 1) % self.factor; + + return self.sample.clone(); + } +} + +pub struct Downsampler<'a, NumType> +where + NumType: Num + Clone + NumCast, +{ + factor: u16, + iterator: Box + 'a>, +} + +impl<'a, NumType> Downsampler<'a, NumType> +where + NumType: Num + Clone + NumCast, +{ + pub fn from(iterator: I, factor: u16) -> Downsampler<'a, NumType> + where + I: Iterator + 'a, + { + Downsampler { + factor: factor, + iterator: Box::new(iterator), + } + } +} + +impl<'a, NumType> Iterator for Downsampler<'a, NumType> +where + NumType: Num + Clone + NumCast, +{ + type Item = NumType; + + fn next(&mut self) -> Option { + let mut result = NumType::zero(); + for _ in 0..self.factor { + match self.iterator.next() { + Some(x) => result = result + x, + None => return None, + } + } + result = result / NumType::from(self.factor).unwrap(); + + return Some(result); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn upsampler_test() { + let test_data = vec![1_f32, 2_f32, 3_f32, 4_f32, 5_f32]; + + let upsampler = Upsampler::from(test_data.into_iter(), 2); + + let result_data = Vec::from_iter(upsampler); + assert_eq!(result_data.len(), 10); + assert_eq!( + result_data, + [1_f32, 1_f32, 2_f32, 2_f32, 3_f32, 3_f32, 4_f32, 4_f32, 5_f32, 5_f32] + ); + } + + #[test] + fn downsampler_test() { + let test_data = vec![ + 1_f32, 1_f32, 2_f32, 2_f32, 3_f32, 3_f32, 4_f32, 4_f32, 5_f32, 5_f32, + ]; + + let downsampler = Downsampler::from(test_data.into_iter(), 2); + + let result_data = Vec::from_iter(downsampler); + assert_eq!(result_data.len(), 5); + assert_eq!(result_data, [1_f32, 2_f32, 3_f32, 4_f32, 5_f32]); + } +}