mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-01 07:20:44 +00:00
274 lines
7.2 KiB
Rust
274 lines
7.2 KiB
Rust
use anyhow::Result;
|
|
#[cfg(feature = "serde")]
|
|
use serde::Serialize;
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize))]
|
|
pub enum CFList {
|
|
Channels(CFListChannels),
|
|
ChannelMask(CFListChannelMasks),
|
|
}
|
|
|
|
impl CFList {
|
|
pub fn from_bytes(b: [u8; 16]) -> Result<Self> {
|
|
let mut bb: [u8; 15] = [0; 15];
|
|
bb.clone_from_slice(&b[..15]);
|
|
|
|
// match on CFListType.
|
|
match b[15] {
|
|
0x00 => Ok(CFList::Channels(CFListChannels::from_bytes(bb))),
|
|
0x01 => Ok(CFList::ChannelMask(CFListChannelMasks::from_bytes(bb))),
|
|
_ => Err(anyhow!("unexpected CFListType")),
|
|
}
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> Result<[u8; 16]> {
|
|
let mut b: [u8; 16] = [0; 16];
|
|
|
|
match self {
|
|
CFList::Channels(v) => {
|
|
b[..15].clone_from_slice(&v.to_bytes()?);
|
|
b[15] = 0x00;
|
|
}
|
|
CFList::ChannelMask(v) => {
|
|
b[..15].clone_from_slice(&v.to_bytes()?);
|
|
b[15] = 0x01;
|
|
}
|
|
}
|
|
|
|
Ok(b)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize))]
|
|
pub struct CFListChannels([u32; 5]);
|
|
|
|
impl CFListChannels {
|
|
pub fn new(channels: [u32; 5]) -> Self {
|
|
CFListChannels(channels)
|
|
}
|
|
|
|
pub fn from_slice(channels: &[u32]) -> Result<Self> {
|
|
let mut channels_array: [u32; 5] = [0; 5];
|
|
if channels.len() > channels_array.len() {
|
|
return Err(anyhow!(
|
|
"Max number of channels is {}",
|
|
channels_array.len()
|
|
));
|
|
}
|
|
|
|
for (i, c) in channels.iter().enumerate() {
|
|
channels_array[i] = *c;
|
|
}
|
|
|
|
Ok(Self::new(channels_array))
|
|
}
|
|
|
|
pub fn from_bytes(b: [u8; 15]) -> Self {
|
|
let mut channels: [u32; 5] = [0; 5];
|
|
|
|
for (i, ch) in channels.iter_mut().enumerate() {
|
|
let index = i * 3;
|
|
*ch = u32::from_le_bytes([b[index], b[index + 1], b[index + 2], 0x00]) * 100;
|
|
}
|
|
|
|
CFListChannels(channels)
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> Result<[u8; 15]> {
|
|
let mut b: [u8; 15] = [0; 15];
|
|
|
|
for (i, f) in self.0.iter().enumerate() {
|
|
if f % 100 != 0 {
|
|
return Err(anyhow!("frequency must be a multiple of 100"));
|
|
}
|
|
|
|
let f = f / 100;
|
|
if f > (1 << 24) - 1 {
|
|
return Err(anyhow!("max value of frequency is 2^24-1"));
|
|
}
|
|
|
|
let index = i * 3;
|
|
b[index..index + 3].clone_from_slice(&f.to_le_bytes()[..3]);
|
|
}
|
|
|
|
Ok(b)
|
|
}
|
|
|
|
pub fn iter(&self) -> std::slice::Iter<'_, u32> {
|
|
self.0.iter()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize))]
|
|
pub struct CFListChannelMasks(Vec<ChMask>);
|
|
|
|
impl CFListChannelMasks {
|
|
pub fn new(masks: Vec<ChMask>) -> Self {
|
|
CFListChannelMasks(masks)
|
|
}
|
|
|
|
pub fn from_bytes(b: [u8; 15]) -> Self {
|
|
let b = b[..14].to_vec(); // each mask uses 2 bytes
|
|
|
|
let mut masks: Vec<ChMask> = Vec::with_capacity(b.len() / 3);
|
|
let mut pending: Vec<ChMask> = Vec::with_capacity(b.len() / 2);
|
|
|
|
for i in (0..b.len()).step_by(2) {
|
|
pending.push(ChMask::from_bytes([b[i], b[i + 1]]));
|
|
|
|
if b[i..i + 2] != vec![0x00, 0x00] {
|
|
masks.append(&mut pending);
|
|
}
|
|
}
|
|
|
|
CFListChannelMasks(masks)
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> Result<[u8; 15]> {
|
|
if self.0.len() > 6 {
|
|
return Err(anyhow!("max number of channel-masks is 6"));
|
|
}
|
|
|
|
let mut b: [u8; 15] = [0; 15];
|
|
|
|
for (i, mask) in self.0.iter().enumerate() {
|
|
let index = i * 2;
|
|
b[index..index + 2].clone_from_slice(&mask.to_bytes());
|
|
}
|
|
|
|
Ok(b)
|
|
}
|
|
|
|
pub fn iter(&self) -> std::slice::Iter<'_, ChMask> {
|
|
self.0.iter()
|
|
}
|
|
}
|
|
|
|
/// ChMask encodes the channels usable for uplink access. 0 = channel 1,
|
|
/// 15 = channel 16.
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize))]
|
|
pub struct ChMask([bool; 16]);
|
|
|
|
impl ChMask {
|
|
const SIZE: usize = 2;
|
|
|
|
pub fn new(mask: [bool; 16]) -> Self {
|
|
ChMask(mask)
|
|
}
|
|
|
|
pub fn set(&mut self, i: usize, v: bool) {
|
|
self.0[i] = v
|
|
}
|
|
|
|
pub fn from_slice(mask: &[bool]) -> Result<Self> {
|
|
let mut mask_array: [bool; 16] = [false; 16];
|
|
|
|
if mask.len() > mask_array.len() {
|
|
return Err(anyhow!("Max number of masks is {}", mask_array.len()));
|
|
}
|
|
|
|
for (i, m) in mask.iter().enumerate() {
|
|
mask_array[i] = *m;
|
|
}
|
|
|
|
Ok(Self::new(mask_array))
|
|
}
|
|
|
|
pub fn from_bytes(b: [u8; Self::SIZE]) -> Self {
|
|
let mut mask: [bool; 16] = [false; 16];
|
|
|
|
let n = u16::from_le_bytes(b);
|
|
for (i, m) in mask.iter_mut().enumerate() {
|
|
*m = n & (1 << i) != 0;
|
|
}
|
|
|
|
ChMask(mask)
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
|
|
let mut n: u16 = 0;
|
|
for i in 0..16 {
|
|
if self.0[i] {
|
|
n |= 1 << i;
|
|
}
|
|
}
|
|
|
|
n.to_le_bytes()
|
|
}
|
|
}
|
|
|
|
impl IntoIterator for ChMask {
|
|
type Item = bool;
|
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
|
|
|
#[allow(clippy::unnecessary_to_owned)]
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.0.to_vec().into_iter()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
struct Test {
|
|
cflist: CFList,
|
|
bytes: [u8; 16],
|
|
}
|
|
|
|
fn tests() -> Vec<Test> {
|
|
vec![
|
|
Test {
|
|
cflist: CFList::ChannelMask(CFListChannelMasks::new(vec![ChMask::new([
|
|
true, true, true, true, true, true, true, true, false, false, false, false,
|
|
false, false, false, false,
|
|
])])),
|
|
bytes: [
|
|
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x01,
|
|
],
|
|
},
|
|
Test {
|
|
cflist: CFList::ChannelMask(CFListChannelMasks::new(vec![
|
|
ChMask::new([false; 16]),
|
|
ChMask::new([
|
|
true, true, true, true, true, true, true, true, false, false, false, false,
|
|
false, false, false, false,
|
|
]),
|
|
])),
|
|
bytes: [
|
|
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x01,
|
|
],
|
|
},
|
|
Test {
|
|
cflist: CFList::Channels(CFListChannels::new([
|
|
867100000, 867300000, 867500000, 867700000, 867900000,
|
|
])),
|
|
bytes: [
|
|
0x18, 0x4f, 0x84, 0xe8, 0x56, 0x84, 0xb8, 0x5e, 0x84, 0x88, 0x66, 0x84, 0x58,
|
|
0x6e, 0x84, 0x0,
|
|
],
|
|
},
|
|
]
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_bytes() {
|
|
for tst in tests() {
|
|
assert_eq!(tst.bytes, tst.cflist.to_bytes().unwrap());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_from_bytes() {
|
|
for tst in tests() {
|
|
assert_eq!(tst.cflist, CFList::from_bytes(tst.bytes).unwrap());
|
|
}
|
|
}
|
|
}
|