#[must_use]
pub fn manchester_encode_scalar(bits: &[u8], amplitude: f32) -> Vec<f32> {
let mut out = Vec::with_capacity(bits.len() * 2);
let mut polarity = true;
for &b in bits {
polarity = !polarity;
let hi = if polarity { amplitude } else { -amplitude };
let lo = -hi;
if b != 0 {
out.push(hi);
out.push(lo);
} else {
out.push(hi);
out.push(hi);
}
}
out
}
#[must_use]
pub fn manchester_decode_scalar(samples: &[f32], threshold: f32) -> Option<Vec<u8>> {
if samples.len() % 2 != 0 {
return None;
}
let mut bits = Vec::with_capacity(samples.len() / 2);
for chunk in samples.chunks_exact(2) {
let s0 = chunk[0];
let s1 = chunk[1];
if s0.abs() < threshold && s1.abs() < threshold {
bits.push(0u8);
continue;
}
let mid_transition = (s0 > 0.0) != (s1 > 0.0);
bits.push(if mid_transition { 1 } else { 0 });
}
Some(bits)
}
#[must_use]
#[allow(unsafe_code)]
pub fn manchester_encode_simd(bits: &[u8], amplitude: f32) -> Vec<f32> {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
return unsafe { manchester_encode_avx2(bits, amplitude) };
}
}
manchester_encode_scalar(bits, amplitude)
}
#[must_use]
#[allow(unsafe_code)]
pub fn manchester_decode_simd(samples: &[f32], threshold: f32) -> Option<Vec<u8>> {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
return unsafe { manchester_decode_avx2(samples, threshold) };
}
}
manchester_decode_scalar(samples, threshold)
}
#[cfg(target_arch = "x86_64")]
#[allow(unsafe_code)]
#[target_feature(enable = "avx2")]
unsafe fn manchester_encode_avx2(bits: &[u8], amplitude: f32) -> Vec<f32> {
use std::arch::x86_64::*;
let len = bits.len();
let mut out = Vec::with_capacity(len * 2);
let mut polarity = true;
let chunks = len / 32;
let tail_start = chunks * 32;
for chunk_idx in 0..chunks {
let chunk = &bits[chunk_idx * 32..(chunk_idx + 1) * 32];
let b_vec = _mm256_loadu_si256(chunk.as_ptr() as *const __m256i);
let zeros = _mm256_setzero_si256();
let is_zero_mask = _mm256_cmpeq_epi8(b_vec, zeros);
let zero_bits = _mm256_movemask_epi8(is_zero_mask) as u32;
for i in 0..32usize {
polarity = !polarity; let hi = if polarity { amplitude } else { -amplitude };
let bit_is_zero = (zero_bits >> i) & 1 == 1;
if bit_is_zero {
out.push(hi);
out.push(hi);
} else {
out.push(hi);
out.push(-hi);
}
}
}
for &b in &bits[tail_start..] {
polarity = !polarity;
let hi = if polarity { amplitude } else { -amplitude };
if b == 0 {
out.push(hi);
out.push(hi);
} else {
out.push(hi);
out.push(-hi);
}
}
out
}
#[cfg(target_arch = "x86_64")]
#[allow(unsafe_code)]
#[target_feature(enable = "avx2")]
unsafe fn manchester_decode_avx2(samples: &[f32], threshold: f32) -> Option<Vec<u8>> {
if samples.len() % 2 != 0 {
return None;
}
use std::arch::x86_64::*;
let pairs = samples.len() / 2;
let mut bits = Vec::with_capacity(pairs);
let simd_pairs = pairs / 8;
let tail_start_sample = simd_pairs * 16;
let thr_vec = _mm256_set1_ps(threshold);
let zero_vec = _mm256_setzero_ps();
for chunk_idx in 0..simd_pairs {
let base = chunk_idx * 16;
let s_lo = _mm256_loadu_ps(samples[base..].as_ptr());
let s_hi = _mm256_loadu_ps(samples[base + 8..].as_ptr());
let s_lo_arr: [f32; 8] = std::mem::transmute(s_lo);
let s_hi_arr: [f32; 8] = std::mem::transmute(s_hi);
let abs_s_lo = _mm256_andnot_ps(_mm256_set1_ps(-0.0_f32), s_lo);
let abs_s_hi = _mm256_andnot_ps(_mm256_set1_ps(-0.0_f32), s_hi);
let active_lo = _mm256_cmp_ps(abs_s_lo, thr_vec, _CMP_GE_OQ); let active_hi = _mm256_cmp_ps(abs_s_hi, thr_vec, _CMP_GE_OQ);
let _ = (active_lo, active_hi);
let sign_lo = _mm256_cmp_ps(s_lo, zero_vec, _CMP_LT_OQ); let sign_hi = _mm256_cmp_ps(s_hi, zero_vec, _CMP_LT_OQ);
let _ = (sign_lo, sign_hi);
for arr in [&s_lo_arr, &s_hi_arr] {
for i in 0..4usize {
let s0 = arr[i * 2];
let s1 = arr[i * 2 + 1];
if s0.abs() < threshold && s1.abs() < threshold {
bits.push(0u8);
} else {
bits.push(if (s0 > 0.0) != (s1 > 0.0) { 1 } else { 0 });
}
}
}
}
for chunk in samples[tail_start_sample..].chunks_exact(2) {
let s0 = chunk[0];
let s1 = chunk[1];
if s0.abs() < threshold && s1.abs() < threshold {
bits.push(0u8);
} else {
bits.push(if (s0 > 0.0) != (s1 > 0.0) { 1 } else { 0 });
}
}
Some(bits)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scalar_roundtrip_all_zeros() {
let bits: Vec<u8> = vec![0u8; 80];
let samples = manchester_encode_scalar(&bits, 1.0);
let decoded = manchester_decode_scalar(&samples, 0.1).expect("decode ok");
assert_eq!(decoded, bits);
}
#[test]
fn test_scalar_roundtrip_all_ones() {
let bits: Vec<u8> = vec![1u8; 80];
let samples = manchester_encode_scalar(&bits, 1.0);
let decoded = manchester_decode_scalar(&samples, 0.1).expect("decode ok");
assert_eq!(decoded, bits);
}
#[test]
fn test_scalar_roundtrip_alternating() {
let bits: Vec<u8> = (0..80).map(|i| (i % 2) as u8).collect();
let samples = manchester_encode_scalar(&bits, 0.5);
let decoded = manchester_decode_scalar(&samples, 0.1).expect("decode ok");
assert_eq!(decoded, bits);
}
#[test]
fn test_scalar_sample_count() {
let bits = vec![0u8, 1, 0, 1, 1, 0];
let samples = manchester_encode_scalar(&bits, 1.0);
assert_eq!(samples.len(), bits.len() * 2);
}
#[test]
fn test_decode_odd_length_returns_none() {
let samples = vec![0.5f32; 3]; assert!(manchester_decode_scalar(&samples, 0.1).is_none());
}
#[test]
fn test_simd_matches_scalar_encode() {
let bits: Vec<u8> = (0..80).map(|i| ((i / 3) % 2) as u8).collect();
let scalar_out = manchester_encode_scalar(&bits, 0.7);
let simd_out = manchester_encode_simd(&bits, 0.7);
assert_eq!(scalar_out.len(), simd_out.len());
for (a, b) in scalar_out.iter().zip(simd_out.iter()) {
assert!(
(a - b).abs() < 1e-6,
"SIMD and scalar outputs differ: {a} vs {b}"
);
}
}
#[test]
fn test_simd_matches_scalar_decode() {
let bits: Vec<u8> = (0..80).map(|i| (i % 3 == 0) as u8).collect();
let samples = manchester_encode_scalar(&bits, 0.9);
let scalar_dec = manchester_decode_scalar(&samples, 0.1).expect("decode ok");
let simd_dec = manchester_decode_simd(&samples, 0.1).expect("decode ok");
assert_eq!(scalar_dec, simd_dec);
assert_eq!(scalar_dec, bits);
}
#[test]
fn test_simd_roundtrip() {
let patterns: &[&[u8]] = &[
&[0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0],
&[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
];
for &pattern in patterns {
let samples = manchester_encode_simd(pattern, 1.0);
let decoded = manchester_decode_simd(&samples, 0.1).expect("decode ok");
assert_eq!(
decoded, pattern,
"Round-trip failed for pattern {pattern:?}"
);
}
}
#[test]
fn test_scalar_fallback_non_simd() {
let bits: Vec<u8> = vec![1, 0, 1, 0, 1, 1, 0, 0];
let enc = manchester_encode_scalar(&bits, 1.0);
let dec = manchester_decode_scalar(&enc, 0.05).expect("decode ok");
assert_eq!(dec, bits);
}
}