use crate::error::{Error, Result};
use num_complex::Complex32;
use std::f32::consts::PI;
#[derive(Debug, Clone)]
pub struct Freqdem {
ref_: f32, r_prime: Complex32, }
impl Freqdem {
pub fn new(kf: f32) -> Result<Self> {
if kf <= 0.0 {
return Err(Error::Config(format!(
"modulation factor {:.4e} must be greater than 0",
kf
)));
}
let mut q = Self {
ref_: 1.0 / (2.0 * PI * kf),
r_prime: Complex32::new(0.0, 0.0),
};
q.reset()?;
Ok(q)
}
pub fn reset(&mut self) -> Result<()> {
self.r_prime = Complex32::new(0.0, 0.0);
Ok(())
}
pub fn demodulate(&mut self, r: Complex32) -> Result<f32> {
let m = (self.r_prime.conj() * r).arg() * self.ref_;
self.r_prime = r;
Ok(m)
}
pub fn demodulate_block(&mut self, r: &[Complex32], m: &mut [f32]) -> Result<()> {
if r.len() != m.len() {
return Err(Error::Range(
"input and output arrays must be same length".into()
));
}
for (x, y) in r.iter().zip(m.iter_mut()) {
*y = self.demodulate(*x)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_macro::autotest_annotate;
use crate::modem::freqmod::Freqmod;
use approx::assert_relative_eq;
#[test]
fn test_freqdem_create() {
let result = Freqdem::new(0.5);
assert!(result.is_ok());
let result = Freqdem::new(0.0);
assert!(result.is_err());
let result = Freqdem::new(-1.0);
assert!(result.is_err());
}
#[test]
fn test_freqdem_demodulate() -> Result<()> {
let mut dem = Freqdem::new(0.5)?;
let r = Complex32::new(1.0, 0.0);
let result = dem.demodulate(r);
assert!(result.is_ok());
let r = vec![Complex32::new(1.0, 0.0); 10];
let mut m = vec![0.0f32; 10];
assert!(dem.demodulate_block(&r, &mut m).is_ok());
let mut m_short = vec![0.0f32; 5];
assert!(dem.demodulate_block(&r, &mut m_short).is_err());
Ok(())
}
fn freqmodem_test(kf: f32) -> Result<()> {
let num_samples = 1024;
let tol = 5e-2f32;
let mut mod_ = Freqmod::new(kf)?; let mut dem = Freqdem::new(kf)?;
let mut m = vec![0.0f32; num_samples]; let mut r = vec![Complex32::new(0.0, 0.0); num_samples]; let mut y = vec![0.0f32; num_samples];
for i in 0..num_samples {
let i = i as f32;
m[i as usize] = 0.3 * (2.0 * PI * 0.013 * i + 0.0).cos() +
0.2 * (2.0 * PI * 0.021 * i + 0.4).cos() +
0.4 * (2.0 * PI * 0.037 * i + 1.7).cos();
}
mod_.modulate_block(&m, &mut r)?;
dem.demodulate_block(&r, &mut y)?;
for i in 1..num_samples {
assert_relative_eq!(y[i], m[i], epsilon = tol);
}
Ok(())
}
#[test]
#[autotest_annotate(autotest_freqmodem_kf_0_02)]
fn test_freqmodem_kf_0_02() -> Result<()> { freqmodem_test(0.02) }
#[test]
#[autotest_annotate(autotest_freqmodem_kf_0_04)]
fn test_freqmodem_kf_0_04() -> Result<()> { freqmodem_test(0.04) }
#[test]
#[autotest_annotate(autotest_freqmodem_kf_0_08)]
fn test_freqmodem_kf_0_08() -> Result<()> { freqmodem_test(0.08) }
}