use super::{decode::DecodeResult, encode::message_to_tones};
use crate::core::dsp::subtract::{subtract_tones, subtract_tones_lpf};
use super::decode::FT4_SUBTRACT;
fn get_tones(result: &DecodeResult) -> Option<Vec<u8>> {
let m77 = <[u8; 77]>::try_from(result.message77()).ok()?;
Some(message_to_tones(&m77))
}
const LPF_HALF_SAMPLES: usize = 700;
const REFINE_RADIUS_HZ: f32 = 5.0;
const REFINE_STEP_HZ: f32 = 0.1;
#[inline]
pub fn subtract_signal(audio: &mut [i16], result: &DecodeResult) {
subtract_signal_weighted(audio, result, 1.0);
}
#[inline]
pub fn subtract_signal_weighted(audio: &mut [i16], result: &DecodeResult, gain: f32) {
let tones = match get_tones(result) {
Some(t) => t,
None => return,
};
subtract_tones(
audio,
&tones,
result.freq_hz,
result.dt_sec,
gain,
&FT4_SUBTRACT,
);
}
pub fn subtract_signal_lpf(audio: &mut [i16], result: &DecodeResult) {
let tones = match get_tones(result) {
Some(t) => t,
None => return,
};
subtract_tones_lpf(
audio,
&tones,
result.freq_hz,
result.dt_sec,
&FT4_SUBTRACT,
LPF_HALF_SAMPLES,
);
}
pub fn refine_signal_freq(audio: &[i16], result: &DecodeResult) -> f32 {
let tones = match get_tones(result) {
Some(t) => t,
None => return result.freq_hz,
};
crate::core::dsp::subtract::refine_freq(
audio,
&tones,
result.freq_hz,
result.dt_sec,
&FT4_SUBTRACT,
REFINE_RADIUS_HZ,
REFINE_STEP_HZ,
)
}
#[cfg(test)]
mod tests {
use super::super::encode::{message_to_tones, tones_to_i16};
use super::*;
fn synthetic_result(msg: [u8; 77], freq_hz: f32, dt_sec: f32) -> DecodeResult {
let mut info = vec![0u8; 91].into_boxed_slice();
info[..77].copy_from_slice(&msg);
DecodeResult {
info,
freq_hz,
dt_sec,
hard_errors: 0,
sync_score: 10.0,
pass: 0,
sync_cv: 0.0,
snr_db: 0.0,
}
}
#[test]
fn subtract_with_exact_timing_near_zero() {
let msg = [1u8; 77];
let itone = message_to_tones(&msg);
let samples = tones_to_i16(&itone, 1500.0, 20_000);
let mut audio = vec![0i16; 90_000];
let offset = 6_000usize; let len = samples.len().min(90_000 - offset);
audio[offset..offset + len].copy_from_slice(&samples[..len]);
let power_before: f64 = audio.iter().map(|&s| (s as f64).powi(2)).sum::<f64>();
let result = synthetic_result(msg, 1500.0, 0.0);
subtract_signal(&mut audio, &result);
let power_after: f64 = audio.iter().map(|&s| (s as f64).powi(2)).sum::<f64>();
assert!(
power_after < power_before * 0.02,
"power before={power_before:.0} after={power_after:.0}"
);
}
#[test]
fn subtract_reduces_power() {
let msg = [0u8; 77];
let itone = message_to_tones(&msg);
let samples = tones_to_i16(&itone, 1500.0, 15_000);
let mut audio = vec![0i16; 90_000];
let offset = 6_000usize;
let len = samples.len().min(90_000 - offset);
audio[offset..offset + len].copy_from_slice(&samples[..len]);
let power_before: f64 =
audio.iter().map(|&s| (s as f64).powi(2)).sum::<f64>() / audio.len() as f64;
let result = synthetic_result(msg, 1500.0, 0.0);
subtract_signal(&mut audio, &result);
let power_after: f64 =
audio.iter().map(|&s| (s as f64).powi(2)).sum::<f64>() / audio.len() as f64;
assert!(
power_after < power_before * 0.10,
"power before={power_before:.1} after={power_after:.1}"
);
}
}