[refactor](trx-ftx): consolidate FT2 decoder with shared FT8 code
Eliminate duplicated code between FT2 and FT8/shared modules: - Share parity8() from encode.rs, remove copies in ft2/mod.rs and osd.rs - Share pack_bits() from decode.rs, remove pack_bits91() from osd.rs - Add verify_crc_and_build_message() to decode.rs, used by both FT8 and FT2 - Add normalize_llr() to decode.rs, replacing per-module normalization - Make encode174() pub(crate), add encode174_to_bits() for bit-array output - Wire FT2 decode_hit to use full BP+OSD decoder from osd.rs instead of separate BP + sum-product + OSD-lite flow - Align LLR scale factor to 2.83 matching reference implementation Net -178 lines removed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -521,6 +521,73 @@ fn ft2_extract_logl_seq(
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize LLR array by dividing by standard deviation, then optionally
|
||||
/// scaling to a target variance.
|
||||
///
|
||||
/// If `target_variance` is `Some(v)`, the output has variance ≈ v.
|
||||
/// If `None`, the output has unit variance (σ = 1).
|
||||
pub(crate) fn normalize_llr(log174: &mut [f32; FTX_LDPC_N], target_variance: Option<f32>) {
|
||||
let mut sum = 0.0f32;
|
||||
let mut sum2 = 0.0f32;
|
||||
for &v in log174.iter() {
|
||||
sum += v;
|
||||
sum2 += v * v;
|
||||
}
|
||||
let inv_n = 1.0 / FTX_LDPC_N as f32;
|
||||
let variance = (sum2 - sum * sum * inv_n) * inv_n;
|
||||
if variance <= 1e-12 {
|
||||
return;
|
||||
}
|
||||
let scale = match target_variance {
|
||||
Some(tv) => (tv / variance).sqrt(),
|
||||
None => 1.0 / variance.sqrt(),
|
||||
};
|
||||
for v in log174.iter_mut() {
|
||||
*v *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify CRC of a 174-bit plaintext and build an FtxMessage.
|
||||
///
|
||||
/// `plain174`: decoded LDPC codeword (174 bits, each 0 or 1).
|
||||
/// `uses_xor`: true for FT4/FT2 (apply XOR sequence), false for FT8.
|
||||
///
|
||||
/// Returns `None` if CRC check fails.
|
||||
pub(crate) fn verify_crc_and_build_message(
|
||||
plain174: &[u8; FTX_LDPC_N],
|
||||
uses_xor: bool,
|
||||
) -> Option<FtxMessage> {
|
||||
let mut a91 = [0u8; crate::protocol::FTX_LDPC_K_BYTES];
|
||||
pack_bits(plain174, crate::protocol::FTX_LDPC_K, &mut a91);
|
||||
|
||||
let crc_extracted = crate::crc::ftx_extract_crc(&a91);
|
||||
a91[9] &= 0xF8;
|
||||
a91[10] = 0x00;
|
||||
let crc_calculated = crate::crc::ftx_compute_crc(&a91, 96 - 14);
|
||||
|
||||
if crc_extracted != crc_calculated {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Re-read a91 since we modified it for CRC check
|
||||
pack_bits(plain174, crate::protocol::FTX_LDPC_K, &mut a91);
|
||||
|
||||
let mut message = FtxMessage {
|
||||
hash: crc_calculated,
|
||||
payload: [0; FTX_PAYLOAD_LENGTH_BYTES],
|
||||
};
|
||||
|
||||
if uses_xor {
|
||||
for i in 0..10 {
|
||||
message.payload[i] = a91[i] ^ FT4_XOR_SEQUENCE[i];
|
||||
}
|
||||
} else {
|
||||
message.payload[..10].copy_from_slice(&a91[..10]);
|
||||
}
|
||||
|
||||
Some(message)
|
||||
}
|
||||
|
||||
/// Normalize log-likelihoods.
|
||||
fn ftx_normalize_logl(log174: &mut [f32; FTX_LDPC_N]) {
|
||||
let mut sum = 0.0f32;
|
||||
@@ -583,32 +650,7 @@ pub fn ftx_decode_candidate(
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut a91 = [0u8; FTX_LDPC_K_BYTES];
|
||||
pack_bits(&plain174, FTX_LDPC_K, &mut a91);
|
||||
|
||||
let crc_extracted = crate::crc::ftx_extract_crc(&a91);
|
||||
a91[9] &= 0xF8;
|
||||
a91[10] = 0x00;
|
||||
let crc_calculated = crate::crc::ftx_compute_crc(&a91, 96 - 14);
|
||||
|
||||
if crc_extracted != crc_calculated {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut message = FtxMessage {
|
||||
hash: crc_calculated,
|
||||
payload: [0; FTX_PAYLOAD_LENGTH_BYTES],
|
||||
};
|
||||
|
||||
if wf.protocol.uses_ft4_layout() {
|
||||
for i in 0..10 {
|
||||
message.payload[i] = a91[i] ^ FT4_XOR_SEQUENCE[i];
|
||||
}
|
||||
} else {
|
||||
message.payload[..10].copy_from_slice(&a91[..10]);
|
||||
}
|
||||
|
||||
Some(message)
|
||||
verify_crc_and_build_message(&plain174, wf.protocol.uses_ft4_layout())
|
||||
}
|
||||
|
||||
fn max4(a: f32, b: f32, c: f32, d: f32) -> f32 {
|
||||
|
||||
@@ -10,11 +10,11 @@ use crate::crc::ftx_add_crc;
|
||||
use crate::protocol::{FT4_NN, FT8_NN, FTX_LDPC_K, FTX_LDPC_K_BYTES, FTX_LDPC_M, FTX_LDPC_N_BYTES};
|
||||
|
||||
/// Returns 1 if an odd number of bits are set in `x`, zero otherwise.
|
||||
fn parity8(x: u8) -> u8 {
|
||||
pub(crate) fn parity8(x: u8) -> u8 {
|
||||
let x = x ^ (x >> 4);
|
||||
let x = x ^ (x >> 2);
|
||||
let x = x ^ (x >> 1);
|
||||
x % 2
|
||||
x & 1
|
||||
}
|
||||
|
||||
/// Encode via LDPC a 91-bit message and return a 174-bit codeword.
|
||||
@@ -24,7 +24,7 @@ fn parity8(x: u8) -> u8 {
|
||||
///
|
||||
/// `message` must be at least `FTX_LDPC_K_BYTES` (12) bytes.
|
||||
/// `codeword` must be at least `FTX_LDPC_N_BYTES` (22) bytes.
|
||||
fn encode174(message: &[u8], codeword: &mut [u8]) {
|
||||
pub(crate) fn encode174(message: &[u8], codeword: &mut [u8]) {
|
||||
// Fill the codeword with message and zeros
|
||||
for j in 0..FTX_LDPC_N_BYTES {
|
||||
codeword[j] = if j < FTX_LDPC_K_BYTES { message[j] } else { 0 };
|
||||
@@ -53,6 +53,23 @@ fn encode174(message: &[u8], codeword: &mut [u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a packed 91-bit message into a 174-bit codeword (bit array).
|
||||
///
|
||||
/// Each element of the returned array is 0 or 1.
|
||||
/// Uses the same (174, 91) LDPC generator as `encode174`.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn encode174_to_bits(a91: &[u8; FTX_LDPC_K_BYTES]) -> [u8; crate::protocol::FTX_LDPC_N] {
|
||||
use crate::protocol::FTX_LDPC_N;
|
||||
let mut codeword_packed = [0u8; FTX_LDPC_N_BYTES];
|
||||
encode174(a91, &mut codeword_packed);
|
||||
|
||||
let mut bits = [0u8; FTX_LDPC_N];
|
||||
for i in 0..FTX_LDPC_N {
|
||||
bits[i] = (codeword_packed[i / 8] >> (7 - (i % 8))) & 0x01;
|
||||
}
|
||||
bits
|
||||
}
|
||||
|
||||
/// Generate FT8 tone sequence from payload data.
|
||||
///
|
||||
/// `payload` is a 10-byte array containing 77 bits of payload data.
|
||||
|
||||
@@ -16,10 +16,7 @@ pub mod sync;
|
||||
use num_complex::Complex32;
|
||||
use realfft::RealFftPlanner;
|
||||
|
||||
use crate::constants::FT4_XOR_SEQUENCE;
|
||||
use crate::crc::{ftx_compute_crc, ftx_extract_crc};
|
||||
use crate::decode::{pack_bits, FtxMessage};
|
||||
use crate::ldpc;
|
||||
use crate::decode::{normalize_llr, verify_crc_and_build_message, FtxMessage};
|
||||
use crate::protocol::*;
|
||||
|
||||
use bitmetrics::BitMetricsWorkspace;
|
||||
@@ -41,9 +38,6 @@ pub const FT2_FRAME_SYMBOLS: usize = FT2_NN - FT2_NR;
|
||||
pub const FT2_FRAME_SAMPLES: usize = FT2_FRAME_SYMBOLS * FT2_NSS;
|
||||
pub const FT2_SYMBOL_PERIOD_F: f32 = FT2_SYMBOL_PERIOD;
|
||||
|
||||
/// Maximum hard-error count for accepting an OSD result.
|
||||
const FT2_OSD_MAX_HARD_ERRORS: usize = 36;
|
||||
|
||||
/// Frequency offset applied to FT2 candidates.
|
||||
pub fn ft2_frequency_offset_hz() -> f32 {
|
||||
-1.5 / FT2_SYMBOL_PERIOD_F
|
||||
@@ -603,9 +597,9 @@ impl Ft2Pipeline {
|
||||
|
||||
// Scale and derive combined passes
|
||||
for i in 0..FTX_LDPC_N {
|
||||
llr_passes[0][i] *= 3.2;
|
||||
llr_passes[1][i] *= 3.2;
|
||||
llr_passes[2][i] *= 3.2;
|
||||
llr_passes[0][i] *= 2.83;
|
||||
llr_passes[1][i] *= 2.83;
|
||||
llr_passes[2][i] *= 2.83;
|
||||
|
||||
let a = llr_passes[0][i];
|
||||
let b = llr_passes[1][i];
|
||||
@@ -624,66 +618,39 @@ impl Ft2Pipeline {
|
||||
llr_passes[4][i] = (a + b + c) / 3.0;
|
||||
}
|
||||
|
||||
// Multi-pass LDPC decode
|
||||
// Multi-pass LDPC decode using full BP+OSD decoder
|
||||
let mut ok = false;
|
||||
let mut message = FtxMessage::default();
|
||||
let mut global_best_errors = FTX_LDPC_M as i32;
|
||||
let mut apmask = [0u8; FTX_LDPC_N];
|
||||
|
||||
for pass in 0..5 {
|
||||
if ok {
|
||||
break;
|
||||
}
|
||||
let mut log174 = llr_passes[pass];
|
||||
normalize_log174(&mut log174);
|
||||
normalize_llr(&mut log174, None);
|
||||
|
||||
let mut nharderror = FTX_LDPC_M as i32;
|
||||
let mut message91 = [0u8; FTX_LDPC_K];
|
||||
let mut cw = [0u8; FTX_LDPC_N];
|
||||
let mut ntype = 0i32;
|
||||
let mut nharderror = -1i32;
|
||||
let mut dmin = 0.0f32;
|
||||
|
||||
// BP decode
|
||||
let mut bp_plain = [0u8; FTX_LDPC_N];
|
||||
let bp_errors = ldpc::bp_decode(&log174, 50, &mut bp_plain);
|
||||
if bp_errors < nharderror {
|
||||
nharderror = bp_errors;
|
||||
}
|
||||
if bp_errors == 0 {
|
||||
if let Some(msg) = unpack_message(&bp_plain) {
|
||||
message = msg;
|
||||
ok = true;
|
||||
nharderror = 0;
|
||||
}
|
||||
}
|
||||
osd::ft2_decode174_91_osd(
|
||||
&mut log174,
|
||||
FTX_LDPC_K,
|
||||
3,
|
||||
3,
|
||||
&mut apmask,
|
||||
&mut message91,
|
||||
&mut cw,
|
||||
&mut ntype,
|
||||
&mut nharderror,
|
||||
&mut dmin,
|
||||
);
|
||||
|
||||
// Sum-product decode (fallback)
|
||||
if !ok {
|
||||
let mut sp_log174 = llr_passes[pass];
|
||||
normalize_log174(&mut sp_log174);
|
||||
let mut sp_plain = [0u8; FTX_LDPC_N];
|
||||
let sp_errors = ldpc::ldpc_decode(&mut sp_log174, 50, &mut sp_plain);
|
||||
if sp_errors < nharderror {
|
||||
nharderror = sp_errors;
|
||||
}
|
||||
if sp_errors == 0 {
|
||||
if let Some(msg) = unpack_message(&sp_plain) {
|
||||
message = msg;
|
||||
ok = true;
|
||||
nharderror = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if nharderror < global_best_errors {
|
||||
global_best_errors = nharderror;
|
||||
}
|
||||
}
|
||||
|
||||
// CRC-based OSD-1/OSD-2 fallback when LDPC was close to converging
|
||||
if !ok && global_best_errors <= 6 {
|
||||
for pass in 0..5 {
|
||||
if ok {
|
||||
break;
|
||||
}
|
||||
let mut osd_log174 = llr_passes[pass];
|
||||
normalize_log174(&mut osd_log174);
|
||||
if let Some(msg) = osd_lite_decode(&osd_log174) {
|
||||
if ntype > 0 && nharderror >= 0 {
|
||||
if let Some(msg) = verify_crc_and_build_message(&cw, true) {
|
||||
message = msg;
|
||||
ok = true;
|
||||
}
|
||||
@@ -775,177 +742,6 @@ fn extract_signal_region(input: &[Complex32], start: i32, out_signal: &mut [Comp
|
||||
.copy_from_slice(&input[src_start..(src_start + copy_len)]);
|
||||
}
|
||||
|
||||
/// Normalize LLR array (divide by standard deviation).
|
||||
fn normalize_log174(log174: &mut [f32; FTX_LDPC_N]) {
|
||||
let mut sum = 0.0f32;
|
||||
let mut sum2 = 0.0f32;
|
||||
for &v in log174.iter() {
|
||||
sum += v;
|
||||
sum2 += v * v;
|
||||
}
|
||||
let inv_n = 1.0 / FTX_LDPC_N as f32;
|
||||
let variance = (sum2 - sum * sum * inv_n) * inv_n;
|
||||
if variance <= 1e-12 {
|
||||
return;
|
||||
}
|
||||
let sigma = variance.sqrt();
|
||||
for v in log174.iter_mut() {
|
||||
*v /= sigma;
|
||||
}
|
||||
}
|
||||
|
||||
/// Unpack a 174-bit plaintext into an FtxMessage, verifying CRC and applying XOR sequence.
|
||||
fn unpack_message(plain174: &[u8; FTX_LDPC_N]) -> Option<FtxMessage> {
|
||||
let mut a91 = [0u8; FTX_LDPC_K_BYTES];
|
||||
pack_bits(plain174, FTX_LDPC_K, &mut a91);
|
||||
|
||||
let crc_extracted = ftx_extract_crc(&a91);
|
||||
a91[9] &= 0xF8;
|
||||
a91[10] = 0x00;
|
||||
let crc_calculated = ftx_compute_crc(&a91, 96 - 14);
|
||||
|
||||
if crc_extracted != crc_calculated {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Re-read a91 since we modified it for CRC check
|
||||
pack_bits(plain174, FTX_LDPC_K, &mut a91);
|
||||
|
||||
let mut msg = FtxMessage {
|
||||
hash: crc_calculated,
|
||||
payload: [0; FTX_PAYLOAD_LENGTH_BYTES],
|
||||
};
|
||||
for i in 0..10 {
|
||||
msg.payload[i] = a91[i] ^ FT4_XOR_SEQUENCE[i];
|
||||
}
|
||||
Some(msg)
|
||||
}
|
||||
|
||||
/// Encode a packed 91-bit message into a 174-bit codeword (bit array).
|
||||
fn encode_codeword_from_a91(a91: &[u8; FTX_LDPC_K_BYTES]) -> [u8; FTX_LDPC_N] {
|
||||
let mut codeword = [0u8; FTX_LDPC_N];
|
||||
// Systematic part
|
||||
for i in 0..FTX_LDPC_K {
|
||||
codeword[i] = (a91[i / 8] >> (7 - (i % 8))) & 0x01;
|
||||
}
|
||||
// Parity part using generator matrix
|
||||
for i in 0..FTX_LDPC_M {
|
||||
let mut nsum: u8 = 0;
|
||||
for j in 0..FTX_LDPC_K_BYTES {
|
||||
let x = a91[j] & crate::constants::FTX_LDPC_GENERATOR[i][j];
|
||||
nsum ^= parity8(x);
|
||||
}
|
||||
codeword[FTX_LDPC_K + i] = nsum & 0x01;
|
||||
}
|
||||
codeword
|
||||
}
|
||||
|
||||
/// Count parity of a byte.
|
||||
fn parity8(x: u8) -> u8 {
|
||||
let x = x ^ (x >> 4);
|
||||
let x = x ^ (x >> 2);
|
||||
let x = x ^ (x >> 1);
|
||||
x & 1
|
||||
}
|
||||
|
||||
/// Count hard errors between LLR signs and a candidate codeword.
|
||||
fn count_hard_errors_vs_llr(log174: &[f32; FTX_LDPC_N], codeword: &[u8; FTX_LDPC_N]) -> usize {
|
||||
let mut errors = 0;
|
||||
for i in 0..FTX_LDPC_N {
|
||||
let received = if log174[i] >= 0.0 { 1u8 } else { 0u8 };
|
||||
if received != codeword[i] {
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
errors
|
||||
}
|
||||
|
||||
/// Try a CRC candidate: encode the packed message, verify CRC and hard-error count.
|
||||
fn try_crc_candidate(
|
||||
a91: &[u8; FTX_LDPC_K_BYTES],
|
||||
log174: &[f32; FTX_LDPC_N],
|
||||
) -> Option<FtxMessage> {
|
||||
let codeword = encode_codeword_from_a91(a91);
|
||||
|
||||
// Check CRC via unpack
|
||||
let mut plain174 = [0u8; FTX_LDPC_N];
|
||||
plain174.copy_from_slice(&codeword);
|
||||
let msg = unpack_message(&plain174)?;
|
||||
|
||||
// Verify consistency with received LLRs
|
||||
if count_hard_errors_vs_llr(log174, &codeword) > FT2_OSD_MAX_HARD_ERRORS {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(msg)
|
||||
}
|
||||
|
||||
/// Reliability entry for OSD-lite sorting.
|
||||
struct ReliabilityEntry {
|
||||
index: usize,
|
||||
reliability: f32,
|
||||
}
|
||||
|
||||
/// CRC-guided OSD-1/OSD-2 lite decoder.
|
||||
///
|
||||
/// Tries flipping each of the 16 least-reliable systematic bits (OSD-1),
|
||||
/// then all pairs (OSD-2). Returns decoded message on CRC match.
|
||||
fn osd_lite_decode(log174: &[f32; FTX_LDPC_N]) -> Option<FtxMessage> {
|
||||
// Build base hard decision from systematic bits
|
||||
let mut base_a91 = [0u8; FTX_LDPC_K_BYTES];
|
||||
for i in 0..FTX_LDPC_K {
|
||||
if log174[i] >= 0.0 {
|
||||
base_a91[i / 8] |= 0x80u8 >> (i % 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Try base (zero flips)
|
||||
if let Some(msg) = try_crc_candidate(&base_a91, log174) {
|
||||
return Some(msg);
|
||||
}
|
||||
|
||||
// Sort systematic bits by reliability (ascending = least reliable first)
|
||||
let mut rel: Vec<ReliabilityEntry> = (0..FTX_LDPC_K)
|
||||
.map(|i| ReliabilityEntry {
|
||||
index: i,
|
||||
reliability: log174[i].abs(),
|
||||
})
|
||||
.collect();
|
||||
rel.sort_by(|a, b| {
|
||||
a.reliability
|
||||
.partial_cmp(&b.reliability)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
|
||||
let max_candidates = 16.min(FTX_LDPC_K);
|
||||
|
||||
// OSD-1: single bit flips
|
||||
for i in 0..max_candidates {
|
||||
let mut trial = base_a91;
|
||||
let b0 = rel[i].index;
|
||||
trial[b0 / 8] ^= 0x80u8 >> (b0 % 8);
|
||||
if let Some(msg) = try_crc_candidate(&trial, log174) {
|
||||
return Some(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// OSD-2: all pairs
|
||||
for i in 0..max_candidates {
|
||||
for j in (i + 1)..max_candidates {
|
||||
let mut trial = base_a91;
|
||||
let b0 = rel[i].index;
|
||||
let b1 = rel[j].index;
|
||||
trial[b0 / 8] ^= 0x80u8 >> (b0 % 8);
|
||||
trial[b1 / 8] ^= 0x80u8 >> (b1 % 8);
|
||||
if let Some(msg) = try_crc_candidate(&trial, log174) {
|
||||
return Some(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -996,17 +792,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parity8_basic() {
|
||||
assert_eq!(parity8(0x00), 0);
|
||||
assert_eq!(parity8(0x01), 1);
|
||||
assert_eq!(parity8(0x03), 0);
|
||||
assert_eq!(parity8(0xFF), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_codeword_all_zeros() {
|
||||
fn encode174_to_bits_all_zeros() {
|
||||
let a91 = [0u8; FTX_LDPC_K_BYTES];
|
||||
let cw = encode_codeword_from_a91(&a91);
|
||||
let cw = crate::encode::encode174_to_bits(&a91);
|
||||
for &b in &cw {
|
||||
assert_eq!(b, 0);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
use crate::constants::{FTX_LDPC_GENERATOR, FTX_LDPC_MN, FTX_LDPC_NM, FTX_LDPC_NUM_ROWS};
|
||||
use crate::crc::{ftx_compute_crc, ftx_extract_crc};
|
||||
use crate::decode::pack_bits;
|
||||
use crate::encode::parity8;
|
||||
use crate::protocol::{FTX_LDPC_K, FTX_LDPC_K_BYTES, FTX_LDPC_M, FTX_LDPC_N};
|
||||
|
||||
/// Check LDPC parity of a 174-bit codeword. Returns number of parity errors.
|
||||
@@ -67,30 +69,10 @@ fn platanh(x: f32) -> f32 {
|
||||
isign * 7.0
|
||||
}
|
||||
|
||||
/// Pack bit array into bytes (MSB first).
|
||||
fn pack_bits91(bit_array: &[u8], num_bits: usize, packed: &mut [u8]) {
|
||||
let num_bytes = num_bits.div_ceil(8);
|
||||
for b in packed[..num_bytes].iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
let mut mask: u8 = 0x80;
|
||||
let mut byte_idx = 0;
|
||||
for i in 0..num_bits {
|
||||
if bit_array[i] != 0 {
|
||||
packed[byte_idx] |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
if mask == 0 {
|
||||
mask = 0x80;
|
||||
byte_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check CRC of a 91-bit message (in bit array form).
|
||||
fn check_crc91(plain91: &[u8]) -> bool {
|
||||
let mut a91 = [0u8; FTX_LDPC_K_BYTES];
|
||||
pack_bits91(plain91, FTX_LDPC_K, &mut a91);
|
||||
pack_bits(plain91, FTX_LDPC_K, &mut a91);
|
||||
let crc_extracted = ftx_extract_crc(&a91);
|
||||
a91[9] &= 0xF8;
|
||||
a91[10] = 0x00;
|
||||
@@ -98,18 +80,10 @@ fn check_crc91(plain91: &[u8]) -> bool {
|
||||
crc_extracted == crc_calculated
|
||||
}
|
||||
|
||||
/// Compute parity of a byte.
|
||||
fn parity8(x: u8) -> u8 {
|
||||
let x = x ^ (x >> 4);
|
||||
let x = x ^ (x >> 2);
|
||||
let x = x ^ (x >> 1);
|
||||
x & 1
|
||||
}
|
||||
|
||||
/// Encode a 91-bit message (bit array) into a 174-bit codeword without CRC computation.
|
||||
fn encode174_91_nocrc_bits(message91: &[u8], codeword: &mut [u8; FTX_LDPC_N]) {
|
||||
let mut packed = [0u8; FTX_LDPC_K_BYTES];
|
||||
pack_bits91(message91, FTX_LDPC_K, &mut packed);
|
||||
pack_bits(message91, FTX_LDPC_K, &mut packed);
|
||||
|
||||
// Systematic bits
|
||||
for i in 0..FTX_LDPC_K {
|
||||
@@ -904,12 +878,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pack_bits91_basic() {
|
||||
fn shared_pack_bits_basic() {
|
||||
let mut bits = [0u8; FTX_LDPC_K];
|
||||
bits[0] = 1;
|
||||
bits[7] = 1;
|
||||
let mut packed = [0u8; FTX_LDPC_K_BYTES];
|
||||
pack_bits91(&bits, FTX_LDPC_K, &mut packed);
|
||||
pack_bits(&bits, FTX_LDPC_K, &mut packed);
|
||||
assert_eq!(packed[0], 0x81);
|
||||
}
|
||||
|
||||
@@ -923,7 +897,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parity8_basic() {
|
||||
fn shared_parity8_basic() {
|
||||
assert_eq!(parity8(0x00), 0);
|
||||
assert_eq!(parity8(0x01), 1);
|
||||
assert_eq!(parity8(0x03), 0);
|
||||
|
||||
Reference in New Issue
Block a user