use alloc::vec::Vec;
use super::Ft4;
use crate::core::dsp::downsample::DownsampleCfg;
use crate::core::dsp::subtract::SubtractCfg;
use crate::core::equalize::EqMode;
use crate::core::pipeline::{self, FftCache};
use crate::msg::pipeline_ap;
use crate::core::pipeline::DecodeStrictness;
pub use crate::core::pipeline::{DecodeDepth, DecodeResult};
pub use crate::msg::ApHint;
pub const FT4_DOWNSAMPLE: DownsampleCfg = DownsampleCfg {
input_rate: 12_000,
fft1_size: 92_160,
fft2_size: 5_120,
tone_spacing_hz: 20.833,
leading_pad_tones: 1.5,
trailing_pad_tones: 1.5,
ntones: 4,
edge_taper_bins: 101,
};
pub const FT4_SUBTRACT: SubtractCfg = SubtractCfg {
sample_rate: 12_000.0,
tone_spacing_hz: 20.833,
samples_per_symbol: 576,
base_offset_s: 0.5,
gfsk: Some(crate::core::dsp::subtract::GfskParams {
bt: 1.0,
hmod: 1.0,
ramp_samples: 576 / 8,
}),
};
const REFINE_STEPS: i32 = 32;
const SYNC_Q_MIN: u32 = 8;
pub fn decode_frame(
audio: &[i16],
freq_min: f32,
freq_max: f32,
sync_min: f32,
max_cand: usize,
) -> Vec<DecodeResult> {
decode_frame_with_options(
audio,
freq_min,
freq_max,
sync_min,
None,
DecodeDepth::BpAllOsd,
max_cand,
)
}
pub fn decode_frame_with_options(
audio: &[i16],
freq_min: f32,
freq_max: f32,
sync_min: f32,
freq_hint: Option<f32>,
depth: DecodeDepth,
max_cand: usize,
) -> Vec<DecodeResult> {
pipeline::decode_frame::<Ft4>(
audio,
&FT4_DOWNSAMPLE,
freq_min,
freq_max,
sync_min,
freq_hint,
depth,
max_cand,
DecodeStrictness::Normal,
EqMode::Off,
REFINE_STEPS,
SYNC_Q_MIN,
)
.0
}
pub fn decode_frame_with_cache(
audio: &[i16],
freq_min: f32,
freq_max: f32,
sync_min: f32,
max_cand: usize,
) -> (Vec<DecodeResult>, FftCache) {
decode_frame_with_cache_and_options(
audio,
freq_min,
freq_max,
sync_min,
None,
DecodeDepth::BpAllOsd,
max_cand,
)
}
pub fn decode_frame_with_cache_and_options(
audio: &[i16],
freq_min: f32,
freq_max: f32,
sync_min: f32,
freq_hint: Option<f32>,
depth: DecodeDepth,
max_cand: usize,
) -> (Vec<DecodeResult>, FftCache) {
pipeline::decode_frame::<Ft4>(
audio,
&FT4_DOWNSAMPLE,
freq_min,
freq_max,
sync_min,
freq_hint,
depth,
max_cand,
DecodeStrictness::Normal,
EqMode::Off,
REFINE_STEPS,
SYNC_Q_MIN,
)
}
pub fn decode_frame_subtract(
audio: &[i16],
freq_min: f32,
freq_max: f32,
sync_min: f32,
max_cand: usize,
) -> Vec<DecodeResult> {
decode_frame_subtract_with_options(
audio,
freq_min,
freq_max,
sync_min,
None,
DecodeDepth::BpAllOsd,
max_cand,
)
}
pub fn decode_frame_subtract_with_options(
audio: &[i16],
freq_min: f32,
freq_max: f32,
sync_min: f32,
freq_hint: Option<f32>,
depth: DecodeDepth,
max_cand: usize,
) -> Vec<DecodeResult> {
pipeline::decode_frame_subtract::<Ft4>(
audio,
&FT4_DOWNSAMPLE,
&FT4_SUBTRACT,
freq_min,
freq_max,
sync_min,
freq_hint,
depth,
max_cand,
DecodeStrictness::Normal,
REFINE_STEPS,
SYNC_Q_MIN,
)
}
pub fn decode_sniper_ap(
audio: &[i16],
target_freq: f32,
max_cand: usize,
eq_mode: EqMode,
ap_hint: Option<&ApHint>,
) -> Vec<DecodeResult> {
decode_sniper_ap_with_options(
audio,
target_freq,
DecodeDepth::BpAllOsd,
max_cand,
eq_mode,
ap_hint,
)
}
pub fn decode_sniper_ap_with_options(
audio: &[i16],
target_freq: f32,
depth: DecodeDepth,
max_cand: usize,
eq_mode: EqMode,
ap_hint: Option<&ApHint>,
) -> Vec<DecodeResult> {
let max_cand = max_cand.min(15);
pipeline_ap::decode_sniper_ap::<Ft4>(
audio,
&FT4_DOWNSAMPLE,
target_freq,
250.0,
0.5,
depth,
max_cand,
DecodeStrictness::Normal,
eq_mode,
REFINE_STEPS,
SYNC_Q_MIN / 2,
ap_hint,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_frame_with_options_accepts_all_param_combos() {
let empty = vec![0i16; 12 * 7500]; for &depth in &[DecodeDepth::BpAll, DecodeDepth::BpAllOsd] {
let _ = decode_frame_with_options(&empty, 100.0, 3000.0, 1.0, None, depth, 5);
}
}
#[test]
fn decode_frame_with_cache_and_options_accepts_all_param_combos() {
let empty = vec![0i16; 12 * 7500];
for &depth in &[DecodeDepth::BpAll, DecodeDepth::BpAllOsd] {
let _ = decode_frame_with_cache_and_options(&empty, 100.0, 3000.0, 1.0, None, depth, 5);
}
}
#[test]
fn decode_frame_subtract_with_options_accepts_all_param_combos() {
let empty = vec![0i16; 12 * 7500];
for &depth in &[DecodeDepth::BpAll, DecodeDepth::BpAllOsd] {
let _ = decode_frame_subtract_with_options(&empty, 100.0, 3000.0, 1.0, None, depth, 5);
}
}
#[test]
fn decode_sniper_ap_with_options_accepts_all_param_combos() {
let empty = vec![0i16; 12 * 7500];
for &depth in &[DecodeDepth::BpAll, DecodeDepth::BpAllOsd] {
let _ = decode_sniper_ap_with_options(&empty, 1500.0, depth, 5, EqMode::Off, None);
}
}
}