use std::f64::consts::LOG10_2;
#[derive(Debug, Clone)]
pub struct OpticalOrthogonalCode {
pub n: usize,
pub w: usize,
pub lambda_a: usize,
pub lambda_c: usize,
}
impl OpticalOrthogonalCode {
pub fn new(n: usize, w: usize, lambda_a: usize, lambda_c: usize) -> Self {
Self {
n,
w,
lambda_a,
lambda_c,
}
}
pub fn max_codewords(&self) -> usize {
if self.w <= 1 {
return self.n;
}
(self.n - 1) / (self.w * (self.w - 1))
}
pub fn crosscorrelation(&self, c1: &[u8], c2: &[u8], tau: usize) -> usize {
let n = c1.len().min(c2.len());
(0..n)
.map(|i| (c1[i] as usize) * (c2[(i + tau) % n] as usize))
.sum()
}
pub fn autocorrelation(&self, code: &[u8], tau: usize) -> usize {
self.crosscorrelation(code, code, tau)
}
pub fn verify_code(&self, code1: &[u8], code2: &[u8]) -> bool {
let n = self.n;
let w1: usize = code1.iter().map(|&b| b as usize).sum();
let w2: usize = code2.iter().map(|&b| b as usize).sum();
if w1 != self.w || w2 != self.w {
return false;
}
for tau in 1..n {
if self.autocorrelation(code1, tau) > self.lambda_a {
return false;
}
}
for tau in 1..n {
if self.autocorrelation(code2, tau) > self.lambda_a {
return false;
}
}
for tau in 0..n {
if self.crosscorrelation(code1, code2, tau) > self.lambda_c {
return false;
}
}
true
}
pub fn generate_codes(&self) -> Vec<Vec<u8>> {
let max = self.max_codewords();
let mut accepted: Vec<Vec<u8>> = Vec::new();
let mut positions = vec![0usize; self.w];
for (i, p) in positions.iter_mut().enumerate() {
*p = i;
}
'outer: loop {
let mut cand = vec![0u8; self.n];
for &p in &positions {
cand[p] = 1;
}
let mut ac_ok = true;
for tau in 1..self.n {
if self.autocorrelation(&cand, tau) > self.lambda_a {
ac_ok = false;
break;
}
}
if ac_ok {
let mut cc_ok = true;
'cc: for prev in &accepted {
for tau in 0..self.n {
if self.crosscorrelation(&cand, prev, tau) > self.lambda_c {
cc_ok = false;
break 'cc;
}
}
}
if cc_ok {
accepted.push(cand);
if accepted.len() >= max {
break;
}
}
}
let w = self.w;
let n = self.n;
let mut i = w;
loop {
if i == 0 {
break 'outer; }
i -= 1;
positions[i] += 1;
if positions[i] <= n - (w - i) {
for j in (i + 1)..w {
positions[j] = positions[j - 1] + 1;
}
break;
}
}
}
accepted
}
}
#[derive(Debug, Clone)]
pub struct OvsfTree {
pub max_sf: usize,
}
impl OvsfTree {
pub fn new(max_sf: usize) -> Self {
debug_assert!(max_sf.is_power_of_two(), "max_sf must be a power of two");
Self { max_sf }
}
pub fn code(&self, sf: usize, idx: usize) -> Vec<i8> {
if sf == 1 {
return vec![1i8];
}
let parent_sf = sf / 2;
let parent_idx = idx / 2;
let parent = self.code(parent_sf, parent_idx);
let sign: i8 = if idx % 2 == 0 { 1 } else { -1 };
let mut out = Vec::with_capacity(sf);
out.extend_from_slice(&parent);
out.extend(parent.iter().map(|&v| sign * v));
out
}
pub fn are_orthogonal(&self, sf1: usize, idx1: usize, sf2: usize, idx2: usize) -> bool {
let c1 = self.code(sf1, idx1);
let c2 = self.code(sf2, idx2);
let max_len = c1.len().max(c2.len());
let dot: i64 = (0..max_len)
.map(|i| (c1[i % c1.len()] as i64) * (c2[i % c2.len()] as i64))
.sum();
dot == 0
}
pub fn capacity_at_sf(&self, sf: usize) -> usize {
sf
}
}
#[derive(Debug, Clone)]
pub struct GoldCode {
pub length: usize,
pub n_bits: usize,
}
impl GoldCode {
pub fn new(n_bits: usize) -> Self {
Self {
length: (1usize << n_bits).saturating_sub(1),
n_bits,
}
}
pub fn m_sequence(&self, seed: u32, taps: &[usize]) -> Vec<i8> {
let n = self.n_bits;
let len = self.length;
let mask = ((1u32 << n) - 1).max(1);
let mut state = (seed & mask).max(1) & mask;
let mut out = Vec::with_capacity(len);
for _ in 0..len {
let bit = (state & 1) as i8;
out.push(if bit == 0 { 1i8 } else { -1i8 });
let feedback: u32 = taps
.iter()
.map(|&t| (state >> (t - 1)) & 1)
.fold(0u32, |acc, b| acc ^ b);
state = ((state >> 1) | (feedback << (n - 1))) & mask;
}
out
}
fn default_taps(&self) -> (Vec<usize>, Vec<usize>) {
match self.n_bits {
3 => (vec![3, 1], vec![3, 2]),
4 => (vec![4, 1], vec![4, 3]),
5 => (vec![5, 2], vec![5, 4, 2, 1]),
6 => (vec![6, 1], vec![6, 5, 2, 1]),
7 => (vec![7, 3], vec![7, 3, 2, 1]),
8 => (vec![8, 4, 3, 2], vec![8, 6, 5, 1]),
9 => (vec![9, 4], vec![9, 6, 4, 3]),
10 => (vec![10, 3], vec![10, 8, 3, 2]),
_ => (vec![self.n_bits, 1], vec![self.n_bits, 2]),
}
}
pub fn gold_code(&self, seed1: u32, seed2: u32) -> Vec<i8> {
let (taps1, taps2) = self.default_taps();
let seq1 = self.m_sequence(seed1, &taps1);
let seq2 = self.m_sequence(seed2, &taps2);
seq1.iter().zip(seq2.iter()).map(|(&a, &b)| a * b).collect()
}
pub fn processing_gain_db(&self, bits_per_symbol: usize) -> f64 {
if bits_per_symbol == 0 {
return 0.0;
}
10.0 * (self.length as f64 / bits_per_symbol as f64).log10()
}
pub fn cross_correlation_bound(&self) -> i64 {
let exp = (self.n_bits + 2).div_ceil(2); (1i64 << exp) + 1
}
}
#[allow(dead_code)]
const LOG10_2_CACHED: f64 = LOG10_2;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ooc_max_codewords() {
let ooc = OpticalOrthogonalCode {
n: 13,
w: 3,
lambda_a: 1,
lambda_c: 1,
};
assert_eq!(ooc.max_codewords(), 2);
}
#[test]
fn ooc_autocorrelation_peak() {
let ooc = OpticalOrthogonalCode {
n: 7,
w: 3,
lambda_a: 1,
lambda_c: 1,
};
let code = vec![0, 0, 0, 1, 0, 1, 1];
assert_eq!(ooc.autocorrelation(&code, 0), 3); }
#[test]
fn ooc_generate_codes_valid() {
let ooc = OpticalOrthogonalCode {
n: 13,
w: 3,
lambda_a: 1,
lambda_c: 1,
};
let codes = ooc.generate_codes();
assert!(!codes.is_empty());
for c in &codes {
assert_eq!(c.iter().map(|&b| b as usize).sum::<usize>(), 3);
}
if codes.len() >= 2 {
assert!(ooc.verify_code(&codes[0], &codes[1]));
}
}
#[test]
fn ovsf_orthogonal_same_sf() {
let tree = OvsfTree::new(8);
assert!(tree.are_orthogonal(4, 0, 4, 1));
assert!(tree.are_orthogonal(4, 0, 4, 2));
assert!(tree.are_orthogonal(4, 1, 4, 3));
}
#[test]
fn ovsf_parent_child_not_orthogonal() {
let tree = OvsfTree::new(8);
assert!(!tree.are_orthogonal(4, 0, 2, 0));
assert!(!tree.are_orthogonal(2, 0, 4, 0));
}
#[test]
fn ovsf_code_length() {
let tree = OvsfTree::new(16);
assert_eq!(tree.code(8, 3).len(), 8);
assert_eq!(tree.code(1, 0), vec![1i8]);
}
#[test]
fn gold_processing_gain() {
let gc = GoldCode::new(7);
let pg = gc.processing_gain_db(1);
assert!((pg - 21.0).abs() < 1.0, "PG = {} dB (expected ≈21 dB)", pg);
}
#[test]
fn gold_m_sequence_length() {
let gc = GoldCode::new(5);
let seq = gc.m_sequence(1, &[5, 2]);
assert_eq!(seq.len(), 31); assert!(seq.iter().all(|&x| x == 1 || x == -1));
}
}