use crate::sync::waterfall::Waterfall;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Candidate {
pub time_sym: i32,
pub freq_bin: usize,
pub score: f32,
}
pub fn costas_score(
wf: &Waterfall,
costas: &[u8],
sync_pos: &[i32],
time_sym: i32,
freq_bin: usize,
) -> f32 {
let costas_len = costas.len() as i32;
let mut total = 0.0f32;
for &block_start in sync_pos {
for ci in 0..costas_len {
let sym = time_sym + block_start + ci;
if sym < 0 || sym >= wf.num_syms as i32 {
continue;
}
let sym = sym as usize;
let expected_tone = costas[ci as usize] as usize;
let bin = freq_bin + expected_tone;
if bin >= wf.num_tones {
continue;
}
let e_signal = wf.get(sym, bin);
let e_freq = {
let left = if bin > 0 {
wf.get(sym, bin - 1)
} else {
f32::NEG_INFINITY
};
let right = if bin + 1 < wf.num_tones {
wf.get(sym, bin + 1)
} else {
f32::NEG_INFINITY
};
left.max(right)
};
let e_time = {
let prev = if sym > 0 {
wf.get(sym - 1, bin)
} else {
f32::NEG_INFINITY
};
let next = if sym + 1 < wf.num_syms {
wf.get(sym + 1, bin)
} else {
f32::NEG_INFINITY
};
prev.max(next)
};
let diff = e_signal - e_freq.max(e_time);
total += diff.max(0.0);
}
}
total
}
pub fn find_candidates(
wf: &Waterfall,
costas: &[u8],
sync_pos: &[i32],
num_tones: usize,
t_min: i32,
t_max: i32,
max_candidates: usize,
) -> Vec<Candidate> {
let mut heap: Vec<Candidate> = Vec::with_capacity(max_candidates + 1);
let max_freq_bin = if wf.num_tones > num_tones {
wf.num_tones - num_tones
} else {
return vec![];
};
for time_sym in t_min..=t_max {
for freq_bin in 0..=max_freq_bin {
let score = costas_score(wf, costas, sync_pos, time_sym, freq_bin);
if heap.len() < max_candidates {
heap.push(Candidate {
time_sym,
freq_bin,
score,
});
if heap.len() == max_candidates {
heap.sort_by(|a, b| a.score.partial_cmp(&b.score).unwrap());
}
} else if score > heap[0].score {
heap[0] = Candidate {
time_sym,
freq_bin,
score,
};
heap.sort_by(|a, b| a.score.partial_cmp(&b.score).unwrap());
}
}
}
heap.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap());
heap
}