use crate::types::Bit;
const STAGES: u32 = 17;
const STATE_MASK: u32 = (1 << STAGES) - 1;
const TAP_A: u32 = 12;
const TAP_B: u32 = 17;
const fn taps(state: u32) -> u32 {
((state >> (TAP_A - 1)) ^ (state >> (TAP_B - 1))) & 1
}
const fn shift_in(state: u32, bit: Bit) -> u32 {
((state << 1) | bit as u32) & STATE_MASK
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Scrambler {
state: u32,
}
impl Scrambler {
#[must_use]
pub const fn new() -> Self {
Self { state: 0 }
}
#[must_use]
pub const fn with_state(state: u32) -> Self {
Self {
state: state & STATE_MASK,
}
}
pub const fn scramble(&mut self, bit: Bit) -> Bit {
let out = (bit as u32 ^ taps(self.state)) & 1;
let out = if out == 1 { Bit::One } else { Bit::Zero };
self.state = shift_in(self.state, out);
out
}
pub fn scramble_iter<I>(self, bits: I) -> ScrambleIter<I>
where
I: Iterator<Item = Bit>,
{
ScrambleIter {
scrambler: self,
bits,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Descrambler {
state: u32,
}
impl Descrambler {
#[must_use]
pub const fn new() -> Self {
Self { state: 0 }
}
#[must_use]
pub const fn with_state(state: u32) -> Self {
Self {
state: state & STATE_MASK,
}
}
pub const fn descramble(&mut self, bit: Bit) -> Bit {
let out = (bit as u32 ^ taps(self.state)) & 1;
self.state = shift_in(self.state, bit);
if out == 1 { Bit::One } else { Bit::Zero }
}
pub fn descramble_iter<I>(self, bits: I) -> DescrambleIter<I>
where
I: Iterator<Item = Bit>,
{
DescrambleIter {
descrambler: self,
bits,
}
}
}
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ScrambleIter<I> {
scrambler: Scrambler,
bits: I,
}
impl<I> Iterator for ScrambleIter<I>
where
I: Iterator<Item = Bit>,
{
type Item = Bit;
fn next(&mut self) -> Option<Bit> {
self.bits.next().map(|bit| self.scrambler.scramble(bit))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.bits.size_hint()
}
}
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct DescrambleIter<I> {
descrambler: Descrambler,
bits: I,
}
impl<I> Iterator for DescrambleIter<I>
where
I: Iterator<Item = Bit>,
{
type Item = Bit;
fn next(&mut self) -> Option<Bit> {
self.bits.next().map(|bit| self.descrambler.descramble(bit))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.bits.size_hint()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pseudo_random_bits(seed: u32, len: usize) -> impl Iterator<Item = Bit> {
let mut state = seed | 1;
core::iter::repeat_with(move || {
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
Bit::from(state & 1 != 0)
})
.take(len)
}
fn bit_array<const N: usize>(bits: impl Iterator<Item = Bit>) -> [Bit; N] {
let mut out = [Bit::Zero; N];
let mut filled = 0usize;
for (slot, bit) in out.iter_mut().zip(bits) {
*slot = bit;
filled += 1;
}
assert_eq!(filled, N, "source iterator yielded fewer than {N} bits");
out
}
#[test]
fn roundtrip_structured_sequences() {
let sequences: &[&[Bit]] = &[
&[],
&[Bit::Zero],
&[Bit::One],
&[Bit::One; 64],
&[Bit::Zero; 64],
&[
Bit::Zero,
Bit::One,
Bit::One,
Bit::One,
Bit::One,
Bit::One,
Bit::One,
Bit::Zero, ],
];
for &seq in sequences {
let mut tx = Scrambler::new();
let mut rx = Descrambler::new();
for &bit in seq {
assert_eq!(rx.descramble(tx.scramble(bit)), bit);
}
}
}
#[test]
fn roundtrip_pseudo_random_sequences() {
for seed in [1, 0xDEAD_BEEF, 0x1234_5678] {
let mut tx = Scrambler::new();
let mut rx = Descrambler::new();
for bit in pseudo_random_bits(seed, 8192) {
assert_eq!(rx.descramble(tx.scramble(bit)), bit);
}
}
}
#[test]
fn roundtrip_with_matching_nonzero_seed() {
for seed in [1, 0x1_FFFF, 0x0AAAA, 0x15555] {
let mut tx = Scrambler::with_state(seed);
let mut rx = Descrambler::with_state(seed);
for bit in pseudo_random_bits(seed, 2048) {
assert_eq!(rx.descramble(tx.scramble(bit)), bit);
}
}
}
#[test]
fn descrambler_self_synchronizes_within_17_bits() {
for tx_seed in [0, 1, 0x1_FFFF, 0x12345] {
for rx_seed in [0, 0x1_FFFF, 0x0F0F0, 0x1BEEF] {
let mut tx = Scrambler::with_state(tx_seed);
let mut rx = Descrambler::with_state(rx_seed);
for (n, bit) in pseudo_random_bits(7, 1024).enumerate() {
let out = rx.descramble(tx.scramble(bit));
if n >= 17 {
assert_eq!(out, bit, "bit {n} must be correct after sync");
}
}
}
}
}
fn lfsr_reference(seed: u32, len: usize) -> impl Iterator<Item = Bit> {
let mut history = [0u8; 17];
for (d, slot) in history.iter_mut().enumerate() {
*slot = ((seed >> d) & 1) as u8;
}
core::iter::repeat_with(move || {
let out = history[11] ^ history[16];
history.rotate_right(1);
history[0] = out;
Bit::from(out != 0)
})
.take(len)
}
#[test]
fn all_zeros_input_is_pure_lfsr_sequence() {
for seed in [1, 0x00800, 0x1_FFFF, 0x13579] {
let mut tx = Scrambler::with_state(seed);
for (n, expected) in lfsr_reference(seed, 4096).enumerate() {
assert_eq!(tx.scramble(Bit::Zero), expected, "seed {seed:#x} bit {n}");
}
}
}
#[test]
fn all_zeros_state_and_input_stay_zero() {
let mut tx = Scrambler::new();
for _ in 0..256 {
assert_eq!(tx.scramble(Bit::Zero), Bit::Zero);
}
}
#[test]
fn lfsr_sequence_has_maximal_period() {
let period: u32 = (1 << 17) - 1;
let start = Scrambler::with_state(1);
let mut tx = start;
let mut steps = 0u32;
loop {
tx.scramble(Bit::Zero);
steps += 1;
if tx == start {
break;
}
assert!(steps <= period, "period must not exceed 2^17 - 1");
}
assert_eq!(steps, period);
}
#[test]
fn single_channel_error_corrupts_exactly_offsets_0_12_17() {
const TOTAL: usize = 256;
const FLIP_AT: usize = 100;
let data: [Bit; TOTAL] = bit_array(pseudo_random_bits(99, TOTAL));
let mut channel: [Bit; TOTAL] =
bit_array(Scrambler::new().scramble_iter(data.iter().copied()));
channel[FLIP_AT] = match channel[FLIP_AT] {
Bit::Zero => Bit::One,
Bit::One => Bit::Zero,
};
let mut wrong = [0usize; 4];
let mut count = 0usize;
for (n, (got, want)) in Descrambler::new()
.descramble_iter(channel.iter().copied())
.zip(data.iter().copied())
.enumerate()
{
if got != want {
assert!(count < wrong.len(), "more than {} wrong bits", wrong.len());
wrong[count] = n;
count += 1;
}
}
assert_eq!(&wrong[..count], &[FLIP_AT, FLIP_AT + 12, FLIP_AT + 17]);
}
#[test]
fn iterator_adapters_agree_with_push_api() {
const LEN: usize = 64;
let data: [Bit; LEN] = bit_array(pseudo_random_bits(5, LEN));
let mut tx = Scrambler::new();
let mut rx = Descrambler::new();
let mut compared = 0usize;
for (&bit, iter_out) in data.iter().zip(
Descrambler::new()
.descramble_iter(Scrambler::new().scramble_iter(data.iter().copied())),
) {
assert_eq!(rx.descramble(tx.scramble(bit)), iter_out);
assert_eq!(iter_out, bit);
compared += 1;
}
assert_eq!(compared, LEN, "the adapter pair must preserve length");
}
#[test]
fn defaults_match_new() {
assert_eq!(Scrambler::default(), Scrambler::new());
assert_eq!(Descrambler::default(), Descrambler::new());
}
#[test]
fn with_state_masks_to_17_bits() {
assert_eq!(
Scrambler::with_state(u32::MAX),
Scrambler::with_state(0x1_FFFF)
);
assert_eq!(
Descrambler::with_state(u32::MAX),
Descrambler::with_state(0x1_FFFF)
);
}
#[test]
fn size_hints_pass_through() {
let data = [Bit::One, Bit::Zero];
assert_eq!(
Scrambler::new()
.scramble_iter(data.iter().copied())
.size_hint(),
(2, Some(2))
);
assert_eq!(
Descrambler::new()
.descramble_iter(data.iter().copied())
.size_hint(),
(2, Some(2))
);
}
}