use super::dvb_t_frame::{DvbTFrameDemod, DvbTRxError};
use crate::modulate::dvb_t_super_frame::{DVB_T_FRAMES_PER_SUPER_FRAME, DvbTSuperFrameParams};
use crate::waveform::dvb_t::DVB_T_N_FFT;
use num_complex::Complex32 as C32;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DvbTRxSuperFrame {
pub payload: Vec<u8>,
pub cell_id: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum DvbTRxSuperFrameError {
#[error("super-frame: frame {frame} failed: {source}")]
Frame {
frame: usize,
#[source]
source: DvbTRxError,
},
#[error("super-frame: frame numbers out of sequence (got {got:?}, expected 0..3)")]
FrameSequence {
got: [u8; DVB_T_FRAMES_PER_SUPER_FRAME],
},
#[error("super-frame: too few samples for four frames")]
Incomplete,
}
#[derive(Debug, Clone)]
pub struct DvbTSuperFrameDemod {
params: DvbTSuperFrameParams,
integer_cfo: bool,
rx_window_backoff: usize,
}
impl DvbTSuperFrameDemod {
pub fn new(params: DvbTSuperFrameParams) -> Self {
Self {
params,
integer_cfo: false,
rx_window_backoff: 0,
}
}
pub fn with_integer_cfo_correction(mut self, on: bool) -> Self {
self.integer_cfo = on;
self
}
pub fn with_rx_window_backoff(mut self, backoff: usize) -> Self {
self.rx_window_backoff = backoff;
self
}
pub fn params(&self) -> DvbTSuperFrameParams {
self.params
}
pub fn integer_cfo_correction(&self) -> bool {
self.integer_cfo
}
pub fn rx_window_backoff(&self) -> usize {
self.rx_window_backoff
}
pub fn decode(
&self,
iq: &[C32],
symbols_per_frame: usize,
frame_payload_lens: [usize; DVB_T_FRAMES_PER_SUPER_FRAME],
) -> Result<DvbTRxSuperFrame, DvbTRxSuperFrameError> {
let params = self.params;
let cp_len = params.frame(0).config().carrier_plan.cp_len();
let sps = DVB_T_N_FFT + cp_len;
let frame_samples = symbols_per_frame * sps;
let mut payload = Vec::new();
let mut frame_numbers = [0u8; DVB_T_FRAMES_PER_SUPER_FRAME];
let mut cell_hi = 0u8;
let mut cell_lo = 0u8;
for f in 0..DVB_T_FRAMES_PER_SUPER_FRAME {
let start = f * frame_samples;
let sub = iq.get(start..).ok_or(DvbTRxSuperFrameError::Incomplete)?;
let rx = DvbTFrameDemod::new(params.frame(f as u8))
.with_integer_cfo_correction(self.integer_cfo)
.with_rx_window_backoff(self.rx_window_backoff)
.decode(sub, symbols_per_frame, frame_payload_lens[f])
.map_err(|source| DvbTRxSuperFrameError::Frame { frame: f, source })?;
frame_numbers[f] = rx.tps.frame_number;
if f.is_multiple_of(2) {
cell_hi = rx.tps.cell_id; } else {
cell_lo = rx.tps.cell_id; }
payload.extend_from_slice(&rx.payload);
}
if frame_numbers != [0, 1, 2, 3] {
return Err(DvbTRxSuperFrameError::FrameSequence { got: frame_numbers });
}
let cell_id = ((cell_hi as u16) << 8) | cell_lo as u16;
Ok(DvbTRxSuperFrame { payload, cell_id })
}
}