Crudely ported my C wspr encoder
This commit is contained in:
parent
97c91a7e84
commit
d04f567294
|
@ -7,6 +7,7 @@ use stm32f1xx_hal::{
|
|||
};
|
||||
|
||||
use crate::application::App;
|
||||
use crate::loc;
|
||||
use crate::time;
|
||||
|
||||
impl App {
|
||||
|
@ -18,8 +19,12 @@ impl App {
|
|||
match result {
|
||||
Ok(ParseResult::GGA(Some(gga))) => {
|
||||
time::set(&gga.time);
|
||||
defmt::info!("Got GGA")
|
||||
} // Got GGA sentence
|
||||
self.locator = loc::locator_from_coordinates(
|
||||
gga.latitude.as_f64(),
|
||||
gga.longitude.as_f64(),
|
||||
);
|
||||
defmt::info!("Got GGA. New locator: {}", self.locator.as_str());
|
||||
}
|
||||
Ok(_) => {} // Some other sentences..
|
||||
Err(_) => {} // Got parse error
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ pub struct App {
|
|||
),
|
||||
>,
|
||||
gps_parser: Parser,
|
||||
locator: arrayvec::ArrayString<6>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
|
@ -66,5 +66,6 @@ pub fn setup(cp: cortex_m::peripheral::Peripherals, dp: stm32::Peripherals) -> A
|
|||
board_led,
|
||||
serial,
|
||||
gps_parser,
|
||||
locator: arrayvec::ArrayString::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use stm32f1xx_hal as _;
|
|||
pub mod application;
|
||||
pub mod loc;
|
||||
pub mod time;
|
||||
pub mod wspr;
|
||||
|
||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||
|
|
|
@ -3,16 +3,16 @@ use arrayvec::ArrayString;
|
|||
const FIELD_SYMBOLS: &str = "ABCDEFGHIJKLMNOPQR";
|
||||
const SUBSQUARE_SYMBOLS: &str = "abcdefghijklmnopqrstuvwx";
|
||||
|
||||
pub fn locator_from_coordinates(lat: f32, long: f32) -> ArrayString<6> {
|
||||
pub fn locator_from_coordinates(lat: f64, long: f64) -> ArrayString<6> {
|
||||
let mut target_buf = ArrayString::<6>::new();
|
||||
|
||||
let false_east = if long < 180.0 {
|
||||
long + 180.0
|
||||
} else {
|
||||
long - 180.0
|
||||
};
|
||||
} as f64;
|
||||
|
||||
let false_north = if lat < 90.0 { lat + 90.0 } else { lat - 90.0 };
|
||||
let false_north = if lat < 90.0 { lat + 90.0 } else { lat - 90.0 } as f32;
|
||||
|
||||
let long_field = (false_east / 20.0) as usize;
|
||||
let mut long_rest = false_east % 20.0;
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// 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 endcode_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;
|
||||
}
|
|
@ -10,6 +10,10 @@ version = "0.1.0"
|
|||
name = "loc"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "wspr"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
wspr-beacon = { path = ".." }
|
||||
cortex-m = "0.7.1"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use wspr_beacon as _; // memory layout + panic handler
|
||||
|
||||
// See https://crates.io/crates/defmt-test/0.1.0 for more documentation (e.g. about the 'state'
|
||||
// feature)
|
||||
#[defmt_test::tests]
|
||||
mod tests {
|
||||
use arrayvec::ArrayString;
|
||||
use defmt::{assert, assert_eq, Debug2Format};
|
||||
use wspr_beacon::wspr;
|
||||
|
||||
#[test]
|
||||
fn test_encoding() {
|
||||
let call_sign = ArrayString::<6>::from(" K1ABC").unwrap();
|
||||
let loc = ArrayString::<6>::from("FN42").unwrap();
|
||||
let symbols = wspr::endcode_message(&call_sign, &loc, 37);
|
||||
|
||||
let expected_symbols: [u8; 162] = [
|
||||
3, 3, 0, 0, 2, 0, 0, 0, 1, 0, 2, 0, 1, 3, 1, 2, 2, 2, 1, 0, 0, 3, 2, 3, 1, 3, 3, 2, 2,
|
||||
0, 2, 0, 0, 0, 3, 2, 0, 1, 2, 3, 2, 2, 0, 0, 2, 2, 3, 2, 1, 1, 0, 2, 3, 3, 2, 1, 0, 2,
|
||||
2, 1, 3, 2, 1, 2, 2, 2, 0, 3, 3, 0, 3, 0, 3, 0, 1, 2, 1, 0, 2, 1, 2, 0, 3, 2, 1, 3, 2,
|
||||
0, 0, 3, 3, 2, 3, 0, 3, 2, 2, 0, 3, 0, 2, 0, 2, 0, 1, 0, 2, 3, 0, 2, 1, 1, 1, 2, 3, 3,
|
||||
0, 2, 3, 1, 2, 1, 2, 2, 2, 1, 3, 3, 2, 0, 0, 0, 0, 1, 0, 3, 2, 0, 1, 3, 2, 2, 2, 2, 2,
|
||||
0, 2, 3, 3, 2, 3, 2, 3, 3, 2, 0, 0, 3, 1, 2, 2, 2,
|
||||
];
|
||||
|
||||
for i in 0..162 {
|
||||
assert_eq!(expected_symbols[i], symbols[i]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue