2021-04-25 17:33:35 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-25 20:54:46 +02:00
|
|
|
pub fn encode_message(
|
2021-04-25 17:33:35 +02:00
|
|
|
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;
|
|
|
|
}
|