Compare commits
2 Commits
main
...
rtic-migra
Author | SHA1 | Date |
---|---|---|
Sebastian | 36da28fc02 | |
Sebastian | db95b2fceb |
|
@ -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",
|
||||
|
|
30
Cargo.toml
30
Cargo.toml
|
@ -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,7 +40,7 @@ codegen-units = 1
|
|||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
opt-level = 3 # <-
|
||||
opt-level = 'z' # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
# cargo test
|
||||
|
@ -60,7 +58,7 @@ codegen-units = 1
|
|||
debug = 2
|
||||
debug-assertions = false # <-
|
||||
incremental = false
|
||||
#lto = 'fat'
|
||||
lto = 'fat'
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = false # <-
|
||||
|
||||
|
|
|
@ -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(_)) => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
30
src/lib.rs
30
src/lib.rs
|
@ -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();
|
||||
}
|
||||
}
|
319
src/main.rs
319
src/main.rs
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
104
src/time.rs
104
src/time.rs
|
@ -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;
|
||||
}
|
|
@ -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 = []
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue