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::application::App;
|
||||||
|
use crate::loc;
|
||||||
use crate::time;
|
use crate::time;
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -18,8 +19,12 @@ impl App {
|
||||||
match result {
|
match result {
|
||||||
Ok(ParseResult::GGA(Some(gga))) => {
|
Ok(ParseResult::GGA(Some(gga))) => {
|
||||||
time::set(&gga.time);
|
time::set(&gga.time);
|
||||||
defmt::info!("Got GGA")
|
self.locator = loc::locator_from_coordinates(
|
||||||
} // Got GGA sentence
|
gga.latitude.as_f64(),
|
||||||
|
gga.longitude.as_f64(),
|
||||||
|
);
|
||||||
|
defmt::info!("Got GGA. New locator: {}", self.locator.as_str());
|
||||||
|
}
|
||||||
Ok(_) => {} // Some other sentences..
|
Ok(_) => {} // Some other sentences..
|
||||||
Err(_) => {} // Got parse error
|
Err(_) => {} // Got parse error
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub struct App {
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
gps_parser: Parser,
|
gps_parser: Parser,
|
||||||
|
locator: arrayvec::ArrayString<6>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
|
@ -66,5 +66,6 @@ pub fn setup(cp: cortex_m::peripheral::Peripherals, dp: stm32::Peripherals) -> A
|
||||||
board_led,
|
board_led,
|
||||||
serial,
|
serial,
|
||||||
gps_parser,
|
gps_parser,
|
||||||
|
locator: arrayvec::ArrayString::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use stm32f1xx_hal as _;
|
||||||
pub mod application;
|
pub mod application;
|
||||||
pub mod loc;
|
pub mod loc;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
pub mod wspr;
|
||||||
|
|
||||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
// 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
|
// 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 FIELD_SYMBOLS: &str = "ABCDEFGHIJKLMNOPQR";
|
||||||
const SUBSQUARE_SYMBOLS: &str = "abcdefghijklmnopqrstuvwx";
|
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 mut target_buf = ArrayString::<6>::new();
|
||||||
|
|
||||||
let false_east = if long < 180.0 {
|
let false_east = if long < 180.0 {
|
||||||
long + 180.0
|
long + 180.0
|
||||||
} else {
|
} else {
|
||||||
long - 180.0
|
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 long_field = (false_east / 20.0) as usize;
|
||||||
let mut long_rest = false_east % 20.0;
|
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"
|
name = "loc"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "wspr"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wspr-beacon = { path = ".." }
|
wspr-beacon = { path = ".." }
|
||||||
cortex-m = "0.7.1"
|
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