use std::collections::{HashMap, HashSet};
use std::fmt;
use std::str::FromStr;
use std::time::Duration;
use anyhow::{Context, Result};
#[cfg(feature = "sqlite")]
use diesel::sqlite::Sqlite;
#[cfg(feature = "diesel")]
use diesel::{
backend::Backend,
sql_types::Text,
{deserialize, serialize},
};
#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize};
use crate::{
CFList, CFListChannelMasks, CFListChannels, ChMask, DevAddr, LinkADRReqPayload, Redundancy,
};
pub mod as923;
pub mod au915;
pub mod cn470;
pub mod cn779;
pub mod eu433;
pub mod eu868;
pub mod in865;
pub mod ism2400;
pub mod kr920;
pub mod ru864;
pub mod us915;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "diesel", derive(AsExpression, FromSqlRow))]
#[cfg_attr(feature = "diesel", diesel(sql_type = diesel::sql_types::Text))]
pub enum CommonName {
EU868,
US915,
CN779,
EU433,
AU915,
CN470,
AS923,
AS923_2,
AS923_3,
AS923_4,
KR920,
IN865,
RU864,
ISM2400,
}
impl fmt::Display for CommonName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl FromStr for CommonName {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"EU868" => CommonName::EU868,
"US915" => CommonName::US915,
"CN779" => CommonName::CN779,
"EU433" => CommonName::EU433,
"AU915" => CommonName::AU915,
"CN470" => CommonName::CN470,
"AS923" => CommonName::AS923,
"AS923_2" | "AS923-2" => CommonName::AS923_2,
"AS923_3" | "AS923-3" => CommonName::AS923_3,
"AS923_4" | "AS923-4" => CommonName::AS923_4,
"KR920" => CommonName::KR920,
"IN865" => CommonName::IN865,
"RU864" => CommonName::RU864,
"ISM2400" => CommonName::ISM2400,
_ => {
return Err(anyhow!("Unexpected CommonName: {}", s));
}
})
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CommonName {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
}
}
#[cfg(feature = "diesel")]
impl<DB> deserialize::FromSql<Text, DB> for CommonName
where
DB: Backend,
*const str: deserialize::FromSql<Text, DB>,
{
fn from_sql(value: <DB as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
let string = <*const str>::from_sql(value)?;
Ok(Self::from_str(unsafe { &*string })?)
}
}
#[cfg(feature = "postgres")]
impl serialize::ToSql<Text, diesel::pg::Pg> for CommonName
where
str: serialize::ToSql<Text, diesel::pg::Pg>,
{
fn to_sql<'b>(
&'b self,
out: &mut serialize::Output<'b, '_, diesel::pg::Pg>,
) -> serialize::Result {
<str as serialize::ToSql<Text, diesel::pg::Pg>>::to_sql(
&self.to_string(),
&mut out.reborrow(),
)
}
}
#[cfg(feature = "sqlite")]
impl serialize::ToSql<Text, Sqlite> for CommonName {
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Sqlite>) -> serialize::Result {
out.set_value(self.to_string());
Ok(serialize::IsNull::No)
}
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "diesel", derive(AsExpression, FromSqlRow))]
#[cfg_attr(feature = "diesel", diesel(sql_type = diesel::sql_types::Text))]
pub enum Revision {
Latest,
A,
B,
RP002_1_0_0,
RP002_1_0_1,
RP002_1_0_2,
RP002_1_0_3,
RP002_1_0_4,
}
impl Revision {
fn _to_string(&self) -> String {
match self {
Revision::A => "A".to_string(),
Revision::B => "B".to_string(),
Revision::RP002_1_0_0 => "RP002-1.0.0".to_string(),
Revision::RP002_1_0_1 => "RP002-1.0.1".to_string(),
Revision::RP002_1_0_2 => "RP002-1.0.2".to_string(),
Revision::RP002_1_0_3 => "RP002-1.0.3".to_string(),
Revision::RP002_1_0_4 | Revision::Latest => "RP002-1.0.4".to_string(),
}
}
}
impl fmt::Display for Revision {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self._to_string(),)
}
}
impl fmt::Debug for Revision {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self._to_string(),)
}
}
impl FromStr for Revision {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"A" => Revision::A,
"B" => Revision::B,
"RP002-1.0.0" => Revision::RP002_1_0_0,
"RP002-1.0.1" => Revision::RP002_1_0_1,
"RP002-1.0.2" => Revision::RP002_1_0_2,
"RP002-1.0.3" => Revision::RP002_1_0_3,
"RP002-1.0.4" => Revision::RP002_1_0_4,
_ => {
return Err(anyhow!("Unexpected Revision: {}", s));
}
})
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Revision {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
}
}
#[cfg(feature = "diesel")]
impl<DB> deserialize::FromSql<Text, DB> for Revision
where
DB: Backend,
*const str: deserialize::FromSql<Text, DB>,
{
fn from_sql(value: <DB as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
let string = <*const str>::from_sql(value)?;
Ok(Self::from_str(unsafe { &*string })?)
}
}
#[cfg(feature = "postgres")]
impl serialize::ToSql<Text, diesel::pg::Pg> for Revision
where
str: serialize::ToSql<Text, diesel::pg::Pg>,
{
fn to_sql<'b>(
&'b self,
out: &mut serialize::Output<'b, '_, diesel::pg::Pg>,
) -> serialize::Result {
<str as serialize::ToSql<Text, diesel::pg::Pg>>::to_sql(
&self.to_string(),
&mut out.reborrow(),
)
}
}
#[cfg(feature = "sqlite")]
impl serialize::ToSql<Text, Sqlite> for Revision {
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Sqlite>) -> serialize::Result {
out.set_value(self.to_string());
Ok(serialize::IsNull::No)
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "diesel", derive(AsExpression, FromSqlRow))]
#[cfg_attr(feature = "diesel", diesel(sql_type = diesel::sql_types::Text))]
pub enum MacVersion {
Latest,
LORAWAN_1_0_0,
LORAWAN_1_0_1,
LORAWAN_1_0_2,
LORAWAN_1_0_3,
LORAWAN_1_0_4,
LORAWAN_1_1_0,
}
impl MacVersion {
fn _to_string(&self) -> String {
match self {
MacVersion::LORAWAN_1_0_0 => "1.0.0".to_string(),
MacVersion::LORAWAN_1_0_1 => "1.0.1".to_string(),
MacVersion::LORAWAN_1_0_2 => "1.0.2".to_string(),
MacVersion::LORAWAN_1_0_3 => "1.0.3".to_string(),
MacVersion::LORAWAN_1_0_4 => "1.0.4".to_string(),
MacVersion::LORAWAN_1_1_0 | MacVersion::Latest => "1.1.0".to_string(),
}
}
}
impl fmt::Display for MacVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self._to_string(),)
}
}
impl fmt::Debug for MacVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self._to_string(),)
}
}
impl FromStr for MacVersion {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
"1.0.0" | "1.0" => MacVersion::LORAWAN_1_0_0,
"1.0.1" => MacVersion::LORAWAN_1_0_1,
"1.0.2" => MacVersion::LORAWAN_1_0_2,
"1.0.3" => MacVersion::LORAWAN_1_0_3,
"1.0.4" => MacVersion::LORAWAN_1_0_4,
"1.1.0" | "1.1" => MacVersion::LORAWAN_1_1_0,
_ => {
return Err(anyhow!("Unexpected MacVersion: {}", s));
}
})
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MacVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
}
}
#[cfg(feature = "diesel")]
impl<DB> deserialize::FromSql<Text, DB> for MacVersion
where
DB: Backend,
*const str: deserialize::FromSql<Text, DB>,
{
fn from_sql(value: <DB as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
let string = <*const str>::from_sql(value)?;
Ok(Self::from_str(unsafe { &*string })?)
}
}
#[cfg(feature = "postgres")]
impl serialize::ToSql<Text, diesel::pg::Pg> for MacVersion
where
str: serialize::ToSql<Text, diesel::pg::Pg>,
{
fn to_sql<'b>(
&'b self,
out: &mut serialize::Output<'b, '_, diesel::pg::Pg>,
) -> serialize::Result {
<str as serialize::ToSql<Text, diesel::pg::Pg>>::to_sql(
&self.to_string(),
&mut out.reborrow(),
)
}
}
#[cfg(feature = "sqlite")]
impl serialize::ToSql<Text, Sqlite> for MacVersion {
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Sqlite>) -> serialize::Result {
out.set_value(self.to_string());
Ok(serialize::IsNull::No)
}
}
#[derive(Clone)]
pub struct DataRate {
pub uplink: bool,
pub downlink: bool,
pub modulation: DataRateModulation,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum DataRateModulation {
Lora(LoraDataRate),
Fsk(FskDataRate),
LrFhss(LrFhssDataRate),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct LoraDataRate {
pub spreading_factor: u8,
pub bandwidth: u32,
pub coding_rate: String,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FskDataRate {
pub bitrate: u32,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct LrFhssDataRate {
pub coding_rate: String,
pub occupied_channel_width: u32,
}
pub struct Defaults {
pub rx2_frequency: u32,
pub rx2_dr: u8,
pub rx1_delay: Duration,
pub rx2_delay: Duration,
pub join_accept_delay1: Duration,
pub join_accept_delay2: Duration,
}
#[derive(Clone)]
pub struct MaxPayloadSize {
pub m: usize,
pub n: usize,
}
#[derive(Clone, Default)]
pub struct Channel {
pub frequency: u32,
pub min_dr: u8,
pub max_dr: u8,
pub enabled: bool,
pub user_defined: bool,
}
pub trait Region {
fn get_name(&self) -> CommonName;
fn get_data_rate_index(&self, uplink: bool, modulation: &DataRateModulation) -> Result<u8>;
fn get_data_rate(&self, dr: u8) -> Result<DataRateModulation>;
fn get_max_payload_size(
&self,
mac_version: MacVersion,
reg_params_revision: Revision,
dr: u8,
) -> Result<MaxPayloadSize>;
fn get_rx1_data_rate_index(&self, uplink_dr: u8, rx1_dr_offset: usize) -> Result<u8>;
fn get_tx_power_offset(&self, tx_power: usize) -> Result<isize>;
fn add_channel(&mut self, frequency: u32, min_dr: u8, max_dr: u8) -> Result<()>;
fn get_uplink_channel(&self, channel: usize) -> Result<Channel>;
fn get_uplink_channel_index(&self, frequency: u32, user_defined: bool) -> Result<usize>;
fn get_uplink_channel_index_for_freq_dr(&self, frequency: u32, dr: u8) -> Result<usize>;
fn get_downlink_channel(&self, channel: usize) -> Result<Channel>;
fn disable_uplink_channel_index(&mut self, channel: usize) -> Result<()>;
fn enable_uplink_channel_index(&mut self, channel: usize) -> Result<()>;
fn get_uplink_channel_indices(&self) -> Vec<usize>;
fn get_default_uplink_channel_indices(&self) -> Vec<usize>;
fn get_user_defined_uplink_channel_indices(&self) -> Vec<usize>;
fn get_enabled_uplink_channel_indices(&self) -> Vec<usize>;
fn get_disabled_uplink_channel_indices(&self) -> Vec<usize>;
fn get_enabled_uplink_data_rates(&self) -> Vec<u8>;
fn get_rx1_channel_index_for_uplink_channel_index(
&self,
uplink_channel: usize,
) -> Result<usize>;
fn get_rx1_frequency_for_uplink_frequency(&self, uplink_freq: u32) -> Result<u32>;
fn get_ping_slot_frequency(&self, dev_addr: DevAddr, beacon_time: Duration) -> Result<u32>;
fn get_cf_list(&self, mac_version: MacVersion) -> Option<CFList>;
fn get_link_adr_req_payloads_for_enabled_uplink_channel_indices(
&self,
device_enabled_channels: &[usize],
) -> Vec<LinkADRReqPayload>;
fn get_enabled_uplink_channel_indices_for_link_adr_payloads(
&self,
device_enabled_channels: &[usize],
pls: &[LinkADRReqPayload],
) -> Result<Vec<usize>>;
fn get_downlink_tx_power_eirp(&self, frequency: u32) -> isize;
fn get_defaults(&self) -> Defaults;
fn implements_tx_param_setup(&self, mac_version: MacVersion) -> bool;
}
struct RegionBaseConfig {
supports_user_channels: bool,
cf_list_min_dr: u8,
cf_list_max_dr: u8,
data_rates: HashMap<u8, DataRate>,
max_payload_size_per_dr: HashMap<MacVersion, HashMap<Revision, HashMap<u8, MaxPayloadSize>>>,
rx1_data_rate_table: HashMap<u8, Vec<u8>>,
tx_power_offsets: Vec<isize>,
uplink_channels: Vec<Channel>,
downlink_channels: Vec<Channel>,
}
impl RegionBaseConfig {
fn get_data_rate_index(&self, uplink: bool, modulation: &DataRateModulation) -> Result<u8> {
for (i, dr) in &self.data_rates {
if uplink != dr.uplink && uplink == dr.downlink {
continue;
}
if modulation == &dr.modulation {
return Ok(*i);
}
}
Err(anyhow!("Unknown data-rate: {:?}", modulation))
}
fn get_data_rate(&self, dr: u8) -> Result<DataRateModulation> {
Ok(self
.data_rates
.get(&dr)
.ok_or_else(|| anyhow!("Unknown data-rate index"))?
.modulation
.clone())
}
fn get_max_payload_size(
&self,
mac_version: MacVersion,
reg_params_revision: Revision,
dr: u8,
) -> Result<MaxPayloadSize> {
let reg_params_map = match self.max_payload_size_per_dr.get(&mac_version) {
Some(v) => v,
None => self
.max_payload_size_per_dr
.get(&MacVersion::Latest)
.ok_or_else(|| anyhow!("Unknown mac-version"))?,
};
let dr_map = match reg_params_map.get(®_params_revision) {
Some(v) => v,
None => reg_params_map
.get(&Revision::Latest)
.ok_or_else(|| anyhow!("Unknown revision"))?,
};
Ok(dr_map
.get(&dr)
.ok_or_else(|| anyhow!("Invalid data-rate"))?
.clone())
}
fn get_rx1_data_rate_index(&self, uplink_dr: u8, rx1_dr_offset: usize) -> Result<u8> {
let offset_vec = self
.rx1_data_rate_table
.get(&uplink_dr)
.ok_or_else(|| anyhow!("Unknown data-rate"))?;
Ok(*offset_vec
.get(rx1_dr_offset)
.ok_or_else(|| anyhow!("Invalid rx1 data-rate offset"))?)
}
fn get_tx_power_offset(&self, tx_power: usize) -> Result<isize> {
Ok(*self
.tx_power_offsets
.get(tx_power)
.ok_or_else(|| anyhow!("Invalid tx-power"))?)
}
fn add_channel(&mut self, frequency: u32, min_dr: u8, max_dr: u8) -> Result<()> {
if !self.supports_user_channels {
return Err(anyhow!(
"User defined channels are not supported for this band"
));
}
let c = Channel {
frequency,
min_dr,
max_dr,
user_defined: true,
enabled: frequency != 0,
};
self.uplink_channels.push(c.clone());
self.downlink_channels.push(c);
Ok(())
}
fn get_uplink_channel(&self, channel: usize) -> Result<Channel> {
Ok(self
.uplink_channels
.get(channel)
.ok_or_else(|| anyhow!("Invalid channel"))?
.clone())
}
fn get_uplink_channel_index(&self, frequency: u32, user_defined: bool) -> Result<usize> {
for (i, c) in self.uplink_channels.iter().enumerate() {
if frequency == c.frequency && user_defined == c.user_defined {
return Ok(i);
}
}
Err(anyhow!("Unknown channel for frequency: {}", frequency))
}
fn get_uplink_channel_index_for_freq_dr(&self, frequency: u32, dr: u8) -> Result<usize> {
for user_defined in &[true, false] {
let i = match self.get_uplink_channel_index(frequency, *user_defined) {
Ok(v) => v,
Err(_) => {
continue;
}
};
let c = self.get_uplink_channel(i).context("Get channel error")?;
if c.min_dr <= dr && c.max_dr >= dr {
return Ok(i);
}
}
Err(anyhow!(
"No channel found for frequency: {}, dr: {}",
frequency,
dr
))
}
fn get_downlink_channel(&self, channel: usize) -> Result<Channel> {
Ok(self
.downlink_channels
.get(channel)
.ok_or_else(|| anyhow!("Invalid channel"))?
.clone())
}
fn disable_uplink_channel_index(&mut self, channel: usize) -> Result<()> {
let channel = self
.uplink_channels
.get_mut(channel)
.ok_or_else(|| anyhow!("Invalid channel"))?;
channel.enabled = false;
Ok(())
}
fn enable_uplink_channel_index(&mut self, channel: usize) -> Result<()> {
let channel = self
.uplink_channels
.get_mut(channel)
.ok_or_else(|| anyhow!("Invalid channel"))?;
channel.enabled = true;
Ok(())
}
fn get_uplink_channel_indices(&self) -> Vec<usize> {
let mut out = Vec::new();
for (i, _) in self.uplink_channels.iter().enumerate() {
out.push(i);
}
out
}
fn get_default_uplink_channel_indices(&self) -> Vec<usize> {
let mut out = Vec::new();
for (i, c) in self.uplink_channels.iter().enumerate() {
if !c.user_defined {
out.push(i);
}
}
out
}
fn get_user_defined_uplink_channel_indices(&self) -> Vec<usize> {
let mut out = Vec::new();
for (i, c) in self.uplink_channels.iter().enumerate() {
if c.user_defined {
out.push(i);
}
}
out
}
fn get_enabled_uplink_channel_indices(&self) -> Vec<usize> {
let mut out = Vec::new();
for (i, c) in self.uplink_channels.iter().enumerate() {
if c.enabled {
out.push(i);
}
}
out
}
fn get_disabled_uplink_channel_indices(&self) -> Vec<usize> {
let mut out = Vec::new();
for (i, c) in self.uplink_channels.iter().enumerate() {
if !c.enabled {
out.push(i);
}
}
out
}
fn get_enabled_uplink_data_rates(&self) -> Vec<u8> {
let mut out: HashSet<u8> = HashSet::new();
for uc in &self.uplink_channels {
for dr in uc.min_dr..=uc.max_dr {
out.insert(dr);
}
}
let mut out: Vec<u8> = out.iter().cloned().collect();
out.sort_unstable();
out
}
fn get_cf_list(&self, mac_version: MacVersion) -> Option<CFList> {
if self.supports_user_channels {
return self.get_cf_list_channels();
}
if !self.supports_user_channels
&& (mac_version == MacVersion::LORAWAN_1_0_0
|| mac_version == MacVersion::LORAWAN_1_0_1
|| mac_version == MacVersion::LORAWAN_1_0_2)
{
return None;
}
self.get_cf_list_channel_mask()
}
fn get_cf_list_channels(&self) -> Option<CFList> {
let mut channels: [u32; 5] = [0; 5];
let mut i = 0;
for c in &self.uplink_channels {
if c.user_defined
&& i < channels.len()
&& c.min_dr == self.cf_list_min_dr
&& c.max_dr == self.cf_list_max_dr
{
channels[i] = c.frequency;
i += 1;
}
}
if i == 0 {
return None;
}
Some(CFList::Channels(CFListChannels::new(channels)))
}
fn get_cf_list_channel_mask(&self) -> Option<CFList> {
const SIZE: usize = 16;
let mut masks: Vec<ChMask> = Vec::new();
let mut mask: [bool; SIZE] = [false; SIZE];
for (i, c) in self.uplink_channels.iter().enumerate() {
if i != 0 && i % SIZE == 0 {
masks.push(ChMask::new(mask));
mask = [false; SIZE];
}
mask[i % SIZE] = c.enabled;
}
masks.push(ChMask::new(mask));
Some(CFList::ChannelMask(CFListChannelMasks::new(masks)))
}
fn get_link_adr_req_payloads_for_enabled_uplink_channel_indices(
&self,
device_enabled_channels: &[usize],
) -> Vec<LinkADRReqPayload> {
let enabled_channels = self.get_enabled_uplink_channel_indices();
let device_set: HashSet<usize> = device_enabled_channels.iter().cloned().collect();
let enabled_set: HashSet<usize> = enabled_channels.iter().cloned().collect();
let mut diff: Vec<usize> = enabled_set
.symmetric_difference(&device_set)
.cloned()
.collect();
let mut filtered_diff: Vec<usize> = Vec::new();
for i in &diff {
if device_set.contains(i) || !self.uplink_channels[*i].user_defined {
filtered_diff.push(*i);
}
}
if diff.is_empty() || filtered_diff.is_empty() {
return vec![];
}
diff.sort_unstable();
let mut out: Vec<LinkADRReqPayload> = Vec::new();
let mut ch_mask_cntl = -1;
for i in diff {
if i as isize / 16 != ch_mask_cntl {
ch_mask_cntl = i as isize / 16;
let mut chmask: [bool; 16] = [false; 16];
for e in &enabled_channels {
if (!self.uplink_channels[*e].user_defined || device_set.contains(e))
&& (*e as isize) >= ch_mask_cntl * 16
&& (*e as isize) < (ch_mask_cntl + 1) * 16
{
chmask[e % 16] = true;
}
}
out.push(LinkADRReqPayload {
dr: 0,
tx_power: 0,
ch_mask: ChMask::new(chmask),
redundancy: Redundancy {
ch_mask_cntl: ch_mask_cntl as u8,
nb_rep: 0,
},
});
}
}
out
}
fn get_enabled_uplink_channel_indices_for_link_adr_payloads(
&self,
device_enabled_channels: &[usize],
pls: &[LinkADRReqPayload],
) -> Result<Vec<usize>> {
let mut chmask: Vec<bool> = vec![false; self.uplink_channels.len()];
for c in device_enabled_channels {
if *c < chmask.len() {
chmask[*c] = true;
}
}
for pl in pls {
for (i, enabled) in pl.ch_mask.into_iter().enumerate() {
let ii = (pl.redundancy.ch_mask_cntl as usize * 16) + i;
if ii >= chmask.len() && !enabled {
continue;
}
if ii >= chmask.len() {
return Err(anyhow!("Channel does not exist"));
}
chmask[ii] = enabled;
}
}
let mut out: Vec<usize> = Vec::new();
for (i, enabled) in chmask.iter().enumerate() {
if *enabled {
out.push(i);
}
}
Ok(out)
}
}
pub fn get(
common_name: CommonName,
repeater_compatible: bool,
dwell_time_400ms: bool,
) -> Box<dyn Region + Sync + Send> {
match common_name {
CommonName::AS923 => Box::new(as923::Configuration::new(
CommonName::AS923,
repeater_compatible,
dwell_time_400ms,
)),
CommonName::AS923_2 => Box::new(as923::Configuration::new(
CommonName::AS923_2,
repeater_compatible,
dwell_time_400ms,
)),
CommonName::AS923_3 => Box::new(as923::Configuration::new(
CommonName::AS923_3,
repeater_compatible,
dwell_time_400ms,
)),
CommonName::AS923_4 => Box::new(as923::Configuration::new(
CommonName::AS923_4,
repeater_compatible,
dwell_time_400ms,
)),
CommonName::AU915 => Box::new(au915::Configuration::new(
repeater_compatible,
dwell_time_400ms,
)),
CommonName::CN470 => Box::new(cn470::Configuration::new(repeater_compatible)),
CommonName::CN779 => Box::new(cn779::Configuration::new(repeater_compatible)),
CommonName::EU433 => Box::new(eu433::Configuration::new(repeater_compatible)),
CommonName::EU868 => Box::new(eu868::Configuration::new(repeater_compatible)),
CommonName::IN865 => Box::new(in865::Configuration::new(repeater_compatible)),
CommonName::ISM2400 => Box::new(ism2400::Configuration::new(repeater_compatible)),
CommonName::KR920 => Box::new(kr920::Configuration::new(repeater_compatible)),
CommonName::RU864 => Box::new(ru864::Configuration::new(repeater_compatible)),
CommonName::US915 => Box::new(us915::Configuration::new(repeater_compatible)),
}
}