// Implemented by following http://www.g4jnt.com/Coding/WSPR_Coding_Process.pdf const WSPR_LENGTH: usize = 162; const WSPR_POWER_LOC_BITS: usize = 22; const WSPR_SYNC: [u8; WSPR_LENGTH] = [ 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, ]; fn encode_call_char(call_sign: &arrayvec::ArrayString<6>, idx: usize) -> u32 { let c = call_sign.chars().nth(idx).unwrap().to_ascii_uppercase(); if c >= '0' && c <= '9' { return (c as u32) - ('0' as u32); } else if c >= 'A' && c <= 'Z' { return 10 + (c as u32) - ('A' as u32); } // Turn everything else to space else { return 36; } } fn encode_loc_char(locator: &arrayvec::ArrayString<6>, idx: usize) -> u32 { let c = locator.chars().nth(idx).unwrap().to_ascii_uppercase(); if c >= '0' && c <= '9' { return (c as u32) - ('0' as u32); } else if c >= 'A' && c <= 'R' { return (c as u32) - ('A' as u32); } // Everthing else becomes 9/I else { return 9; } } fn encode_power(power: u8) -> u32 { if power > 60 { return 60 as u32; } return power as u32; } fn message_to_bits( call_sign: &arrayvec::ArrayString<6>, locator: &arrayvec::ArrayString<6>, power: u8, ) -> u64 { let mut n = encode_call_char(call_sign, 0); n = n * 36 + encode_call_char(call_sign, 1); n = n * 10 + encode_call_char(call_sign, 2); n = n * 27 + encode_call_char(call_sign, 3) - 10; n = n * 27 + encode_call_char(call_sign, 4) - 10; n = n * 27 + encode_call_char(call_sign, 5) - 10; // lowest 28 bits of n contain the encoded callsign let mut m = 179 - 10 * encode_loc_char(locator, 0) - encode_loc_char(locator, 2); m = m * 180 + 10 * encode_loc_char(locator, 1) + encode_loc_char(locator, 3); m = m * 128 + encode_power(power) + 64; // lowest 22 bits of m contain the encoded locator (15bit) and the encoded power (7 bits) // Concactenate to 50bit wspr message return (n as u64) << WSPR_POWER_LOC_BITS | (m as u64); } const FEC_TAPS1: u32 = 0xF2D05351; const FEC_TAPS2: u32 = 0xE4613C47; fn parity32(mut x: u32) -> u8 { x ^= x >> 16; x ^= x >> 8; x ^= x >> 4; x ^= x >> 2; x ^= x >> 1; return (x & 1) as u8; } fn generate_fec(msg: u64) -> [u8; WSPR_LENGTH] { let mut output: [u8; WSPR_LENGTH] = [0; WSPR_LENGTH]; let mut reg: u32 = 0; for i in 0..81 { let bit = if i < 50 { ((msg >> (50 - i - 1)) & 1) as u32 } else { 0 }; reg = (reg << 1) | bit; let tabs1 = reg & FEC_TAPS1; let tabs2 = reg & FEC_TAPS2; output[i * 2] = parity32(tabs1); output[i * 2 + 1] = parity32(tabs2); } return output; } fn interleave_message(input: [u8; WSPR_LENGTH]) -> [u8; WSPR_LENGTH] { let mut output: [u8; WSPR_LENGTH] = [0; WSPR_LENGTH]; let mut p: u8 = 0; let mut i: u8 = 0; while p < WSPR_LENGTH as u8 { let rev_i = i.reverse_bits(); if rev_i < WSPR_LENGTH as u8 { output[rev_i as usize] = input[p as usize]; p += 1; } i += 1; } return output; } fn add_sync(symbols: &mut [u8; WSPR_LENGTH]) { for i in 0..WSPR_LENGTH { symbols[i] = WSPR_SYNC[i] | symbols[i] << 1; } } pub fn encode_message( call_sign: &arrayvec::ArrayString<6>, locator: &arrayvec::ArrayString<6>, power: u8, ) -> [u8; WSPR_LENGTH] { let bits = message_to_bits(call_sign, locator, power); let msg_fec = generate_fec(bits); let mut msg = interleave_message(msg_fec); add_sync(&mut msg); return msg; }