const GF128_PRIM: u16 = 0x89;
const GF128_ORDER: usize = 127;
const TPS_BCH_GEN: u32 = 0x4377;
pub const TPS_CODEWORD_BITS: usize = 67;
pub const TPS_INFO_BITS: usize = 53;
pub const TPS_PARITY_BITS: usize = 14;
struct Gf128 {
exp: [u8; 2 * GF128_ORDER],
log: [u8; GF128_ORDER + 1],
}
impl Gf128 {
fn new() -> Self {
let mut exp = [0u8; 2 * GF128_ORDER];
let mut log = [0u8; GF128_ORDER + 1];
let mut x: u16 = 1;
for (i, slot) in exp.iter_mut().enumerate().take(GF128_ORDER) {
*slot = x as u8;
log[x as usize] = i as u8;
x <<= 1;
if x & 0x80 != 0 {
x ^= GF128_PRIM;
}
}
for i in GF128_ORDER..2 * GF128_ORDER {
exp[i] = exp[i - GF128_ORDER];
}
Self { exp, log }
}
#[inline]
fn mul(&self, a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
0
} else {
self.exp[self.log[a as usize] as usize + self.log[b as usize] as usize]
}
}
#[inline]
fn pow_alpha(&self, i: usize) -> u8 {
self.exp[i % GF128_ORDER]
}
}
pub fn tps_bch_encode(info: &[u8]) -> [u8; TPS_CODEWORD_BITS] {
assert_eq!(info.len(), TPS_INFO_BITS, "TPS info must be 53 bits");
let parity = tps_bch_parity(info);
let mut out = [0u8; TPS_CODEWORD_BITS];
out[..TPS_INFO_BITS].copy_from_slice(info);
for (i, slot) in out[TPS_INFO_BITS..].iter_mut().enumerate() {
*slot = ((parity >> (TPS_PARITY_BITS - 1 - i)) & 1) as u8;
}
out
}
fn tps_bch_parity(info: &[u8]) -> u32 {
let mut reg: u32 = 0;
let top = 1u32 << TPS_PARITY_BITS; let feed = info
.iter()
.copied()
.chain(std::iter::repeat_n(0u8, TPS_PARITY_BITS));
for b in feed {
reg = (reg << 1) | (b & 1) as u32;
if reg & top != 0 {
reg ^= TPS_BCH_GEN;
}
}
reg & ((1 << TPS_PARITY_BITS) - 1)
}
pub fn tps_bch_decode(codeword: &[u8]) -> Option<[u8; TPS_INFO_BITS]> {
if codeword.len() != TPS_CODEWORD_BITS {
return None;
}
let gf = Gf128::new();
let n_shift = GF128_ORDER - TPS_CODEWORD_BITS;
let mut synd = [0u8; 4];
for (s, syn) in synd.iter_mut().enumerate() {
let i = s + 1;
let mut acc = 0u8;
for (pos, &bit) in codeword.iter().enumerate() {
if bit & 1 != 0 {
let deg = TPS_CODEWORD_BITS - 1 - pos + n_shift;
acc ^= gf.pow_alpha(i * deg);
}
}
*syn = acc;
}
if synd.iter().all(|&s| s == 0) {
return Some(info_of(codeword));
}
let s1 = synd[0];
let s3 = synd[2];
let (sig1, sig2) = if s1 == 0 {
return None;
} else {
let s1_3 = gf.mul(gf.mul(s1, s1), s1);
let num = s3 ^ s1_3;
let sig2 = if num == 0 {
0
} else {
gf.exp[(gf.log[num as usize] as usize + GF128_ORDER - gf.log[s1 as usize] as usize)
% GF128_ORDER]
};
(s1, sig2)
};
let mut err = [0u8; TPS_CODEWORD_BITS];
let mut found = 0usize;
for (pos, e) in err.iter_mut().enumerate() {
let deg = TPS_CODEWORD_BITS - 1 - pos + n_shift;
let x = gf.pow_alpha((GF128_ORDER - (deg % GF128_ORDER)) % GF128_ORDER);
let x2 = gf.mul(x, x);
let val = 1u8 ^ gf.mul(sig1, x) ^ gf.mul(sig2, x2);
if val == 0 {
*e = 1;
found += 1;
}
}
let expected = if sig2 == 0 { 1 } else { 2 };
if found != expected {
return None;
}
let mut fixed = [0u8; TPS_CODEWORD_BITS];
for i in 0..TPS_CODEWORD_BITS {
fixed[i] = codeword[i] ^ err[i];
}
let re = tps_bch_encode(&info_of(&fixed));
if re != fixed {
return None;
}
Some(info_of(&fixed))
}
fn info_of(codeword: &[u8]) -> [u8; TPS_INFO_BITS] {
let mut info = [0u8; TPS_INFO_BITS];
info.copy_from_slice(&codeword[..TPS_INFO_BITS]);
info
}
use crate::fec::PunctureRate;
use crate::modulate::ConstellationOrder;
use crate::waveform::dvb_t::{
DVB_T_ACTIVE_CARRIERS, DVB_T_TPS_CARRIERS_2K, GuardInterval, wk_prbs,
};
use num_complex::Complex32 as C32;
pub const TPS_SYNC_WORD_13: u16 = 0b0011_0101_1110_1110;
pub const TPS_SYNC_WORD_24: u16 = 0b1100_1010_0001_0001;
const TPS_LENGTH_WITH_CELL_ID: u8 = 0b011111;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TpsWord {
pub frame_number: u8,
pub constellation: ConstellationOrder,
pub code_rate_hp: PunctureRate,
pub guard: GuardInterval,
pub cell_id: u8,
}
impl TpsWord {
fn constellation_code(self) -> u8 {
match self.constellation {
ConstellationOrder::Qam16 => 0b01,
ConstellationOrder::Qam64 => 0b10,
_ => 0b00, }
}
fn constellation_from_code(code: u8) -> Option<ConstellationOrder> {
match code {
0b00 => Some(ConstellationOrder::Qpsk),
0b01 => Some(ConstellationOrder::Qam16),
0b10 => Some(ConstellationOrder::Qam64),
_ => None, }
}
fn rate_code(rate: PunctureRate) -> u8 {
match rate {
PunctureRate::R1_2 => 0b000,
PunctureRate::R2_3 => 0b001,
PunctureRate::R3_4 => 0b010,
PunctureRate::R5_6 => 0b011,
PunctureRate::R7_8 => 0b100,
}
}
fn rate_from_code(code: u8) -> Option<PunctureRate> {
match code {
0b000 => Some(PunctureRate::R1_2),
0b001 => Some(PunctureRate::R2_3),
0b010 => Some(PunctureRate::R3_4),
0b011 => Some(PunctureRate::R5_6),
0b100 => Some(PunctureRate::R7_8),
_ => None, }
}
fn guard_code(guard: GuardInterval) -> u8 {
match guard {
GuardInterval::G1_32 => 0b00,
GuardInterval::G1_16 => 0b01,
GuardInterval::G1_8 => 0b10,
GuardInterval::G1_4 => 0b11,
}
}
fn guard_from_code(code: u8) -> GuardInterval {
match code & 0b11 {
0b00 => GuardInterval::G1_32,
0b01 => GuardInterval::G1_16,
0b10 => GuardInterval::G1_8,
_ => GuardInterval::G1_4,
}
}
fn sync_word(self) -> u16 {
if self.frame_number.is_multiple_of(2) {
TPS_SYNC_WORD_13 } else {
TPS_SYNC_WORD_24 }
}
pub fn pack(self) -> [u8; 68] {
let mut info = [0u8; TPS_INFO_BITS]; let mut set = |range: std::ops::Range<usize>, value: u32| {
let width = range.len();
for (j, idx) in range.enumerate() {
info[idx] = ((value >> (width - 1 - j)) & 1) as u8;
}
};
set(0..16, self.sync_word() as u32);
set(16..22, TPS_LENGTH_WITH_CELL_ID as u32);
set(22..24, (self.frame_number & 0b11) as u32);
set(24..26, self.constellation_code() as u32);
set(26..29, 0);
set(29..32, Self::rate_code(self.code_rate_hp) as u32);
set(32..35, Self::rate_code(self.code_rate_hp) as u32);
set(35..37, Self::guard_code(self.guard) as u32);
set(37..39, 0);
set(39..47, self.cell_id as u32);
let cw = tps_bch_encode(&info);
let mut out = [0u8; 68];
out[1..].copy_from_slice(&cw);
out
}
pub fn unpack(bits: &[u8]) -> Option<Self> {
if bits.len() != 68 {
return None;
}
let info = tps_bch_decode(&bits[1..])?;
let get = |range: std::ops::Range<usize>| -> u32 {
let mut v = 0u32;
for idx in range {
v = (v << 1) | (info[idx] & 1) as u32;
}
v
};
let frame_number = get(22..24) as u8;
let constellation = Self::constellation_from_code(get(24..26) as u8)?;
let code_rate_hp = Self::rate_from_code(get(29..32) as u8)?;
let guard = Self::guard_from_code(get(35..37) as u8);
let cell_id = get(39..47) as u8;
Some(Self {
frame_number,
constellation,
code_rate_hp,
guard,
cell_id,
})
}
}
pub const TPS_CARRIER_COUNT: usize = DVB_T_TPS_CARRIERS_2K.len();
pub const TPS_SYMBOLS_PER_FRAME: usize = 68;
fn tps_reference_signs() -> [f32; TPS_CARRIER_COUNT] {
let wk = wk_prbs(DVB_T_ACTIVE_CARRIERS);
let mut signs = [0.0f32; TPS_CARRIER_COUNT];
for (s, &k) in signs.iter_mut().zip(DVB_T_TPS_CARRIERS_2K.iter()) {
*s = 2.0 * (0.5 - wk[k] as f32); }
signs
}
#[derive(Debug, Clone)]
pub struct TpsEncoder {
signs: [f32; TPS_CARRIER_COUNT],
symbol: usize,
}
impl Default for TpsEncoder {
fn default() -> Self {
Self::new()
}
}
impl TpsEncoder {
pub fn new() -> Self {
Self {
signs: tps_reference_signs(),
symbol: 0,
}
}
pub fn reset(&mut self) {
self.signs = tps_reference_signs();
self.symbol = 0;
}
pub fn next_symbol(&mut self, bit: u8) -> [C32; TPS_CARRIER_COUNT] {
if self.symbol > 0 && (bit & 1) == 1 {
for s in self.signs.iter_mut() {
*s = -*s;
}
}
let mut out = [C32::default(); TPS_CARRIER_COUNT];
for (o, &s) in out.iter_mut().zip(self.signs.iter()) {
*o = C32::new(s, 0.0);
}
self.symbol += 1;
out
}
}
#[derive(Debug, Clone)]
pub struct TpsDecoder {
prev: [C32; TPS_CARRIER_COUNT],
symbol: usize,
bits: Vec<u8>,
}
impl Default for TpsDecoder {
fn default() -> Self {
Self::new()
}
}
impl TpsDecoder {
pub fn new() -> Self {
Self {
prev: [C32::default(); TPS_CARRIER_COUNT],
symbol: 0,
bits: Vec::with_capacity(TPS_SYMBOLS_PER_FRAME),
}
}
pub fn reset(&mut self) {
self.prev = [C32::default(); TPS_CARRIER_COUNT];
self.symbol = 0;
self.bits.clear();
}
pub fn feed_symbol(&mut self, cells: &[C32]) {
debug_assert!(cells.len() >= TPS_CARRIER_COUNT);
if self.symbol == 0 {
self.bits.push(0); } else {
let mut acc = 0.0f32;
for (cell, prev) in cells[..TPS_CARRIER_COUNT].iter().zip(self.prev.iter()) {
acc += (cell * prev.conj()).re;
}
self.bits.push(u8::from(acc < 0.0));
}
self.prev[..TPS_CARRIER_COUNT].copy_from_slice(&cells[..TPS_CARRIER_COUNT]);
self.symbol += 1;
}
pub fn is_complete(&self) -> bool {
self.bits.len() >= TPS_SYMBOLS_PER_FRAME
}
pub fn bits(&self) -> &[u8] {
&self.bits
}
pub fn word(&self) -> Option<TpsWord> {
if !self.is_complete() {
return None;
}
TpsWord::unpack(&self.bits[..TPS_SYMBOLS_PER_FRAME])
}
}