use alloc::vec;
use alloc::vec::Vec;
use core::f32::consts::PI;
#[cfg(not(feature = "std"))]
use num_traits::Float;
use super::baseband::CENTER_HZ;
use super::demod::{N_SYMBOLS, NSPS_BASEBAND, TONE_SPACING_HZ};
const NFILT: usize = 360;
pub fn subtract_signal_baseband(
idat: &mut [f32],
qdat: &mut [f32],
f0_audio_hz: f32,
shift_baseband: i32,
drift_hz: f32,
channel_symbols: &[u8; N_SYMBOLS],
) {
debug_assert_eq!(idat.len(), qdat.len());
let np = idat.len() as i32;
let nsig = N_SYMBOLS * NSPS_BASEBAND; let f0_baseband_hz = f0_audio_hz - CENTER_HZ;
let mut refi = vec![0.0f32; nsig];
let mut refq = vec![0.0f32; nsig];
let dt = 1.0 / super::baseband::BASEBAND_RATE;
let twopidt = 2.0 * PI * dt;
let mut phi = 0.0f32;
for i in 0..N_SYMBOLS {
let cs = channel_symbols[i] as f32;
let dphi = twopidt
* (f0_baseband_hz
+ (drift_hz / 2.0) * (i as f32 - N_SYMBOLS as f32 / 2.0)
/ (N_SYMBOLS as f32 / 2.0)
+ (cs - 1.5) * TONE_SPACING_HZ);
for j in 0..NSPS_BASEBAND {
let ii = NSPS_BASEBAND * i + j;
refi[ii] = phi.cos();
refq[ii] = phi.sin();
phi += dphi;
}
}
let mut window = [0.0f32; NFILT];
let mut norm = 0.0f32;
for i in 0..NFILT {
window[i] = (PI * i as f32 / (NFILT - 1) as f32).sin();
norm += window[i];
}
for w in window.iter_mut() {
*w /= norm;
}
let mut partial = [0.0f32; NFILT];
for i in 1..NFILT {
partial[i] = partial[i - 1] + window[i];
}
let pad = NFILT;
let nc2 = nsig + 2 * NFILT;
let mut ci = vec![0.0f32; nc2];
let mut cq = vec![0.0f32; nc2];
for i in 0..nsig {
let k = shift_baseband + i as i32;
if k > 0 && k < np {
let id = idat[k as usize];
let qd = qdat[k as usize];
ci[i + pad] = id * refi[i] + qd * refq[i];
cq[i + pad] = qd * refi[i] - id * refq[i];
}
}
let mut cfi = vec![0.0f32; nc2];
let mut cfq = vec![0.0f32; nc2];
let half = NFILT / 2;
for i in half..(nc2 - half) {
let mut acc_i = 0.0f32;
let mut acc_q = 0.0f32;
for j in 0..NFILT {
acc_i += window[j] * ci[i - half + j];
acc_q += window[j] * cq[i - half + j];
}
cfi[i] = acc_i;
cfq[i] = acc_q;
}
for i in 0..nsig {
let n = if i < half {
partial[half + i]
} else if i > nsig - 1 - half {
partial[half + nsig - 1 - i]
} else {
1.0
};
let k = shift_baseband + i as i32;
let j = i + pad;
if k > 0 && k < np && n > 0.0 {
idat[k as usize] -= (cfi[j] * refi[i] - cfq[j] * refq[i]) / n;
qdat[k as usize] -= (cfi[j] * refq[i] + cfq[j] * refi[i]) / n;
}
}
}
pub fn subtract_all<F>(
idat: &mut [f32],
qdat: &mut [f32],
decodes: &[super::WsprDecode],
audio_to_baseband_lag: F,
) where
F: Fn(&super::WsprDecode) -> i32,
{
for d in decodes {
let symbols = super::encode_channel_symbols(&d.info_bits);
let f0_audio = d.freq_hz + 1.5 * TONE_SPACING_HZ; let shift_baseband = audio_to_baseband_lag(d);
subtract_signal_baseband(
idat,
qdat,
f0_audio,
shift_baseband,
0.0, &symbols,
);
}
let _ = Vec::<u8>::new(); }
#[cfg(test)]
mod tests {
use super::*;
use crate::wspr::baseband::{NPOINTS_MAX, decimate_to_baseband};
use crate::wspr::tx::synthesize_type1;
#[test]
fn subtract_attenuates_synth_tone() {
let audio = synthesize_type1("K1ABC", "FN42", 37, 12_000, 1500.0, 0.5).expect("synth");
let mut padded = vec![0.0f32; NPOINTS_MAX];
padded[..audio.len()].copy_from_slice(&audio);
let (mut idat, mut qdat) = decimate_to_baseband(&padded);
let pre_pwr: f32 =
idat.iter().map(|&x| x * x).sum::<f32>() + qdat.iter().map(|&x| x * x).sum::<f32>();
let r = crate::wspr::decode_at(&audio, 12_000, 0, 1500.0).expect("decode synth");
let symbols = crate::wspr::encode_channel_symbols(&r.info_bits);
subtract_signal_baseband(
&mut idat,
&mut qdat,
1500.0 + 1.5 * TONE_SPACING_HZ,
0,
0.0,
&symbols,
);
let post_pwr: f32 =
idat.iter().map(|&x| x * x).sum::<f32>() + qdat.iter().map(|&x| x * x).sum::<f32>();
assert!(
post_pwr < pre_pwr * 0.5,
"subtract should remove most of the signal energy: pre={:.2e} post={:.2e}",
pre_pwr,
post_pwr
);
}
}