use crate::alt_freq_table::{AfTable, Freq, FreqType};
use thiserror::Error;
#[derive(Debug, Clone, PartialEq)]
enum CodeType {
Unassigned, Frequency, AltFreqCount, LfMfFollows, Filler, }
fn categorize_vhf_code(code: u8) -> CodeType {
match code {
1..=204 => CodeType::Frequency,
205 => CodeType::Filler,
224..=249 => CodeType::AltFreqCount,
250 => CodeType::LfMfFollows,
_ => CodeType::Unassigned,
}
}
fn decode_freq_cnt(code: u8) -> u8 {
code - 224_u8
}
fn get_lf_mf_frequency(idx: u8) -> u32 {
if (1..16).contains(&idx) {
return 153_000 + (u32::from(idx) - 1) * 9000;
}
531_000 + (u32::from(idx) - 16) * 9000
}
pub fn get_vhf_frequency(idx: u8) -> u32 {
assert_ne!(idx, 0);
87_600_000 + u32::from(idx - 1) * 100_000
}
#[derive(Error, Debug)]
pub enum DecodeError {
#[error("Use of invalid code")]
InvalidCode,
#[error("Invalid frequency count")]
InvalidFreqCount,
}
#[derive(Debug, Default, Clone, PartialEq)]
enum EncodingMethod {
#[default]
Unknown, MethodA, MethodB, }
#[derive(Debug, Default, Clone, PartialEq)]
pub struct AfDecoder {
awaiting_freq_cnt: u8, next_freq_is_lf_mf: bool, first_freq_code: u8, first_freq_in_table: bool, encoding_method: EncodingMethod, }
impl AfDecoder {
fn reset(&mut self) {
self.awaiting_freq_cnt = 0;
self.next_freq_is_lf_mf = false;
self.first_freq_code = 0;
self.first_freq_in_table = false;
self.encoding_method = EncodingMethod::Unknown;
}
fn write_first_freq_to_table(&mut self, table: &mut AfTable) {
assert!(!self.first_freq_in_table);
assert_ne!(self.first_freq_code, 0);
self.first_freq_in_table = true;
let _ = table.add(Freq {
frequency: get_vhf_frequency(self.first_freq_code),
freq_type: FreqType::SameProgram,
});
}
fn decrement_awaiting_freq_cnt(&mut self) {
if self.awaiting_freq_cnt > 0 {
self.awaiting_freq_cnt -= 1;
}
if self.awaiting_freq_cnt == 0 {
self.reset();
}
}
fn decode_for_method_a_code(
&mut self,
code: u8,
table: &mut AfTable,
) -> Result<(), DecodeError> {
if !self.first_freq_in_table && self.first_freq_code != 0 {
self.write_first_freq_to_table(table);
}
match categorize_vhf_code(code) {
CodeType::Unassigned | CodeType::AltFreqCount => {
return Err(DecodeError::InvalidCode);
}
CodeType::LfMfFollows => {
if self.next_freq_is_lf_mf {
return Err(DecodeError::InvalidCode);
}
self.next_freq_is_lf_mf = true;
}
CodeType::Frequency => {
if self.awaiting_freq_cnt == 0 {
return Err(DecodeError::InvalidFreqCount);
}
let freq: u32;
if self.next_freq_is_lf_mf {
self.next_freq_is_lf_mf = false;
freq = get_lf_mf_frequency(code);
} else {
freq = get_vhf_frequency(code);
}
let _ = table.add(Freq {
frequency: freq,
freq_type: FreqType::SameProgram,
});
self.decrement_awaiting_freq_cnt();
}
CodeType::Filler => (),
}
Ok(())
}
fn decode_for_method_a(&mut self, code_pair: [u8; 2], table: &mut AfTable) {
let _ = self.decode_for_method_a_code(code_pair[0], table);
let _ = self.decode_for_method_a_code(code_pair[1], table);
}
fn decode_for_method_b(&mut self, code_pair: [u8; 2], table: &mut AfTable) {
let freq_type = if code_pair[0] < code_pair[1] {
FreqType::SameProgram
} else {
FreqType::RegionalVariant
};
if code_pair[0] == self.first_freq_code {
assert_ne!(code_pair[1], self.first_freq_code);
let _ = table.add(Freq {
frequency: get_vhf_frequency(code_pair[1]),
freq_type,
});
} else if code_pair[1] == self.first_freq_code {
assert_ne!(code_pair[0], self.first_freq_code);
let _ = table.add(Freq {
frequency: get_vhf_frequency(code_pair[0]),
freq_type,
});
} else {
return;
}
self.decrement_awaiting_freq_cnt();
self.decrement_awaiting_freq_cnt();
}
fn decode_for_unknown_method(
&mut self,
code_pair: [u8; 2],
table: &mut AfTable,
) -> Result<(), DecodeError> {
let ct1 = categorize_vhf_code(code_pair[0]);
let ct2 = categorize_vhf_code(code_pair[1]);
if self.first_freq_code == 0 {
assert!(!self.first_freq_in_table);
match ct1 {
CodeType::Unassigned => return Err(DecodeError::InvalidCode),
CodeType::AltFreqCount => {
self.awaiting_freq_cnt = decode_freq_cnt(code_pair[0]);
if ct2 == CodeType::Frequency {
self.first_freq_code = code_pair[1];
if self.awaiting_freq_cnt < 3 {
self.encoding_method = EncodingMethod::MethodA;
self.write_first_freq_to_table(table);
}
self.decrement_awaiting_freq_cnt();
return Ok(());
}
self.encoding_method = EncodingMethod::MethodA;
return self.decode_for_method_a_code(code_pair[1], table);
}
_ => {
return Ok(());
}
}
}
if ct1 != CodeType::Frequency || ct2 != CodeType::Frequency {
self.encoding_method = EncodingMethod::MethodA;
} else if code_pair[0] == self.first_freq_code || code_pair[1] == self.first_freq_code {
self.encoding_method = EncodingMethod::MethodB;
} else {
self.encoding_method = EncodingMethod::MethodA;
}
match self.encoding_method {
EncodingMethod::MethodA => self.decode_for_method_a(code_pair, table),
EncodingMethod::MethodB => self.decode_for_method_b(code_pair, table),
EncodingMethod::Unknown => panic!("Shouldn't get here"),
}
Ok(())
}
pub fn decode_freq_block(
&mut self,
block_c: Option<u16>,
table: &mut AfTable,
) -> Result<(), DecodeError> {
if block_c.is_none() {
self.reset();
return Ok(());
}
let code_pair = block_c.unwrap().to_be_bytes();
match self.encoding_method {
EncodingMethod::MethodA => self.decode_for_method_a(code_pair, table),
EncodingMethod::MethodB => self.decode_for_method_b(code_pair, table),
EncodingMethod::Unknown => self.decode_for_unknown_method(code_pair, table)?,
}
Ok(())
}
}
#[cfg(test)]
mod tests;