use super::bitstream::parse;
use super::decoder_tables::{
DHF_MR102, DHF_MR122, DHF_MR475, DHF_MR515, DHF_MR59, DHF_MR67, DHF_MR74, DHF_MR795,
};
pub const HOMING_SAMPLE: i16 = 0x0008;
const L_FRAME: usize = 160;
const PRMNOFSF: [usize; 8] = [7, 7, 7, 7, 7, 8, 12, 18];
const fn pattern(mode_index: u8) -> &'static [i16] {
match mode_index {
0 => &DHF_MR475,
1 => &DHF_MR515,
2 => &DHF_MR59,
3 => &DHF_MR67,
4 => &DHF_MR74,
5 => &DHF_MR795,
6 => &DHF_MR102,
_ => &DHF_MR122,
}
}
#[must_use]
pub fn is_encoder_homing_frame(samples: &[i16]) -> bool {
samples.len() == L_FRAME && samples.iter().all(|&s| s == HOMING_SAMPLE)
}
#[must_use]
pub fn is_decoder_homing_frame(payload: &[u8], mode_index: u8) -> bool {
matches(payload, mode_index, usize::MAX)
}
#[must_use]
pub fn is_decoder_homing_frame_first(payload: &[u8], mode_index: u8) -> bool {
matches(payload, mode_index, PRMNOFSF[usize::from(mode_index)])
}
fn matches(payload: &[u8], mode_index: u8, count: usize) -> bool {
assert!(
mode_index < 8,
"homing is defined for the eight speech modes"
);
let Some(params) = parse(mode_index, payload) else {
return false;
};
let want = pattern(mode_index);
let count = count.min(want.len());
params.len() >= count
&& (0..count).all(|i| i16::try_from(params[i]).is_ok_and(|p| p == want[i]))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codecs::amr::nb::bitstream::parameter_widths;
use crate::codecs::amr::nb::tables::{
SORT_102, SORT_122, SORT_475, SORT_515, SORT_59, SORT_67, SORT_74, SORT_795,
};
const fn sort_table(mode_index: u8) -> &'static [u16] {
match mode_index {
0 => &SORT_475,
1 => &SORT_515,
2 => &SORT_59,
3 => &SORT_67,
4 => &SORT_74,
5 => &SORT_795,
6 => &SORT_102,
_ => &SORT_122,
}
}
fn homing_payload(mode_index: u8, flip: Option<usize>) -> Vec<u8> {
let want = pattern(mode_index);
let widths = parameter_widths(mode_index);
let mut codec = Vec::new();
for (&value, &width) in want.iter().zip(widths.iter()) {
for k in (0..width).rev() {
codec.push(u8::try_from((value >> k) & 1).expect("one bit"));
}
}
if let Some(bit) = flip {
codec[bit] ^= 1;
}
let sort = sort_table(mode_index);
let mut out = vec![0u8; codec.len().div_ceil(8)];
for (i, &source) in sort.iter().enumerate() {
out[i / 8] |= codec[source as usize] << (7 - (i % 8));
}
out
}
#[test]
fn the_encoder_homing_frame_is_exactly_that_and_nothing_near_it() {
assert!(is_encoder_homing_frame(&[HOMING_SAMPLE; L_FRAME]));
let mut nearly = [HOMING_SAMPLE; L_FRAME];
nearly[159] = 9;
assert!(!is_encoder_homing_frame(&nearly));
assert!(!is_encoder_homing_frame(&[0i16; L_FRAME]));
assert!(!is_encoder_homing_frame(&[HOMING_SAMPLE; 320]));
}
#[test]
fn each_mode_recognises_its_own_pattern_and_rejects_the_others() {
let mut cross = 0;
for mode in 0..8u8 {
let payload = homing_payload(mode, None);
assert!(
is_decoder_homing_frame(&payload, mode),
"mode {mode} rejected its own"
);
assert!(is_decoder_homing_frame_first(&payload, mode));
for other in 0..8u8 {
if other == mode {
continue;
}
assert!(
!is_decoder_homing_frame(&payload, other),
"mode {other} accepted mode {mode}'s homing frame"
);
cross += 1;
}
}
assert_eq!(cross, 56, "eight modes cross-checked pairwise");
}
#[test]
fn the_encoder_emits_each_modes_homing_pattern() {
use crate::codecs::amr::nb::enc::encoder::{NbEncoder, Rate};
for mode in 0..8u8 {
let mut encoder = NbEncoder::new();
let rate = Rate::from_index(mode).expect("a speech mode");
let payload = encoder.encode_frame(&[HOMING_SAMPLE; L_FRAME], rate);
assert!(
is_decoder_homing_frame(&payload, mode),
"mode {mode} did not emit its homing pattern"
);
}
}
#[test]
fn the_first_subframe_test_ignores_what_the_full_test_catches() {
let mut checked = 0;
for mode in 0..8u8 {
let widths = parameter_widths(mode);
let first = PRMNOFSF[usize::from(mode)];
assert!(
first < widths.len(),
"mode {mode}: PRMNOFSF covers the whole frame"
);
let offset: usize = widths[..first].iter().sum();
let payload = homing_payload(mode, Some(offset));
assert!(
is_decoder_homing_frame_first(&payload, mode),
"mode {mode}: a later subframe's bit reached the truncated test"
);
assert!(
!is_decoder_homing_frame(&payload, mode),
"mode {mode}: the full test missed a flipped bit"
);
let payload = homing_payload(mode, Some(0));
assert!(!is_decoder_homing_frame_first(&payload, mode));
assert!(!is_decoder_homing_frame(&payload, mode));
checked += 1;
}
assert_eq!(checked, 8);
}
#[test]
fn an_ordinary_frame_is_not_a_homing_frame() {
for mode in 0..8u8 {
assert!(!is_decoder_homing_frame(&[0x5Au8; 32], mode));
assert!(!is_decoder_homing_frame(&[0u8; 32], mode));
assert!(!is_decoder_homing_frame(&[0xFFu8; 2], mode));
}
}
}