Compare commits

...

2 Commits

Author SHA1 Message Date
Sebastian 36da28fc02 Complete rewrite using RTIC
Stripped out tests for now
2022-11-20 20:42:50 +01:00
Sebastian db95b2fceb Started migrating to rtic 2022-05-08 22:59:40 +02:00
11 changed files with 328 additions and 575 deletions

View File

@ -1,6 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output)
runner = "probe-run --chip STM32F103CB"
runner = "probe-run --chip STM32F103C8"
rustflags = [
"-C", "linker=flip-link",
"-C", "link-arg=-Tlink.x",

View File

@ -1,24 +1,22 @@
[package]
# TODO(1) fix `authors` and `name` if you didn't use `cargo-generate`
authors = ["LongHairedHacker <sebastian@sebastians-site.de>"]
name = "wspr-beacon"
edition = "2018"
version = "0.1.0"
[workspace]
members = ["testsuite"]
[dependencies]
cortex-m = "~0.7.1"
cortex-m-rt = "~0.6.13"
defmt = "~0.2.0"
defmt-rtt = "~0.2.0"
panic-probe = { version = "~0.2.0", features = ["print-defmt"] }
stm32f1xx-hal = { version = "~0.6.1", features = ["stm32f103", "rt"] }
embedded-hal = {version = "~0.2.3"}
nb = "~1.0.0"
nmea0183 = "~0.2.3"
arrayvec = {version = "~0.7.0", default-features = false}
cortex-m = "0.7.6"
cortex-m-rt = "0.7.2"
cortex-m-rtic = "1.1.3"
defmt = "0.3.2"
defmt-rtt = "0.3.2"
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
stm32f1xx-hal = { version = "0.9.0", features = ["stm32f103", "rt"] }
embedded-hal = {version = "0.2.3"}
nb = "1.0.0"
nmea0183 = "0.3.0"
arrayvec = {version = "0.7.0", default-features = false}
systick-monotonic = "1.0.0"
[features]
@ -42,8 +40,8 @@ codegen-units = 1
debug = 2
debug-assertions = true # <-
incremental = false
opt-level = 3 # <-
overflow-checks = true # <-
opt-level = 'z' # <-
overflow-checks = true # <-
# cargo test
[profile.test]
@ -51,8 +49,8 @@ codegen-units = 1
debug = 2
debug-assertions = true # <-
incremental = false
opt-level = 3 # <-
overflow-checks = true # <-
opt-level = 3 # <-
overflow-checks = true # <-
# cargo build/run --release
[profile.release]
@ -60,9 +58,9 @@ codegen-units = 1
debug = 2
debug-assertions = false # <-
incremental = false
#lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-
lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-
# cargo test --release
[profile.bench]
@ -71,8 +69,8 @@ debug = 2
debug-assertions = false # <-
incremental = false
lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-
opt-level = 3 # <-
overflow-checks = false # <-
# uncomment this to switch from the crates.io version of defmt to its git version
# check app-template's README for instructions

View File

@ -1,39 +0,0 @@
use nmea0183::{ParseResult, Parser};
use stm32f1xx_hal::{
delay::Delay,
prelude::*,
serial::{Config, Serial},
stm32,
};
use crate::application::App;
use crate::loc;
use crate::time;
impl App {
pub fn poll_gps(&mut self) -> () {
match self.serial.read() {
Ok(byte) => {
self.board_led.toggle().unwrap();
if let Some(result) = self.gps_parser.parse_from_byte(byte) {
match result {
Ok(ParseResult::GGA(Some(gga))) => {
if !self.transmitting {
time::set(&gga.time);
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
}
}
}
Err(nb::Error::WouldBlock) => {}
Err(nb::Error::Other(_)) => {}
}
}
}

View File

@ -1,144 +0,0 @@
use cortex_m::{asm::wfi, prelude::*};
use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
use nb::{self, block};
use nmea0183::{ParseResult, Parser};
use stm32f1xx_hal::{
delay::Delay,
gpio::{gpiob, gpioc, Alternate, Floating, Input, OpenDrain, Output, PushPull},
i2c::BlockingI2c,
pac::I2C1,
prelude::*,
serial::Serial,
stm32,
};
mod gps;
mod setup;
//use crate::exit;
pub use setup::setup;
use crate::si5153;
use crate::time;
use crate::wspr;
pub struct App {
delay: Delay,
board_led: gpioc::PC13<Output<PushPull>>,
serial: Serial<
stm32::USART3,
(
gpiob::PB10<Alternate<PushPull>>,
gpiob::PB11<Input<Floating>>,
),
>,
i2c: BlockingI2c<
I2C1,
(
gpiob::PB6<Alternate<OpenDrain>>,
gpiob::PB7<Alternate<OpenDrain>>,
),
>,
gps_parser: Parser,
locator: arrayvec::ArrayString<6>,
transmitting: bool,
}
// For PLL with 800MHz
const WSPR_SYMBOLS: [si5153::PllParams; 4] = [
// 7040100.000000: 113 + 44687 / 70401
// actual: 7040100.000000
si5153::PllParams {
p1: 14033,
p2: 17455,
p3: 70401,
},
// 7040101.464844: 113 + 447801 / 705503
// actual: 7040101.464844
si5153::PllParams {
p1: 14033,
p2: 172785,
p3: 705503,
},
// 7040102.929688: 113 + 38515 / 60682
// actual: 7040102.929688
si5153::PllParams {
p1: 14033,
p2: 14678,
p3: 60682,
},
// 7040104.394531: 113 + 123233 / 194166
// actual: 7040104.394531
si5153::PllParams {
p1: 14033,
p2: 46378,
p3: 194166,
},
];
impl App {
pub fn run(mut self) -> ! {
let callsign = arrayvec::ArrayString::<6>::from("DL1SSK").unwrap();
defmt::info!("Application Startup!");
let mut si_pll = si5153::Si5153::new(&self.i2c);
si_pll.init(&mut self.i2c, 25000000, 800000000 - 55_000, 800000000);
si_pll.set_ms_source(&mut self.i2c, si5153::Multisynth::MS0, si5153::PLL::A);
defmt::info!("PLL setup complete.");
let mut symbol_start = time::get();
let mut symbol_idx: usize = 0;
let mut message: [u8; 162] = [0; 162];
loop {
self.poll_gps();
if !self.transmitting {
let ts = time::get();
if self.locator.len() > 0
&& ts.minutes % 2 == 0
&& ts.seconds > 1.0
&& ts.seconds < 2.0
{
defmt::info!("Starting tranmission");
self.transmitting = true;
message = wspr::encode_message(&callsign, &self.locator, 27);
symbol_start = ts;
symbol_idx = 0;
si_pll.write_synth_params(
&mut self.i2c,
si5153::Multisynth::MS0,
&WSPR_SYMBOLS[message[symbol_idx] as usize],
);
si_pll.enable_ms_output(&mut self.i2c, si5153::Multisynth::MS0);
}
} else {
let ts = time::get();
let delta_sec = (ts.seconds - symbol_start.seconds)
+ (ts.minutes - symbol_start.minutes) as f32 * 60.0;
if delta_sec >= 0.683 {
symbol_start = ts;
symbol_idx += 1;
if symbol_idx < 162 {
si_pll.write_synth_params(
&mut self.i2c,
si5153::Multisynth::MS0,
&WSPR_SYMBOLS[message[symbol_idx] as usize],
);
defmt::info!("Tranmitting Symbol {}", symbol_idx);
} else {
si_pll.disable_ms_output(&mut self.i2c, si5153::Multisynth::MS0);
self.transmitting = false;
defmt::info!("Transmission ended");
}
}
}
//wfi();
}
//exit();
}
}

View File

@ -1,91 +0,0 @@
use stm32f1xx_hal::{
delay::Delay,
i2c,
pac::Interrupt,
prelude::*,
serial::{Config, Serial},
stm32,
timer::{Event, Timer},
};
use nmea0183::Parser;
use crate::application::App;
use crate::time;
pub fn setup(cp: cortex_m::peripheral::Peripherals, dp: stm32::Peripherals) -> App {
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
// HAL structs
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
// `clocks`
let clocks = rcc
.cfgr
.use_hse(8.mhz())
.sysclk(72.mhz())
.pclk1(36.mhz())
.freeze(&mut flash.acr);
defmt::info!("Clock Setup done");
// Acquire the GPIOC peripheral
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
let board_led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
let delay = Delay::new(cp.SYST, clocks);
// USART3
// Configure pb10 as a push_pull output, this will be the tx pin
let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
// Take ownership over pb11
let rx = gpiob.pb11;
// Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of
// the registers are used to enable and configure the device.
let serial = Serial::usart3(
dp.USART3,
(tx, rx),
&mut afio.mapr,
Config::default().baudrate(9600.bps()),
clocks,
&mut rcc.apb1,
);
let gps_parser = Parser::new();
let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
let mut i2c = i2c::BlockingI2c::i2c1(
dp.I2C1,
(scl, sda),
&mut afio.mapr,
i2c::Mode::Standard {
frequency: 400_000.hz(),
},
clocks,
&mut rcc.apb1,
5,
1,
5,
5,
);
time::setup(dp.TIM2, &clocks, &mut rcc.apb1);
App {
delay,
board_led,
serial,
i2c,
gps_parser,
locator: arrayvec::ArrayString::new(),
transmitting: false,
}
}

View File

@ -1,30 +0,0 @@
#![no_std]
use defmt_rtt as _; // global logger
use panic_probe as _;
use stm32f1xx_hal as _;
pub mod application;
pub mod loc;
mod si5153;
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
#[defmt::panic_handler]
fn panic() -> ! {
cortex_m::asm::udf()
}
defmt::timestamp!("{=u32}", {
// NOTE(no-CAS) `timestamps` runs with interrupts disabled
time::get_timestamp()
});
/// Terminates the application and makes `probe-run` exit with exit-code = 0
pub fn exit() -> ! {
loop {
cortex_m::asm::bkpt();
}
}

View File

@ -1,17 +1,310 @@
#![no_std]
#![no_main]
#![no_std]
use defmt_rtt as _; // global logger
use stm32f1xx_hal::pac;
use panic_probe as _;
use stm32f1xx_hal as _;
use wspr_beacon::application;
#[cortex_m_rt::entry]
fn main() -> ! {
// Get access to the core peripherals from the cortex-m crate
let cp = cortex_m::Peripherals::take().unwrap();
// Get access to the device specific peripherals from the peripheral access crate
let dp = pac::Peripherals::take().unwrap();
let app = application::setup(cp, dp);
app.run()
// 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
#[defmt::panic_handler]
fn panic() -> ! {
cortex_m::asm::udf()
}
use rtic::app;
mod loc;
mod si5153;
mod wspr;
#[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [TIM3])]
mod app {
use stm32f1xx_hal::{
gpio::{gpiob, gpioc, Alternate, Floating, Input, OpenDrain, Output, PushPull},
i2c,
i2c::BlockingI2c,
pac::I2C1,
prelude::*,
serial::{Config, Serial},
stm32,
timer::{self, Event},
};
use systick_monotonic::Systick;
use arrayvec::ArrayString;
use nmea0183::{datetime::Time, ParseResult, Parser};
use crate::loc;
use crate::si5153;
use crate::wspr;
type AppI2C1 = BlockingI2c<
I2C1,
(
gpiob::PB6<Alternate<OpenDrain>>,
gpiob::PB7<Alternate<OpenDrain>>,
),
>;
#[monotonic(binds = SysTick, default = true)]
type MonoTimer = Systick<1000>;
#[shared]
struct Shared {
time: Time,
locator: ArrayString<6>,
}
#[local]
struct Local {
gps_parser: Parser,
usart: Serial<
stm32::USART3,
(
gpiob::PB10<Alternate<PushPull>>,
gpiob::PB11<Input<Floating>>,
),
>,
pll: si5153::Si5153<AppI2C1>,
i2c: AppI2C1,
board_led: gpioc::PC13<Output<PushPull>>,
transmitting: bool,
symbol_pos: usize,
message: [u8; 162],
}
#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let mut flash = cx.device.FLASH.constrain();
let rcc = cx.device.RCC.constrain();
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
// `clocks`
let clocks = rcc
.cfgr
.use_hse(8.MHz())
.sysclk(72.MHz())
.pclk1(36.MHz())
.freeze(&mut flash.acr);
defmt::info!("Clock Setup done");
let mono = Systick::new(cx.core.SYST, clocks.pclk1().to_Hz());
let mut afio = cx.device.AFIO.constrain();
let mut timer = timer::Timer2::new(cx.device.TIM2, &clocks).counter_hz();
timer.start(1.kHz()).unwrap();
// Generate an interrupt when the timer expires
timer.listen(Event::Update);
// Acquire the GPIOC peripheral
let mut gpiob = cx.device.GPIOB.split();
let mut gpioc = cx.device.GPIOC.split();
let board_led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
// USART3
// Configure pb10 as a push_pull output, this will be the tx pin
let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
// Take ownership over pb11
let rx = gpiob.pb11;
// Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of
// the registers are used to enable and configure the device.
let usart = Serial::usart3(
cx.device.USART3,
(tx, rx),
&mut afio.mapr,
Config::default().baudrate(9600.bps()),
clocks,
);
let gps_parser = Parser::new();
let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
let i2c = i2c::BlockingI2c::i2c1(
cx.device.I2C1,
(scl, sda),
&mut afio.mapr,
i2c::Mode::Standard {
frequency: 400.kHz(),
},
clocks,
5,
1,
5,
5,
);
let pll = si5153::Si5153::new(&i2c);
let time = Time {
hours: 0,
minutes: 0,
seconds: 0.0,
};
let locator = ArrayString::new();
transmit::spawn().unwrap();
(
Shared { time, locator },
Local {
gps_parser,
usart,
i2c,
pll,
board_led,
transmitting: false,
symbol_pos: 0,
message: [0; 162],
},
init::Monotonics(mono),
)
}
const CALLSIGN: &str = "DL1SSk";
const POWER: u8 = 37;
const WSPR_SYMBOL_LENGTH: u32 = 683;
// For PLL with 800MHz
const WSPR_SYMBOLS: [si5153::PllParams; 4] = [
// 7040100.000000: 113 + 44687 / 70401
// actual: 7040100.000000
si5153::PllParams {
p1: 14033,
p2: 17455,
p3: 70401,
},
// 7040101.464844: 113 + 447801 / 705503
// actual: 7040101.464844
si5153::PllParams {
p1: 14033,
p2: 172785,
p3: 705503,
},
// 7040102.929688: 113 + 38515 / 60682
// actual: 7040102.929688
si5153::PllParams {
p1: 14033,
p2: 14678,
p3: 60682,
},
// 7040104.394531: 113 + 123233 / 194166
// actual: 7040104.394531
si5153::PllParams {
p1: 14033,
p2: 46378,
p3: 194166,
},
];
#[task(local=[pll, i2c, transmitting, symbol_pos, message], shared=[time, locator])]
fn transmit(mut ctx: transmit::Context) {
if !*ctx.local.transmitting {
let loc = ctx.shared.locator.lock(|l| l.clone());
if loc.is_empty() {
transmit::spawn_after(10.secs().into()).unwrap();
return;
}
let time = ctx.shared.time.lock(|l| l.clone());
if time.minutes % 2 != 0 || time.seconds < 1.0 || time.seconds > 2.0 {
transmit::spawn_after(100.millis().into()).unwrap();
return;
}
defmt::info!("Starting tranmission");
let callsign = arrayvec::ArrayString::<6>::from(CALLSIGN).unwrap();
*ctx.local.message = wspr::encode_message(&callsign, &loc, POWER);
*ctx.local.symbol_pos = 0;
*ctx.local.transmitting = true;
}
let sym = ctx.local.message[*ctx.local.symbol_pos] as usize;
defmt::info!("transmitting symbol {} : {}", *ctx.local.symbol_pos, sym);
ctx.local.pll.write_synth_params(
&mut ctx.local.i2c,
si5153::Multisynth::MS0,
&WSPR_SYMBOLS[sym],
);
if *ctx.local.symbol_pos == 0 {
ctx.local
.pll
.enable_ms_output(&mut ctx.local.i2c, si5153::Multisynth::MS0);
}
if *ctx.local.symbol_pos < 162 {
*ctx.local.symbol_pos += 1;
transmit::spawn_after(WSPR_SYMBOL_LENGTH.millis().into()).unwrap();
return;
}
defmt::info!("Stopping transmission.");
ctx.local
.pll
.disable_ms_output(&mut ctx.local.i2c, si5153::Multisynth::MS0);
*ctx.local.transmitting = false;
transmit::spawn_after(100.millis().into()).unwrap();
}
#[task(binds = USART3, local=[usart, board_led, gps_parser], shared=[time, locator])]
fn usart3(mut ctx: usart3::Context) {
match ctx.local.usart.read() {
Ok(byte) => {
ctx.local.board_led.toggle();
if let Some(result) = ctx.local.gps_parser.parse_from_byte(byte) {
match result {
Ok(ParseResult::GGA(Some(gga))) => {
ctx.shared.time.lock(|time| {
time.hours = gga.time.hours;
time.minutes = gga.time.minutes;
time.seconds = gga.time.seconds;
});
ctx.shared.locator.lock(|locator| {
*locator = loc::locator_from_coordinates(
gga.latitude.as_f64(),
gga.longitude.as_f64(),
);
defmt::info!("Got GGA. New locator: {}", locator.as_str());
});
}
Ok(_) => {} // Some other sentences..
Err(_) => {} // Got parse error
}
}
}
Err(nb::Error::WouldBlock) => {}
Err(nb::Error::Other(_)) => {}
}
}
#[task(binds = TIM2, shared=[time])]
fn tim2(mut ctx: tim2::Context) {
ctx.shared.time.lock(|time| {
time.seconds += 0.001;
if time.seconds >= 60.0 {
time.seconds -= 60.0;
time.minutes += 1;
}
if time.minutes == 60 {
time.minutes = 0;
time.hours += 1;
}
if time.hours == 24 {
time.hours = 0;
}
});
}
}

View File

@ -1,104 +0,0 @@
use core::cell::RefCell;
use core::ops::DerefMut;
use cortex_m::interrupt::{free, Mutex};
use stm32f1xx_hal::{
delay::Delay,
pac::interrupt,
pac::{Interrupt, TIM2},
prelude::*,
rcc::{Clocks, APB1},
serial::{Config, Serial},
stm32,
timer::{CountDownTimer, Event, Timer},
};
use nmea0183::datetime::Time;
static TIME: Mutex<RefCell<Time>> = Mutex::new(RefCell::new(Time {
hours: 0,
minutes: 0,
seconds: 0.0,
}));
static TIMER_TIM2: Mutex<RefCell<Option<CountDownTimer<TIM2>>>> = Mutex::new(RefCell::new(None));
pub fn setup(tim2: TIM2, clocks: &Clocks, apb1: &mut APB1) {
let mut timer = Timer::tim2(tim2, clocks, apb1).start_count_down(1.khz());
// Generate an interrupt when the timer expires
timer.listen(Event::Update);
// Move the timer into our global storage
cortex_m::interrupt::free(|cs| *TIMER_TIM2.borrow(cs).borrow_mut() = Some(timer));
unsafe {
cortex_m::peripheral::NVIC::unmask(Interrupt::TIM2);
}
}
#[interrupt]
fn TIM2() {
free(|cs| {
if let Some(ref mut tim2) = TIMER_TIM2.borrow(cs).borrow_mut().deref_mut() {
tim2.clear_update_interrupt_flag();
}
let mut time = TIME.borrow(cs).borrow_mut();
time.seconds += 0.001;
if time.seconds >= 60.0 {
time.seconds -= 60.0;
time.minutes += 1;
}
if time.minutes == 60 {
time.minutes = 0;
time.hours += 1;
}
if time.hours == 24 {
time.hours = 0;
}
});
}
pub fn get() -> Time {
let mut res = Time {
hours: 0,
minutes: 0,
seconds: 0.0,
};
free(|cs| {
let time = TIME.borrow(cs).borrow();
res.seconds = time.seconds;
res.minutes = time.minutes;
res.hours = time.hours;
});
return res;
}
pub fn set(new_time: &Time) -> () {
free(|cs| {
let mut time = TIME.borrow(cs).borrow_mut();
let sec_delta = time.seconds - new_time.seconds;
if time.hours != new_time.hours
|| time.minutes != new_time.minutes
|| sec_delta > 0.05
|| sec_delta < -0.05
{
time.seconds = new_time.seconds;
time.minutes = new_time.minutes;
time.hours = new_time.hours;
}
});
}
pub fn get_timestamp() -> u32 {
let mut res = 0;
free(|cs| {
let time = TIME.borrow(cs).borrow();
res = (time.seconds * 1000.0) as u32;
res += (time.minutes as u32) * 60 * 1000;
res += (time.hours as u32) * 60 * 60 * 1000;
});
return res;
}

View File

@ -1,41 +0,0 @@
[package]
# TODO(1) fix `authors` if you didn't use `cargo-generate`
authors = ["LongHairedHacker <sebastian@sebastians-site.de>"]
name = "testsuite"
publish = false
edition = "2018"
version = "0.1.0"
[[test]]
name = "loc"
harness = false
[[test]]
name = "wspr"
harness = false
[dependencies]
wspr-beacon = { path = ".." }
cortex-m = "0.7.1"
cortex-m-rt = "0.6.12"
defmt = "0.2.0"
defmt-rtt = "0.2.0"
defmt-test = "0.2.0"
panic-probe = { version = "0.2.0", features = ["print-defmt"] }
arrayvec = {version = "~0.7.0", default-features = false}
[features]
# set logging levels here
default = [
# in tests, enable all logs
"defmt-trace",
# "dependency-a/defmt-trace",
]
# do NOT modify these features
defmt-default = []
defmt-trace = []
defmt-debug = []
defmt-info = []
defmt-warn = []
defmt-error = []

View File

@ -1,56 +0,0 @@
#![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};
use wspr_beacon::loc;
#[test]
fn test_locations() {
assert_eq!(
loc::locator_from_coordinates(49.4395, 7.7635).as_str(),
"JN39vk"
);
assert_eq!(
loc::locator_from_coordinates(74.998306, 148.833363).as_str(),
"QQ44kx"
);
assert_eq!(
loc::locator_from_coordinates(64.701746, -18.640812).as_str(),
"IP04qq"
);
assert_eq!(
loc::locator_from_coordinates(19.251825, -81.541811).as_str(),
"EK99fg"
);
assert_eq!(
loc::locator_from_coordinates(-23.587708, 45.25882).as_str(),
"LG26pj"
);
assert_eq!(
loc::locator_from_coordinates(-77.566746, 167.875652).as_str(),
"RB32wk"
);
assert_eq!(
loc::locator_from_coordinates(-17.632622, -149.472404).as_str(),
"BH52gi"
);
assert_eq!(
loc::locator_from_coordinates(-19.77516, -43.959542).as_str(),
"GH80af"
);
}
}

View File

@ -1,33 +0,0 @@
#![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::encode_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]);
}
}
}