use super::ofdm_frame::{CodecCache, encode_chain, symbols_for_coded_bits};
use crate::core::Block;
use crate::fec::{CrcKind, InterleaverKind, ScramblerKind, ScramblerPos};
use crate::multicarrier::{CyclicPrefixInsert, IfftBlock, SymbolWindow, TxLowpass};
use crate::waveform::dvb_t::{
DVB_T_DATA_CARRIERS, DVB_T_FRAME_OUTER, DVB_T_FRAME_OUTER_IL, DVB_T_KMAX, DVB_T_N_FFT,
DvbTFrameParams, ScatteredPilotMapper, dvb_t_coded_bits_with, dvb_t_frame_fill_with,
dvb_t_map_symbol, tps_carrier_bins,
};
use crate::waveform::dvb_t_tps::{TPS_SYMBOLS_PER_FRAME, TpsEncoder};
use crate::waveform::dvb_t_ts::{
TS_PACKET_LEN, ts_energy_disperse, ts_packetize, ts_stuff_null_packets,
};
use num_complex::Complex32 as C32;
#[derive(Debug, Clone)]
pub struct DvbTFrame {
pub iq: Vec<C32>,
pub n_symbols: usize,
pub samples_per_symbol: usize,
}
#[derive(Debug, Clone)]
pub struct DvbTFrameMod {
params: DvbTFrameParams,
window_roll_off: usize,
tx_lowpass: Option<TxLowpass>,
}
impl DvbTFrameMod {
pub fn new(params: DvbTFrameParams) -> Self {
Self {
params,
window_roll_off: 0,
tx_lowpass: None,
}
}
pub fn with_symbol_window(mut self, roll_off: usize) -> Self {
self.window_roll_off = roll_off;
self
}
pub fn with_tx_lowpass(mut self, lowpass: TxLowpass) -> Self {
self.tx_lowpass = Some(lowpass);
self
}
pub fn tx_lowpass_for_2k(num_taps: usize, stopband_db: f32) -> TxLowpass {
TxLowpass::for_null_band(DVB_T_N_FFT, DVB_T_KMAX / 2, num_taps, stopband_db)
}
pub fn params(&self) -> DvbTFrameParams {
self.params
}
pub fn modulate(&self, payload: &[u8]) -> DvbTFrame {
let params = self.params;
let cache = CodecCache::new();
let base = params.config();
let cp_len = base.carrier_plan.cp_len();
let n_fft = DVB_T_N_FFT;
let sps = n_fft + cp_len;
let vbits = params.constellation().bits_per_symbol();
let bits_per_sym = DVB_T_DATA_CARRIERS * vbits;
let mut ts = ts_packetize(payload);
let n_real_packets = ts.len() / TS_PACKET_LEN;
let payload_syms = symbols_for_coded_bits(
&base,
params.constellation(),
dvb_t_coded_bits_with(params, n_real_packets, &cache),
);
let n_symbols = payload_syms.max(TPS_SYMBOLS_PER_FRAME);
let fill = dvb_t_frame_fill_with(params, n_real_packets.max(1), n_symbols, &cache);
debug_assert!(
fill.n_ts_packets >= n_real_packets,
"frame filling must not drop real payload packets"
);
debug_assert_eq!(fill.capacity_bits, n_symbols * bits_per_sym);
ts_stuff_null_packets(&mut ts, fill.n_ts_packets);
ts_energy_disperse(&mut ts);
let mut coded_bits = encode_chain(
&ts,
CrcKind::None, DVB_T_FRAME_OUTER,
params.inner(),
DVB_T_FRAME_OUTER_IL,
InterleaverKind::None,
ScramblerKind::None,
ScramblerPos::BeforeOuterFec,
0,
&cache,
);
debug_assert_eq!(coded_bits.len(), fill.coded_bits);
let filler = fill.filler_bits();
debug_assert!(
filler < coded_bits.len(),
"the remainder is under one packet's coded step, far below a frame"
);
coded_bits.extend_from_within(..filler);
debug_assert_eq!(coded_bits.len(), fill.capacity_bits);
let mut mapper = ScatteredPilotMapper::new(params.guard());
let mut tps_enc = TpsEncoder::new();
let tps_block = params.tps_word().pack();
let tps_bins = tps_carrier_bins();
let mut ifft = IfftBlock::new(n_fft);
let mut cp_insert = CyclicPrefixInsert::new(n_fft, cp_len);
let mut data_syms = vec![C32::default(); DVB_T_DATA_CARRIERS];
let mut freq = vec![C32::default(); n_fft];
let mut time = vec![C32::default(); n_fft];
let mut iq = vec![C32::default(); n_symbols * sps];
for s in 0..n_symbols {
for (c, slot) in data_syms.iter_mut().enumerate() {
let bit_base = s * bits_per_sym + c * vbits;
*slot =
dvb_t_map_symbol(&coded_bits[bit_base..bit_base + vbits]).expect("DVB-T order");
}
mapper.map_symbol(&data_syms, &mut freq);
let tps_bit = tps_block[s % TPS_SYMBOLS_PER_FRAME];
let cells = tps_enc.next_symbol(tps_bit);
for (&bin, &cell) in tps_bins.iter().zip(cells.iter()) {
freq[bin] = cell;
}
if (s + 1) % TPS_SYMBOLS_PER_FRAME == 0 {
tps_enc.reset();
}
ifft.process(&freq, &mut time);
cp_insert.process(&time, &mut iq[s * sps..(s + 1) * sps]);
}
if self.window_roll_off > 0 {
let mut win = SymbolWindow::new(sps, self.window_roll_off);
for s in 0..n_symbols {
let symbol: Vec<C32> = iq[s * sps..(s + 1) * sps].to_vec();
win.process(&symbol, &mut iq[s * sps..(s + 1) * sps]);
}
}
if let Some(lowpass) = self.tx_lowpass {
lowpass.apply(&mut iq);
}
DvbTFrame {
iq,
n_symbols,
samples_per_symbol: sps,
}
}
}