implement a BPSK decoder for demodulated data, add hal feature for use on non-STM32 targets
This commit is contained in:
55
Cargo.toml
55
Cargo.toml
@@ -15,36 +15,39 @@ members = ["examples/stm32wle5jc"]
|
|||||||
# Default chip for development/CI. Downstream users should use default-features = false
|
# Default chip for development/CI. Downstream users should use default-features = false
|
||||||
# and enable their specific chip feature.
|
# and enable their specific chip feature.
|
||||||
default = ["stm32wle5jc"]
|
default = ["stm32wle5jc"]
|
||||||
|
# Hardware abstraction layer, which enables embedded radio driver and all hardware deps
|
||||||
|
# When it's disabled, some parts of the code, such as BPSK frame decoding, can be used
|
||||||
|
hal = ["dep:embassy-stm32", "dep:embassy-time", "dep:defmt", "dep:cortex-m", "dep:embedded-hal", "dep:embedded-hal-async"]
|
||||||
# Single-core variants
|
# Single-core variants
|
||||||
stm32wle4c8 = ["embassy-stm32/stm32wle4c8"]
|
stm32wle4c8 = ["hal", "embassy-stm32/stm32wle4c8"]
|
||||||
stm32wle4cb = ["embassy-stm32/stm32wle4cb"]
|
stm32wle4cb = ["hal", "embassy-stm32/stm32wle4cb"]
|
||||||
stm32wle4cc = ["embassy-stm32/stm32wle4cc"]
|
stm32wle4cc = ["hal", "embassy-stm32/stm32wle4cc"]
|
||||||
stm32wle4j8 = ["embassy-stm32/stm32wle4j8"]
|
stm32wle4j8 = ["hal", "embassy-stm32/stm32wle4j8"]
|
||||||
stm32wle4jb = ["embassy-stm32/stm32wle4jb"]
|
stm32wle4jb = ["hal", "embassy-stm32/stm32wle4jb"]
|
||||||
stm32wle4jc = ["embassy-stm32/stm32wle4jc"]
|
stm32wle4jc = ["hal", "embassy-stm32/stm32wle4jc"]
|
||||||
stm32wle5c8 = ["embassy-stm32/stm32wle5c8"]
|
stm32wle5c8 = ["hal", "embassy-stm32/stm32wle5c8"]
|
||||||
stm32wle5cb = ["embassy-stm32/stm32wle5cb"]
|
stm32wle5cb = ["hal", "embassy-stm32/stm32wle5cb"]
|
||||||
stm32wle5cc = ["embassy-stm32/stm32wle5cc"]
|
stm32wle5cc = ["hal", "embassy-stm32/stm32wle5cc"]
|
||||||
stm32wle5j8 = ["embassy-stm32/stm32wle5j8"]
|
stm32wle5j8 = ["hal", "embassy-stm32/stm32wle5j8"]
|
||||||
stm32wle5jb = ["embassy-stm32/stm32wle5jb"]
|
stm32wle5jb = ["hal", "embassy-stm32/stm32wle5jb"]
|
||||||
stm32wle5jc = ["embassy-stm32/stm32wle5jc"]
|
stm32wle5jc = ["hal", "embassy-stm32/stm32wle5jc"]
|
||||||
# Dual-core variants
|
# Dual-core variants
|
||||||
stm32wl54cc-cm0p = ["embassy-stm32/stm32wl54cc-cm0p"]
|
stm32wl54cc-cm0p = ["hal", "embassy-stm32/stm32wl54cc-cm0p"]
|
||||||
stm32wl54cc-cm4 = ["embassy-stm32/stm32wl54cc-cm4"]
|
stm32wl54cc-cm4 = ["hal", "embassy-stm32/stm32wl54cc-cm4"]
|
||||||
stm32wl54jc-cm0p = ["embassy-stm32/stm32wl54jc-cm0p"]
|
stm32wl54jc-cm0p = ["hal", "embassy-stm32/stm32wl54jc-cm0p"]
|
||||||
stm32wl54jc-cm4 = ["embassy-stm32/stm32wl54jc-cm4"]
|
stm32wl54jc-cm4 = ["hal", "embassy-stm32/stm32wl54jc-cm4"]
|
||||||
stm32wl55cc-cm0p = ["embassy-stm32/stm32wl55cc-cm0p"]
|
stm32wl55cc-cm0p = ["hal", "embassy-stm32/stm32wl55cc-cm0p"]
|
||||||
stm32wl55cc-cm4 = ["embassy-stm32/stm32wl55cc-cm4"]
|
stm32wl55cc-cm4 = ["hal", "embassy-stm32/stm32wl55cc-cm4"]
|
||||||
stm32wl55jc-cm0p = ["embassy-stm32/stm32wl55jc-cm0p"]
|
stm32wl55jc-cm0p = ["hal", "embassy-stm32/stm32wl55jc-cm0p"]
|
||||||
stm32wl55jc-cm4 = ["embassy-stm32/stm32wl55jc-cm4"]
|
stm32wl55jc-cm4 = ["hal", "embassy-stm32/stm32wl55jc-cm4"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-stm32 = { version = "0.5.0", features = ["unstable-pac"] }
|
embassy-stm32 = { version = "0.5.0", features = ["unstable-pac"], optional = true }
|
||||||
embassy-time = "0.5.0"
|
embassy-time = { version = "0.5.0", optional = true }
|
||||||
defmt = "1.0.1"
|
defmt = { version = "1.0.1", optional = true }
|
||||||
cortex-m = { version = "0.7.6", features = ["inline-asm"] }
|
cortex-m = { version = "0.7.6", features = ["inline-asm"], optional = true }
|
||||||
embedded-hal = "1.0.0"
|
embedded-hal = { version = "1.0.0", optional = true }
|
||||||
embedded-hal-async = "1.0.0"
|
embedded-hal-async = { version = "1.0.0", optional = true }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 2
|
debug = 2
|
||||||
|
|||||||
13
src/lib.rs
13
src/lib.rs
@@ -1,13 +1,22 @@
|
|||||||
#![no_std]
|
#![cfg_attr(feature = "hal", no_std)]
|
||||||
#![allow(async_fn_in_trait)]
|
#![allow(async_fn_in_trait)]
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub mod modulations;
|
pub mod modulations;
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
|
pub mod error;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub mod radio;
|
pub mod radio;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub use error::RadioError;
|
pub use error::RadioError;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub use radio::{PaSelection, Radio};
|
pub use radio::{PaSelection, Radio};
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub use spi::SubGhzSpiDevice;
|
pub use spi::SubGhzSpiDevice;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub use traits::{Configure, Receive, Transmit};
|
pub use traits::{Configure, Receive, Transmit};
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
|
#[cfg(feature = "hal")]
|
||||||
use defmt::debug;
|
use defmt::debug;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
use embedded_hal_async::spi::SpiDevice;
|
use embedded_hal_async::spi::SpiDevice;
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
use crate::{
|
use crate::{
|
||||||
RadioError,
|
RadioError,
|
||||||
radio::{PaSelection, PacketType, Radio, RampTime, irq},
|
radio::{PaSelection, PacketType, Radio, RampTime, irq},
|
||||||
traits::{Configure, Transmit},
|
traits::{Configure, Transmit},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "hal"))]
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
/// BPSK bitrate
|
/// BPSK bitrate
|
||||||
/// Formula: register = (32 * 32_000_000) / bps
|
/// Formula: register = (32 * 32_000_000) / bps
|
||||||
#[derive(Clone, Copy, defmt::Format)]
|
#[derive(Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "hal", derive(defmt::Format))]
|
||||||
pub enum Bitrate {
|
pub enum Bitrate {
|
||||||
/// 100 bits per second
|
/// 100 bits per second
|
||||||
Bps100,
|
Bps100,
|
||||||
@@ -22,7 +30,7 @@ pub enum Bitrate {
|
|||||||
|
|
||||||
impl Bitrate {
|
impl Bitrate {
|
||||||
/// Get the 3-byte register value for this bitrate
|
/// Get the 3-byte register value for this bitrate
|
||||||
fn to_bytes(self) -> [u8; 3] {
|
pub fn to_bytes(self) -> [u8; 3] {
|
||||||
let val = match self {
|
let val = match self {
|
||||||
Bitrate::Bps100 => 0x9C4000,
|
Bitrate::Bps100 => 0x9C4000,
|
||||||
Bitrate::Bps600 => 0x1A0AAA,
|
Bitrate::Bps600 => 0x1A0AAA,
|
||||||
@@ -32,7 +40,8 @@ impl Bitrate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, defmt::Format)]
|
#[derive(Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "hal", derive(defmt::Format))]
|
||||||
pub enum CrcType {
|
pub enum CrcType {
|
||||||
None,
|
None,
|
||||||
/// Using a common 0x07 polynomial
|
/// Using a common 0x07 polynomial
|
||||||
@@ -42,7 +51,7 @@ pub enum CrcType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CrcType {
|
impl CrcType {
|
||||||
fn compute(self, data: &[u8]) -> (u16, usize) {
|
pub fn compute(self, data: &[u8]) -> (u16, usize) {
|
||||||
match self {
|
match self {
|
||||||
CrcType::None => (0, 0),
|
CrcType::None => (0, 0),
|
||||||
CrcType::Crc8 => {
|
CrcType::Crc8 => {
|
||||||
@@ -76,7 +85,7 @@ impl CrcType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(self, crc: u16, buf: &mut [u8]) {
|
pub fn write(self, crc: u16, buf: &mut [u8]) {
|
||||||
match self {
|
match self {
|
||||||
CrcType::None => {}
|
CrcType::None => {}
|
||||||
CrcType::Crc8 => {
|
CrcType::Crc8 => {
|
||||||
@@ -90,14 +99,15 @@ impl CrcType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, defmt::Format)]
|
#[derive(Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "hal", derive(defmt::Format))]
|
||||||
pub enum Whitening {
|
pub enum Whitening {
|
||||||
None,
|
None,
|
||||||
Ccitt,
|
Ccitt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Whitening {
|
impl Whitening {
|
||||||
fn apply(self, seed: u16, data: &mut [u8]) {
|
pub fn apply(self, seed: u16, data: &mut [u8]) {
|
||||||
match self {
|
match self {
|
||||||
Whitening::None => return,
|
Whitening::None => return,
|
||||||
Whitening::Ccitt => {}
|
Whitening::Ccitt => {}
|
||||||
@@ -117,7 +127,20 @@ impl Whitening {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, defmt::Format)]
|
#[cfg(not(feature = "hal"))]
|
||||||
|
pub struct DecodeResult {
|
||||||
|
/// On which bit the sync word was found
|
||||||
|
pub bit_offset: usize,
|
||||||
|
/// Signal was phase-inverted
|
||||||
|
pub inverted: bool,
|
||||||
|
/// Decoded payload
|
||||||
|
pub payload: Vec<u8>,
|
||||||
|
/// CRC matches
|
||||||
|
pub crc_valid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "hal", derive(defmt::Format))]
|
||||||
pub enum BpskPacket {
|
pub enum BpskPacket {
|
||||||
/// No framing, just send raw data
|
/// No framing, just send raw data
|
||||||
Raw,
|
Raw,
|
||||||
@@ -129,8 +152,6 @@ pub enum BpskPacket {
|
|||||||
sync_word: [u8; 32],
|
sync_word: [u8; 32],
|
||||||
/// Sync word length
|
/// Sync word length
|
||||||
sync_word_len: usize,
|
sync_word_len: usize,
|
||||||
/// Enable/disable reporting length in the packet
|
|
||||||
include_len: bool,
|
|
||||||
/// CRC size (0, 1 or 2 bytes)
|
/// CRC size (0, 1 or 2 bytes)
|
||||||
crc_type: CrcType,
|
crc_type: CrcType,
|
||||||
/// Whitening algorithm
|
/// Whitening algorithm
|
||||||
@@ -145,42 +166,46 @@ impl BpskPacket {
|
|||||||
Self::Framing {
|
Self::Framing {
|
||||||
preamble_len: 32,
|
preamble_len: 32,
|
||||||
// Baker-13 code (2 bytes)
|
// Baker-13 code (2 bytes)
|
||||||
sync_word: [0x1F, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
sync_word: [
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x1F, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
],
|
||||||
sync_word_len: 2,
|
sync_word_len: 2,
|
||||||
include_len: true,
|
|
||||||
crc_type: CrcType::Crc16,
|
crc_type: CrcType::Crc16,
|
||||||
whitening: Whitening::Ccitt,
|
whitening: Whitening::Ccitt,
|
||||||
whitening_seed: 0x1FF,
|
whitening_seed: 0x1FF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_bytes(self, payload: &[u8], buf: &mut [u8]) -> Result<u8, RadioError> {
|
#[cfg(feature = "hal")]
|
||||||
|
pub fn to_bytes(self, payload: &[u8], buf: &mut [u8]) -> Result<u8, RadioError> {
|
||||||
match self {
|
match self {
|
||||||
BpskPacket::Raw => {
|
BpskPacket::Raw => {
|
||||||
// Simple copy operation, no modifications made
|
// Simple copy operation, no modifications made
|
||||||
buf[..payload.len()].copy_from_slice(payload);
|
buf[..payload.len()].copy_from_slice(payload);
|
||||||
payload.len().try_into().map_err(|_| RadioError::PayloadTooLarge)
|
payload
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| RadioError::PayloadTooLarge)
|
||||||
}
|
}
|
||||||
BpskPacket::Framing {
|
BpskPacket::Framing {
|
||||||
preamble_len,
|
preamble_len,
|
||||||
sync_word,
|
sync_word,
|
||||||
sync_word_len,
|
sync_word_len,
|
||||||
include_len,
|
|
||||||
crc_type,
|
crc_type,
|
||||||
whitening,
|
whitening,
|
||||||
whitening_seed,
|
whitening_seed,
|
||||||
} => {
|
} => {
|
||||||
let len_field_size = if include_len { 1 } else { 0 };
|
let len_field_size = 1;
|
||||||
let crc_size = match crc_type {
|
let crc_size = match crc_type {
|
||||||
CrcType::None => 0,
|
CrcType::None => 0,
|
||||||
CrcType::Crc8 => 1,
|
CrcType::Crc8 => 1,
|
||||||
CrcType::Crc16 => 2,
|
CrcType::Crc16 => 2,
|
||||||
};
|
};
|
||||||
// Validate packet length
|
// Validate packet length
|
||||||
let total = preamble_len + sync_word_len + len_field_size + payload.len() + crc_size;
|
let total =
|
||||||
|
preamble_len + sync_word_len + len_field_size + payload.len() + crc_size;
|
||||||
|
|
||||||
if total > buf.len() {
|
if total > buf.len() {
|
||||||
return Err(RadioError::PayloadTooLarge);
|
return Err(RadioError::PayloadTooLarge);
|
||||||
@@ -200,12 +225,13 @@ impl BpskPacket {
|
|||||||
// Actual payload starts here
|
// Actual payload starts here
|
||||||
let data_start = pos;
|
let data_start = pos;
|
||||||
|
|
||||||
// If enabled in the config, write length info
|
// Write length info
|
||||||
if include_len {
|
let payload_len: u8 = payload
|
||||||
let payload_len: u8 = payload.len().try_into().map_err(|_| RadioError::PayloadTooLarge)?;
|
.len()
|
||||||
buf[pos] = payload_len;
|
.try_into()
|
||||||
pos += 1;
|
.map_err(|_| RadioError::PayloadTooLarge)?;
|
||||||
}
|
buf[pos] = payload_len;
|
||||||
|
pos += 1;
|
||||||
|
|
||||||
// Copy the original payload itself
|
// Copy the original payload itself
|
||||||
buf[pos..pos + payload.len()].copy_from_slice(payload);
|
buf[pos..pos + payload.len()].copy_from_slice(payload);
|
||||||
@@ -224,8 +250,127 @@ impl BpskPacket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "hal"))]
|
||||||
|
pub fn decode(&self, data: &[u8]) -> Vec<DecodeResult> {
|
||||||
|
match self {
|
||||||
|
// Can't do much with Raw packets so just return the same data and accept as valid
|
||||||
|
BpskPacket::Raw => vec![DecodeResult {
|
||||||
|
bit_offset: 0,
|
||||||
|
inverted: false,
|
||||||
|
payload: data.to_vec(),
|
||||||
|
crc_valid: true,
|
||||||
|
}],
|
||||||
|
BpskPacket::Framing {
|
||||||
|
preamble_len: _,
|
||||||
|
sync_word,
|
||||||
|
sync_word_len,
|
||||||
|
crc_type,
|
||||||
|
whitening,
|
||||||
|
whitening_seed,
|
||||||
|
} => {
|
||||||
|
let sync_word = &sync_word[..*sync_word_len];
|
||||||
|
let sync_bits = sync_word_len * 8;
|
||||||
|
let total_bits = data.len() * 8;
|
||||||
|
let mut results = Vec::new();
|
||||||
|
|
||||||
|
let crc_len = match crc_type {
|
||||||
|
CrcType::None => 0,
|
||||||
|
CrcType::Crc8 => 1,
|
||||||
|
CrcType::Crc16 => 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Go through every bit, giving space to the sync word length
|
||||||
|
for i in 0..total_bits.saturating_sub(sync_bits) {
|
||||||
|
let mut matching: usize = 0;
|
||||||
|
for j in 0..sync_bits {
|
||||||
|
// Compare the sync word bit-by-bit against the data stream
|
||||||
|
let data_bit = (data[(i + j) / 8] >> (7 - ((i + j) % 8))) & 1;
|
||||||
|
let sync_bit = (sync_word[j / 8] >> (7 - (j % 8))) & 1;
|
||||||
|
|
||||||
|
if data_bit == sync_bit {
|
||||||
|
matching += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow up to 2 bit errors, if <= 2 bits match or unmatch, it's an accepted
|
||||||
|
// normal or phase-inverted data
|
||||||
|
let inverted = if sync_bits - matching <= 2 {
|
||||||
|
false
|
||||||
|
} else if matching <= 2 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract bytes that happen after the sync word is found at any bit offset
|
||||||
|
let bit_start = i + sync_bits;
|
||||||
|
let remaining_bytes = (total_bits - bit_start) / 8;
|
||||||
|
if remaining_bytes == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reassemble bytes from arbitrary bit positions after sync word is found at offset `i`
|
||||||
|
let mut raw = vec![0u8; remaining_bytes];
|
||||||
|
for b in 0..remaining_bytes {
|
||||||
|
let mut byte = 0u8;
|
||||||
|
for bit in 0..8 {
|
||||||
|
let idx = bit_start + b * 8 + bit;
|
||||||
|
let mut val = (data[idx / 8] >> (7 - (idx % 8))) & 1;
|
||||||
|
// Handle the 180 degree phase ambiguity
|
||||||
|
if inverted {
|
||||||
|
val ^= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte |= val << (7 - bit);
|
||||||
|
}
|
||||||
|
raw[b] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
// De-whiten by applying the same whitening operation again (it's reversible)
|
||||||
|
whitening.apply(*whitening_seed, &mut raw);
|
||||||
|
|
||||||
|
// Extract payload length
|
||||||
|
let (payload_start, payload_len) = (1, raw[0] as usize);
|
||||||
|
|
||||||
|
if payload_len == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload_start + payload_len + crc_len > raw.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify CRC over len field and payload
|
||||||
|
let crc_data = &raw[..payload_start + payload_len];
|
||||||
|
let (crc_computed, _) = crc_type.compute(crc_data);
|
||||||
|
|
||||||
|
let crc_valid = match crc_type {
|
||||||
|
CrcType::None => true,
|
||||||
|
CrcType::Crc8 => raw[payload_start + payload_len] == crc_computed as u8,
|
||||||
|
CrcType::Crc16 => {
|
||||||
|
// Assemble u16 from two bytes
|
||||||
|
let received = ((raw[payload_start + payload_len] as u16) << 8)
|
||||||
|
| raw[payload_start + payload_len + 1] as u16;
|
||||||
|
received == crc_computed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(DecodeResult {
|
||||||
|
bit_offset: i,
|
||||||
|
inverted,
|
||||||
|
payload: raw[payload_start..payload_start + payload_len].to_vec(),
|
||||||
|
crc_valid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
#[derive(Clone, Copy, defmt::Format)]
|
#[derive(Clone, Copy, defmt::Format)]
|
||||||
pub struct BpskConfig {
|
pub struct BpskConfig {
|
||||||
pub frequency: u32,
|
pub frequency: u32,
|
||||||
@@ -236,6 +381,7 @@ pub struct BpskConfig {
|
|||||||
pub packet: BpskPacket,
|
pub packet: BpskPacket,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
impl Default for BpskConfig {
|
impl Default for BpskConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -250,12 +396,14 @@ impl Default for BpskConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// BPSK modulation - borrows a Radio, implements Configure + Transmit
|
/// BPSK modulation - borrows a Radio, implements Configure + Transmit
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub struct BpskRadio<'a, SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> {
|
pub struct BpskRadio<'a, SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> {
|
||||||
radio: &'a mut Radio<SPI, TX, RX, EN>,
|
radio: &'a mut Radio<SPI, TX, RX, EN>,
|
||||||
payload_len: u8,
|
payload_len: u8,
|
||||||
config: BpskConfig,
|
config: BpskConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
impl<'a, SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin>
|
impl<'a, SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin>
|
||||||
BpskRadio<'a, SPI, TX, RX, EN>
|
BpskRadio<'a, SPI, TX, RX, EN>
|
||||||
{
|
{
|
||||||
@@ -283,6 +431,7 @@ impl<'a, SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
impl<SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> Configure
|
impl<SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> Configure
|
||||||
for BpskRadio<'_, SPI, TX, RX, EN>
|
for BpskRadio<'_, SPI, TX, RX, EN>
|
||||||
{
|
{
|
||||||
@@ -315,6 +464,7 @@ impl<SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> Configure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
impl<SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> Transmit
|
impl<SPI: SpiDevice, TX: OutputPin, RX: OutputPin, EN: OutputPin> Transmit
|
||||||
for BpskRadio<'_, SPI, TX, RX, EN>
|
for BpskRadio<'_, SPI, TX, RX, EN>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
pub mod bpsk;
|
pub mod bpsk;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub mod fsk;
|
pub mod fsk;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub mod lora;
|
pub mod lora;
|
||||||
|
#[cfg(feature = "hal")]
|
||||||
pub mod msk;
|
pub mod msk;
|
||||||
|
|||||||
Reference in New Issue
Block a user