use super::lsis;
use super::params::L1CP_WEIL_INSERTION;
pub const I_PRIMARY_LEN: usize = 2046;
pub const Q_PRIMARY_LEN: usize = 10230;
pub const Q_SECONDARY_LEN: usize = 4;
pub const Q_TERTIARY_LEN: usize = 1500;
pub const Q_PRIMARY_LEGENDRE_LEN: usize = 10223;
pub const Q_TERTIARY_LEGENDRE_LEN: usize = 1499;
pub const MAX_PRN: u16 = 210;
pub const Q_EXPANSION: [u8; 7] = [0, 1, 1, 0, 1, 0, 0];
pub const Q_SECONDARY_CODES: [[u8; 4]; 4] =
[[1, 1, 1, 0], [0, 1, 1, 1], [1, 0, 1, 1], [1, 1, 0, 1]];
#[inline]
pub fn chip_level(bit: u8) -> f64 {
if bit & 1 == 1 {
-1.0
} else {
1.0
}
}
fn check_prn(prn: u16) -> Result<usize, String> {
if (1..=MAX_PRN).contains(&prn) {
Ok((prn - 1) as usize)
} else {
Err(format!("AFS PRN {prn} is outside 1..={MAX_PRN}"))
}
}
fn m_sequence_11(taps: &[usize]) -> Vec<u8> {
let mut reg = [1u8; 11];
let mut out = Vec::with_capacity(2047);
for _ in 0..2047 {
out.push(reg[10]);
let fb = taps.iter().fold(0u8, |acc, &s| acc ^ reg[s - 1]);
for i in (1..11).rev() {
reg[i] = reg[i - 1];
}
reg[0] = fb;
}
out
}
pub fn g1_sequence() -> Vec<u8> {
m_sequence_11(&[2, 11])
}
pub fn g2_sequence() -> Vec<u8> {
m_sequence_11(&[2, 5, 8, 11])
}
pub fn afs_i_from_delay(d: usize) -> Vec<u8> {
let g1 = g1_sequence();
let g2 = g2_sequence();
(0..I_PRIMARY_LEN)
.map(|t| g1[t] ^ g2[(t + 2047 - d % 2047) % 2047])
.collect()
}
pub fn g2_initial_state(d: usize) -> u16 {
let g2 = g2_sequence();
(0..11).fold(0u16, |acc, t| {
(acc << 1) | g2[(t + 2047 - d % 2047) % 2047] as u16
})
}
pub fn g2_delay_of(code: &[u8]) -> Option<usize> {
if code.len() != I_PRIMARY_LEN {
return None;
}
let g1 = g1_sequence();
let g2 = g2_sequence();
(0..2047).find(|&d| (0..I_PRIMARY_LEN).all(|t| code[t] ^ g1[t] == g2[(t + 2047 - d) % 2047]))
}
pub fn legendre_sequence(n: usize) -> Vec<u8> {
let mut l = vec![0u8; n];
for x in 1..n {
l[(x * x) % n] = 1;
}
l
}
fn weil(l: &[u8], k: usize) -> Vec<u8> {
let n = l.len();
(0..n).map(|t| l[t] ^ l[(t + k) % n]).collect()
}
pub fn afs_q_primary(prn: u16) -> Result<Vec<u8>, String> {
let (w, p) = L1CP_WEIL_INSERTION[check_prn(prn)?];
let w = weil(&legendre_sequence(Q_PRIMARY_LEGENDRE_LEN), w as usize);
let p = p as usize;
if !(1..=Q_PRIMARY_LEGENDRE_LEN).contains(&p) {
return Err(format!("insertion index {p} outside 1..=10223"));
}
let mut code = Vec::with_capacity(Q_PRIMARY_LEN);
code.extend_from_slice(&w[..p - 1]);
code.extend_from_slice(&Q_EXPANSION);
code.extend_from_slice(&w[p - 1..]);
debug_assert_eq!(code.len(), Q_PRIMARY_LEN);
Ok(code)
}
pub fn weil_tertiary(k: usize) -> Vec<u8> {
let mut code = weil(&legendre_sequence(Q_TERTIARY_LEGENDRE_LEN), k);
code.push(0);
code
}
#[derive(Clone, Debug)]
pub struct LsisCodes {
pub g2_delay: Vec<u16>,
pub tertiary: Vec<Vec<u8>>,
}
impl LsisCodes {
pub fn load() -> Result<Self, String> {
let gold = lsis::annex3_hex_lines(
&lsis::read_verified_text("006_GoldCode2046hex210prns.txt")?,
500,
);
let ter = lsis::annex3_hex_lines(
&lsis::read_verified_text("008_Weil1500hex210prns.txt")?,
370,
);
if gold.len() != 210 || ter.len() != 210 {
return Err(format!(
"Annex 3 files hold {} and {} codes, 210 expected",
gold.len(),
ter.len()
));
}
let mut g2_delay = Vec::with_capacity(210);
for (i, h) in gold.iter().enumerate() {
let code = lsis::hex_bits_last(h, I_PRIMARY_LEN);
let d = g2_delay_of(&code).ok_or_else(|| {
format!(
"Annex 3 AFS-I PRN {} is not a Gold code of g1, g2 (LSIS Figure C-1)",
i + 1
)
})?;
g2_delay.push(d as u16);
}
let tertiary = ter
.iter()
.map(|h| lsis::hex_bits_last(h, Q_TERTIARY_LEN))
.collect();
Ok(LsisCodes { g2_delay, tertiary })
}
pub fn afs_i_primary(&self, prn: u16) -> Result<Vec<u8>, String> {
Ok(afs_i_from_delay(self.g2_delay[check_prn(prn)?] as usize))
}
pub fn afs_q_tertiary(&self, prn: u16) -> Result<Vec<u8>, String> {
Ok(self.tertiary[check_prn(prn)?].clone())
}
}
pub fn afs_q_secondary(id: u8) -> Result<[u8; 4], String> {
Q_SECONDARY_CODES
.get(id as usize)
.copied()
.ok_or_else(|| format!("secondary code S{id} does not exist (S0 to S3)"))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct NodeCodes {
pub node_id: u16,
pub i_prn: u16,
pub q_prn: u16,
pub secondary: u8,
pub tertiary_prn: u16,
pub tertiary_phase: u16,
}
impl NodeCodes {
pub fn interim(node_id: u16) -> Result<Self, String> {
if !(1..=12).contains(&node_id) {
return Err(format!(
"LSIS V1.0 Table 11 assigns codes to node identifiers 1 to 12 only, not {node_id}"
));
}
Ok(NodeCodes {
node_id,
i_prn: node_id,
q_prn: node_id,
secondary: ((node_id - 1) % 4) as u8,
tertiary_prn: node_id,
tertiary_phase: 0,
})
}
}
#[derive(Clone, Debug)]
pub struct NodeChips {
pub codes: NodeCodes,
pub i_primary: Vec<u8>,
pub q_primary: Vec<u8>,
pub q_secondary: [u8; 4],
pub q_tertiary: Vec<u8>,
}
impl NodeChips {
pub fn new(codes: NodeCodes, lsis: &LsisCodes) -> Result<Self, String> {
Ok(NodeChips {
codes,
i_primary: lsis.afs_i_primary(codes.i_prn)?,
q_primary: afs_q_primary(codes.q_prn)?,
q_secondary: afs_q_secondary(codes.secondary)?,
q_tertiary: lsis.afs_q_tertiary(codes.tertiary_prn)?,
})
}
#[inline]
pub fn q_tiered_chip(&self, n: u64) -> u8 {
let p = (n % Q_PRIMARY_LEN as u64) as usize;
let epoch = n / Q_PRIMARY_LEN as u64;
let s = (epoch % Q_SECONDARY_LEN as u64) as usize;
let t = ((epoch / Q_SECONDARY_LEN as u64 + self.codes.tertiary_phase as u64)
% Q_TERTIARY_LEN as u64) as usize;
self.q_primary[p] ^ self.q_secondary[s] ^ self.q_tertiary[t]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn g1_and_g2_are_maximal_length_sequences() {
for s in [g1_sequence(), g2_sequence()] {
assert_eq!(s.iter().filter(|&&b| b == 1).count(), 1024);
}
}
#[test]
fn code_lengths_legendre_balance_and_delay_round_trip() {
let l = legendre_sequence(Q_PRIMARY_LEGENDRE_LEN);
assert_eq!(l.iter().filter(|&&b| b == 1).count(), 5111);
let l = legendre_sequence(Q_TERTIARY_LEGENDRE_LEN);
assert_eq!(l.iter().filter(|&&b| b == 1).count(), 749);
for prn in [1, 105, 210] {
assert_eq!(afs_q_primary(prn).unwrap().len(), Q_PRIMARY_LEN);
}
assert_eq!(weil_tertiary(1).len(), Q_TERTIARY_LEN);
assert!(afs_q_primary(0).is_err() && afs_q_primary(211).is_err());
for d in [0usize, 1, 170, 1845, 2046] {
assert_eq!(g2_delay_of(&afs_i_from_delay(d)), Some(d));
}
let mut c = afs_i_from_delay(170);
c[100] ^= 1;
assert_eq!(g2_delay_of(&c), None);
}
#[test]
fn tiered_chip_combines_the_three_tiers() {
let lsis = LsisCodes {
g2_delay: (0..210).map(|i| i as u16).collect(),
tertiary: (1..=210).map(weil_tertiary).collect(),
};
let c = NodeChips::new(NodeCodes::interim(3).unwrap(), &lsis).unwrap();
assert_eq!(c.codes.secondary, 2);
for n in [0u64, 10229, 10230, 40919, 40920, 61_379_999] {
let p = (n % 10230) as usize;
let s = ((n / 10230) % 4) as usize;
let t = ((n / 40920) % 1500) as usize;
assert_eq!(
c.q_tiered_chip(n),
c.q_primary[p] ^ c.q_secondary[s] ^ c.q_tertiary[t]
);
}
assert!(NodeCodes::interim(13).is_err());
}
}