gnss-dsp 0.1.0

Digital Signal Processing algorithms for GNSS
use anyhow::Result;

// G2 delay table from pages 6--8 and pages 57--61 of IS-GPS-200H
// This table is indexed by PRN-1
static G2_DELAYS: [u16; 210] = [
    5, 6, 7, 8, 17, 18, 139, 140, 141, 251, 252, 254, 255, 256, 257, 258, 469, 470, 471, 472, 473,
    474, 509, 512, 513, 514, 515, 516, 859, 860, 861, 862, 863, 950, 947, 948, 950, 67, 103, 91,
    19, 679, 225, 625, 946, 638, 161, 1001, 554, 280, 710, 709, 775, 864, 558, 220, 397, 55, 898,
    759, 367, 299, 1018, 729, 695, 780, 801, 788, 732, 34, 320, 327, 389, 407, 525, 405, 221, 761,
    260, 326, 955, 653, 699, 422, 188, 438, 959, 539, 879, 677, 586, 153, 792, 814, 446, 264, 1015,
    278, 536, 819, 156, 957, 159, 712, 885, 461, 248, 713, 126, 807, 279, 122, 197, 693, 632, 771,
    467, 647, 203, 145, 175, 52, 21, 237, 235, 886, 657, 634, 762, 355, 1012, 176, 603, 130, 359,
    595, 68, 386, 797, 456, 499, 883, 307, 127, 211, 121, 118, 163, 628, 853, 484, 289, 811, 202,
    1021, 463, 568, 904, 670, 230, 911, 684, 309, 644, 932, 12, 314, 891, 212, 185, 675, 503, 150,
    395, 345, 846, 798, 992, 357, 995, 877, 112, 144, 476, 193, 109, 445, 291, 87, 399, 292, 901,
    339, 208, 711, 189, 263, 537, 663, 942, 173, 900, 30, 500, 935, 556, 373, 85, 652, 310,
];

pub const CA_CODE_LEN: usize = 1023;

fn lfsr(feedback: impl Fn(u16) -> u16) -> impl Iterator<Item = u8> {
    let mut reg: u16 = 0x3ff;
    std::iter::repeat_with(move || {
        let x = (reg & 1).try_into().unwrap();
        reg = (reg >> 1) | ((feedback(reg) & 1) << 9);
        x
    })
}

fn g1() -> impl Iterator<Item = u8> {
    lfsr(|reg| reg ^ (reg >> 7))
}

fn g2() -> impl Iterator<Item = u8> {
    lfsr(|reg| reg ^ (reg >> 1) ^ (reg >> 2) ^ (reg >> 4) ^ (reg >> 7) ^ (reg >> 8))
}

/// Returns the GPS L1 C/A code corresponding to a given PRN.
///
/// The C/A code is returned as a 1023-bit array using unpacked bits. This
/// function returns an error if `prn` does not correspond to a PRN for which a
/// C/A code is defined.
pub fn ca_code(prn: usize) -> Result<[u8; CA_CODE_LEN]> {
    anyhow::ensure!(
        (1..=G2_DELAYS.len()).contains(&prn),
        "PRN {prn} does not have a corresponding GPS C/A code"
    );
    let g2_delay = usize::from(G2_DELAYS[prn - 1]);
    let g1_code = g1();
    let g2_code = g2();
    let mut code = g1_code
        .zip(g2_code.skip(CA_CODE_LEN - g2_delay))
        .map(|(a, b)| a ^ b);
    Ok(std::array::from_fn(|_| code.next().unwrap()))
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn ca_code_prn1() {
        let prn = 1;
        let code = ca_code(prn).unwrap();
        let expected = [
            1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0,
            1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0,
            0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,
            1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0,
            0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1,
            1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
            1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1,
            0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1,
            0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
            0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1,
            1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
            1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1,
            1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
            1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
            0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
            1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1,
            1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0,
            1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0,
            0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1,
            0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0,
            0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1,
            1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0,
            0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
            0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
            1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1,
            0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0,
            1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0,
            0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,
            0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
            0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,
            0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0,
            1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
            1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1,
            0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1,
            0, 0, 0, 1, 0, 0, 0, 0,
        ];
        assert_eq!(code, expected);
    }

    #[test]
    fn ca_code_zero() {
        assert!(ca_code(0).is_err());
    }

    #[test]
    fn ca_code_large() {
        assert!(ca_code(211).is_err());
    }
}