use crate::error::{Error, Result};
use num_complex::Complex32;
use std::f32::consts::PI;
#[derive(Debug, Clone)]
pub struct Freqmod {
ref_: f32, sincos_table_len: usize, sincos_table_phase: u16, sincos_table: Vec<Complex32>, }
impl Freqmod {
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_: kf * (1 << 16) as f32,
sincos_table_len: 1024,
sincos_table_phase: 0,
sincos_table: Vec::with_capacity(1024),
};
for i in 0..q.sincos_table_len {
let phase = 2.0 * PI * i as f32 / q.sincos_table_len as f32;
q.sincos_table.push(Complex32::from_polar(1.0, phase));
}
q.reset()?;
Ok(q)
}
pub fn reset(&mut self) -> Result<()> {
self.sincos_table_phase = 0;
Ok(())
}
pub fn modulate(&mut self, m: f32) -> Result<Complex32> {
self.sincos_table_phase = (self.sincos_table_phase as i32
+ (1 << 16)
+ (self.ref_ * m).round() as i32) as u16;
let index = ((self.sincos_table_phase as u32 + 0x0020) >> 6) & 0x03ff;
Ok(self.sincos_table[index as usize])
}
pub fn modulate_block(&mut self, m: &[f32], s: &mut [Complex32]) -> Result<()> {
if m.len() != s.len() {
return Err(Error::Range(
"input and output arrays must be same length".into()
));
}
for (x, y) in m.iter().zip(s.iter_mut()) {
*y = self.modulate(*x)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_freqmod_create() {
let result = Freqmod::new(0.5);
assert!(result.is_ok());
let result = Freqmod::new(0.0);
assert!(result.is_err());
let result = Freqmod::new(-1.0);
assert!(result.is_err());
}
#[test]
fn test_freqmod_modulate() -> Result<()> {
let mut mod_ = Freqmod::new(0.5)?;
let result = mod_.modulate(1.0);
assert!(result.is_ok());
let m = vec![0.0f32; 10];
let mut s = vec![Complex32::new(0.0, 0.0); 10];
assert!(mod_.modulate_block(&m, &mut s).is_ok());
let mut s_short = vec![Complex32::new(0.0, 0.0); 5];
assert!(mod_.modulate_block(&m, &mut s_short).is_err());
Ok(())
}
}