use super::{Stream, Tag};
use num_complex::Complex;
#[derive(Debug, Clone)]
pub struct SimpleUpsampler {
rate_in: u32,
rate_out: u32,
alpha: f32,
increment: f32,
prev_sample: Complex<f32>,
output: Vec<Complex<f32>>,
idx_out: usize,
}
impl SimpleUpsampler {
pub fn new(rate_in: u32, rate_out: u32) -> Self {
assert!(rate_in > 0, "Input sample rate must be greater than 0");
assert!(rate_out > 0, "Output sample rate must be greater than 0");
assert!(
rate_in <= rate_out,
"SimpleUpsampler only supports upsampling (rate_in <= rate_out)"
);
let increment = rate_in as f32 / rate_out as f32;
Self {
rate_in,
rate_out,
alpha: 0.0,
increment,
prev_sample: Complex::new(0.0, 0.0),
output: Vec::new(),
idx_out: 0,
}
}
pub fn input_rate(&self) -> u32 {
self.rate_in
}
pub fn output_rate(&self) -> u32 {
self.rate_out
}
pub fn ratio(&self) -> f32 {
1.0 / self.increment
}
pub fn reset(&mut self) {
self.alpha = 0.0;
self.prev_sample = Complex::new(0.0, 0.0);
self.idx_out = 0;
self.output.clear();
}
}
impl Stream<Complex<f32>, Complex<f32>> for SimpleUpsampler {
fn receive(&mut self, data: &[Complex<f32>], _tag: &mut Tag) -> Vec<Complex<f32>> {
if data.is_empty() {
return Vec::new();
}
let estimated_output_len = ((data.len() as f32 * self.ratio()).ceil() as usize).max(1);
let mut output = Vec::with_capacity(estimated_output_len + 1);
let mut a = self.prev_sample;
let mut alpha = self.alpha;
for &b in data {
loop {
let interpolated = a * (1.0 - alpha) + b * alpha;
output.push(interpolated);
alpha += self.increment;
if alpha >= 1.0 {
break;
}
}
alpha -= 1.0;
a = b;
}
self.prev_sample = a;
self.alpha = alpha;
output
}
fn reset(&mut self) {
Self::reset(self);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_upsampler_creation() {
let upsampler = SimpleUpsampler::new(3_000_000, 3_072_000);
assert!((upsampler.ratio() - 1.024).abs() < 0.001);
}
#[test]
fn test_upsampler_interpolation() {
let mut upsampler = SimpleUpsampler::new(1, 3); let input = vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)];
let mut tag = Tag::default();
let output = upsampler.receive(&input, &mut tag);
assert!(output.len() >= 3);
assert!((output[0].re - 0.0).abs() < 0.1);
let has_interpolated = output.iter().any(|s| s.re > 0.1 && s.re < 0.9);
assert!(
has_interpolated,
"Should have interpolated samples between 0 and 1"
);
}
#[test]
#[should_panic(expected = "SimpleUpsampler only supports upsampling")]
fn test_upsampler_downsampling_panics() {
SimpleUpsampler::new(3_072_000, 3_000_000);
}
#[test]
fn test_upsampler_output_count() {
let mut upsampler = SimpleUpsampler::new(3_000_000, 3_072_000);
let input_len = 1000;
let input: Vec<Complex<f32>> = (0..input_len)
.map(|i| Complex::new(i as f32, (i as f32) * 0.5))
.collect();
let mut tag = Tag::default();
let output = upsampler.receive(&input, &mut tag);
let expected_len = (input_len as f32 * 1.024).round() as usize;
assert!(
output.len() >= expected_len - 1 && output.len() <= expected_len + 1,
"Output length {} not within ±1 of expected {}",
output.len(),
expected_len
);
}
#[test]
fn test_upsampler_no_phase_shift_i_q() {
let mut upsampler = SimpleUpsampler::new(3_000_000, 3_072_000);
let input: Vec<Complex<f32>> = (0..100)
.map(|i| Complex::new((i as f32).sin(), (i as f32).cos()))
.collect();
let mut tag = Tag::default();
let output = upsampler.receive(&input, &mut tag);
let non_zero_i = output.iter().filter(|s| s.re.abs() > 0.01).count();
let non_zero_q = output.iter().filter(|s| s.im.abs() > 0.01).count();
assert!(non_zero_i > output.len() / 2, "I channel has samples");
assert!(non_zero_q > output.len() / 2, "Q channel has samples");
}
#[test]
fn test_upsampler_reset() {
let mut upsampler = SimpleUpsampler::new(3_000_000, 3_072_000);
let input = vec![Complex::new(1.0, 1.0)];
let mut tag = Tag::default();
let _ = upsampler.receive(&input, &mut tag);
upsampler.reset();
assert_eq!(upsampler.alpha, 0.0);
assert_eq!(upsampler.prev_sample, Complex::new(0.0, 0.0));
}
#[test]
fn test_upsampler_streaming() {
let mut upsampler = SimpleUpsampler::new(3_000_000, 3_072_000);
let chunk1: Vec<Complex<f32>> = (0..100).map(|i| Complex::new(i as f32, 0.0)).collect();
let chunk2: Vec<Complex<f32>> = (100..200).map(|i| Complex::new(i as f32, 0.0)).collect();
let mut tag = Tag::default();
let output1 = upsampler.receive(&chunk1, &mut tag);
let output2 = upsampler.receive(&chunk2, &mut tag);
assert!(!output1.is_empty());
assert!(!output2.is_empty());
let total_input = chunk1.len() + chunk2.len();
let total_output = output1.len() + output2.len();
let expected = (total_input as f32 * 1.024).round() as usize;
assert!(
total_output >= expected - 2 && total_output <= expected + 2,
"Total output {} not within ±2 of expected {}",
total_output,
expected
);
}
}