#![allow(dead_code)]
use crate::alt_freq_decoder::get_vhf_frequency;
use crate::alt_freq_table::{Freq, FreqType};
use crate::oda::{OdaEntry, decode_oda, is_oda_group_type_used, is_valid_oda_app_id};
use crate::ptyn::decode_ptyn;
use crate::radiotext::RtVariant;
use crate::rds::RdsData;
use crate::types::{
Content, Group, GroupType, GroupVersion, NUM_TDC, Pin, ProgramInformation, ProgramType,
SlcData, ValidFields,
};
use core::ops::BitOr;
use modular_bitfield_msb::prelude::*;
#[bitfield(bits = 16)]
struct GroupType2BlockB {
group_type: GroupType, tp: bool, program_type: ProgramType, text_flag: RtVariant,
text_segment_addr: B4,
}
#[bitfield(bits = 16)]
#[derive(Default, Clone, PartialEq, Eq)]
struct GroupType3ABlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, application_group: GroupType, }
impl BitOr for ValidFields {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
const N: usize = core::mem::size_of::<ValidFields>();
let l = self.into_bytes();
let r = rhs.into_bytes();
let mut m = [0u8; N];
for i in 0..N {
m[i] = l[i] | r[i];
}
Self::from_bytes(m)
}
}
impl Group {
fn get_type(&self) -> GroupType {
GroupType2BlockB::from_bytes(self.b.unwrap().to_be_bytes()).group_type()
}
}
fn decode_block_b_common(block: &GroupType2BlockB, rds_data: &mut RdsData) -> ValidFields {
rds_data.tn.traffic.set_tp(block.tp());
rds_data.tn.program_type = block.program_type();
ValidFields::new().with_tp(true).with_pty(true)
}
fn decode_group_type_0(
group: &Group,
rds_data: &mut RdsData,
advanced_ps_decoding: bool,
) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, traffic_announcement: bool, ms: Content, di_bit: bool, seg_addr: B2, }
let mut valid = ValidFields::new();
let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
if block_b.group_type().version() == GroupVersion::B && group.c.is_some() {
let _ = rds_data
.alt_freq_decoder
.decode_freq_block(group.c, &mut rds_data.alt_freqs);
valid.set_af(true);
}
match block_b.seg_addr() {
0 => rds_data.did_pty.set_dynamic_pty(block_b.di_bit()),
1 => rds_data.did_pty.set_compressed(block_b.di_bit()),
2 => rds_data.did_pty.set_artificial_head(block_b.di_bit()),
3 => rds_data.did_pty.set_stereo(block_b.di_bit()),
_ => return valid,
}
if group.d.is_none() {
return valid;
}
rds_data.tn.traffic.set_ta(block_b.traffic_announcement());
valid.set_ta(true);
rds_data.content = block_b.ms();
valid.set_ms(true);
let pair_idx = 2 * block_b.seg_addr();
let ps_bytes = group.d.unwrap().to_be_bytes();
if advanced_ps_decoding {
if rds_data
.tn
.ps
.update_advanced(pair_idx as usize, ps_bytes[0])
{
valid.set_ps(true);
}
if rds_data
.tn
.ps
.update_advanced((pair_idx + 1) as usize, ps_bytes[1])
{
valid.set_ps(true);
}
} else {
rds_data.tn.ps.update_simple(pair_idx as usize, ps_bytes);
valid.set_ps(true);
}
valid
}
fn decode_group_type_1(group: &Group, rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct GroupType1BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, radio_paging_codes: B5, }
let mut valid = ValidFields::new();
let block_b = GroupType1BlockB::from_bytes(group.b.unwrap().to_be_bytes());
if block_b.group_type().version() == GroupVersion::A
&& let Some(group_c) = group.c
{
rds_data.slc = SlcData::from_bytes(group_c.to_be_bytes());
valid.set_slc(true);
}
rds_data.tn.pin = group
.d
.map(|d| Pin::from_bytes(d.to_be_bytes()))
.unwrap_or_default();
if group.d.is_some() {
valid.set_pin(true);
}
valid
}
fn decode_group_type_2a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
let block_b = GroupType2BlockB::from_bytes(group.b.unwrap().to_be_bytes());
let chars: [Option<[u8; 2]>; 2] =
[group.c.map(u16::to_be_bytes), group.d.map(u16::to_be_bytes)];
let rt = match block_b.text_flag() {
RtVariant::A => &mut rds_data.rt.a,
RtVariant::B => &mut rds_data.rt.b,
};
let addr = (block_b.text_segment_addr() as usize) * 4;
rt.update_rt_simple(addr, &chars);
if rds_data.rt.decode_rt != block_b.text_flag() {
rt.bump_rt_validation_count();
}
rt.update_rt_advance(addr, &chars);
rds_data.rt.decode_rt = block_b.text_flag();
ValidFields::new().with_rt(true)
}
fn decode_group_type_2b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
if group.d.is_none() {
return ValidFields::new();
}
let block_b = GroupType2BlockB::from_bytes(group.b.unwrap().to_be_bytes());
let chars: [Option<[u8; 2]>; 1] = [group.d.map(u16::to_be_bytes)];
let rt = match block_b.text_flag() {
RtVariant::A => &mut rds_data.rt.a,
RtVariant::B => &mut rds_data.rt.b,
};
let addr = (block_b.text_segment_addr() as usize) * 2;
rt.update_rt_simple(addr, &chars);
if rds_data.rt.decode_rt != block_b.text_flag() {
rt.bump_rt_validation_count();
}
rt.update_rt_advance(addr, &chars);
rds_data.rt.decode_rt = block_b.text_flag();
ValidFields::new().with_rt(true)
}
fn decode_group_type_3a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
let valid = ValidFields::new();
if group.d.is_none() {
return valid;
}
let block_b = GroupType3ABlockB::from_bytes(group.b.unwrap().to_be_bytes());
let app_id = group.d.unwrap();
if !is_valid_oda_app_id(app_id) {
return valid;
}
let entry = rds_data.oda.get_mut(&app_id);
if let Some(e) = entry {
e.group_type = block_b.group_type();
} else if !rds_data.oda.is_full() {
let _ = rds_data.oda.insert(
app_id,
OdaEntry {
group_type: block_b.group_type(),
packet_count: 0,
},
);
}
valid
}
fn decode_group_type_3b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_oda(group, group.get_type(), rds_data)
}
fn decode_group_type_4a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, spare: B3, date_msb: B2, }
#[bitfield(bits = 16)]
struct BlockC {
date: B15,
hour_msb: B1,
}
#[bitfield(bits = 16)]
struct BlockD {
hour: B4,
minute: B6,
local_offset_dir: B1, local_offset_val: B5, }
if group.c.is_none() || group.d.is_none() {
return ValidFields::new();
}
let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
let block_c = BlockC::from_bytes(group.b.unwrap().to_be_bytes());
let block_d = BlockD::from_bytes(group.b.unwrap().to_be_bytes());
rds_data.clock.mjd = (u32::from(block_b.date_msb()) << 15) + u32::from(block_c.date());
rds_data.clock.hour = (block_c.hour_msb() << 4) + block_d.hour();
rds_data.clock.minute = block_d.minute();
rds_data.clock.utc_offset_half_hours = if block_d.local_offset_dir() == 0 {
block_d.local_offset_val().cast_signed()
} else {
-(block_d.local_offset_val().cast_signed())
};
ValidFields::new().with_clock(true)
}
fn decode_group_type_4b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_oda(group, group.get_type(), rds_data)
}
fn decode_tdc_block(block: u16, rds_data: &mut RdsData) {
let channel = rds_data.tdc.current_channel as usize;
assert!(channel < NUM_TDC);
rds_data.tdc.data[channel].write((block >> 8) as u8);
rds_data.tdc.data[channel].write((block & 0xff) as u8);
}
fn decode_group_type_5a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, address: B5,
}
let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
if is_oda_group_type_used(&rds_data.oda, block_b.group_type()) {
return decode_oda(group, block_b.group_type(), rds_data);
}
let mut valid = ValidFields::new();
rds_data.tdc.current_channel = block_b.address();
if let Some(group_c) = group.c {
decode_tdc_block(group_c, rds_data);
valid.set_tdc(true);
}
if let Some(group_d) = group.d {
decode_tdc_block(group_d, rds_data);
valid.set_tdc(true);
}
valid
}
fn decode_group_type_5b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
const GROUP_TYPE: GroupType = GroupType::from_bytes([5 << (1 + GroupVersion::B as u8)]);
if is_oda_group_type_used(&rds_data.oda, GROUP_TYPE) {
return decode_oda(group, GROUP_TYPE, rds_data);
}
ValidFields::new()
}
fn decode_group_type_6(group: &Group, rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, unused: B5,
}
let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
if is_oda_group_type_used(&rds_data.oda, block_b.group_type()) {
return decode_oda(group, block_b.group_type(), rds_data);
}
ValidFields::new()
}
fn decode_group_type_7a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
const GROUP_TYPE: GroupType = GroupType::from_bytes([7 << (1 + GroupVersion::A as u8)]);
if is_oda_group_type_used(&rds_data.oda, GROUP_TYPE) {
return decode_oda(group, GROUP_TYPE, rds_data);
}
ValidFields::new()
}
fn decode_group_type_7b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_oda(group, group.get_type(), rds_data)
}
fn decode_group_type_8(group: &Group, rds_data: &mut RdsData) -> ValidFields {
let gt = group.get_type();
if is_oda_group_type_used(&rds_data.oda, gt) {
return decode_oda(group, gt, rds_data);
}
if gt.version() == GroupVersion::A {
}
ValidFields::new()
}
fn decode_group_type_9(group: &Group, rds_data: &mut RdsData) -> ValidFields {
let gt = group.get_type();
if is_oda_group_type_used(&rds_data.oda, gt) {
return decode_oda(group, gt, rds_data);
}
let mut valid = ValidFields::new();
if gt.version() == GroupVersion::B {
return valid;
}
if group.c.is_none() || group.d.is_none() {
return valid;
}
rds_data
.ews
.set_block_b_lsb((group.b.unwrap() & 0b11111) as u8);
rds_data.ews.set_block_c(group.c.unwrap());
rds_data.ews.set_block_d(group.d.unwrap());
valid.set_ews(true);
valid
}
fn decode_group_type_10a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_ptyn(group, rds_data)
}
fn decode_group_type_10b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
match group.get_type().version() {
GroupVersion::A => decode_ptyn(group, rds_data),
GroupVersion::B => decode_oda(group, group.get_type(), rds_data),
}
}
fn decode_group_type_11(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_oda(group, group.get_type(), rds_data)
}
fn decode_group_type_12(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_oda(group, group.get_type(), rds_data)
}
fn decode_group_type_13a(group: &Group, _rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, information: B2,
sty: B3,
}
let _block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
ValidFields::new()
}
fn decode_group_type_13b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
decode_oda(group, group.get_type(), rds_data)
}
fn decode_group_type_14a(group: &Group, rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, traffic_program: bool, program_type: ProgramType, tp_on: bool, variant_code: B4,
}
let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
let mut valid = ValidFields::new().with_tp_on(true);
rds_data.on.traffic.set_tp(block_b.tp_on());
match block_b.variant_code() {
0..=3 => {
let idx: usize = 2 * (block_b.variant_code() as usize);
if let Some(group_c) = group.c {
rds_data.tn.ps.update_simple(idx, group_c.to_be_bytes());
valid.set_ps_on(true);
}
}
4 => {
let _ = rds_data
.on_freq_decoder
.decode_freq_block(group.c, &mut rds_data.on_freqs);
valid.set_on_freqs(true);
}
5..=9 => {
if let Some(group_c) = group.c {
let freqs = group_c.to_be_bytes();
if freqs[1] != 0 {
rds_data.map_freqs.add(Freq {
frequency: get_vhf_frequency(freqs[1]),
freq_type: FreqType::SameProgram,
});
valid.set_map_freqs(true);
}
}
}
13 => {
if let Some(group_c) = group.c {
rds_data.on.traffic.set_ta((group_c & 0b1) != 0);
valid.set_ta_on(true);
}
}
14 if group.c.is_some() => {
rds_data.on.pin = group
.c
.map(|c| Pin::from_bytes(c.to_be_bytes()))
.unwrap_or_default();
valid.set_pin_on(true);
}
_ => {}
}
valid
}
fn decode_group_type_14b(group: &Group, rds_data: &mut RdsData) -> ValidFields {
#[bitfield(bits = 16)]
struct BlockB {
group_type: GroupType, tp: bool, program_type: ProgramType, tp_on: bool, ta_on: bool, unused: B3,
}
let block_b = BlockB::from_bytes(group.b.unwrap().to_be_bytes());
let valid = ValidFields::new()
.with_tp(true)
.with_ta_on(true)
.with_tp_on(true);
rds_data.tn.traffic.set_tp(block_b.tp());
rds_data.on.traffic.set_ta(block_b.ta_on());
rds_data.on.traffic.set_tp(block_b.tp_on());
valid
}
fn decode_group_type_15(_group: &Group, _rds_data: &RdsData) -> ValidFields {
ValidFields::new()
}
pub struct Decoder {
advanced_ps_decoding: bool,
}
impl Decoder {
#[must_use]
pub fn new(advanced_ps_decoding: bool) -> Self {
Decoder {
advanced_ps_decoding,
}
}
pub fn decode(&mut self, group: &Group, rds_data: &mut RdsData) -> ValidFields {
let mut valid = ValidFields::default();
if let Some(group_a) = group.a {
rds_data.program_information = ProgramInformation::from_bytes(group_a.to_be_bytes());
valid.set_pi(true);
}
let Some(blk_b) = group.b else {
rds_data.valid = rds_data.valid | valid;
return valid;
};
let block_b = GroupType2BlockB::from_bytes(blk_b.to_be_bytes());
valid = valid | decode_block_b_common(&block_b, rds_data);
let new_valid = match (block_b.group_type().code(), block_b.group_type().version()) {
(0, GroupVersion::A | GroupVersion::B) => {
decode_group_type_0(group, rds_data, self.advanced_ps_decoding)
}
(1, GroupVersion::A | GroupVersion::B) => decode_group_type_1(group, rds_data),
(2, GroupVersion::A) => decode_group_type_2a(group, rds_data),
(2, GroupVersion::B) => decode_group_type_2b(group, rds_data),
(3, GroupVersion::A) => decode_group_type_3a(group, rds_data),
(3, GroupVersion::B) => decode_group_type_3b(group, rds_data),
(4, GroupVersion::A) => decode_group_type_4a(group, rds_data),
(4, GroupVersion::B) => decode_group_type_4b(group, rds_data),
(5, GroupVersion::A) => decode_group_type_5a(group, rds_data),
(5, GroupVersion::B) => decode_group_type_5b(group, rds_data),
(6, GroupVersion::A | GroupVersion::B) => decode_group_type_6(group, rds_data),
(7, GroupVersion::A) => decode_group_type_7a(group, rds_data),
(7, GroupVersion::B) => decode_group_type_7b(group, rds_data),
(8, GroupVersion::A | GroupVersion::B) => decode_group_type_8(group, rds_data),
(9, GroupVersion::A | GroupVersion::B) => decode_group_type_9(group, rds_data),
(10, GroupVersion::A) => decode_group_type_10a(group, rds_data),
(10, GroupVersion::B) => decode_group_type_10b(group, rds_data),
(11, GroupVersion::A | GroupVersion::B) => decode_group_type_11(group, rds_data),
(12, GroupVersion::A | GroupVersion::B) => decode_group_type_12(group, rds_data),
(13, GroupVersion::A) => decode_group_type_13a(group, rds_data),
(13, GroupVersion::B) => decode_group_type_13b(group, rds_data),
(14, GroupVersion::A) => decode_group_type_14a(group, rds_data),
(14, GroupVersion::B) => decode_group_type_14b(group, rds_data),
(15, GroupVersion::A | GroupVersion::B) => decode_group_type_15(group, rds_data),
_ => {
ValidFields::new()
}
};
valid = valid | new_valid; rds_data.valid = rds_data.valid | valid; valid
}
}
#[cfg(test)]
mod tests;