use std::f32::consts::PI;
use num_complex::Complex32;
use crate::error::{Error, Result};
use crate::nco::{Osc, OscScheme};
#[derive(Clone, Debug)]
pub struct Fskmod {
k: usize, bandwidth: f32, m_size: usize, m2: f32, oscillator: Osc, }
impl Fskmod {
pub fn new(m: usize, k: usize, bandwidth: f32) -> Result<Self> {
if m == 0 {
return Err(Error::Config("bits/symbol must be greater than 0".into()));
}
if k < 2 || k > 2048 {
return Err(Error::Config("samples/symbol must be in [2^m, 2048]".into()));
}
if !(0.0..0.5).contains(&bandwidth) {
return Err(Error::Config("bandwidth must be in (0,0.5)".into()));
}
let m_size = 1 << m;
let m2 = 0.5 * (m_size - 1) as f32;
let mut q = Self {
k,
bandwidth,
m_size,
m2,
oscillator: Osc::new(OscScheme::Vco),
};
q.reset()?;
Ok(q)
}
pub fn reset(&mut self) -> Result<()> {
self.oscillator.reset();
Ok(())
}
pub fn modulate(&mut self, s: usize, y: &mut [Complex32]) -> Result<()> {
if s >= self.m_size {
return Err(Error::Range(format!(
"input symbol ({}) exceeds maximum ({})",
s, self.m_size
)));
}
if y.len() != self.k {
return Err(Error::Range(format!(
"output buffer length ({}) must match samples/symbol ({})",
y.len(), self.k
)));
}
let dphi = ((s as f32) - self.m2) * 2.0 * PI * self.bandwidth / self.m2;
self.oscillator.set_frequency(dphi);
for i in 0..self.k {
y[i] = self.oscillator.cexp();
self.oscillator.step();
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_macro::autotest_annotate;
#[test]
fn test_fskmod_create() {
let result = Fskmod::new(2, 8, 0.25);
assert!(result.is_ok());
let result = Fskmod::new(0, 8, 0.25);
assert!(result.is_err());
let result = Fskmod::new(2, 1, 0.25);
assert!(result.is_err());
let result = Fskmod::new(2, 8, 0.6);
assert!(result.is_err());
}
#[test]
fn test_fskmod_modulate() -> Result<()> {
let mut mod_ = Fskmod::new(2, 8, 0.25)?;
let mut y = vec![Complex32::new(0.0, 0.0); 8];
assert!(mod_.modulate(0, &mut y).is_ok());
assert!(mod_.modulate(4, &mut y).is_err());
let mut y_short = vec![Complex32::new(0.0, 0.0); 4];
assert!(mod_.modulate(0, &mut y_short).is_err());
Ok(())
}
#[test]
#[autotest_annotate(autotest_fskmod_copy)]
fn test_fskmod_copy() -> Result<()> {
let m = 3; let k = 200; let bw = 0.2345;
let mut mod_orig = Fskmod::new(m, k, bw)?;
let num_symbols = 96;
let mut buf_orig = vec![Complex32::new(0.0, 0.0); k];
let mut buf_copy = vec![Complex32::new(0.0, 0.0); k];
let mut ms = crate::sequence::MSequence::create_default(7)?;
for _ in 0..num_symbols {
let s = ms.generate_symbol(m as u32) as usize;
mod_orig.modulate(s, &mut buf_orig)?;
}
let mut mod_copy = mod_orig.clone();
for _ in 0..num_symbols {
let s = ms.generate_symbol(m as u32) as usize;
mod_orig.modulate(s, &mut buf_orig)?;
mod_copy.modulate(s, &mut buf_copy)?;
assert_eq!(buf_orig, buf_copy);
}
Ok(())
}
}