use num_complex::Complex32;
use crate::error::{Error, Result};
use crate::fft::{Fft, Direction};
#[derive(Clone, Debug)]
pub struct Fskdem {
k: usize, m_size: usize, k_size: usize, buf_time: Vec<Complex32>, buf_freq: Vec<Complex32>, fft: Fft<f32>, demod_map: Vec<usize>, s_demod: usize, }
impl Fskdem {
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 df = bandwidth / m2;
let mut err_min = 1e9f32;
let k_min = k;
let k_max = (k * 4).min(16);
let mut k_size = k_min;
for k_hat in k_min..=k_max {
let v = 0.5 * df * k_hat as f32;
let err = (v.round() - v).abs();
if k_hat == k_min || err < err_min {
k_size = k_hat;
err_min = err;
}
if err < 1e-6 {
break;
}
}
let mut demod_map = vec![0; m_size];
for i in 0..m_size {
let freq = ((i as f32) - m2) * bandwidth / m2;
let idx = freq * k_size as f32;
let index = if idx < 0.0 {
(idx + k_size as f32).round() as usize
} else {
idx.round() as usize
};
demod_map[i] = index;
}
for i in 1..m_size {
if demod_map[i] == demod_map[i-1] {
return Err(Error::Config("demod map is not unique; consider increasing bandwidth".into()));
}
}
let buf_time = vec![Complex32::new(0.0, 0.0); k_size];
let buf_freq = vec![Complex32::new(0.0, 0.0); k_size];
let fft = Fft::new(k_size, Direction::Forward);
let mut q = Self {
k,
m_size,
k_size,
buf_time,
buf_freq,
fft,
demod_map,
s_demod: 0,
};
q.reset()?;
Ok(q)
}
pub fn reset(&mut self) -> Result<()> {
self.buf_time.fill(Complex32::new(0.0, 0.0));
self.buf_freq.fill(Complex32::new(0.0, 0.0));
self.s_demod = 0;
Ok(())
}
pub fn demodulate(&mut self, y: &[Complex32]) -> Result<usize> {
if y.len() != self.k {
return Err(Error::Range("input length must match samples/symbol".into()));
}
self.buf_time[..self.k].copy_from_slice(y);
self.fft.run(&mut self.buf_time, &mut self.buf_freq);
let mut vmax = 0.0;
self.s_demod = 0;
for s in 0..self.m_size {
let v = self.buf_freq[self.demod_map[s]].norm();
if s == 0 || v > vmax {
self.s_demod = s;
vmax = v;
}
}
Ok(self.s_demod)
}
pub fn get_frequency_error(&self) -> f32 {
let vm = self.buf_freq[(self.s_demod + self.k_size - 1) % self.k_size].norm(); let v0 = self.buf_freq[self.s_demod].norm(); let vp = self.buf_freq[(self.s_demod + 1) % self.k_size].norm();
(vp - vm) / v0
}
pub fn get_symbol_energy(&self, s: usize, range: usize) -> Result<f32> {
if s >= self.m_size {
return Err(Error::Range(format!(
"input symbol ({}) exceeds maximum ({})",
s, self.m_size
)));
}
let range = range.min(self.k_size);
let index = self.demod_map[s];
let mut energy = self.buf_freq[index].norm_sqr();
for i in 1..=range {
let i0 = (index + i) % self.k_size;
let i1 = (index + self.k_size - i) % self.k_size;
energy += self.buf_freq[i0].norm_sqr();
energy += self.buf_freq[i1].norm_sqr();
}
Ok(energy)
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_macro::autotest_annotate;
use crate::{modem::fskmod::Fskmod, random::randnf};
#[test]
fn test_fskdem_create() {
let result = Fskdem::new(2, 8, 0.25);
assert!(result.is_ok());
let result = Fskdem::new(0, 8, 0.25);
assert!(result.is_err());
let result = Fskdem::new(2, 1, 0.25);
assert!(result.is_err());
let result = Fskdem::new(2, 8, 0.6);
assert!(result.is_err());
}
#[test]
fn test_fskdem_demodulate() -> Result<()> {
let mut dem = Fskdem::new(2, 8, 0.25)?;
let y = vec![Complex32::new(0.0, 0.0); 8];
assert!(dem.demodulate(&y).is_ok());
let y_short = vec![Complex32::new(0.0, 0.0); 4];
assert!(dem.demodulate(&y_short).is_err());
Ok(())
}
fn fskmodem_test_mod_demod(m: usize, k: usize, bandwidth: f32) -> Result<()> {
let mut modulator = Fskmod::new(m, k, bandwidth)?;
let mut demodulator = Fskdem::new(m, k, bandwidth)?;
let m_size = 1 << m; let mut buf = vec![Complex32::new(0.0, 0.0); k];
for sym_in in 0..m_size {
modulator.modulate(sym_in, &mut buf)?;
let sym_out = demodulator.demodulate(&buf)?;
assert_eq!(sym_in, sym_out);
}
Ok(())
}
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M2)]
fn test_fskmodem_norm_m2() -> Result<()> { fskmodem_test_mod_demod(1, 4, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M4)]
fn test_fskmodem_norm_m4() -> Result<()> { fskmodem_test_mod_demod(2, 8, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M8)]
fn test_fskmodem_norm_m8() -> Result<()> { fskmodem_test_mod_demod(3, 16, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M16)]
fn test_fskmodem_norm_m16() -> Result<()> { fskmodem_test_mod_demod(4, 32, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M32)]
fn test_fskmodem_norm_m32() -> Result<()> { fskmodem_test_mod_demod(5, 64, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M64)]
fn test_fskmodem_norm_m64() -> Result<()> { fskmodem_test_mod_demod(6, 128, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M128)]
fn test_fskmodem_norm_m128() -> Result<()> { fskmodem_test_mod_demod(7, 256, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M256)]
fn test_fskmodem_norm_m256() -> Result<()> { fskmodem_test_mod_demod(8, 512, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M512)]
fn test_fskmodem_norm_m512() -> Result<()> { fskmodem_test_mod_demod(9, 1024, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_norm_M1024)]
fn test_fskmodem_norm_m1024() -> Result<()> { fskmodem_test_mod_demod(10, 2048, 0.25) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M2)]
fn test_fskmodem_misc_m2() -> Result<()> { fskmodem_test_mod_demod(1, 5, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M4)]
fn test_fskmodem_misc_m4() -> Result<()> { fskmodem_test_mod_demod(2, 10, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M8)]
fn test_fskmodem_misc_m8() -> Result<()> { fskmodem_test_mod_demod(3, 20, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M16)]
fn test_fskmodem_misc_m16() -> Result<()> { fskmodem_test_mod_demod(4, 30, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M32)]
fn test_fskmodem_misc_m32() -> Result<()> { fskmodem_test_mod_demod(5, 60, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M64)]
fn test_fskmodem_misc_m64() -> Result<()> { fskmodem_test_mod_demod(6, 100, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M128)]
fn test_fskmodem_misc_m128() -> Result<()> { fskmodem_test_mod_demod(7, 200, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M256)]
fn test_fskmodem_misc_m256() -> Result<()> { fskmodem_test_mod_demod(8, 500, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M512)]
fn test_fskmodem_misc_m512() -> Result<()> { fskmodem_test_mod_demod(9, 1000, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskmodem_misc_M1024)]
fn test_fskmodem_misc_m1024() -> Result<()> { fskmodem_test_mod_demod(10, 2000, 0.3721451) }
#[test]
#[autotest_annotate(autotest_fskdem_copy)]
fn test_fskdem_copy() -> Result<()> {
let m = 3; let k = 200; let bw = 0.2345;
let mut dem_orig = Fskdem::new(m, k, bw)?;
let num_symbols = 96;
let mut buf = vec![Complex32::new(0.0, 0.0); k];
for _ in 0..num_symbols {
for j in 0..k {
buf[j] = Complex32::new(randnf(), randnf());
}
dem_orig.demodulate(&buf)?;
}
let mut dem_copy = dem_orig.clone();
for _ in 0..num_symbols {
for j in 0..k {
buf[j] = Complex32::new(randnf(), randnf());
}
let sym_orig = dem_orig.demodulate(&buf)?;
let sym_copy = dem_copy.demodulate(&buf)?;
assert_eq!(sym_orig, sym_copy);
}
Ok(())
}
}