1use lora_modulation::{Bandwidth, BaseBandModulationParams, CodingRate, SpreadingFactor};
3use lorawan::{maccommands::ChannelMask, parser::CfList};
4use rand_core::RngCore;
5
6use crate::mac::{Frame, Window};
7pub(crate) mod constants;
8pub(crate) use crate::radio::*;
9use constants::*;
10
11#[cfg(not(any(
12 feature = "region-as923-1",
13 feature = "region-as923-2",
14 feature = "region-as923-3",
15 feature = "region-as923-4",
16 feature = "region-eu433",
17 feature = "region-eu868",
18 feature = "region-in865",
19 feature = "region-au915",
20 feature = "region-us915"
21)))]
22compile_error!("You must enable at least one region! eg: `region-eu868`, `region-us915`...");
23
24#[cfg(any(
25 feature = "region-as923-1",
26 feature = "region-as923-2",
27 feature = "region-as923-3",
28 feature = "region-as923-4",
29 feature = "region-eu433",
30 feature = "region-eu868",
31 feature = "region-in865"
32))]
33mod dynamic_channel_plans;
34#[cfg(feature = "region-as923-1")]
35pub(crate) use dynamic_channel_plans::AS923_1;
36#[cfg(feature = "region-as923-2")]
37pub(crate) use dynamic_channel_plans::AS923_2;
38#[cfg(feature = "region-as923-3")]
39pub(crate) use dynamic_channel_plans::AS923_3;
40#[cfg(feature = "region-as923-4")]
41pub(crate) use dynamic_channel_plans::AS923_4;
42#[cfg(feature = "region-eu433")]
43pub(crate) use dynamic_channel_plans::EU433;
44#[cfg(feature = "region-eu868")]
45pub(crate) use dynamic_channel_plans::EU868;
46#[cfg(feature = "region-in865")]
47pub(crate) use dynamic_channel_plans::IN865;
48
49#[cfg(any(feature = "region-us915", feature = "region-au915"))]
50mod fixed_channel_plans;
51#[cfg(any(feature = "region-us915", feature = "region-au915"))]
52pub use fixed_channel_plans::Subband;
53#[cfg(feature = "region-au915")]
54pub use fixed_channel_plans::AU915;
55#[cfg(feature = "region-us915")]
56pub use fixed_channel_plans::US915;
57
58pub(crate) trait ChannelRegion<const D: usize> {
59 fn datarates() -> &'static [Option<Datarate>; D];
60
61 fn get_max_payload_length(datarate: DR, repeater_compatible: bool, dwell_time: bool) -> u8 {
62 let Some(Some(dr)) = Self::datarates().get(datarate as usize) else {
63 return 0;
64 };
65 let max_size = if dwell_time {
66 dr.max_mac_payload_size_with_dwell_time
67 } else {
68 dr.max_mac_payload_size
69 };
70 if repeater_compatible && max_size > 230 {
71 230
72 } else {
73 max_size
74 }
75 }
76}
77
78#[derive(Clone)]
79pub struct Configuration {
82 state: State,
83}
84
85seq_macro::seq!(
86 N in 0..=15 {
87 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
88 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
89 #[repr(u8)]
90 pub enum DR {
93 #(
94 _~N = N,
95 )*
96 }
97 }
98);
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
101pub enum Region {
105 #[cfg(feature = "region-as923-1")]
106 AS923_1,
107 #[cfg(feature = "region-as923-2")]
108 AS923_2,
109 #[cfg(feature = "region-as923-3")]
110 AS923_3,
111 #[cfg(feature = "region-as923-4")]
112 AS923_4,
113 #[cfg(feature = "region-au915")]
114 AU915,
115 #[cfg(feature = "region-eu868")]
116 EU868,
117 #[cfg(feature = "region-eu433")]
118 EU433,
119 #[cfg(feature = "region-in865")]
120 IN865,
121 #[cfg(feature = "region-us915")]
122 US915,
123}
124
125#[derive(Clone)]
126enum State {
127 #[cfg(feature = "region-as923-1")]
128 AS923_1(AS923_1),
129 #[cfg(feature = "region-as923-2")]
130 AS923_2(AS923_2),
131 #[cfg(feature = "region-as923-3")]
132 AS923_3(AS923_3),
133 #[cfg(feature = "region-as923-4")]
134 AS923_4(AS923_4),
135 #[cfg(feature = "region-au915")]
136 AU915(AU915),
137 #[cfg(feature = "region-eu868")]
138 EU868(EU868),
139 #[cfg(feature = "region-eu433")]
140 EU433(EU433),
141 #[cfg(feature = "region-in865")]
142 IN865(IN865),
143 #[cfg(feature = "region-us915")]
144 US915(US915),
145}
146
147impl State {
148 pub fn new(region: Region) -> State {
149 match region {
150 #[cfg(feature = "region-as923-1")]
151 Region::AS923_1 => State::AS923_1(AS923_1::default()),
152 #[cfg(feature = "region-as923-2")]
153 Region::AS923_2 => State::AS923_2(AS923_2::default()),
154 #[cfg(feature = "region-as923-3")]
155 Region::AS923_3 => State::AS923_3(AS923_3::default()),
156 #[cfg(feature = "region-as923-4")]
157 Region::AS923_4 => State::AS923_4(AS923_4::default()),
158 #[cfg(feature = "region-au915")]
159 Region::AU915 => State::AU915(AU915::default()),
160 #[cfg(feature = "region-eu868")]
161 Region::EU868 => State::EU868(EU868::default()),
162 #[cfg(feature = "region-eu433")]
163 Region::EU433 => State::EU433(EU433::default()),
164 #[cfg(feature = "region-in865")]
165 Region::IN865 => State::IN865(IN865::default()),
166 #[cfg(feature = "region-us915")]
167 Region::US915 => State::US915(US915::default()),
168 }
169 }
170
171 #[allow(dead_code)]
172 pub fn region(&self) -> Region {
173 match self {
174 #[cfg(feature = "region-as923-1")]
175 Self::AS923_1(_) => Region::AS923_1,
176 #[cfg(feature = "region-as923-2")]
177 Self::AS923_2(_) => Region::AS923_2,
178 #[cfg(feature = "region-as923-3")]
179 Self::AS923_3(_) => Region::AS923_3,
180 #[cfg(feature = "region-as923-4")]
181 Self::AS923_4(_) => Region::AS923_4,
182 #[cfg(feature = "region-au915")]
183 Self::AU915(_) => Region::AU915,
184 #[cfg(feature = "region-eu433")]
185 Self::EU433(_) => Region::EU433,
186 #[cfg(feature = "region-eu868")]
187 Self::EU868(_) => Region::EU868,
188 #[cfg(feature = "region-in865")]
189 Self::IN865(_) => Region::IN865,
190 #[cfg(feature = "region-us915")]
191 Self::US915(_) => Region::US915,
192 }
193 }
194}
195
196#[derive(Debug, Clone)]
198pub(crate) struct Datarate {
199 bandwidth: Bandwidth,
200 spreading_factor: SpreadingFactor,
201 max_mac_payload_size: u8,
202 max_mac_payload_size_with_dwell_time: u8,
203}
204macro_rules! mut_region_dispatch {
205 ($s:expr, $t:tt) => {
206 match &mut $s.state {
207 #[cfg(feature = "region-as923-1")]
208 State::AS923_1(state) => state.$t(),
209 #[cfg(feature = "region-as923-2")]
210 State::AS923_2(state) => state.$t(),
211 #[cfg(feature = "region-as923-3")]
212 State::AS923_3(state) => state.$t(),
213 #[cfg(feature = "region-as923-4")]
214 State::AS923_4(state) => state.$t(),
215 #[cfg(feature = "region-au915")]
216 State::AU915(state) => state.0.$t(),
217 #[cfg(feature = "region-eu868")]
218 State::EU868(state) => state.$t(),
219 #[cfg(feature = "region-eu433")]
220 State::EU433(state) => state.$t(),
221 #[cfg(feature = "region-in865")]
222 State::IN865(state) => state.$t(),
223 #[cfg(feature = "region-us915")]
224 State::US915(state) => state.0.$t(),
225 }
226 };
227 ($s:expr, $t:tt, $($arg:tt)*) => {
228 match &mut $s.state {
229 #[cfg(feature = "region-as923-1")]
230 State::AS923_1(state) => state.$t($($arg)*),
231 #[cfg(feature = "region-as923-2")]
232 State::AS923_2(state) => state.$t($($arg)*),
233 #[cfg(feature = "region-as923-3")]
234 State::AS923_3(state) => state.$t($($arg)*),
235 #[cfg(feature = "region-as923-4")]
236 State::AS923_4(state) => state.$t($($arg)*),
237 #[cfg(feature = "region-au915")]
238 State::AU915(state) => state.0.$t($($arg)*),
239 #[cfg(feature = "region-eu868")]
240 State::EU868(state) => state.$t($($arg)*),
241 #[cfg(feature = "region-eu433")]
242 State::EU433(state) => state.$t($($arg)*),
243 #[cfg(feature = "region-in865")]
244 State::IN865(state) => state.$t($($arg)*),
245 #[cfg(feature = "region-us915")]
246 State::US915(state) => state.0.$t($($arg)*),
247 }
248 };
249}
250
251macro_rules! region_dispatch {
252 ($s:expr, $t:tt) => {
253 match &$s.state {
254 #[cfg(feature = "region-as923-1")]
255 State::AS923_1(state) => state.$t(),
256 #[cfg(feature = "region-as923-2")]
257 State::AS923_2(state) => state.$t(),
258 #[cfg(feature = "region-as923-3")]
259 State::AS923_3(state) => state.$t(),
260 #[cfg(feature = "region-as923-4")]
261 State::AS923_4(state) => state.$t(),
262 #[cfg(feature = "region-au915")]
263 State::AU915(state) => state.0.$t(),
264 #[cfg(feature = "region-eu868")]
265 State::EU868(state) => state.$t(),
266 #[cfg(feature = "region-eu433")]
267 State::EU433(state) => state.$t(),
268 #[cfg(feature = "region-in865")]
269 State::IN865(state) => state.$t(),
270 #[cfg(feature = "region-us915")]
271 State::US915(state) => state.0.$t(),
272 }
273 };
274 ($s:expr, $t:tt, $($arg:tt)*) => {
275 match &$s.state {
276 #[cfg(feature = "region-as923-1")]
277 State::AS923_1(state) => state.$t($($arg)*),
278 #[cfg(feature = "region-as923-2")]
279 State::AS923_2(state) => state.$t($($arg)*),
280 #[cfg(feature = "region-as923-3")]
281 State::AS923_3(state) => state.$t($($arg)*),
282 #[cfg(feature = "region-as923-4")]
283 State::AS923_4(state) => state.$t($($arg)*),
284 #[cfg(feature = "region-au915")]
285 State::AU915(state) => state.0.$t($($arg)*),
286 #[cfg(feature = "region-eu868")]
287 State::EU868(state) => state.$t($($arg)*),
288 #[cfg(feature = "region-eu433")]
289 State::EU433(state) => state.$t($($arg)*),
290 #[cfg(feature = "region-in865")]
291 State::IN865(state) => state.$t($($arg)*),
292 #[cfg(feature = "region-us915")]
293 State::US915(state) => state.0.$t($($arg)*),
294 }
295 };
296}
297
298macro_rules! region_static_dispatch {
299 ($s:expr, $t:tt) => {
300 match &$s.state {
301 #[cfg(feature = "region-as923-1")]
302 State::AS923_1(_) => dynamic_channel_plans::AS923_1::$t(),
303 #[cfg(feature = "region-as923-2")]
304 State::AS923_2(_) => dynamic_channel_plans::AS923_2::$t(),
305 #[cfg(feature = "region-as923-3")]
306 State::AS923_3(_) => dynamic_channel_plans::AS923_3::$t(),
307 #[cfg(feature = "region-as923-4")]
308 State::AS923_4(_) => dynamic_channel_plans::AS923_4::$t(),
309 #[cfg(feature = "region-au915")]
310 State::AU915(_) => fixed_channel_plans::AU915::$t(),
311 #[cfg(feature = "region-eu868")]
312 State::EU868(_) => dynamic_channel_plans::EU868::$t(),
313 #[cfg(feature = "region-eu433")]
314 State::EU433(_) => dynamic_channel_plans::EU433::$t(),
315 #[cfg(feature = "region-in865")]
316 State::IN865(_) => dynamic_channel_plans::IN865::$t(),
317 #[cfg(feature = "region-us915")]
318 State::US915(_) => fixed_channel_plans::US915::$t(),
319 }
320 };
321 ($s:expr, $t:tt, $($arg:tt)*) => {
322 match &$s.state {
323 #[cfg(feature = "region-as923-1")]
324 State::AS923_1(_) => dynamic_channel_plans::AS923_1::$t($($arg)*),
325 #[cfg(feature = "region-as923-2")]
326 State::AS923_2(_) => dynamic_channel_plans::AS923_2::$t($($arg)*),
327 #[cfg(feature = "region-as923-3")]
328 State::AS923_3(_) => dynamic_channel_plans::AS923_3::$t($($arg)*),
329 #[cfg(feature = "region-as923-4")]
330 State::AS923_4(_) => dynamic_channel_plans::AS923_4::$t($($arg)*),
331 #[cfg(feature = "region-au915")]
332 State::AU915(_) => fixed_channel_plans::AU915::$t($($arg)*),
333 #[cfg(feature = "region-eu868")]
334 State::EU868(_) => dynamic_channel_plans::EU868::$t($($arg)*),
335 #[cfg(feature = "region-eu433")]
336 State::EU433(_) => dynamic_channel_plans::EU433::$t($($arg)*),
337 #[cfg(feature = "region-in865")]
338 State::IN865(_) => dynamic_channel_plans::IN865::$t($($arg)*),
339 #[cfg(feature = "region-us915")]
340 State::US915(_) => fixed_channel_plans::US915::$t($($arg)*),
341 }
342 };
343}
344
345impl Configuration {
346 pub fn new(region: Region) -> Configuration {
347 Configuration::with_state(State::new(region))
348 }
349
350 fn with_state(state: State) -> Configuration {
351 Configuration { state }
352 }
353
354 pub fn get_max_payload_length(
355 &self,
356 datarate: DR,
357 repeater_compatible: bool,
358 dwell_time: bool,
359 ) -> u8 {
360 region_static_dispatch!(
361 self,
362 get_max_payload_length,
363 datarate,
364 repeater_compatible,
365 dwell_time
366 )
367 }
368
369 pub(crate) fn create_tx_config<RNG: RngCore>(
370 &mut self,
371 rng: &mut RNG,
372 datarate: DR,
373 frame: &Frame,
374 ) -> TxConfig {
375 let (dr, frequency) = self.get_tx_dr_and_frequency(rng, datarate, frame);
376 TxConfig {
377 pw: self.get_dbm(),
378 rf: RfConfig {
379 frequency,
380 bb: BaseBandModulationParams::new(
381 dr.spreading_factor,
382 dr.bandwidth,
383 self.get_coding_rate(),
384 ),
385 },
386 }
387 }
388
389 fn get_tx_dr_and_frequency<RNG: RngCore>(
390 &mut self,
391 rng: &mut RNG,
392 datarate: DR,
393 frame: &Frame,
394 ) -> (Datarate, u32) {
395 mut_region_dispatch!(self, get_tx_dr_and_frequency, rng, datarate, frame)
396 }
397
398 pub(crate) fn get_rx_config(&self, datarate: DR, frame: &Frame, window: &Window) -> RfConfig {
399 let dr = self.get_rx_datarate(datarate, frame, window);
400 RfConfig {
401 frequency: self.get_rx_frequency(frame, window),
402 bb: BaseBandModulationParams::new(
403 dr.spreading_factor,
404 dr.bandwidth,
405 self.get_coding_rate(),
406 ),
407 }
408 }
409
410 pub(crate) fn process_join_accept<T: AsRef<[u8]>, C>(
411 &mut self,
412 join_accept: &DecryptedJoinAcceptPayload<T, C>,
413 ) {
414 mut_region_dispatch!(self, process_join_accept, join_accept)
415 }
416
417 pub(crate) fn set_channel_mask(
418 &mut self,
419 channel_mask_control: u8,
420 channel_mask: ChannelMask<2>,
421 ) {
422 mut_region_dispatch!(self, handle_link_adr_channel_mask, channel_mask_control, channel_mask)
423 }
424
425 pub(crate) fn get_rx_frequency(&self, frame: &Frame, window: &Window) -> u32 {
426 region_dispatch!(self, get_rx_frequency, frame, window)
427 }
428
429 pub(crate) fn get_default_datarate(&self) -> DR {
430 region_dispatch!(self, get_default_datarate)
431 }
432
433 pub(crate) fn get_rx_datarate(&self, datarate: DR, frame: &Frame, window: &Window) -> Datarate {
434 region_dispatch!(self, get_rx_datarate, datarate, frame, window)
435 }
436
437 pub(crate) fn get_rxc_config(&self, datarate: DR) -> RfConfig {
441 let dr = self.get_rx_datarate(datarate, &Frame::Data, &Window::_2);
442 let frequency = self.get_rx_frequency(&Frame::Data, &Window::_2);
443 RfConfig {
444 frequency,
445 bb: BaseBandModulationParams::new(
446 dr.spreading_factor,
447 dr.bandwidth,
448 self.get_coding_rate(),
449 ),
450 }
451 }
452
453 pub(crate) fn get_dbm(&self) -> i8 {
454 region_dispatch!(self, get_dbm)
455 }
456
457 pub(crate) fn get_coding_rate(&self) -> CodingRate {
458 region_dispatch!(self, get_coding_rate)
459 }
460
461 #[allow(dead_code)]
462 pub(crate) fn get_current_region(&self) -> super::region::Region {
463 self.state.region()
464 }
465}
466
467macro_rules! from_region {
468 ($r:tt) => {
469 impl From<$r> for Configuration {
470 fn from(region: $r) -> Configuration {
471 Configuration::with_state(State::$r(region))
472 }
473 }
474 };
475}
476
477#[cfg(feature = "region-as923-1")]
478from_region!(AS923_1);
479#[cfg(feature = "region-as923-2")]
480from_region!(AS923_2);
481#[cfg(feature = "region-as923-3")]
482from_region!(AS923_3);
483#[cfg(feature = "region-as923-4")]
484from_region!(AS923_4);
485#[cfg(feature = "region-in865")]
486from_region!(IN865);
487#[cfg(feature = "region-au915")]
488from_region!(AU915);
489#[cfg(feature = "region-eu868")]
490from_region!(EU868);
491#[cfg(feature = "region-eu433")]
492from_region!(EU433);
493#[cfg(feature = "region-us915")]
494from_region!(US915);
495
496use lorawan::parser::DecryptedJoinAcceptPayload;
497
498pub(crate) trait RegionHandler {
499 fn process_join_accept<T: AsRef<[u8]>, C>(
500 &mut self,
501 join_accept: &DecryptedJoinAcceptPayload<T, C>,
502 );
503
504 fn handle_link_adr_channel_mask(
505 &mut self,
506 channel_mask_control: u8,
507 channel_mask: ChannelMask<2>,
508 );
509
510 fn get_default_datarate(&self) -> DR {
511 DR::_0
512 }
513 fn get_tx_dr_and_frequency<RNG: RngCore>(
514 &mut self,
515 rng: &mut RNG,
516 datarate: DR,
517 frame: &Frame,
518 ) -> (Datarate, u32);
519
520 fn get_rx_frequency(&self, frame: &Frame, window: &Window) -> u32;
521 fn get_rx_datarate(&self, datarate: DR, frame: &Frame, window: &Window) -> Datarate;
522 fn get_dbm(&self) -> i8 {
523 DEFAULT_DBM
524 }
525 fn get_coding_rate(&self) -> CodingRate {
526 DEFAULT_CODING_RATE
527 }
528}