use super::ofdm::{EqualizerMethod, OfdmEqualizer};
use super::ofdm_frame::decode_chain;
use crate::core::Block;
use crate::fec::{CrcKind, DecodeRule, InterleaverKind, ScramblerKind, ScramblerPos};
use crate::modulate::ofdm_frame::{CodecCache, block_plan};
use crate::multicarrier::{CyclicPrefixRemove, FftBlock};
use crate::sync::dvb_t_gi_sync;
use crate::waveform::dvb_t::{
DVB_T_DATA_CARRIERS, DVB_T_FRAME_OUTER, DVB_T_FRAME_OUTER_IL, DVB_T_N_FFT, DvbTFrameParams,
ScatteredPilotExtractor, dvb_t_soft_llr, tps_carrier_bins,
};
use crate::waveform::dvb_t_tps::{TPS_SYMBOLS_PER_FRAME, TpsDecoder, TpsWord};
use crate::waveform::dvb_t_ts::{TS_PACKET_LEN, ts_depacketize, ts_energy_disperse};
use num_complex::Complex32 as C32;
#[derive(Debug, Clone, PartialEq)]
pub struct DvbTRxFrame {
pub payload: Vec<u8>,
pub tps: TpsWord,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum DvbTRxError {
#[error("guard-interval acquisition failed (buffer too short or no CP lock)")]
Acquisition,
#[error("too few samples for the expected frame")]
Incomplete,
#[error("TPS word failed to decode (BCH uncorrectable)")]
TpsDecode,
#[error("payload FEC/CRC decode failed")]
PayloadDecode,
}
pub fn dvb_t_frame_demodulate(
params: DvbTFrameParams,
iq: &[C32],
n_symbols: usize,
payload_len: usize,
) -> Result<DvbTRxFrame, DvbTRxError> {
let cache = CodecCache::new();
let base = params.config();
let n_fft = DVB_T_N_FFT;
let cp_len = base.carrier_plan.cp_len();
let sps = n_fft + cp_len;
let vbits = params.constellation.bits_per_symbol();
let acq = dvb_t_gi_sync(iq, n_fft, cp_len, base.fs, sps).ok_or(DvbTRxError::Acquisition)?;
let start = acq.start_sample;
if iq.len() < start + n_symbols * sps {
return Err(DvbTRxError::Incomplete);
}
let mut extractor = ScatteredPilotExtractor::new(params.guard);
let mut eq = OfdmEqualizer::new(&base, EqualizerMethod::PerSymbolPilotInterp);
let mut cp_remove = CyclicPrefixRemove::new(n_fft, cp_len);
let mut fft = FftBlock::new(n_fft);
let mut tps_dec = TpsDecoder::new();
let tps_bins = tps_carrier_bins();
let mut time = vec![C32::default(); n_fft];
let mut freq = vec![C32::default(); n_fft];
let mut equalized = vec![C32::default(); n_fft];
let mut data_syms = vec![C32::default(); DVB_T_DATA_CARRIERS];
let bits_per_sym = DVB_T_DATA_CARRIERS * vbits;
let mut llrs = vec![0.0f32; n_symbols * bits_per_sym];
let mut tps_word: Option<TpsWord> = None;
for s in 0..n_symbols {
let off = start + s * sps;
if cp_remove.process(&iq[off..], &mut time).out_written != n_fft {
return Err(DvbTRxError::Incomplete);
}
fft.process(&time, &mut freq);
let cells: Vec<C32> = tps_bins.iter().map(|&b| freq[b]).collect();
tps_dec.feed_symbol(&cells);
if (s + 1) % TPS_SYMBOLS_PER_FRAME == 0 && tps_word.is_none() {
tps_word = tps_dec.word();
tps_dec.reset();
}
let pilots = extractor.current_pilot_bins().to_vec();
let data_bins = extractor.data_bins().to_vec();
eq.set_pilot_bins(&pilots, &data_bins);
eq.process(&freq, &mut equalized);
extractor.extract_symbol(&equalized, &mut data_syms);
let sym_llrs = &mut llrs[s * bits_per_sym..(s + 1) * bits_per_sym];
for (c, &sym) in data_syms.iter().enumerate() {
let l = dvb_t_soft_llr(sym, vbits).expect("DVB-T order");
sym_llrs[c * vbits..(c + 1) * vbits].copy_from_slice(&l);
}
}
let tps = tps_word.ok_or(DvbTRxError::TpsDecode)?;
let n_ts_packets = payload_len.div_ceil(TS_PACKET_LEN - 1).max(1);
let ts_bytes_len = n_ts_packets * TS_PACKET_LEN;
let plan = block_plan(
ts_bytes_len,
CrcKind::None,
DVB_T_FRAME_OUTER,
params.inner(),
DVB_T_FRAME_OUTER_IL,
InterleaverKind::None,
&cache,
);
let (mut ts, ok) = decode_chain(
&llrs,
&plan,
CrcKind::None,
DVB_T_FRAME_OUTER,
params.inner(),
DVB_T_FRAME_OUTER_IL,
InterleaverKind::None,
ScramblerKind::None,
ScramblerPos::BeforeOuterFec,
0,
&cache,
DecodeRule::SumProduct,
)
.map_err(|_| DvbTRxError::PayloadDecode)?;
if !ok {
return Err(DvbTRxError::PayloadDecode);
}
if ts.len() < ts_bytes_len {
return Err(DvbTRxError::PayloadDecode);
}
ts.truncate(ts_bytes_len);
ts_energy_disperse(&mut ts);
let payload = ts_depacketize(&ts).ok_or(DvbTRxError::PayloadDecode)?;
let payload = payload
.get(..payload_len)
.map(|s| s.to_vec())
.unwrap_or(payload);
Ok(DvbTRxFrame { payload, tps })
}